aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2011-09-22 12:58:32 +0200
committerBjørn Mork <bjorn@mork.no>2011-09-22 12:58:32 +0200
commit8f7a623a2ce25cdd5f4f075562d6336044f3ad1f (patch)
tree9bd37b82716620ee7f83ce8b2cd48ec9b279e3c8
Initial import from ftp://ftp.isc.org/isc/dhcp/dhcp-4.2.2.tar.gzv4.2.2
Signed-off-by: Bjørn Mork <bjorn@mork.no>
-rw-r--r--.gitignore12
-rw-r--r--LICENSE20
-rw-r--r--Makefile.am27
-rw-r--r--Makefile.in631
-rw-r--r--README655
-rw-r--r--RELNOTES3237
-rw-r--r--aclocal.m4868
-rw-r--r--bind/Makefile75
-rw-r--r--bind/bind.tar.gzbin0 -> 7702703 bytes
-rw-r--r--bind/version.tmp10
-rw-r--r--client/Makefile.am18
-rw-r--r--client/Makefile.in561
-rw-r--r--client/clparse.c2235
-rw-r--r--client/dhc6.c5128
-rw-r--r--client/dhclient-script.8234
-rw-r--r--client/dhclient.8486
-rw-r--r--client/dhclient.c4282
-rw-r--r--client/dhclient.conf36
-rw-r--r--client/dhclient.conf.5739
-rw-r--r--client/dhclient.leases.557
-rwxr-xr-xclient/scripts/bsdos324
-rwxr-xr-xclient/scripts/freebsd392
-rwxr-xr-xclient/scripts/linux318
-rwxr-xr-xclient/scripts/macos202
-rwxr-xr-xclient/scripts/netbsd324
-rw-r--r--client/scripts/nextstep61
-rw-r--r--client/scripts/openbsd318
-rwxr-xr-xclient/scripts/openwrt279
-rwxr-xr-xclient/scripts/solaris203
-rw-r--r--common/Makefile.am13
-rw-r--r--common/Makefile.in605
-rw-r--r--common/alloc.c1304
-rw-r--r--common/bpf.c609
-rw-r--r--common/comapi.c922
-rw-r--r--common/conflex.c1523
-rw-r--r--common/ctrace.c295
-rw-r--r--common/dhcp-eval.5552
-rw-r--r--common/dhcp-options.52141
-rw-r--r--common/discover.c1867
-rw-r--r--common/dispatch.c416
-rw-r--r--common/dlpi.c1416
-rw-r--r--common/dns.c1706
-rw-r--r--common/ethernet.c93
-rw-r--r--common/execute.c1152
-rw-r--r--common/fddi.c92
-rw-r--r--common/icmp.c308
-rw-r--r--common/inet.c631
-rw-r--r--common/lpf.c470
-rw-r--r--common/memory.c154
-rw-r--r--common/nit.c422
-rw-r--r--common/ns_name.c651
-rw-r--r--common/options.c4079
-rw-r--r--common/packet.c346
-rw-r--r--common/parse.c5898
-rw-r--r--common/print.c1518
-rw-r--r--common/raw.c139
-rw-r--r--common/resolv.c195
-rw-r--r--common/socket.c1128
-rw-r--r--common/tables.c1414
-rw-r--r--common/tests/Makefile.am11
-rw-r--r--common/tests/Makefile.in468
-rw-r--r--common/tests/test_alloc.c497
-rw-r--r--common/tr.c331
-rw-r--r--common/tree.c4461
-rw-r--r--common/upf.c373
-rwxr-xr-xconfigure9935
-rw-r--r--configure.ac583
-rwxr-xr-xcontrib/3.0b1-lease-convert126
-rw-r--r--contrib/dhclient-tz-exithook.sh179
-rw-r--r--contrib/dhcp.spec157
-rw-r--r--contrib/ldap/README.ldap191
-rw-r--r--contrib/ldap/dhcp.schema462
-rw-r--r--contrib/ldap/dhcpd-conf-to-ldap760
-rw-r--r--contrib/ms2isc/Registry.pm361
-rw-r--r--contrib/ms2isc/ms2isc.pl634
-rw-r--r--contrib/ms2isc/readme.txt15
-rw-r--r--contrib/sethostname.sh29
-rw-r--r--contrib/solaris.init28
-rwxr-xr-xdepcomp530
-rw-r--r--dhcpctl/Makefile.am15
-rw-r--r--dhcpctl/Makefile.in591
-rw-r--r--dhcpctl/callback.c168
-rw-r--r--dhcpctl/cltest.c179
-rw-r--r--dhcpctl/dhcpctl.3493
-rw-r--r--dhcpctl/dhcpctl.c590
-rw-r--r--dhcpctl/dhcpctl.h125
-rw-r--r--dhcpctl/omshell.1334
-rw-r--r--dhcpctl/omshell.c734
-rw-r--r--dhcpctl/remote.c361
-rw-r--r--doc/IANA-arp-parameters145
-rw-r--r--doc/Makefile29
-rw-r--r--doc/References.html1031
-rw-r--r--doc/References.txt1120
-rw-r--r--doc/References.xml788
-rw-r--r--doc/api+protocol436
-rw-r--r--doc/examples/dhclient-dhcpv6.conf11
-rw-r--r--doc/examples/dhcpd-dhcpv6.conf105
-rw-r--r--doc/ja_JP.eucJP/dhclient-script.8242
-rw-r--r--doc/ja_JP.eucJP/dhclient.8365
-rw-r--r--doc/ja_JP.eucJP/dhclient.conf.5625
-rw-r--r--doc/ja_JP.eucJP/dhclient.leases.562
-rw-r--r--doc/ja_JP.eucJP/dhcp-eval.5488
-rw-r--r--doc/ja_JP.eucJP/dhcp-options.51582
-rw-r--r--dst/Makefile.am8
-rw-r--r--dst/Makefile.in439
-rw-r--r--dst/base64.c326
-rw-r--r--dst/dst_api.c1089
-rw-r--r--dst/dst_internal.h171
-rw-r--r--dst/dst_support.c465
-rw-r--r--dst/hmac_link.c496
-rw-r--r--dst/md5.h123
-rw-r--r--dst/md5_dgst.c396
-rw-r--r--dst/md5_locl.h211
-rw-r--r--dst/prandom.c957
-rw-r--r--includes/Makefile.am11
-rw-r--r--includes/Makefile.in403
-rw-r--r--includes/arpa/nameser.h467
-rw-r--r--includes/arpa/nameser_compat.h183
-rw-r--r--includes/cdefs.h60
-rw-r--r--includes/config.h.in265
-rw-r--r--includes/ctrace.h77
-rw-r--r--includes/dhcp.h202
-rw-r--r--includes/dhcp6.h213
-rw-r--r--includes/dhcpd.h3552
-rw-r--r--includes/dhctoken.h370
-rw-r--r--includes/failover.h400
-rw-r--r--includes/heap.h163
-rw-r--r--includes/inet.h85
-rw-r--r--includes/isc-dhcp/dst.h142
-rw-r--r--includes/minires.h43
-rw-r--r--includes/netinet/if_ether.h66
-rw-r--r--includes/netinet/ip.h163
-rw-r--r--includes/netinet/ip_icmp.h182
-rw-r--r--includes/netinet/udp.h70
-rw-r--r--includes/omapip/alloc.h111
-rw-r--r--includes/omapip/buffer.h83
-rw-r--r--includes/omapip/convert.h52
-rw-r--r--includes/omapip/hash.h167
-rw-r--r--includes/omapip/isclib.h122
-rw-r--r--includes/omapip/omapip.h621
-rw-r--r--includes/omapip/omapip_p.h306
-rw-r--r--includes/omapip/result.h120
-rw-r--r--includes/omapip/trace.h115
-rw-r--r--includes/osdep.h293
-rw-r--r--includes/site.h229
-rw-r--r--includes/statement.h110
-rw-r--r--includes/t_api.h103
-rw-r--r--includes/tree.h355
-rwxr-xr-xinstall-sh323
-rwxr-xr-xmissing360
-rw-r--r--omapip/Makefile.am14
-rw-r--r--omapip/Makefile.in529
-rw-r--r--omapip/alloc.c1169
-rw-r--r--omapip/array.c162
-rw-r--r--omapip/auth.c284
-rw-r--r--omapip/buffer.c723
-rw-r--r--omapip/connection.c1109
-rw-r--r--omapip/convert.c185
-rw-r--r--omapip/dispatch.c912
-rw-r--r--omapip/errwarn.c364
-rw-r--r--omapip/generic.c305
-rw-r--r--omapip/handle.c310
-rw-r--r--omapip/hash.c566
-rw-r--r--omapip/inet_addr.c147
-rw-r--r--omapip/isclib.c219
-rw-r--r--omapip/iscprint.c539
-rw-r--r--omapip/listener.c478
-rw-r--r--omapip/message.c768
-rw-r--r--omapip/omapi.3247
-rw-r--r--omapip/protocol.c1314
-rw-r--r--omapip/result.c86
-rw-r--r--omapip/support.c852
-rw-r--r--omapip/test.c111
-rw-r--r--omapip/toisc.c217
-rw-r--r--omapip/trace.c716
-rw-r--r--relay/Makefile.am9
-rw-r--r--relay/Makefile.in470
-rw-r--r--relay/dhcrelay.8258
-rw-r--r--relay/dhcrelay.c1679
-rw-r--r--server/Makefile.am16
-rw-r--r--server/Makefile.in817
-rw-r--r--server/bootp.c422
-rw-r--r--server/class.c308
-rw-r--r--server/confpars.c5504
-rw-r--r--server/db.c1180
-rw-r--r--server/ddns.c1764
-rw-r--r--server/dhcp.c4474
-rw-r--r--server/dhcpd.8805
-rw-r--r--server/dhcpd.c1504
-rw-r--r--server/dhcpd.conf104
-rw-r--r--server/dhcpd.conf.53005
-rw-r--r--server/dhcpd.leases.5289
-rw-r--r--server/dhcpleasequery.c1268
-rw-r--r--server/dhcpv6.c6056
-rw-r--r--server/failover.c6391
-rw-r--r--server/ldap.c2004
-rw-r--r--server/ldap_casa.c159
-rw-r--r--server/mdb.c2989
-rw-r--r--server/mdb6.c2555
-rw-r--r--server/omapi.c2584
-rw-r--r--server/salloc.c254
-rw-r--r--server/stables.c510
-rw-r--r--tests/DHCPv6/000-badmsgtype.pl164
-rw-r--r--tests/DHCPv6/010-solicit-noclientid.pl212
-rw-r--r--tests/DHCPv6/011-solicit-serverid.pl215
-rw-r--r--tests/DHCPv6/020-advertise-mcast.pl164
-rw-r--r--tests/DHCPv6/030-request-noclientid.pl212
-rw-r--r--tests/DHCPv6/031-request-noserverid.pl212
-rw-r--r--tests/DHCPv6/032-request-badduid.pl216
-rw-r--r--tests/DHCPv6/110-information-request-ia_na.pl212
-rw-r--r--tests/DHCPv6/111-information-request-ia_ta.pl212
-rw-r--r--tests/DHCPv6/112-badduid.pl208
-rw-r--r--tests/DHCPv6/210-solicit-nohost.pl212
-rw-r--r--tests/DHCPv6/211-solicit-opt-in-na.pl218
-rw-r--r--tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl218
-rw-r--r--tests/DHCPv6/280-release-nohost.pl175
-rw-r--r--tests/DHCPv6/281-release-bad-address.pl187
-rw-r--r--tests/DHCPv6/282-release-no-address.pl183
-rw-r--r--tests/DHCPv6/283-release.pl189
-rw-r--r--tests/DHCPv6/290-decline-nohost.pl175
-rw-r--r--tests/DHCPv6/291-decline-bad-address.pl187
-rw-r--r--tests/DHCPv6/292-decline-no-address.pl183
-rw-r--r--tests/DHCPv6/293-decline.pl189
-rw-r--r--tests/DHCPv6/README62
-rw-r--r--tests/DHCPv6/dhcp_client.pm454
-rw-r--r--tests/DHCPv6/stubcli-opt-in-na.pl217
-rw-r--r--tests/DHCPv6/stubcli.pl212
-rw-r--r--tests/DHCPv6/test-a.conf67
-rw-r--r--tests/DHCPv6/test-b.conf60
-rw-r--r--tests/HOWTO-unit-test153
-rw-r--r--tests/Makefile.am33
-rw-r--r--tests/Makefile.in420
-rw-r--r--tests/failover/dhcp-1.cf154
-rw-r--r--tests/failover/dhcp-2.cf152
-rwxr-xr-xtests/failover/new-failover28
-rw-r--r--tests/t_api.c824
-rw-r--r--tests/t_api_dhcp.c44
-rw-r--r--tests/unit_test_sample.c25
-rw-r--r--util/bindvar.sh35
239 files changed, 166596 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1850904
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+*~
+*.o
+*.a
+*.d
+*.lo
+*.la
+*.so
+*.lai
+.libs
+.depends
+config.log
+config.status
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e537b29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+# Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+# Copyright (c) 1995-2003 by Internet Software Consortium
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..928d926
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,27 @@
+#
+# automake adds dependencies that we don't like, so we explicitly remove them
+#
+Makefile:
+
+#
+# We have a lot of files that we want shipped with the distribution.
+#
+EXTRA_DIST = RELNOTES LICENSE \
+ contrib/3.0b1-lease-convert contrib/dhclient-tz-exithook.sh \
+ contrib/dhcp.spec contrib/sethostname.sh contrib/solaris.init \
+ contrib/ms2isc/Registry.pm contrib/ms2isc/ms2isc.pl \
+ contrib/ms2isc/readme.txt contrib/ldap/dhcpd-conf-to-ldap \
+ contrib/ldap/dhcp.schema contrib/ldap/README.ldap \
+ doc/IANA-arp-parameters doc/Makefile doc/References.html \
+ doc/References.txt doc/References.xml doc/api+protocol \
+ doc/ja_JP.eucJP/dhclient-script.8 doc/ja_JP.eucJP/dhclient.8 \
+ doc/ja_JP.eucJP/dhclient.conf.5 doc/ja_JP.eucJP/dhclient.leases.5 \
+ doc/ja_JP.eucJP/dhcp-eval.5 doc/ja_JP.eucJP/dhcp-options.5 \
+ doc/examples/dhclient-dhcpv6.conf doc/examples/dhcpd-dhcpv6.conf \
+ util/bindvar.sh \
+ bind/Makefile bind/bind.tar.gz bind/version.tmp
+
+SUBDIRS = bind includes tests common dst omapip client dhcpctl relay server
+
+nobase_include_HEADERS = dhcpctl/dhcpctl.h
+
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..40b8f5e
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,631 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = .
+DIST_COMMON = README $(am__configure_deps) $(nobase_include_HEADERS) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(top_srcdir)/configure depcomp install-sh missing
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(includedir)"
+nobase_includeHEADERS_INSTALL = $(install_sh_DATA)
+HEADERS = $(nobase_include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d $(distdir) \
+ || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+#
+# We have a lot of files that we want shipped with the distribution.
+#
+EXTRA_DIST = RELNOTES LICENSE \
+ contrib/3.0b1-lease-convert contrib/dhclient-tz-exithook.sh \
+ contrib/dhcp.spec contrib/sethostname.sh contrib/solaris.init \
+ contrib/ms2isc/Registry.pm contrib/ms2isc/ms2isc.pl \
+ contrib/ms2isc/readme.txt contrib/ldap/dhcpd-conf-to-ldap \
+ contrib/ldap/dhcp.schema contrib/ldap/README.ldap \
+ doc/IANA-arp-parameters doc/Makefile doc/References.html \
+ doc/References.txt doc/References.xml doc/api+protocol \
+ doc/ja_JP.eucJP/dhclient-script.8 doc/ja_JP.eucJP/dhclient.8 \
+ doc/ja_JP.eucJP/dhclient.conf.5 doc/ja_JP.eucJP/dhclient.leases.5 \
+ doc/ja_JP.eucJP/dhcp-eval.5 doc/ja_JP.eucJP/dhcp-options.5 \
+ doc/examples/dhclient-dhcpv6.conf doc/examples/dhcpd-dhcpv6.conf \
+ util/bindvar.sh \
+ bind/Makefile bind/bind.tar.gz bind/version.tmp
+
+SUBDIRS = bind includes tests common dst omapip client dhcpctl relay server
+nobase_include_HEADERS = dhcpctl/dhcpctl.h
+all: all-recursive
+
+.SUFFIXES:
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+ cd $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+install-nobase_includeHEADERS: $(nobase_include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @$(am__vpath_adj_setup) \
+ list='$(nobase_include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ $(am__vpath_adj) \
+ echo " $(nobase_includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(nobase_includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-nobase_includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @$(am__vpath_adj_setup) \
+ list='$(nobase_include_HEADERS)'; for p in $$list; do \
+ $(am__vpath_adj) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(am__remove_distdir)
+ test -d $(distdir) || mkdir $(distdir)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r $(distdir)
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && cd $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @cd $(distuninstallcheck_dir) \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-nobase_includeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-nobase_includeHEADERS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am am--refresh check check-am clean clean-generic \
+ ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \
+ dist-lzma dist-shar dist-tarZ dist-zip distcheck distclean \
+ distclean-generic distclean-tags distcleancheck distdir \
+ distuninstallcheck dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-nobase_includeHEADERS install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-nobase_includeHEADERS
+
+
+#
+# automake adds dependencies that we don't like, so we explicitly remove them
+#
+Makefile:
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/README b/README
new file mode 100644
index 0000000..5921344
--- /dev/null
+++ b/README
@@ -0,0 +1,655 @@
+ Internet Systems Consortium DHCP Distribution
+ Version 4.2.2
+ 27 July 2011
+
+ README FILE
+
+You should read this file carefully before trying to install or use
+the ISC DHCP Distribution.
+
+ TABLE OF CONTENTS
+
+ 1 WHERE TO FIND DOCUMENTATION
+ 2 RELEASE STATUS
+ 3 BUILDING THE DHCP DISTRIBUTION
+ 3.1 UNPACKING IT
+ 3.2 CONFIGURING IT
+ 3.2.1 DYNAMIC DNS UPDATES
+ 3.2.2 LOCALLY DEFINED OPTIONS
+ 3.3 BUILDING IT
+ 4 INSTALLING THE DHCP DISTRIBUTION
+ 5 USING THE DHCP DISTRIBUTION
+ 5.1 FIREWALL RULES
+ 5.2 LINUX
+ 5.2.1 IF_TR.H NOT FOUND
+ 5.2.2 SO_ATTACH_FILTER UNDECLARED
+ 5.2.3 PROTOCOL NOT CONFIGURED
+ 5.2.4 BROADCAST
+ 5.2.6 IP BOOTP AGENT
+ 5.2.7 MULTIPLE INTERFACES
+ 5.3 SCO
+ 5.4 HP-UX
+ 5.5 ULTRIX
+ 5.6 FreeBSD
+ 5.7 NeXTSTEP
+ 5.8 SOLARIS
+ 5.8.1 Solaris 11
+ 5.8.2 Other Solaris Items
+ 5.9 AIX
+ 5.10 MacOS X
+ 6 SUPPORT
+ 6.1 HOW TO REPORT BUGS
+
+ WHERE TO FIND DOCUMENTATION
+
+Documentation for this software includes this README file, the
+RELNOTES file, and the manual pages, which are in the server, common,
+client and relay subdirectories. The README file (this file) includes
+late-breaking operational and system-specific information that you
+should read even if you don't want to read the manual pages, and that
+you should *certainly* read if you run into trouble. Internet
+standards relating to the DHCP protocol are listed in the References
+document that is available in html, txt and xml formats in doc/
+subdirectory. You will have the best luck reading the manual pages if
+you build this software and then install it, although you can read
+them directly out of the distribution if you need to.
+
+DHCP server documentation is in the dhcpd man page. Information about
+the DHCP server lease database is in the dhcpd.leases man page.
+Server configuration documentation is in the dhcpd.conf man page as
+well as the dhcp-options man page. A sample DHCP server
+configuration is in the file server/dhcpd.conf. The source for the
+dhcpd, dhcpd.leases and dhcpd.conf man pages is in the server/ sub-
+directory in the distribution. The source for the dhcp-options.5
+man page is in the common/ subdirectory.
+
+DHCP Client documentation is in the dhclient man page. DHCP client
+configuration documentation is in the dhclient.conf man page and the
+dhcp-options man page. The DHCP client configuration script is
+documented in the dhclient-script man page. The format of the DHCP
+client lease database is documented in the dhclient.leases man page.
+The source for all these man pages is in the client/ subdirectory in
+the distribution. In addition, the dhcp-options man page should be
+referred to for information about DHCP options.
+
+DHCP relay agent documentation is in the dhcrelay man page, the source
+for which is distributed in the relay/ subdirectory.
+
+To read installed manual pages, use the man command. Type "man page"
+where page is the name of the manual page. This will only work if
+you have installed the ISC DHCP distribution using the ``make install''
+command (described later).
+
+If you want to read manual pages that aren't installed, you can type
+``nroff -man page |more'' where page is the filename of the
+unformatted manual page. The filename of an unformatted manual page
+is the name of the manual page, followed by '.', followed by some
+number - 5 for documentation about files, and 8 for documentation
+about programs. For example, to read the dhcp-options man page,
+you would type ``nroff -man common/dhcp-options.5 |more'', assuming
+your current working directory is the top level directory of the ISC
+DHCP Distribution.
+
+Please note that the pathnames of files to which our manpages refer
+will not be correct for your operating system until after you iterate
+'make install' (so if you're reading a manpage out of the source
+directory, it may not have up-to-date information).
+
+ RELEASE STATUS
+
+This is ISC DHCP 4.2.2, a maintenance release containing patches.
+
+In this release, the DHCPv6 server should be fully functional on Linux,
+Solaris, or any BSD. The DHCPv6 client should be similarly functional
+except on Solaris.
+
+The DHCPv4 server, relay, and client, should be fully functional
+on Linux, Solaris, any BSD, HPUX, SCO, NextSTEP, and Irix.
+
+If you are running the DHCP distribution on a machine which is a
+firewall, or if there is a firewall between your DHCP server(s) and
+DHCP clients, please read the section on firewalls which appears later
+in this document.
+
+If you wish to run the DHCP Distribution on Linux, please see the
+Linux-specific notes later in this document. If you wish to run on an
+SCO release, please see the SCO-specific notes later in this document.
+You particularly need to read these notes if you intend to support
+Windows 95 clients. If you are running HP-UX or Ultrix, please read the
+notes for those operating systems below. If you are running NeXTSTEP,
+please see the notes on NeXTSTEP below.
+
+If you start dhcpd and get a message, "no free bpf", that means you
+need to configure the Berkeley Packet Filter into your operating
+system kernel. On NetBSD, FreeBSD and BSD/os, type ``man bpf'' for
+information. On Digital Unix, type ``man pfilt''.
+
+
+ BUILDING THE DHCP DISTRIBUTION
+
+ UNPACKING IT
+
+To build the DHCP Distribution, unpack the compressed tar file using
+the tar utility and the gzip command - type something like:
+
+ gunzip dhcp-4.2.2.tar.gz
+ tar xvf dhcp-4.2.2.tar
+
+ CONFIGURING IT
+
+Now, cd to the dhcp-4.2.2 subdirectory that you've just created and
+configure the source tree by typing:
+
+ ./configure
+
+If the configure utility can figure out what sort of system you're
+running on, it will create a custom Makefile for you for that
+system; otherwise, it will complain. If it can't figure out what
+system you are using, that system is not supported - you are on
+your own.
+
+ DYNAMIC DNS UPDATES
+
+A fully-featured implementation of dynamic DNS updates is included in
+this release. It uses libraries from BIND and, to avoid issues with
+different versions, includes the necessary BIND version. The appropriate
+BIND libraries will be compiled and installed in the bind subdirectory
+as part of the make step. In order to build the necessary libraries you
+will need to have "gmake" available on your build system.
+
+
+There is documentation for the DDNS support in the dhcpd.conf manual
+page - see the beginning of this document for information on finding
+manual pages.
+
+ LOCALLY DEFINED OPTIONS
+
+In previous versions of the DHCP server there was a mechanism whereby
+options that were not known by the server could be configured using
+a name made up of the option code number and an identifier:
+"option-nnn" This is no longer supported, because it is not future-
+proof. Instead, if you want to use an option that the server doesn't
+know about, you must explicitly define it using the method described
+in the dhcp-options man page under the DEFINING NEW OPTIONS heading.
+
+ BUILDING IT
+
+Once you've run configure, just type ``make'', and after a while
+you should have a dhcp server. If you get compile errors on one
+of the supported systems mentioned earlier, please let us know.
+If you get warnings, it's not likely to be a problem - the DHCP
+server compiles completely warning-free on as many architectures
+as we can manage, but there are a few for which this is difficult.
+If you get errors on a system not mentioned above, you will need
+to do some programming or debugging on your own to get the DHCP
+Distribution working.
+
+ INSTALLING THE DHCP DISTRIBUTION
+
+Once you have successfully gotten the DHCP Distribution to build, you
+can install it by typing ``make install''. If you already have an old
+version of the DHCP Distribution installed, you may want to save it
+before typing ``make install''.
+
+ USING THE DHCP DISTRIBUTION
+
+ FIREWALL RULES
+
+If you are running the DHCP server or client on a computer that's also
+acting as a firewall, you must be sure to allow DHCP packets through
+the firewall. In particular, your firewall rules _must_ allow packets
+from IP address 0.0.0.0 to IP address 255.255.255.255 from UDP port 68
+to UDP port 67 through. They must also allow packets from your local
+firewall's IP address and UDP port 67 through to any address your DHCP
+server might serve on UDP port 68. Finally, packets from relay agents
+on port 67 to the DHCP server on port 67, and vice versa, must be
+permitted.
+
+We have noticed that on some systems where we are using a packet
+filter, if you set up a firewall that blocks UDP port 67 and 68
+entirely, packets sent through the packet filter will not be blocked.
+However, unicast packets will be blocked. This can result in strange
+behaviour, particularly on DHCP clients, where the initial packet
+exchange is broadcast, but renewals are unicast - the client will
+appear to be unable to renew until it starts broadcasting its
+renewals, and then suddenly it'll work. The fix is to fix the
+firewall rules as described above.
+
+ PARTIAL SERVERS
+
+If you have a server that is connected to two networks, and you only
+want to provide DHCP service on one of those networks (e.g., you are
+using a cable modem and have set up a NAT router), if you don't write
+any subnet declaration for the network you aren't supporting, the DHCP
+server will ignore input on that network interface if it can. If it
+can't, it will refuse to run - some operating systems do not have the
+capability of supporting DHCP on machines with more than one
+interface, and ironically this is the case even if you don't want to
+provide DHCP service on one of those interfaces.
+
+ LINUX
+
+There are three big LINUX issues: the all-ones broadcast address,
+Linux 2.1 ip_bootp_agent enabling, and operations with more than one
+network interface. There are also two potential compilation/runtime
+problems for Linux 2.1/2.2: the "SO_ATTACH_FILTER undeclared" problem
+and the "protocol not configured" problem.
+
+ LINUX: PROTOCOL NOT CONFIGURED
+
+If you get the following message, it's because your kernel doesn't
+have the linux packetfilter or raw packet socket configured:
+
+ Make sure CONFIG_PACKET (Packet socket) and CONFIG_FILTER (Socket
+ Filtering) are enabled in your kernel configuration
+
+If this happens, you need to configure your Linux kernel to support
+Socket Filtering and the Packet socket, or to select a kernel provided
+by your Linux distribution that has these enabled (virtually all modern
+ones do by default).
+
+ LINUX: BROADCAST
+
+If you are running a recent version of Linux, this won't be a problem,
+but on older versions of Linux (kernel versions prior to 2.2), there
+is a potential problem with the broadcast address being sent
+incorrectly.
+
+In order for dhcpd to work correctly with picky DHCP clients (e.g.,
+Windows 95), it must be able to send packets with an IP destination
+address of 255.255.255.255. Unfortunately, Linux changes an IP
+destination of 255.255.255.255 into the local subnet broadcast address
+(here, that's 192.5.5.223).
+
+This isn't generally a problem on Linux 2.2 and later kernels, since
+we completely bypass the Linux IP stack, but on old versions of Linux
+2.1 and all versions of Linux prior to 2.1, it is a problem - pickier
+DHCP clients connected to the same network as the ISC DHCP server or
+ISC relay agent will not see messages from the DHCP server. It *is*
+possible to run into trouble with this on Linux 2.2 and later if you
+are running a verson of the DHCP server that was compiled on a Linux
+2.0 system, though.
+
+It is possible to work around this problem on some versions of Linux
+by creating a host route from your network interface address to
+255.255.255.255. The command you need to use to do this on Linux
+varies from version to version. The easiest version is:
+
+ route add -host 255.255.255.255 dev eth0
+
+On some older Linux systems, you will get an error if you try to do
+this. On those systems, try adding the following entry to your
+/etc/hosts file:
+
+255.255.255.255 all-ones
+
+Then, try:
+
+ route add -host all-ones dev eth0
+
+Another route that has worked for some users is:
+
+ route add -net 255.255.255.0 dev eth0
+
+If you are not using eth0 as your network interface, you should
+specify the network interface you *are* using in your route command.
+
+ LINUX: IP BOOTP AGENT
+
+Some versions of the Linux 2.1 kernel apparently prevent dhcpd from
+working unless you enable it by doing the following:
+
+ echo 1 >/proc/sys/net/ipv4/ip_bootp_agent
+
+
+ LINUX: MULTIPLE INTERFACES
+
+Very old versions of the Linux kernel do not provide a networking API
+that allows dhcpd to operate correctly if the system has more than one
+broadcast network interface. However, Linux 2.0 kernels with version
+numbers greater than or equal to 2.0.31 add an API feature: the
+SO_BINDTODEVICE socket option. If SO_BINDTODEVICE is present, it is
+possible for dhcpd to operate on Linux with more than one network
+interface. In order to take advantage of this, you must be running a
+2.0.31 or greater kernel, and you must have 2.0.31 or later system
+headers installed *before* you build the DHCP Distribution.
+
+We have heard reports that you must still add routes to 255.255.255.255
+in order for the all-ones broadcast to work, even on 2.0.31 kernels.
+In fact, you now need to add a route for each interface. Hopefully
+the Linux kernel gurus will get this straight eventually.
+
+Linux 2.1 and later kernels do not use SO_BINDTODEVICE or require the
+broadcast address hack, but do support multiple interfaces, using the
+Linux Packet Filter.
+
+ LINUX: OpenWrt
+
+DHCP 4.1 has been tested on OpenWrt 7.09 and 8.09. In keeping with
+standard practice, client/scripts now includes a dhclient-script file
+for OpenWrt. However, this is not sufficient by itself to run dhcp on
+OpenWrt; a full OpenWrt package for DHCP is available at
+ftp://ftp.isc.org/isc/dhcp/dhcp-4.1.0-openwrt.tar.gz
+
+ LINUX: 802.1q VLAN INTERFACES
+
+If you're using 802.1q vlan interfaces on Linux, it is necessary to
+vconfig the subinterface(s) to rewrite the 802.1q information out of
+packets received by the dhcpd daemon via LPF:
+
+ vconfig set_flag eth1.523 1 1
+
+Note that this may affect the performance of your system, since the
+Linux kernel must rewrite packets received via this interface. For
+more information, consult the vconfig man pages.
+
+ SCO
+
+ISC DHCP will now work correctly on newer versions of SCO out of the
+box (tested on OpenServer 5.05b, assumed to work on UnixWare 7).
+
+Older versions of SCO have the same problem as Linux (described earlier).
+The thing is, SCO *really* doesn't want to let you add a host route to
+the all-ones broadcast address.
+
+You can try the following:
+
+ ifconfig net0 xxx.xxx.xxx.xxx netmask 0xNNNNNNNN broadcast 255.255.255.255
+
+If this doesn't work, you can also try the following strange hack:
+
+ ifconfig net0 alias 10.1.1.1 netmask 8.0.0.0
+
+Apparently this works because of an interaction between SCO's support
+for network classes and the weird netmask. The 10.* network is just a
+dummy that can generally be assumed to be safe. Don't ask why this
+works. Just try it. If it works for you, great.
+
+ HP-UX
+
+HP-UX has the same problem with the all-ones broadcast address that
+SCO and Linux have. One user reported that adding the following to
+/etc/rc.config.d/netconf helped (you may have to modify this to suit
+your local configuration):
+
+INTERFACE_NAME[0]=lan0
+IP_ADDRESS[0]=1.1.1.1
+SUBNET_MASK[0]=255.255.255.0
+BROADCAST_ADDRESS[0]="255.255.255.255"
+LANCONFIG_ARGS[0]="ether"
+DHCP_ENABLE[0]=0
+
+ ULTRIX
+
+Now that we have Ultrix packet filter support, the DHCP Distribution
+on Ultrix should be pretty trouble-free. However, one thing you do
+need to be aware of is that it now requires that the pfilt device be
+configured into your kernel and present in /dev. If you type ``man
+packetfilter'', you will get some information on how to configure your
+kernel for the packet filter (if it isn't already) and how to make an
+entry for it in /dev.
+
+ FreeBSD
+
+Versions of FreeBSD prior to 2.2 have a bug in BPF support in that the
+ethernet driver swaps the ethertype field in the ethernet header
+downstream from BPF, which corrupts the output packet. If you are
+running a version of FreeBSD prior to 2.2, and you find that dhcpd
+can't communicate with its clients, you should #define BROKEN_FREEBSD_BPF
+in site.h and recompile.
+
+Modern versions of FreeBSD include the ISC DHCP 3.0 client as part of
+the base system, and the full distribution (for the DHCP server and
+relay agent) is available from the Ports Collection in
+/usr/ports/net/isc-dhcp3, or as a package on FreeBSD installation
+CDROMs.
+
+ NeXTSTEP
+
+The NeXTSTEP support uses the NeXTSTEP Berkeley Packet Filter
+extension, which is not included in the base NextStep system. You
+must install this extension in order to get dhcpd or dhclient to work.
+
+ SOLARIS
+
+There are two known issues seen when compiling using the Sun compiler.
+
+The first is that older Sun compilers generate an error on some of
+our uses of the flexible array option. Newer versions only generate
+a warning, which can be safely ignored. If you run into this error
+("type of struct member "buf" can not be derived from structure with
+flexible array member"), upgrade your tools to Oracle Solaris Studio
+(previously Sun Studio) 12 or something newer.
+
+The second is the interaction between the configure script and the
+makefiles for the Bind libraries. Currently we don't pass all
+environment variables between the DHCP configure and the Bind configure.
+
+If you attempt to specify the compiler you wish to use like this:
+
+ CC=/opt/SUNWspro/bin/cc ./configure
+
+"make" may not build the Bind libraries with that compiler.
+
+In order to use the same compiler for Bind and DHCP we suggest the
+following commands:
+
+ CC=/opt/SUNWspro/bin/cc ./configure
+ CC=/opt/SUNWspro/bin/cc make
+
+ Solaris 11
+
+We have integrated a patch from Oracle to use sockets instead of
+DLPI on Solaris 11. This functionality was written for use with
+Solaris Studio 12.2 and requires the system/header package.
+
+By default this code is disabled in order to minimize disruptions
+for current users. In order to enable this code you will need to
+enable both USE_SOCKETS and USE_V4_PKTINFO as part of the
+configuration step. The command line would be something like:
+
+ ./configure --enable-use-sockets --enable-ipv4-pktinfo
+
+ Other Solaris Items
+
+One problem which has been observed and is not fixed in this
+patchlevel has to do with using DLPI on Solaris machines. The symptom
+of this problem is that the DHCP server never receives any requests.
+This has been observed with Solaris 2.6 and Solaris 7 on Intel x86
+systems, although it may occur with other systems as well. If you
+encounter this symptom, and you are running the DHCP server on a
+machine with a single broadcast network interface, you may wish to
+edit the includes/site.h file and uncomment the #define USE_SOCKETS
+line. Then type ``make clean; make''. As an alternative workaround,
+it has been reported that running 'snoop' will cause the dhcp server
+to start receiving packets. So the practice reported to us is to run
+snoop at dhcpd startup time, with arguments to cause it to receive one
+packet and exit.
+
+ snoop -c 1 udp port 67 > /dev/null &
+
+The DHCP client on Solaris will only work with DLPI. If you run it
+and it just keeps saying it's sending DHCPREQUEST packets, but never
+gets a response, you may be having DLPI trouble as described above.
+If so, we have no solution to offer at this time, aside from the above
+workaround which should also work here. Also, because Solaris requires
+you to "plumb" an interface before it can be detected by the DHCP client,
+you must either specify the name(s) of the interface(s) you want to
+configure on the command line, or must plumb the interfaces prior to
+invoking the DHCP client. This can be done with ``ifconfig iface plumb'',
+where iface is the name of the interface (e.g., ``ifconfig hme0 plumb'').
+
+It should be noted that Solaris versions from 2.6 onward include a
+DHCP client that you can run with ``/sbin/ifconfig iface dhcp start''
+rather than using the ISC DHCP client, including DHCPv6. Consequently,
+we don't believe there is a need for the client to run on Solaris, and
+have not engineered the needed DHCPv6 modifications for the dhclient-script.
+If you feel this is in error, or have a need, please contact us.
+
+ AIX
+
+The AIX support uses the BSD socket API, which cannot differentiate on
+which network interface a broadcast packet was received; thus the DHCP
+server and relay will work only on a single interface. (They do work
+on multi-interface machines if configured to listen on only one of the
+interfaces.)
+
+We have reports of Windows XP clients having difficutly retrieving
+addresses from a server running on an AIX machine. This issue
+was traced to the client requiring messages be sent to the all ones
+broadcast address (255.255.255.255) while the AIX server was sending
+to 192.168.0.255.
+
+You may be able to solve this by including a relay between the client
+and server with the relay configured to use a broadcast of all-ones.
+
+A second option that worked for AIX 5.1 but doesn't seem to work for
+AIX 5.3 was to:
+ create a host file entry for all-ones (255.255.255.255)
+and then add a route:
+ route add -host all-ones -interface <local-ip-address>
+
+The ISC DHCP distribution does not include a dhclient-script for AIX--
+AIX comes with a DHCP client. Contribution of a working dhclient-script
+for AIX would be welcome.
+
+
+ MacOS X
+
+The MacOS X system uses a TCP/IP stack derived from FreeBSD with a
+user-friendly interface named the System Configuration Framework.
+As it includes a builtin DHCPv4 client (you are better just using that),
+this text is only about the DHCPv6 client (``dhclient -6 ...''). The DNS
+configuration (domain search list and name servers' addresses) is managed
+by a System Configuration agent, not by /etc/resolv.conf (which is a link
+to /var/run/resolv.conf, which itself only reflects the internal state;
+the System Configuration agent's Dynamic Store).
+
+This means that modifying resolv.conf directly doesn't have the intended
+effect, so the macos script sample uses its own resolv.conf.dhclient6 in
+/var/run, and inserts the contents of this file into the System
+Configuration agent. Because the System Configuration agent expects the
+prefix along with the configured address, and a default router, this is
+not usable (the DHCPv6 protocol does not today deliver this information).
+Instead, ifconfig is directly used for address configuration.
+
+Note the Dynamic Store (from which /var/run/resolv.conf is built) is
+recomputed from scratch when the current location/set is changed, for
+instance when a laptop is resumed from sleep. In this case running the
+dhclient-script could reinstall the resolv.conf.dhclient6 configuration.
+
+ SUPPORT
+
+The Internet Systems Consortium DHCP server is developed and distributed
+by ISC in the public trust, thanks to the generous donations of its
+sponsors. ISC now also offers commercial quality support contracts for
+ISC DHCP, more information about ISC Support Contracts can be found at
+the following URL:
+
+ https://www.isc.org/services/support/
+
+Please understand that we may not respond to support inquiries unless
+you have a support contract. ISC will continue its practice of always
+responding to critical items that effect the entire community, and
+responding to all other requests for support upon ISC's mailing lists
+on a best-effort basis.
+
+However, ISC DHCP has attracted a fairly sizable following on the
+Internet, which means that there are a lot of knowledgeable users who
+may be able to help you if you get stuck. These people generally
+read the dhcp-users@isc.org mailing list. Be sure to provide as much
+detail in your query as possible.
+
+If you are going to use ISC DHCP, you should probably subscribe to
+the dhcp-users or dhcp-announce mailing lists.
+
+WHERE TO SEND FEATURE REQUESTS: We like to hear your feedback. We may
+not respond to it all the time, but we do read it. If ISC DHCP doesn't
+work well for you, or you have an idea that would improve it for your
+use, please send your suggestion to dhcp-suggest@isc.org. This is also
+an excellent place to send patches that add new features.
+
+WHERE TO REPORT BUGS: If you want the act of sending in a bug report
+to result in you getting help in the form of a fixed piece of
+software, you are asking for help. Your bug report is helpful to us,
+but fundamentally you are making a support request, so please use the
+addresses described in the previous paragraphs. If you are _sure_ that
+your problem is a bug, and not user error, or if your bug report
+includes a patch, you can send it to our ticketing system at
+dhcp-bugs@isc.org. If you have not received a notice that the ticket
+has been resolved, then we're still working on it.
+
+PLEASE DO NOT REPORT BUGS IN OLD SOFTWARE RELEASES! Fetch the latest
+release and see if the bug is still in that version of the software,
+and if it is still present, _then_ report it. ISC release versions
+always have three numbers, for example: 1.2.3. The 'major release' is
+1 here, the 'minor release' is 2, and the 'maintenance release' is 3.
+ISC will accept bug reports against the most recent two major.minor
+releases: for example, 1.0.0 and 0.9.0, but not 0.8.* or prior.
+
+PLEASE take a moment to determine where the ISC DHCP distribution
+that you're using came from. ISC DHCP is sometimes heavily modified
+by integrators in various operating systems - it's not that we
+feel that our software is perfect and incapable of having bugs, but
+rather that it is very frustrating to find out after many days trying
+to help someone that the sources you're looking at aren't what they're
+running. When in doubt, please retrieve the source distribution from
+ISC's web page and install it.
+
+ HOW TO REPORT BUGS OR REQUEST HELP
+
+When you report bugs or ask for help, please provide us complete
+information. A list of information we need follows. Please read it
+carefully, and put all the information you can into your initial bug
+report. This will save us a great deal of time and more informative
+bug reports are more likely to get handled more quickly overall.
+
+ 1. The specific operating system name and version of the
+ machine on which the DHCP server or client is running.
+ 2. The specific operating system name and version of the
+ machine on which the client is running, if you are having
+ trouble getting a client working with the server.
+ 3. If you're running Linux, the version number we care about is
+ the kernel version and maybe the library version, not the
+ distribution version - e.g., while we don't mind knowing
+ that you're running Redhat version mumble.foo, we must know
+ what kernel version you're running, and it helps if you can
+ tell us what version of the C library you're running,
+ although if you don't know that off the top of your head it
+ may be hard for you to figure it out, so don't go crazy
+ trying.
+ 4. The specific version of the DHCP distribution you're
+ running, as reported by dhcpd -t.
+ 5. Please explain the problem carefully, thinking through what
+ you're saying to ensure that you don't assume we know
+ something about your situation that we don't know.
+ 6. Include your dhcpd.conf and dhcpd.leases file as MIME attachments
+ if they're not over 100 kilobytes in size each. If they are
+ this large, please make them available to us eg via a hidden
+ http:// URL or FTP site. If you're not comfortable releasing
+ this information due to sensitive contents, you may encrypt
+ the file to our release signing key, available on our website.
+ 7. Include a log of your server or client running until it
+ encounters the problem - for example, if you are having
+ trouble getting some client to get an address, restart the
+ server with the -d flag and then restart the client, and
+ send us what the server prints. Likewise, with the client,
+ include the output of the client as it fails to get an
+ address or otherwise does the wrong thing. Do not leave
+ out parts of the output that you think aren't interesting.
+ 8. If the client or server is dumping core, please run the
+ debugger and get a stack trace, and include that in your
+ bug report. For example, if your debugger is gdb, do the
+ following:
+
+ gdb dhcpd dhcpd.core
+ (gdb) where
+ [...]
+ (gdb) quit
+
+ This assumes that it's the dhcp server you're debugging, and
+ that the core file is in dhcpd.core.
+
+Please see https://www.isc.org/software/dhcp/ for details on how to subscribe
+to the ISC DHCP mailing lists.
+
diff --git a/RELNOTES b/RELNOTES
new file mode 100644
index 0000000..d534397
--- /dev/null
+++ b/RELNOTES
@@ -0,0 +1,3237 @@
+ Internet Systems Consortium DHCP Distribution
+ Version 4.2.2
+ 27 July 2011
+
+ Release Notes
+
+ NEW FEATURES
+
+ISC DHCP 4.2.x includes features that were not included in DHCP 4.1.x.
+These include:
+
+Processing the DHCP to DNS server transactions in an asynchronous fashion.
+The DHCP server or client can now continue with it's processing while
+awaiting replies from the DNS server.
+
+There are a number of DHCPv6 limitations and features missing in this
+release, which will be addressed in the future:
+
+- Only Solaris, Linux, FreeBSD, NetBSD, and OpenBSD are supported.
+
+- DHCPv6 includes human-readable text in status code messages, in
+ English. A method to reconfigure or support other languages would
+ be preferable.
+
+- The "host-identifier" option is limited to a simple token.
+
+- The client and server can only operate DHCPv4 or DHCPv6 at a time,
+ not both. To use both protocols simultaneously, two instances of the
+ relevant daemon are required, one with the '-6' command line option.
+
+For information on how to install, configure and run this software, as
+well as how to find documentation and report bugs, please consult the
+README file.
+
+ISC DHCP uses standard GNU configure for installation. Please review the
+output of "./configure --help" to see what options are available.
+
+The system has only been tested on Linux, FreeBSD, and Solaris, and may not
+work on other platforms. Please report any problems and suggested fixes to
+<dhcp-users@isc.org>.
+
+ Changes since 4.2.2rc1
+
+! Two packets were found that cause a server to halt. The code
+ has been updated to properly process or reject the packets as
+ appropriate. Thanks to David Zych at University of Illinois
+ for reporting this issue. [ISC-Bugs #24960]
+ One CVE number for each class of packet.
+ CVE-2011-2748
+ CVE-2011-2749
+
+ Changes since 4.2.2b1
+
+- Strict checks for content of domain-name DHCPv4 option can now be
+ configured during compilation time. Even though RFC2132 does not allow
+ to store more than one domain in domain-name option, such behavior is
+ now enabled by default, but this may change some time in the future.
+ See ACCEPT_LIST_IN_DOMAIN_NAME define in includes/site.h.
+ [ISC-Bugs #24167]
+
+- DNS Update fix. A misconfigured server could crash during DNS update
+ processing if the configuration included overlapping pools or
+ multiple fixed-address entries for a single address. This issue
+ affected both IPv4 and IPv6. The fix allows a server to detect such
+ conditions, provides the user with extra information and recommended
+ steps to fix the problem. If the user enables the appropriate option
+ in site.h then server will be terminated
+ [ISC-Bugs #23595]
+
+ Changes since 4.2.1
+
+! In dhclient check the data for some string options for
+ reasonableness before passing it along to the script that
+ interfaces with the OS.
+ [ISC-Bugs #23722]
+ CVE: CVE-2011-0997
+
+- DHCPv6 server now responds properly if client asks for a prefix that
+ is already assigned to a different client. [ISC-Bugs #23948]
+
+- Add the option "--no-pid" to the client, relay and server code,
+ to disable writing a pid file. Add the option "-pf pidfile"
+ to the relay to allow the user to supply the pidfile name at
+ runtime. Add the "with-relay6-pid-file" option to configure
+ to allow the user to supply the pidfile name for the relay
+ in v6 mode at configure time.
+ [ISC-Bugs #23351] [ISC-Bugs #17541]
+
+- 'dhclient' no longer waits a random interval after first starting up to
+ begin in the INIT state. This conforms to RFC 2131, but elects not to
+ implement a 'SHOULD' direction in section 4.1. [ISC-Bugs #19660]
+
+- Added 'initial-delay' parameter that specifies maximum amount of time
+ before client goes to the INIT state. The default value is 0. In previous
+ versions of the code client could wait up to 5 seconds. The old behavior
+ may be restored by using 'initial-delay 5;' in the client config file.
+ [ISC-Bugs #19660]
+
+- ICMP ping-check should now sit closer to precisely the number of seconds
+ configured (or default 1), due to making use of the new microsecond
+ scale timer internally to dhcpd. This corrects a bug where the server
+ may immediately timeout an ICMP ping-check if it was made late in the
+ current second. [ISC-Bugs #19660]
+
+- The DHCP client will schedule renewal and rebinding events in
+ microseconds if the DHCP server provided a lease-time that would result
+ in sub-1-second timers. This corrects a bug where a 2-second or lower
+ lease-time would cause the DHCP client to enter an infinite loop by
+ scheduling renewal at zero seconds. [ISC-Bugs #19660]
+
+- Client lease records are recorded at most once every 15 seconds. This
+ keeps the client from filling the lease database disk quickly on very small
+ lease times. [ISC-Bugs #19660]
+
+- To defend against RFC 2131 non-compliant DHCP servers which fail to
+ advertise a lease-time (either mangled, or zero in value) the DHCP
+ client now adds the server to the reject list ACL and returns to INIT
+ state to hopefully find an RFC 2131 compliant server (or retry in INIT
+ forever). [ISC-Bugs #19660]
+
+- Parameters configured to evaluate from user defined function calls can
+ now be correctly written to dhcpd.leases (as on 'on events' or dynamic
+ host records inserted via OMAPI). [ISC-Bugs #22266]
+
+- If a 'next-server' parameter is configured in a dynamic host record via
+ OMAPI as a domain name, the syntax written to disk is now correctly parsed
+ upon restart. [ISC-Bugs #22266]
+
+- The DHCP server now responds to DHCPLEASEQUERY messages from agents using
+ IP addresses not covered by a subnet in configuration. Whether or not to
+ respond to such an agent is still governed by the 'allow leasequery;'
+ configuration parameter, in the case of an agent not covered by a configured
+ subnet the root configuration area is examined. Server now also returns
+ vendor-class-id option, if client sent it. [ISC-Bugs #21094]
+
+- Documentation fixes
+ [ISC-Bugs #17959] add text to AIX section describing how to have it send
+ responses to the all-ones address.
+ [ISC-Bugs #19615] update the includes in dhcpctl/dhcpctl.3 to be more correct
+ [ISC-Bugs #20676] update dhcpd.conf.5 to include the RFC numbers for DDNS
+
+- Linux Packet Filter interface improvement. sockaddr_pkt structure is used,
+ rather than sockaddr. Packet etherType is now forced to ETH_P_IP.
+ [ISC-Bugs #18975]
+
+- Minor code cleanups - but note port change for #23196
+ [ISC-Bugs #23470] - Modify when an ignore return macro is defined to
+ handle unsed error return warnings for more versions of gcc.
+ [ISC-Bugs #23196] - Modify the reply handling in the server code to
+ send to a specified port rather than to the source port for the incoming
+ message. Sending to the source port was test code that should have
+ been removed. The previous functionality may be restored by defining
+ REPLY_TO_SOURCE_PORT in the includes/site.h file. We suggest you don't
+ enable this except for testing purposes.
+ [ISC-Bugs #22695] - Close a file descriptor in an error path.
+ [ISC-Bugs #19368] - Tidy up variable types in validate_port.
+
+- Code cleanup
+ [ISC-Bugs #13151] remove obsolete PROTO, KandR, INLINE and ANSI_DECL macros
+
+- Compilation problem with gcc4.5 and omshell.c resolved. [ISC-Bugs #23831]
+
+- Client Script fixes
+ [ISC-Bugs #23045] Typos in client/scripts/openbsd
+ [ISC-Bugs #23565] In the client scripts add a zone id (interface id) if
+ the domain search address is link local.
+ [ISC-Bugs #1277] In some of the client scripts add code to handle the
+ case of the default router information being changed without the address
+ being changed.
+
+- Documentation cleanup
+ [ISC-Bugs #23326] Updated References document, several man page updates
+
+- Server no longer complains about NULL pointer when configured
+ server-identifier expression fails to evaluate. [ISC-Bugs #24547]
+
+- Convert ISC_R_INPROGRESS status to ISC_R_SUCCESS when called from other
+ than the dispatch handler. This fixes an issue where omshell, when
+ run from the same platform as the server, would appear to fail to
+ connect. This is a companion to #21839. [ISC-Bugs #23592]
+
+- Enlarge the buffer size used by the Omshell code and some of the
+ print routines to allow for greater than 60 characters or, when
+ printing as hex strings, 20 characters. [ISC-Bugs #22743]
+
+- In Solaris 11 switch to using sockets instead of DLPI, thanks
+ to a patch form Oracle. [ISC-Bugs #24634].
+
+ Changes since 4.2.1rc1
+
+- None
+
+ Changes since 4.2.1b1
+
+- Removed the restriction on using IPv6 addresses in IPv4 mode. This
+ allows IPv4 options which contain IPv6 addresses to be specified. For
+ example the 6rd option can be specified and used like this:
+ [ISC-Bugs #23039]
+
+ option 6rd code 212 = { integer 8, integer 8,
+ ip6-address, array of ip-address };
+ option 6rd 16 10 2001:: 1.2.3.4, 5.6.7.8;
+
+- Handle some DDNS corner cases better. Maintain the DDNS transaction
+ information when updating a lease and cancel any existing transactions
+ when removing the ddns information.
+ [ISC-Bugs #23103]
+
+- Some fixes for LDAP
+ [ISC-Bugs #21783] - Include lber library when building ldap
+ [ISC-Bugs #22888] - Enable the ldap code when buidling common
+ The above fixes are from Jiri Popelka at Red Hat.
+
+- Modify the dlpi code to accept getmsg() returning a positive value.
+ [ISC-Bugs #22824]
+
+ Changes since 4.2.0
+
+- 'get-host-names true;' now also works even if 'use-host-decl-names true;'
+ was also configured. The nature of this repair also fixes another
+ error; the host-name supplied by a client is no longer overridden by a
+ reverse lookup of the lease address. Thanks to a patch from Wilco Baan
+ Hofman supplied to us by the Debian package maintenance team.
+ [ISC-Bugs #21691] {Debian Bug#509445}
+
+- The .TH tag for the dhcp-options manpage was typo repaired
+ thanks to a report from jidanni and the Debian package maintenance
+ team. [ISC-Bugs #21676] {Debian Bug#563613}
+
+- More documentation changes - primarily to put the options in the dhclient
+ and dhcpd man pages into the standard form. Thanks in part to a patch
+ from David Cantrell at Red Hat.
+ [ISC-Bugs #20264] and parts of [ISC-Bugs #17744] dhclient.8 changes
+
+- Add code to clear the pointer to an object in an OMAPI handle when the
+ object is freed due to a dereference. [ISC-Bugs #21306]
+
+- Fixed a bug that leaks host record references onto lease structures,
+ causing the server to apply configuration intended for one host to any
+ other innocent clients that come along later. [ISC-Bugs #22018]
+
+- Minor code fixes
+ [ISC-Bugs #19566] When trying to find the zone for a name for ddns allow
+ the name to be at the apex of the zone.
+ [ISC-Bugs #19617] Restrict length of interface name read from command line
+ in dhcpd - based on a patch from David Cantrell at Red Hat.
+ [ISC-Bugs #20039] Correct some error messages in dhcpd.c
+ [ISC-Bugs #20070] Better range check on values when creating a DHCID.
+ [ISC-Bugs #20198] Avoid writing past the end of the field when adding
+ overly long file or server names to a packet and add a log message
+ if the configuration supplied overly long names for these fields.
+ Thanks to Martin Pala.
+ [ISC-Bugs #21497] Add a little more randomness to rng seed in client
+ thanks to a patch from Jeremiah Jinno.
+
+- Correct error handling in DLPI [ISC-Bugs #20378]
+
+- Remove __sun__ and __hpux__ typedefs in osdep.h as they are now being
+ checked in configure. [ISC-Bugs #20443]
+
+- Modify how the cmsg header is allocated the v6 send and received routines
+ to compile on more compilers. [ISC-Bugs #20524]
+
+- When parsing a domain name free the memory for the name after we are
+ done with it. [ISC-Bugs #20824]
+
+- Add an elapsed time option to the release message and refactor the
+ code to move most of the common code to a single routine.
+ [ISC-Bugs #21171].
+
+- Parse date strings more properly - the code now handles semi-colons in
+ date strings correctly. Thanks to a patch from Jiri Popelka at Red Hat.
+ [ISC-Bugs #21501, #20598]
+
+- Fixes to lease input and output.
+ [ISC-Bugs #20418] - Some systems don't support the "%s" argument to
+ strftime, paste together the same string using mktime instead.
+ [ISC-Bugs #19596] - When parsing iaid values accept printable
+ characters.
+ [ISC-Bugs #21585] - Always print time values in omshell as hex
+ instead of ascii if the values happen to be printable characters.
+
+- Minor changes for scripts, configure.ac and Makefiles
+ [ISC-Bugs #19147] Use domain-search instead of domain-name in manual and
+ example conf file. Thanks to a patch from David Cantrell
+ at Red Hat.
+ [ISC-Bugs #19761] Restore address when doing a rebind in DHCPv6
+ [ISC-Bugs #19945] Properly close the quote on some arguments.
+ [ISC-Bugs #20952] Add 64 bit types to configure.ac
+ [ISC-Bugs #21308] Add "PATH=" to CLIENT_PATH envrionment variable
+
+- Update the code to parse dhcpv6 lease files to accept a semi-colon at
+ the end of the max-life and preferred-life clauses. In order to be
+ backwards compatible with older lease files not finding a semi-colon
+ is also accepted. [ISC-Bugs #22303].
+
+! Handle a relay forward message with an unspecified address in the
+ link address field. Previously such a message would cause the
+ server to crash. Thanks to a report from John Gibbons. [ISC-Bugs #21992]
+ CERT: VU#102047 CVE: CVE-2010-3611
+
+- ./configure on longer searches for -lcrypto to explicitly link against.
+ This fixes a bug where 'dhclient' would have shared library dependencies
+ on '/usr/lib'. [ISC-Bugs #21967]
+
+- Handle pipe failures more gracefully. Some OSes pass a SIGPIPE
+ signal to a process and will kill the process if the signal isn't
+ caught. This patch adds code to turn off the SIGPIPE signal via
+ a setsockopt() call. The signal is already being ignored as part
+ of the ISC library. [ISC-Bugs #22269]
+
+- Restore printing of values in omshell to the style pre 21585. For
+ 21585 we changed the print routines to always display time values
+ as a hex list. This had a side effect of printing all data strings
+ as a hex list. We shall investigate other ways of displaying time
+ values more usefully. [ISC-Bugs #22626]
+
+! Fix the handling of connection requests on the failover port.
+ Previously a connection request from a source that wasn't
+ listed as a failover peer would cause the server to become
+ non-responsive. Thanks to a report from Brad Bendily, brad@bendily.com.
+ [ISC-Bugs #22679]
+ CERT: VU#159528 CVE: CVE-2010-3616
+
+- Don't pass the ISC_R_INPROGRESS status to the omapi signal handlers.
+ Passing it through to the handlers caused the omshell program to fail
+ to connect to the server. [ISC-Bugs #21839]
+
+- Fix the paranthesis in the code to process configuration statements
+ beginning with "auth". The previous arrangement caused
+ "auto-partner-down" to be processed incorrectly. [ISC-Bugs #21854]
+
+- Limit the timeout period allowed in the dispatch code to 2^^32-1 seconds.
+ Thanks to a report from Jiri Popelka at Red Hat.
+ [ISC-Bugs #22033], [Red Hat Bug #628258]
+
+- When processing the format flags for a given option consume the
+ flag indicating an optional value correctly. A symptom of this
+ bug was an infinite loop when trying to parse the slp-service-scope
+ option. Thanks to a patch from Marius Tomaschewski.
+ [ISC-Bugs #22055]
+
+- Disable the use of kqueue in the ISC library. This avoids a problem
+ between the fork and socket code that caused the dhcpd process to
+ use all available cpu if the program daemonized itself.
+ [ISC-Bugs #21911]
+
+! When processing a request in the DHCPv6 server code that specifies
+ an address that is tagged as abandoned (meaning we received a
+ decline request for it previously) don't attempt to move it from
+ the inactive to active pool as doing so can result in the server
+ crashing on an assert failure. Also retag the lease as active
+ and reset it's timeout value.
+ [ISC-Bugs #21921]
+
+- Relay no longer crashes, when DHCP packet is received over interface without
+ any IPv4 address assigned. [ISC-Bugs #22409]
+
+ Changes since 4.2.0rc1
+
+- Documentation cleanup covering multiple tickets
+ [ISC-Bugs #20265] [ISC-Bugs #20259] minor cleanup
+ [ISC-Bugs #20263] add text describing some default values
+ [ISC-Bugs #20193] single quotes at the start of a line indicate a control
+ line to nroff, escape them if we actually want a quote.
+ [ISC-Bugs #18916] sync the pointer to web pages amongst the different docs
+
+ Changes since 4.2.0b2
+
+- Add declaration for variable in debug code in alloc.c. [ISC-Bugs #21472]
+
+ Changes since 4.2.0b1
+
+- Prohibit including lease time information in a response to a DHCP INFORM.
+ [ISC-Bugs #21092]
+
+! Accept a client id of length 0 while hashing. Previously the server would
+ exit if it attempted to hash a zero length client id, providing attackers
+ with a simple denial of service attack. [ISC-Bugs #21253]
+ CERT: VU#541921 - CVE: CVE-2010-2156
+
+- A memory leak in ddns processing was closed. [ISC-Bugs #21377]
+
+- Modify the exception handling for initial context creation. Previously
+ we would try and clean up before exiting. This could present problems
+ when the cleanup required part of the context that wasn't available. It
+ also didn't do much as we exited afterwards anyway. Now we simply log
+ the error and exit. [ISC-Bugs #21093]
+
+- A bug was fixed that could cause the DHCPv6 server to advertise/assign a
+ previously allocated (active) lease to a client that has changed subnets,
+ despite being on different shared networks. Dynamic prefixes specifically
+ allocated in shared networks also now are not offered if the client has
+ moved. [ISC-Bugs #21152]
+
+- Add some debugging output for use with the DDNS code. [ISC-Bugs #20916]
+
+- Fix the trace code to handle timing events better and to truncate a file
+ before using instead of overwriting it. [ISC-Bugs #20969]
+
+- Modify the determination of the default TTL to use for DDNS updates.
+ The user may still configure the ttl via ddns-ttl. The default for
+ both v4 and v6 is now 1/2 the (preferred) lease time with a limit. The
+ previous defaults (1/2 lease time without a limit for v4 and a default
+ value for v6) may be used by defining USE_OLD_DDNS_TTL in site.h
+ [ISC-Bugs #21126]
+
+- libisc/libdns is now brought up to version 9.7.1rc1. This corrects
+ three reported flaws in ISC DHCP;
+
+ o DHCP processes (dhcpd, dhclient) fail to start if one of either the
+ IPv4 or IPv6 address families is not present. [ISC-Bugs #21122]
+
+ o Assertion failure when attempting to cancel a previously running DDNS
+ update. [ISC-Bugs #21133]
+
+ o Compilation failure of libisc/libdns due to the use of a flexible
+ array member. [ISC-Bugs #21316]
+
+ Changes since 4.2.0a2
+
+- Update the fsync code to work with the changes to the DDNS code. It now
+ uses a timer instead of noticing if there are no more packets to process.
+
+- When constructing the DNS name structure from a text string append
+ the root to relative names. This satisfies a requirement in the DNS
+ library that names be absolute instead of relative and prevents DHCP
+ from crashing. [ISC-Bugs #21054]
+
+- "The LDAP Patch" that has been circulating for some time, written by
+ Brian Masney and S.Kalyanasundraram and maintained for application to
+ the DHCP-4 sources by David Cantrell has been included. Please be
+ advised that these sources were contributed, and do not yet meet the
+ high standards we place on production sources we include by default.
+ As a result, the LDAP features are only included by using a compile-time
+ option which defaults off, and if you enable it you do so under your
+ own recognizance. We will be improving this software over time.
+ [ISC-Bugs #17741]
+
+ Changes since 4.2.0a1
+
+- When using 'ignore client-updates;', the FQDN returned to the client
+ is no longer truncated to one octet.
+
+- Cleaned up an unused hardware address variable in nak_lease().
+
+- Manpage entries for the ia-pd and ia-prefix options were updated to
+ reflect support for prefix delegation.
+
+- Cleaned up some compiler warnings
+
+- An optimization described in the failover protocol draft is now included,
+ which permits a DHCP server operating in communications-interrupted state
+ to 'rewind' a lease to the state most recently transmitted to its peer,
+ greatly increasing a server's endurance in communications-interrupted.
+ This is supported using a new 'rewind state' record on the dhcpd.leases
+ entry for each lease.
+
+- Fix the trace code which was broken by the changes to the DDNS code.
+
+ Changes since 4.1.0 (new features)
+
+- Failover port configuration can now be left to defaults (port 647) as
+ described in the -12 revision of the Failover draft (and assigned by
+ IANA). Thanks in part to a patch from David Cantrell at Red Hat.
+
+- If configured, dhclient may now transmit to an anycast MAC address,
+ rather than using a broadcast address. Thanks to a patch from David
+ Cantrell at Red Hat.
+
+- Added client support for setting interface MTU and metric, thanks to
+ Roy "UberLord" Marples <roy@marples.name>.
+
+- Added client -D option to specify DUID type to send.
+
+- A new failover configuration parameter has been introduced for those
+ environments where DHCP servers can be reasonably guaranteed to be
+ "down" when the failover TCP socket is severed, "auto-partner-down".
+ This parameter is not generally safe, and by default is disabled, so
+ please carefully review the documentation of this parameter in the
+ dhcpd.conf(5) manpage before determining to use it yourself.
+
+- Added a configuration function, 'gethostname()', which calls the system
+ function of the same name and presents the results as a data expression.
+ This function can be used to incorporate the system level hostname of
+ the system the DHCP software is operating on in responses or queries (such
+ as including a failover partner's hostname in a dhcp message or binding
+ scope, or having a DHCP client send any system hostname in the host-name or
+ FQDN options by default).
+
+- The dhcp-renewal-time and dhcp-rebinding-time options may now be configured
+ for DHCPv4 operation and used independently of the dhcp-lease-time
+ calculations. Invalid renew and rebinding times (e.g., greater than the
+ determined lease time) are omitted.
+
+- Processing the DHCP to DNS server transactions in an asyncrhonous fashion.
+ The DHCP server or client can now continue with it's processing while
+ awaiting replies from the DNS server.
+
+- The 'hardware [ethernet|etc] ...;' parameter in host records has been
+ extended to attempt to match DHCPv6 clients by the last octets of a
+ DUID-LL or DUID-LLT provided by the client.
+
+ Changes since 4.1.0 (bug fixes)
+
+- Remove infinite loop in token_print_indent_concat().
+
+- Validate the argument to the -p option.
+
+- The notorious 'option <unknown> ... larger than buffer' log line,
+ which is seen in some malformed DHCP client packets, was modified.
+ It now logs the universe name, and does not log the length values
+ (which are bogus corruption read from the packet anyway). It also
+ carries a hopefully more useful explanation.
+
+- Suppress spurious warnings from configure about --datarootdir
+
+- A bug was fixed that caused the server not to answer some valid Solicit
+ and Request packets, if the dynamic range covering any requested addresses
+ had been deleted from configuration.
+
+- Update the code to deal with GCC 4.3. This included two sets of changes.
+ The first is to the configuration files to include the use of
+ AC_USE_SYSTEM_EXTENSIONS. The second is to deal with return values that
+ were being ignored.
+
+- The db-time-format option was documented in manpages.
+
+- Using reserved leases no longer results in 'lease with binding state
+ free not on its queue' error messages, thanks to a patch from Frode
+ Nordahl.
+
+- Fix a build error in dhcrelay, using older versions of gcc with
+ dhcpv6 disabled.
+
+- Two uninitialized stack structures are now memset to zero, thanks to a
+ patch from David Cantrell at Red Hat.
+
+- Fixed a cosmetic bug where pretty-printing valid domain-search options would
+ result in an erroneous error log message ('garbage in format string').
+
+- A bug in DLPI packet transmission (Solaris, HP/UX) that caused the server
+ to stop receiving packets is fixed. The same fix also means that the MAC
+ address will no longer appear 'bogus' on DLPI-based systems.
+
+- A bug in select handling was discovered where the results of one select()
+ call were discarded, causing the server to process the next select() call
+ and use more system calls than required. This has been repaired - the
+ sockets will be handled after the first return from select(), resulting in
+ fewer system calls.
+
+- The update-conflict-detection feature would leave an FQDN updated without
+ a DHCID (still currently implemented as a TXT RR). This would cause later
+ expiration or release events to fail to remove the domain name. The feature
+ now also inserts the client's up to date DHCID record, so records may safely
+ be removed at expiration or release time. Thanks to a patch submitted by
+ Christof Chen.
+
+- Memory leak in the load_balance_mine() function is fixed. This would
+ leak ~20-30 octets per DHCPDISCOVER packet while failover was in use
+ and in normal state.
+
+- Various compilation fixes have been included for the memory related
+ DEBUG #defines in includes/site.h.
+
+- Fixed Linux client script 'unary operator expected' errors with DHCPv6.
+
+- Fixed setting hostname in Linux hosts that require hostname argument
+ to be double-quoted. Also allow server-provided hostname to
+ override hostnames 'localhost' and '(none)'.
+
+- Fixed failover reconnection retry code to continue to retry to reconnect
+ rather than restarting the listener.
+
+- Compilation on Solaris with USE_SOCKETS defined in includes/site.h has
+ been repaired. Other USE_ overrides should work better.
+
+- A check for the local flavor of IFNAMSIZ had a broken 'else' condition,
+ that probably still resulted in the correct behaviour (but wouldn't use
+ a larger defined value provided by the host OS).
+
+- Fixed a bug where an OMAPI socket disconnection message would not result
+ in scheduling a failover reconnection, if the link had not negotiated a
+ failover connect yet (e.g.: connection refused, asynch socket connect()
+ timeouts).
+
+- A bug was fixed that caused the 'conflict-done' state to fail to be parsed
+ in failover state records.
+
+! A stack overflow vulnerability was fixed in dhclient that could allow
+ remote attackers to execute arbitrary commands as root on the system,
+ or simply terminate the client, by providing an over-long subnet-mask
+ option. CERT VU#410676 - CVE-2009-0692
+
+- Fixed a bug where relay agent options would never be returned when
+ processing a DHCPINFORM.
+
+- Versions 3.0.x syntax with multiple name->code option definitions is now
+ supported. Note that, similarly to 3.0.x, for by-code lookups only the
+ last option definition is used.
+
+- Fixed a bug where a time difference of greater than 60 seconds between a
+ failover pair could cause the primary to crash on contact with the
+ secondary. Thanks to a patch from Steinar Haug.
+
+- Don't look for IPv6 interfaces on Linux when running in DHCPv4 mode.
+ Thanks to patches from Matthew Newton and David Cantrell.
+
+- Secondary servers in a failover pair will now perform ddns removals if
+ they had performed ddns updates on a lease that is expiring, or was
+ released through the primary. As part of the same fix, stale binding scopes
+ will now be removed if a change in identity of a lease's active client is
+ detected, rather than simply if a lease is noticed to have expired (which it
+ may have expired without a failover server noticing in some situations).
+
+- A patch supplied by David Cantrell at RedHat was applied that detects
+ invalid calling parameters given to the ns_name_ntop() function.
+ Specifically, it detects if the caller passed a pointer and size pair
+ that causes the pointer to integer-wrap past zero.
+
+! Fixed a fenceposting bug when a client had two host records configured,
+ one using 'uid' and the other using 'hardware ethernet'. CVE-2009-1892
+
+- Fixed the check in the dhcp_interface_signal_handler routine to verify
+ the existence of the linked signal handler before calling it.
+
+- Both host and subnet6 configuration groups are now included whether a
+ fixed-address6 (DHCPv6) is in use or not. Host scoped configuration takes
+ precedence. This fixes two bugs, one where host scoped configuration
+ would not be included from a non-fixed-address6 host record, and the equal
+ and opposite bug where subnet6 scoped configuration would not be used when
+ over-riding values were not present in a matching fixed-address6 host
+ configuration.
+
+- ./configure now checks to ensure the intX_t and u_intX_t types are defined,
+ correcting a compilation failure when using Sun's compiler.
+
+- Modified the handling of a connection to avoid releasing the omapi io
+ object for the connection while it is still in use. One symptom from
+ this error was a segfault when a failover secondary attempted to connect
+ to the failover primary if their clocks were not synchronized.
+
+- Clean up to allow compilation with gcc 2.95.4 on FreeBSD. Remove an
+ extra semi-colon from common/dns.c and moved setting a variable to NULL
+ in server/dhcpv6.c to allow the compiler to decide that the variable
+ was always properly set.
+
+ Changes since 4.1.0b1
+
+- A missing "else" in dhcrelay.c could have caused an interface not to
+ be recognized.
+
+ Changes since 4.1.0a2
+
+- A cosmetic bug in DHCPDECLINE processing was fixed which caused all
+ successful DHCPDECLINEs to be logged as "not found" rather than
+ "abandoned".
+
+- Added configuration file examples for DHCPv6.
+
+- Some failover debugging #defines have been better defined and some
+ high frequency messages moved to a deeper debugging symbol.
+
+- The CLTT parameter in failover is now only updated by client activity,
+ and not by failover binding updates (taking on the peer's CLTT).
+
+- Failover BNDUPD messages are now discarded if they conflict with an
+ update that has been transmitted, but not acknowledged.
+
+- A bug cleaning up unknown-xxx temporary option definitions was fixed.
+
+- Delayed-ack is now a compile-time option, compiled out by default.
+ This feature is simply too experimental for right now, and causes
+ some problems to some failover installations. We will revisit this
+ in future releases.
+
+- The !inet_pton() call in res_mkupdrec was adjusted to '<= 0' as
+ inet_pton returns either 1, 0, or -1.
+
+- A dhclient-script for MacOS X has been included, which enables
+ 'dhclient -6' support.
+
+- DDNS removal routines were updated so that the DHCID is not removed until
+ the client has been deprived of all A and AAAA records (not only the last
+ one of either of those). This resolves a bug where dual stack clients
+ would not be able to regain their names after either expiration event.
+
+ Changes since 4.1.0a1
+
+- Corrected list of failover state values in dhcpd man page.
+
+- Fixed a bug that caused some request types to be logged incorrectly.
+
+- Clients that sent a parameter request list containing the
+ routers option before the subnet mask option were receiving
+ only the latter. Fixed.
+
+- The server wasn't always sending the FQDN option when it should.
+
+- A partner-down failover server no longer emits 'peer holds all free leases'
+ if it is able to newly-allocate one of the peer's leases.
+
+- Fixed a coredump when adding a class via OMAPI.
+
+- Check whether files are zero length before trying to parse them.
+
+- Ari Edelkind's PARANOIA patch has been included and may be compiled in
+ via two ./configure parameters, --enable-paranoia and
+ --enable-early-chroot.
+
+- ./configure was extended to cover many optional build features, such
+ as failover, server tracing, debugging, and the execute() command.
+
+- There is now a default 1/4 of a second scheduled delay between delayed
+ fsync()'s, it can be configured by the max-ack-delay configuration
+ parameter.
+
+- A bug was fixed where the length of a hostname was miscalculated, so that
+ hosts were given odd-looking domain names ("foo.bar.ba.example.com").
+
+- Shared network selection should be done from the innermost relay
+ valid link-address field, rather than the outermost.
+
+- Prefix pools are attached to shared network scopes.
+
+- Merged IA_XX related structures.
+
+- Add DHCPv6 files in configure.
+
+- A memory leak when using omapi has been fixed.
+
+- DHCPv6 vendor-class options (VSIO) are now only sent when they appear
+ on the DHCPv6 ORO. This resolves a bug where VSIO options were placed
+ in IA_NA encapsulated options fields.
+
+- Integrated client with stateless, temporary address and prefix delegation
+ support.
+
+- A double-dereference in dhclient transmission of DHCPDECLINEs was
+ repaired.
+
+- Fix handling of format code 'Z'.
+
+- Support "-1" argument in DHCPv6.
+
+- Merge DHCPv6-only "dhcrelay6" into general-purpose "dhcrelay" (use
+ "-6" option to select DHCPv6 mode).
+
+- Fix handling of -A and -a flags in dhcrelay; it was failing to expand
+ packet size as needed to add relay agent options.
+
+- A bug in subnet6 parsing where options contained in subnet6 clauses would
+ not be applied to clients addressed within that network was repaired.
+
+- When configuring a "subnet {}" or "subnet6 {}" without an explicit
+ shared-network enclosing it, the DHCP software would synthesize a
+ shared-network to contain the subnet. However, all configuration
+ parameters within the subnet more intuitively belong "to any client
+ on that interface", or rather the synthesized shared-network. So,
+ when a shared-network is synthesized, it is used to contain the
+ configuration present inside the subnet {} clause. This means that
+ the configuration will be valid for all clients on that network, not
+ just those addressed out of the stated subnet. If you intended the
+ opposite, the workaround is to explicitly configure an empty
+ shared-network.
+
+- A bug was fixed where Information-Request processing was not sourcing
+ configured option values.
+
+- A warning was added since the DHCPv6 processing software does not yet
+ support class statements.
+
+- Compliation warnings on GCC 4.3 relating to bootp source address
+ selection were repaired.
+
+- The v6 BSD socket method was updated to use a single UDP BSD socket
+ no matter how many interfaces are involved, differentiating the
+ interfaces the packets were received on by the interface index supplied
+ by the OS.
+
+- The relay agent no longer listens to the All DHCP Servers Multicast
+ address.
+
+- A bug was fixed in data_string_sprintfa() where va_start was only called
+ once for two invocations of vsprintf() variants.
+
+- ERO (RFC 4994) server support.
+
+- Basic and partial DHCPv6 leasequery support.
+
+- Reliable DHCPv6 release (previous behavior, send release and exit, is
+ still available with dhclient -6 -1 -r).
+
+ Changes since 4.0.0 (new features)
+
+- Added DHCPv6 rapid commit support.
+
+- Added explicit parser support for zero-length DHCP options, such as
+ rapid-commit, via format code 'Z'.
+
+- It's now possible to update the "ends" field of a lease with OMAPI.
+ This is useful if you want not only to release a lease, but also make
+ it available for reuse right away. Hat tip to Christof Chen.
+
+- Fixed definition of the iaaddr hash functions to use the correct
+ functions when referencing and dereferencing memory.
+
+- Some definitions not in phase with the IANA registry were updated.
+
+- Allocated interface IDs are better controlled ('u' bit set to zero,
+ reserved IDs avoided).
+
+- Unicast options are taken into account only for RENEWs.
+
+- NoAddrsAvail answers to SOLICITs are always ADVERTISEs even when a SOLICIT
+ carries a rapid-commit option.
+
+- Return in place of raise an impossible condition when one tries to release
+ an empty active lease.
+
+- Timer granularity is now 1/100s in the DHCPv6 client.
+
+- The dhclient-script was updated to create a host route for the default
+ gateway if the supplied subnet mask for an IPv4 address was a /32. This
+ allows the client to work in 'captive' network environments, where the
+ operator does not want clients to crosstalk directly.
+
+- MINUS tokens should be parseable again.
+
+- Multiple (up to "delayed-ack x;" maximum) DHCPv4 packets are now queued and
+ released in bursts after single fsync() events when the upper limit is
+ reached or if the receiving sockets go dry. The practical upshot is
+ that fsync-coupled server performance is now multiplicitively increased.
+ The default delayed ack limit is 28. Thanks entirely to a patch from
+ Christof Chen.
+
+ Changes since 4.0.0 (bug fixes)
+
+- DHCP now builds on AIX.
+
+- Exit with warning when DHCPv6-specific statements are used in the
+ config file but -6 is not specified.
+
+- Fixed "--version" flag in dhcrelay
+
+- The 'min-secs' configuration parameter's log message has been updated to
+ be more helpful.
+
+- The warning logged when an address range doesn't fit in the subnets
+ they were declared has been updated to be more helpful and identify the
+ typo in configuration that created the spanning addresses.
+
+- A bug in failover pool rebalancing that caused POOLREQ message ping-pongs
+ was repaired.
+
+- A flaw in failover pool rebalancing that could cause POOLREQ messages to
+ be sent outside of the min-balance/max-balance scheduled intervals has
+ been repaired.
+
+- A cosmetic bug during potential-conflict recovery that caused the peer's
+ 'conflict-done' state message to be logged as 'unknown-state' has been
+ repaired. It is now logged correctly.
+
+- A bug was fixed where the 'giaddr' may be used to find the client's subnet
+ rather than its own 'ciaddr'.
+
+- A log message was introduced to clarify the situation where a failover
+ 'address' parameter (the server's local address) did not resolve to an
+ IPv4 address.
+
+- The minimum site code value was set to 224 in 3.1.0 to track RFC3942. This
+ broke a lot of legacy site local configurations. The new code in place will
+ track site local space minimum option codes and logs a warning to encourage
+ updates and exploration of site local code migration problems. Option
+ codes less than 128 in site local spaces remain inaccessible.
+
+- A possible relay agent option bug was repaired where random server
+ initialization state may have been used to signal the relay agent
+ information options sub-option code for the 'END' of the option space.
+
+- Fixes to allow code to compile and run on Solaris 9.
+
+- Fixes to allow code to compile on Mac OS X Leopard (10.5).
+
+- When server is configured with options that it overrides, a warning is
+ issued when the configuration file is read, rather than at the time the
+ option is overridden. This was important, because the warning was given
+ every time the option was overridden, which could create a lot of
+ unnecessary logging.
+
+- Fixed a compilation problems on platforms that define a value for FDDI,
+ which conflicts with a dhcp configuration syntax token by the same name.
+
+- When a failover server suspects it has encountered a peer running a
+ version 3.0.x failover server, a warning that the failover wire protocol
+ is incompatible is printed.
+
+- The failover server no longer issues a floating point error if it encounters
+ a previously undefined option code.
+
+- Fix startup error messages to report a missing "subnet6 declaration", rather
+ than a missing "subnet declaration", when running as a DHCPv6 server.
+
+- DHCPv6 client timestamp in DUID was based on the year 1970 rather
+ than the year 2000.
+
+- Warn when attempting to use a hardware parameter in DHCPv6.
+
+- DHCPv6 released resources are now marked as released by the client.
+
+- 'Soft' bindings have no more side-effects.
+
+ Changes since 4.0.0b3
+
+- The reverse dns name for PTR updates on IPv6 addresses has been fixed to
+ use ip6.arpa. rather than default to in-addr.arpa and require user
+ configuration.
+
+- dhc6_lease_destroy() and dhc6_ia_destroy() now set lease and IA pointers
+ to NULL after freeing, to prevent subsequent accesses to freed memory.
+
+- The DHCPv6 server would not send the preference option unless the
+ client requested it, via the ORO. This has been fixed, so the DHCPv6
+ server will always send the preference value if it is configured.
+
+- When addresses were passed as hints to the server in an IA, they were
+ incorrectly handled, sometimes being treated as an error. Now the
+ server will treat these as hints and ignore them if it cannot supply
+ a requested address.
+
+- If the client had multiple addresses, and one expired (was not renewed
+ by the server), the client would continue to attempt to renew the same
+ old address over and over. Now, the client will omit any expired
+ addresses from future Confirm, Renew, or Rebind messages.
+
+- dhclient -6 will now select renew/rebind timers based upon the longest
+ address expiration time rather than the shortest expiration time, in
+ order to avoid cascading renewals in the event a server elects not to
+ extend one of multiple IAADDR leases.
+
+- The server now limits clients that request multiple addresses to one
+ address per IA by default, which can be adjusted through the
+ "limit-addrs-per-ia" configuration option.
+
+- The DHCPv6 client now issues fresh transaction IDs on Renew and Rebind
+ message exchanges, rather than using the most recent ID.
+
+- The DHCPv6 server now replies to Information-Request messages.
+
+- A bug was fixed in the dhclient-script for BSDs to correctly carry error
+ codes through some conditions.
+
+- The parsing of some options in the dhclient lease file, in particular
+ the success DHCPv6 status-code, was fixed.
+
+- A bug was fixed that caused the DHCPv6 ORO option to be corrupted with
+ seemingly random values.
+
+- A reference overleak in DHCPv6 shared network processing was repaired.
+
+- ./configure now autodetects local database locations rather than trying
+ to put dhcpd.leases and dhclient.leases in /usr/local/var/db, which no
+ one ever has.
+
+- Regression fix for bug where server advertised a IPv6 address in
+ response to a SOLICIT but would not return the address in response
+ to a REQUEST.
+
+- A bug was fixed where the DHCPv6 server puts the NoAddrsAvail status
+ code in the IA_NA was fixed. The status code now appears in the root
+ level.
+
+ Changes since 4.0.0b2
+
+- Clarified error message when lease limit exceeded
+
+- Relative time may now be used as a qualifier for 'allow' and 'deny' access
+ control lists. These directives may be used to assist in re-addressing
+ address pools without having to constantly reconfigure the server. Please
+ see 'man dhcpd.conf' for more information on allow/deny 'after time' syntax.
+ Thanks to a patch from Christof Chen.
+
+- The server will now include multiple IA_NA's and multiple IAADDRs within
+ them, if advertised by the client. It still only seeks to allocate one
+ new address.
+
+ Changes since 4.0.0b1
+
+- Use different paths for PID and lease files when running in DHCPv4
+ or DHCPv6 mode, so that servers for both protcols can be run
+ simultaneously on a single interface.
+
+- Fixed a buffer overflow error which could have allowed a denial
+ of service under unusual server configurations
+
+- Eliminated a spurious error message from the client
+
+- A number of bugs with the internal handling of lease state on the
+ server have been fixed. Some of these could cause server crashes.
+
+- The peer_wants_leases() changes pulled up from 3.1.0 were corrected,
+ 'never used' leases will no longer consistently shift between servers
+ on every pool rebalance run.
+
+- sendmsg()/recvmsg() control buffers are now declared in such a way to
+ ensure they are correctly aligned on all (esp. 64-bit) architectures.
+
+- The client leasing subsystem was streamlined and corrected to account
+ more closely for changes in client link attachment selection.
+
+ Changes since 4.0.0a3
+
+- The DHCP server no longer requires a "ddns-update-style" statement,
+ and now defaults to "none", which means DNS updates are disabled.
+
+- Log messages when failover peer names mismatch have been improved to
+ point out the problem.
+
+- Bug where server advertised a IPv6 address in response to a SOLICIT
+ but would not return the address in response to a REQUEST. Thanks to
+ Dennis Kou for finding the bug.
+
+- Fixed an error causing the server to lock up on lease expiration,
+ reported independently by Jothilingam Vasu and Dennis Kou.
+
+- Fixed a ./configure bug where compile tests were failing due to
+ "-Werror" (unused variable) rather than the actual test failure. Lead
+ to inconsistent and unworkable auto-configurations.
+
+- Compilation with DLPI and -Werror has been repaired.
+
+- Error in decoding IA_NA option if multiple interfaces are present
+ fixed by Marcus Goller.
+
+- DHCPv6 server Confirm message processing has been enhanced - it no
+ longer replies only to clients with host {} records, it now replies
+ as directed in RFC3315 section 18.2.2 - that is, to all clients
+ regardless of the existence of bindings.
+
+- A core dump during expired lease cleanup has been repaired.
+
+- DDNS updates state information are now stored in 'binding scopes' that
+ follow the leases through their lifecycles. This enables DDNS teardowns
+ on leases that are assigned and expired inbetween a server restart (the
+ state is recovered from dhcpd.leases). Arbitrary user-specified binding
+ scopes ('set var = "value";') are not yet supported.
+
+- Additional compilation problems on HP/UX have been repaired.
+
+ Changes since 4.0.0a2
+
+- Fix for startup where there are no IPv4 addresses on an interface.
+ Thanks to Marcus Goller for reporting the bug.
+
+- Fixed file descriptor leak on listen failure. Thanks to Tom Clark.
+
+- Bug in server configuration parser caused server to get stuck on
+ startup for certain bad pool declarations. Thanks to Guillaume
+ Knispel for the bug report and fix.
+
+- Code cleaned to remove warnings reported by "gcc -Wall".
+
+- DHCPv6 is now the default. You can disable DHCPv6 support using the
+ "--disable-dhcpv6" flag when you run the configure script.
+
+- An internal database inconsistency bug was repaired where the server
+ would segfault if a client attempted to renew a lease that had been
+ loaded from persistent storage.
+
+- 'request' and 'also request' syntaxes have been added to accommodate
+ the DHCPv6 client configuration. 'send dhcp6.oro' is no longer
+ necessary.
+
+- Bug fixed where configuration file parsing did not work with
+ zero-length options; this made it impossible to set the
+ rapid-commit option.
+
+- Bogus messages about host records with IPv4 fixed-addresses being of
+ non-128-bits in length were removed.
+
+ Changes since 4.0.0a1
+
+- Bug in octal parsing fixed. Thanks to Bernd Fuhrmann for the report
+ and fix.
+
+- Autoconf now supplies proper flags for Solaris DHCPv6 builds.
+
+- Fix for parsing error on some IPv6 addresses.
+
+- Invalid CIDR representation for IPv6 subnets or ranges now checked
+ for when loading configuration.
+
+- Compilation on HP/UX has been repaired. The changes should generally
+ apply to any architecture that supplies SIOCGLIFCONF but does not
+ use 'struct lifconf' structures to pass values.
+
+- Two new operators, ~= and ~~, have been integrated to implement
+ boolean matches by regular expression (such as may be used in
+ class matching statements). Thanks to a patch by Alexandr S.
+ Agranovsky, which underwent slight modification.
+
+- Fix for icmp packets on 64-bit systems (bug introduced in 4.0).
+
+- A bug was fixed in interface discovery wherein an error identifying
+ a server-configured interface with no IPv4 addresses would SEGV.
+
+- Fixed a bug in which write_lease() might report a failure incorrectly
+
+- Added support for DHCPv6 Release messages
+
+- Added -x option to dhclient, which triggers dhclient processes
+ to exit gracefully without releasing leases first
+
+- All binaries (client, server, relay) now change directories
+ to / before going into daemon mode, so as not to hold $CWD open
+
+- Fixed a bug parsing DHCPv6 client-id's in host-identifier statements
+
+- Fixed a bug with the 'ddns-updates' boolean server configuration
+ parameter, which caused the server to fail.
+
+ Changes since 4.0.0-20070413
+
+- Old (expired) leases are now cleaned.
+
+- IPv6 subnets now have support for arbitrary allocation ranges via
+ a new 'range6' configuration directive.
+
+- An obviated option code hash lookup to find D6O_CLIENTID was removed.
+
+- Corrected some situations where variables might be used without being
+ initialized.
+
+- Silenced several other compiler warnings.
+
+- Include the more standard sys/uio.h rather than rely upon other
+ header files to include it (fixes a BSD 4.2 compile failure).
+
+- Duplicate dhclient-script updates for DHCPv6 to all provided scripts.
+
+- DHCPv4 I/O methods that failed to sense hardware address were corrected.
+
+- DHCPv4 is now the default (as documented) rather than DHCPv6. The default
+ was set to DHCPv6 to facilitate ease early development, and forgotten.
+
+- Corrected a segmentation violation in DHCPv4 socket processing.
+
+- dhclient will now fork() into the background once it binds to an
+ IPv6 address, or immediately if the -n flag is supplied.
+
+- -q is now the default behaviour on dhclient, with -d or -v enabling
+ non-quiet (stderr logging) mode.
+
+- Fix documentation of the domain-search atom (quoted, with commas).
+
+- Document DHCPv6 options presently in the default table.
+
+- Replaced ./configure shellscripting with GNU Autoconf.
+
+ Changes since 3.1.0 (NEW FEATURES)
+
+- DHCPv6 Client and Server protocol support. Use '-6' to run the daemons
+ as v6-only. Use '-4' to run the daemons as v4-only (default. There is
+ no support currently for both.
+
+- Server support for multiple IA_NA options, containing at most one
+ IAADDR option.
+
+- Client support for one IA_NA option, containing any number of IAADDR
+ options.
+
+- Server support for the DHCPv6 Information-request message.
+
+- Inappropriate unicast DHCPv6 messages sent to the server are now
+ discarded, and this has rearchitected the IO system slightly.
+
+- The DHCPv6 server DUID defaults to type 1, is persistently stored in
+ the leases database, and can be over-ridden (either completely, or by
+ specifying type 1 or type 2).
+
+- The server only uses Rapid-Commit if it has been configured with the
+ Rapid-Commit option and the client requests it.
+
+- DDNS support. We now update AAAA records in the same place we would
+ update A records, if we have an IPv6 address. We also generate IP6.ARPA
+ style names for PTR records if we're dealing with an IPv6 address. Both
+ A and AAAA updates are done using the same 'fqdn.' virtual option space
+ (although the DHCPv4 FQDN and DHCPv6 FQDN options are formatted
+ differently, they both use the same code here).
+
+- The Linux dhclient-script attempts to set and remove assigned addresses,
+ and to configure /etc/resolv.conf from nameserver and domain name
+ configurations. It can be extended to configure other parameters.
+
+- Initial DHCPv6 lease support.
+
+- The IO system now tracks all local IP addresses, so that the DHCP
+ applications (particularly the dhcrelay) can discern between what frames
+ were transmitted to it, and what frames are being carried through it which
+ it should not intercept.
+
+ Changes since 3.1.0 (Maintenance)
+
+- A bug was repaired where MAC Address Affinity for virgin leases always
+ mapped to the primary. Virgin leases now have an interleaved preference
+ between primary and secondary.
+
+- A bug was repaired where MAC Address Affinity for clients with no client
+ identifier was sometimes mishashed to the peer. Load balancing during
+ runtime and pool rebalancing were opposing.
+
+- An assertion in lease counting relating to reserved leases was repaired.
+
+- The subnet-mask option inclusion now conforms with RFC2132 section 3.3;
+ it will only appear prior to the routers option if it is present on the
+ Parameter-Request-List. The subnet-mask option will also only be
+ included by default (if it is not on the PRL) in response to DISCOVER
+ or REQUEST messages.
+
+- The FQDN option is only supplied if the client supplied an FQDN option or
+ if the FQDN option was explicitly requested on the PRL.
+
+- Dynamic BOOTP leases are now load balanced in failover.
+
+ Changes since 3.1.0rc1
+
+- The parse warning that 'deny dyanmic bootp;' must be configured for
+ failover protected subnets was removed.
+
+ Changes since 3.1.0b2
+
+- Failover rebalance events no longer play ping pong with round errors
+ (moving leases between free and back to backup where there are an
+ odd number of leases).
+
+- The 'pool' log line has been split into two messages, one before the
+ rebalance run, and one after.
+
+- Any queued BNDACKs are transmitted before transmitting new BNDUPDs.
+ This enforces the correct sequence of events for the remote server
+ processing these messages.
+
+ Changes since 3.1.0b1
+
+- Fixed a bug that caused OMAPI clients to freeze when opening lease
+ objects.
+
+- A new server config option "fqdn-reply" specifies whether the server
+ should send out option 81 (FQDN). Defaults to "on". If set to "off",
+ the FQDN option is not sent, even if the client requested it. This is
+ needed because some clients misbehave otherwise. Thanks to Christof Chen
+ at Allianz.
+
+- Allow trace output files (-tf option) to be overwritten, rather than
+ crashing dhcpd if the file already exists
+
+- A bug was fixed that caused dhcpd to segfault if a pool was declared
+ outside the scope of a subnet in dhcpd.conf.
+
+- Some uninitialized values were repaired in dhcpleasequery.c that
+ caused the server to abort.
+
+- A new server config option, 'do-reverse-updates', has been added
+ which causes the server to abstain from performing updates on PTR
+ records. Thanks to a patch from Christof Chen at Allianz.
+
+- A bug was repaired in subencapsulation support, where spaces separated
+ by empty spaces would not get included.
+
+- A bug in dhclient was repaired which caused it to send parameter request
+ lists of 55 bytes in length no matter how long the declared PRL was.
+
+- 'dhcp.c(3953): non-null pointer' has been repaired. This fixes a flaw
+ wherein the DHCPv4 server may ignore a configured server-identifier.
+
+- A flaw in failover startup sequences was repaired that sometimes left
+ the primary DHCP server's pool rebalance schedules unscheduled.
+
+- Corrected a flaw that broke encapsulated spaces included due to presence
+ on the parameter request list.
+
+ Changes since 3.1.0a3
+
+- Some spelling fixes.
+
+ Changes since 3.1.0a2
+
+- A bug was fixed where attempting to permit leasequeries results in a
+ fatal internal error, "Unable to find server option 49".
+
+- A bug was fixed in dhclient rendering the textual output form of the
+ domain-search option syntax.
+
+ Changes since 3.1.0a1
+
+- A bug in the FQDN universe that added FQDN codes to the NWIP universe's
+ hash table was repaired.
+
+- The servers now try harder to transmit pending binding updates when
+ entering normal state.
+
+- UPDREQ/UPDREQALL handling was optimized - it no longer dequeues and
+ requeues all pending updates. This should reduce the number of spurious
+ 'xid mismatch' log messages.
+
+- An option definition referencing leak was fixed, which resulted in early
+ termination of dhclient upon the renewal event.
+
+- Some default hash table sizes were tweaked, some upwards, some downwards.
+ 3.1.0a1's tables resulted in a reduction in default server memory use.
+ The new selected values provide more of a zero sum (increasing the size
+ of tables likely to be populated, decreasing the size of tables unlikely).
+
+- Lease structures appear in three separate hashes: by IP address, by UID,
+ and by hardware address. One type of table was used for all three, and
+ improvements to IP address hashing were applied to all three (so UID and
+ hardware addresses were treated like 4-byte integers). There are now two
+ types of tables, and the uid/hw hashes use functions more appropriate
+ to their needs.
+
+- The max-lease-misbalance percentage no longer causes scheduled rebalance
+ runs to be skipped: it still governs the schedule, but every scheduled
+ run will attempt balance.
+
+- A segfault bug in recursive encapsulation support has been corrected.
+
+ Changes since 3.0 (New Features)
+
+- A workaround for certain STSN servers that send a mangled domain-name
+ option was introduced for dhclient. The client will now accept corrupted
+ server responses, if they contain a valid DHCP_MESSAGE_TYPE (OFFER, ACK,
+ or NAK). The server will continue to not accept corrupt client packets.
+
+- Support for 'reserved' (pseudo-static) and BOOTP leases via failover
+ was introduced.
+
+- Support for adding, removing, and managing class and subclass statements
+ via OMAPI.
+
+- The failover implementation was updated to comply with revision 12 of
+ the protocol draft.
+
+- 'make install' now creates the initial zero-length dhcpd.leases file if
+ one does not already exist on the system.
+
+- RFC3942 compliance, site-local option spaces start at 224 now, not 128.
+
+- The Load Balance Algorithm was misimplemented. The current implementation
+ matches RFC 3074.
+
+- lcase() and ucase() configuration expressions have been added which adjust
+ their arguments from upper to lower and lower to upper cases respectively.
+ Thanks to a patch from Albert Herranz.
+
+- The dhclient 'reject ...;' statement, which rejects leases given by named
+ server-identifiers, now permits address ranges to be specified in CIDR
+ notation. Thanks to a patch from David Boyce.
+
+- The subnet-mask option is now supplied by default, but at lowest
+ priority. This helps a small minority of clients that provide parameter
+ request lists, but do not list the subnet-mask option because they were
+ designed to interoperate with a server that behaves in this manner.
+
+- The FQDN option is similarly supplied even if it does not appear on the
+ parameter request list, but not to the exclusion of options that do
+ appear at the parameter request list. Up until now it had ultimate
+ priority over the client's parameter request list.
+
+- Varying option space code and length bit widths (8/16/32) are now
+ supported. This is a milestone in achieving RFC 3925 "VIVSO" and
+ DHCPv6 support.
+
+- A new common (server or client) option, 'db-time-format local;', has
+ been added which prints the local time in /var/db/dhcpd.leases rather
+ than UTC. Thanks to a patch from Ken Lalonde.
+
+- Some patches to improve DHCP Server startup speed from Andrew Matheson
+ have been incorporated.
+
+- Failover pairs now implement 'MAC Affinity' on leases moving from the
+ active to free states. Leases that belonged to the failover secondary
+ are moved to BACKUP state rather than FREE upon exiting EXPIRED state.
+ If lease rebalancing must move leases, it tries first to move leases
+ that belong to the peer in need.
+
+- The server no longer sends POOLREQ messages unless the pool is severely
+ misbalanced in the peer's favor (see 'man dhcpd.conf' for more details).
+
+- Pool rebalance events no longer happen upon successfully allocating a
+ lease. Instead, they happen on a schedule. See 'man dhcpd.conf' for the
+ min-balance and max-balance statements for more information.
+
+- The DHCP Relay Agent Information Option / Link Selection Sub-Option
+ is now supported. (See RFC3527 for details).
+
+- A new DDNS related server option, update-conflict-detection, has been
+ added. If this option is enabled, dhcpd will perform normal DHCID
+ conflict resolution (the default). If this option is disabled, it will
+ instead trust the assigned name implicitly (removing any other bindings
+ on that name). This option has not been made available in dhclient.
+
+- In those cases where the DHCP software manufactures an IP header (to
+ transmit via bpf, lpf, etc), the IP TTL the software selects has been
+ increased from 16 to 128. This is intended to match Microsoft Windows
+ DHCP Client behaviour, to increase compatibility.
+
+- 'ignore client-updates;' now has behaviour that is different from
+ 'deny client-updates;'. The client's request is not truly ignored,
+ rather it is encouraged. Should this value be configured, the server
+ updates DNS as though client-updates were set to 'deny'. That is, it
+ enters into DNS whatever it is configured to do already, provided it is
+ configured to. Then it sends a response to the client that lets the
+ client believe it is performing client updates (which it will), probably
+ for a different name. In essence, this lets the client do as it will,
+ ignoring this aspect of their request.
+
+- Support for compressed 'domain name list' style DHCP option contents, and
+ in particular the domain search option (#119) was added.
+
+- The DHCP LEASEQUERY protocol as defined in RFC4388 is now implemented.
+ LEASEQUERY lets you query the DHCP server for information about a lease,
+ using either an IP address, MAC address, or client identifier. Thanks
+ to a patch from Justin Haddad.
+
+- DHCPD is now RFC2131 section 4.1 compliant (broadcast to all-ones ip and
+ ethernet mac address) on the SCO platform specifically without any strange
+ ifconfig hacks. Many thanks go to the Kroger Co. for donating the
+ hardware and funding the development.
+
+- A new common configuration executable statement, execute(), has been
+ added. This permits dhcpd or dhclient to execute a named external
+ program with command line arguments specified from other configuration
+ language. Thanks to a patch written by Mattias Ronnblom, gotten to us
+ via Robin Breathe.
+
+- A new dhcp server option 'adaptive-lease-time-threshold' has been added
+ which causes the server to substantially reduce lease-times if there are
+ few (configured percentage) remaining leases. Thanks to a patch submitted
+ from Christof Chen.
+
+- Encapsulated option spaces within encapsulated option spaces is now
+ formally supported.
+
+ Changes since 3.0.6rc1
+
+- supersede_lease() now requeues leases in their respective hardware
+ address hash bucket. This mirrors client identifier behaviour.
+
+ Changes since 3.0.5
+
+- Assorted fixes for broken network devices: Packet length is now
+ determined from the IP header length field to finally calculate the
+ UDP payload length, because some NIC drivers return more data than
+ they actually received.
+
+- UDP packets are now stored in aligned data structures.
+
+- A logic error in omapi interface code was repaired that might result in
+ incorrectly indicating 'up' state when any flags were set, rather than
+ specifically the INTERFACE_REQUESTED flag. Thanks to a patch from
+ Jochen Voss which got to us via Andrew Pollock at Debian.
+
+- A reference leak on binding scopes set by ddns updates was repaired.
+
+- A memory leak in the minires_nsendsigned() function call was repaired.
+ Effectively, this leaked ~176 bytes per DDNS update.
+
+- In the case where an "L2" DHCP Relay Agent (one that does not set giaddr)
+ was directly attached to the same broadcast domain as the DHCP server,
+ the RFC3046 relay agent information option was not being returned to the
+ relay in the server's replies. This was fixed; the dhcp server no longer
+ requires the giaddr to reply with relay agent information. Note that
+ this also improves compatibility with L2 devices that "intercept" DHCP
+ packets and expect relay agent information even in unicast (renewal)
+ replies. Thanks to a patch from Pekka Silvonen.
+
+- A bug was fixed where the BOOTP header 'sname' field had a value, the
+ copy written to persistent storage was actually the contents of the
+ 'file' field.
+
+- A bug was fixed where the nwip virtual option space was referencing
+ the fqdn option's virtual option space's option cache.
+
+- Timestamp parsing errors that indicated missing "minutes" fields rather
+ than the actually missing "seconds" fields have been repaired thanks to
+ a patch from Kevin Steves.
+
+- A grammar error in the dhclient.8 manpage was repaired thanks to a patch
+ from Chris Wagner.
+
+- Several spelling typos were repaired, and some cross-references to other
+ relevant documents were included in the manpages, thanks to a patch
+ by Andrew Pollock which got to us via Tomas Pospisek.
+
+- Some bugs were fixed in the 'emergency relay agent options hologram'
+ which is used to retain relay agent option contents from when the
+ client was in INIT or REBIND states. This should solve problems where
+ relay agent options were not echoed from the server, even when giaddr
+ was set.
+
+- dhclient now closes its descriptor to dhclient.leases prior to executing
+ dhclient-script. Thanks to a patch from Tomas Pospisek.
+
+- The server's "by client-id" and "by hardware address" hash table lists
+ are now sorted according to the preference to re-allocate that lease to
+ returning clients. This should eliminate pool starvation problems
+ arising when "INIT" clients were given new leases rather than presently
+ active ones.
+
+ Changes since 3.0.5rc1
+
+- A bug was repaired in fixes to the dhclient, which sought to run the
+ dhclient-script with the 'EXPIRE' state should it receive a NAK in
+ response to a REQUEST. The client now iterates the PREINIT state
+ after the EXPIRE state, so that interfaces that might be configured
+ 'down' can be brought back 'up' and initialized.
+
+- DHCPINFORM handling for clients that properly set ciaddr and come to the
+ server via a relay aget has been repaired.
+
+ Changes since 3.0.4
+
+- A warning that host statements declared within subnet or shared-network
+ scopes are actually global has been added.
+
+- The default minimum lease time (if min-lease-time was not specified)
+ was raised from 0 to 300. 0 is not thought to be sensible, and is
+ known to be damaging.
+
+- Added additional fatal error sanity checks surrounding lease binding
+ state count calculations (free/active counts used for failover pool
+ balancing).
+
+- Some time value size fixes in 3.0.4 brought on from FreeBSD /usr/ports were
+ misapplied to server values rather than client values. The server no longer
+ advertises 8-byte lease-time options when on 64-bit platforms.
+
+- A bug where leases not in ACTIVE state would get billed to billed classes
+ (classes with lease limitations) was fixed. Non-active leases OFFERed
+ to clients are no longer billed (but billing is checked before offering).
+
+- The dhcpd.conf.5 manpage was updated in regard to the ddns-domainname
+ configuration option - the default configuration and results should be
+ more clear now.
+
+- If the dhclient were to receive a DHCPNAK while it was in the RENEW
+ state (and consequently, had an active, 'bound' address and related
+ configuration options), it would fail to 'tear down' this information
+ before proceeding into INIT state. dhclient now iterates the dhclient-
+ script with the 'EXPIRE' action to cause these teardowns prior to entering
+ INIT state. Thanks to a patch from Chris Zimmerman.
+
+- The omapi.1 manpage had some formatting errors repaired thanks to a patch
+ from Yoshihiko Sarumaru.
+
+- A few lines of code that were failover-specific were moved within
+ #if defined() clauses so that compilation without failover could be
+ made possible.
+
+- The log message emitted when the 'leased-address' value was not available
+ in dhcpd.conf "executable statements" has been updated to be more helpful.
+ Manpage information for this value has also been updated.
+
+- Abandoned or dissociated (err condition) leases now remove any related
+ dynamic dns bindings. Thanks to a patch from Patrick Schoo.
+
+- Attempting to write a new lease file to replace a corrupt (due to
+ encountering non-retryable errors during writing) lease file should
+ no longer result in an infinite recursion.
+
+- Host declaration hardware addresses and client identifiers may only be
+ configured once. dhcpd will now fail to load config files that specify
+ multiple identifiers (previous versions would silently over-ride the
+ value with the later configured value).
+
+- Several option codes that have been allocated since our last release
+ have been named and documented.
+
+- Option names of the form "unknown-123" have been removed from the in-
+ memory hash tables. In order to support options of these names that
+ may appear in dhclient.leases or similar in previous versions, the
+ parser will now find the new option code definition, or mock up a
+ generic option code definition. This should result in a smooth
+ transition from one name to the other, as the new name is used to
+ write new output.
+
+ Changes since 3.0.4rc1
+
+- The dhcp-options.5 manpage was updated to correct indentation errors
+ thanks to a patch from Jean Delvare.
+
+ Changes since 3.0.4b3
+
+- Some manual pages were clarified pursuant to discussion on the dhcp-server
+ mailing list.
+
+ Changes since 3.0.4b2
+
+- Null-termination sensing for certain clients that unfortunately require
+ it in DHCPINFORM processing was repaired.
+
+- The host-name option and a few others were moved from "X" format to "t"
+ format to be compatible with new NULL handling functions.
+
+- DHCPINFORM processing is a little more careful about return addressing
+ its responses, or if responding via a relay. The INFORM related
+ messages also log the 'effective client ip address' rather than the
+ client's supplied ciaddr (since some clients produce null ciaddrs).
+
+- The server was inappropriately sending leases to the RESET state in the
+ event that multiple active leases were found to match a singly-identified
+ client. This was changed to RELEASED (by accepting a different, ACTIVE
+ binding, the client is implicitly releasing its lease). This repairs a
+ bug wherein secondary servers in failover pairs detecting this condition
+ move leases to RESET, and primaries refuse to accept that state
+ transition (properly).
+
+- The memset-after-dmalloc() changes made in 3.0.4b1 have been backed out.
+
+ Changes since 3.0.4b1
+
+- Command line parsing in omshell was repaired - it no longer closes
+ STDIN after reading one line.
+
+- The resolver library no longer closes the /etc/resolv.conf file
+ descriptor it opened twice.
+
+- Changes to trailing NULL removal in 't' option-atoms has been rethought,
+ it now includes 'd' (domain name) types, and tries hard not to rewind an
+ option beyond the start of the text field it is un-terminating.
+
+ Changes since 3.0.3
+
+- A DDNS update handling function was misusing the DNS error codes, rather
+ than the internal generic result enumeration. The result is a confusing
+ syslog line, logging the wrong condition.
+
+- The DHCP Server was not checking pool balance in the case where it brought
+ a non-ACTIVE lease out of storage for a client that was returning to use
+ a lease it once had long ago, and had since expired.
+
+- Failover peers no longer bother to look for free leases to allocate when
+ they already found the client's ACTIVE lease. DISCOVERs are load balanced
+ whether freely-allocated or not, unless the server doubts the peer has
+ leases to allocate.
+
+- Fixed a bug in dhcrelay agent addition code that suppressed trailing
+ PAD options - it was suppressing only one trailing PAD option, rather
+ than the entire block of them.
+
+! Fixed some unlikely overlapping-region memcpy() bugs in dhcrelay agent
+ option addition and stripping code. Added a few sanity checks. Although
+ highly improbable, due to requiring the reception of a DHCP datagram well
+ in excess of all known to be used physical MTU limitations, it is possible
+ this may have been used in a stack overflow security vulnerability. Thanks
+ to a patch from infamous42md.
+
+! Added some sanity checks to OMAPI connection/authentication code.
+ Although highly improbable, due to having to deliver in excess of 2^32
+ bytes of data via the OMAPI channel, not to mention requiring dhcpd to
+ be able to malloc() a memory region 2^32 bytes in size, it was possible
+ this might have resulted in a heap overflow security vulnerability.
+ Thanks to a patch from infamous42md.
+
+- dmalloc() memset()'s the non-debug (data) portion of the allocated
+ memory to zero. Code that memset()'s the result returned by dmalloc() to
+ zero is redundant. These redundancies were removed.
+
+- Some type declaration corrections to u_int16_t were made in common/tr.c
+ (Token Ring support) thanks to a patch from Jason Vas Dias at Red Hat.
+
+- A failover bug that was allowing leases that EXPIRED or were RELEASED
+ where tsfp and tstp are identical timestamps to languish in these
+ transitional states has been repaired. As a side effect, lease
+ databases should be kept more consistent overall, not just for these
+ transitional states.
+
+- If the lease db is deleted out from under the daemon, and it moves to rewrite
+ the db, it will go ahead with the operation and move the new db into place
+ once it detects the old db does not exist.
+
+- dhclient now ignores IRDA, SIT, and IEEE1394 network interfaces, as it
+ is either nonsensical or (in the case of IEEE1394) is not known to support
+ these interfaces. Thanks to Marius Gedminas and Andrew Pollock of Debian.
+
+- Some previously undocumented reasons for dhclient-script invoking has
+ been documented in the dhclient-script.8 manpage.
+
+- Failover potential expiry calculations (TSTP) have been corrected. Results
+ should be substantially more consistent, and proper given the constraints.
+
+- Adjusted lease state validation checks in potential-conflict, to
+ account for possible clock skew similarly to normal state, and several
+ previously illegal transitions were made legal (ex: active->released).
+
+- An impossible sanity check was removed from omapi/buffer.c, thanks to a
+ patch from 'infamous42md'.
+
+- An OMAPI host/network byte order problem in lease time values has been
+ repaired.
+
+- Several minor bugs, largely relating to treating 8-byte time values as
+ 4-byte entities, have been repaired after careful review of the FreeBSD
+ ports collection's patch set. Thanks to the nameless entities who have
+ contributed to the FreeBSD ports.
+
+- When writing a trace file, the file is now created with permissions 0600,
+ to help administrators avoid accidentally publicising sensitive config
+ data.
+
+- The calculation of the maximum size of DHCP packets no longer includes
+ Ethernet framing overhead. The result is that the 'Maximum Message
+ Size' option advertised by clients, or the default value 576, is no
+ longer reduced by 14 bytes, and instead directly reflects the IP level
+ MTU (and the default, minimum allowed IP MTU of 576).
+
+- The special status of RELEASED/EXPIRED/RESET leases when a server
+ is operating in partner-down was fixed. It no longer requires a
+ lease be twice the MCLT beyond STOS to 'reallocate', and the expiry
+ event to turn these into FREE leases without peer acknowledgement
+ (after STOS+MCLT) has been repaired.
+
+- Compilation on older Solaris systems (lacking /usr/include/sys/int_types.h)
+ has been repaired.
+
+- "append"ing a string onto the end of a "t" type option (such as the
+ domain-name field) that had been improperly NULL-terminated by the
+ DHCP server will no longer result in a truncated string containing
+ only the option from the server, and not the expected appended value.
+ Thanks to a patch from Jason Vas Dias at Red Hat.
+
+- File handlers on configuration state (config files and lease dbs) should
+ be treated consistently, regardless of whether TRACING is defined or not.
+
+- The Linux build environment has had some minor improvements - better
+ sensing of 64-bit pointer sizes (only used for establishing an icmp_id),
+ and corrections to #if operators regarding LINUX_MAJOR should it ever
+ move to 3.[01].x.
+
+- The server now tries harder to survive the condition where it is unable
+ to open a new lease file to rewrite the lease state database.
+
+ Changes since 3.0.3b3
+
+- dhclient.conf documentation for interface {} was updated to reflect recent
+ discussion on the dhcp-hackers mailing list.
+
+- In response to reports that the software does not compile on GCC 4.0.0,
+ -Werror was removed from Makefile.conf for all platforms that used it.
+ We will address the true problem in a future release; this is a temporary
+ workaround.
+
+ Changes since 3.0.3b2
+
+- An error in code changes introduced in 3.0.3b2 was corrected, which caused
+ static BOOTP clients to receive random addresses.
+
+ Changes since 3.0.3b1
+
+- A bug was fixed in BOOTPREQUEST handling code wherein stale references to
+ host records would be left behind on leases that were not allocated to the
+ client currently booting (eg in the case where the host was denied booting).
+
+- The dhcpd.conf.5 manpage was updated to be more clear in regards to
+ multiple host declarations (thanks to Vincent McIntyre). 'Interim' style
+ dynamic updates were also retouched.
+
+ Changes since 3.0.2
+
+- A bug was fixed where a server might load balance a DHCP REQUEST to its
+ peer after already choosing not to load balance the preceding DISCOVER.
+ The peer cannot allocate the originating server's lease.
+
+- In the case where a secondary server lost its stable storage while the
+ primary was still in communications-interrupted, and came back online,
+ the lease databases would not be fully transferred to the secondary.
+ This was due to the secondary errantly sending an extra UPDREQ message
+ when the primary made its state transition to PARTNER-DOWN known.
+
+- The package will now compile cleanly in gcc 3.3 and 3.4. As a side effect,
+ lease structures will be 9 bytes smaller on all platforms. Thanks to
+ Jason Vas Dias at Red Hat.
+
+- Interface discovery code in DISCOVER_UNCONFIGURED mode is now
+ properly restricted to only detecting broadcast interfaces. Thanks
+ to a patch from Jason Vas Dias at Red Hat.
+
+- decode_udp_ip_header was changed so that the IP address was copied out
+ to a variable, rather than referenced by a pointer. This enforces 4-byte
+ alignment of the 32-bit IP address value. Thanks to a patch from Dr.
+ Peter Poeml.
+
+- An incorrect log message was corrected thanks to a patch from
+ Dr. Peter Poeml.
+
+- A bug in DDNS was repaired, where if the server's first DDNS action was
+ a DDNS removal rather than a DDNS update, the resolver library's
+ retransmit timer and retry timer was set to the default, implying a
+ 15 second timeout interval. Which is a little excessive in a synchronous,
+ single-threaded system. In all cases, ISC DHCP should now hold fast to
+ a 1-second timeout, trying only once.
+
+- The siaddr field was being improperly set to the server-identifier when
+ responding to DHCP messages. RFC2131 clarified the siaddr field as
+ meaning the 'next server in the bootstrap process', eg a tftp server.
+ The siaddr field is now left zeroed unless next-server is configured.
+
+- mockup_lease() could have returned in an error condition (or in the
+ condition where no fixed-address was found matching the shared
+ network) with stale references to a host record. This is probably not
+ a memory leak since host records generally never die anyway.
+
+- A bug was repaired where failover servers would let stale client identifiers
+ persist on leases that were reallocated to new clients not sending an id.
+
+- Binding scopes ("set var = value;") are now removed from leases allocated
+ by failover peers if the lease had expired. This should help reduce the
+ number of stale binding scopes on leases.
+
+- A small memory leak was closed involving client identifiers larger than
+ 7 bytes, and failover.
+
+- Configuring a subnet in dhcpd.conf with a subnet mask of 32 bits might
+ cause an internal function to overflow heap. Thanks to Jason Vas Dias
+ at Red Hat.
+
+- Some inconsistencies in treating numbers that the lexer parsed as 'NUMBER'
+ or 'NUMBER_OR_NAME' was repaired. Hexadecimal parsing is affected, and
+ should work better.
+
+- In several cases, parse warnings were being issued before the lexical
+ token had been advanced to the token whose value was causing an error...
+ causing parse warnings to claim the problem is on the wrong token.
+
+- Host declarations matching on client identifier for dynamic leases will
+ no longer match fixed-address host declarations (this is now identical
+ to behaviour for host records matching on hardware address).
+
+ Changes since 3.0.2rc3
+
+- A previously undocumented configuration directive, 'local-address',
+ was documented in the dhcpd.conf manpage.
+
+ Changes since 3.0.2rc2
+
+- Two variables introduced in 3.0.2b1 were used without being initialized
+ in the case where neither the FILE nor SNAME fields were available for
+ overloading. This was repaired.
+
+- A heretofore believed to be impossible corner case of the option
+ overloading implementation turned out to be possible ("Unable to sort
+ overloaded options after 10 tries."). The implementation was reworked
+ to consider the case of an option so large it would require more than
+ three chunks to fit.
+
+- Many other instances of variables being used without being initialized
+ were repaired.
+
+- An uninitialized variable in omapi_io_destroy() led to the discovery
+ that this function may result in orphaned pointers (and hence, a memory
+ leak).
+
+ Changes since 3.0.2rc1
+
+- allocate_lease() was rewritten to repair a bug in which the server would
+ try to allocate an ABANDONED lease when FREE leases were available.
+
+ Changes since 3.0.2b1
+
+- Some dhcp-eval.5 manpage formatting was repaired.
+
+ Changes since 3.0.1
+
+- A bug was fixed in the server's 'option overloading' implementation,
+ where options loaded into the 'file' and 'sname' packet fields were
+ not aligned precisely as rfc2131 dictates.
+
+- The FreeBSD client script was changed to support the case where a domain
+ name was not provided by the server.
+
+- A memory leak in 'omshell' per each command line parsed was
+ repaired, thanks to a patch from Jarkko Torppa.
+
+- Log functions writing to stderr were adjusted to use the STDERR_FILENO
+ system definition rather than '2'. This is a no-op for 90% of platforms.
+
+- One call to trace_write_packet_iov() counted the number of io vectors
+ incorrectly, causing inconsistent tracefiles. This was fixed.
+
+- Some expression parse failure memory leaks were closed.
+
+- A host byte order problem in tracefiles was repaired.
+
+- Pools configured in DHCPD for failover possessing permission lists that
+ previously were assumed to not include dyanmic bootp clients are now
+ a little more pessimistic. The result is, dhcpd will nag you about just
+ about most pools that possess a 'allow' statement with no 'deny' that
+ would definitely match a dynamic bootp client.
+
+- The 'ddns-update-style' configuration warning bit now insists that
+ the configuration be globally scoped.
+
+- Two memory leaks in dhclient were closed thanks to a patch from Felix
+ Farkas.
+
+- Some minor but excellently pedantic documentation errors were fixed
+ thanks to a patch from Thomas Klausner.
+
+- Bugs in operator precedence in executable statements have been repaired
+ once again. More legal syntaxes should be parsed legally.
+
+- Failing to initialize a tracefile for any reason if a tracefile was
+ specified is now a fatal error. Thanks to a patch from Albert Herranz.
+
+- Corrected a bug in which the number of leases transferred as calculated
+ by the failover primary and sent to peers in POOLRESP responses may be
+ incorrect. This value is not believed to be used by other failover
+ implementations, excepting perhaps as logged information.
+
+- Corrected a bug in which 'dhcp_failover_send_poolresp()' was in fact
+ sending POOLREQ messages instead of POOLRESP mesasges. This message
+ was essentially ignored since failover secondaries effectively do not
+ respond to POOLREQ messages.
+
+- Type definitions for various bitwidths of integers in the sunos5-5
+ build of ISC DHCP have been fixed. It should compile and run more
+ easily when built in 64-bit for this platform.
+
+- "allow known-clients;" is now a legal syntax, to avoid confusion.
+
+- If one dhcp server chooses to 'load balance' a request to its failover
+ peer, it first checks to see if it believes said peer has a free
+ lease to allocate before ignoring the DISCOVER.
+
+- log() was logging a work buffer, rather than the value returned by
+ executing the statements configured by the user. In some cases,
+ the work buffer and the intended results were the same. In some other
+ cases, they were not. This was fixed thanks to a patch from Gunnar
+ Fjone and directconnect.no.
+
+- Compiler warnings for some string type conversions was fixed, thanks
+ to Andreas Gustafsson.
+
+- The netbsd build environments were simplified to one, in which
+ -Wconversion is not used, thanks to Andreas Gustafsson.
+
+- How randomness in the backoff-cutoff dhclient configuration variable
+ is implemented was better documented in the manpage, and the behaviour
+ of dhclient in REQUEST timeout handling was changed to match that of
+ DISCOVER timeout handling.
+
+- Omapi was hardened against clients that pass in null values, thanks
+ to a patch from Mark Jason Dominus.
+
+- A bug was fixed in dhclient that kept it from doing client-side
+ ddns updates. Thanks to a patch from Andreas Gustafsson, which
+ underwent some modification after review by Jason Vas Dias.
+
+- Failover implementations disconnected due to the network between
+ them (rather than one of the two shutting down) will now try to
+ re-establish the failover connection every 5 seconds, rather than
+ to simply try once and give up until one of them is restarted.
+ Thanks to a patch from Ulf Ekberg from Infoblox, and field testing
+ by Greger V. Teigre which led to an enhancement to it.
+
+- A problem that kept DHCP Failover secondaries from tearing down
+ ddns records was repaired. Thanks to a patch from Ulf Ekberg from
+ Infoblox.
+
+- 64bit pointer sizes are detected properly on FreeBSD now.
+
+- A bug was repaired where the DHCP server would leave stale references
+ to host records on leases it once thought about offering to certain
+ clients. The result would be to apply host and 'known' scopes to the
+ wrong clients (possibly denying booting). NOTE: The 'mis-host' patch
+ that was being circulated as a workaround is not the way this bug was
+ fixed. If you were a victim of this bug in 3.0.1, you are cautioned
+ to proceed carefully and see if it fixes your problem.
+
+- A bug was repaired in the server's DHCPINFORM handling, where it
+ tried to divine the client's address from the source packet and
+ would get it wrong. Thanks to Anshuman Singh Rawat.
+
+- A log message was introduced to help illuminate the case where the
+ server was unable to find a lease to assign to any BOOTP client.
+ Thanks to Daniel Baker.
+
+- A minor dhcpd.conf.5 manpage error was fixed.
+
+ Changes since 3.0.1rc14
+
+- The global variable 'cur_time' was centralized and is now uniformly of a
+ type #defined in system-dependent headers. It had previously been defined
+ in one of many places as a 32-bit value, and this causes mayhem on 64-bit
+ big endian systems. It probably wasn't too healthy on little endian
+ systems either.
+
+- A printf format string error introduced in rc14 was repaired.
+
+- AIX system-dependent header file was altered to only define NO_SNPRINTF
+ if the condition used to #ifdef in vsnprintf in AIX' header files
+ is false.
+
+- The Alpha/OSF system-dependent header file was altered to define
+ NO_SNPRINTF on OS revisions older than 4.0G.
+
+- omapip/test.c had string.h added to its includes.
+
+ Changes since 3.0.1rc13
+
+! CAN-2004-0460 - CERT VU#317350: Five stack overflow exploits were closed
+ in logging messages with excessively long hostnames provided by the
+ clients. It is highly probable that these could have been used by
+ attackers to gain arbitrary root access on systems using ISC DHCP 3.0.1
+ release candidates 12 or 13. Special thanks to Gregory Duchemin for
+ both finding and solving the problem.
+
+! CAN-2004-0461 - CERT VU#654390: Once the above was closed, an opening
+ in log_*() functions was evidenced, on some specific platforms where
+ vsnprintf() was not believed to be available and calls were wrapped to
+ sprintf() instead. Again, credit goes to Gregory Duchemin for finding
+ the problem. Calls to snprintf() are now linked to a distribution-local
+ snprintf implementation, only in those cases where the architecture is
+ not known to provide one (see includes/cf/[arch].h). If you experience
+ linking problems with snprintf/vsnprintf or 'isc_print_' functions, this
+ is where to look. This vulnerability did not exist in any previously
+ published version of ISC DHCP.
+
+- Compilation on hpux 11.11 was repaired.
+
+- 'The cross-compile bug fix' was backed out.
+
+ Changes since 3.0.1rc12
+
+- Fixed a bug in omapi lease lookup function, to form the hardware
+ address for the hash lookup correctly, thanks to a patch from
+ Richard Hirst.
+
+- Fixed a bug where dhcrelay was sending relayed responses back to the
+ broadcast address, but with the source's unicast mac address. Should
+ now conform to rfc2131 section 4.1.
+
+- Cross-compile bug fix; use $(AR) instead of ar. Thanks to Morten Brorup.
+
+- Fixed a crash bug in dhclient where dhcpd servers that do not provide
+ renewal times results in an FPE. As a side effect, dhclient can now
+ properly handle 0xFFFFFFFF (-1) expiry times supplied by servers. Thanks
+ to a patch from Burt Silverman.
+
+- The 'ping timeout' debugs from rc12 were removed to -DDEBUG only,
+ and reformatted to correct a compilation error on Solaris platforms.
+
+- A patch was applied which fixes a case where leases read from the
+ leases database do not properly over-ride previously read leases.
+
+- dhcpctl.3 manpage was tweaked.
+
+ Changes since 3.0.1rc11
+
+- A patch from Steve Campbell was applied with minor modifications to
+ permit reverse dns PTR record updates with values containing spaces.
+
+- A patch from Florian Lohoff was applied with some modifications to
+ dhcrelay. It now discards packets whose hop count exceeds 10 by default,
+ and a command-line option (-c) can be used to set this threshold.
+
+- A failover bug relating to identifying peers by name length instead of
+ by name was fixed.
+
+- Declaring failover configs within shared-network statements should no
+ longer result in error.
+
+- The -nw command line option to dhclient now works.
+
+- Thanks to a patch from Michael Richardson:
+ - Some problems with long option processing have been fixed.
+ - Some fixes to minires so that updates of KEY records will work.
+
+- contrib/ms2isc was updated by Shu-Min Chang of the Intel Corporation.
+ see contrib/ms2isc/readme.txt for revision notes.
+
+- Dhclient no longer uses shell commands to kill another instance of
+ itself, it sends the signal directly. Thanks to a patch from Martin
+ Blapp.
+
+- The FreeBSD dhclient-script was changed so that a failure to write to
+ /etc/resolv.conf does not prematurely end the script. This keeps dhclient
+ from looping infinitely when this is the case. Thanks to a patch from
+ Martin Blapp.
+
+- A patch from Bill Stephens was applied which resolves a problem with lease
+ expiry times in failover configurations.
+
+- A memory leak in configuration parsing was closed thanks to a patch from
+ Steve G.
+
+- The function which discovers interfaces will now skip non-broadcast or
+ point-to-point interfaces, thanks to a patch from David Brownlee.
+
+- Options not yet known by the dhcpd or dhclient have had their names
+ changed such that they do not contain # symbols, in case they should ever
+ appear in a lease file. An option that might have been named "#144" is
+ now "unknown-144".
+
+- Another patch from Bill Stephens which allows the ping-check timeout to
+ be configured as 'ping-timeout'. Defaults to 1.
+
+ Changes since 3.0.1rc10
+
+- Potential buffer overflows in minires repaired.
+
+- A change to the linux client script to use /bin/bash, since /bin/sh may
+ not be bash.
+
+- Some missing va_end cleanups thanks to a patch from Thomas Klausner.
+
+- A correction of boolean parsing syntax validation - some illegal syntaxes
+ that worked before are now detected and produce errs, some legal syntaxes
+ that errored before will now work properly.
+
+- Some search-and-replace errors that caused some options to change their
+ names was repaired.
+
+- Shu-min Chang of the Intel corporation has contributed a perl script and
+ module that converts the MS NT4 DHCP configuration to a ISC DHCP3
+ configuration file.
+
+- Applied the remainder of the dhcpctl memory leak patch provided by Bill
+ Squier at ReefEdge, Inc. (groo@reefedge.com).
+
+- Missing non-optional failover peer configurations will now result in a soft
+ error rather than a null dereference.
+
+ Changes since 3.0.1rc9
+
+- A format string was corrected to fix compiler warnings.
+
+- A number of spelling corrections were made in the man pages.
+
+- The dhclient.conf.5 man page was changed to refer to do-forward-updates
+ rather than a configuration option that doesn't exist.
+
+- A FreeBSD-specific bug in the interface removal handling was fixed.
+
+- A Linux-specific Token Ring detection problem was fixed.
+
+- Hashes removed from as-yet-unknown agent options, having those options
+ appear in reality before we know about them will no longer produce
+ self-corrupting lease databases.
+
+- dhclient will use the proper port numbers now when using the -g option.
+
+- A order-of-operations bug with 2 match clauses in 1 class statement is
+ fixed thanks to a patch from Andrew Matheson.
+
+- Compilation problems on Solaris were fixed.
+
+- Compilation problems when built with DEBUG or DEBUG_PACKET were repaired.
+
+- A fix to the dhcp ack process which makes certain group options will be
+ included in the first DHCPOFFER message was made thanks to a patch from
+ Ling Gou.
+
+- A few memory leaks were repaired thanks to patches from Bill Squier at
+ ReefEdge, Inc. (groo@reefedge.com).
+
+- A fix for shared-networks that sometimes give clients options for the
+ wrong subnets (in particular, 'option routers') was applied, thanks to
+ Ted Lemon for the patch.
+
+- Omshell's handling of dotted octets as values was changed such that dots
+ one after the other produce zero values in the integer string.
+
+ Changes since 3.0.1rc8
+
+- Fix a format string vulnerability in the server that could lead to a
+ remote root compromise (discovered by NGSEC Research Team, www.ngsec.com).
+
+- Add additional support for NetBSD/sparc64.
+
+- Fix a bug in the command-line parsing of the client. Also, resolve
+ a memory leak.
+
+- Add better support for shells other than bash in the Linux client
+ script.
+
+- Various build fixes for modern versions of FreeBSD and Linux.
+
+- Fix a bad bounds check when printing binding state names.
+
+- Clarify documentation about fixed-address and multiple addresses.
+
+- Fix a typo in the authoritative error message.
+
+- Make a log entry when we can't write a billing class.
+
+- Use conversion targets that are the right size on all architectures.
+
+- Increment the hop count when relaying.
+
+- Log a message when lease state is changed through OMAPI.
+
+- Don't rerun the shared_network when evaluating the pool.
+
+- Fix a reversed test in the parser.
+
+- Change the type of rbuf_max.
+
+- Make FTS_LAST a manifest constant to quiet warnings.
+
+ Changes since 3.0.1rc7
+
+- Fix two compiler warnings that are generated when compiling on Solaris
+ with gcc. These stop the build, even though they weren't actually
+ errors, because we prefer that our builds generate no warnings.
+
+ Changes since 3.0.1rc6
+
+- Don't allow a lease that's in the EXPIRED, RELEASED or RESET state
+ to be renewed.
+
+- Implement lease stealing for cases where the primary has fewer leases
+ than the secondary, as called for by the standard.
+
+- Add a fudge factor to the lease expiry acceptance code, (suggested
+ by Kevin Miller of CMU).
+
+- Fix a bug in permit_list_match that made it much too willing to say
+ that two permit lists matched.
+
+- Unless DEBUG_DNS_UPDATES is defined, print more user-friendly (and
+ also more compact) messages about DNS updates.
+
+- Fix a bug in generating wire-format domain names for the FQDN option.
+
+- Fix a bug where the FQDN option would not be returned if the client
+ requested it, contrary to the standard.
+
+- On Darwin, use the FreeBSD DHCP client script.
+
+- On NetBSD/sparc, don't check for casting warnings.
+
+- Add a flag in the DHCP client to disable updating the client's A
+ record when sending an FQDN option indicating that the client is
+ going to update its A record.
+
+- In the client, don't attempt a DNS update until one second after
+ configuring the new IP address, and if the update times out, keep
+ trying until a response, positive or negative, is received from the
+ DNS server.
+
+- Fix an uninitialized memory bug in the DHCP client.
+
+- Apply some FreeBSD-specific bug fixes suggested by Murray Stokely.
+
+- Fix a bug in ns_parserr(), where it was returning the wrong sort
+ of result code in some cases (suggested by Ben Harris of the
+ NetBSD project).
+
+- Fix a bug in is_identifier(), where it was checking against EOF
+ instead of the END_OF_FILE token (also suggested by Ben Harris).
+
+- Fix a bug where if an option universe contained no options, the
+ DHCP server could dump core (Walter Steiner).
+
+- Fix a bug in the handling of encapsulated options.
+
+- Fix a bug that prevented NWIP suboptions from being processed.
+
+- Delete the FTS_BOOTP and FTS_RESERVED states and implement them
+ as modifier flags to the FTS_ACTIVE state, as called for in the
+ failover protocol standard.
+
+- Fix bugs in the pool merging code that resulted in references and
+ dereferences of null pointers. This bug had no impact unless the
+ POINTER_DEBUG flag was defined.
+
+- In the server, added a do-forward-updates flag that can be used to
+ disable forward updates in all cases, so that sites that want the
+ clients to take sole responsibility for updating their A record can
+ do so.
+
+- Make it possible to disable optimization of PTR record updates.
+
+ Changes since 3.0.1rc5
+
+- Include some new documentation and changes provided by Karl Auer.
+
+- Add a workaround for some Lexmark printers that send a double-NUL-
+ terminated host-name option, which would break DNS updates.
+
+- Fix an off-by-one error in the MAC-address checking code for
+ DHCPRELEASE that was added in 3.0.1rc5.
+
+- Fix a bug where client-specific information was not being discarded
+ from the lease when it expired or was released, resulting in
+ problems if the lease was reallocated to a different client.
+
+- If more than one allocation pool is specified that has the same set
+ of constraints as another allocation pool on the same shared
+ network, merge the two pools.
+
+- Don't print an error in fallback_discard, since this just causes
+ confusion and does not appear to be helping to encourage anyone to
+ fix this bug.
+
+ Changes since 3.0.1rc4
+
+- Fix a bug that would cause the DHCP server to spin if asked to parse
+ a certain kind of incorrect statement.
+
+- Fix a related bug that would prevent an error from being reported in
+ the same case.
+
+- Additional documentation.
+
+- Make sure that the hardware address matches the lease when
+ processing a DHCPRELEASE message.
+
+ Changes since 3.0.1rc3
+
+- A minor bug fix in the arguments to a logging function call.
+- Documentation update for dhcpd.conf.
+
+ Changes since 3.0.1rc2
+
+- Allow the primary to send a POOLREQ message. This isn't what the current
+ failover draft says to do, so we may have to back it out if I can't get the
+ authors to relent, but the scheme for balancing that's specified in the
+ current draft seems needlessly hairy, so I'm floating a trial balloon.
+ The rc1 code did not implement the method described in the draft either.
+
+ Changes since 3.0.1rc1
+
+- Treat NXDOMAIN and NXRRSET as success when we are trying to delete a
+ domain or RRSET. This allows the DHCP server to forget about a name
+ it added to the DNS once it's been removed, even if the DHCP server
+ wasn't the one that removed it.
+
+- Install defaults for failover maximum outstanding updates and maximum
+ silent time. This prevents problems that might occur if these values
+ were not configured.
+
+- Don't do DDNS deletes if ddns-update-style is none.
+
+- Return relay agent information options in DHCPNAK. This prevents DHCPNAK
+ messages from being dropped when the relay agent information option contains
+ routing information.
+
+- Fix a problem where coming up in recover wouldn't result in an update
+ request being sent.
+
+- Add some more chatty messages when we start a recovery update and when it's
+ done.
+
+- Fix a possible problem where some state might have been left around
+ after the peer lost contact and regained contact about how many updates
+ were pending.
+
+- Don't nix a lease update because of a lease conflict. This test has
+ never (as far as I know) prevented a mistake, and it appears to cause
+ problems with failover.
+
+- Add support in rc history code for keeping a selective history, rather
+ than a history of all references and dereferences. This code is only used
+ when extensive additional debugging is enabled.
+
+ Changes since 3.0
+
+- Make allocators for hash tables. As a side effect, this fixes a memory
+ smash in the subclass allocation code.
+
+- Fix a small bug in omshell where if you try to close an object when
+ no object is open, it dumps core.
+
+- Fix an obscure coredump that could occur on shutdown.
+
+- Fix a bug in the recording of host declaration rubouts in the lease file.
+
+- Fix two potential spins in the host deletion code.
+
+- Fix a core dump that would happen if an application tried to update
+ a host object attribute with a null value.
+
+ Changes since 3.0 Release Candidate 12
+
+- Fix a memory leak in the evaluation code.
+
+- Fix an obscure core dump.
+
+- Print a couple of new warnings when parsing the configuration file
+ when crucial information is left out.
+
+- Log "no free leases" as an error.
+
+- Documentation updates.
+
+ Changes since 3.0 Release Candidate 11
+
+- Always return a subnet selection option if one is sent.
+
+- Fix a warning that was being printed because an automatic data
+ structure wasn't zeroed.
+
+- Fix some failover state transitions that were being handled
+ incorrectly.
+
+- When supersede_lease is called on a lease whose end time has already
+ expired, but for which a state transition has not yet been done, do
+ a state transition. This fixes the case where if the secondary
+ allocated a lease to a client and the lease "expired" while the
+ secondary was in partner-down, no expiry event would actually
+ happen, so the lease would remain active until the primary was
+ restarted.
+
+ Changes since 3.0 Release Candidate 10
+
+- Fix a bug that was preventing released leases from changing state
+ in failover-enabled pools.
+
+- Fix a core dump in the client identifier finder code (for host
+ declarations).
+
+- Finish fixing a bug where bogus data would sometimes get logged to
+ the dhclient.leases file because it was opened as descriptor 2.
+
+- Fix the Linux dhclient-script according to suggestions made by
+ several people on the dhcp-client mailing list.
+
+- Log successful DNS updates at LOG_INFO, not LOG_ERROR.
+
+- Print an error message and refuse to run if a failover peer is
+ defined but not referenced by any pools.
+
+- Correct a confusing error message in failover.
+
+ Changes since 3.0 Release Candidate 9
+
+- Fix a bug in lease allocation for Dynamic BOOTP clients.
+
+ Changes since 3.0 Release Candidate 8 Patchlevel 2
+
+- Fix a bug that prevented update-static-leases from working.
+
+- Document failover-state OMAPI object.
+
+- Fix a compilation error on SunOS 4.
+
+ Changes since 3.0 Release Candidate 8 Patchlevel 1
+
+- Fix a parsing bug that broke dns updates (both interim and ad-hoc).
+ This was introduced in rc8pl1 as an unintended result of the memory
+ leakage fixes that were in pl1.
+
+- Fix a long-standing bug where the server would record that an update
+ had been done for a client with no name, even though no update had
+ been done, and then when the client's lease expired the deletion of
+ that nonexistant record would time out because the name was the null
+ string.
+
+- Clean up the omshell, dhcpctl and omapi man pages a bit.
+
+ Changes since 3.0 Release Candidate 8
+
+- Fix a bug that could cause the DHCP server to spin if
+ one-lease-per-client was enabled.
+
+- Fix a bug that was causing core dumps on BSD/os in the presence of
+ malformed packets.
+
+- In partner-down state, don't restrict lease lengths to MCLT.
+
+- On the failover secondary, record the MCLT received from the primary
+ so that if we come up without a connection to the primary we don't
+ wind up giving out zero-length leases.
+
+- Fix some compilation problems on BSD/os.
+
+- Fix a bunch of memory leaks.
+
+- Fix a couple of bugs in the option printer.
+
+- Fix an obscure error reporting bug in the dns update code, and also
+ make the message clearer when a key algorithm isn't supported.
+
+- Fix a bug in the tracing code that prevented trace runs that used
+ tcp connections from being played back.
+
+- Add some additional debugging capability for catching memory leaks
+ on exit.
+
+- Make the client release the lease correctly on shutdown.
+
+- Add some configurability to the build system.
+
+- Install omshell manual page in man1, not man8.
+
+- Craig Gwydir sent in a patch that fixes a long-standing bug in the
+ DHCP client that could cause core dumps, but that for some reason
+ hadn't been noticed until now.
+
+ Changes since 3.0 Release Candidate 7
+
+- Fix a bug in failover where we weren't sending updates after a
+ transition from communications-interrupted to normal.
+
+- Handle expired/released/reset -> free transition according to the
+ protocol specification (this works - the other way not only wasn't
+ conformant, but also didn't work).
+
+- Add a control object in both client and server that allows either
+ daemon to be shut down cleanly.
+
+- When writing a lease, if we run out of disk space, shut down the
+ output file and insist on writing a new one before proceeding.
+
+- In the server, if the OMAPI listener port is occupied, keep trying
+ to get it, rather than simply giving up and exiting.
+
+- Support fetching variables from leases and also updating and adding
+ variables to leases via OMAPI.
+
+- If two failover peers have wildly different clocks, refuse to start
+ doing failover.
+
+- Fix a bug in the DNS update code that could cause core dumps when
+ running on alpha processors.
+
+- Fixed a bug in ddns updates for static lease entries, thanks to a
+ patch from Andrey M Linkevitch.
+
+- Add support for Darwin/MacOS X
+
+- Install omshell (including new documentation).
+
+- Support DNS updates in the client (this is a very obscure feature
+ that most DHCP client users probably will not be able to use).
+
+- Somewhat cleaner status logging in the client.
+
+- Make OMAPI key naming syntax compatible with the way keys are
+ actually named (key names are domain names).
+
+- Fix a bug in the lease file writer.
+
+- Install DHCP ISC headers in a different place than BIND 9 ISC
+ headers, to avoid causing trouble in BIND 9 builds.
+
+- Don't send updates for attributes on an object when the attributes
+ haven't changed. Support deleting attributes on remote objects.
+
+- Fix a number of bugs in omshell, and add the unset and refresh
+ statements.
+
+- Handle disconnects in OMAPI a little bit more intelligently (so that
+ the caller gets ECONNRESET instead of EINVAL).
+
+- Fix a bunch of bugs in the handling of clients that have existing
+ leases when the try to renew their leases while failover is
+ operating.
+
+ Changes since 3.0 Release Candidate 6
+
+- Fix a core dump that could happen when processing a DHCPREQUEST from
+ a client that had a host declaration that contained both a
+ fixed-address declaration and a dhcp-client-identifier option
+ declaration, if the client identifier was longer than nine bytes.
+
+- Fix a memory leak that could happen in certain obscure cases when
+ using omapi to manipulate leases.
+
+- Fix some bugs and omissions in omshell.
+
+ Changes since 3.0 Release Candidate 5
+
+- Fix a bug in omapi_object_dereference that prevented objects in
+ chains from having their reference counts decreased on dereference.
+
+- Fix a bug in omapi_object_dereference that would prevent object
+ chains from being freed upon removal of the last reference external
+ to the chain.
+
+- Fix a number of other memory leaks in the OMAPI protocol subsystem.
+
+- Add code in the OMAPI protocol handler to trace memory leakage.
+
+- Clean up the memory allocation/reference history printer.
+
+- Support input of dotted quads and colon-separated hex lists as
+ attribute values in omshell.
+
+- Fix a typo in the Linux interface discovery code.
+
+- Conditionalize a piece of trace code that wasn't conditional.
+
+ Changes since 3.0 Release Candidate 4
+
+- Fix a bug that would prevent leases from being abandoned properly on
+ DHCPDECLINE.
+
+- Fix failover peer OMAPI support.
+
+- In failover, correctly handle expiration of leases. Previously,
+ leases would never be reclaimed because they couldn't make the
+ transition from EXPIRED to FREE.
+
+- Fix some broken failover state transitions.
+
+- Documentation fixes.
+
+- Take out an unnecessary check in DHCP relay agent information option
+ stashing code that was preventing REBINDING clients from rebinding.
+
+- Prevent failover peers from allocating leases in DHCPREQUEST
+ processing if the lease belongs to the other server.
+
+- Record server version in lease file introductory comment.
+
+- Correctly report connection errors in OMAPI and failover.
+
+- Make authentication signature algorithm name comparisons in OMAPI
+ case-insensitive.
+
+- Fix compile problem on SunOS 4.x
+
+- If a signature algorithm is not terminated with '.', terminate it so
+ that comparisons between fully-qualified names will work
+ consistently.
+
+- Different SIOCGIFCONF probe code, may "fix" problem on some Linux
+ systems with the probe not working correctly.
+
+- Don't allow user to type omapi key on command line of omshell.
+
+ Changes since 3.0 Release Candidate 3
+
+- Do lease billing on startup in a way that I *think* will finally do
+ the billing correctly - the previous method could overbill as a
+ result of duplicate leases.
+
+- Document OMAPI server objects.
+
+ Changes since 3.0 Release Candidate 2 Patchlevel 1
+
+- Fix some problems in the DDNS update code. Thanks to Albert
+ Herranz for figuring out the main problem.
+
+- Fix some reference counting errors on host entries that were causing
+ core dumps.
+
+- Fix a byte-swap bug in the token ring code, thanks to Jochen
+ Friedrich.
+
+- Fix a bug in lease billing, thanks to Jonas Bulow.
+
+ Changes since 3.0 Release Candidate 2
+
+- Change the conditions under which a DHCPRELEASE is actually
+ committed to be consistent with lease binding states rather than
+ using the lease end time. This may fix some problems with the
+ billing class code.
+
+- Fix a bug where lease updates would fail on Digital Unix (and maybe
+ others) because malloc was called with a size of zero.
+
+- Fix a core dump that happens when the DHCP server can't create its
+ trace file.
+
+ Changes since 3.0 Release Candidate 1 Patchlevel 1
+
+- Fix the dhcp_failover_put_message to not attempt to allocate a
+ zero-length buffer. Some versions of malloc() fail if you try to
+ allocate a zero-length buffer, and this was causing problems on,
+ e.g., Digital Unix.
+
+- Fix a case where the failover code was printing an error message
+ when no error had occurred.
+
+- Fix a problem where when a server went down and back up again, the
+ peer would not see a state transition and so would stay in the
+ non-communicating state.
+
+- Be smart about going into recover_wait.
+
+- Fix a problem in the failover implementation where peers would fail
+ to come into sync if interrupted in the RECOVER state. This could
+ have been the cause of some problems people have reported recently.
+
+- Fix a problem with billing classes where they would not be unbilled
+ when the client lease expired.
+
+- If select fails, figure out which descriptor is bad, and cut it out
+ of the I/O loop. This prevents a potentially nasty spin. I
+ haven't heard any report it in a while, but it came up consistently
+ in testing.
+
+- Fix a bug in the relay agent where if you specified interfaces on
+ the command line, it would fail.
+
+- Fix a couple of small bugs in the omapi connection object (no known
+ user impact).
+
+- Add the missing 3.0 Beta 1 lease conversion script.
+
+- Read dhcp client script hooks if they exist, rather than only if
+ they're executable.
+
+ Changes since 3.0 Release Candidate 1
+
+- Fix a memory smash that happens when fixed-address leases are used.
+ ANY SITE AT WHICH FIXED-ADDRESS STATEMENTS ARE BEING USED SHOULD
+ UPGRADE IMMEDIATELY. This has been a long-standing bug - thanks to
+ Alvise Nobile for discovering it and helping me to find it!
+
+- Fix a small bug in binary-to-ascii, thanks to H. Peter Anvin of
+ Transmeta.
+
+- There is a known problem with the DHCP server doing failover on
+ Compaq Alpha systems. This patchlevel is not a release candidate
+ because of this bug. The bug should be straightforward to fix, so
+ a new release candidate is expected shortly.
+
+- There is a known problem in the DDNS update code that is probably a
+ bug, and is not, as far as we know, fixed in this patchlevel.
+
+ Changes since 3.0 Beta 2 Patchlevel 24
+
+- Went over problematic failover state transitions and made them all
+ work, so that failover should now much less fragile.
+
+- Add some dhcpctl and omapi documentation
+
+- Fix compile errors when compiling with unusual predefines.
+
+- Make Token Ring work on Linux 2.4
+
+- Fix the Digital Unix BPF_WORDALIGN bug.
+
+- Fix some dhcp client documentation errors.
+
+- Update some parts of the README file.
+
+- Support GCC on SCO.
+
+ Changes since 3.0 Beta 2 Patchlevel 23
+
+- Fix a bug in the DNS update code where a status code was not being
+ checked. This may have been causing core dumps.
+
+- When parsing the lease file, if a lease declaration includes a
+ billing class statement, and the lease already has a billing class,
+ unbill the old class.
+
+- When processing failover transactions, where acks will be deferred,
+ process the state transition immediately.
+
+- Don't try to use the new SIOCGIFCONF buffer size detection code on
+ Linux 2.0, which doesn't provide this functionality.
+
+- Apply a patch suggested by Tuan Uong for a problem in dlpi.c.
+
+- Fix a problem in using the which command in the configure script.
+
+- Fix a parse error in the client when setting up an omapi listener.
+
+- Document the -n and -g flags to the client.
+
+- Make sure there is always a stdin and stdout on startup. This
+ prevents shell scripts from accidentally writing error messages into
+ configuration files that happen to be opened as stderr.
+
+- If an interface is removed, the client will now notice that it is
+ gone rather than spinning. This has only been tested on NetBSD.
+
+- The client will attempt to get an address even if it can't create a
+ lease file.
+
+- Don't overwrite tracefiles.
+
+- Fix some memory allocation bugs in failover.
+
+ Changes since 3.0 Beta 2 Patchlevel 22
+
+- Apply some patches suggested by Cyrille Lefevre, who is maintaining
+ the FreeBSD ISC DHCP Distribution port.
+
+- Fix a core dump in DHCPRELEASE.
+
+ Changes since 3.0 Beta 2 Patchlevel 21
+
+- This time for sure: fix the spin described in the changes for pl20.
+
+ Changes since 3.0 Beta 2 Patchlevel 20
+
+- Fix a problem with Linux detecting large numbers of interfaces (Ben)
+
+- Fix a memory smash in the quotify code, which was introduced in
+ pl19.
+
+- Actually fix the spin described in the changes for pl20. The
+ previous fix only partially fixed the problem - enough to get it
+ past the regression test.
+
+ Changes since 3.0 Beta 2 Patchlevel 19
+
+- Fix a bug that could cause the server to abort if compiled with
+ POINTER_DEBUG enabled.
+
+- Fix a bug that could cause the server to spin when responding to a
+ DHCPREQUEST.
+
+- Apply Joost Mulders' suggested patches for DLPI on x86.
+
+- Support NUL characters in quoted strings.
+
+- Install unformatted man pages on SunOS.
+
+ Changes since 3.0 Beta 2 Patchlevel 18
+
+- Allow the server to be placed in partner-down state using OMAPI.
+ (Damien Neil)
+
+- Implement omshell, which can be used to do arbitrary things to the
+ server (in theory). (Damien Neil)
+
+- Fix a case where if a client had two different leases the server could
+ actually dereference the second one when it hadn't been referenced,
+ leading to memory corruption and a core dump. (James Brister)
+
+- Fix a case where a client could request the address of another client's
+ lease, but find_lease wouldn't detect that the other client had it, and
+ would attempt to allocate it to the client, resulting in a lease conflict
+ message.
+
+- Fix a case where a client with more than one client identifier could be
+ given a lease where the hardware address was correct but the client
+ identifier was not, resulting in a lease conflict message.
+
+- Fix a problem where the server could write out a colon-separated
+ hex list as a value for a variable, which would then not parse.
+ The fix is to always write strings as quoted strings, with any
+ non-printable characters quoted as octal escape sequences. So
+ a file written the old way still won't work, but new files written
+ this way will work.
+
+- Fix documentation for sending non-standard options.
+
+- Use unparsable names for unknown options. WARNING: this will
+ break any configuration files that use the option-nnn convention.
+ If you want to continue to use this convention for some options,
+ please be sure to write a definition, like this:
+
+ option option-nnn code nnn = string;
+
+ You can use a descriptive name instead of option-nnn if you like.
+
+- Fix a problem where we would see a DHCPDISCOVER/DHCPOFFER/
+ DHCPREQUEST/DHCPACK/DHCPREQUEST/DHCPNAK sequence. This was the
+ result of a deceptively silly bug in supersede_lease.
+
+- Fix client script exit status check, according to a fix supplied by
+ Hermann Lauer.
+
+- Fix an endianness bug in the tracefile support, regarding ICMP
+ messages.
+
+- Fix a bug in the client where the medium would not work correctly if
+ it contained quoted strings.
+
+ ** there was no pl17 **
+
+ Changes since 3.0 Beta 2 Patchlevel 16
+
+- Add support for transaction tracing. This allows the state of the
+ DHCP server on startup, and all the subsequent transactions, to be
+ recorded in a file which can then be played back to reproduce the
+ behaviour of the DHCP server. This can be used to quickly
+ reproduce bugs that cause core dumps or corruption, and also for
+ tracking down memory leaks.
+
+- Incorporate some bug fixes provided by Joost Mulders for the DLPI
+ package which should clear up problems people have been seeing on
+ Solaris.
+
+- Fix bugs in the handling of options stored as linked lists (agent
+ options, fqdn options and nwip options) that could cause memory
+ corruption and core dumps.
+
+- Fix a bug in DHCPREQUEST handling that resulted in DHCPNAK messages
+ not being send in some cases when they were needed.
+
+- Make the lease structure somewhat more compact.
+
+- Make initial failover startup *much* faster. This was researched
+ and implemented by Damien Neil.
+
+- Add a --version flag to all executables, which prints the program
+ name and version to standard output.
+
+- Don't rewrite the lease file every thousand leases.
+
+- A bug in nit.c for older SunOS machines was fixed by a patch sent in
+ by Takeshi Hagiwara.
+
+- Fix a memory corruption bug in the DHCP client.
+
+- Lots of documentation updates.
+
+- Add a feature allowing environment variables to be passed to the
+ DHCP client script on the DHCP client command line.
+
+- Fix client medium support, which had been broken for some time.
+
+- Fix a bug in the DHCP client initial startup backoff interval, which
+ would cause two DHCPDISCOVERS to be sent back-to-back on startup.
+
+ Changes since 3.0 Beta 2 Patchlevel 15
+
+- Some documentation tweaks.
+
+- Maybe fix a problem in the DLPI code.
+
+- Fix some error code space inconsistencies in ddns update code.
+
+- Support relay agents that intercept unicast DHCP messages to stuff
+ agent options into them.
+
+- Fix a small memory leak in the relay agent option support code.
+
+- Fix a core dump that would occur if a packet was sent with no
+ options.
+
+ Changes since 3.0 Beta 2 Patchlevel 14
+
+- Finish fixing a long-standing bug in the agent options code. This
+ was causing core dumps and failing to operate correctly - in
+ particular, agent option stashing wasn't working. Agent option
+ stashing should now be working, meaning that agent options can be
+ used in class statements to control address allocation.
+
+- Fix up documentation.
+
+- Fix a couple of small memory leaks that would have added up
+ significantly in a high-demand situation.
+
+- Add a log-facility configuration parameter.
+
+- Fix a compile error on some older operating systems.
+
+- Add the ability in the client to execute certain statements before
+ transmitting packets to the server. Handy for debugging; not much
+ practical use otherwise.
+
+- Don't send faked-out giaddr when renewing or bound - again, useful
+ for debugging.
+
+ Changes since 3.0 Beta 2 Patchlevel 13
+
+- Fixed a problem where the fqdn decoder would sometimes try to store
+ an option with an (unsigned) negative length, resulting in a core
+ dump on some systems.
+
+- Work around the Win98 DHCP client, which NUL-terminates the FQDN
+ option.
+
+- Work around Win98 and Win2k clients that will claim they want to do
+ the update even when they don't have any way to do it.
+
+- Fix some log messages that can be printed when failover is operating
+ that were not printing enough information.
+
+- It was possible for a DHCPDISCOVER to get an allocation even when
+ the state machine said the server shouldn't be responding.
+
+- Don't load balance DHCPREQUESTs from clients in RENEWING and
+ REBINDING, since in RENEWING, if we heard it, it's for us, and in
+ REBINDING, the client wouldn't have got to REBINDING if its primary
+ were answering.
+
+- When we get a bogus state lease binding state transition, don't do
+ the transition.
+
+
+ Changes since 3.0 Beta 2 Patchlevel 12
+
+- Fixed a couple of silly compile errors.
+
+ Changes since 3.0 Beta 2 Patchlevel 11
+
+- Albert Herranz tracked down and fixed a subtle bug in the base64
+ decoder that would prevent any key with an 'x' in its base64
+ representation from working correctly.
+
+- Thanks to Chris Cheney and Michael Sanders, we have a fix for the
+ hang that they both spotted in the DHCP server - when
+ one-lease-per-client was set, the code to release the "other" lease
+ could spin.
+
+- Fix a problem with alignment of the input buffer in bpf in cases
+ where two packets arrive in the same bpf read.
+
+- Fix a problem where the relay agent would crash if you specified an
+ interface name on the command line.
+
+- Add the ability to conditionalize client behaviour based on the
+ client state.
+
+- Add support for the FQDN option, and added support for a new way of
+ doing ddns updates (ddns update style interim) that allows more than
+ one DHCP server to update the DNS for the same network(s). This
+ was implemented by Damien Neil with some additional functionality
+ added by Ted Lemon.
+
+- Damien added a "log" statement, so that the configuration file can
+ be made to log debugging information and other information.
+
+- Fixed a bug that caused option buffers not to be terminated with an
+ end option.
+
+- Fixed a long-standing bug in the support for option spaces where the
+ options are stored as an ordered list rather than in a hash table,
+ which could theoretically result in memory pool corruption.
+
+- Prevent hardware declarations with no actual hardware address from
+ being written as something unparsable, and behave correctly in the
+ face of a null hardware address on input.
+
+- Allow key names to be FQDNs, and qualify the algorithm name if it is
+ specified unqualified.
+
+- Modify the DDNS update code so that it never prints the "resolver
+ failed" message, but instead says *why* the resolver failed.
+
+- Officially support the subnet selection option, which now has an
+ RFC.
+
+- Fix a build bug on MacOS X.
+
+- Allow administrator to disable ping checking.
+
+- Clean up dhcpd.conf documentation and add more information about how
+ it works.
+
+ Changes since 3.0 Beta 2 Patchlevel 10
+
+- Fix a bug introduced during debugging (!) and accidentally committed
+ to CVS.
+
+ Changes since 3.0 Beta 2 Patchlevel 9
+
+- Fix DHCP client handling of vendor encapsulated options.
+
+- Fix a bug in the handling of relay agent information options introduced
+ in patchlevel 9.
+
+- Stash agent options on client leases by default, and use the stashed
+ options at renewal time.
+
+- Add the ability to test the client's binding state in the client
+ configuration language.
+
+- Fix a core dump in the DNS update code.
+
+- Fix some expression evaluation bugs that were causing updates to be
+ done when no client hostname was received.
+
+- Fix expression evaluation debugging printfs.
+
+- Teach pretty_print_option to print options in option spaces other than
+ the DHCP option space.
+
+- Add a warning message if the RHS of a not is not boolean.
+
+- Never select for more than a day, because some implementations of
+ select will just fail if the timeout is too long (!).
+
+- Fix a case where a DHCPDISCOVER from an unknown network would be
+ silently dropped.
+
+- Fix a bug where if a client requested an IP address for which a different
+ client had the lease, the DHCP server would reallocate it anyway.
+
+- Fix the DNS update code so that if the client changes its name, the DNS
+ will be correctly updated.
+
+ Changes since 3.0 Beta 2 Patchlevel 8
+
+- Oops, there was another subtle math error in the header-length
+ bounds-checking.
+
+ Changes since 3.0 Beta 2 Patchlevel 7
+
+- Oops, forgot to byte-swap udp header length before bounds-checking it.
+
+ Changes since 3.0 Beta 2 Patchlevel 6
+
+- Fix a possible DoS attack where a client could cause the checksummer
+ to dump core. This was a read, not a write, so it shouldn't be
+ possible to exploit it any further than that.
+
+- Implement client- and server-side support for using the Client FQDN
+ option.
+
+- Support for other option spaces in the client has been added. This
+ means that it is now possible to define a vendor option space on the
+ client, request options in that space from the server (which must
+ define the same option space), and then use those options in the
+ client. This also allows NWIP and Client FQDN options to be used
+ meaningfully.
+
+- Add object initializer support. This means that objects can now be
+ initialized to something other than all-zeros when allocated, which
+ makes, e.g., the interface object support code a little more robust.
+
+- Fix an off-by-one bug in the host stuffer. This was causing host
+ deletes not the work, and may also have been causing OMAPI
+ connections to get dropped. Thanks to James Brister for tracking
+ this one down!
+
+- Fixed a core dump in the interface discovery code that is triggered
+ when there is no subnet declaration for an interface, but the server
+ decides to continue running. Thanks to Shane Kerr for tracking
+ down and fixing this problem.
+
+ Changes since 3.0 Beta 2 Patchlevel 5
+
+- Fix a bug in the recent enhancement to the interface discovery code
+ to support arbitrary-length interface lists.
+
+- Support NUL-terminated DHCP options when initializing client-script
+ environment.
+
+- Fix suffix operator.
+
+- Fix NetWare/IP option parsing.
+
+- Better error/status checking in dhcpctl initialization and omapi
+ connection code.
+
+- Fix a potential memory smash in dhcpctl code.
+
+- Fix SunOS4 and (maybe) Ultrix builds.
+
+- Fix a bug where a certain sort of incoming packet could cause a core
+ dump on Solaris (and probably elsewhere).
+
+- Add some more safety checks in error logging code.
+
+- Add support for ISC_R_INCOMPLETE in OMAPI protocol connection code.
+
+- Fix relay agent so that if an interface is specified on the command
+ line, the relay agent does not dump core.
+
+- Fix class matching so that match if can be combined with match or
+ spawn with.
+
+- Do not allow spurious leases in the lease database to introduce
+ potentially bogus leases into the in-memory database.
+
+- Fix a byte-order problem in the client hardware address type code
+ for OMAPI.
+
+- Be slightly less picky about what sort of hardware addresses OMAPI
+ can install in host declarations.
+
+ Changes since 3.0 Beta 2 Patchlevel 4
+
+- Incorporated Peter Marschall's proposed change to array/record
+ parsing, which allows things like the slp-agent option to be encoded
+ correctly. Thanks very much to Peter for taking the initiative to
+ do this, and for doing such a careful job of it (e.g., updating the
+ comments)!
+
+- Added an encoding for the slp-agent option. :')
+
+- Fixed SunOS 4 build. Thanks to Robert Elz for responding to my
+ request for help on this with patches!
+
+- Incorporated a change that should fix a problem reported by Philippe
+ Jumelle where when the network connection between two servers is
+ lost, they never reconnect.
+
+- Fix client script files other than that for NetBSD to actually use
+ make_resolv_conf as documented in the manual page.
+
+- Fix a bug in the packet handling code that could result in a core
+ dump.
+
+- Fix a bug in the bootp code where responses on the local net would
+ be sent to the wrong MAC address. Thanks to Jerry Schave for
+ catching this one.
+
+ Changes since 3.0 Beta 2 Patchlevel 3
+
+- In the DHCP client, execute client statements prior to using the values
+ of options, so that the client configuration can overridden, e.g., the
+ lease renewal time.
+
+- Fix a reference counting error that would result in very reproducible
+ failures in updates, as well as occasional core dumps, if a zone was
+ declared without a key.
+
+- Fix some Linux 2.0 compilation problems.
+
+- Fix a bug in scope evaluation during execution of "on" statements that
+ caused values not to be recorded on leases.
+
+- If the dhcp-max-message-size option is specified in scope, and the
+ client didn't send this option, use the one specified in scope to
+ determine the maximum size of the response.
+
+ Changes since 3.0 Beta 2 Patchlevel 2
+
+- Fix a case where spawning subclasses were being allocated
+ incorrectly, resulting in a core dump.
+
+- Fix a case where the DHCP server might inappropriately NAK a
+ RENEWING client.
+
+- Fix a place dhcprequest() where static leases could leak.
+
+- Include memory.h in omapip_p.h so that we don't get warnings about
+ using memcmp().
+
+ Changes since 3.0 Beta 2 Patchlevel 1
+
+- Notice when SIOCFIGCONF returns more data than fit in the buffer -
+ allocate a larger buffer, and retry. Thanks to Greg Fausak for
+ pointing this out.
+
+- In the server, if no interfaces were configured, report an error and
+ exit.
+
+- Don't ever record a state of 'startup'.
+
+- Don't try to evaluate the local failover binding address if none was
+ specified. Thanks to Joseph Breu for finding this.
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..10de37b
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,868 @@
+# generated automatically by aclocal 1.10.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(AC_AUTOCONF_VERSION, [2.67],,
+[m4_warning([this file was generated for autoconf 2.67.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.10'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.10.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.10.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(AC_AUTOCONF_VERSION)])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ case $depmode in
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ none) break ;;
+ esac
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this.
+ if depmode=$depmode \
+ source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 3
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[for mf in $CONFIG_FILES; do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+done
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 13
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.60])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 3
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+ am__include=include
+ am__quote=
+ _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ fi
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 3
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t $srcdir/configure conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/bind/Makefile b/bind/Makefile
new file mode 100644
index 0000000..030759c
--- /dev/null
+++ b/bind/Makefile
@@ -0,0 +1,75 @@
+#
+# Copyright (C) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.bind,v 1.2.2.7 2011-04-06 22:00:40 marka Exp $
+
+# Configure and build the bind libraries for use by DHCP
+
+include ./version.tmp
+version=${MAJORVER}.${MINORVER}.${PATCHVER}${RELEASETYPE}${RELEASEVER}
+
+# bindvar.tmp is constructed by configure, it has the paths for
+# if GMAKE is blank the shell script couldn't find a gmake to use.
+# binddir=
+# GMAKE=
+include ./bindvar.tmp
+
+bindsrcdir=bind-${version}
+
+all:
+# Extract the source from the tarball, if it hasn't been already.
+ @if test -d ${bindsrcdir} ; then \
+ echo ${bindsrcdir} already unpacked... ; \
+ else \
+ gunzip -c bind.tar.gz | tar xf - ; \
+ fi
+
+ @if test -z "${GMAKE}"; then \
+ echo "unable to find gmake" 1>&2 ; \
+ exit 1; \
+ fi
+
+# Configure the export libraries
+# Currently disable the epoll and devpoll options as they don't interact
+# well with the DHCP code.
+ @echo Configuring BIND Export libraries for DHCP.
+ @(cd ${bindsrcdir} && ./configure --disable-kqueue --disable-epoll --disable-devpoll --without-openssl --without-libxml2 --enable-exportlib --enable-threads=no --with-export-includedir=${binddir}/include --with-export-libdir=${binddir}/lib --with-gssapi=no > ${binddir}/configure.log)
+
+# Build the export libraries
+ @echo Building BIND Export libraries - this takes some time.
+ @(cd ${bindsrcdir}/lib/export ; \
+ echo building in `pwd` ; \
+ MAKE=${GMAKE} ${GMAKE} > ${binddir}/build.log)
+
+ @echo Installing BIND Export libraries to ${binddir}.
+ @(cd ${bindsrcdir}/lib/export ; \
+ MAKE=${GMAKE} ${GMAKE} install > ${binddir}/install.log)
+
+clean:
+ @echo Cleaning BIND export library.
+ rm -rf ${bindsrcdir} ./lib ./include ./configure.log ./build.log \
+ ./install.log
+
+# Include the following so that this Makefile is happy when the parent
+# tries to use them.
+
+distdir:
+
+distclean:
+
+install:
+
+check:
+
diff --git a/bind/bind.tar.gz b/bind/bind.tar.gz
new file mode 100644
index 0000000..af7e251
--- /dev/null
+++ b/bind/bind.tar.gz
Binary files differ
diff --git a/bind/version.tmp b/bind/version.tmp
new file mode 100644
index 0000000..1d9fbd2
--- /dev/null
+++ b/bind/version.tmp
@@ -0,0 +1,10 @@
+# $Id: version,v 1.53.8.2.2.4 2011-06-21 20:44:01 each Exp $
+#
+# This file must follow /bin/sh rules. It is imported directly via
+# configure.
+#
+MAJORVER=9
+MINORVER=8
+PATCHVER=0
+RELEASETYPE=-P
+RELEASEVER=4
diff --git a/client/Makefile.am b/client/Makefile.am
new file mode 100644
index 0000000..57c6ac9
--- /dev/null
+++ b/client/Makefile.am
@@ -0,0 +1,18 @@
+dist_sysconf_DATA = dhclient.conf
+sbin_PROGRAMS = dhclient
+dhclient_SOURCES = clparse.c dhclient.c dhc6.c \
+ scripts/bsdos scripts/freebsd scripts/linux scripts/macos \
+ scripts/netbsd scripts/nextstep scripts/openbsd \
+ scripts/solaris scripts/openwrt
+dhclient_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+man_MANS = dhclient.8 dhclient-script.8 dhclient.conf.5 dhclient.leases.5
+EXTRA_DIST = $(man_MANS)
+
+dhclient.o: dhclient.c
+ $(COMPILE) -DCLIENT_PATH='"PATH=$(sbindir):/sbin:/bin:/usr/sbin:/usr/bin"' \
+ -DLOCALSTATEDIR='"$(localstatedir)"' -c dhclient.c
+
+dhc6.o: dhc6.c
+ $(COMPILE) -DCLIENT_PATH='"PATH=$(sbindir):/sbin:/bin:/usr/sbin:/usr/bin"' \
+ -DLOCALSTATEDIR='"$(localstatedir)"' -c dhc6.c
diff --git a/client/Makefile.in b/client/Makefile.in
new file mode 100644
index 0000000..581398e
--- /dev/null
+++ b/client/Makefile.in
@@ -0,0 +1,561 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+sbin_PROGRAMS = dhclient$(EXEEXT)
+subdir = client
+DIST_COMMON = $(dist_sysconf_DATA) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(sysconfdir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(sbin_PROGRAMS)
+am_dhclient_OBJECTS = clparse.$(OBJEXT) dhclient.$(OBJEXT) \
+ dhc6.$(OBJEXT)
+dhclient_OBJECTS = $(am_dhclient_OBJECTS)
+dhclient_DEPENDENCIES = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(dhclient_SOURCES)
+DIST_SOURCES = $(dhclient_SOURCES)
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+dist_sysconfDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(dist_sysconf_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+dist_sysconf_DATA = dhclient.conf
+dhclient_SOURCES = clparse.c dhclient.c dhc6.c \
+ scripts/bsdos scripts/freebsd scripts/linux scripts/macos \
+ scripts/netbsd scripts/nextstep scripts/openbsd \
+ scripts/solaris scripts/openwrt
+
+dhclient_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+
+man_MANS = dhclient.8 dhclient-script.8 dhclient.conf.5 dhclient.leases.5
+EXTRA_DIST = $(man_MANS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign client/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign client/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+dhclient$(EXEEXT): $(dhclient_OBJECTS) $(dhclient_DEPENDENCIES)
+ @rm -f dhclient$(EXEEXT)
+ $(LINK) $(dhclient_OBJECTS) $(dhclient_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clparse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhc6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhclient.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man5: $(man5_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+install-dist_sysconfDATA: $(dist_sysconf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(sysconfdir)" || $(MKDIR_P) "$(DESTDIR)$(sysconfdir)"
+ @list='$(dist_sysconf_DATA)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(dist_sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
+ $(dist_sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
+ done
+
+uninstall-dist_sysconfDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sysconf_DATA)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(sysconfdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-dist_sysconfDATA install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man5 install-man8
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_sysconfDATA uninstall-man \
+ uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man5 uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dist_sysconfDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man5 install-man8 install-pdf install-pdf-am \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-dist_sysconfDATA uninstall-man \
+ uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS
+
+
+dhclient.o: dhclient.c
+ $(COMPILE) -DCLIENT_PATH='"PATH=$(sbindir):/sbin:/bin:/usr/sbin:/usr/bin"' \
+ -DLOCALSTATEDIR='"$(localstatedir)"' -c dhclient.c
+
+dhc6.o: dhc6.c
+ $(COMPILE) -DCLIENT_PATH='"PATH=$(sbindir):/sbin:/bin:/usr/sbin:/usr/bin"' \
+ -DLOCALSTATEDIR='"$(localstatedir)"' -c dhc6.c
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/client/clparse.c b/client/clparse.c
new file mode 100644
index 0000000..9de4ce2
--- /dev/null
+++ b/client/clparse.c
@@ -0,0 +1,2235 @@
+/* clparse.c
+
+ Parser for dhclient config and lease files... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <errno.h>
+
+struct client_config top_level_config;
+
+#define NUM_DEFAULT_REQUESTED_OPTS 9
+struct option *default_requested_options[NUM_DEFAULT_REQUESTED_OPTS + 1];
+
+static void parse_client_default_duid(struct parse *cfile);
+static void parse_client6_lease_statement(struct parse *cfile);
+#ifdef DHCPv6
+static struct dhc6_ia *parse_client6_ia_na_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_ta_statement(struct parse *cfile);
+static struct dhc6_ia *parse_client6_ia_pd_statement(struct parse *cfile);
+static struct dhc6_addr *parse_client6_iaaddr_statement(struct parse *cfile);
+static struct dhc6_addr *parse_client6_iaprefix_statement(struct parse *cfile);
+#endif /* DHCPv6 */
+
+/* client-conf-file :== client-declarations END_OF_FILE
+ client-declarations :== <nil>
+ | client-declaration
+ | client-declarations client-declaration */
+
+isc_result_t read_client_conf ()
+{
+ struct client_config *config;
+ struct interface_info *ip;
+ struct parse *parse;
+ isc_result_t status;
+ unsigned code;
+
+ /* Initialize the default request list. */
+ memset(default_requested_options, 0, sizeof(default_requested_options));
+
+ /* 1 */
+ code = DHO_SUBNET_MASK;
+ option_code_hash_lookup(&default_requested_options[0],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 2 */
+ code = DHO_BROADCAST_ADDRESS;
+ option_code_hash_lookup(&default_requested_options[1],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 3 */
+ code = DHO_TIME_OFFSET;
+ option_code_hash_lookup(&default_requested_options[2],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 4 */
+ code = DHO_ROUTERS;
+ option_code_hash_lookup(&default_requested_options[3],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 5 */
+ code = DHO_DOMAIN_NAME;
+ option_code_hash_lookup(&default_requested_options[4],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 6 */
+ code = DHO_DOMAIN_NAME_SERVERS;
+ option_code_hash_lookup(&default_requested_options[5],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 7 */
+ code = DHO_HOST_NAME;
+ option_code_hash_lookup(&default_requested_options[6],
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* 8 */
+ code = D6O_NAME_SERVERS;
+ option_code_hash_lookup(&default_requested_options[7],
+ dhcpv6_universe.code_hash, &code, 0, MDL);
+
+ /* 9 */
+ code = D6O_DOMAIN_SEARCH;
+ option_code_hash_lookup(&default_requested_options[8],
+ dhcpv6_universe.code_hash, &code, 0, MDL);
+
+ for (code = 0 ; code < NUM_DEFAULT_REQUESTED_OPTS ; code++) {
+ if (default_requested_options[code] == NULL)
+ log_fatal("Unable to find option definition for "
+ "index %u during default parameter request "
+ "assembly.", code);
+ }
+
+ /* 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;
+
+ /*
+ * RFC 2131, section 4.4.1 specifies that the client SHOULD wait a
+ * random time between 1 and 10 seconds. However, we choose to not
+ * implement this default. If user is inclined to really have that
+ * delay, he is welcome to do so, using 'initial-delay X;' parameter
+ * in config file.
+ */
+ top_level_config.initial_delay = 0;
+
+ top_level_config.bootp_policy = P_ACCEPT;
+ top_level_config.script_name = path_dhclient_script;
+ top_level_config.requested_options = default_requested_options;
+ top_level_config.omapi_port = -1;
+ top_level_config.do_forward_update = 1;
+ /* Requested lease time, used by DHCPv6 (DHCPv4 uses the option cache)
+ */
+ top_level_config.requested_lease = 7200;
+
+ group_allocate (&top_level_config.on_receipt, MDL);
+ if (!top_level_config.on_receipt)
+ log_fatal ("no memory for top-level on_receipt group");
+
+ group_allocate (&top_level_config.on_transmission, MDL);
+ if (!top_level_config.on_transmission)
+ log_fatal ("no memory for top-level on_transmission group");
+
+ status = read_client_conf_file (path_dhclient_conf,
+ (struct interface_info *)0,
+ &top_level_config);
+
+ parse = NULL;
+ if (status != ISC_R_SUCCESS) {
+ ;
+#ifdef LATER
+ /* Set up the standard name service updater routine. */
+ status = new_parse(&parse, -1, default_client_config,
+ sizeof(default_client_config) - 1,
+ "default client configuration", 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("can't begin default client config!");
+ }
+
+ if (parse != NULL) {
+ do {
+ token = peek_token(&val, NULL, cfile);
+ if (token == END_OF_FILE)
+ break;
+ parse_client_statement(cfile, NULL, &top_level_config);
+ } while (1);
+ end_parse(&parse);
+#endif
+ }
+
+ /* Set up state and config structures for clients that don't
+ have per-interface configuration statements. */
+ config = (struct client_config *)0;
+ for (ip = interfaces; ip; ip = ip -> next) {
+ if (!ip -> client) {
+ ip -> client = (struct client_state *)
+ dmalloc (sizeof (struct client_state), MDL);
+ if (!ip -> client)
+ log_fatal ("no memory for client state.");
+ memset (ip -> client, 0, sizeof *(ip -> client));
+ ip -> client -> interface = ip;
+ }
+
+ if (!ip -> client -> config) {
+ if (!config) {
+ config = (struct client_config *)
+ dmalloc (sizeof (struct client_config),
+ MDL);
+ if (!config)
+ log_fatal ("no memory for client config.");
+ memcpy (config, &top_level_config,
+ sizeof top_level_config);
+ }
+ ip -> client -> config = config;
+ }
+ }
+ return status;
+}
+
+int read_client_conf_file (const char *name, struct interface_info *ip,
+ struct client_config *client)
+{
+ int file;
+ struct parse *cfile;
+ const char *val;
+ int token;
+ isc_result_t status;
+
+ if ((file = open (name, O_RDONLY)) < 0)
+ return uerr2isc (errno);
+
+ cfile = NULL;
+ status = new_parse(&cfile, file, NULL, 0, path_dhclient_conf, 0);
+ if (status != ISC_R_SUCCESS || cfile == NULL)
+ return status;
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE)
+ break;
+ parse_client_statement (cfile, ip, client);
+ } while (1);
+ token = next_token (&val, (unsigned *)0, cfile);
+ status = (cfile -> warnings_occurred
+ ? DHCP_R_BADPARSE
+ : ISC_R_SUCCESS);
+ end_parse (&cfile);
+ return status;
+}
+
+
+/* lease-file :== client-lease-statements END_OF_FILE
+ client-lease-statements :== <nil>
+ | client-lease-statements LEASE client-lease-statement */
+
+void read_client_leases ()
+{
+ int file;
+ isc_result_t status;
+ struct parse *cfile;
+ const char *val;
+ int token;
+
+ /* Open the lease file. If we can't open it, just return -
+ we can safely trust the server to remember our state. */
+ if ((file = open (path_dhclient_db, O_RDONLY)) < 0)
+ return;
+
+ cfile = NULL;
+ status = new_parse(&cfile, file, NULL, 0, path_dhclient_db, 0);
+ if (status != ISC_R_SUCCESS || cfile == NULL)
+ return;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE)
+ break;
+
+ switch (token) {
+ case DEFAULT_DUID:
+ parse_client_default_duid(cfile);
+ break;
+
+ case LEASE:
+ parse_client_lease_statement(cfile, 0);
+ break;
+
+ case LEASE6:
+ parse_client6_lease_statement(cfile);
+ break;
+
+ default:
+ log_error ("Corrupt lease file - possible data loss!");
+ skip_to_semi (cfile);
+ break;
+ }
+ } while (1);
+
+ end_parse (&cfile);
+}
+
+/* client-declaration :==
+ SEND option-decl |
+ DEFAULT option-decl |
+ SUPERSEDE option-decl |
+ PREPEND option-decl |
+ APPEND option-decl |
+ hardware-declaration |
+ ALSO REQUEST option-list |
+ ALSO REQUIRE option-list |
+ REQUEST option-list |
+ REQUIRE option-list |
+ TIMEOUT number |
+ RETRY number |
+ REBOOT number |
+ SELECT_TIMEOUT number |
+ SCRIPT string |
+ VENDOR_SPACE string |
+ interface-declaration |
+ LEASE client-lease-statement |
+ ALIAS client-lease-statement |
+ KEY key-definition */
+
+void parse_client_statement (cfile, ip, config)
+ struct parse *cfile;
+ struct interface_info *ip;
+ struct client_config *config;
+{
+ int token;
+ const char *val;
+ struct option *option = NULL;
+ struct executable_statement *stmt;
+ int lose;
+ char *name;
+ enum policy policy;
+ int known;
+ int tmp, i;
+ isc_result_t status;
+ struct option ***append_list, **new_list, **cat_list;
+
+ switch (peek_token (&val, (unsigned *)0, cfile)) {
+ case INCLUDE:
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "filename string expected.");
+ skip_to_semi (cfile);
+ } else {
+ status = read_client_conf_file (val, ip, config);
+ if (status != ISC_R_SUCCESS)
+ parse_warn (cfile, "%s: bad parse.", val);
+ parse_semi (cfile);
+ }
+ return;
+
+ case KEY:
+ next_token (&val, (unsigned *)0, cfile);
+ if (ip) {
+ /* This may seem arbitrary, but there's a reason for
+ doing it: the authentication key database is not
+ scoped. If we allow the user to declare a key other
+ than in the outer scope, the user is very likely to
+ believe that the key will only be used in that
+ scope. If the user only wants the key to be used on
+ one interface, because it's known that the other
+ interface may be connected to an insecure net and
+ the secret key is considered sensitive, we don't
+ want to lull them into believing they've gotten
+ their way. This is a bit contrived, but people
+ tend not to be entirely rational about security. */
+ parse_warn (cfile, "key definition not allowed here.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_key (cfile);
+ return;
+
+ case TOKEN_ALSO:
+ /* consume ALSO */
+ next_token(&val, NULL, cfile);
+
+ /* consume type of ALSO list. */
+ token = next_token(&val, NULL, cfile);
+
+ if (token == REQUEST) {
+ append_list = &config->requested_options;
+ } else if (token == REQUIRE) {
+ append_list = &config->required_options;
+ } else {
+ parse_warn(cfile, "expected REQUEST or REQUIRE list");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /* If there is no list, cut the concat short. */
+ if (*append_list == NULL) {
+ parse_option_list(cfile, append_list);
+ return;
+ }
+
+ /* Count the length of the existing list. */
+ for (i = 0 ; (*append_list)[i] != NULL ; i++)
+ ; /* This space intentionally left blank. */
+
+ /* If there's no codes on the list, cut the concat short. */
+ if (i == 0) {
+ parse_option_list(cfile, append_list);
+ return;
+ }
+
+ tmp = parse_option_list(cfile, &new_list);
+
+ if (tmp == 0 || new_list == NULL)
+ return;
+
+ /* Allocate 'i + tmp' buckets plus a terminator. */
+ cat_list = dmalloc(sizeof(struct option *) * (i + tmp + 1),
+ MDL);
+
+ if (cat_list == NULL) {
+ log_error("Unable to allocate memory for new "
+ "request list.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ for (i = 0 ; (*append_list)[i] != NULL ; i++)
+ option_reference(&cat_list[i], (*append_list)[i], MDL);
+
+ tmp = i;
+
+ for (i = 0 ; new_list[i] != 0 ; i++)
+ option_reference(&cat_list[tmp++], new_list[i], MDL);
+
+ cat_list[tmp] = 0;
+
+ /* XXX: We cannot free the old list, because it may have been
+ * XXX: assigned from an outer configuration scope (or may be
+ * XXX: the static default setting).
+ */
+ *append_list = cat_list;
+
+ return;
+
+ /* REQUIRE can either start a policy statement or a
+ comma-separated list of names of required options. */
+ case REQUIRE:
+ next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == AUTHENTICATION) {
+ policy = P_REQUIRE;
+ goto do_policy;
+ }
+ parse_option_list (cfile, &config -> required_options);
+ return;
+
+ case IGNORE:
+ next_token (&val, (unsigned *)0, cfile);
+ policy = P_IGNORE;
+ goto do_policy;
+
+ case ACCEPT:
+ next_token (&val, (unsigned *)0, cfile);
+ policy = P_ACCEPT;
+ goto do_policy;
+
+ case PREFER:
+ next_token (&val, (unsigned *)0, cfile);
+ policy = P_PREFER;
+ goto do_policy;
+
+ case DONT:
+ next_token (&val, (unsigned *)0, cfile);
+ policy = P_DONT;
+ goto do_policy;
+
+ do_policy:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == AUTHENTICATION) {
+ if (policy != P_PREFER &&
+ policy != P_REQUIRE &&
+ policy != P_DONT) {
+ parse_warn (cfile,
+ "invalid authentication policy.");
+ skip_to_semi (cfile);
+ return;
+ }
+ config -> auth_policy = policy;
+ } else if (token != TOKEN_BOOTP) {
+ if (policy != P_PREFER &&
+ policy != P_IGNORE &&
+ policy != P_ACCEPT) {
+ parse_warn (cfile, "invalid bootp policy.");
+ skip_to_semi (cfile);
+ return;
+ }
+ config -> bootp_policy = policy;
+ } else {
+ parse_warn (cfile, "expecting a policy type.");
+ skip_to_semi (cfile);
+ return;
+ }
+ break;
+
+ case OPTION:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SPACE) {
+ if (ip) {
+ parse_warn (cfile,
+ "option space definitions %s",
+ " may not be scoped.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_option_space_decl (cfile);
+ return;
+ }
+
+ known = 0;
+ status = parse_option_name(cfile, 1, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL)
+ return;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != CODE) {
+ parse_warn (cfile, "expecting \"code\" keyword.");
+ skip_to_semi (cfile);
+ option_dereference(&option, MDL);
+ return;
+ }
+ if (ip) {
+ parse_warn (cfile,
+ "option definitions may only appear in %s",
+ "the outermost scope.");
+ skip_to_semi (cfile);
+ option_dereference(&option, MDL);
+ return;
+ }
+
+ /*
+ * If the option was known, remove it from the code and name
+ * hash tables before redefining it.
+ */
+ if (known) {
+ option_name_hash_delete(option->universe->name_hash,
+ option->name, 0, MDL);
+ option_code_hash_delete(option->universe->code_hash,
+ &option->code, 0, MDL);
+ }
+
+ parse_option_code_definition(cfile, option);
+ option_dereference(&option, MDL);
+ return;
+
+ case MEDIA:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_string_list (cfile, &config -> media, 1);
+ return;
+
+ case HARDWARE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (ip) {
+ parse_hardware_param (cfile, &ip -> hw_address);
+ } else {
+ parse_warn (cfile, "hardware address parameter %s",
+ "not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return;
+
+ case ANYCAST_MAC:
+ token = next_token(&val, NULL, cfile);
+ if (ip != NULL) {
+ parse_hardware_param(cfile, &ip->anycast_mac_addr);
+ } else {
+ parse_warn(cfile, "anycast mac address parameter "
+ "not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return;
+
+ case REQUEST:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (config -> requested_options == default_requested_options)
+ config -> requested_options = NULL;
+ parse_option_list (cfile, &config -> requested_options);
+ return;
+
+ case TIMEOUT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> timeout);
+ return;
+
+ case RETRY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> retry_interval);
+ return;
+
+ case SELECT_TIMEOUT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> select_interval);
+ return;
+
+ case OMAPI:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != PORT) {
+ parse_warn (cfile,
+ "unexpected omapi subtype: %s", val);
+ skip_to_semi (cfile);
+ return;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "invalid port number: `%s'", val);
+ skip_to_semi (cfile);
+ return;
+ }
+ tmp = atoi (val);
+ if (tmp < 0 || tmp > 65535)
+ parse_warn (cfile, "invalid omapi port %d.", tmp);
+ else if (config != &top_level_config)
+ parse_warn (cfile,
+ "omapi port only works at top level.");
+ else
+ config -> omapi_port = tmp;
+ parse_semi (cfile);
+ return;
+
+ case DO_FORWARD_UPDATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!strcasecmp (val, "on") ||
+ !strcasecmp (val, "true"))
+ config -> do_forward_update = 1;
+ else if (!strcasecmp (val, "off") ||
+ !strcasecmp (val, "false"))
+ config -> do_forward_update = 0;
+ else {
+ parse_warn (cfile, "expecting boolean value.");
+ skip_to_semi (cfile);
+ return;
+ }
+ parse_semi (cfile);
+ return;
+
+ case REBOOT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> reboot_timeout);
+ return;
+
+ case BACKOFF_CUTOFF:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> backoff_cutoff);
+ return;
+
+ case INITIAL_INTERVAL:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> initial_interval);
+ return;
+
+ case INITIAL_DELAY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_lease_time (cfile, &config -> initial_delay);
+ return;
+
+ case SCRIPT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_string (cfile, &config -> script_name, (unsigned *)0);
+ return;
+
+ case VENDOR:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != OPTION) {
+ parse_warn (cfile, "expecting 'vendor option space'");
+ skip_to_semi (cfile);
+ return;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SPACE) {
+ parse_warn (cfile, "expecting 'vendor option space'");
+ skip_to_semi (cfile);
+ return;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "expecting an identifier.");
+ skip_to_semi (cfile);
+ return;
+ }
+ config -> vendor_space_name = dmalloc (strlen (val) + 1, MDL);
+ if (!config -> vendor_space_name)
+ log_fatal ("no memory for vendor option space name.");
+ strcpy (config -> vendor_space_name, val);
+ for (i = 0; i < universe_count; i++)
+ if (!strcmp (universes [i] -> name,
+ config -> vendor_space_name))
+ break;
+ if (i == universe_count) {
+ log_error ("vendor option space %s not found.",
+ config -> vendor_space_name);
+ }
+ parse_semi (cfile);
+ return;
+
+ case INTERFACE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (ip)
+ parse_warn (cfile, "nested interface declaration.");
+ parse_interface_declaration (cfile, config, (char *)0);
+ return;
+
+ case PSEUDO:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ name = dmalloc (strlen (val) + 1, MDL);
+ if (!name)
+ log_fatal ("no memory for pseudo interface name");
+ strcpy (name, val);
+ parse_interface_declaration (cfile, config, name);
+ return;
+
+ case LEASE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_client_lease_statement (cfile, 1);
+ return;
+
+ case ALIAS:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_client_lease_statement (cfile, 2);
+ return;
+
+ case REJECT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_reject_statement (cfile, config);
+ return;
+
+ default:
+ lose = 0;
+ stmt = (struct executable_statement *)0;
+ if (!parse_executable_statement (&stmt,
+ cfile, &lose, context_any)) {
+ if (!lose) {
+ parse_warn (cfile, "expecting a statement.");
+ skip_to_semi (cfile);
+ }
+ } else {
+ struct executable_statement **eptr, *sptr;
+ if (stmt &&
+ (stmt -> op == send_option_statement ||
+ (stmt -> op == on_statement &&
+ (stmt -> data.on.evtypes & ON_TRANSMISSION)))) {
+ eptr = &config -> on_transmission -> statements;
+ if (stmt -> op == on_statement) {
+ sptr = (struct executable_statement *)0;
+ executable_statement_reference
+ (&sptr,
+ stmt -> data.on.statements, MDL);
+ executable_statement_dereference (&stmt,
+ MDL);
+ executable_statement_reference (&stmt,
+ sptr,
+ MDL);
+ executable_statement_dereference (&sptr,
+ MDL);
+ }
+ } else
+ eptr = &config -> on_receipt -> statements;
+
+ if (stmt) {
+ for (; *eptr; eptr = &(*eptr) -> next)
+ ;
+ executable_statement_reference (eptr,
+ stmt, MDL);
+ }
+ return;
+ }
+ break;
+ }
+ parse_semi (cfile);
+}
+
+/* option-list :== option_name |
+ option_list COMMA option_name */
+
+int
+parse_option_list(struct parse *cfile, struct option ***list)
+{
+ int ix;
+ int token;
+ const char *val;
+ pair p = (pair)0, q = (pair)0, r;
+ struct option *option = NULL;
+ isc_result_t status;
+
+ ix = 0;
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ break;
+ }
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "%s: expected option name.", val);
+ token = next_token (&val, (unsigned *)0, cfile);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ status = parse_option_name(cfile, 0, NULL, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ parse_warn (cfile, "%s: expected option name.", val);
+ return 0;
+ }
+ r = new_pair (MDL);
+ if (!r)
+ log_fatal ("can't allocate pair for option code.");
+ /* XXX: we should probably carry a reference across this */
+ r->car = (caddr_t)option;
+ option_dereference(&option, MDL);
+ r -> cdr = (pair)0;
+ if (p)
+ q -> cdr = r;
+ else
+ p = r;
+ q = r;
+ ++ix;
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ /* XXX we can't free the list here, because we may have copied
+ XXX it from an outer config state. */
+ *list = NULL;
+ if (ix) {
+ *list = dmalloc ((ix + 1) * sizeof(struct option *), MDL);
+ if (!*list)
+ log_error ("no memory for option list.");
+ else {
+ ix = 0;
+ for (q = p; q; q = q -> cdr)
+ option_reference(&(*list)[ix++],
+ (struct option *)q->car, MDL);
+ (*list)[ix] = NULL;
+ }
+ while (p) {
+ q = p -> cdr;
+ free_pair (p, MDL);
+ p = q;
+ }
+ }
+
+ return ix;
+}
+
+/* interface-declaration :==
+ INTERFACE string LBRACE client-declarations RBRACE */
+
+void parse_interface_declaration (cfile, outer_config, name)
+ struct parse *cfile;
+ struct client_config *outer_config;
+ char *name;
+{
+ int token;
+ const char *val;
+ struct client_state *client, **cp;
+ struct interface_info *ip = (struct interface_info *)0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting interface name (in quotes).");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ if (!interface_or_dummy (&ip, val))
+ log_fatal ("Can't allocate interface %s.", val);
+
+ /* If we were given a name, this is a pseudo-interface. */
+ if (name) {
+ make_client_state (&client);
+ client -> name = name;
+ client -> interface = ip;
+ for (cp = &ip -> client; *cp; cp = &((*cp) -> next))
+ ;
+ *cp = client;
+ } else {
+ if (!ip -> client) {
+ make_client_state (&ip -> client);
+ ip -> client -> interface = ip;
+ }
+ client = ip -> client;
+ }
+
+ if (!client -> config)
+ make_client_config (client, outer_config);
+
+ ip -> flags &= ~INTERFACE_AUTOMATIC;
+ interfaces_requested = 1;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE) {
+ parse_warn (cfile,
+ "unterminated interface declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_statement (cfile, ip, client -> config);
+ } while (1);
+ token = next_token (&val, (unsigned *)0, cfile);
+}
+
+int interface_or_dummy (struct interface_info **pi, const char *name)
+{
+ struct interface_info *i;
+ struct interface_info *ip = (struct interface_info *)0;
+ isc_result_t status;
+
+ /* Find the interface (if any) that matches the name. */
+ for (i = interfaces; i; i = i -> next) {
+ if (!strcmp (i -> name, name)) {
+ interface_reference (&ip, i, MDL);
+ break;
+ }
+ }
+
+ /* If it's not a real interface, see if it's on the dummy list. */
+ if (!ip) {
+ for (ip = dummy_interfaces; ip; ip = ip -> next) {
+ if (!strcmp (ip -> name, name)) {
+ interface_reference (&ip, i, MDL);
+ break;
+ }
+ }
+ }
+
+ /* If we didn't find an interface, make a dummy interface as
+ a placeholder. */
+ if (!ip) {
+ if ((status = interface_allocate (&ip, MDL)) != ISC_R_SUCCESS)
+ log_fatal ("Can't record interface %s: %s",
+ name, isc_result_totext (status));
+
+ if (strlen(name) >= sizeof(ip->name)) {
+ interface_dereference(&ip, MDL);
+ return 0;
+ }
+ strcpy(ip->name, name);
+
+ if (dummy_interfaces) {
+ interface_reference (&ip -> next,
+ dummy_interfaces, MDL);
+ interface_dereference (&dummy_interfaces, MDL);
+ }
+ interface_reference (&dummy_interfaces, ip, MDL);
+ }
+ if (pi)
+ status = interface_reference (pi, ip, MDL);
+ else
+ status = ISC_R_FAILURE;
+ interface_dereference (&ip, MDL);
+ if (status != ISC_R_SUCCESS)
+ return 0;
+ return 1;
+}
+
+void make_client_state (state)
+ struct client_state **state;
+{
+ *state = ((struct client_state *)dmalloc (sizeof **state, MDL));
+ if (!*state)
+ log_fatal ("no memory for client state\n");
+ memset (*state, 0, sizeof **state);
+}
+
+void make_client_config (client, config)
+ struct client_state *client;
+ struct client_config *config;
+{
+ client -> config = (((struct client_config *)
+ dmalloc (sizeof (struct client_config), MDL)));
+ if (!client -> config)
+ log_fatal ("no memory for client config\n");
+ memcpy (client -> config, config, sizeof *config);
+ if (!clone_group (&client -> config -> on_receipt,
+ config -> on_receipt, MDL) ||
+ !clone_group (&client -> config -> on_transmission,
+ config -> on_transmission, MDL))
+ log_fatal ("no memory for client state groups.");
+}
+
+/* client-lease-statement :==
+ LBRACE client-lease-declarations RBRACE
+
+ client-lease-declarations :==
+ <nil> |
+ client-lease-declaration |
+ client-lease-declarations client-lease-declaration */
+
+
+void parse_client_lease_statement (cfile, is_static)
+ struct parse *cfile;
+ int is_static;
+{
+ struct client_lease *lease, *lp, *pl, *next;
+ struct interface_info *ip = (struct interface_info *)0;
+ int token;
+ const char *val;
+ struct client_state *client = (struct client_state *)0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ lease = ((struct client_lease *)
+ dmalloc (sizeof (struct client_lease), MDL));
+ if (!lease)
+ log_fatal ("no memory for lease.\n");
+ memset (lease, 0, sizeof *lease);
+ lease -> is_static = is_static;
+ if (!option_state_allocate (&lease -> options, MDL))
+ log_fatal ("no memory for lease options.\n");
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE) {
+ parse_warn (cfile, "unterminated lease declaration.");
+ return;
+ }
+ if (token == RBRACE)
+ break;
+ parse_client_lease_declaration (cfile, lease, &ip, &client);
+ } while (1);
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* If the lease declaration didn't include an interface
+ declaration that we recognized, it's of no use to us. */
+ if (!ip) {
+ destroy_client_lease (lease);
+ return;
+ }
+
+ /* Make sure there's a client state structure... */
+ if (!ip -> client) {
+ make_client_state (&ip -> client);
+ ip -> client -> interface = ip;
+ }
+ if (!client)
+ client = ip -> client;
+
+ /* 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 = (struct client_lease *)0;
+ for (lp = client -> leases; lp; lp = next) {
+ next = lp -> next;
+ if (lp -> address.len == lease -> address.len &&
+ !memcmp (lp -> address.iabuf, lease -> address.iabuf,
+ lease -> address.len)) {
+ if (pl)
+ pl -> next = next;
+ else
+ client -> leases = next;
+ destroy_client_lease (lp);
+ break;
+ } else
+ pl = lp;
+ }
+
+ /* 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 = client -> leases;
+ 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 (client -> active) {
+ if (client -> active -> expiry < cur_time)
+ destroy_client_lease (client -> active);
+ else if (client -> active -> address.len ==
+ lease -> address.len &&
+ !memcmp (client -> active -> address.iabuf,
+ lease -> address.iabuf,
+ lease -> address.len))
+ destroy_client_lease (client -> active);
+ else {
+ client -> active -> next = client -> leases;
+ client -> leases = client -> active;
+ }
+ }
+ 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 |
+ KEY id */
+
+void parse_client_lease_declaration (cfile, lease, ipp, clientp)
+ struct parse *cfile;
+ struct client_lease *lease;
+ struct interface_info **ipp;
+ struct client_state **clientp;
+{
+ int token;
+ const char *val;
+ struct interface_info *ip;
+ struct option_cache *oc;
+ struct client_state *client = (struct client_state *)0;
+
+ switch (next_token (&val, (unsigned *)0, cfile)) {
+ case KEY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING && !is_identifier (token)) {
+ parse_warn (cfile, "expecting key name.");
+ skip_to_semi (cfile);
+ break;
+ }
+ if (omapi_auth_key_lookup_name (&lease -> key, val) !=
+ ISC_R_SUCCESS)
+ parse_warn (cfile, "unknown key %s", val);
+ parse_semi (cfile);
+ break;
+ case TOKEN_BOOTP:
+ lease -> is_bootp = 1;
+ break;
+
+ case INTERFACE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile,
+ "expecting interface name (in quotes).");
+ skip_to_semi (cfile);
+ break;
+ }
+ if (!interface_or_dummy (ipp, val))
+ log_fatal ("Can't allocate interface %s.", val);
+ break;
+
+ case NAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ ip = *ipp;
+ if (!ip) {
+ parse_warn (cfile, "state name precedes interface.");
+ break;
+ }
+ for (client = ip -> client; client; client = client -> next)
+ if (client -> name && !strcmp (client -> name, val))
+ break;
+ if (!client)
+ parse_warn (cfile,
+ "lease specified for unknown pseudo.");
+ *clientp = client;
+ 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:
+ parse_string (cfile, &lease -> filename, (unsigned *)0);
+ return;
+
+ case SERVER_NAME:
+ parse_string (cfile, &lease -> server_name, (unsigned *)0);
+ 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:
+ oc = (struct option_cache *)0;
+ if (parse_option_decl (&oc, cfile)) {
+ save_option(oc->option->universe, lease->options, oc);
+ option_cache_dereference (&oc, MDL);
+ }
+ return;
+
+ default:
+ parse_warn (cfile, "expecting lease declaration.");
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ }
+}
+
+/* Parse a default-duid ""; statement.
+ */
+static void
+parse_client_default_duid(struct parse *cfile)
+{
+ struct data_string new_duid;
+ const char *val = NULL;
+ unsigned len;
+ int token;
+
+ memset(&new_duid, 0, sizeof(new_duid));
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "Expected DUID string.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (len <= 2) {
+ parse_warn(cfile, "Invalid DUID contents.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!buffer_allocate(&new_duid.buffer, len, MDL)) {
+ parse_warn(cfile, "Out of memory parsing default DUID.");
+ skip_to_semi(cfile);
+ return;
+ }
+ new_duid.data = new_duid.buffer->data;
+ new_duid.len = len;
+
+ memcpy(new_duid.buffer->data, val, len);
+
+ /* Rotate the last entry into place. */
+ if (default_duid.buffer != NULL)
+ data_string_forget(&default_duid, MDL);
+ data_string_copy(&default_duid, &new_duid, MDL);
+ data_string_forget(&new_duid, MDL);
+
+ parse_semi(cfile);
+}
+
+/* Parse a lease6 {} construct. The v6 client is a little different
+ * than the v4 client today, in that it only retains one lease, the
+ * active lease, and discards any less recent information. It may
+ * be useful in the future to cache additional information, but it
+ * is not worth the effort for the moment.
+ */
+static void
+parse_client6_lease_statement(struct parse *cfile)
+{
+#if !defined(DHCPv6)
+ parse_warn(cfile, "No DHCPv6 support.");
+ skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+ struct option_cache *oc = NULL;
+ struct dhc6_lease *lease;
+ struct dhc6_ia **ia;
+ struct client_state *client = NULL;
+ struct interface_info *iface = NULL;
+ struct data_string ds;
+ const char *val;
+ unsigned len;
+ int token, has_ia, no_semi, has_name;
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly brace.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ lease = dmalloc(sizeof(*lease), MDL);
+ if (lease == NULL) {
+ parse_warn(cfile, "Unable to allocate lease state.");
+ skip_to_rbrace(cfile, 1);
+ return;
+ }
+
+ option_state_allocate(&lease->options, MDL);
+ if (lease->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option cache.");
+ skip_to_rbrace(cfile, 1);
+ dfree(lease, MDL);
+ return;
+ }
+
+ has_ia = 0;
+ has_name = 0;
+ ia = &lease->bindings;
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch(token) {
+ case IA_NA:
+ *ia = parse_client6_ia_na_statement(cfile);
+ if (*ia != NULL) {
+ ia = &(*ia)->next;
+ has_ia = 1;
+ }
+
+ no_semi = 1;
+
+ break;
+
+ case IA_TA:
+ *ia = parse_client6_ia_ta_statement(cfile);
+ if (*ia != NULL) {
+ ia = &(*ia)->next;
+ has_ia = 1;
+ }
+
+ no_semi = 1;
+
+ break;
+
+ case IA_PD:
+ *ia = parse_client6_ia_pd_statement(cfile);
+ if (*ia != NULL) {
+ ia = &(*ia)->next;
+ has_ia = 1;
+ }
+
+ no_semi = 1;
+
+ break;
+
+ case INTERFACE:
+ if (iface != NULL) {
+ parse_warn(cfile, "Multiple interface names?");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ break;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ strerror:
+ parse_warn(cfile, "Expecting a string.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ break;
+ }
+
+ for (iface = interfaces ; iface != NULL ;
+ iface = iface->next) {
+ if (strcmp(iface->name, val) == 0)
+ break;
+ }
+
+ if (iface == NULL) {
+ parse_warn(cfile, "Unknown interface.");
+ break;
+ }
+
+ break;
+
+ case NAME:
+ has_name = 1;
+
+ if (client != NULL) {
+ parse_warn(cfile, "Multiple state names?");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ break;
+ }
+
+ if (iface == NULL) {
+ parse_warn(cfile, "Client name without "
+ "interface.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ break;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING)
+ goto strerror;
+
+ for (client = iface->client ; client != NULL ;
+ client = client->next) {
+ if ((client->name != NULL) &&
+ (strcmp(client->name, val) == 0))
+ break;
+ }
+
+ if (client == NULL) {
+ parse_warn(cfile, "Unknown client state %s.",
+ val);
+ break;
+ }
+
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ lease->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ case TOKEN_RELEASED:
+ case TOKEN_ABANDONED:
+ lease->released = ISC_TRUE;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token, %s.", val);
+ no_semi = 1;
+ skip_to_semi(cfile);
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ if (!has_ia) {
+ log_debug("Lease with no IA's discarded from lease db.");
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ }
+
+ if (iface == NULL)
+ parse_warn(cfile, "Lease has no interface designation.");
+ else if (!has_name && (client == NULL)) {
+ for (client = iface->client ; client != NULL ;
+ client = client->next) {
+ if (client->name == NULL)
+ break;
+ }
+ }
+
+ if (client == NULL) {
+ parse_warn(cfile, "No matching client state.");
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ }
+
+ /* Fetch Preference option from option cache. */
+ memset(&ds, 0, sizeof(ds));
+ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE);
+ if ((oc != NULL) &&
+ evaluate_option_cache(&ds, NULL, NULL, NULL, lease->options,
+ NULL, &global_scope, oc, MDL)) {
+ if (ds.len != 1) {
+ log_error("Invalid length of DHCPv6 Preference option "
+ "(%d != 1)", ds.len);
+ data_string_forget(&ds, MDL);
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ } else
+ lease->pref = ds.data[0];
+
+ data_string_forget(&ds, MDL);
+ }
+
+ /* Fetch server-id option from option cache. */
+ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_SERVERID);
+ if ((oc == NULL) ||
+ !evaluate_option_cache(&lease->server_id, NULL, NULL, NULL,
+ lease->options, NULL, &global_scope, oc,
+ MDL) ||
+ (lease->server_id.len == 0)) {
+ /* This should be impossible... */
+ log_error("Invalid SERVERID option cache.");
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ }
+
+ if (client->active_lease != NULL)
+ dhc6_lease_destroy(&client->active_lease, MDL);
+
+ client->active_lease = lease;
+#endif /* defined(DHCPv6) */
+}
+
+/* Parse an ia_na object from the client lease.
+ */
+#ifdef DHCPv6
+static struct dhc6_ia *
+parse_client6_ia_na_statement(struct parse *cfile)
+{
+ struct option_cache *oc = NULL;
+ struct dhc6_ia *ia;
+ struct dhc6_addr **addr;
+ const char *val;
+ int token, no_semi, len;
+ u_int8_t buf[5];
+
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ parse_warn(cfile, "Out of memory allocating IA_NA state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+ ia->ia_type = D6O_IA_NA;
+
+ /* Get IAID. */
+ len = parse_X(cfile, buf, 5);
+ if (len == 4) {
+ memcpy(ia->iaid, buf, 4);
+ } else {
+ parse_warn(cfile, "Expecting IAID of length 4, got %d.", len);
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly brace.");
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ option_state_allocate(&ia->options, MDL);
+ if (ia->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option state.");
+ skip_to_rbrace(cfile, 1);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ addr = &ia->addrs;
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch (token) {
+ case STARTS:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->starts = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case RENEW:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->renew = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case REBIND:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->rebind = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case IAADDR:
+ *addr = parse_client6_iaaddr_statement(cfile);
+
+ if (*addr != NULL)
+ addr = &(*addr)->next;
+
+ no_semi = 1;
+
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ ia->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ no_semi = 1;
+ skip_to_semi(cfile);
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ return ia;
+}
+#endif /* DHCPv6 */
+
+/* Parse an ia_ta object from the client lease.
+ */
+#ifdef DHCPv6
+static struct dhc6_ia *
+parse_client6_ia_ta_statement(struct parse *cfile)
+{
+ struct option_cache *oc = NULL;
+ struct dhc6_ia *ia;
+ struct dhc6_addr **addr;
+ const char *val;
+ int token, no_semi, len;
+ u_int8_t buf[5];
+
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ parse_warn(cfile, "Out of memory allocating IA_TA state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+ ia->ia_type = D6O_IA_TA;
+
+ /* Get IAID. */
+ len = parse_X(cfile, buf, 5);
+ if (len == 4) {
+ memcpy(ia->iaid, buf, 4);
+ } else {
+ parse_warn(cfile, "Expecting IAID of length 4, got %d.", len);
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly brace.");
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ option_state_allocate(&ia->options, MDL);
+ if (ia->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option state.");
+ skip_to_rbrace(cfile, 1);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ addr = &ia->addrs;
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch (token) {
+ case STARTS:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->starts = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ /* No RENEW or REBIND */
+
+ case IAADDR:
+ *addr = parse_client6_iaaddr_statement(cfile);
+
+ if (*addr != NULL)
+ addr = &(*addr)->next;
+
+ no_semi = 1;
+
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ ia->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ no_semi = 1;
+ skip_to_semi(cfile);
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ return ia;
+}
+#endif /* DHCPv6 */
+
+/* Parse an ia_pd object from the client lease.
+ */
+#ifdef DHCPv6
+static struct dhc6_ia *
+parse_client6_ia_pd_statement(struct parse *cfile)
+{
+ struct option_cache *oc = NULL;
+ struct dhc6_ia *ia;
+ struct dhc6_addr **pref;
+ const char *val;
+ int token, no_semi, len;
+ u_int8_t buf[5];
+
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ parse_warn(cfile, "Out of memory allocating IA_PD state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+ ia->ia_type = D6O_IA_PD;
+
+ /* Get IAID. */
+ len = parse_X(cfile, buf, 5);
+ if (len == 4) {
+ memcpy(ia->iaid, buf, 4);
+ } else {
+ parse_warn(cfile, "Expecting IAID of length 4, got %d.", len);
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly brace.");
+ skip_to_semi(cfile);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ option_state_allocate(&ia->options, MDL);
+ if (ia->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option state.");
+ skip_to_rbrace(cfile, 1);
+ dfree(ia, MDL);
+ return NULL;
+ }
+
+ pref = &ia->addrs;
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch (token) {
+ case STARTS:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->starts = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case RENEW:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->renew = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case REBIND:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ ia->rebind = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case IAPREFIX:
+ *pref = parse_client6_iaprefix_statement(cfile);
+
+ if (*pref != NULL)
+ pref = &(*pref)->next;
+
+ no_semi = 1;
+
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ ia->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ no_semi = 1;
+ skip_to_semi(cfile);
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ return ia;
+}
+#endif /* DHCPv6 */
+
+/* Parse an iaaddr {} structure. */
+#ifdef DHCPv6
+static struct dhc6_addr *
+parse_client6_iaaddr_statement(struct parse *cfile)
+{
+ struct option_cache *oc = NULL;
+ struct dhc6_addr *addr;
+ const char *val;
+ int token, no_semi;
+
+ addr = dmalloc(sizeof(*addr), MDL);
+ if (addr == NULL) {
+ parse_warn(cfile, "Unable to allocate IAADDR state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+
+ /* Get IP address. */
+ if (!parse_ip6_addr(cfile, &addr->address)) {
+ skip_to_semi(cfile);
+ dfree(addr, MDL);
+ return NULL;
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly bracket.");
+ skip_to_semi(cfile);
+ dfree(addr, MDL);
+ return NULL;
+ }
+
+ option_state_allocate(&addr->options, MDL);
+ if (addr->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option state.");
+ skip_to_semi(cfile);
+ dfree(addr, MDL);
+ return NULL;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch (token) {
+ case STARTS:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ addr->starts = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case PREFERRED_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ addr->preferred_life = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case MAX_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ addr->max_life = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ addr->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ skip_to_rbrace(cfile, 1);
+ no_semi = 1;
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ return addr;
+}
+#endif /* DHCPv6 */
+
+/* Parse an iaprefix {} structure. */
+#ifdef DHCPv6
+static struct dhc6_addr *
+parse_client6_iaprefix_statement(struct parse *cfile)
+{
+ struct option_cache *oc = NULL;
+ struct dhc6_addr *pref;
+ const char *val;
+ int token, no_semi;
+
+ pref = dmalloc(sizeof(*pref), MDL);
+ if (pref == NULL) {
+ parse_warn(cfile, "Unable to allocate IAPREFIX state.");
+ skip_to_semi(cfile);
+ return NULL;
+ }
+
+ /* Get IP prefix. */
+ if (!parse_ip6_prefix(cfile, &pref->address, &pref->plen)) {
+ skip_to_semi(cfile);
+ dfree(pref, MDL);
+ return NULL;
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "Expecting open curly bracket.");
+ skip_to_semi(cfile);
+ dfree(pref, MDL);
+ return NULL;
+ }
+
+ option_state_allocate(&pref->options, MDL);
+ if (pref->options == NULL) {
+ parse_warn(cfile, "Unable to allocate option state.");
+ skip_to_semi(cfile);
+ dfree(pref, MDL);
+ return NULL;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ while (token != RBRACE) {
+ no_semi = 0;
+
+ switch (token) {
+ case STARTS:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ pref->starts = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case PREFERRED_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ pref->preferred_life = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case MAX_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ pref->max_life = atoi(val);
+ } else {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ no_semi = 1;
+ }
+ break;
+
+ case OPTION:
+ if (parse_option_decl(&oc, cfile)) {
+ save_option(oc->option->universe,
+ pref->options, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+ no_semi = 1;
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ skip_to_rbrace(cfile, 1);
+ no_semi = 1;
+ break;
+ }
+
+ if (!no_semi)
+ parse_semi(cfile);
+
+ token = next_token(&val, NULL, cfile);
+ if (token == END_OF_FILE) {
+ parse_warn(cfile, "Unexpected end of file.");
+ break;
+ }
+ }
+
+ return pref;
+}
+#endif /* DHCPv6 */
+
+void parse_string_list (cfile, lp, multiple)
+ struct parse *cfile;
+ struct string_list **lp;
+ int multiple;
+{
+ int token;
+ const 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)
+ ;
+ } else {
+ cur = (struct string_list *)0;
+ }
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "Expecting media options.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ tmp = ((struct string_list *)
+ dmalloc (strlen (val) + sizeof (struct string_list),
+ MDL));
+ if (!tmp)
+ log_fatal ("no memory for string list entry.");
+
+ strcpy (tmp -> string, val);
+ tmp -> next = (struct string_list *)0;
+
+ /* Store this medium at the end of the media list. */
+ if (cur)
+ cur -> next = tmp;
+ else
+ *lp = tmp;
+ cur = tmp;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (multiple && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ }
+}
+
+void parse_reject_statement (cfile, config)
+ struct parse *cfile;
+ struct client_config *config;
+{
+ int token;
+ const char *val;
+ struct iaddrmatch match;
+ struct iaddrmatchlist *list;
+ int i;
+
+ do {
+ if (!parse_ip_addr_with_subnet (cfile, &match)) {
+ /* no warn: parser will have reported what's wrong */
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* check mask is not all zeros (because that would
+ * reject EVERY address). This check could be
+ * simplified if we assume that the mask *always*
+ * represents a prefix .. but perhaps it might be
+ * useful to have a mask which is not a proper prefix
+ * (perhaps for ipv6?). The following is almost as
+ * efficient as inspection of match.mask.iabuf[0] when
+ * it IS a true prefix, and is more general when it is
+ * not.
+ */
+
+ for (i=0 ; i < match.mask.len ; i++) {
+ if (match.mask.iabuf[i]) {
+ break;
+ }
+ }
+
+ if (i == match.mask.len) {
+ /* oops we found all zeros */
+ parse_warn(cfile, "zero-length prefix is not permitted "
+ "for reject statement");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ list = dmalloc(sizeof(struct iaddrmatchlist), MDL);
+ if (!list)
+ log_fatal ("no memory for reject list!");
+
+ list->match = match;
+ list->next = config->reject_list;
+ config->reject_list = list;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ }
+}
+
+/* allow-deny-keyword :== BOOTP
+ | BOOTING
+ | DYNAMIC_BOOTP
+ | UNKNOWN_CLIENTS */
+
+int parse_allow_deny (oc, cfile, flag)
+ struct option_cache **oc;
+ struct parse *cfile;
+ int flag;
+{
+ parse_warn (cfile, "allow/deny/ignore not permitted here.");
+ skip_to_semi (cfile);
+ return 0;
+}
diff --git a/client/dhc6.c b/client/dhc6.c
new file mode 100644
index 0000000..633f9b1
--- /dev/null
+++ b/client/dhc6.c
@@ -0,0 +1,5128 @@
+/* dhc6.c - DHCPv6 client routines. */
+
+/*
+ * Copyright (c) 2006-2010 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#include "dhcpd.h"
+
+#ifdef DHCPv6
+
+struct sockaddr_in6 DHCPv6DestAddr;
+
+/*
+ * Option definition structures that are used by the software - declared
+ * here once and assigned at startup to save lookups.
+ */
+struct option *clientid_option = NULL;
+struct option *elapsed_option = NULL;
+struct option *ia_na_option = NULL;
+struct option *ia_ta_option = NULL;
+struct option *ia_pd_option = NULL;
+struct option *iaaddr_option = NULL;
+struct option *iaprefix_option = NULL;
+struct option *oro_option = NULL;
+struct option *irt_option = NULL;
+
+static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease,
+ const char *file, int line);
+static struct dhc6_ia *dhc6_dup_ia(struct dhc6_ia *ia,
+ const char *file, int line);
+static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr,
+ const char *file, int line);
+static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line);
+static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia,
+ struct packet *packet,
+ struct option_state *options);
+static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia,
+ struct packet *packet,
+ struct option_state *options);
+static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia,
+ struct packet *packet,
+ struct option_state *options);
+static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr,
+ struct packet *packet,
+ struct option_state *options);
+static isc_result_t dhc6_parse_prefixes(struct dhc6_addr **ppref,
+ struct packet *packet,
+ struct option_state *options);
+static struct dhc6_ia *find_ia(struct dhc6_ia *head,
+ u_int16_t type, const char *id);
+static struct dhc6_addr *find_addr(struct dhc6_addr *head,
+ struct iaddr *address);
+static struct dhc6_addr *find_pref(struct dhc6_addr *head,
+ struct iaddr *prefix, u_int8_t plen);
+void init_handler(struct packet *packet, struct client_state *client);
+void info_request_handler(struct packet *packet, struct client_state *client);
+void rapid_commit_handler(struct packet *packet, struct client_state *client);
+void do_init6(void *input);
+void do_info_request6(void *input);
+void do_confirm6(void *input);
+void reply_handler(struct packet *packet, struct client_state *client);
+static isc_result_t dhc6_add_ia_na(struct client_state *client,
+ struct data_string *packet,
+ struct dhc6_lease *lease,
+ u_int8_t message);
+static isc_result_t dhc6_add_ia_ta(struct client_state *client,
+ struct data_string *packet,
+ struct dhc6_lease *lease,
+ u_int8_t message);
+static isc_result_t dhc6_add_ia_pd(struct client_state *client,
+ struct data_string *packet,
+ struct dhc6_lease *lease,
+ u_int8_t message);
+static isc_boolean_t stopping_finished(void);
+static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst);
+void do_select6(void *input);
+void do_refresh6(void *input);
+static void do_release6(void *input);
+static void start_bound(struct client_state *client);
+static void start_informed(struct client_state *client);
+void informed_handler(struct packet *packet, struct client_state *client);
+void bound_handler(struct packet *packet, struct client_state *client);
+void start_renew6(void *input);
+void start_rebind6(void *input);
+void do_depref(void *input);
+void do_expire(void *input);
+static void make_client6_options(struct client_state *client,
+ struct option_state **op,
+ struct dhc6_lease *lease, u_int8_t message);
+static void script_write_params6(struct client_state *client,
+ const char *prefix,
+ struct option_state *options);
+static isc_boolean_t active_prefix(struct client_state *client);
+
+static int check_timing6(struct client_state *client, u_int8_t msg_type,
+ char *msg_str, struct dhc6_lease *lease,
+ struct data_string *ds);
+
+extern int onetry;
+extern int stateless;
+
+/*
+ * The "best" default DUID, since we cannot predict any information
+ * about the system (such as whether or not the hardware addresses are
+ * integrated into the motherboard or similar), is the "LLT", link local
+ * plus time, DUID. For real stateless "LL" is better.
+ *
+ * Once generated, this duid is stored into the state database, and
+ * retained across restarts.
+ *
+ * For the time being, there is probably a different state database for
+ * every daemon, so this winds up being a per-interface identifier...which
+ * is not how it is intended. Upcoming rearchitecting the client should
+ * address this "one daemon model."
+ */
+void
+form_duid(struct data_string *duid, const char *file, int line)
+{
+ struct interface_info *ip;
+ int len;
+
+ /* For now, just use the first interface on the list. */
+ ip = interfaces;
+
+ if (ip == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if ((ip->hw_address.hlen == 0) ||
+ (ip->hw_address.hlen > sizeof(ip->hw_address.hbuf)))
+ log_fatal("Impossible hardware address length at %s:%d.", MDL);
+
+ if (duid_type == 0)
+ duid_type = stateless ? DUID_LL : DUID_LLT;
+
+ /*
+ * 2 bytes for the 'duid type' field.
+ * 2 bytes for the 'htype' field.
+ * (DUID_LLT) 4 bytes for the 'current time'.
+ * enough bytes for the hardware address (note that hw_address has
+ * the 'htype' on byte zero).
+ */
+ len = 4 + (ip->hw_address.hlen - 1);
+ if (duid_type == DUID_LLT)
+ len += 4;
+ if (!buffer_allocate(&duid->buffer, len, MDL))
+ log_fatal("no memory for default DUID!");
+ duid->data = duid->buffer->data;
+ duid->len = len;
+
+ /* Basic Link Local Address type of DUID. */
+ if (duid_type == DUID_LLT) {
+ putUShort(duid->buffer->data, DUID_LLT);
+ putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
+ putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH);
+ memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1,
+ ip->hw_address.hlen - 1);
+ } else {
+ putUShort(duid->buffer->data, DUID_LL);
+ putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]);
+ memcpy(duid->buffer->data + 4, ip->hw_address.hbuf + 1,
+ ip->hw_address.hlen - 1);
+ }
+}
+
+/*
+ * Assign DHCPv6 port numbers as a client.
+ */
+void
+dhcpv6_client_assignments(void)
+{
+ struct servent *ent;
+ unsigned code;
+
+ if (path_dhclient_pid == NULL)
+ path_dhclient_pid = _PATH_DHCLIENT6_PID;
+ if (path_dhclient_db == NULL)
+ path_dhclient_db = _PATH_DHCLIENT6_DB;
+
+ if (local_port == 0) {
+ ent = getservbyname("dhcpv6-client", "udp");
+ if (ent == NULL)
+ local_port = htons(546);
+ else
+ local_port = ent->s_port;
+ }
+
+ if (remote_port == 0) {
+ ent = getservbyname("dhcpv6-server", "udp");
+ if (ent == NULL)
+ remote_port = htons(547);
+ else
+ remote_port = ent->s_port;
+ }
+
+ memset(&DHCPv6DestAddr, 0, sizeof(DHCPv6DestAddr));
+ DHCPv6DestAddr.sin6_family = AF_INET6;
+ DHCPv6DestAddr.sin6_port = remote_port;
+ inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
+ &DHCPv6DestAddr.sin6_addr);
+
+ code = D6O_CLIENTID;
+ if (!option_code_hash_lookup(&clientid_option,
+ dhcpv6_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find the CLIENTID option definition.");
+
+ code = D6O_ELAPSED_TIME;
+ if (!option_code_hash_lookup(&elapsed_option,
+ dhcpv6_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find the ELAPSED_TIME option definition.");
+
+ code = D6O_IA_NA;
+ if (!option_code_hash_lookup(&ia_na_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IA_NA option definition.");
+
+ code = D6O_IA_TA;
+ if (!option_code_hash_lookup(&ia_ta_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IA_TA option definition.");
+
+ code = D6O_IA_PD;
+ if (!option_code_hash_lookup(&ia_pd_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IA_PD option definition.");
+
+ code = D6O_IAADDR;
+ if (!option_code_hash_lookup(&iaaddr_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IAADDR option definition.");
+
+ code = D6O_IAPREFIX;
+ if (!option_code_hash_lookup(&iaprefix_option,
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IAPREFIX option definition.");
+
+ code = D6O_ORO;
+ if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the ORO option definition.");
+
+ code = D6O_INFORMATION_REFRESH_TIME;
+ if (!option_code_hash_lookup(&irt_option, dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the IRT option definition.");
+
+#ifndef __CYGWIN32__ /* XXX */
+ endservent();
+#endif
+}
+
+/*
+ * Instead of implementing RFC3315 RAND (section 14) as a float "between"
+ * -0.1 and 0.1 non-inclusive, we implement it as an integer.
+ *
+ * The result is expected to follow this table:
+ *
+ * split range answer
+ * - ERROR - base <= 0
+ * 0 1 0..0 1 <= base <= 10
+ * 1 3 -1..1 11 <= base <= 20
+ * 2 5 -2..2 21 <= base <= 30
+ * 3 7 -3..3 31 <= base <= 40
+ * ...
+ *
+ * XXX: For this to make sense, we really need to do timing on a
+ * XXX: usec scale...we currently can assume zero for any value less than
+ * XXX: 11, which are very common in early stages of transmission for most
+ * XXX: messages.
+ */
+static TIME
+dhc6_rand(TIME base)
+{
+ TIME rval;
+ TIME range;
+ TIME split;
+
+ /*
+ * A zero or less timeout is a bad thing...we don't want to
+ * DHCP-flood anyone.
+ */
+ if (base <= 0)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /*
+ * The first thing we do is count how many random integers we want
+ * in either direction (best thought of as the maximum negative
+ * integer, as we will subtract this potentially from a random 0).
+ */
+ split = (base - 1) / 10;
+
+ /* Don't bother with the rest of the math if we know we'll get 0. */
+ if (split == 0)
+ return 0;
+
+ /*
+ * Then we count the total number of integers in this set. This
+ * is twice the number of integers in positive and negative
+ * directions, plus zero (-1, 0, 1 is 3, -2..2 adds 2 to 5, so forth).
+ */
+ range = (split * 2) + 1;
+
+ /* Take a random number from [0..(range-1)]. */
+ rval = random();
+ rval %= range;
+
+ /* Offset it to uncover potential negative values. */
+ rval -= split;
+
+ return rval;
+}
+
+/* Initialize message exchange timers (set RT from Initial-RT). */
+static void
+dhc6_retrans_init(struct client_state *client)
+{
+ int xid;
+
+ /* Initialize timers. */
+ client->txcount = 0;
+ client->RT = client->IRT + dhc6_rand(client->IRT);
+
+ /* Generate a new random 24-bit transaction ID for this exchange. */
+
+#if (RAND_MAX >= 0x00ffffff)
+ xid = random();
+#elif (RAND_MAX >= 0x0000ffff)
+ xid = (random() << 16) ^ random();
+#elif (RAND_MAX >= 0x000000ff)
+ xid = (random() << 16) ^ (random() << 8) ^ random();
+#else
+# error "Random number generator of less than 8 bits not supported."
+#endif
+
+ client->dhcpv6_transaction_id[0] = (xid >> 16) & 0xff;
+ client->dhcpv6_transaction_id[1] = (xid >> 8) & 0xff;
+ client->dhcpv6_transaction_id[2] = xid & 0xff;
+}
+
+/* Advance the DHCPv6 retransmission state once. */
+static void
+dhc6_retrans_advance(struct client_state *client)
+{
+ struct timeval elapsed;
+
+ /* elapsed = cur - start */
+ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
+ elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
+ if (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ /* retrans_advance is called after consuming client->RT. */
+ /* elapsed += RT */
+ elapsed.tv_sec += client->RT / 100;
+ elapsed.tv_usec += (client->RT % 100) * 10000;
+ if (elapsed.tv_usec >= 1000000) {
+ elapsed.tv_sec += 1;
+ elapsed.tv_usec -= 1000000;
+ }
+
+ /*
+ * RT for each subsequent message transmission is based on the previous
+ * value of RT:
+ *
+ * RT = 2*RTprev + RAND*RTprev
+ */
+ client->RT += client->RT + dhc6_rand(client->RT);
+
+ /*
+ * MRT specifies an upper bound on the value of RT (disregarding the
+ * randomization added by the use of RAND). If MRT has a value of 0,
+ * there is no upper limit on the value of RT. Otherwise:
+ *
+ * if (RT > MRT)
+ * RT = MRT + RAND*MRT
+ */
+ if ((client->MRT != 0) && (client->RT > client->MRT))
+ client->RT = client->MRT + dhc6_rand(client->MRT);
+
+ /*
+ * Further, if there's an MRD, we should wake up upon reaching
+ * the MRD rather than at some point after it.
+ */
+ if (client->MRD == 0) {
+ /* Done. */
+ client->txcount++;
+ return;
+ }
+ /* elapsed += client->RT */
+ elapsed.tv_sec += client->RT / 100;
+ elapsed.tv_usec += (client->RT % 100) * 10000;
+ if (elapsed.tv_usec >= 1000000) {
+ elapsed.tv_sec += 1;
+ elapsed.tv_usec -= 1000000;
+ }
+ if (elapsed.tv_sec >= client->MRD) {
+ /*
+ * wake at RT + cur = start + MRD
+ */
+ client->RT = client->MRD +
+ (client->start_time.tv_sec - cur_tv.tv_sec);
+ client->RT = client->RT * 100 +
+ (client->start_time.tv_usec - cur_tv.tv_usec) / 10000;
+ }
+ client->txcount++;
+}
+
+/* Quick validation of DHCPv6 ADVERTISE packet contents. */
+static int
+valid_reply(struct packet *packet, struct client_state *client)
+{
+ struct data_string sid, cid;
+ struct option_cache *oc;
+ int rval = ISC_TRUE;
+
+ memset(&sid, 0, sizeof(sid));
+ memset(&cid, 0, sizeof(cid));
+
+ if (!lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID)) {
+ log_error("Response without a server identifier received.");
+ rval = ISC_FALSE;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID);
+ if (!oc ||
+ !evaluate_option_cache(&sid, packet, NULL, client, packet->options,
+ client->sent_options, &global_scope, oc,
+ MDL)) {
+ log_error("Response without a client identifier.");
+ rval = ISC_FALSE;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, client->sent_options,
+ D6O_CLIENTID);
+ if (!oc ||
+ !evaluate_option_cache(&cid, packet, NULL, client,
+ client->sent_options, NULL, &global_scope,
+ oc, MDL)) {
+ log_error("Local client identifier is missing!");
+ rval = ISC_FALSE;
+ }
+
+ if (sid.len == 0 ||
+ sid.len != cid.len ||
+ memcmp(sid.data, cid.data, sid.len)) {
+ log_error("Advertise with matching transaction ID, but "
+ "mismatching client id.");
+ rval = ISC_FALSE;
+ }
+
+ return rval;
+}
+
+/*
+ * Create a complete copy of a DHCPv6 lease structure.
+ */
+static struct dhc6_lease *
+dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line)
+{
+ struct dhc6_lease *copy;
+ struct dhc6_ia **insert_ia, *ia;
+
+ copy = dmalloc(sizeof(*copy), file, line);
+ if (copy == NULL) {
+ log_error("Out of memory for v6 lease structure.");
+ return NULL;
+ }
+
+ data_string_copy(&copy->server_id, &lease->server_id, file, line);
+ copy->pref = lease->pref;
+
+ memcpy(copy->dhcpv6_transaction_id, lease->dhcpv6_transaction_id,
+ sizeof(copy->dhcpv6_transaction_id));
+
+ option_state_reference(&copy->options, lease->options, file, line);
+
+ insert_ia = &copy->bindings;
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ *insert_ia = dhc6_dup_ia(ia, file, line);
+
+ if (*insert_ia == NULL) {
+ dhc6_lease_destroy(&copy, file, line);
+ return NULL;
+ }
+
+ insert_ia = &(*insert_ia)->next;
+ }
+
+ return copy;
+}
+
+/*
+ * Duplicate an IA structure.
+ */
+static struct dhc6_ia *
+dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line)
+{
+ struct dhc6_ia *copy;
+ struct dhc6_addr **insert_addr, *addr;
+
+ copy = dmalloc(sizeof(*ia), file, line);
+
+ memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid));
+
+ copy->ia_type = ia->ia_type;
+ copy->starts = ia->starts;
+ copy->renew = ia->renew;
+ copy->rebind = ia->rebind;
+
+ insert_addr = &copy->addrs;
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ *insert_addr = dhc6_dup_addr(addr, file, line);
+
+ if (*insert_addr == NULL) {
+ dhc6_ia_destroy(&copy, file, line);
+ return NULL;
+ }
+
+ insert_addr = &(*insert_addr)->next;
+ }
+
+ if (ia->options != NULL)
+ option_state_reference(&copy->options, ia->options,
+ file, line);
+
+ return copy;
+}
+
+/*
+ * Duplicate an IAADDR or IAPREFIX structure.
+ */
+static struct dhc6_addr *
+dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line)
+{
+ struct dhc6_addr *copy;
+
+ copy = dmalloc(sizeof(*addr), file, line);
+
+ if (copy == NULL)
+ return NULL;
+
+ memcpy(&copy->address, &addr->address, sizeof(copy->address));
+
+ copy->plen = addr->plen;
+ copy->flags = addr->flags;
+ copy->starts = addr->starts;
+ copy->preferred_life = addr->preferred_life;
+ copy->max_life = addr->max_life;
+
+ if (addr->options != NULL)
+ option_state_reference(&copy->options, addr->options,
+ file, line);
+
+ return copy;
+}
+
+/*
+ * Form a DHCPv6 lease structure based upon packet contents. Creates and
+ * populates IA's and any IAADDR/IAPREFIX's they contain.
+ * Parsed options are deleted in order to not save them in the lease file.
+ */
+static struct dhc6_lease *
+dhc6_leaseify(struct packet *packet)
+{
+ struct data_string ds;
+ struct dhc6_lease *lease;
+ struct option_cache *oc;
+
+ lease = dmalloc(sizeof(*lease), MDL);
+ if (lease == NULL) {
+ log_error("Out of memory for v6 lease structure.");
+ return NULL;
+ }
+
+ memcpy(lease->dhcpv6_transaction_id, packet->dhcpv6_transaction_id, 3);
+ option_state_reference(&lease->options, packet->options, MDL);
+
+ memset(&ds, 0, sizeof(ds));
+
+ /* Determine preference (default zero). */
+ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE);
+ if (oc &&
+ evaluate_option_cache(&ds, packet, NULL, NULL, lease->options,
+ NULL, &global_scope, oc, MDL)) {
+ if (ds.len != 1) {
+ log_error("Invalid length of DHCPv6 Preference option "
+ "(%d != 1)", ds.len);
+ data_string_forget(&ds, MDL);
+ dhc6_lease_destroy(&lease, MDL);
+ return NULL;
+ } else {
+ lease->pref = ds.data[0];
+ log_debug("RCV: X-- Preference %u.",
+ (unsigned)lease->pref);
+ }
+
+ data_string_forget(&ds, MDL);
+ }
+ delete_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE);
+
+ /*
+ * Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR
+ * options.
+ */
+ if (dhc6_parse_ia_na(&lease->bindings, packet,
+ lease->options) != ISC_R_SUCCESS) {
+ /* Error conditions are logged by the caller. */
+ dhc6_lease_destroy(&lease, MDL);
+ return NULL;
+ }
+ /*
+ * Dig into recursive DHCPv6 pockets for IA_TA and contained IAADDR
+ * options.
+ */
+ if (dhc6_parse_ia_ta(&lease->bindings, packet,
+ lease->options) != ISC_R_SUCCESS) {
+ /* Error conditions are logged by the caller. */
+ dhc6_lease_destroy(&lease, MDL);
+ return NULL;
+ }
+ /*
+ * Dig into recursive DHCPv6 pockets for IA_PD and contained IAPREFIX
+ * options.
+ */
+ if (dhc6_parse_ia_pd(&lease->bindings, packet,
+ lease->options) != ISC_R_SUCCESS) {
+ /* Error conditions are logged by the caller. */
+ dhc6_lease_destroy(&lease, MDL);
+ return NULL;
+ }
+
+ /*
+ * This is last because in the future we may want to make a different
+ * key based upon additional information from the packet (we may need
+ * to allow multiple leases in one client state per server, but we're
+ * not sure based on what additional keys now).
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
+ if (!evaluate_option_cache(&lease->server_id, packet, NULL, NULL,
+ lease->options, NULL, &global_scope,
+ oc, MDL) ||
+ lease->server_id.len == 0) {
+ /* This should be impossible due to validation checks earlier.
+ */
+ log_error("Invalid SERVERID option cache.");
+ dhc6_lease_destroy(&lease, MDL);
+ return NULL;
+ } else {
+ log_debug("RCV: X-- Server ID: %s",
+ print_hex_1(lease->server_id.len,
+ lease->server_id.data, 52));
+ }
+
+ return lease;
+}
+
+static isc_result_t
+dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct dhc6_ia *ia;
+ struct option_cache *oc;
+ isc_result_t result;
+
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_IA_NA);
+ for ( ; oc != NULL ; oc = oc->next) {
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ log_error("Out of memory allocating IA_NA structure.");
+ return ISC_R_NOMEMORY;
+ } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ options, NULL,
+ &global_scope, oc, MDL) &&
+ ds.len >= 12) {
+ memcpy(ia->iaid, ds.data, 4);
+ ia->ia_type = D6O_IA_NA;
+ ia->starts = cur_time;
+ ia->renew = getULong(ds.data + 4);
+ ia->rebind = getULong(ds.data + 8);
+
+ log_debug("RCV: X-- IA_NA %s",
+ print_hex_1(4, ia->iaid, 59));
+ /* XXX: This should be the printed time I think. */
+ log_debug("RCV: | X-- starts %u",
+ (unsigned)ia->starts);
+ log_debug("RCV: | X-- t1 - renew +%u", ia->renew);
+ log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind);
+
+ /*
+ * RFC3315 section 22.4, discard IA_NA's that
+ * have t1 greater than t2, and both not zero.
+ * Since RFC3315 defines this behaviour, it is not
+ * an error - just normal operation.
+ *
+ * Note that RFC3315 says we MUST honor these values
+ * if they are not zero. So insane values are
+ * totally OK.
+ */
+ if ((ia->renew > 0) && (ia->rebind > 0) &&
+ (ia->renew > ia->rebind)) {
+ log_debug("RCV: | !-- INVALID renew/rebind "
+ "times, IA_NA discarded.");
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ continue;
+ }
+
+ if (ds.len > 12) {
+ log_debug("RCV: | X-- [Options]");
+
+ if (!option_state_allocate(&ia->options,
+ MDL)) {
+ log_error("Out of memory allocating "
+ "IA_NA option state.");
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!parse_option_buffer(ia->options,
+ ds.data + 12,
+ ds.len - 12,
+ &dhcpv6_universe)) {
+ log_error("Corrupt IA_NA options.");
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return DHCP_R_BADPARSE;
+ }
+ }
+ data_string_forget(&ds, MDL);
+
+ if (ia->options != NULL) {
+ result = dhc6_parse_addrs(&ia->addrs, packet,
+ ia->options);
+ if (result != ISC_R_SUCCESS) {
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ return result;
+ }
+ }
+
+ while (*pia != NULL)
+ pia = &(*pia)->next;
+ *pia = ia;
+ pia = &ia->next;
+ } else {
+ log_error("Invalid IA_NA option cache.");
+ dfree(ia, MDL);
+ if (ds.len != 0)
+ data_string_forget(&ds, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ }
+ delete_option(&dhcpv6_universe, options, D6O_IA_NA);
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct dhc6_ia *ia;
+ struct option_cache *oc;
+ isc_result_t result;
+
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_IA_TA);
+ for ( ; oc != NULL ; oc = oc->next) {
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ log_error("Out of memory allocating IA_TA structure.");
+ return ISC_R_NOMEMORY;
+ } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ options, NULL,
+ &global_scope, oc, MDL) &&
+ ds.len >= 4) {
+ memcpy(ia->iaid, ds.data, 4);
+ ia->ia_type = D6O_IA_TA;
+ ia->starts = cur_time;
+
+ log_debug("RCV: X-- IA_TA %s",
+ print_hex_1(4, ia->iaid, 59));
+ /* XXX: This should be the printed time I think. */
+ log_debug("RCV: | X-- starts %u",
+ (unsigned)ia->starts);
+
+ if (ds.len > 4) {
+ log_debug("RCV: | X-- [Options]");
+
+ if (!option_state_allocate(&ia->options,
+ MDL)) {
+ log_error("Out of memory allocating "
+ "IA_TA option state.");
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!parse_option_buffer(ia->options,
+ ds.data + 4,
+ ds.len - 4,
+ &dhcpv6_universe)) {
+ log_error("Corrupt IA_TA options.");
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return DHCP_R_BADPARSE;
+ }
+ }
+ data_string_forget(&ds, MDL);
+
+ if (ia->options != NULL) {
+ result = dhc6_parse_addrs(&ia->addrs, packet,
+ ia->options);
+ if (result != ISC_R_SUCCESS) {
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ return result;
+ }
+ }
+
+ while (*pia != NULL)
+ pia = &(*pia)->next;
+ *pia = ia;
+ pia = &ia->next;
+ } else {
+ log_error("Invalid IA_TA option cache.");
+ dfree(ia, MDL);
+ if (ds.len != 0)
+ data_string_forget(&ds, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ }
+ delete_option(&dhcpv6_universe, options, D6O_IA_TA);
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct dhc6_ia *ia;
+ struct option_cache *oc;
+ isc_result_t result;
+
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_IA_PD);
+ for ( ; oc != NULL ; oc = oc->next) {
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (ia == NULL) {
+ log_error("Out of memory allocating IA_PD structure.");
+ return ISC_R_NOMEMORY;
+ } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ options, NULL,
+ &global_scope, oc, MDL) &&
+ ds.len >= 12) {
+ memcpy(ia->iaid, ds.data, 4);
+ ia->ia_type = D6O_IA_PD;
+ ia->starts = cur_time;
+ ia->renew = getULong(ds.data + 4);
+ ia->rebind = getULong(ds.data + 8);
+
+ log_debug("RCV: X-- IA_PD %s",
+ print_hex_1(4, ia->iaid, 59));
+ /* XXX: This should be the printed time I think. */
+ log_debug("RCV: | X-- starts %u",
+ (unsigned)ia->starts);
+ log_debug("RCV: | X-- t1 - renew +%u", ia->renew);
+ log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind);
+
+ /*
+ * RFC3633 section 9, discard IA_PD's that
+ * have t1 greater than t2, and both not zero.
+ * Since RFC3633 defines this behaviour, it is not
+ * an error - just normal operation.
+ */
+ if ((ia->renew > 0) && (ia->rebind > 0) &&
+ (ia->renew > ia->rebind)) {
+ log_debug("RCV: | !-- INVALID renew/rebind "
+ "times, IA_PD discarded.");
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ continue;
+ }
+
+ if (ds.len > 12) {
+ log_debug("RCV: | X-- [Options]");
+
+ if (!option_state_allocate(&ia->options,
+ MDL)) {
+ log_error("Out of memory allocating "
+ "IA_PD option state.");
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!parse_option_buffer(ia->options,
+ ds.data + 12,
+ ds.len - 12,
+ &dhcpv6_universe)) {
+ log_error("Corrupt IA_PD options.");
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ data_string_forget(&ds, MDL);
+ return DHCP_R_BADPARSE;
+ }
+ }
+ data_string_forget(&ds, MDL);
+
+ if (ia->options != NULL) {
+ result = dhc6_parse_prefixes(&ia->addrs,
+ packet,
+ ia->options);
+ if (result != ISC_R_SUCCESS) {
+ option_state_dereference(&ia->options,
+ MDL);
+ dfree(ia, MDL);
+ return result;
+ }
+ }
+
+ while (*pia != NULL)
+ pia = &(*pia)->next;
+ *pia = ia;
+ pia = &ia->next;
+ } else {
+ log_error("Invalid IA_PD option cache.");
+ dfree(ia, MDL);
+ if (ds.len != 0)
+ data_string_forget(&ds, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ }
+ delete_option(&dhcpv6_universe, options, D6O_IA_PD);
+
+ return ISC_R_SUCCESS;
+}
+
+
+static isc_result_t
+dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct option_cache *oc;
+ struct dhc6_addr *addr;
+
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_IAADDR);
+ for ( ; oc != NULL ; oc = oc->next) {
+ addr = dmalloc(sizeof(*addr), MDL);
+ if (addr == NULL) {
+ log_error("Out of memory allocating "
+ "address structure.");
+ return ISC_R_NOMEMORY;
+ } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ options, NULL, &global_scope,
+ oc, MDL) &&
+ (ds.len >= 24)) {
+
+ addr->address.len = 16;
+ memcpy(addr->address.iabuf, ds.data, 16);
+ addr->starts = cur_time;
+ addr->preferred_life = getULong(ds.data + 16);
+ addr->max_life = getULong(ds.data + 20);
+
+ log_debug("RCV: | | X-- IAADDR %s",
+ piaddr(addr->address));
+ log_debug("RCV: | | | X-- Preferred lifetime %u.",
+ addr->preferred_life);
+ log_debug("RCV: | | | X-- Max lifetime %u.",
+ addr->max_life);
+
+ /*
+ * RFC 3315 section 22.6 says we must discard
+ * addresses whose pref is later than valid.
+ */
+ if ((addr->preferred_life > addr->max_life)) {
+ log_debug("RCV: | | | !-- INVALID lifetimes, "
+ "IAADDR discarded. Check your "
+ "server configuration.");
+ dfree(addr, MDL);
+ data_string_forget(&ds, MDL);
+ continue;
+ }
+
+ /*
+ * Fortunately this is the last recursion in the
+ * protocol.
+ */
+ if (ds.len > 24) {
+ if (!option_state_allocate(&addr->options,
+ MDL)) {
+ log_error("Out of memory allocating "
+ "IAADDR option state.");
+ dfree(addr, MDL);
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!parse_option_buffer(addr->options,
+ ds.data + 24,
+ ds.len - 24,
+ &dhcpv6_universe)) {
+ log_error("Corrupt IAADDR options.");
+ option_state_dereference(&addr->options,
+ MDL);
+ dfree(addr, MDL);
+ data_string_forget(&ds, MDL);
+ return DHCP_R_BADPARSE;
+ }
+ }
+
+ if (addr->options != NULL)
+ log_debug("RCV: | | | X-- "
+ "[Options]");
+
+ data_string_forget(&ds, MDL);
+
+ *paddr = addr;
+ paddr = &addr->next;
+ } else {
+ log_error("Invalid IAADDR option cache.");
+ dfree(addr, MDL);
+ if (ds.len != 0)
+ data_string_forget(&ds, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ }
+ delete_option(&dhcpv6_universe, options, D6O_IAADDR);
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+dhc6_parse_prefixes(struct dhc6_addr **ppfx, struct packet *packet,
+ struct option_state *options)
+{
+ struct data_string ds;
+ struct option_cache *oc;
+ struct dhc6_addr *pfx;
+
+ memset(&ds, 0, sizeof(ds));
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_IAPREFIX);
+ for ( ; oc != NULL ; oc = oc->next) {
+ pfx = dmalloc(sizeof(*pfx), MDL);
+ if (pfx == NULL) {
+ log_error("Out of memory allocating "
+ "prefix structure.");
+ return ISC_R_NOMEMORY;
+ } else if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ options, NULL, &global_scope,
+ oc, MDL) &&
+ (ds.len >= 25)) {
+
+ pfx->preferred_life = getULong(ds.data);
+ pfx->max_life = getULong(ds.data + 4);
+ pfx->plen = getUChar(ds.data + 8);
+ pfx->address.len = 16;
+ memcpy(pfx->address.iabuf, ds.data + 9, 16);
+ pfx->starts = cur_time;
+
+ log_debug("RCV: | | X-- IAPREFIX %s/%d",
+ piaddr(pfx->address), (int)pfx->plen);
+ log_debug("RCV: | | | X-- Preferred lifetime %u.",
+ pfx->preferred_life);
+ log_debug("RCV: | | | X-- Max lifetime %u.",
+ pfx->max_life);
+
+ /* Sanity check over the prefix length */
+ if ((pfx->plen < 4) || (pfx->plen > 128)) {
+ log_debug("RCV: | | | !-- INVALID prefix "
+ "length, IAPREFIX discarded. "
+ "Check your server configuration.");
+ dfree(pfx, MDL);
+ data_string_forget(&ds, MDL);
+ continue;
+ }
+ /*
+ * RFC 3633 section 10 says we must discard
+ * prefixes whose pref is later than valid.
+ */
+ if ((pfx->preferred_life > pfx->max_life)) {
+ log_debug("RCV: | | | !-- INVALID lifetimes, "
+ "IAPREFIX discarded. Check your "
+ "server configuration.");
+ dfree(pfx, MDL);
+ data_string_forget(&ds, MDL);
+ continue;
+ }
+
+ /*
+ * Fortunately this is the last recursion in the
+ * protocol.
+ */
+ if (ds.len > 25) {
+ if (!option_state_allocate(&pfx->options,
+ MDL)) {
+ log_error("Out of memory allocating "
+ "IAPREFIX option state.");
+ dfree(pfx, MDL);
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ if (!parse_option_buffer(pfx->options,
+ ds.data + 25,
+ ds.len - 25,
+ &dhcpv6_universe)) {
+ log_error("Corrupt IAPREFIX options.");
+ option_state_dereference(&pfx->options,
+ MDL);
+ dfree(pfx, MDL);
+ data_string_forget(&ds, MDL);
+ return DHCP_R_BADPARSE;
+ }
+ }
+
+ if (pfx->options != NULL)
+ log_debug("RCV: | | | X-- "
+ "[Options]");
+
+ data_string_forget(&ds, MDL);
+
+ *ppfx = pfx;
+ ppfx = &pfx->next;
+ } else {
+ log_error("Invalid IAPREFIX option cache.");
+ dfree(pfx, MDL);
+ if (ds.len != 0)
+ data_string_forget(&ds, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ }
+ delete_option(&dhcpv6_universe, options, D6O_IAPREFIX);
+
+ return ISC_R_SUCCESS;
+}
+
+/* Clean up a lease object, deallocate all its parts, and set it to NULL. */
+void
+dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line)
+{
+ struct dhc6_ia *ia, *nia;
+ struct dhc6_lease *lease;
+
+ if (src == NULL || *src == NULL) {
+ log_error("Attempt to destroy null lease.");
+ return;
+ }
+ lease = *src;
+
+ if (lease->server_id.len != 0)
+ data_string_forget(&lease->server_id, file, line);
+
+ for (ia = lease->bindings ; ia != NULL ; ia = nia) {
+ nia = ia->next;
+
+ dhc6_ia_destroy(&ia, file, line);
+ }
+
+ if (lease->options != NULL)
+ option_state_dereference(&lease->options, file, line);
+
+ dfree(lease, file, line);
+ *src = NULL;
+}
+
+/*
+ * Traverse the addresses list, and destroy their contents, and NULL the
+ * list pointer.
+ */
+static void
+dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line)
+{
+ struct dhc6_addr *addr, *naddr;
+ struct dhc6_ia *ia;
+
+ if (src == NULL || *src == NULL) {
+ log_error("Attempt to destroy null IA.");
+ return;
+ }
+ ia = *src;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = naddr) {
+ naddr = addr->next;
+
+ if (addr->options != NULL)
+ option_state_dereference(&addr->options, file, line);
+
+ dfree(addr, file, line);
+ }
+
+ if (ia->options != NULL)
+ option_state_dereference(&ia->options, file, line);
+
+ dfree(ia, file, line);
+ *src = NULL;
+}
+
+/*
+ * For a given lease, insert it into the tail of the lease list. Upon
+ * finding a duplicate by server id, remove it and take over its position.
+ */
+static void
+insert_lease(struct dhc6_lease **head, struct dhc6_lease *new)
+{
+ while (*head != NULL) {
+ if ((*head)->server_id.len == new->server_id.len &&
+ memcmp((*head)->server_id.data, new->server_id.data,
+ new->server_id.len) == 0) {
+ new->next = (*head)->next;
+ dhc6_lease_destroy(head, MDL);
+ break;
+ }
+
+ head= &(*head)->next;
+ }
+
+ *head = new;
+ return;
+}
+
+/*
+ * Not really clear what to do here yet.
+ */
+static int
+dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ struct option **req;
+ int i;
+
+ if (lease->score)
+ return lease->score;
+
+ lease->score = 1;
+
+ /* If this lease lacks a required option, dump it. */
+ /* XXX: we should be able to cache the failure... */
+ req = client->config->required_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (lookup_option(&dhcpv6_universe, lease->options,
+ req[i]->code) == NULL) {
+ lease->score = 0;
+ return lease->score;
+ }
+ }
+ }
+
+ /* If this lease contains a requested option, improve its score. */
+ req = client->config->requested_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (lookup_option(&dhcpv6_universe, lease->options,
+ req[i]->code) != NULL)
+ lease->score++;
+ }
+ }
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ lease->score += 50;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ lease->score += 100;
+ }
+ }
+
+ return lease->score;
+}
+
+/*
+ * start_init6() kicks off the process, transmitting a packet and
+ * scheduling a retransmission event.
+ */
+void
+start_init6(struct client_state *client)
+{
+ struct timeval tv;
+
+ log_debug("PRC: Soliciting for leases (INIT).");
+ client->state = S_INIT;
+
+ /* Initialize timers, RFC3315 section 17.1.2. */
+ client->IRT = SOL_TIMEOUT * 100;
+ client->MRT = SOL_MAX_RT * 100;
+ client->MRC = 0;
+ /* Default is 0 (no max) but -1 changes this. */
+ if (!onetry)
+ client->MRD = 0;
+ else
+ client->MRD = client->config->timeout;
+
+ dhc6_retrans_init(client);
+
+ /*
+ * RFC3315 section 17.1.2 goes out of its way:
+ * Also, the first RT MUST be selected to be strictly greater than IRT
+ * by choosing RAND to be strictly greater than 0.
+ */
+ /* if RAND < 0 then RAND = -RAND */
+ if (client->RT <= client->IRT)
+ client->RT = client->IRT + (client->IRT - client->RT);
+ /* if RAND == 0 then RAND = 1 */
+ if (client->RT <= client->IRT)
+ client->RT = client->IRT + 1;
+
+ client->v6_handler = init_handler;
+
+ /*
+ * RFC3315 section 17.1.2 says we MUST start the first packet
+ * between 0 and SOL_MAX_DELAY seconds. The good news is
+ * SOL_MAX_DELAY is 1.
+ */
+ tv.tv_sec = cur_tv.tv_sec;
+ tv.tv_usec = cur_tv.tv_usec;
+ tv.tv_usec += (random() % (SOL_MAX_DELAY * 100)) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_init6, client, NULL, NULL);
+
+ if (nowait)
+ go_daemon();
+}
+
+/*
+ * start_info_request6() kicks off the process, transmitting an info
+ * request packet and scheduling a retransmission event.
+ */
+void
+start_info_request6(struct client_state *client)
+{
+ struct timeval tv;
+
+ log_debug("PRC: Requesting information (INIT).");
+ client->state = S_INIT;
+
+ /* Initialize timers, RFC3315 section 18.1.5. */
+ client->IRT = INF_TIMEOUT * 100;
+ client->MRT = INF_MAX_RT * 100;
+ client->MRC = 0;
+ /* Default is 0 (no max) but -1 changes this. */
+ if (!onetry)
+ client->MRD = 0;
+ else
+ client->MRD = client->config->timeout;
+
+ dhc6_retrans_init(client);
+
+ client->v6_handler = info_request_handler;
+
+ /*
+ * RFC3315 section 18.1.5 says we MUST start the first packet
+ * between 0 and INF_MAX_DELAY seconds. The good news is
+ * INF_MAX_DELAY is 1.
+ */
+ tv.tv_sec = cur_tv.tv_sec;
+ tv.tv_usec = cur_tv.tv_usec;
+ tv.tv_usec += (random() % (INF_MAX_DELAY * 100)) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_info_request6, client, NULL, NULL);
+
+ if (nowait)
+ go_daemon();
+}
+
+/*
+ * start_confirm6() kicks off an "init-reboot" version of the process, at
+ * startup to find out if old bindings are 'fair' and at runtime whenever
+ * a link cycles state we'll eventually want to do this.
+ */
+void
+start_confirm6(struct client_state *client)
+{
+ struct timeval tv;
+
+ /* If there is no active lease, there is nothing to check. */
+ if ((client->active_lease == NULL) ||
+ !active_prefix(client) ||
+ client->active_lease->released) {
+ start_init6(client);
+ return;
+ }
+
+ log_debug("PRC: Confirming active lease (INIT-REBOOT).");
+ client->state = S_REBOOTING;
+
+ /* Initialize timers, RFC3315 section 17.1.3. */
+ client->IRT = CNF_TIMEOUT * 100;
+ client->MRT = CNF_MAX_RT * 100;
+ client->MRC = 0;
+ client->MRD = CNF_MAX_RD;
+
+ dhc6_retrans_init(client);
+
+ client->v6_handler = reply_handler;
+
+ /*
+ * RFC3315 section 18.1.2 says we MUST start the first packet
+ * between 0 and CNF_MAX_DELAY seconds. The good news is
+ * CNF_MAX_DELAY is 1.
+ */
+ tv.tv_sec = cur_tv.tv_sec;
+ tv.tv_usec = cur_tv.tv_usec;
+ tv.tv_usec += (random() % (CNF_MAX_DELAY * 100)) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ if (wanted_ia_pd != 0) {
+ client->state = S_REBINDING;
+ client->refresh_type = DHCPV6_REBIND;
+ add_timeout(&tv, do_refresh6, client, NULL, NULL);
+ } else
+ add_timeout(&tv, do_confirm6, client, NULL, NULL);
+}
+
+/*
+ * check_timing6() check on the timing for sending a v6 message
+ * and then do the basic initialization for a v6 message.
+ */
+#define CHK_TIM_SUCCESS 0
+#define CHK_TIM_MRC_EXCEEDED 1
+#define CHK_TIM_MRD_EXCEEDED 2
+#define CHK_TIM_ALLOC_FAILURE 3
+
+int
+check_timing6 (struct client_state *client, u_int8_t msg_type,
+ char *msg_str, struct dhc6_lease *lease,
+ struct data_string *ds)
+{
+ struct timeval elapsed;
+
+ /*
+ * Start_time starts at the first transmission.
+ */
+ if (client->txcount == 0) {
+ client->start_time.tv_sec = cur_tv.tv_sec;
+ client->start_time.tv_usec = cur_tv.tv_usec;
+ } else if ((client->MRC != 0) && (client->txcount > client->MRC)) {
+ log_info("Max retransmission count exceeded.");
+ return(CHK_TIM_MRC_EXCEEDED);
+ }
+
+ /* elapsed = cur - start */
+ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
+ elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
+ if (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+
+ /* Check if finished (-1 argument). */
+ if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) {
+ log_info("Max retransmission duration exceeded.");
+ return(CHK_TIM_MRD_EXCEEDED);
+ }
+
+ memset(ds, 0, sizeof(*ds));
+ if (!buffer_allocate(&(ds->buffer), 4, MDL)) {
+ log_error("Unable to allocate memory for %s.", msg_str);
+ return(CHK_TIM_ALLOC_FAILURE);
+ }
+ ds->data = ds->buffer->data;
+ ds->len = 4;
+
+ ds->buffer->data[0] = msg_type;
+ memcpy(ds->buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+ /* Form an elapsed option. */
+ /* Maximum value is 65535 1/100s coded as 0xffff. */
+ if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
+ ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
+ client->elapsed = 0xffff;
+ } else {
+ client->elapsed = elapsed.tv_sec * 100;
+ client->elapsed += elapsed.tv_usec / 10000;
+ }
+
+ if (client->elapsed == 0)
+ log_debug("XMT: Forming %s, 0 ms elapsed.", msg_str);
+ else
+ log_debug("XMT: Forming %s, %u0 ms elapsed.", msg_str,
+ (unsigned)client->elapsed);
+
+ client->elapsed = htons(client->elapsed);
+
+ make_client6_options(client, &client->sent_options, lease, msg_type);
+
+ return(CHK_TIM_SUCCESS);
+}
+
+/*
+ * do_init6() marshals and transmits a solicit.
+ */
+void
+do_init6(void *input)
+{
+ struct client_state *client;
+ struct dhc6_ia *old_ia;
+ struct dhc6_addr *old_addr;
+ struct data_string ds;
+ struct data_string ia;
+ struct data_string addr;
+ struct timeval tv;
+ u_int32_t t1, t2;
+ int i, idx, len, send_ret;
+
+ client = input;
+
+ /*
+ * In RFC3315 section 17.1.2, the retransmission timer is
+ * used as the selecting timer.
+ */
+ if (client->advertised_leases != NULL) {
+ start_selecting6(client);
+ return;
+ }
+
+ switch(check_timing6(client, DHCPV6_SOLICIT, "Solicit", NULL, &ds)) {
+ case CHK_TIM_MRC_EXCEEDED:
+ case CHK_TIM_ALLOC_FAILURE:
+ return;
+ case CHK_TIM_MRD_EXCEEDED:
+ client->state = S_STOPPED;
+ if (client->active_lease != NULL) {
+ dhc6_lease_destroy(&client->active_lease, MDL);
+ client->active_lease = NULL;
+ }
+ /* Stop if and only if this is the last client. */
+ if (stopping_finished())
+ exit(2);
+ return;
+ }
+
+ /*
+ * Fetch any configured 'sent' options (includes DUID) in wire format.
+ */
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
+ NULL, client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Use a specific handler with rapid-commit. */
+ if (lookup_option(&dhcpv6_universe, client->sent_options,
+ D6O_RAPID_COMMIT) != NULL) {
+ client->v6_handler = rapid_commit_handler;
+ }
+
+ /* Append IA_NA. */
+ for (i = 0; i < wanted_ia_na; i++) {
+ /*
+ * XXX: maybe the IA_NA('s) should be put into the sent_options
+ * cache. They'd have to be pulled down as they also contain
+ * different option caches in the same universe...
+ */
+ memset(&ia, 0, sizeof(ia));
+ if (!buffer_allocate(&ia.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_NA.");
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ ia.data = ia.buffer->data;
+ ia.len = 12;
+
+ /*
+ * A simple IAID is the last 4 bytes
+ * of the hardware address.
+ */
+ if (client->interface->hw_address.hlen > 4) {
+ idx = client->interface->hw_address.hlen - 4;
+ len = 4;
+ } else {
+ idx = 0;
+ len = client->interface->hw_address.hlen;
+ }
+ memcpy(ia.buffer->data,
+ client->interface->hw_address.hbuf + idx,
+ len);
+ if (i)
+ ia.buffer->data[3] += i;
+
+ t1 = client->config->requested_lease / 2;
+ t2 = t1 + (t1 / 2);
+ putULong(ia.buffer->data + 4, t1);
+ putULong(ia.buffer->data + 8, t2);
+
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, ia.buffer->data, 55));
+ log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1);
+ log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2);
+
+ if ((client->active_lease != NULL) &&
+ ((old_ia = find_ia(client->active_lease->bindings,
+ D6O_IA_NA,
+ (char *)ia.buffer->data)) != NULL)) {
+ /*
+ * For each address in the old IA_NA,
+ * request a binding.
+ */
+ memset(&addr, 0, sizeof(addr));
+ for (old_addr = old_ia->addrs ; old_addr != NULL ;
+ old_addr = old_addr->next) {
+ if (old_addr->address.len != 16) {
+ log_error("Invalid IPv6 address "
+ "length %d. "
+ "Ignoring. (%s:%d)",
+ old_addr->address.len,
+ MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addr.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory "
+ "for IAADDR.");
+ data_string_forget(&ia, MDL);
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ addr.data = addr.buffer->data;
+ addr.len = 24;
+
+ memcpy(addr.buffer->data,
+ old_addr->address.iabuf,
+ 16);
+
+ t1 = client->config->requested_lease;
+ t2 = t1 + (t1 / 2);
+ putULong(addr.buffer->data + 16, t1);
+ putULong(addr.buffer->data + 20, t2);
+
+ log_debug("XMT: | X-- Request address %s.",
+ piaddr(old_addr->address));
+ log_debug("XMT: | | X-- Request "
+ "preferred in +%u",
+ (unsigned)t1);
+ log_debug("XMT: | | X-- Request valid "
+ "in +%u",
+ (unsigned)t2);
+
+ append_option(&ia, &dhcpv6_universe,
+ iaaddr_option,
+ &addr);
+
+ data_string_forget(&addr, MDL);
+ }
+ }
+
+ append_option(&ds, &dhcpv6_universe, ia_na_option, &ia);
+ data_string_forget(&ia, MDL);
+ }
+
+ /* Append IA_TA. */
+ for (i = 0; i < wanted_ia_ta; i++) {
+ /*
+ * XXX: maybe the IA_TA('s) should be put into the sent_options
+ * cache. They'd have to be pulled down as they also contain
+ * different option caches in the same universe...
+ */
+ memset(&ia, 0, sizeof(ia));
+ if (!buffer_allocate(&ia.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for IA_TA.");
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ ia.data = ia.buffer->data;
+ ia.len = 4;
+
+ /*
+ * A simple IAID is the last 4 bytes
+ * of the hardware address.
+ */
+ if (client->interface->hw_address.hlen > 4) {
+ idx = client->interface->hw_address.hlen - 4;
+ len = 4;
+ } else {
+ idx = 0;
+ len = client->interface->hw_address.hlen;
+ }
+ memcpy(ia.buffer->data,
+ client->interface->hw_address.hbuf + idx,
+ len);
+ if (i)
+ ia.buffer->data[3] += i;
+
+ log_debug("XMT: X-- IA_TA %s",
+ print_hex_1(4, ia.buffer->data, 55));
+
+ if ((client->active_lease != NULL) &&
+ ((old_ia = find_ia(client->active_lease->bindings,
+ D6O_IA_TA,
+ (char *)ia.buffer->data)) != NULL)) {
+ /*
+ * For each address in the old IA_TA,
+ * request a binding.
+ */
+ memset(&addr, 0, sizeof(addr));
+ for (old_addr = old_ia->addrs ; old_addr != NULL ;
+ old_addr = old_addr->next) {
+ if (old_addr->address.len != 16) {
+ log_error("Invalid IPv6 address "
+ "length %d. "
+ "Ignoring. (%s:%d)",
+ old_addr->address.len,
+ MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addr.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory "
+ "for IAADDR.");
+ data_string_forget(&ia, MDL);
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ addr.data = addr.buffer->data;
+ addr.len = 24;
+
+ memcpy(addr.buffer->data,
+ old_addr->address.iabuf,
+ 16);
+
+ t1 = client->config->requested_lease;
+ t2 = t1 + (t1 / 2);
+ putULong(addr.buffer->data + 16, t1);
+ putULong(addr.buffer->data + 20, t2);
+
+ log_debug("XMT: | X-- Request address %s.",
+ piaddr(old_addr->address));
+ log_debug("XMT: | | X-- Request "
+ "preferred in +%u",
+ (unsigned)t1);
+ log_debug("XMT: | | X-- Request valid "
+ "in +%u",
+ (unsigned)t2);
+
+ append_option(&ia, &dhcpv6_universe,
+ iaaddr_option,
+ &addr);
+
+ data_string_forget(&addr, MDL);
+ }
+ }
+
+ append_option(&ds, &dhcpv6_universe, ia_ta_option, &ia);
+ data_string_forget(&ia, MDL);
+ }
+
+ /* Append IA_PD. */
+ for (i = 0; i < wanted_ia_pd; i++) {
+ /*
+ * XXX: maybe the IA_PD('s) should be put into the sent_options
+ * cache. They'd have to be pulled down as they also contain
+ * different option caches in the same universe...
+ */
+ memset(&ia, 0, sizeof(ia));
+ if (!buffer_allocate(&ia.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_PD.");
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ ia.data = ia.buffer->data;
+ ia.len = 12;
+
+ /*
+ * A simple IAID is the last 4 bytes
+ * of the hardware address.
+ */
+ if (client->interface->hw_address.hlen > 4) {
+ idx = client->interface->hw_address.hlen - 4;
+ len = 4;
+ } else {
+ idx = 0;
+ len = client->interface->hw_address.hlen;
+ }
+ memcpy(ia.buffer->data,
+ client->interface->hw_address.hbuf + idx,
+ len);
+ if (i)
+ ia.buffer->data[3] += i;
+
+ t1 = client->config->requested_lease / 2;
+ t2 = t1 + (t1 / 2);
+ putULong(ia.buffer->data + 4, t1);
+ putULong(ia.buffer->data + 8, t2);
+
+ log_debug("XMT: X-- IA_PD %s",
+ print_hex_1(4, ia.buffer->data, 55));
+ log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1);
+ log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2);
+
+ if ((client->active_lease != NULL) &&
+ ((old_ia = find_ia(client->active_lease->bindings,
+ D6O_IA_PD,
+ (char *)ia.buffer->data)) != NULL)) {
+ /*
+ * For each prefix in the old IA_PD,
+ * request a binding.
+ */
+ memset(&addr, 0, sizeof(addr));
+ for (old_addr = old_ia->addrs ; old_addr != NULL ;
+ old_addr = old_addr->next) {
+ if (old_addr->address.len != 16) {
+ log_error("Invalid IPv6 prefix, "
+ "Ignoring. (%s:%d)",
+ MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addr.buffer, 25, MDL)) {
+ log_error("Unable to allocate memory "
+ "for IAPREFIX.");
+ data_string_forget(&ia, MDL);
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ addr.data = addr.buffer->data;
+ addr.len = 25;
+
+ t1 = client->config->requested_lease;
+ t2 = t1 + (t1 / 2);
+ putULong(addr.buffer->data, t1);
+ putULong(addr.buffer->data + 4, t2);
+
+ putUChar(addr.buffer->data + 8,
+ old_addr->plen);
+ memcpy(addr.buffer->data + 9,
+ old_addr->address.iabuf,
+ 16);
+
+ log_debug("XMT: | X-- Request prefix %s/%u.",
+ piaddr(old_addr->address),
+ (unsigned) old_addr->plen);
+ log_debug("XMT: | | X-- Request "
+ "preferred in +%u",
+ (unsigned)t1);
+ log_debug("XMT: | | X-- Request valid "
+ "in +%u",
+ (unsigned)t2);
+
+ append_option(&ia, &dhcpv6_universe,
+ iaprefix_option,
+ &addr);
+
+ data_string_forget(&addr, MDL);
+ }
+ }
+
+ append_option(&ds, &dhcpv6_universe, ia_pd_option, &ia);
+ data_string_forget(&ia, MDL);
+ }
+
+ /* Transmit and wait. */
+
+ log_info("XMT: Solicit on %s, interval %ld0ms.",
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface,
+ ds.data, ds.len, &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: send_packet6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_init6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* do_info_request6() marshals and transmits an information-request. */
+void
+do_info_request6(void *input)
+{
+ struct client_state *client;
+ struct data_string ds;
+ struct timeval tv;
+ int send_ret;
+
+ client = input;
+
+ switch(check_timing6(client, DHCPV6_INFORMATION_REQUEST,
+ "Info-Request", NULL, &ds)) {
+ case CHK_TIM_MRC_EXCEEDED:
+ case CHK_TIM_ALLOC_FAILURE:
+ return;
+ case CHK_TIM_MRD_EXCEEDED:
+ exit(2);
+ case CHK_TIM_SUCCESS:
+ break;
+ }
+
+ /* Fetch any configured 'sent' options (includes DUID) in wire format.
+ */
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
+ NULL, client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Transmit and wait. */
+
+ log_info("XMT: Info-Request on %s, interval %ld0ms.",
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface,
+ ds.data, ds.len, &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: send_packet6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_info_request6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* do_confirm6() creates a Confirm packet and transmits it. This function
+ * is called on every timeout to (re)transmit.
+ */
+void
+do_confirm6(void *input)
+{
+ struct client_state *client;
+ struct data_string ds;
+ int send_ret;
+ struct timeval tv;
+
+ client = input;
+
+ if (client->active_lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /* In section 17.1.3, it is said:
+ *
+ * If the client receives no responses before the message
+ * transmission process terminates, as described in section 14,
+ * the client SHOULD continue to use any IP addresses, using the
+ * last known lifetimes for those addresses, and SHOULD continue
+ * to use any other previously obtained configuration parameters.
+ *
+ * So if confirm times out, we go active.
+ *
+ * XXX: Should we reduce all IA's t1 to 0, so that we renew and
+ * stick there until we get a reply?
+ */
+
+ switch(check_timing6(client, DHCPV6_CONFIRM, "Confirm",
+ client->active_lease, &ds)) {
+ case CHK_TIM_MRC_EXCEEDED:
+ case CHK_TIM_MRD_EXCEEDED:
+ start_bound(client);
+ return;
+ case CHK_TIM_ALLOC_FAILURE:
+ return;
+ case CHK_TIM_SUCCESS:
+ break;
+ }
+
+ /* Fetch any configured 'sent' options (includes DUID') in wire format.
+ */
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
+ client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Append IA's. */
+ if (wanted_ia_na &&
+ dhc6_add_ia_na(client, &ds, client->active_lease,
+ DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ if (wanted_ia_ta &&
+ dhc6_add_ia_ta(client, &ds, client->active_lease,
+ DHCPV6_CONFIRM) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ /* Transmit and wait. */
+
+ log_info("XMT: Confirm on %s, interval %ld0ms.",
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface, ds.data, ds.len,
+ &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: sendpacket6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_confirm6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/*
+ * Release addresses.
+ */
+void
+start_release6(struct client_state *client)
+{
+ /* Cancel any pending transmissions */
+ cancel_timeout(do_confirm6, client);
+ cancel_timeout(do_select6, client);
+ cancel_timeout(do_refresh6, client);
+ cancel_timeout(do_release6, client);
+ client->state = S_STOPPED;
+
+ /*
+ * It is written: "The client MUST NOT use any of the addresses it
+ * is releasing as the source address in the Release message or in
+ * any subsequently transmitted message." So unconfigure now.
+ */
+ unconfigure6(client, "RELEASE6");
+
+ /* Note this in the lease file. */
+ if (client->active_lease == NULL)
+ return;
+ client->active_lease->released = ISC_TRUE;
+ write_client6_lease(client, client->active_lease, 0, 1);
+
+ /* Set timers per RFC3315 section 18.1.6. */
+ client->IRT = REL_TIMEOUT * 100;
+ client->MRT = 0;
+ client->MRC = REL_MAX_RC;
+ client->MRD = 0;
+
+ dhc6_retrans_init(client);
+ client->v6_handler = reply_handler;
+
+ do_release6(client);
+}
+/*
+ * do_release6() creates a Release packet and transmits it.
+ */
+static void
+do_release6(void *input)
+{
+ struct client_state *client;
+ struct data_string ds;
+ int send_ret;
+ struct timeval tv;
+
+ client = input;
+
+ if ((client->active_lease == NULL) || !active_prefix(client))
+ return;
+
+ switch(check_timing6(client, DHCPV6_RELEASE, "Release",
+ client->active_lease, &ds)) {
+ case CHK_TIM_MRC_EXCEEDED:
+ case CHK_TIM_ALLOC_FAILURE:
+ case CHK_TIM_MRD_EXCEEDED:
+ goto release_done;
+ case CHK_TIM_SUCCESS:
+ break;
+ }
+
+ /*
+ * Don't use unicast as we don't know if we still have an
+ * available address with enough scope.
+ */
+
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
+ client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Append IA's (but don't release temporary addresses). */
+ if (wanted_ia_na &&
+ dhc6_add_ia_na(client, &ds, client->active_lease,
+ DHCPV6_RELEASE) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ goto release_done;
+ }
+ if (wanted_ia_pd &&
+ dhc6_add_ia_pd(client, &ds, client->active_lease,
+ DHCPV6_RELEASE) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ goto release_done;
+ }
+
+ /* Transmit and wait. */
+ log_info("XMT: Release on %s, interval %ld0ms.",
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface, ds.data, ds.len,
+ &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: sendpacket6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_release6, client, NULL, NULL);
+ dhc6_retrans_advance(client);
+ return;
+
+ release_done:
+ dhc6_lease_destroy(&client->active_lease, MDL);
+ client->active_lease = NULL;
+ if (stopping_finished())
+ exit(0);
+}
+
+/* status_log() just puts a status code into displayable form and logs it
+ * to info level.
+ */
+static void
+status_log(int code, const char *scope, const char *additional, int len)
+{
+ const char *msg = NULL;
+
+ switch(code) {
+ case STATUS_Success:
+ msg = "Success";
+ break;
+
+ case STATUS_UnspecFail:
+ msg = "UnspecFail";
+ break;
+
+ case STATUS_NoAddrsAvail:
+ msg = "NoAddrsAvail";
+ break;
+
+ case STATUS_NoBinding:
+ msg = "NoBinding";
+ break;
+
+ case STATUS_NotOnLink:
+ msg = "NotOnLink";
+ break;
+
+ case STATUS_UseMulticast:
+ msg = "UseMulticast";
+ break;
+
+ case STATUS_NoPrefixAvail:
+ msg = "NoPrefixAvail";
+ break;
+
+ default:
+ msg = "UNKNOWN";
+ break;
+ }
+
+ if (len > 0)
+ log_info("%s status code %s: %s", scope, msg,
+ print_hex_1(len,
+ (const unsigned char *)additional, 50));
+ else
+ log_info("%s status code %s.", scope, msg);
+}
+
+/* Acquire a status code.
+ */
+static isc_result_t
+dhc6_get_status_code(struct option_state *options, unsigned *code,
+ struct data_string *msg)
+{
+ struct option_cache *oc;
+ struct data_string ds;
+ isc_result_t rval = ISC_R_SUCCESS;
+
+ if ((options == NULL) || (code == NULL))
+ return DHCP_R_INVALIDARG;
+
+ if ((msg != NULL) && (msg->len != 0))
+ return DHCP_R_INVALIDARG;
+
+ memset(&ds, 0, sizeof(ds));
+
+ /* Assume success if there is no option. */
+ *code = STATUS_Success;
+
+ oc = lookup_option(&dhcpv6_universe, options, D6O_STATUS_CODE);
+ if ((oc != NULL) &&
+ evaluate_option_cache(&ds, NULL, NULL, NULL, options,
+ NULL, &global_scope, oc, MDL)) {
+ if (ds.len < 2) {
+ log_error("Invalid status code length %d.", ds.len);
+ rval = DHCP_R_FORMERR;
+ } else
+ *code = getUShort(ds.data);
+
+ if ((msg != NULL) && (ds.len > 2)) {
+ data_string_copy(msg, &ds, MDL);
+ msg->data += 2;
+ msg->len -= 2;
+ }
+
+ data_string_forget(&ds, MDL);
+ return rval;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+/* Look at status codes in an advertise, and reform the return value.
+ */
+static isc_result_t
+dhc6_check_status(isc_result_t rval, struct option_state *options,
+ const char *scope, unsigned *code)
+{
+ struct data_string msg;
+ isc_result_t status;
+
+ if ((scope == NULL) || (code == NULL))
+ return DHCP_R_INVALIDARG;
+
+ /* If we don't find a code, we assume success. */
+ *code = STATUS_Success;
+
+ /* If there is no options cache, then there is no code. */
+ if (options != NULL) {
+ memset(&msg, 0, sizeof(msg));
+ status = dhc6_get_status_code(options, code, &msg);
+
+ if (status == ISC_R_SUCCESS) {
+ status_log(*code, scope, (char *)msg.data, msg.len);
+ data_string_forget(&msg, MDL);
+
+ if (*code != STATUS_Success)
+ rval = ISC_R_FAILURE;
+
+ } else if (status != ISC_R_NOTFOUND)
+ rval = status;
+ }
+
+ return rval;
+}
+
+/* Look in the packet, any IA's, and any IAADDR's within those IA's to find
+ * status code options that are not SUCCESS.
+ */
+static isc_result_t
+dhc6_check_advertise(struct dhc6_lease *lease)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ isc_result_t rval = ISC_R_SUCCESS;
+ int have_addrs = ISC_FALSE;
+ unsigned code;
+ const char *scope;
+
+ rval = dhc6_check_status(rval, lease->options, "message", &code);
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ switch (ia->ia_type) {
+ case D6O_IA_NA:
+ scope = "IA_NA";
+ break;
+ case D6O_IA_TA:
+ scope = "IA_TA";
+ break;
+ case D6O_IA_PD:
+ scope = "IA_PD";
+ break;
+ default:
+ log_error("dhc6_check_advertise: no type.");
+ return ISC_R_FAILURE;
+ }
+ rval = dhc6_check_status(rval, ia->options, scope, &code);
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (ia->ia_type != D6O_IA_PD)
+ scope = "IAADDR";
+ else
+ scope = "IAPREFIX";
+ rval = dhc6_check_status(rval, addr->options,
+ scope, &code);
+ have_addrs = ISC_TRUE;
+ }
+ }
+
+ if (have_addrs != ISC_TRUE)
+ rval = ISC_R_ADDRNOTAVAIL;
+
+ return rval;
+}
+
+/* status code <-> action matrix for the client in INIT state
+ * (rapid/commit). Returns always false as no action is defined.
+ */
+static isc_boolean_t
+dhc6_init_action(struct client_state *client, isc_result_t *rvalp,
+ unsigned code)
+{
+ if (rvalp == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (client == NULL) {
+ *rvalp = DHCP_R_INVALIDARG;
+ return ISC_FALSE;
+ }
+
+ if (*rvalp == ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ /* No possible action in any case... */
+ return ISC_FALSE;
+}
+
+/* status code <-> action matrix for the client in SELECT state
+ * (request/reply). Returns true if action was taken (and the
+ * packet should be ignored), or false if no action was taken.
+ */
+static isc_boolean_t
+dhc6_select_action(struct client_state *client, isc_result_t *rvalp,
+ unsigned code)
+{
+ struct dhc6_lease *lease;
+ isc_result_t rval;
+
+ if (rvalp == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (client == NULL) {
+ *rvalp = DHCP_R_INVALIDARG;
+ return ISC_FALSE;
+ }
+ rval = *rvalp;
+
+ if (rval == ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ switch (code) {
+ /* We may have an earlier failure status code (so no
+ * success rval), and a success code now. This
+ * doesn't upgrade the rval to success, but it does
+ * mean we take no action here.
+ */
+ case STATUS_Success:
+ /* Gimpy server, or possibly an attacker. */
+ case STATUS_NoBinding:
+ case STATUS_UseMulticast:
+ /* Take no action. */
+ return ISC_FALSE;
+
+ /* If the server can't deal with us, either try the
+ * next advertised server, or continue retrying if there
+ * weren't any.
+ */
+ default:
+ case STATUS_UnspecFail:
+ if (client->advertised_leases != NULL) {
+ dhc6_lease_destroy(&client->selected_lease, MDL);
+ client->selected_lease = NULL;
+
+ start_selecting6(client);
+
+ break;
+ } else /* Take no action - continue to retry. */
+ return ISC_FALSE;
+
+ /* If the server has no addresses, try other servers if
+ * we got some, otherwise go to INIT to hope for more
+ * servers.
+ */
+ case STATUS_NoAddrsAvail:
+ case STATUS_NoPrefixAvail:
+ if (client->state == S_REBOOTING)
+ return ISC_FALSE;
+
+ if (client->selected_lease == NULL)
+ log_fatal("Impossible case at %s:%d.", MDL);
+
+ dhc6_lease_destroy(&client->selected_lease, MDL);
+ client->selected_lease = NULL;
+
+ if (client->advertised_leases != NULL)
+ start_selecting6(client);
+ else
+ start_init6(client);
+
+ break;
+
+ /* If we got a NotOnLink from a Confirm, then we're not
+ * on link. Kill the old-active binding and start over.
+ *
+ * If we got a NotOnLink from our Request, something weird
+ * happened. Start over from scratch anyway.
+ */
+ case STATUS_NotOnLink:
+ if (client->state == S_REBOOTING) {
+ if (client->active_lease == NULL)
+ log_fatal("Impossible case at %s:%d.", MDL);
+
+ dhc6_lease_destroy(&client->active_lease, MDL);
+ } else {
+ if (client->selected_lease == NULL)
+ log_fatal("Impossible case at %s:%d.", MDL);
+
+ dhc6_lease_destroy(&client->selected_lease, MDL);
+ client->selected_lease = NULL;
+
+ while (client->advertised_leases != NULL) {
+ lease = client->advertised_leases;
+ client->advertised_leases = lease->next;
+
+ dhc6_lease_destroy(&lease, MDL);
+ }
+ }
+
+ start_init6(client);
+ break;
+ }
+
+ return ISC_TRUE;
+}
+
+static void
+dhc6_withdraw_lease(struct client_state *client)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+
+ if ((client == NULL) || (client->active_lease == NULL))
+ return;
+
+ for (ia = client->active_lease->bindings ; ia != NULL ;
+ ia = ia->next) {
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ addr->max_life = addr->preferred_life = 0;
+ }
+ }
+
+ /* Perform expiry. */
+ do_expire(client);
+}
+
+/* status code <-> action matrix for the client in BOUND state
+ * (request/reply). Returns true if action was taken (and the
+ * packet should be ignored), or false if no action was taken.
+ */
+static isc_boolean_t
+dhc6_reply_action(struct client_state *client, isc_result_t *rvalp,
+ unsigned code)
+{
+ isc_result_t rval;
+
+ if (rvalp == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (client == NULL) {
+ *rvalp = DHCP_R_INVALIDARG;
+ return ISC_FALSE;
+ }
+ rval = *rvalp;
+
+ if (rval == ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ switch (code) {
+ /* It's possible an earlier status code set rval to a failure
+ * code, and we've encountered a later success.
+ */
+ case STATUS_Success:
+ /* In "refreshes" (where we get replies), we probably
+ * still have a valid lease. So "take no action" and
+ * the upper levels will keep retrying until the lease
+ * expires (or we rebind).
+ */
+ case STATUS_UnspecFail:
+ /* For unknown codes...it's a soft (retryable) error. */
+ default:
+ return ISC_FALSE;
+
+ /* The server is telling us to use a multicast address, so
+ * we have to delete the unicast option from the active
+ * lease, then allow retransmission to occur normally.
+ * (XXX: It might be preferable in this case to retransmit
+ * sooner than the current interval, but for now we don't.)
+ */
+ case STATUS_UseMulticast:
+ if (client->active_lease != NULL)
+ delete_option(&dhcp_universe,
+ client->active_lease->options,
+ D6O_UNICAST);
+ return ISC_FALSE;
+
+ /* "When the client receives a NotOnLink status from the
+ * server in response to a Request, the client can either
+ * re-issue the Request without specifying any addresses
+ * or restart the DHCP server discovery process."
+ *
+ * This is strange. If competing server evaluation is
+ * useful (and therefore in the protocol), then why would
+ * a client's first reaction be to request from the same
+ * server on a different link? Surely you'd want to
+ * re-evaluate your server selection.
+ *
+ * Well, I guess that's the answer.
+ */
+ case STATUS_NotOnLink:
+ /* In this case, we need to rescind all current active
+ * bindings (just 'expire' them all normally, if early).
+ * They're no use to us on the wrong link. Then head back
+ * to init, redo server selection and get new addresses.
+ */
+ dhc6_withdraw_lease(client);
+ break;
+
+ /* "If the status code is NoAddrsAvail, the client has
+ * received no usable addresses in the IA and may choose
+ * to try obtaining addresses for the IA from another
+ * server."
+ */
+ case STATUS_NoAddrsAvail:
+ case STATUS_NoPrefixAvail:
+ /* Head back to init, keeping any active bindings (!). */
+ start_init6(client);
+ break;
+
+ /* - sends a Request message if the IA contained a Status
+ * Code option with the NoBinding status (and does not
+ * send any additional Renew/Rebind messages)
+ */
+ case STATUS_NoBinding:
+ if (client->advertised_leases != NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ client->advertised_leases =
+ dhc6_dup_lease(client->active_lease, MDL);
+ start_selecting6(client);
+ break;
+ }
+
+ return ISC_TRUE;
+}
+
+/* status code <-> action matrix for the client in STOPPED state
+ * (release/decline). Returns true if action was taken (and the
+ * packet should be ignored), or false if no action was taken.
+ * NoBinding is translated into Success.
+ */
+static isc_boolean_t
+dhc6_stop_action(struct client_state *client, isc_result_t *rvalp,
+ unsigned code)
+{
+ isc_result_t rval;
+
+ if (rvalp == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (client == NULL) {
+ *rvalp = DHCP_R_INVALIDARG;
+ return ISC_FALSE;
+ }
+ rval = *rvalp;
+
+ if (rval == ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ switch (code) {
+ /* It's possible an earlier status code set rval to a failure
+ * code, and we've encountered a later success.
+ */
+ case STATUS_Success:
+ /* For unknown codes...it's a soft (retryable) error. */
+ case STATUS_UnspecFail:
+ default:
+ return ISC_FALSE;
+
+ /* NoBinding is not an error */
+ case STATUS_NoBinding:
+ if (rval == ISC_R_FAILURE)
+ *rvalp = ISC_R_SUCCESS;
+ return ISC_FALSE;
+
+ /* Should not happen */
+ case STATUS_NoAddrsAvail:
+ case STATUS_NoPrefixAvail:
+ break;
+
+ /* Give up on it */
+ case STATUS_NotOnLink:
+ break;
+
+ /* The server is telling us to use a multicast address, so
+ * we have to delete the unicast option from the active
+ * lease, then allow retransmission to occur normally.
+ * (XXX: It might be preferable in this case to retransmit
+ * sooner than the current interval, but for now we don't.)
+ */
+ case STATUS_UseMulticast:
+ if (client->active_lease != NULL)
+ delete_option(&dhcp_universe,
+ client->active_lease->options,
+ D6O_UNICAST);
+ return ISC_FALSE;
+ }
+
+ return ISC_TRUE;
+}
+
+/* Look at a new and old lease, and make sure the new information is not
+ * losing us any state.
+ */
+static isc_result_t
+dhc6_check_reply(struct client_state *client, struct dhc6_lease *new)
+{
+ isc_boolean_t (*action)(struct client_state *,
+ isc_result_t *, unsigned);
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ isc_result_t rval = ISC_R_SUCCESS;
+ unsigned code;
+ const char *scope;
+ int nscore, sscore;
+
+ if ((client == NULL) || (new == NULL))
+ return DHCP_R_INVALIDARG;
+
+ switch (client->state) {
+ case S_INIT:
+ action = dhc6_init_action;
+ break;
+
+ case S_SELECTING:
+ case S_REBOOTING:
+ action = dhc6_select_action;
+ break;
+
+ case S_RENEWING:
+ case S_REBINDING:
+ action = dhc6_reply_action;
+ break;
+
+ case S_STOPPED:
+ action = dhc6_stop_action;
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ return ISC_R_CANCELED;
+ }
+
+ /* If there is a code to extract, and if there is some
+ * action to take based on that code, then take the action
+ * and do not continue.
+ */
+ rval = dhc6_check_status(rval, new->options, "message", &code);
+ if (action(client, &rval, code))
+ return ISC_R_CANCELED;
+
+ for (ia = new->bindings ; ia != NULL ; ia = ia->next) {
+ switch (ia->ia_type) {
+ case D6O_IA_NA:
+ scope = "IA_NA";
+ break;
+ case D6O_IA_TA:
+ scope = "IA_TA";
+ break;
+ case D6O_IA_PD:
+ scope = "IA_PD";
+ break;
+ default:
+ log_error("dhc6_check_reply: no type.");
+ return DHCP_R_INVALIDARG;
+ }
+ rval = dhc6_check_status(rval, ia->options,
+ scope, &code);
+ if (action(client, &rval, code))
+ return ISC_R_CANCELED;
+
+ for (addr = ia->addrs ; addr != NULL ;
+ addr = addr->next) {
+ if (ia->ia_type != D6O_IA_PD)
+ scope = "IAADDR";
+ else
+ scope = "IAPREFIX";
+ rval = dhc6_check_status(rval, addr->options,
+ scope, &code);
+ if (action(client, &rval, code))
+ return ISC_R_CANCELED;
+ }
+ }
+
+ /* A Confirm->Reply is unsuitable for comparison to the old lease. */
+ if (client->state == S_REBOOTING)
+ return rval;
+
+ /* No old lease in rapid-commit. */
+ if (client->state == S_INIT)
+ return rval;
+
+ switch (client->state) {
+ case S_SELECTING:
+ /* Compare the new lease with the selected lease to make
+ * sure there is no risky business.
+ */
+ nscore = dhc6_score_lease(client, new);
+ sscore = dhc6_score_lease(client, client->selected_lease);
+ if ((client->advertised_leases != NULL) &&
+ (nscore < (sscore / 2))) {
+ /* XXX: An attacker might reply this way to make
+ * XXX: sure we latch onto their configuration.
+ * XXX: We might want to ignore the packet and
+ * XXX: schedule re-selection at the next timeout?
+ */
+ log_error("PRC: BAIT AND SWITCH detected. Score of "
+ "supplied lease (%d) is substantially "
+ "smaller than the advertised score (%d). "
+ "Trying other servers.",
+ nscore, sscore);
+
+ dhc6_lease_destroy(&client->selected_lease, MDL);
+ client->selected_lease = NULL;
+
+ start_selecting6(client);
+
+ return ISC_R_CANCELED;
+ }
+ break;
+
+ case S_RENEWING:
+ case S_REBINDING:
+ /* This leaves one RFC3315 status check unimplemented:
+ *
+ * - sends a Renew/Rebind if the IA is not in the Reply
+ * message
+ *
+ * We rely on the scheduling system to note that the IA has
+ * not left Renewal/Rebinding/whatever since it still carries
+ * old times from the last successful binding. So this is
+ * implemented actually, just not explicitly.
+ */
+ break;
+
+ case S_STOPPED:
+ /* Nothing critical to do at this stage. */
+ break;
+
+ default:
+ log_fatal("REALLY impossible condition at %s:%d.", MDL);
+ return ISC_R_CANCELED;
+ }
+
+ return rval;
+}
+
+/* While in init state, we only collect advertisements. If there happens
+ * to be an advertisement with a preference option of 255, that's an
+ * automatic exit. Otherwise, we collect advertisements until our timeout
+ * expires (client->RT).
+ */
+void
+init_handler(struct packet *packet, struct client_state *client)
+{
+ struct dhc6_lease *lease;
+
+ /* In INIT state, we send solicits, we only expect to get
+ * advertises (rapid commit has its own handler).
+ */
+ if (packet->dhcpv6_msg_type != DHCPV6_ADVERTISE)
+ return;
+
+ /* RFC3315 section 15.3 validation (same as 15.10 since we
+ * always include a client id).
+ */
+ if (!valid_reply(packet, client)) {
+ log_error("Invalid Advertise - rejecting.");
+ return;
+ }
+
+ lease = dhc6_leaseify(packet);
+
+ if (dhc6_check_advertise(lease) != ISC_R_SUCCESS) {
+ log_debug("PRC: Lease failed to satisfy.");
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ }
+
+ insert_lease(&client->advertised_leases, lease);
+
+ /* According to RFC3315 section 17.1.2, the client MUST wait for
+ * the first RT before selecting a lease. But on the 400th RT,
+ * we dont' want to wait the full timeout if we finally get an
+ * advertise. We could probably wait a second, but ohwell,
+ * RFC3315 doesn't say so.
+ *
+ * If the lease is highest possible preference, 255, RFC3315 claims
+ * we should continue immediately even on the first RT. We probably
+ * should not if the advertise contains less than one IA and address.
+ */
+ if ((client->txcount > 1) ||
+ ((lease->pref == 255) &&
+ (dhc6_score_lease(client, lease) > 150))) {
+ log_debug("RCV: Advertisement immediately selected.");
+ cancel_timeout(do_init6, client);
+ start_selecting6(client);
+ } else
+ log_debug("RCV: Advertisement recorded.");
+}
+
+/* info_request_handler() accepts a Reply to an Info-request.
+ */
+void
+info_request_handler(struct packet *packet, struct client_state *client)
+{
+ isc_result_t check_status;
+ unsigned code;
+
+ if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
+ return;
+
+ /* RFC3315 section 15.10 validation (same as 15.3 since we
+ * always include a client id).
+ */
+ if (!valid_reply(packet, client)) {
+ log_error("Invalid Reply - rejecting.");
+ return;
+ }
+
+ check_status = dhc6_check_status(ISC_R_SUCCESS, packet->options,
+ "message", &code);
+ if (check_status != ISC_R_SUCCESS) {
+ /* If no action was taken, but there is an error, then
+ * we wait for a retransmission.
+ */
+ if (check_status != ISC_R_CANCELED)
+ return;
+ }
+
+ /* We're done retransmitting at this point. */
+ cancel_timeout(do_info_request6, client);
+
+ /* Action was taken, so now that we've torn down our scheduled
+ * retransmissions, return.
+ */
+ if (check_status == ISC_R_CANCELED)
+ return;
+
+ /* Cleanup if a previous attempt to go bound failed. */
+ if (client->old_lease != NULL) {
+ dhc6_lease_destroy(&client->old_lease, MDL);
+ client->old_lease = NULL;
+ }
+
+ /* Cache options in the active_lease. */
+ if (client->active_lease != NULL)
+ client->old_lease = client->active_lease;
+ client->active_lease = dmalloc(sizeof(struct dhc6_lease), MDL);
+ if (client->active_lease == NULL)
+ log_fatal("Out of memory for v6 lease structure.");
+ option_state_reference(&client->active_lease->options,
+ packet->options, MDL);
+
+ start_informed(client);
+}
+
+/* Specific version of init_handler() for rapid-commit.
+ */
+void
+rapid_commit_handler(struct packet *packet, struct client_state *client)
+{
+ struct dhc6_lease *lease;
+ isc_result_t check_status;
+
+ /* On ADVERTISE just fall back to the init_handler().
+ */
+ if (packet->dhcpv6_msg_type == DHCPV6_ADVERTISE) {
+ init_handler(packet, client);
+ return;
+ } else if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
+ return;
+
+ /* RFC3315 section 15.10 validation (same as 15.3 since we
+ * always include a client id).
+ */
+ if (!valid_reply(packet, client)) {
+ log_error("Invalid Reply - rejecting.");
+ return;
+ }
+
+ /* A rapid-commit option MUST be here. */
+ if (lookup_option(&dhcpv6_universe, packet->options,
+ D6O_RAPID_COMMIT) == 0) {
+ log_error("Reply without Rapid-Commit - rejecting.");
+ return;
+ }
+
+ lease = dhc6_leaseify(packet);
+
+ /* This is an out of memory condition...hopefully a temporary
+ * problem. Returning now makes us try to retransmit later.
+ */
+ if (lease == NULL)
+ return;
+
+ check_status = dhc6_check_reply(client, lease);
+ if (check_status != ISC_R_SUCCESS) {
+ dhc6_lease_destroy(&lease, MDL);
+ return;
+ }
+
+ /* Jump to the selecting state. */
+ cancel_timeout(do_init6, client);
+ client->state = S_SELECTING;
+
+ /* Merge any bindings in the active lease (if there is one) into
+ * the new active lease.
+ */
+ dhc6_merge_lease(client->active_lease, lease);
+
+ /* Cleanup if a previous attempt to go bound failed. */
+ if (client->old_lease != NULL) {
+ dhc6_lease_destroy(&client->old_lease, MDL);
+ client->old_lease = NULL;
+ }
+
+ /* Make this lease active and BIND to it. */
+ if (client->active_lease != NULL)
+ client->old_lease = client->active_lease;
+ client->active_lease = lease;
+
+ /* We're done with the ADVERTISEd leases, if any. */
+ while(client->advertised_leases != NULL) {
+ lease = client->advertised_leases;
+ client->advertised_leases = lease->next;
+
+ dhc6_lease_destroy(&lease, MDL);
+ }
+
+ start_bound(client);
+}
+
+/* Find the 'best' lease in the cache of advertised leases (usually). From
+ * RFC3315 Section 17.1.3:
+ *
+ * Upon receipt of one or more valid Advertise messages, the client
+ * selects one or more Advertise messages based upon the following
+ * criteria.
+ *
+ * - Those Advertise messages with the highest server preference value
+ * are preferred over all other Advertise messages.
+ *
+ * - Within a group of Advertise messages with the same server
+ * preference value, a client MAY select those servers whose
+ * Advertise messages advertise information of interest to the
+ * client. For example, the client may choose a server that returned
+ * an advertisement with configuration options of interest to the
+ * client.
+ *
+ * - The client MAY choose a less-preferred server if that server has a
+ * better set of advertised parameters, such as the available
+ * addresses advertised in IAs.
+ *
+ * Note that the first and third contradict each other. The third should
+ * probably be taken to mean that the client should prefer answers that
+ * offer bindings, even if that violates the preference rule.
+ *
+ * The above also isn't deterministic where there are ties. So the final
+ * tiebreaker we add, if all other values are equal, is to compare the
+ * server identifiers and to select the numerically lower one.
+ */
+static struct dhc6_lease *
+dhc6_best_lease(struct client_state *client, struct dhc6_lease **head)
+{
+ struct dhc6_lease **rpos, *rval, **candp, *cand;
+ int cscore, rscore;
+
+ if (head == NULL || *head == NULL)
+ return NULL;
+
+ rpos = head;
+ rval = *rpos;
+ rscore = dhc6_score_lease(client, rval);
+ candp = &rval->next;
+ cand = *candp;
+
+ log_debug("PRC: Considering best lease.");
+ log_debug("PRC: X-- Initial candidate %s (s: %d, p: %u).",
+ print_hex_1(rval->server_id.len,
+ rval->server_id.data, 48),
+ rscore, (unsigned)rval->pref);
+
+ for (; cand != NULL ; candp = &cand->next, cand = *candp) {
+ cscore = dhc6_score_lease(client, cand);
+
+ log_debug("PRC: X-- Candidate %s (s: %d, p: %u).",
+ print_hex_1(cand->server_id.len,
+ cand->server_id.data, 48),
+ cscore, (unsigned)cand->pref);
+
+ /* Above you'll find quoted RFC3315 Section 17.1.3.
+ *
+ * The third clause tells us to give up on leases that
+ * have no bindings even if their preference is better.
+ * So where our 'selected' lease's score is less than 150
+ * (1 ia + 1 addr), choose any candidate >= 150.
+ *
+ * The first clause tells us to make preference the primary
+ * deciding factor. So if it's lower, reject, if it's
+ * higher, select.
+ *
+ * The second clause tells us where the preference is
+ * equal, we should use 'our judgement' of what we like
+ * to see in an advertisement primarily.
+ *
+ * But there can still be a tie. To make this deterministic,
+ * we compare the server identifiers and select the binary
+ * lowest.
+ *
+ * Since server id's are unique in this list, there is
+ * no further tie to break.
+ */
+ if ((rscore < 150) && (cscore >= 150)) {
+ log_debug("PRC: | X-- Selected, has bindings.");
+ } else if (cand->pref < rval->pref) {
+ log_debug("PRC: | X-- Rejected, lower preference.");
+ continue;
+ } else if (cand->pref > rval->pref) {
+ log_debug("PRC: | X-- Selected, higher preference.");
+ } else if (cscore > rscore) {
+ log_debug("PRC: | X-- Selected, equal preference, "
+ "higher score.");
+ } else if (cscore < rscore) {
+ log_debug("PRC: | X-- Rejected, equal preference, "
+ "lower score.");
+ continue;
+ } else if ((cand->server_id.len < rval->server_id.len) ||
+ ((cand->server_id.len == rval->server_id.len) &&
+ (memcmp(cand->server_id.data,
+ rval->server_id.data,
+ cand->server_id.len) < 0))) {
+ log_debug("PRC: | X-- Selected, equal preference, "
+ "equal score, binary lesser server ID.");
+ } else {
+ log_debug("PRC: | X-- Rejected, equal preference, "
+ "equal score, binary greater server ID.");
+ continue;
+ }
+
+ rpos = candp;
+ rval = cand;
+ rscore = cscore;
+ }
+
+ /* Remove the selected lease from the chain. */
+ *rpos = rval->next;
+
+ return rval;
+}
+
+/* Select a lease out of the advertised leases and setup state to try and
+ * acquire that lease.
+ */
+void
+start_selecting6(struct client_state *client)
+{
+ struct dhc6_lease *lease;
+
+ if (client->advertised_leases == NULL) {
+ log_error("Can not enter DHCPv6 SELECTING state with no "
+ "leases to select from!");
+ return;
+ }
+
+ log_debug("PRC: Selecting best advertised lease.");
+ client->state = S_SELECTING;
+
+ lease = dhc6_best_lease(client, &client->advertised_leases);
+
+ if (lease == NULL)
+ log_fatal("Impossible error at %s:%d.", MDL);
+
+ client->selected_lease = lease;
+
+ /* Set timers per RFC3315 section 18.1.1. */
+ client->IRT = REQ_TIMEOUT * 100;
+ client->MRT = REQ_MAX_RT * 100;
+ client->MRC = REQ_MAX_RC;
+ client->MRD = 0;
+
+ dhc6_retrans_init(client);
+
+ client->v6_handler = reply_handler;
+
+ /* ("re")transmit the first packet. */
+ do_select6(client);
+}
+
+/* Transmit a Request to select a lease offered in Advertisements. In
+ * the event of failure, either move on to the next-best advertised lease,
+ * or head back to INIT state if there are none.
+ */
+void
+do_select6(void *input)
+{
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct data_string ds;
+ struct timeval tv;
+ int send_ret;
+
+ client = input;
+
+ /* 'lease' is fewer characters to type. */
+ lease = client->selected_lease;
+ if (lease == NULL || lease->bindings == NULL) {
+ log_error("Illegal to attempt selection without selecting "
+ "a lease.");
+ return;
+ }
+
+ switch(check_timing6(client, DHCPV6_REQUEST, "Request", lease, &ds)) {
+ case CHK_TIM_MRC_EXCEEDED:
+ case CHK_TIM_MRD_EXCEEDED:
+ log_debug("PRC: Lease %s failed.",
+ print_hex_1(lease->server_id.len,
+ lease->server_id.data, 56));
+
+ /* Get rid of the lease that timed/counted out. */
+ dhc6_lease_destroy(&lease, MDL);
+ client->selected_lease = NULL;
+
+ /* If there are more leases great. If not, get more. */
+ if (client->advertised_leases != NULL)
+ start_selecting6(client);
+ else
+ start_init6(client);
+ return;
+ case CHK_TIM_ALLOC_FAILURE:
+ return;
+ case CHK_TIM_SUCCESS:
+ break;
+ }
+
+ /* Now make a packet that looks suspiciously like the one we
+ * got from the server. But different.
+ *
+ * XXX: I guess IAID is supposed to be something the client
+ * indicates and uses as a key to its internal state. It is
+ * kind of odd to ask the server for IA's whose IAID the client
+ * did not manufacture. We first need a formal dhclient.conf
+ * construct for the iaid, then we can delve into this matter
+ * more properly. In the time being, this will work.
+ */
+
+ /* Fetch any configured 'sent' options (includes DUID) in wire format.
+ */
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client,
+ NULL, client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Now append any IA's, and within them any IAADDR/IAPREFIXs. */
+ if (wanted_ia_na &&
+ dhc6_add_ia_na(client, &ds, lease,
+ DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ if (wanted_ia_ta &&
+ dhc6_add_ia_ta(client, &ds, lease,
+ DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ if (wanted_ia_pd &&
+ dhc6_add_ia_pd(client, &ds, lease,
+ DHCPV6_REQUEST) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ log_info("XMT: Request on %s, interval %ld0ms.",
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface,
+ ds.data, ds.len, &DHCPv6DestAddr);
+ if (send_ret != ds.len) {
+ log_error("dhc6: send_packet6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_select6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* For each IA_NA in the lease, for each address in the IA_NA,
+ * append that information onto the packet-so-far.
+ */
+static isc_result_t
+dhc6_add_ia_na(struct client_state *client, struct data_string *packet,
+ struct dhc6_lease *lease, u_int8_t message)
+{
+ struct data_string iads;
+ struct data_string addrds;
+ struct dhc6_addr *addr;
+ struct dhc6_ia *ia;
+ isc_result_t rval = ISC_R_SUCCESS;
+ TIME t1, t2;
+
+ memset(&iads, 0, sizeof(iads));
+ memset(&addrds, 0, sizeof(addrds));
+ for (ia = lease->bindings;
+ ia != NULL && rval == ISC_R_SUCCESS;
+ ia = ia->next) {
+ if (ia->ia_type != D6O_IA_NA)
+ continue;
+
+ if (!buffer_allocate(&iads.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_NA.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ /* Copy the IAID into the packet buffer. */
+ memcpy(iads.buffer->data, ia->iaid, 4);
+ iads.data = iads.buffer->data;
+ iads.len = 12;
+
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+
+ t1 = client->config->requested_lease / 2;
+ t2 = t1 + (t1 / 2);
+#if MAX_TIME > 0xffffffff
+ if (t1 > 0xffffffff)
+ t1 = 0xffffffff;
+ if (t2 > 0xffffffff)
+ t2 = 0xffffffff;
+#endif
+ putULong(iads.buffer->data + 4, t1);
+ putULong(iads.buffer->data + 8, t2);
+
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, iads.data, 59));
+ log_debug("XMT: | X-- Requested renew +%u",
+ (unsigned) t1);
+ log_debug("XMT: | X-- Requested rebind +%u",
+ (unsigned) t2);
+ break;
+
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RELEASE:
+ case DHCPV6_DECLINE:
+ /* Set t1 and t2 to zero; server will ignore them */
+ memset(iads.buffer->data + 4, 0, 8);
+ log_debug("XMT: X-- IA_NA %s",
+ print_hex_1(4, iads.buffer->data, 55));
+
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ /*
+ * Do not confirm expired addresses, do not request
+ * expired addresses (but we keep them around for
+ * solicit).
+ */
+ if (addr->flags & DHC6_ADDR_EXPIRED)
+ continue;
+
+ if (addr->address.len != 16) {
+ log_error("Illegal IPv6 address length (%d), "
+ "ignoring. (%s:%d)",
+ addr->address.len, MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory for "
+ "IAADDR.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ addrds.data = addrds.buffer->data;
+ addrds.len = 24;
+
+ /* Copy the address into the packet buffer. */
+ memcpy(addrds.buffer->data, addr->address.iabuf, 16);
+
+ /* Copy in additional information as appropriate */
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ t1 = client->config->requested_lease;
+ t2 = t1 + 300;
+ putULong(addrds.buffer->data + 16, t1);
+ putULong(addrds.buffer->data + 20, t2);
+
+ log_debug("XMT: | | X-- IAADDR %s",
+ piaddr(addr->address));
+ log_debug("XMT: | | | X-- Preferred "
+ "lifetime +%u", (unsigned)t1);
+ log_debug("XMT: | | | X-- Max lifetime +%u",
+ (unsigned)t2);
+
+ break;
+
+ case DHCPV6_CONFIRM:
+ /*
+ * Set preferred and max life to zero,
+ * per 17.1.3.
+ */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Confirm Address %s",
+ piaddr(addr->address));
+ break;
+
+ case DHCPV6_RELEASE:
+ /* Preferred and max life are irrelevant */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Release Address %s",
+ piaddr(addr->address));
+ break;
+
+ case DHCPV6_DECLINE:
+ /* Preferred and max life are irrelevant */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Decline Address %s",
+ piaddr(addr->address));
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.",
+ MDL);
+ }
+
+ append_option(&iads, &dhcpv6_universe, iaaddr_option,
+ &addrds);
+ data_string_forget(&addrds, MDL);
+ }
+
+ /*
+ * It doesn't make sense to make a request without an
+ * address.
+ */
+ if (ia->addrs == NULL) {
+ log_debug("!!!: V IA_NA has no IAADDRs - removed.");
+ rval = ISC_R_FAILURE;
+ } else if (rval == ISC_R_SUCCESS) {
+ log_debug("XMT: V IA_NA appended.");
+ append_option(packet, &dhcpv6_universe, ia_na_option,
+ &iads);
+ }
+
+ data_string_forget(&iads, MDL);
+ }
+
+ return rval;
+}
+
+/* For each IA_TA in the lease, for each address in the IA_TA,
+ * append that information onto the packet-so-far.
+ */
+static isc_result_t
+dhc6_add_ia_ta(struct client_state *client, struct data_string *packet,
+ struct dhc6_lease *lease, u_int8_t message)
+{
+ struct data_string iads;
+ struct data_string addrds;
+ struct dhc6_addr *addr;
+ struct dhc6_ia *ia;
+ isc_result_t rval = ISC_R_SUCCESS;
+ TIME t1, t2;
+
+ memset(&iads, 0, sizeof(iads));
+ memset(&addrds, 0, sizeof(addrds));
+ for (ia = lease->bindings;
+ ia != NULL && rval == ISC_R_SUCCESS;
+ ia = ia->next) {
+ if (ia->ia_type != D6O_IA_TA)
+ continue;
+
+ if (!buffer_allocate(&iads.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for IA_TA.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ /* Copy the IAID into the packet buffer. */
+ memcpy(iads.buffer->data, ia->iaid, 4);
+ iads.data = iads.buffer->data;
+ iads.len = 4;
+
+ log_debug("XMT: X-- IA_TA %s",
+ print_hex_1(4, iads.buffer->data, 55));
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ /*
+ * Do not confirm expired addresses, do not request
+ * expired addresses (but we keep them around for
+ * solicit).
+ */
+ if (addr->flags & DHC6_ADDR_EXPIRED)
+ continue;
+
+ if (addr->address.len != 16) {
+ log_error("Illegal IPv6 address length (%d), "
+ "ignoring. (%s:%d)",
+ addr->address.len, MDL);
+ continue;
+ }
+
+ if (!buffer_allocate(&addrds.buffer, 24, MDL)) {
+ log_error("Unable to allocate memory for "
+ "IAADDR.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ addrds.data = addrds.buffer->data;
+ addrds.len = 24;
+
+ /* Copy the address into the packet buffer. */
+ memcpy(addrds.buffer->data, addr->address.iabuf, 16);
+
+ /* Copy in additional information as appropriate */
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ t1 = client->config->requested_lease;
+ t2 = t1 + 300;
+ putULong(addrds.buffer->data + 16, t1);
+ putULong(addrds.buffer->data + 20, t2);
+
+ log_debug("XMT: | | X-- IAADDR %s",
+ piaddr(addr->address));
+ log_debug("XMT: | | | X-- Preferred "
+ "lifetime +%u", (unsigned)t1);
+ log_debug("XMT: | | | X-- Max lifetime +%u",
+ (unsigned)t2);
+
+ break;
+
+ case DHCPV6_CONFIRM:
+ /*
+ * Set preferred and max life to zero,
+ * per 17.1.3.
+ */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Confirm Address %s",
+ piaddr(addr->address));
+ break;
+
+ case DHCPV6_RELEASE:
+ /* Preferred and max life are irrelevant */
+ memset(addrds.buffer->data + 16, 0, 8);
+ log_debug("XMT: | X-- Release Address %s",
+ piaddr(addr->address));
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.",
+ MDL);
+ }
+
+ append_option(&iads, &dhcpv6_universe, iaaddr_option,
+ &addrds);
+ data_string_forget(&addrds, MDL);
+ }
+
+ /*
+ * It doesn't make sense to make a request without an
+ * address.
+ */
+ if (ia->addrs == NULL) {
+ log_debug("!!!: V IA_TA has no IAADDRs - removed.");
+ rval = ISC_R_FAILURE;
+ } else if (rval == ISC_R_SUCCESS) {
+ log_debug("XMT: V IA_TA appended.");
+ append_option(packet, &dhcpv6_universe, ia_ta_option,
+ &iads);
+ }
+
+ data_string_forget(&iads, MDL);
+ }
+
+ return rval;
+}
+
+/* For each IA_PD in the lease, for each prefix in the IA_PD,
+ * append that information onto the packet-so-far.
+ */
+static isc_result_t
+dhc6_add_ia_pd(struct client_state *client, struct data_string *packet,
+ struct dhc6_lease *lease, u_int8_t message)
+{
+ struct data_string iads;
+ struct data_string prefds;
+ struct dhc6_addr *pref;
+ struct dhc6_ia *ia;
+ isc_result_t rval = ISC_R_SUCCESS;
+ TIME t1, t2;
+
+ memset(&iads, 0, sizeof(iads));
+ memset(&prefds, 0, sizeof(prefds));
+ for (ia = lease->bindings;
+ ia != NULL && rval == ISC_R_SUCCESS;
+ ia = ia->next) {
+ if (ia->ia_type != D6O_IA_PD)
+ continue;
+
+ if (!buffer_allocate(&iads.buffer, 12, MDL)) {
+ log_error("Unable to allocate memory for IA_PD.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ /* Copy the IAID into the packet buffer. */
+ memcpy(iads.buffer->data, ia->iaid, 4);
+ iads.data = iads.buffer->data;
+ iads.len = 12;
+
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+
+ t1 = client->config->requested_lease / 2;
+ t2 = t1 + (t1 / 2);
+#if MAX_TIME > 0xffffffff
+ if (t1 > 0xffffffff)
+ t1 = 0xffffffff;
+ if (t2 > 0xffffffff)
+ t2 = 0xffffffff;
+#endif
+ putULong(iads.buffer->data + 4, t1);
+ putULong(iads.buffer->data + 8, t2);
+
+ log_debug("XMT: X-- IA_PD %s",
+ print_hex_1(4, iads.data, 59));
+ log_debug("XMT: | X-- Requested renew +%u",
+ (unsigned) t1);
+ log_debug("XMT: | X-- Requested rebind +%u",
+ (unsigned) t2);
+ break;
+
+ case DHCPV6_RELEASE:
+ /* Set t1 and t2 to zero; server will ignore them */
+ memset(iads.buffer->data + 4, 0, 8);
+ log_debug("XMT: X-- IA_PD %s",
+ print_hex_1(4, iads.buffer->data, 55));
+
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ for (pref = ia->addrs ; pref != NULL ; pref = pref->next) {
+ /*
+ * Do not confirm expired prefixes, do not request
+ * expired prefixes (but we keep them around for
+ * solicit).
+ */
+ if (pref->flags & DHC6_ADDR_EXPIRED)
+ continue;
+
+ if (pref->address.len != 16) {
+ log_error("Illegal IPv6 prefix "
+ "ignoring. (%s:%d)",
+ MDL);
+ continue;
+ }
+
+ if (pref->plen == 0) {
+ log_info("Null IPv6 prefix, "
+ "ignoring. (%s:%d)",
+ MDL);
+ }
+
+ if (!buffer_allocate(&prefds.buffer, 25, MDL)) {
+ log_error("Unable to allocate memory for "
+ "IAPREFIX.");
+ rval = ISC_R_NOMEMORY;
+ break;
+ }
+
+ prefds.data = prefds.buffer->data;
+ prefds.len = 25;
+
+ /* Copy the prefix into the packet buffer. */
+ putUChar(prefds.buffer->data + 8, pref->plen);
+ memcpy(prefds.buffer->data + 9,
+ pref->address.iabuf,
+ 16);
+
+ /* Copy in additional information as appropriate */
+ switch (message) {
+ case DHCPV6_REQUEST:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ t1 = client->config->requested_lease;
+ t2 = t1 + 300;
+ putULong(prefds.buffer->data, t1);
+ putULong(prefds.buffer->data + 4, t2);
+
+ log_debug("XMT: | | X-- IAPREFIX %s/%u",
+ piaddr(pref->address),
+ (unsigned) pref->plen);
+ log_debug("XMT: | | | X-- Preferred "
+ "lifetime +%u", (unsigned)t1);
+ log_debug("XMT: | | | X-- Max lifetime +%u",
+ (unsigned)t2);
+
+ break;
+
+ case DHCPV6_RELEASE:
+ /* Preferred and max life are irrelevant */
+ memset(prefds.buffer->data, 0, 8);
+ log_debug("XMT: | X-- Release Prefix %s/%u",
+ piaddr(pref->address),
+ (unsigned) pref->plen);
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.",
+ MDL);
+ }
+
+ append_option(&iads, &dhcpv6_universe,
+ iaprefix_option, &prefds);
+ data_string_forget(&prefds, MDL);
+ }
+
+ /*
+ * It doesn't make sense to make a request without an
+ * address.
+ */
+ if (ia->addrs == NULL) {
+ log_debug("!!!: V IA_PD has no IAPREFIXs - removed.");
+ rval = ISC_R_FAILURE;
+ } else if (rval == ISC_R_SUCCESS) {
+ log_debug("XMT: V IA_PD appended.");
+ append_option(packet, &dhcpv6_universe,
+ ia_pd_option, &iads);
+ }
+
+ data_string_forget(&iads, MDL);
+ }
+
+ return rval;
+}
+
+/* stopping_finished() checks if there is a remaining work to do.
+ */
+static isc_boolean_t
+stopping_finished(void)
+{
+ struct interface_info *ip;
+ struct client_state *client;
+
+ for (ip = interfaces; ip; ip = ip -> next) {
+ for (client = ip -> client; client; client = client -> next) {
+ if (client->state != S_STOPPED)
+ return ISC_FALSE;
+ if (client->active_lease != NULL)
+ return ISC_FALSE;
+ }
+ }
+ return ISC_TRUE;
+}
+
+/* reply_handler() accepts a Reply while we're attempting Select or Renew or
+ * Rebind. Basically any Reply packet.
+ */
+void
+reply_handler(struct packet *packet, struct client_state *client)
+{
+ struct dhc6_lease *lease;
+ isc_result_t check_status;
+
+ if (packet->dhcpv6_msg_type != DHCPV6_REPLY)
+ return;
+
+ /* RFC3315 section 15.10 validation (same as 15.3 since we
+ * always include a client id).
+ */
+ if (!valid_reply(packet, client)) {
+ log_error("Invalid Reply - rejecting.");
+ return;
+ }
+
+ lease = dhc6_leaseify(packet);
+
+ /* This is an out of memory condition...hopefully a temporary
+ * problem. Returning now makes us try to retransmit later.
+ */
+ if (lease == NULL)
+ return;
+
+ check_status = dhc6_check_reply(client, lease);
+ if (check_status != ISC_R_SUCCESS) {
+ dhc6_lease_destroy(&lease, MDL);
+
+ /* If no action was taken, but there is an error, then
+ * we wait for a retransmission.
+ */
+ if (check_status != ISC_R_CANCELED)
+ return;
+ }
+
+ /* We're done retransmitting at this point. */
+ cancel_timeout(do_confirm6, client);
+ cancel_timeout(do_select6, client);
+ cancel_timeout(do_refresh6, client);
+ cancel_timeout(do_release6, client);
+
+ /* If this is in response to a Release/Decline, clean up and return. */
+ if (client->state == S_STOPPED) {
+ if (client->active_lease == NULL)
+ return;
+
+ dhc6_lease_destroy(&client->active_lease, MDL);
+ client->active_lease = NULL;
+ /* We should never wait for nothing!? */
+ if (stopping_finished())
+ exit(0);
+ return;
+ }
+
+ /* Action was taken, so now that we've torn down our scheduled
+ * retransmissions, return.
+ */
+ if (check_status == ISC_R_CANCELED)
+ return;
+
+ if (client->selected_lease != NULL) {
+ dhc6_lease_destroy(&client->selected_lease, MDL);
+ client->selected_lease = NULL;
+ }
+
+ /* If this is in response to a confirm, we use the lease we've
+ * already got, not the reply we were sent.
+ */
+ if (client->state == S_REBOOTING) {
+ if (client->active_lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ dhc6_lease_destroy(&lease, MDL);
+ start_bound(client);
+ return;
+ }
+
+ /* Merge any bindings in the active lease (if there is one) into
+ * the new active lease.
+ */
+ dhc6_merge_lease(client->active_lease, lease);
+
+ /* Cleanup if a previous attempt to go bound failed. */
+ if (client->old_lease != NULL) {
+ dhc6_lease_destroy(&client->old_lease, MDL);
+ client->old_lease = NULL;
+ }
+
+ /* Make this lease active and BIND to it. */
+ if (client->active_lease != NULL)
+ client->old_lease = client->active_lease;
+ client->active_lease = lease;
+
+ /* We're done with the ADVERTISEd leases, if any. */
+ while(client->advertised_leases != NULL) {
+ lease = client->advertised_leases;
+ client->advertised_leases = lease->next;
+
+ dhc6_lease_destroy(&lease, MDL);
+ }
+
+ start_bound(client);
+}
+
+/* DHCPv6 packets are a little sillier than they needed to be - the root
+ * packet contains options, then IA's which contain options, then within
+ * that IAADDR's which contain options.
+ *
+ * To sort this out at dhclient-script time (which fetches config parameters
+ * in environment variables), start_bound() iterates over each IAADDR, and
+ * calls this function to marshall an environment variable set that includes
+ * the most-specific option values related to that IAADDR in particular.
+ *
+ * To achieve this, we load environment variables for the root options space,
+ * then the IA, then the IAADDR. Any duplicate option names will be
+ * over-written by the later versions.
+ */
+static void
+dhc6_marshall_values(const char *prefix, struct client_state *client,
+ struct dhc6_lease *lease, struct dhc6_ia *ia,
+ struct dhc6_addr *addr)
+{
+ /* Option cache contents, in descending order of
+ * scope.
+ */
+ if ((lease != NULL) && (lease->options != NULL))
+ script_write_params6(client, prefix, lease->options);
+ if ((ia != NULL) && (ia->options != NULL))
+ script_write_params6(client, prefix, ia->options);
+ if ((addr != NULL) && (addr->options != NULL))
+ script_write_params6(client, prefix, addr->options);
+
+ /* addr fields. */
+ if (addr != NULL) {
+ if ((ia != NULL) && (ia->ia_type == D6O_IA_PD)) {
+ client_envadd(client, prefix,
+ "ip6_prefix", "%s/%u",
+ piaddr(addr->address),
+ (unsigned) addr->plen);
+ } else {
+ /* Current practice is that all subnets are /64's, but
+ * some suspect this may not be permanent.
+ */
+ client_envadd(client, prefix, "ip6_prefixlen",
+ "%d", 64);
+ client_envadd(client, prefix, "ip6_address",
+ "%s", piaddr(addr->address));
+ }
+ if ((ia != NULL) && (ia->ia_type == D6O_IA_TA)) {
+ client_envadd(client, prefix,
+ "ip6_type", "temporary");
+ }
+ client_envadd(client, prefix, "life_starts", "%d",
+ (int)(addr->starts));
+ client_envadd(client, prefix, "preferred_life", "%d",
+ (int)(addr->preferred_life));
+ client_envadd(client, prefix, "max_life", "%d",
+ (int)(addr->max_life));
+ }
+
+ /* ia fields. */
+ if (ia != NULL) {
+ client_envadd(client, prefix, "iaid", "%s",
+ print_hex_1(4, ia->iaid, 12));
+ client_envadd(client, prefix, "starts", "%d",
+ (int)(ia->starts));
+ client_envadd(client, prefix, "renew", "%u", ia->renew);
+ client_envadd(client, prefix, "rebind", "%u", ia->rebind);
+ }
+}
+
+/* Look at where the client's active lease is sitting. If it's looking to
+ * time out on renew, rebind, depref, or expiration, do those things.
+ */
+static void
+dhc6_check_times(struct client_state *client)
+{
+ struct dhc6_lease *lease;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ TIME renew=MAX_TIME, rebind=MAX_TIME, depref=MAX_TIME,
+ lo_expire=MAX_TIME, hi_expire=0, tmp;
+ int has_addrs = ISC_FALSE;
+ struct timeval tv;
+
+ lease = client->active_lease;
+
+ /* Bit spammy. We should probably keep record of scheduled
+ * events instead.
+ */
+ cancel_timeout(start_renew6, client);
+ cancel_timeout(start_rebind6, client);
+ cancel_timeout(do_depref, client);
+ cancel_timeout(do_expire, client);
+
+ for(ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ TIME this_ia_lo_expire, this_ia_hi_expire, use_expire;
+
+ this_ia_lo_expire = MAX_TIME;
+ this_ia_hi_expire = 0;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if(!(addr->flags & DHC6_ADDR_DEPREFFED)) {
+ if (addr->preferred_life == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = addr->starts +
+ addr->preferred_life;
+
+ if (tmp < depref)
+ depref = tmp;
+ }
+
+ if (!(addr->flags & DHC6_ADDR_EXPIRED)) {
+ /* Find EPOCH-relative expiration. */
+ if (addr->max_life == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = addr->starts + addr->max_life;
+
+ /* Make the times ia->starts relative. */
+ tmp -= ia->starts;
+
+ if (tmp > this_ia_hi_expire)
+ this_ia_hi_expire = tmp;
+ if (tmp < this_ia_lo_expire)
+ this_ia_lo_expire = tmp;
+
+ has_addrs = ISC_TRUE;
+ }
+ }
+
+ /* These times are ia->starts relative. */
+ if (this_ia_lo_expire <= (this_ia_hi_expire / 2))
+ use_expire = this_ia_hi_expire;
+ else
+ use_expire = this_ia_lo_expire;
+
+ /*
+ * If the auto-selected expiration time is "infinite", or
+ * zero, assert a reasonable default.
+ */
+ if ((use_expire == MAX_TIME) || (use_expire <= 1))
+ use_expire = client->config->requested_lease / 2;
+ else
+ use_expire /= 2;
+
+ /* Don't renew/rebind temporary addresses. */
+ if (ia->ia_type != D6O_IA_TA) {
+
+ if (ia->renew == 0) {
+ tmp = ia->starts + use_expire;
+ } else if (ia->renew == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = ia->starts + ia->renew;
+
+ if (tmp < renew)
+ renew = tmp;
+
+ if (ia->rebind == 0) {
+ /* Set rebind to 3/4 expiration interval. */
+ tmp = ia->starts;
+ tmp += use_expire + (use_expire / 2);
+ } else if (ia->renew == 0xffffffff)
+ tmp = MAX_TIME;
+ else
+ tmp = ia->starts + ia->rebind;
+
+ if (tmp < rebind)
+ rebind = tmp;
+ }
+
+ /*
+ * Return expiration ranges to EPOCH relative for event
+ * scheduling (add_timeout()).
+ */
+ this_ia_hi_expire += ia->starts;
+ this_ia_lo_expire += ia->starts;
+
+ if (this_ia_hi_expire > hi_expire)
+ hi_expire = this_ia_hi_expire;
+ if (this_ia_lo_expire < lo_expire)
+ lo_expire = this_ia_lo_expire;
+ }
+
+ /* If there are no addresses, give up, go to INIT.
+ * Note that if an address is unexpired with a date in the past,
+ * we're scheduling an expiration event to ocurr in the past. We
+ * could probably optimize this to expire now (but then there's
+ * recursion).
+ *
+ * In the future, we may decide that we're done here, or to
+ * schedule a future request (using 4-pkt info-request model).
+ */
+ if (has_addrs == ISC_FALSE) {
+ dhc6_lease_destroy(&client->active_lease, MDL);
+ client->active_lease = NULL;
+
+ /* Go back to the beginning. */
+ start_init6(client);
+ return;
+ }
+
+ switch(client->state) {
+ case S_BOUND:
+ /* We'd like to hit renewing, but if rebinding has already
+ * passed (time warp), head straight there.
+ */
+ if ((rebind > cur_time) && (renew < rebind)) {
+ log_debug("PRC: Renewal event scheduled in %d seconds, "
+ "to run for %u seconds.",
+ (int)(renew - cur_time),
+ (unsigned)(rebind - renew));
+ client->next_MRD = rebind;
+ tv.tv_sec = renew;
+ tv.tv_usec = 0;
+ add_timeout(&tv, start_renew6, client, NULL, NULL);
+
+ break;
+ }
+ /* FALL THROUGH */
+ case S_RENEWING:
+ /* While actively renewing, MRD is bounded by the time
+ * we stop renewing and start rebinding. This helps us
+ * process the state change on time.
+ */
+ client->MRD = rebind - cur_time;
+ if (rebind != MAX_TIME) {
+ log_debug("PRC: Rebind event scheduled in %d seconds, "
+ "to run for %d seconds.",
+ (int)(rebind - cur_time),
+ (int)(hi_expire - rebind));
+ client->next_MRD = hi_expire;
+ tv.tv_sec = rebind;
+ tv.tv_usec = 0;
+ add_timeout(&tv, start_rebind6, client, NULL, NULL);
+ }
+ break;
+
+ case S_REBINDING:
+ /* For now, we rebind up until the last lease expires. In
+ * the future, we might want to start SOLICITing when we've
+ * depreffed an address.
+ */
+ client->MRD = hi_expire - cur_time;
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ /* Separately, set a time at which we will depref and expire
+ * leases. This might happen with multiple addresses while we
+ * keep trying to refresh.
+ */
+ if (depref != MAX_TIME) {
+ log_debug("PRC: Depreference scheduled in %d seconds.",
+ (int)(depref - cur_time));
+ tv.tv_sec = depref;
+ tv.tv_usec = 0;
+ add_timeout(&tv, do_depref, client, NULL, NULL);
+ }
+ if (lo_expire != MAX_TIME) {
+ log_debug("PRC: Expiration scheduled in %d seconds.",
+ (int)(lo_expire - cur_time));
+ tv.tv_sec = lo_expire;
+ tv.tv_usec = 0;
+ add_timeout(&tv, do_expire, client, NULL, NULL);
+ }
+}
+
+/* In a given IA chain, find the IA with the same type and 'iaid'. */
+static struct dhc6_ia *
+find_ia(struct dhc6_ia *head, u_int16_t type, const char *id)
+{
+ struct dhc6_ia *ia;
+
+ for (ia = head ; ia != NULL ; ia = ia->next) {
+ if (ia->ia_type != type)
+ continue;
+ if (memcmp(ia->iaid, id, 4) == 0)
+ return ia;
+ }
+
+ return NULL;
+}
+
+/* In a given address chain, find a matching address. */
+static struct dhc6_addr *
+find_addr(struct dhc6_addr *head, struct iaddr *address)
+{
+ struct dhc6_addr *addr;
+
+ for (addr = head ; addr != NULL ; addr = addr->next) {
+ if ((addr->address.len == address->len) &&
+ (memcmp(addr->address.iabuf, address->iabuf,
+ address->len) == 0))
+ return addr;
+ }
+
+ return NULL;
+}
+
+/* In a given prefix chain, find a matching prefix. */
+static struct dhc6_addr *
+find_pref(struct dhc6_addr *head, struct iaddr *prefix, u_int8_t plen)
+{
+ struct dhc6_addr *pref;
+
+ for (pref = head ; pref != NULL ; pref = pref->next) {
+ if ((pref->address.len == prefix->len) &&
+ (pref->plen == plen) &&
+ (memcmp(pref->address.iabuf, prefix->iabuf,
+ prefix->len) == 0))
+ return pref;
+ }
+
+ return NULL;
+}
+
+/* Merge the bindings from the source lease into the destination lease
+ * structure, where they are missing. We have to copy the stateful
+ * objects rather than move them over, because later code needs to be
+ * able to compare new versus old if they contain any bindings.
+ */
+static void
+dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst)
+{
+ struct dhc6_ia *sia, *dia, *tia;
+ struct dhc6_addr *saddr, *daddr, *taddr;
+ int changes = 0;
+
+ if ((dst == NULL) || (src == NULL))
+ return;
+
+ for (sia = src->bindings ; sia != NULL ; sia = sia->next) {
+ dia = find_ia(dst->bindings, sia->ia_type, (char *)sia->iaid);
+
+ if (dia == NULL) {
+ tia = dhc6_dup_ia(sia, MDL);
+
+ if (tia == NULL)
+ log_fatal("Out of memory merging lease - "
+ "Unable to continue without losing "
+ "state! (%s:%d)", MDL);
+
+ /* XXX: consider sorting? */
+ tia->next = dst->bindings;
+ dst->bindings = tia;
+ changes = 1;
+ } else {
+ for (saddr = sia->addrs ; saddr != NULL ;
+ saddr = saddr->next) {
+ if (sia->ia_type != D6O_IA_PD)
+ daddr = find_addr(dia->addrs,
+ &saddr->address);
+ else
+ daddr = find_pref(dia->addrs,
+ &saddr->address,
+ saddr->plen);
+
+ if (daddr == NULL) {
+ taddr = dhc6_dup_addr(saddr, MDL);
+
+ if (taddr == NULL)
+ log_fatal("Out of memory "
+ "merging lease - "
+ "Unable to continue "
+ "without losing "
+ "state! (%s:%d)",
+ MDL);
+
+ /* XXX: consider sorting? */
+ taddr->next = dia->addrs;
+ dia->addrs = taddr;
+ changes = 1;
+ }
+ }
+ }
+ }
+
+ /* If we made changes, reset the score to 0 so it is recalculated. */
+ if (changes)
+ dst->score = 0;
+}
+
+/* We've either finished selecting or succeeded in Renew or Rebinding our
+ * lease. In all cases we got a Reply. Give dhclient-script a tickle
+ * to inform it about the new values, and then lay in wait for the next
+ * event.
+ */
+static void
+start_bound(struct client_state *client)
+{
+ struct dhc6_ia *ia, *oldia;
+ struct dhc6_addr *addr, *oldaddr;
+ struct dhc6_lease *lease, *old;
+ const char *reason;
+#if defined (NSUPDATE)
+ TIME dns_update_offset = 1;
+#endif
+
+ lease = client->active_lease;
+ if (lease == NULL) {
+ log_error("Cannot enter bound state unless an active lease "
+ "is selected.");
+ return;
+ }
+ lease->released = ISC_FALSE;
+ old = client->old_lease;
+
+ client->v6_handler = bound_handler;
+
+ switch (client->state) {
+ case S_SELECTING:
+ case S_REBOOTING: /* Pretend we got bound. */
+ reason = "BOUND6";
+ break;
+
+ case S_RENEWING:
+ reason = "RENEW6";
+ break;
+
+ case S_REBINDING:
+ reason = "REBIND6";
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ /* Silence compiler warnings. */
+ return;
+ }
+
+ log_debug("PRC: Bound to lease %s.",
+ print_hex_1(client->active_lease->server_id.len,
+ client->active_lease->server_id.data, 55));
+ client->state = S_BOUND;
+
+ write_client6_lease(client, lease, 0, 1);
+
+ oldia = NULL;
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ if (old != NULL)
+ oldia = find_ia(old->bindings,
+ ia->ia_type,
+ (char *)ia->iaid);
+ else
+ oldia = NULL;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (oldia != NULL) {
+ if (ia->ia_type != D6O_IA_PD)
+ oldaddr = find_addr(oldia->addrs,
+ &addr->address);
+ else
+ oldaddr = find_pref(oldia->addrs,
+ &addr->address,
+ addr->plen);
+ } else
+ oldaddr = NULL;
+
+#if defined (NSUPDATE)
+ if ((oldaddr == NULL) && (ia->ia_type == D6O_IA_NA))
+ dhclient_schedule_updates(client,
+ &addr->address,
+ dns_update_offset++);
+#endif
+
+ /* Shell out to setup the new binding. */
+ script_init(client, reason, NULL);
+
+ if (old != NULL)
+ dhc6_marshall_values("old_", client, old,
+ oldia, oldaddr);
+ dhc6_marshall_values("new_", client, lease, ia, addr);
+
+ script_go(client);
+ }
+
+ /* XXX: maybe we should loop on the old values instead? */
+ if (ia->addrs == NULL) {
+ script_init(client, reason, NULL);
+
+ if (old != NULL)
+ dhc6_marshall_values("old_", client, old,
+ oldia,
+ oldia != NULL ?
+ oldia->addrs : NULL);
+
+ dhc6_marshall_values("new_", client, lease, ia,
+ NULL);
+
+ script_go(client);
+ }
+ }
+
+ /* XXX: maybe we should loop on the old values instead? */
+ if (lease->bindings == NULL) {
+ script_init(client, reason, NULL);
+
+ if (old != NULL)
+ dhc6_marshall_values("old_", client, old,
+ old->bindings,
+ (old->bindings != NULL) ?
+ old->bindings->addrs : NULL);
+
+ dhc6_marshall_values("new_", client, lease, NULL, NULL);
+
+ script_go(client);
+ }
+
+ go_daemon();
+
+ if (client->old_lease != NULL) {
+ dhc6_lease_destroy(&client->old_lease, MDL);
+ client->old_lease = NULL;
+ }
+
+ /* Schedule events. */
+ dhc6_check_times(client);
+}
+
+/* While bound, ignore packets. In the future we'll want to answer
+ * Reconfigure-Request messages and the like.
+ */
+void
+bound_handler(struct packet *packet, struct client_state *client)
+{
+ log_debug("RCV: Input packets are ignored once bound.");
+}
+
+/* start_renew6() gets us all ready to go to start transmitting Renew packets.
+ * Note that client->next_MRD must be set before entering this function -
+ * it must be set to the time at which the client should start Rebinding.
+ */
+void
+start_renew6(void *input)
+{
+ struct client_state *client;
+
+ client = (struct client_state *)input;
+
+ log_info("PRC: Renewing lease on %s.",
+ client->name ? client->name : client->interface->name);
+ client->state = S_RENEWING;
+
+ client->v6_handler = reply_handler;
+
+ /* Times per RFC3315 section 18.1.3. */
+ client->IRT = REN_TIMEOUT * 100;
+ client->MRT = REN_MAX_RT * 100;
+ client->MRC = 0;
+ /* MRD is special in renew - we need to set it by checking timer
+ * state.
+ */
+ client->MRD = client->next_MRD - cur_time;
+
+ dhc6_retrans_init(client);
+
+ client->refresh_type = DHCPV6_RENEW;
+ do_refresh6(client);
+}
+
+/* do_refresh6() transmits one DHCPv6 packet, be it a Renew or Rebind, and
+ * gives the retransmission state a bump for the next time. Note that
+ * client->refresh_type must be set before entering this function.
+ */
+void
+do_refresh6(void *input)
+{
+ struct option_cache *oc;
+ struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr;
+ struct data_string ds;
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct timeval elapsed, tv;
+ int send_ret;
+
+ client = (struct client_state *)input;
+ memset(&ds, 0, sizeof(ds));
+
+ lease = client->active_lease;
+ if (lease == NULL) {
+ log_error("Cannot renew without an active binding.");
+ return;
+ }
+
+ /* Ensure we're emitting a valid message type. */
+ switch (client->refresh_type) {
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ break;
+
+ default:
+ log_fatal("Internal inconsistency (%d) at %s:%d.",
+ client->refresh_type, MDL);
+ }
+
+ /*
+ * Start_time starts at the first transmission.
+ */
+ if (client->txcount == 0) {
+ client->start_time.tv_sec = cur_tv.tv_sec;
+ client->start_time.tv_usec = cur_tv.tv_usec;
+ }
+
+ /* elapsed = cur - start */
+ elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec;
+ elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec;
+ if (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ if (((client->MRC != 0) && (client->txcount > client->MRC)) ||
+ ((client->MRD != 0) && (elapsed.tv_sec >= client->MRD))) {
+ /* We're done. Move on to the next phase, if any. */
+ dhc6_check_times(client);
+ return;
+ }
+
+ /*
+ * Check whether the server has sent a unicast option; if so, we can
+ * use the address it specified for RENEWs.
+ */
+ oc = lookup_option(&dhcpv6_universe, lease->options, D6O_UNICAST);
+ if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL,
+ lease->options, NULL, &global_scope,
+ oc, MDL)) {
+ if (ds.len < 16) {
+ log_error("Invalid unicast option length %d.", ds.len);
+ } else {
+ memset(&unicast, 0, sizeof(DHCPv6DestAddr));
+ unicast.sin6_family = AF_INET6;
+ unicast.sin6_port = remote_port;
+ memcpy(&unicast.sin6_addr, ds.data, 16);
+ if (client->refresh_type == DHCPV6_RENEW) {
+ dest_addr = &unicast;
+ }
+ }
+
+ data_string_forget(&ds, MDL);
+ }
+
+ /* Commence forming a renew packet. */
+ memset(&ds, 0, sizeof(ds));
+ if (!buffer_allocate(&ds.buffer, 4, MDL)) {
+ log_error("Unable to allocate memory for packet.");
+ return;
+ }
+ ds.data = ds.buffer->data;
+ ds.len = 4;
+
+ ds.buffer->data[0] = client->refresh_type;
+ memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3);
+
+ /* Form an elapsed option. */
+ /* Maximum value is 65535 1/100s coded as 0xffff. */
+ if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) ||
+ ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) {
+ client->elapsed = 0xffff;
+ } else {
+ client->elapsed = elapsed.tv_sec * 100;
+ client->elapsed += elapsed.tv_usec / 10000;
+ }
+
+ if (client->elapsed == 0)
+ log_debug("XMT: Forming %s, 0 ms elapsed.",
+ dhcpv6_type_names[client->refresh_type]);
+ else
+ log_debug("XMT: Forming %s, %u0 ms elapsed.",
+ dhcpv6_type_names[client->refresh_type],
+ (unsigned)client->elapsed);
+
+ client->elapsed = htons(client->elapsed);
+
+ make_client6_options(client, &client->sent_options, lease,
+ client->refresh_type);
+
+ /* Put in any options from the sent cache. */
+ dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL,
+ client->sent_options, &global_scope,
+ &dhcpv6_universe);
+
+ /* Append IA's */
+ if (wanted_ia_na &&
+ dhc6_add_ia_na(client, &ds, lease,
+ client->refresh_type) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+ if (wanted_ia_pd &&
+ dhc6_add_ia_pd(client, &ds, lease,
+ client->refresh_type) != ISC_R_SUCCESS) {
+ data_string_forget(&ds, MDL);
+ return;
+ }
+
+ log_info("XMT: %s on %s, interval %ld0ms.",
+ dhcpv6_type_names[client->refresh_type],
+ client->name ? client->name : client->interface->name,
+ (long int)client->RT);
+
+ send_ret = send_packet6(client->interface, ds.data, ds.len, dest_addr);
+
+ if (send_ret != ds.len) {
+ log_error("dhc6: send_packet6() sent %d of %d bytes",
+ send_ret, ds.len);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /* Wait RT */
+ tv.tv_sec = cur_tv.tv_sec + client->RT / 100;
+ tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000;
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1000000;
+ }
+ add_timeout(&tv, do_refresh6, client, NULL, NULL);
+
+ dhc6_retrans_advance(client);
+}
+
+/* start_rebind6() gets us all set up to go and rebind a lease. Note that
+ * client->next_MRD must be set before entering this function. In this case,
+ * MRD must be set to the maximum time any address in the packet will
+ * expire.
+ */
+void
+start_rebind6(void *input)
+{
+ struct client_state *client;
+
+ client = (struct client_state *)input;
+
+ log_info("PRC: Rebinding lease on %s.",
+ client->name ? client->name : client->interface->name);
+ client->state = S_REBINDING;
+
+ client->v6_handler = reply_handler;
+
+ /* Times per RFC3315 section 18.1.4. */
+ client->IRT = REB_TIMEOUT * 100;
+ client->MRT = REB_MAX_RT * 100;
+ client->MRC = 0;
+ /* MRD is special in rebind - it's determined by the timer
+ * state.
+ */
+ client->MRD = client->next_MRD - cur_time;
+
+ dhc6_retrans_init(client);
+
+ client->refresh_type = DHCPV6_REBIND;
+ do_refresh6(client);
+}
+
+/* do_depref() runs through a given lease's addresses, for each that has
+ * not yet been depreffed, shells out to the dhclient-script to inform it
+ * of the status change. The dhclient-script should then do...something...
+ * to encourage applications to move off the address and onto one of the
+ * remaining 'preferred' addresses.
+ */
+void
+do_depref(void *input)
+{
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+
+ client = (struct client_state *)input;
+
+ lease = client->active_lease;
+ if (lease == NULL)
+ return;
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (addr->flags & DHC6_ADDR_DEPREFFED)
+ continue;
+
+ if (addr->starts + addr->preferred_life <= cur_time) {
+ script_init(client, "DEPREF6", NULL);
+ dhc6_marshall_values("cur_", client, lease,
+ ia, addr);
+ script_go(client);
+
+ addr->flags |= DHC6_ADDR_DEPREFFED;
+
+ if (ia->ia_type != D6O_IA_PD)
+ log_info("PRC: Address %s depreferred.",
+ piaddr(addr->address));
+ else
+ log_info("PRC: Prefix %s/%u depreferred.",
+ piaddr(addr->address),
+ (unsigned) addr->plen);
+
+#if defined (NSUPDATE)
+ /* Remove DDNS bindings at depref time. */
+ if ((ia->ia_type == D6O_IA_NA) &&
+ client->config->do_forward_update)
+ client_dns_remove(client,
+ &addr->address);
+#endif
+ }
+ }
+ }
+
+ dhc6_check_times(client);
+}
+
+/* do_expire() searches through all the addresses on a given lease, and
+ * expires/removes any addresses that are no longer valid.
+ */
+void
+do_expire(void *input)
+{
+ struct client_state *client;
+ struct dhc6_lease *lease;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ int has_addrs = ISC_FALSE;
+
+ client = (struct client_state *)input;
+
+ lease = client->active_lease;
+ if (lease == NULL)
+ return;
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (addr->flags & DHC6_ADDR_EXPIRED)
+ continue;
+
+ if (addr->starts + addr->max_life <= cur_time) {
+ script_init(client, "EXPIRE6", NULL);
+ dhc6_marshall_values("old_", client, lease,
+ ia, addr);
+ script_go(client);
+
+ addr->flags |= DHC6_ADDR_EXPIRED;
+
+ if (ia->ia_type != D6O_IA_PD)
+ log_info("PRC: Address %s expired.",
+ piaddr(addr->address));
+ else
+ log_info("PRC: Prefix %s/%u expired.",
+ piaddr(addr->address),
+ (unsigned) addr->plen);
+
+#if defined (NSUPDATE)
+ /* We remove DNS records at depref time, but
+ * it is possible that we might get here
+ * without depreffing.
+ */
+ if ((ia->ia_type == D6O_IA_NA) &&
+ client->config->do_forward_update &&
+ !(addr->flags & DHC6_ADDR_DEPREFFED))
+ client_dns_remove(client,
+ &addr->address);
+#endif
+
+ continue;
+ }
+
+ has_addrs = ISC_TRUE;
+ }
+ }
+
+ /* Clean up empty leases. */
+ if (has_addrs == ISC_FALSE) {
+ log_info("PRC: Bound lease is devoid of active addresses."
+ " Re-initializing.");
+
+ dhc6_lease_destroy(&lease, MDL);
+ client->active_lease = NULL;
+
+ start_init6(client);
+ return;
+ }
+
+ /* Schedule the next run through. */
+ dhc6_check_times(client);
+}
+
+/*
+ * Run client script to unconfigure interface.
+ * Called with reason STOP6 when dhclient -x is run, or with reason
+ * RELEASE6 when server has replied to a Release message.
+ * Stateless is a special case.
+ */
+void
+unconfigure6(struct client_state *client, const char *reason)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+
+ if (stateless) {
+ script_init(client, reason, NULL);
+ if (client->active_lease != NULL)
+ script_write_params6(client, "old_",
+ client->active_lease->options);
+ script_go(client);
+ return;
+ }
+
+ if (client->active_lease == NULL)
+ return;
+
+ for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) {
+ if (ia->ia_type == D6O_IA_TA)
+ continue;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ script_init(client, reason, NULL);
+ dhc6_marshall_values("old_", client,
+ client->active_lease, ia, addr);
+ script_go(client);
+
+#if defined (NSUPDATE)
+ if ((ia->ia_type == D6O_IA_NA) &&
+ client->config->do_forward_update)
+ client_dns_remove(client, &addr->address);
+#endif
+ }
+ }
+}
+
+void
+refresh_info_request6(void *input)
+{
+ struct client_state *client;
+
+ client = (struct client_state *)input;
+ start_info_request6(client);
+}
+
+/* Timeout for Information-Request (using the IRT option).
+ */
+static void
+dhc6_check_irt(struct client_state *client)
+{
+ struct option **req;
+ struct option_cache *oc;
+ TIME expire = MAX_TIME;
+ struct timeval tv;
+ int i;
+ isc_boolean_t found = ISC_FALSE;
+
+ cancel_timeout(refresh_info_request6, client);
+
+ req = client->config->requested_options;
+ for (i = 0; req[i] != NULL; i++) {
+ if (req[i] == irt_option) {
+ found = ISC_TRUE;
+ break;
+ }
+ }
+ /* Simply return gives a endless loop waiting for nothing. */
+ if (!found)
+ exit(0);
+
+ oc = lookup_option(&dhcpv6_universe, client->active_lease->options,
+ D6O_INFORMATION_REFRESH_TIME);
+ if (oc != NULL) {
+ struct data_string irt;
+
+ memset(&irt, 0, sizeof(irt));
+ if (!evaluate_option_cache(&irt, NULL, NULL, client,
+ client->active_lease->options,
+ NULL, &global_scope, oc, MDL) ||
+ (irt.len < 4)) {
+ log_error("Can't evaluate IRT.");
+ } else {
+ expire = getULong(irt.data);
+ if (expire < IRT_MINIMUM)
+ expire = IRT_MINIMUM;
+ if (expire == 0xffffffff)
+ expire = MAX_TIME;
+ }
+ data_string_forget(&irt, MDL);
+ } else
+ expire = IRT_DEFAULT;
+
+ if (expire != MAX_TIME) {
+ log_debug("PRC: Refresh event scheduled in %u seconds.",
+ (unsigned) expire);
+ tv.tv_sec = cur_time + expire;
+ tv.tv_usec = 0;
+ add_timeout(&tv, refresh_info_request6, client, NULL, NULL);
+ }
+}
+
+/* We got a Reply. Give dhclient-script a tickle to inform it about
+ * the new values, and then lay in wait for the next event.
+ */
+static void
+start_informed(struct client_state *client)
+{
+ client->v6_handler = informed_handler;
+
+ log_debug("PRC: Done.");
+
+ client->state = S_BOUND;
+
+ script_init(client, "RENEW6", NULL);
+ if (client->old_lease != NULL)
+ script_write_params6(client, "old_",
+ client->old_lease->options);
+ script_write_params6(client, "new_", client->active_lease->options);
+ script_go(client);
+
+ go_daemon();
+
+ if (client->old_lease != NULL) {
+ dhc6_lease_destroy(&client->old_lease, MDL);
+ client->old_lease = NULL;
+ }
+
+ /* Schedule events. */
+ dhc6_check_irt(client);
+}
+
+/* While informed, ignore packets.
+ */
+void
+informed_handler(struct packet *packet, struct client_state *client)
+{
+ log_debug("RCV: Input packets are ignored once bound.");
+}
+
+/* make_client6_options() fetches option caches relevant to the client's
+ * scope and places them into the sent_options cache. This cache is later
+ * used to populate DHCPv6 output packets with options.
+ */
+static void
+make_client6_options(struct client_state *client, struct option_state **op,
+ struct dhc6_lease *lease, u_int8_t message)
+{
+ struct option_cache *oc;
+ struct option **req;
+ struct buffer *buffer;
+ int buflen, i, oro_len;
+
+ if ((op == NULL) || (client == NULL))
+ return;
+
+ if (*op)
+ option_state_dereference(op, MDL);
+
+ /* Create a cache to carry options to transmission. */
+ option_state_allocate(op, MDL);
+
+ /* Create and store an 'elapsed time' option in the cache. */
+ oc = NULL;
+ if (option_cache_allocate(&oc, MDL)) {
+ const unsigned char *cdata;
+
+ cdata = (unsigned char *)&client->elapsed;
+
+ if (make_const_data(&oc->expression, cdata, 2, 0, 0, MDL)) {
+ option_reference(&oc->option, elapsed_option, MDL);
+ save_option(&dhcpv6_universe, *op, oc);
+ }
+
+ option_cache_dereference(&oc, MDL);
+ }
+
+ /* Bring in any configured options to send. */
+ if (client->config->on_transmission)
+ execute_statements_in_scope(NULL, NULL, NULL, client,
+ lease ? lease->options : NULL,
+ *op, &global_scope,
+ client->config->on_transmission,
+ NULL);
+
+ /* Rapid-commit is only for SOLICITs. */
+ if (message != DHCPV6_SOLICIT)
+ delete_option(&dhcpv6_universe, *op, D6O_RAPID_COMMIT);
+
+ /* See if the user configured a DUID in a relevant scope. If not,
+ * introduce our default manufactured id.
+ */
+ if ((oc = lookup_option(&dhcpv6_universe, *op,
+ D6O_CLIENTID)) == NULL) {
+ if (!option_cache(&oc, &default_duid, NULL, clientid_option,
+ MDL))
+ log_fatal("Failure assembling a DUID.");
+
+ save_option(&dhcpv6_universe, *op, oc);
+ option_cache_dereference(&oc, MDL);
+ }
+
+ /* In cases where we're responding to a single server, put the
+ * server's id in the response.
+ *
+ * Note that lease is NULL for SOLICIT or INFO request messages,
+ * and otherwise MUST be present.
+ */
+ if (lease == NULL) {
+ if ((message != DHCPV6_SOLICIT) &&
+ (message != DHCPV6_INFORMATION_REQUEST))
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ } else if ((message != DHCPV6_REBIND) &&
+ (message != DHCPV6_CONFIRM)) {
+ oc = lookup_option(&dhcpv6_universe, lease->options,
+ D6O_SERVERID);
+ if (oc != NULL)
+ save_option(&dhcpv6_universe, *op, oc);
+ }
+
+ /* 'send dhcp6.oro foo;' syntax we used in 4.0.0a1/a2 has been
+ * deprecated by adjustments to the 'request' syntax also used for
+ * DHCPv4.
+ */
+ if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) != NULL)
+ log_error("'send dhcp6.oro' syntax is deprecated, please "
+ "use the 'request' syntax (\"man dhclient.conf\").");
+
+ /* Construct and store an ORO (Option Request Option). It is a
+ * fatal error to fail to send an ORO (of at least zero length).
+ *
+ * Discussion: RFC3315 appears to be inconsistent in its statements
+ * of whether or not the ORO is mandatory. In section 18.1.1
+ * ("Creation and Transmission of Request Messages"):
+ *
+ * The client MUST include an Option Request option (see section
+ * 22.7) to indicate the options the client is interested in
+ * receiving. The client MAY include options with data values as
+ * hints to the server about parameter values the client would like
+ * to have returned.
+ *
+ * This MUST is missing from the creation/transmission of other
+ * messages (such as Renew and Rebind), and the section 22.7 ("Option
+ * Request Option" format and definition):
+ *
+ * A client MAY include an Option Request option in a Solicit,
+ * Request, Renew, Rebind, Confirm or Information-request message to
+ * inform the server about options the client wants the server to
+ * send to the client. A server MAY include an Option Request
+ * option in a Reconfigure option to indicate which options the
+ * client should request from the server.
+ *
+ * seems to relax the requirement from MUST to MAY (and still other
+ * language in RFC3315 supports this).
+ *
+ * In lieu of a clarification of RFC3315, we will conform with the
+ * MUST. Instead of an absent ORO, we will if there are no options
+ * to request supply an empty ORO. Theoretically, an absent ORO is
+ * difficult to interpret (does the client want all options or no
+ * options?). A zero-length ORO is intuitively clear: requesting
+ * nothing.
+ */
+ buffer = NULL;
+ oro_len = 0;
+ buflen = 32;
+ if (!buffer_allocate(&buffer, buflen, MDL))
+ log_fatal("Out of memory constructing DHCPv6 ORO.");
+ req = client->config->requested_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if (buflen == oro_len) {
+ struct buffer *tmpbuf = NULL;
+
+ buflen += 32;
+
+ /* Shell game. */
+ buffer_reference(&tmpbuf, buffer, MDL);
+ buffer_dereference(&buffer, MDL);
+
+ if (!buffer_allocate(&buffer, buflen, MDL))
+ log_fatal("Out of memory resizing "
+ "DHCPv6 ORO buffer.");
+
+ memcpy(buffer->data, tmpbuf->data, oro_len);
+
+ buffer_dereference(&tmpbuf, MDL);
+ }
+
+ if (req[i]->universe == &dhcpv6_universe) {
+ /* Append the code to the ORO. */
+ putUShort(buffer->data + oro_len,
+ req[i]->code);
+ oro_len += 2;
+ }
+ }
+ }
+
+ oc = NULL;
+ if (make_const_option_cache(&oc, &buffer, NULL, oro_len,
+ oro_option, MDL)) {
+ save_option(&dhcpv6_universe, *op, oc);
+ } else {
+ log_fatal("Unable to create ORO option cache.");
+ }
+
+ /*
+ * Note: make_const_option_cache() consumes the buffer, we do not
+ * need to dereference it (XXX).
+ */
+ option_cache_dereference(&oc, MDL);
+}
+
+/* A clone of the DHCPv4 script_write_params() minus the DHCPv4-specific
+ * filename, server-name, etc specifics.
+ *
+ * Simply, store all values present in all universes of the option state
+ * (probably derived from a DHCPv6 packet) into environment variables
+ * named after the option names (and universe names) but with the 'prefix'
+ * prepended.
+ *
+ * Later, dhclient-script may compare for example "new_time_servers" and
+ * "old_time_servers" for differences, and only upon detecting a change
+ * bother to rewrite ntp.conf and restart it. Or something along those
+ * generic lines.
+ */
+static void
+script_write_params6(struct client_state *client, const char *prefix,
+ struct option_state *options)
+{
+ struct envadd_state es;
+ int i;
+
+ if (options == NULL)
+ return;
+
+ es.client = client;
+ es.prefix = prefix;
+
+ for (i = 0 ; i < options->universe_count ; i++) {
+ option_space_foreach(NULL, NULL, client, NULL, options,
+ &global_scope, universes[i], &es,
+ client_option_envadd);
+ }
+}
+
+/*
+ * Check if there is something not fully defined in the active lease.
+ */
+static isc_boolean_t
+active_prefix(struct client_state *client)
+{
+ struct dhc6_lease *lease;
+ struct dhc6_ia *ia;
+ struct dhc6_addr *pref;
+ char zeros[16];
+
+ lease = client->active_lease;
+ if (lease == NULL)
+ return ISC_FALSE;
+ memset(zeros, 0, 16);
+ for (ia = lease->bindings; ia != NULL; ia = ia->next) {
+ if (ia->ia_type != D6O_IA_PD)
+ continue;
+ for (pref = ia->addrs; pref != NULL; pref = pref->next) {
+ if (pref->plen == 0)
+ return ISC_FALSE;
+ if (pref->address.len != 16)
+ return ISC_FALSE;
+ if (memcmp(pref->address.iabuf, zeros, 16) == 0)
+ return ISC_FALSE;
+ }
+ }
+ return ISC_TRUE;
+}
+#endif /* DHCPv6 */
diff --git a/client/dhclient-script.8 b/client/dhclient-script.8
new file mode 100644
index 0000000..7bce675
--- /dev/null
+++ b/client/dhclient-script.8
@@ -0,0 +1,234 @@
+.\" dhclient-script.8
+.\"
+.\" Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004-2005 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.\" $Id: dhclient-script.8,v 1.12.24.2 2010-07-06 19:03:11 sar Exp $
+.\"
+.TH dhclient-script 8
+.SH NAME
+dhclient-script - DHCP client network configuration script
+.SH DESCRIPTION
+The DHCP client network configuration script is invoked from time to
+time by \fBdhclient(8)\fR. 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
+This script is not meant to be customized by the end user. If local
+customizations are needed, they should be possible using the enter and
+exit hooks provided (see HOOKS for details). These hooks will allow the
+user to override the default behaviour of the client in creating a
+.B /etc/resolv.conf
+file.
+.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
+.B ETCDIR/dhclient.conf
+file. If you find that you can't make such a customization without
+customizing
+.B ETCDIR/dhclient.conf
+or using the enter and exit hooks, please submit a bug report.
+.SH HOOKS
+When it starts, the client script first defines a shell function,
+.B make_resolv_conf ,
+which is later used to create the
+.B /etc/resolv.conf
+file. To override the default behaviour, redefine this function in
+the enter hook script.
+.PP
+On after defining the make_resolv_conf function, the client script checks
+for the presence of an executable
+.B ETCDIR/dhclient-enter-hooks
+script, and if present, it invokes the script inline, using the Bourne
+shell \'.\' command. The entire environment documented under OPERATION
+is available to this script, which may modify the environment if needed
+to change the behaviour of the script. If an error occurs during the
+execution of the script, it can set the exit_status variable to a nonzero
+value, and
+.B CLIENTBINDIR/dhclient-script
+will exit with that error code immediately after the client script exits.
+.PP
+After all processing has completed,
+.B CLIENTBINDIR/dhclient-script
+checks for the presence of an executable
+.B ETCDIR/dhclient-exit-hooks
+script, which if present is invoked using the \'.\' command. The exit
+status of dhclient-script will be passed to dhclient-exit-hooks in the
+exit_status shell variable, and will always be zero if the script
+succeeded at the task for which it was invoked. The rest of the
+environment as described previously for dhclient-enter-hooks is also
+present. The
+.B ETCDIR/dhclient-exit-hooks
+script can modify the valid of exit_status to change the exit status
+of dhclient-script.
+.SH OPERATION
+When dhclient needs to invoke the client configuration script, it
+defines a set of variables in the environment, and then invokes
+.B CLIENTBINDIR/dhclient-script.
+In all cases, $reason is set to the name of the reason why the script
+has been invoked. The following reasons are currently defined:
+MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT, EXPIRE, FAIL, STOP, RELEASE,
+NBI and TIMEOUT.
+.PP
+.SH MEDIUM
+The DHCP client is requesting that an interface's media type
+be set. The interface name is passed in $interface, and the media
+type is passed in $medium.
+.SH 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 $interface,
+and the media type in $medium.
+.PP
+If an IP alias has been declared in dhclient.conf, its address will be
+passed in $alias_ip_address, and that ip alias should be deleted from
+the interface, along with any routes to it.
+.SH BOUND
+The DHCP client has done an initial binding to a new address. The
+new ip address is passed in $new_ip_address, and the interface name is
+passed in $interface. The media type is passed in $medium. Any
+options acquired from the server are passed using the option name
+described in \fBdhcp-options\fR, except that dashes (\'-\') are replaced
+by underscores (\'_\') 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 $new_subnet_mask.
+.PP
+Before actually configuring the address, dhclient-script should
+somehow ARP for it and exit with a nonzero status if it receives a
+reply. In this case, the client will send a DHCPDECLINE message to
+the server and acquire a different address. This may also be done in
+the RENEW, REBIND, or REBOOT states, but is not required, and indeed
+may not be desirable.
+.PP
+When a binding has been completed, a lot of network parameters are
+likely to need to be set up. A new /etc/resolv.conf needs to be
+created, using the values of $new_domain_name and
+$new_domain_name_servers (which may list more than one server,
+separated by spaces). A default route should be set using
+$new_routers, and static routes may need to be set up using
+$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 $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
+($new_ip_address), since the other alias parameters may be incorrect
+in this case.
+.SH 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.
+.SH 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.
+.SH REBOOT
+The DHCP client has successfully reacquired its old address after a
+reboot. This can be processed as with BOUND.
+.SH 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.
+.SH 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.
+.SH STOP
+The dhclient has been informed to shut down gracefully, the
+dhclient-script should unconfigure or shutdown the interface as
+appropriate.
+.SH RELEASE
+The dhclient has been executed using the -r flag, indicating that the
+administrator wishes it to release its lease(s). dhclient-script should
+unconfigure or shutdown the interface.
+.SH NBI
+No-Broadcast-Interfaces...dhclient was unable to find any interfaces
+upon which it believed it should commence DHCP. What dhclient-script
+should do in this situation is entirely up to the implementor.
+.SH 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.
+.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 $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 $new_routers, as well as those listed in
+$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 Internet
+Systems 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 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 /etc/resolv.conf. If
+more than one interface is being configured, /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.
+.SH SEE ALSO
+dhclient(8), dhcpd(8), dhcrelay(8), dhclient.conf(5) and
+dhclient.leases(5).
+.SH AUTHOR
+.B dhclient-script(8)
+has been written for Internet Systems Consortium
+by Ted Lemon in cooperation with Vixie
+Enterprises. To learn more about Internet Systems Consortium,
+see
+.B https://www.isc.org.
+To learn more about Vixie
+Enterprises, see
+.B http://www.vix.com.
diff --git a/client/dhclient.8 b/client/dhclient.8
new file mode 100644
index 0000000..6306b08
--- /dev/null
+++ b/client/dhclient.8
@@ -0,0 +1,486 @@
+.\" $Id: dhclient.8,v 1.32.24.4 2011-04-15 22:12:50 sar Exp $
+.\"
+.\" Copyright (c) 2004,2007-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH dhclient 8
+.SH NAME
+dhclient - Dynamic Host Configuration Protocol Client
+.SH SYNOPSIS
+.B dhclient
+[
+.B -4
+|
+.B -6
+]
+[
+.B -S
+]
+[
+.B -N
+[
+.B -N...
+]
+]
+[
+.B -T
+[
+.B -T...
+]
+]
+[
+.B -P
+[
+.B -P...
+]
+]
+[
+.B -D
+.I LL|LLT
+]
+[
+.B -p
+.I port
+]
+[
+.B -d
+]
+[
+.B -e
+.I VAR=value
+]
+[
+.B -q
+]
+[
+.B -1
+]
+[
+.B -r
+|
+.B -x
+]
+[
+.B -lf
+.I lease-file
+]
+[
+.B -pf
+.I pid-file
+]
+[
+.B --no-pid
+]
+[
+.B -cf
+.I config-file
+]
+[
+.B -sf
+.I script-file
+]
+[
+.B -s
+.I server-addr
+]
+[
+.B -g
+.I relay
+]
+[
+.B -n
+]
+[
+.B -nw
+]
+[
+.B -w
+]
+[
+.B -v
+]
+[
+.B --version
+]
+[
+.I if0
+[
+.I ...ifN
+]
+]
+.SH DESCRIPTION
+The Internet Systems Consortium DHCP Client, \fBdhclient\fR, provides a
+means for configuring one or more network interfaces using the Dynamic
+Host Configuration Protocol, BOOTP protocol, or if these protocols
+fail, by statically assigning an address.
+.SH OPERATION
+.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 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
+There are two versions of the DHCP protocol DHCPv4 and DHCPv6. At
+startup the client may be started for one or the other via the
+.B -4
+or
+.B -6
+options.
+.PP
+On startup, \fBdhclient\fR reads the dhclient.conf
+for configuration instructions. It then gets a list of all the
+network interfaces that are configured in the current system. For
+each interface, it attempts to configure the interface using the DHCP
+protocol.
+.PP
+In order to keep track of leases across system reboots and server
+restarts, \fBdhclient\fR keeps a list of leases it has been assigned in the
+dhclient.leases file. On startup, after reading the dhclient.conf
+file, \fBdhclient\fR reads the dhclient.leases file to refresh its memory
+about what leases it has been assigned.
+.PP
+When a new lease is acquired, it is appended to the end of the
+dhclient.leases file. In order to prevent the file from becoming
+arbitrarily large, from time to time \fBdhclient\fR creates a new
+dhclient.leases file from its in-core lease database. The old version
+of the dhclient.leases file is retained under the name
+.IR dhclient.leases~
+until the next time \fBdhclient\fR rewrites the database.
+.PP
+Old leases are kept around in case the DHCP server is unavailable when
+\fBdhclient\fR is first invoked (generally during the initial system boot
+process). In that event, old leases from the dhclient.leases 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, \fBdhclient\fR will try to validate the static lease, and if it
+succeeds, 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 COMMAND LINE
+.PP
+The names of the network interfaces that \fBdhclient\fR should attempt to
+configure may be specified on the command line. If no interface names
+are specified on the command line \fBdhclient\fR will normally identify all
+network interfaces, eliminating non-broadcast interfaces if
+possible, and attempt to configure each interface.
+.PP
+It is also possible to specify interfaces by name in the dhclient.conf
+file. If interfaces are specified in this way, then the client will
+only configure interfaces that are either specified in the
+configuration file or on the command line, and will ignore all other
+interfaces.
+.PP
+The client normally prints no output during its startup sequence. It
+can be made to emit verbose messages displaying the startup sequence events
+until it has acquired an address by supplying the
+.B -v
+command line argument. In either case, the client logs messages using
+the
+.B syslog(3)
+facility.
+.SH OPTIONS
+.TP
+.BI \-4
+Use the DHCPv4 protocol to obtain an IPv4 address and configuration
+parameters. This is the default and cannot be combined with
+\fB\-6\fR.
+.TP
+.BI \-6
+Use the DHCPv6 protocol to obtain whatever IPv6 addresses are available
+along with configuration parameters. It cannot be combined with
+\fB\-4\fR. The \fB\-S -T -P -N\fR and
+\fB\-D\fR arguments provide more control over aspects of the DHCPv6
+processing. Note: it is not recommended to mix queries of different
+types together or even to share the lease file between them.
+.TP
+.BI \-1
+Try to get a lease once. On failure exit with code 2. In DHCPv6 this
+sets the maximum duration of the initial exchange to
+.I timeout
+(from
+.IR dhclient.conf(5)
+with a default of sixty seconds).
+.TP
+.BI \-d
+.\" This is not intuitive.
+Force
+.B dhclient
+to run as a foreground process. Normally the DHCP client will run
+in the foreground until is has configured an interface at which time
+it will revert to running in the background. This option is useful
+when running the client under a debugger, or when running it out of
+inittab on System V systems. This implies \fB-v\fR.
+.TP
+.BI \-nw
+Become a daemon immediately (nowait) rather than waiting until an
+an IP address has been acquired.
+.TP
+.BI \-q
+Be quiet at startup, this is the default.
+.TP
+.BI \-v
+Enable verbose log messages.
+.\" This prints the version, copyright and URL.
+.TP
+.BI \-w
+Continue running even if no broadcast interfaces were found. Normally
+DHCP client will exit if it isn't able to identify any network interfaces
+to configure. On laptop computers and other computers with
+hot-swappable I/O buses, it is possible that a broadcast interface may
+be added after system startup. This flag can be used to cause the client
+not to exit when it doesn't find any such interfaces. The
+.B omshell(1)
+program can then be used to notify the client when a network interface
+has been added or removed, so that the client can attempt to configure an IP
+address on that interface.
+.TP
+.BI \-n
+Do not configure any interfaces. This is most likely to be useful in
+combination with the
+.B -w
+flag.
+.TP
+.BI \-e \ VAR=val
+Define additional environment variables for the environment where
+.B dhclient-script(8)
+executes. You may specify multiple
+.B \-e
+options on the command line.
+.TP
+.BI \-r
+Release the current lease and stop the running DHCP client as previously
+recorded in the PID file. When shutdown via this method
+.B dhclient-script(8)
+will be executed with the specific reason for calling the script set.
+The client normally doesn't release the current lease as this is not
+required by the DHCP protocol but some cable ISPs require their clients
+to notify the server if they wish to release an assigned IP address.
+.\" TODO what dhclient-script argument?
+.\" When released,
+.TP
+.BI \-x
+Stop the running DHCP client without releasing the current lease.
+Kills existing \fBdhclient\fR process as previously recorded in the
+PID file. When shutdown via this method
+.B dhclient-script(8)
+will be executed with the specific reason for calling the script set.
+.TP
+.BI \-p \ port
+The UDP port number on which the DHCP client should listen and transmit.
+If unspecified,
+.B dhclient
+uses the default port of 68. This is mostly useful for debugging purposes.
+If a different port is specified on which the client should listen and
+transmit, the client will also use a different destination port -
+one less than the specified port.
+.TP
+.BI \-s \ server-addr
+Specify the server IP address or fully qualified domain name to use as
+a destination for DHCP protocol messages before
+.B dhclient
+has acquired an IP address. Normally,
+.B dhclient
+transmits these messages to 255.255.255.255 (the IP limited broadcast
+address). Overriding this is mostly useful for debugging purposes. This
+feature is not supported in DHCPv6 (\fB-6\fR) mode.
+.TP
+.BI \-g \ relay
+.\" mockup relay
+Set the giaddr field of all packets to the \fIrelay\fR IP address
+simulating a relay agent. This is for testing pruposes only and
+should not be expected to work in any consistent or useful way.
+.TP
+.BI \--version
+Print version number and exit.
+.PP
+.I Options available for DHCPv6 mode:
+.TP
+.BI \-S
+.\" TODO: mention DUID?
+Use Information-request to get only stateless configuration parameters
+(i.e., without address). This implies \fB\-6\fR. It also doesn't
+rewrite the lease database.
+.\" TODO: May not be used with -N -P or -T. ??
+.TP
+.BI \-T
+.\" TODO wanted_ia_ta++
+Ask for IPv6 temporary addresses, one set per \fB\-T\fR flag. This
+implies \fB\-6\fR and also disables the normal address query.
+See \fB\-N\fR to restore it.
+.TP
+.BI \-P
+Enable IPv6 prefix delegation. This implies \fB\-6\fR and also
+disables the normal address query. See \fB\-N\fR to restore it.
+Note only one requested interface is allowed.
+.TP
+.BI \-D \ LL\ or\ LLT
+Override the default when selecting the type of DUID to use. By default,
+DHCPv6 \fBdhclient\fR creates an identifier based on the link-layer address
+(DUID-LL) if it is running in stateless mode (with \fB\-S\fR, not
+requesting an address), or it creates an identifier based on the
+link-layer address plus a timestamp (DUID-LLT) if it is running in
+stateful mode (without \fB\-S\fR, requesting an address). \fB\-D\fR
+overrides this default, with a value of either \fILL\fR or \fILLT\fR.
+.TP
+.BI \-N
+.\" TODO: is this for telling an already running dhclient?
+Restore normal address query for IPv6. This implies \fB-6\fR.
+It is used to restore normal operation after using \fB-T\fR or \fB-P\fR.
+.PP
+.I Modifying default file locations:
+The following options can be used to modify the locations a client uses
+for it's files. They can be particularly useful if, for example,
+.B DBDIR
+or
+.B RUNDIR
+have not been mounted when the DHCP client is started.
+.TP
+.BI \-cf \ config-file
+Path to the client configuration file. If unspecified, the default
+.B ETCDIR/dhclient.conf
+is used. See \fBdhclient.conf(5)\fR for a description of this file.
+.TP
+.BI \-lf \ lease-file
+Path to the lease database file. If unspecified, the default
+.B DBDIR/dhclient.leases
+is used. See \fBdhclient.leases(5)\fR for a descriptionof this file.
+.TP
+.BI \-pf \ pid-file
+Path to the process ID file. If unspecified, the default
+.B RUNDIR/dhclient.pid
+is used.
+.TP
+.BI \--no-pid
+Option to disable writing pid files. By default the program
+will write a pid file. If the program is invoked with this
+option it will not attempt to kill any existing client processes
+even if invoked with \fB-r\fR or \fB-x\fR.
+.TP
+.BI \-sf \ script-file
+Path to the network configuration script invoked by
+.B dhclient
+when it gets a lease. If unspecified, the default
+.B CLIENTBINDIR/dhclient-script
+is used. See \fBdhclient-script(8)\fR for a description of this file.
+
+
+.PP
+.SH CONFIGURATION
+The syntax of the \fBdhclient.conf(5)\fR file is discussed separately.
+.SH OMAPI
+The DHCP client provides some ability to control it while it is
+running, without stopping it. This capability is provided using OMAPI,
+an API for manipulating remote objects. OMAPI clients connect to the
+client using TCP/IP, authenticate, and can then examine the client's
+current status and make changes to it.
+.PP
+Rather than implementing the underlying OMAPI protocol directly, user
+programs should use the dhcpctl API or OMAPI itself. Dhcpctl is a
+wrapper that handles some of the housekeeping chores that OMAPI does
+not do automatically. Dhcpctl and OMAPI are documented in
+\fBdhcpctl(3)\fR
+and \fBomapi(3)\fR. Most things you'd want to do with the client can
+be done directly using the \fBomshell(1)\fR command, rather than
+having to write a special program.
+.SH THE CONTROL OBJECT
+The control object allows you to shut the client down, releasing all
+leases that it holds and deleting any DNS records it may have added.
+It also allows you to pause the client - this unconfigures any
+interfaces the client is using. You can then restart it, which
+causes it to reconfigure those interfaces. You would normally pause
+the client prior to going into hibernation or sleep on a laptop
+computer. You would then resume it after the power comes back.
+This allows PC cards to be shut down while the computer is hibernating
+or sleeping, and then reinitialized to their previous state once the
+computer comes out of hibernation or sleep.
+.PP
+The control object has one attribute - the state attribute. To shut
+the client down, set its state attribute to 2. It will automatically
+do a DHCPRELEASE. To pause it, set its state attribute to 3. To
+resume it, set its state attribute to 4.
+.PP
+.SH ENVIRONMENT VARIABLES
+.PP
+The following environment variables may be defined
+to override the builtin defaults for file locations.
+Note that use of the related command-line options
+will ignore the corresponding environment variable settings.
+.TP
+.B PATH_DHCLIENT_CONF
+The dhclient.conf configuration file.
+.TP
+.B PATH_DHCLIENT_DB
+The dhclient.leases database.
+.TP
+.B PATH_DHCLIENT_PID
+The dhclient PID file.
+.TP
+.B PATH_DHCLIENT_SCRIPT
+The dhclient-script file.
+.PP
+.SH FILES
+.B CLIENTBINDIR/dhclient-script,
+.B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid,
+.B DBDIR/dhclient.leases~.
+.SH SEE ALSO
+dhcpd(8), dhcrelay(8), dhclient-script(8), dhclient.conf(5),
+dhclient.leases(5), dhcp-eval(5).
+.SH AUTHOR
+.B dhclient(8)
+has been written for Internet Systems Consortium
+by Ted Lemon in cooperation with Vixie
+Enterprises. To learn more about Internet Systems Consortium,
+see
+.B https://www.isc.org
+To learn more about Vixie
+Enterprises, see
+.B http://www.vix.com.
+.PP
+This client was substantially modified and enhanced by Elliot Poger
+for use on Linux while he was working on the MosquitoNet project at
+Stanford.
+.PP
+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 Systems
+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.
+.PP
diff --git a/client/dhclient.c b/client/dhclient.c
new file mode 100644
index 0000000..48707d1
--- /dev/null
+++ b/client/dhclient.c
@@ -0,0 +1,4282 @@
+/* dhclient.c
+
+ DHCP Client. */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This code is based on the original client state machine that was
+ * written by Elliot Poger. The code has been extensively hacked on
+ * by Ted Lemon since then, so any mistakes you find are probably his
+ * fault and not Elliot's.
+ */
+
+#include "dhcpd.h"
+#include <syslog.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <dns/result.h>
+
+TIME default_lease_time = 43200; /* 12 hours... */
+TIME max_lease_time = 86400; /* 24 hours... */
+
+const char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
+const char *path_dhclient_db = NULL;
+const char *path_dhclient_pid = NULL;
+static char path_dhclient_script_array[] = _PATH_DHCLIENT_SCRIPT;
+char *path_dhclient_script = path_dhclient_script_array;
+
+/* False (default) => we write and use a pid file */
+isc_boolean_t no_pid_file = ISC_FALSE;
+
+int dhcp_max_agent_option_packet_length = 0;
+
+int interfaces_requested = 0;
+
+struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
+struct iaddr iaddr_any = { 4, { 0, 0, 0, 0 } };
+struct in_addr inaddr_any;
+struct sockaddr_in sockaddr_broadcast;
+struct in_addr giaddr;
+struct data_string default_duid;
+int duid_type = 0;
+
+/* ASSERT_STATE() does nothing now; it used to be
+ assert (state_is == state_shouldbe). */
+#define ASSERT_STATE(state_is, state_shouldbe) {}
+
+static const char copyright[] =
+"Copyright 2004-2011 Internet Systems Consortium.";
+static const char arr [] = "All rights reserved.";
+static const char message [] = "Internet Systems Consortium DHCP Client";
+static const char url [] =
+"For info, please visit https://www.isc.org/software/dhcp/";
+
+u_int16_t local_port = 0;
+u_int16_t remote_port = 0;
+int no_daemon = 0;
+struct string_list *client_env = NULL;
+int client_env_count = 0;
+int onetry = 0;
+int quiet = 1;
+int nowait = 0;
+int stateless = 0;
+int wanted_ia_na = -1; /* the absolute value is the real one. */
+int wanted_ia_ta = 0;
+int wanted_ia_pd = 0;
+char *mockup_relay = NULL;
+
+void run_stateless(int exit_mode);
+
+static void usage(void);
+
+static isc_result_t write_duid(struct data_string *duid);
+static void add_reject(struct packet *packet);
+
+static int check_domain_name(const char *ptr, size_t len, int dots);
+static int check_domain_name_list(const char *ptr, size_t len, int dots);
+static int check_option_values(struct universe *universe, unsigned int opt,
+ const char *ptr, size_t len);
+
+int
+main(int argc, char **argv) {
+ int fd;
+ int i;
+ struct interface_info *ip;
+ struct client_state *client;
+ unsigned seed;
+ char *server = NULL;
+ isc_result_t status;
+ int exit_mode = 0;
+ int release_mode = 0;
+ struct timeval tv;
+ omapi_object_t *listener;
+ isc_result_t result;
+ int persist = 0;
+ int no_dhclient_conf = 0;
+ int no_dhclient_db = 0;
+ int no_dhclient_pid = 0;
+ int no_dhclient_script = 0;
+#ifdef DHCPv6
+ int local_family_set = 0;
+#endif /* DHCPv6 */
+ char *s;
+
+ /* Initialize client globals. */
+ memset(&default_duid, 0, sizeof(default_duid));
+
+ /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
+ 2 (stderr) are open. To do this, we assume that when we
+ open a file the lowest available file descriptor is used. */
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 0)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 1)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 2)
+ log_perror = 0; /* No sense logging to /dev/null. */
+ else if (fd != -1)
+ close(fd);
+
+ openlog("dhclient", LOG_NDELAY, LOG_DAEMON);
+
+#if !(defined(DEBUG) || defined(__CYGWIN32__))
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+
+ /* Set up the isc and dns library managers */
+ status = dhcp_context_create();
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't initialize context: %s",
+ isc_result_totext(status));
+
+ /* Set up the OMAPI. */
+ status = omapi_init();
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't initialize OMAPI: %s",
+ isc_result_totext(status));
+
+ /* Set up the OMAPI wrappers for various server database internal
+ objects. */
+ dhcp_common_objects_setup();
+
+ dhcp_interface_discovery_hook = dhclient_interface_discovery_hook;
+ dhcp_interface_shutdown_hook = dhclient_interface_shutdown_hook;
+ dhcp_interface_startup_hook = dhclient_interface_startup_hook;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-r")) {
+ release_mode = 1;
+ no_daemon = 1;
+#ifdef DHCPv6
+ } else if (!strcmp(argv[i], "-4")) {
+ if (local_family_set && local_family != AF_INET)
+ log_fatal("Client can only do v4 or v6, not "
+ "both.");
+ local_family_set = 1;
+ local_family = AF_INET;
+ } else if (!strcmp(argv[i], "-6")) {
+ if (local_family_set && local_family != AF_INET6)
+ log_fatal("Client can only do v4 or v6, not "
+ "both.");
+ local_family_set = 1;
+ local_family = AF_INET6;
+#endif /* DHCPv6 */
+ } else if (!strcmp(argv[i], "-x")) { /* eXit, no release */
+ release_mode = 0;
+ no_daemon = 0;
+ exit_mode = 1;
+ } else if (!strcmp(argv[i], "-p")) {
+ if (++i == argc)
+ usage();
+ local_port = validate_port(argv[i]);
+ log_debug("binding to user-specified port %d",
+ ntohs(local_port));
+ } else if (!strcmp(argv[i], "-d")) {
+ no_daemon = 1;
+ quiet = 0;
+ } else if (!strcmp(argv[i], "-pf")) {
+ if (++i == argc)
+ usage();
+ path_dhclient_pid = argv[i];
+ no_dhclient_pid = 1;
+ } else if (!strcmp(argv[i], "--no-pid")) {
+ no_pid_file = ISC_TRUE;
+ } else if (!strcmp(argv[i], "-cf")) {
+ if (++i == argc)
+ usage();
+ path_dhclient_conf = argv[i];
+ no_dhclient_conf = 1;
+ } else if (!strcmp(argv[i], "-lf")) {
+ if (++i == argc)
+ usage();
+ path_dhclient_db = argv[i];
+ no_dhclient_db = 1;
+ } else if (!strcmp(argv[i], "-sf")) {
+ if (++i == argc)
+ usage();
+ path_dhclient_script = argv[i];
+ no_dhclient_script = 1;
+ } else if (!strcmp(argv[i], "-1")) {
+ onetry = 1;
+ } else if (!strcmp(argv[i], "-q")) {
+ quiet = 1;
+ } else if (!strcmp(argv[i], "-s")) {
+ if (++i == argc)
+ usage();
+ server = argv[i];
+ } else if (!strcmp(argv[i], "-g")) {
+ if (++i == argc)
+ usage();
+ mockup_relay = argv[i];
+ } else if (!strcmp(argv[i], "-nw")) {
+ nowait = 1;
+ } else if (!strcmp(argv[i], "-n")) {
+ /* do not start up any interfaces */
+ interfaces_requested = -1;
+ } else if (!strcmp(argv[i], "-w")) {
+ /* do not exit if there are no broadcast interfaces. */
+ persist = 1;
+ } else if (!strcmp(argv[i], "-e")) {
+ struct string_list *tmp;
+ if (++i == argc)
+ usage();
+ tmp = dmalloc(strlen(argv[i]) + sizeof *tmp, MDL);
+ if (!tmp)
+ log_fatal("No memory for %s", argv[i]);
+ strcpy(tmp->string, argv[i]);
+ tmp->next = client_env;
+ client_env = tmp;
+ client_env_count++;
+#ifdef DHCPv6
+ } else if (!strcmp(argv[i], "-S")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ wanted_ia_na = 0;
+ stateless = 1;
+ } else if (!strcmp(argv[i], "-N")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (wanted_ia_na < 0) {
+ wanted_ia_na = 0;
+ }
+ wanted_ia_na++;
+ } else if (!strcmp(argv[i], "-T")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (wanted_ia_na < 0) {
+ wanted_ia_na = 0;
+ }
+ wanted_ia_ta++;
+ } else if (!strcmp(argv[i], "-P")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (wanted_ia_na < 0) {
+ wanted_ia_na = 0;
+ }
+ wanted_ia_pd++;
+ } else if (!strcmp(argv[i], "-D")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (++i == argc)
+ usage();
+ if (!strcasecmp(argv[i], "LL")) {
+ duid_type = DUID_LL;
+ } else if (!strcasecmp(argv[i], "LLT")) {
+ duid_type = DUID_LLT;
+ } else {
+ usage();
+ }
+#endif /* DHCPv6 */
+ } else if (!strcmp(argv[i], "-v")) {
+ quiet = 0;
+ } else if (!strcmp(argv[i], "--version")) {
+ log_info("isc-dhclient-%s", PACKAGE_VERSION);
+ exit(0);
+ } else if (argv[i][0] == '-') {
+ usage();
+ } else if (interfaces_requested < 0) {
+ usage();
+ } else {
+ struct interface_info *tmp = NULL;
+
+ status = interface_allocate(&tmp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't record interface %s:%s",
+ argv[i], isc_result_totext(status));
+ if (strlen(argv[i]) >= sizeof(tmp->name))
+ log_fatal("%s: interface name too long (is %ld)",
+ argv[i], (long)strlen(argv[i]));
+ strcpy(tmp->name, argv[i]);
+ if (interfaces) {
+ interface_reference(&tmp->next,
+ interfaces, MDL);
+ interface_dereference(&interfaces, MDL);
+ }
+ interface_reference(&interfaces, tmp, MDL);
+ tmp->flags = INTERFACE_REQUESTED;
+ interfaces_requested++;
+ }
+ }
+
+ if (wanted_ia_na < 0) {
+ wanted_ia_na = 1;
+ }
+
+ /* Support only one (requested) interface for Prefix Delegation. */
+ if (wanted_ia_pd && (interfaces_requested != 1)) {
+ usage();
+ }
+
+ if (!no_dhclient_conf && (s = getenv("PATH_DHCLIENT_CONF"))) {
+ path_dhclient_conf = s;
+ }
+ if (!no_dhclient_db && (s = getenv("PATH_DHCLIENT_DB"))) {
+ path_dhclient_db = s;
+ }
+ if (!no_dhclient_pid && (s = getenv("PATH_DHCLIENT_PID"))) {
+ path_dhclient_pid = s;
+ }
+ if (!no_dhclient_script && (s = getenv("PATH_DHCLIENT_SCRIPT"))) {
+ path_dhclient_script = s;
+ }
+
+ /* Set up the initial dhcp option universe. */
+ initialize_common_option_spaces();
+
+ /* Assign v4 or v6 specific running parameters. */
+ if (local_family == AF_INET)
+ dhcpv4_client_assignments();
+#ifdef DHCPv6
+ else if (local_family == AF_INET6)
+ dhcpv6_client_assignments();
+#endif /* DHCPv6 */
+ else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /*
+ * convert relative path names to absolute, for files that need
+ * to be reopened after chdir() has been called
+ */
+ if (path_dhclient_db[0] != '/') {
+ char *path = dmalloc(PATH_MAX, MDL);
+ if (path == NULL)
+ log_fatal("No memory for filename\n");
+ path_dhclient_db = realpath(path_dhclient_db, path);
+ if (path_dhclient_db == NULL)
+ log_fatal("%s: %s", path, strerror(errno));
+ }
+
+ if (path_dhclient_script[0] != '/') {
+ char *path = dmalloc(PATH_MAX, MDL);
+ if (path == NULL)
+ log_fatal("No memory for filename\n");
+ path_dhclient_script = realpath(path_dhclient_script, path);
+ if (path_dhclient_script == NULL)
+ log_fatal("%s: %s", path, strerror(errno));
+ }
+
+ /*
+ * See if we should kill off any currently running client
+ * we don't try to kill it off if the user told us not
+ * to write a pid file - we assume they are controlling
+ * the process in some other fashion.
+ */
+ if ((release_mode || exit_mode) && (no_pid_file == ISC_FALSE)) {
+ FILE *pidfd;
+ pid_t oldpid;
+ long temp;
+ int e;
+
+ oldpid = 0;
+ if ((pidfd = fopen(path_dhclient_pid, "r")) != NULL) {
+ e = fscanf(pidfd, "%ld\n", &temp);
+ oldpid = (pid_t)temp;
+
+ if (e != 0 && e != EOF) {
+ if (oldpid)
+ kill(oldpid, SIGTERM);
+ }
+ fclose(pidfd);
+ }
+ }
+
+ if (!quiet) {
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info(copyright);
+ log_info(arr);
+ log_info(url);
+ log_info("%s", "");
+ } else {
+ log_perror = 0;
+ quiet_interface_discovery = 1;
+ }
+
+ /* If we're given a relay agent address to insert, for testing
+ purposes, figure out what it is. */
+ if (mockup_relay) {
+ if (!inet_aton(mockup_relay, &giaddr)) {
+ struct hostent *he;
+ he = gethostbyname(mockup_relay);
+ if (he) {
+ memcpy(&giaddr, he->h_addr_list[0],
+ sizeof giaddr);
+ } else {
+ log_fatal("%s: no such host", mockup_relay);
+ }
+ }
+ }
+
+ /* Get the current time... */
+ gettimeofday(&cur_tv, NULL);
+
+ sockaddr_broadcast.sin_family = AF_INET;
+ sockaddr_broadcast.sin_port = remote_port;
+ if (server) {
+ if (!inet_aton(server, &sockaddr_broadcast.sin_addr)) {
+ struct hostent *he;
+ he = gethostbyname(server);
+ if (he) {
+ memcpy(&sockaddr_broadcast.sin_addr,
+ he->h_addr_list[0],
+ sizeof sockaddr_broadcast.sin_addr);
+ } else
+ sockaddr_broadcast.sin_addr.s_addr =
+ INADDR_BROADCAST;
+ }
+ } else {
+ sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
+ }
+
+ inaddr_any.s_addr = INADDR_ANY;
+
+ /* Stateless special case. */
+ if (stateless) {
+ if (release_mode || (wanted_ia_na > 0) ||
+ wanted_ia_ta || wanted_ia_pd ||
+ (interfaces_requested != 1)) {
+ usage();
+ }
+ run_stateless(exit_mode);
+ return 0;
+ }
+
+ /* Discover all the network interfaces. */
+ discover_interfaces(DISCOVER_UNCONFIGURED);
+
+ /* Parse the dhclient.conf file. */
+ read_client_conf();
+
+ /* Parse the lease database. */
+ read_client_leases();
+
+ /* Rewrite the lease database... */
+ rewrite_client_leases();
+
+ /* XXX */
+/* config_counter(&snd_counter, &rcv_counter); */
+
+ /*
+ * If no broadcast interfaces were discovered, call the script
+ * and tell it so.
+ */
+ if (!interfaces) {
+ /*
+ * Call dhclient-script with the NBI flag,
+ * in case somebody cares.
+ */
+ script_init(NULL, "NBI", NULL);
+ script_go(NULL);
+
+ /*
+ * If we haven't been asked to persist, waiting for new
+ * interfaces, then just exit.
+ */
+ if (!persist) {
+ /* Nothing more to do. */
+ log_info("No broadcast interfaces found - exiting.");
+ exit(0);
+ }
+ } else if (!release_mode && !exit_mode) {
+ /* Call the script with the list of interfaces. */
+ for (ip = interfaces; ip; ip = ip->next) {
+ /*
+ * If interfaces were specified, don't configure
+ * interfaces that weren't specified!
+ */
+ if ((interfaces_requested > 0) &&
+ ((ip->flags & (INTERFACE_REQUESTED |
+ INTERFACE_AUTOMATIC)) !=
+ INTERFACE_REQUESTED))
+ continue;
+
+ if (local_family == AF_INET6) {
+ script_init(ip->client, "PREINIT6", NULL);
+ } else {
+ script_init(ip->client, "PREINIT", NULL);
+ if (ip->client->alias != NULL)
+ script_write_params(ip->client,
+ "alias_",
+ ip->client->alias);
+ }
+ script_go(ip->client);
+ }
+ }
+
+ /* At this point, all the interfaces that the script thinks
+ are relevant should be running, so now we once again call
+ discover_interfaces(), and this time ask it to actually set
+ up the interfaces. */
+ discover_interfaces(interfaces_requested != 0
+ ? DISCOVER_REQUESTED
+ : DISCOVER_RUNNING);
+
+ /* Make up a seed for the random number generator from current
+ time plus the sum of the last four bytes of each
+ interface's hardware address interpreted as an integer.
+ Not much entropy, but we're booting, so we're not likely to
+ find anything better. */
+ seed = 0;
+ for (ip = interfaces; ip; ip = ip->next) {
+ int junk;
+ memcpy(&junk,
+ &ip->hw_address.hbuf[ip->hw_address.hlen -
+ sizeof seed], sizeof seed);
+ seed += junk;
+ }
+ srandom(seed + cur_time + (unsigned)getpid());
+
+ /* Start a configuration state machine for each interface. */
+#ifdef DHCPv6
+ if (local_family == AF_INET6) {
+ /* Establish a default DUID. This may be moved to the
+ * DHCPv4 area later.
+ */
+ if (default_duid.len == 0) {
+ if (default_duid.buffer != NULL)
+ data_string_forget(&default_duid, MDL);
+
+ form_duid(&default_duid, MDL);
+ write_duid(&default_duid);
+ }
+
+ for (ip = interfaces ; ip != NULL ; ip = ip->next) {
+ for (client = ip->client ; client != NULL ;
+ client = client->next) {
+ if (release_mode) {
+ start_release6(client);
+ continue;
+ } else if (exit_mode) {
+ unconfigure6(client, "STOP6");
+ continue;
+ }
+
+ /* If we have a previous binding, Confirm
+ * that we can (or can't) still use it.
+ */
+ if ((client->active_lease != NULL) &&
+ !client->active_lease->released)
+ start_confirm6(client);
+ else
+ start_init6(client);
+ }
+ }
+ } else
+#endif /* DHCPv6 */
+ {
+ for (ip = interfaces ; ip ; ip = ip->next) {
+ ip->flags |= INTERFACE_RUNNING;
+ for (client = ip->client ; client ;
+ client = client->next) {
+ if (exit_mode)
+ state_stop(client);
+ else if (release_mode)
+ do_release(client);
+ else {
+ client->state = S_INIT;
+
+ if (top_level_config.initial_delay>0)
+ {
+ tv.tv_sec = 0;
+ if (top_level_config.
+ initial_delay>1)
+ tv.tv_sec = cur_time
+ + random()
+ % (top_level_config.
+ initial_delay-1);
+ tv.tv_usec = random()
+ % 1000000;
+ /*
+ * this gives better
+ * distribution than just
+ *whole seconds
+ */
+ add_timeout(&tv, state_reboot,
+ client, 0, 0);
+ } else {
+ state_reboot(client);
+ }
+ }
+ }
+ }
+ }
+
+ if (exit_mode)
+ return 0;
+ if (release_mode) {
+#ifndef DHCPv6
+ return 0;
+#else
+ if (local_family == AF_INET6) {
+ if (onetry)
+ return 0;
+ } else
+ return 0;
+#endif /* DHCPv6 */
+ }
+
+ /* Start up a listener for the object management API protocol. */
+ if (top_level_config.omapi_port != -1) {
+ listener = NULL;
+ result = omapi_generic_new(&listener, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal("Can't allocate new generic object: %s\n",
+ isc_result_totext(result));
+ result = omapi_protocol_listen(listener,
+ (unsigned)
+ top_level_config.omapi_port,
+ 1);
+ if (result != ISC_R_SUCCESS)
+ log_fatal("Can't start OMAPI protocol: %s",
+ isc_result_totext (result));
+ }
+
+ /* Set up the bootp packet handler... */
+ bootp_packet_handler = do_packet;
+#ifdef DHCPv6
+ dhcpv6_packet_handler = do_packet6;
+#endif /* DHCPv6 */
+
+#if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \
+ defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ dmalloc_cutoff_generation = dmalloc_generation;
+ dmalloc_longterm = dmalloc_outstanding;
+ dmalloc_outstanding = 0;
+#endif
+
+ /* If we're not supposed to wait before getting the address,
+ don't. */
+ if (nowait)
+ go_daemon();
+
+ /* If we're not going to daemonize, write the pid file
+ now. */
+ if (no_daemon || nowait)
+ write_client_pid_file();
+
+ /* Start dispatching packets and timeouts... */
+ dispatch();
+
+ /*NOTREACHED*/
+ return 0;
+}
+
+static void usage()
+{
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info(copyright);
+ log_info(arr);
+ log_info(url);
+
+
+ log_fatal("Usage: dhclient "
+#ifdef DHCPv6
+ "[-4|-6] [-SNTP1dvrx] [-nw] [-p <port>] [-D LL|LLT]\n"
+#else /* DHCPv6 */
+ "[-1dvrx] [-nw] [-p <port>]\n"
+#endif /* DHCPv6 */
+ " [-s server-addr] [-cf config-file] "
+ "[-lf lease-file]\n"
+ " [-pf pid-file] [--no-pid] [-e VAR=val]\n"
+ " [-sf script-file] [interface]");
+}
+
+void run_stateless(int exit_mode)
+{
+#ifdef DHCPv6
+ struct client_state *client;
+ omapi_object_t *listener;
+ isc_result_t result;
+
+ /* Discover the network interface. */
+ discover_interfaces(DISCOVER_REQUESTED);
+
+ if (!interfaces)
+ usage();
+
+ /* Parse the dhclient.conf file. */
+ read_client_conf();
+
+ /* Parse the lease database. */
+ read_client_leases();
+
+ /* Establish a default DUID. */
+ if (default_duid.len == 0) {
+ if (default_duid.buffer != NULL)
+ data_string_forget(&default_duid, MDL);
+
+ form_duid(&default_duid, MDL);
+ }
+
+ /* Start a configuration state machine. */
+ for (client = interfaces->client ;
+ client != NULL ;
+ client = client->next) {
+ if (exit_mode) {
+ unconfigure6(client, "STOP6");
+ continue;
+ }
+ start_info_request6(client);
+ }
+ if (exit_mode)
+ return;
+
+ /* Start up a listener for the object management API protocol. */
+ if (top_level_config.omapi_port != -1) {
+ listener = NULL;
+ result = omapi_generic_new(&listener, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal("Can't allocate new generic object: %s\n",
+ isc_result_totext(result));
+ result = omapi_protocol_listen(listener,
+ (unsigned)
+ top_level_config.omapi_port,
+ 1);
+ if (result != ISC_R_SUCCESS)
+ log_fatal("Can't start OMAPI protocol: %s",
+ isc_result_totext(result));
+ }
+
+ /* Set up the packet handler... */
+ dhcpv6_packet_handler = do_packet6;
+
+#if defined(DEBUG_MEMORY_LEAKAGE) || defined(DEBUG_MALLOC_POOL) || \
+ defined(DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ dmalloc_cutoff_generation = dmalloc_generation;
+ dmalloc_longterm = dmalloc_outstanding;
+ dmalloc_outstanding = 0;
+#endif
+
+ /* If we're not supposed to wait before getting the address,
+ don't. */
+ if (nowait)
+ go_daemon();
+
+ /* If we're not going to daemonize, write the pid file
+ now. */
+ if (no_daemon || nowait)
+ write_client_pid_file();
+
+ /* Start dispatching packets and timeouts... */
+ dispatch();
+
+ /*NOTREACHED*/
+#endif /* DHCPv6 */
+ return;
+}
+
+isc_result_t find_class (struct class **c,
+ const char *s, const char *file, int line)
+{
+ return 0;
+}
+
+int check_collection (packet, lease, collection)
+ struct packet *packet;
+ struct lease *lease;
+ struct collection *collection;
+{
+ return 0;
+}
+
+void classify (packet, class)
+ struct packet *packet;
+ struct class *class;
+{
+}
+
+int unbill_class (lease, class)
+ struct lease *lease;
+ struct class *class;
+{
+ return 0;
+}
+
+int find_subnet (struct subnet **sp,
+ struct iaddr addr, const char *file, int line)
+{
+ return 0;
+}
+
+/* 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 (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ /* If we don't remember an active lease, go straight to INIT. */
+ if (!client -> active ||
+ client -> active -> is_bootp ||
+ client -> active -> expiry <= cur_time) {
+ state_init (client);
+ return;
+ }
+
+ /* We are in the rebooting state. */
+ 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.
+ */
+ client -> xid = random ();
+
+ /*
+ * Make a DHCPREQUEST packet, and set
+ * appropriate per-interface flags.
+ */
+ make_request (client, client -> active);
+ client -> destination = iaddr_broadcast;
+ client -> first_sending = cur_time;
+ client -> interval = client -> config -> initial_interval;
+
+ /* Zap the medium list... */
+ client -> medium = NULL;
+
+ /* Send out the first DHCPREQUEST packet. */
+ send_request (client);
+}
+
+/* Called when a lease has completely expired and we've been unable to
+ renew it. */
+
+void state_init (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ ASSERT_STATE(state, S_INIT);
+
+ /* Make a DHCPDISCOVER packet, and set appropriate per-interface
+ flags. */
+ make_discover (client, client -> active);
+ client -> xid = client -> packet.xid;
+ client -> destination = iaddr_broadcast;
+ client -> state = S_SELECTING;
+ client -> first_sending = cur_time;
+ client -> interval = client -> config -> initial_interval;
+
+ /* Add an immediate timeout to cause the first DHCPDISCOVER packet
+ to go out. */
+ send_discover (client);
+}
+
+/*
+ * state_selecting is called when one or more DHCPOFFER packets have been
+ * received and a configurable period of time has passed.
+ */
+
+void state_selecting (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+ 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, client);
+ cancel_timeout (send_discover, client);
+
+ /*
+ * 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 = 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) {
+ picked = lp;
+ picked -> next = NULL;
+ } else {
+ destroy_client_lease (lp);
+ }
+ }
+ client -> offered_leases = NULL;
+
+ /*
+ * If we just tossed all the leases we were offered, go back
+ * to square one.
+ */
+ if (!picked) {
+ client -> state = S_INIT;
+ state_init (client);
+ return;
+ }
+
+ /* If it was a BOOTREPLY, we can just take the address right now. */
+ if (picked -> is_bootp) {
+ client -> new = picked;
+
+ /* Make up some lease expiry times
+ XXX these should be configurable. */
+ client -> new -> expiry = cur_time + 12000;
+ client -> new -> renewal += cur_time + 8000;
+ client -> new -> rebind += cur_time + 10000;
+
+ client -> state = S_REQUESTING;
+
+ /* Bind to the address we received. */
+ bind_lease (client);
+ return;
+ }
+
+ /* Go to the REQUESTING state. */
+ client -> destination = iaddr_broadcast;
+ client -> state = S_REQUESTING;
+ client -> first_sending = cur_time;
+ client -> interval = client -> config -> initial_interval;
+
+ /* Make a DHCPREQUEST packet from the lease we picked. */
+ make_request (client, picked);
+ client -> xid = client -> packet.xid;
+
+ /* Toss the lease we picked - we'll get it back in a DHCPACK. */
+ destroy_client_lease (picked);
+
+ /* Add an immediate timeout to send the first DHCPREQUEST packet. */
+ send_request (client);
+}
+
+/* state_requesting is called when we receive a DHCPACK message after
+ having sent out one or more DHCPREQUEST packets. */
+
+void dhcpack (packet)
+ struct packet *packet;
+{
+ struct interface_info *ip = packet -> interface;
+ struct client_state *client;
+ struct client_lease *lease;
+ struct option_cache *oc;
+ struct data_string ds;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ for (client = ip -> client; client; client = client -> next) {
+ if (client -> xid == packet -> raw -> xid)
+ break;
+ }
+ if (!client ||
+ (packet -> interface -> hw_address.hlen - 1 !=
+ packet -> raw -> hlen) ||
+ (memcmp (&packet -> interface -> hw_address.hbuf [1],
+ packet -> raw -> chaddr, packet -> raw -> hlen))) {
+#if defined (DEBUG)
+ log_debug ("DHCPACK in wrong transaction.");
+#endif
+ return;
+ }
+
+ if (client -> state != S_REBOOTING &&
+ client -> state != S_REQUESTING &&
+ client -> state != S_RENEWING &&
+ client -> state != S_REBINDING) {
+#if defined (DEBUG)
+ log_debug ("DHCPACK in wrong state.");
+#endif
+ return;
+ }
+
+ log_info ("DHCPACK from %s", piaddr (packet -> client_addr));
+
+ lease = packet_to_lease (packet, client);
+ if (!lease) {
+ log_info ("packet_to_lease failed.");
+ return;
+ }
+
+ client -> new = lease;
+
+ /* Stop resending DHCPREQUEST. */
+ cancel_timeout (send_request, client);
+
+ /* Figure out the lease time. */
+ oc = lookup_option (&dhcp_universe, client -> new -> options,
+ DHO_DHCP_LEASE_TIME);
+ memset (&ds, 0, sizeof ds);
+ if (oc &&
+ evaluate_option_cache (&ds, packet, (struct lease *)0, client,
+ packet -> options, client -> new -> options,
+ &global_scope, oc, MDL)) {
+ if (ds.len > 3)
+ client -> new -> expiry = getULong (ds.data);
+ else
+ client -> new -> expiry = 0;
+ data_string_forget (&ds, MDL);
+ } else
+ client -> new -> expiry = 0;
+
+ if (client->new->expiry == 0) {
+ struct timeval tv;
+
+ log_error ("no expiry time on offered lease.");
+
+ /* Quench this (broken) server. Return to INIT to reselect. */
+ add_reject(packet);
+
+ /* 1/2 second delay to restart at INIT. */
+ tv.tv_sec = cur_tv.tv_sec;
+ tv.tv_usec = cur_tv.tv_usec + 500000;
+
+ if (tv.tv_usec >= 1000000) {
+ tv.tv_sec++;
+ tv.tv_usec -= 1000000;
+ }
+
+ add_timeout(&tv, state_init, client, 0, 0);
+ return;
+ }
+
+ /*
+ * A number that looks negative here is really just very large,
+ * because the lease expiry offset is unsigned.
+ */
+ if (client->new->expiry < 0)
+ client->new->expiry = TIME_MAX;
+
+ /* Take the server-provided renewal time if there is one. */
+ oc = lookup_option (&dhcp_universe, client -> new -> options,
+ DHO_DHCP_RENEWAL_TIME);
+ if (oc &&
+ evaluate_option_cache (&ds, packet, (struct lease *)0, client,
+ packet -> options, client -> new -> options,
+ &global_scope, oc, MDL)) {
+ if (ds.len > 3)
+ client -> new -> renewal = getULong (ds.data);
+ else
+ client -> new -> renewal = 0;
+ data_string_forget (&ds, MDL);
+ } else
+ client -> new -> renewal = 0;
+
+ /* If it wasn't specified by the server, calculate it. */
+ if (!client -> new -> renewal)
+ client -> new -> renewal = client -> new -> expiry / 2 + 1;
+
+ if (client -> new -> renewal <= 0)
+ client -> new -> renewal = TIME_MAX;
+
+ /* Now introduce some randomness to the renewal time: */
+ if (client->new->renewal <= ((TIME_MAX / 3) - 3))
+ client->new->renewal = (((client->new->renewal * 3) + 3) / 4) +
+ (((random() % client->new->renewal) + 3) / 4);
+
+ /* Same deal with the rebind time. */
+ oc = lookup_option (&dhcp_universe, client -> new -> options,
+ DHO_DHCP_REBINDING_TIME);
+ if (oc &&
+ evaluate_option_cache (&ds, packet, (struct lease *)0, client,
+ packet -> options, client -> new -> options,
+ &global_scope, oc, MDL)) {
+ if (ds.len > 3)
+ client -> new -> rebind = getULong (ds.data);
+ else
+ client -> new -> rebind = 0;
+ data_string_forget (&ds, MDL);
+ } else
+ client -> new -> rebind = 0;
+
+ if (client -> new -> rebind <= 0) {
+ if (client -> new -> expiry <= TIME_MAX / 7)
+ client -> new -> rebind =
+ client -> new -> expiry * 7 / 8;
+ else
+ client -> new -> rebind =
+ client -> new -> expiry / 8 * 7;
+ }
+
+ /* Make sure our randomness didn't run the renewal time past the
+ rebind time. */
+ if (client -> new -> renewal > client -> new -> rebind) {
+ if (client -> new -> rebind <= TIME_MAX / 3)
+ client -> new -> renewal =
+ client -> new -> rebind * 3 / 4;
+ else
+ client -> new -> renewal =
+ client -> new -> rebind / 4 * 3;
+ }
+
+ client -> new -> expiry += cur_time;
+ /* Lease lengths can never be negative. */
+ if (client -> new -> expiry < cur_time)
+ client -> new -> expiry = TIME_MAX;
+ client -> new -> renewal += cur_time;
+ if (client -> new -> renewal < cur_time)
+ client -> new -> renewal = TIME_MAX;
+ client -> new -> rebind += cur_time;
+ if (client -> new -> rebind < cur_time)
+ client -> new -> rebind = TIME_MAX;
+
+ bind_lease (client);
+}
+
+void bind_lease (client)
+ struct client_state *client;
+{
+ struct timeval tv;
+
+ /* Remember the medium. */
+ client -> new -> medium = client -> medium;
+
+ /* Run the client script with the new parameters. */
+ script_init (client, (client -> state == S_REQUESTING
+ ? "BOUND"
+ : (client -> state == S_RENEWING
+ ? "RENEW"
+ : (client -> state == S_REBOOTING
+ ? "REBOOT" : "REBIND"))),
+ client -> new -> medium);
+ if (client -> active && client -> state != S_REBOOTING)
+ script_write_params (client, "old_", client -> active);
+ script_write_params (client, "new_", client -> new);
+ if (client -> alias)
+ script_write_params (client, "alias_", client -> alias);
+
+ /* If the BOUND/RENEW code detects another machine using the
+ offered address, it exits nonzero. We need to send a
+ DHCPDECLINE and toss the lease. */
+ if (script_go (client)) {
+ make_decline (client, client -> new);
+ send_decline (client);
+ destroy_client_lease (client -> new);
+ client -> new = (struct client_lease *)0;
+ state_init (client);
+ return;
+ }
+
+ /* Write out the new lease if it has been long enough. */
+ if (!client->last_write ||
+ (cur_time - client->last_write) >= MIN_LEASE_WRITE)
+ write_client_lease(client, client->new, 0, 0);
+
+ /* Replace the old active lease with the new one. */
+ if (client -> active)
+ destroy_client_lease (client -> active);
+ client -> active = client -> new;
+ client -> new = (struct client_lease *)0;
+
+ /* Set up a timeout to start the renewal process. */
+ tv.tv_sec = client->active->renewal;
+ tv.tv_usec = ((client->active->renewal - cur_tv.tv_sec) > 1) ?
+ random() % 1000000 : cur_tv.tv_usec;
+ add_timeout(&tv, state_bound, client, 0, 0);
+
+ log_info ("bound to %s -- renewal in %ld seconds.",
+ piaddr (client -> active -> address),
+ (long)(client -> active -> renewal - cur_time));
+ client -> state = S_BOUND;
+ reinitialize_interfaces ();
+ go_daemon ();
+#if defined (NSUPDATE)
+ if (client->config->do_forward_update)
+ dhclient_schedule_updates(client, &client->active->address, 1);
+#endif
+}
+
+/* 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 (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+ struct option_cache *oc;
+ struct data_string ds;
+
+ ASSERT_STATE(state, S_BOUND);
+
+ /* T1 has expired. */
+ make_request (client, client -> active);
+ client -> xid = client -> packet.xid;
+
+ memset (&ds, 0, sizeof ds);
+ oc = lookup_option (&dhcp_universe, client -> active -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ if (oc &&
+ evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
+ client, (struct option_state *)0,
+ client -> active -> options,
+ &global_scope, oc, MDL)) {
+ if (ds.len > 3) {
+ memcpy (client -> destination.iabuf, ds.data, 4);
+ client -> destination.len = 4;
+ } else
+ client -> destination = iaddr_broadcast;
+
+ data_string_forget (&ds, MDL);
+ } else
+ client -> destination = iaddr_broadcast;
+
+ client -> first_sending = cur_time;
+ client -> interval = client -> config -> initial_interval;
+ client -> state = S_RENEWING;
+
+ /* Send the first packet immediately. */
+ send_request (client);
+}
+
+/* state_stop is called when we've been told to shut down. We unconfigure
+ the interfaces, and then stop operating until told otherwise. */
+
+void state_stop (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ /* Cancel all timeouts. */
+ cancel_timeout(state_selecting, client);
+ cancel_timeout(send_discover, client);
+ cancel_timeout(send_request, client);
+ cancel_timeout(state_bound, client);
+
+ /* If we have an address, unconfigure it. */
+ if (client->active) {
+ script_init(client, "STOP", client->active->medium);
+ script_write_params(client, "old_", client->active);
+ if (client->alias)
+ script_write_params(client, "alias_", client->alias);
+ script_go(client);
+ }
+}
+
+int commit_leases ()
+{
+ return 0;
+}
+
+int write_lease (lease)
+ struct lease *lease;
+{
+ return 0;
+}
+
+int write_host (host)
+ struct host_decl *host;
+{
+ return 0;
+}
+
+void db_startup (testp)
+ int testp;
+{
+}
+
+void bootp (packet)
+ struct packet *packet;
+{
+ struct iaddrmatchlist *ap;
+ char addrbuf[4*16];
+ char maskbuf[4*16];
+
+ 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_match(&packet->client_addr, &ap->match)) {
+
+ /* piaddr() returns its result in a static
+ buffer sized 4*16 (see common/inet.c). */
+
+ strcpy(addrbuf, piaddr(ap->match.addr));
+ strcpy(maskbuf, piaddr(ap->match.mask));
+
+ log_info("BOOTREPLY from %s rejected by rule %s "
+ "mask %s.", piaddr(packet->client_addr),
+ addrbuf, maskbuf);
+ return;
+ }
+ }
+
+ dhcpoffer (packet);
+
+}
+
+void dhcp (packet)
+ struct packet *packet;
+{
+ struct iaddrmatchlist *ap;
+ void (*handler) (struct packet *);
+ const char *type;
+ char addrbuf[4*16];
+ char maskbuf[4*16];
+
+ 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_match(&packet->client_addr, &ap->match)) {
+
+ /* piaddr() returns its result in a static
+ buffer sized 4*16 (see common/inet.c). */
+
+ strcpy(addrbuf, piaddr(ap->match.addr));
+ strcpy(maskbuf, piaddr(ap->match.mask));
+
+ log_info("%s from %s rejected by rule %s mask %s.",
+ type, piaddr(packet->client_addr),
+ addrbuf, maskbuf);
+ return;
+ }
+ }
+ (*handler) (packet);
+}
+
+#ifdef DHCPv6
+void
+dhcpv6(struct packet *packet) {
+ struct iaddrmatchlist *ap;
+ struct client_state *client;
+ char addrbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")];
+
+ /* Silently drop bogus messages. */
+ if (packet->dhcpv6_msg_type >= dhcpv6_type_name_max)
+ return;
+
+ /* Discard, with log, packets from quenched sources. */
+ for (ap = packet->interface->client->config->reject_list ;
+ ap ; ap = ap->next) {
+ if (addr_match(&packet->client_addr, &ap->match)) {
+ strcpy(addrbuf, piaddr(packet->client_addr));
+ log_info("%s from %s rejected by rule %s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ addrbuf,
+ piaddrmask(&ap->match.addr, &ap->match.mask));
+ return;
+ }
+ }
+
+ /* Screen out nonsensical messages. */
+ switch(packet->dhcpv6_msg_type) {
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_RECONFIGURE:
+ if (stateless)
+ return;
+ /* Falls through */
+ case DHCPV6_REPLY:
+ log_info("RCV: %s message on %s from %s.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ packet->interface->name, piaddr(packet->client_addr));
+ break;
+
+ default:
+ return;
+ }
+
+ /* Find a client state that matches the incoming XID. */
+ for (client = packet->interface->client ; client ;
+ client = client->next) {
+ if (memcmp(&client->dhcpv6_transaction_id,
+ packet->dhcpv6_transaction_id, 3) == 0) {
+ client->v6_handler(packet, client);
+ return;
+ }
+ }
+
+ /* XXX: temporary log for debugging */
+ log_info("Packet received, but nothing done with it.");
+}
+#endif /* DHCPv6 */
+
+void dhcpoffer (packet)
+ struct packet *packet;
+{
+ struct interface_info *ip = packet -> interface;
+ struct client_state *client;
+ struct client_lease *lease, *lp;
+ struct option **req;
+ int i;
+ int stop_selecting;
+ const char *name = packet -> packet_type ? "DHCPOFFER" : "BOOTREPLY";
+ char obuf [1024];
+ struct timeval tv;
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+#endif
+
+ /* Find a client state that matches the xid... */
+ for (client = ip -> client; client; client = client -> next)
+ if (client -> xid == packet -> raw -> xid)
+ break;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (!client ||
+ client -> state != S_SELECTING ||
+ (packet -> interface -> hw_address.hlen - 1 !=
+ packet -> raw -> hlen) ||
+ (memcmp (&packet -> interface -> hw_address.hbuf [1],
+ packet -> raw -> chaddr, packet -> raw -> hlen))) {
+#if defined (DEBUG)
+ log_debug ("%s in wrong transaction.", name);
+#endif
+ return;
+ }
+
+ sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr));
+
+
+ /* If this lease doesn't supply the minimum required DHCPv4 parameters,
+ * ignore it.
+ */
+ req = client->config->required_options;
+ if (req != NULL) {
+ for (i = 0 ; req[i] != NULL ; i++) {
+ if ((req[i]->universe == &dhcp_universe) &&
+ !lookup_option(&dhcp_universe, packet->options,
+ req[i]->code)) {
+ struct option *option = NULL;
+ unsigned code = req[i]->code;
+
+ option_code_hash_lookup(&option,
+ dhcp_universe.code_hash,
+ &code, 0, MDL);
+
+ if (option)
+ log_info("%s: no %s option.", obuf,
+ option->name);
+ else
+ log_info("%s: no unknown-%u option.",
+ obuf, code);
+
+ option_dereference(&option, MDL);
+
+ return;
+ }
+ }
+ }
+
+ /* If we've already seen this lease, don't record it again. */
+ for (lease = 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)) {
+ log_debug ("%s: already seen.", obuf);
+ return;
+ }
+ }
+
+ lease = packet_to_lease (packet, client);
+ if (!lease) {
+ log_info ("%s: packet_to_lease failed.", obuf);
+ return;
+ }
+
+ /* If this lease was acquired through a BOOTREPLY, record that
+ fact. */
+ if (!packet -> options_valid || !packet -> packet_type)
+ lease -> is_bootp = 1;
+
+ /* Record the medium under which this lease was offered. */
+ lease -> medium = client -> medium;
+
+ /* Figure out when we're supposed to stop selecting. */
+ stop_selecting = (client -> first_sending +
+ 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 == client -> requested_address.len &&
+ !memcmp (lease -> address.iabuf,
+ client -> requested_address.iabuf,
+ client -> requested_address.len)) {
+ lease -> next = client -> offered_leases;
+ client -> offered_leases = lease;
+ } else {
+ /* Put the lease at the end of the list. */
+ lease -> next = (struct client_lease *)0;
+ if (!client -> offered_leases)
+ client -> offered_leases = lease;
+ else {
+ for (lp = client -> offered_leases; lp -> next;
+ lp = lp -> next)
+ ;
+ lp -> next = lease;
+ }
+ }
+
+ /* If the selecting interval has expired, go immediately to
+ state_selecting(). Otherwise, time out into
+ state_selecting at the select interval. */
+ if (stop_selecting <= cur_tv.tv_sec)
+ state_selecting (client);
+ else {
+ tv.tv_sec = stop_selecting;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout(&tv, state_selecting, client, 0, 0);
+ cancel_timeout(send_discover, client);
+ }
+ log_info("%s", obuf);
+}
+
+/* Allocate a client_lease structure and initialize it from the parameters
+ in the specified packet. */
+
+struct client_lease *packet_to_lease (packet, client)
+ struct packet *packet;
+ struct client_state *client;
+{
+ struct client_lease *lease;
+ unsigned i;
+ struct option_cache *oc;
+ struct option *option = NULL;
+ struct data_string data;
+
+ lease = (struct client_lease *)new_client_lease (MDL);
+
+ if (!lease) {
+ log_error ("packet_to_lease: no memory to record lease.\n");
+ return (struct client_lease *)0;
+ }
+
+ memset (lease, 0, sizeof *lease);
+
+ /* Copy the lease options. */
+ option_state_reference (&lease -> options, packet -> options, MDL);
+
+ lease -> address.len = sizeof (packet -> raw -> yiaddr);
+ memcpy (lease -> address.iabuf, &packet -> raw -> yiaddr,
+ lease -> address.len);
+
+ memset (&data, 0, sizeof data);
+
+ if (client -> config -> vendor_space_name) {
+ i = DHO_VENDOR_ENCAPSULATED_OPTIONS;
+
+ /* See if there was a vendor encapsulation option. */
+ oc = lookup_option (&dhcp_universe, lease -> options, i);
+ if (oc &&
+ client -> config -> vendor_space_name &&
+ evaluate_option_cache (&data, packet,
+ (struct lease *)0, client,
+ packet -> options, lease -> options,
+ &global_scope, oc, MDL)) {
+ if (data.len) {
+ if (!option_code_hash_lookup(&option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL))
+ log_fatal("Unable to find VENDOR "
+ "option (%s:%d).", MDL);
+ parse_encapsulated_suboptions
+ (packet -> options, option,
+ data.data, data.len, &dhcp_universe,
+ client -> config -> vendor_space_name
+ );
+
+ option_dereference(&option, MDL);
+ }
+ data_string_forget (&data, MDL);
+ }
+ } else
+ i = 0;
+
+ /* Figure out the overload flag. */
+ oc = lookup_option (&dhcp_universe, lease -> options,
+ DHO_DHCP_OPTION_OVERLOAD);
+ if (oc &&
+ evaluate_option_cache (&data, packet, (struct lease *)0, client,
+ packet -> options, lease -> options,
+ &global_scope, oc, MDL)) {
+ if (data.len > 0)
+ i = data.data [0];
+ else
+ i = 0;
+ data_string_forget (&data, MDL);
+ } else
+ i = 0;
+
+ /* If the server name was filled out, copy it. */
+ if (!(i & 2) && packet -> raw -> sname [0]) {
+ unsigned len;
+ /* Don't count on the NUL terminator. */
+ for (len = 0; len < DHCP_SNAME_LEN; len++)
+ if (!packet -> raw -> sname [len])
+ break;
+ lease -> server_name = dmalloc (len + 1, MDL);
+ if (!lease -> server_name) {
+ log_error ("dhcpoffer: no memory for server name.\n");
+ destroy_client_lease (lease);
+ return (struct client_lease *)0;
+ } else {
+ memcpy (lease -> server_name,
+ packet -> raw -> sname, len);
+ lease -> server_name [len] = 0;
+ }
+ }
+
+ /* Ditto for the filename. */
+ if (!(i & 1) && packet -> raw -> file [0]) {
+ unsigned len;
+ /* Don't count on the NUL terminator. */
+ for (len = 0; len < DHCP_FILE_LEN; len++)
+ if (!packet -> raw -> file [len])
+ break;
+ lease -> filename = dmalloc (len + 1, MDL);
+ if (!lease -> filename) {
+ log_error ("dhcpoffer: no memory for filename.\n");
+ destroy_client_lease (lease);
+ return (struct client_lease *)0;
+ } else {
+ memcpy (lease -> filename,
+ packet -> raw -> file, len);
+ lease -> filename [len] = 0;
+ }
+ }
+
+ execute_statements_in_scope ((struct binding_value **)0,
+ (struct packet *)packet,
+ (struct lease *)0, client,
+ lease -> options, lease -> options,
+ &global_scope,
+ client -> config -> on_receipt,
+ (struct group *)0);
+
+ return lease;
+}
+
+void dhcpnak (packet)
+ struct packet *packet;
+{
+ struct interface_info *ip = packet -> interface;
+ struct client_state *client;
+
+ /* Find a client state that matches the xid... */
+ for (client = ip -> client; client; client = client -> next)
+ if (client -> xid == packet -> raw -> xid)
+ break;
+
+ /* If we're not receptive to an offer right now, or if the offer
+ has an unrecognizable transaction id, then just drop it. */
+ if (!client ||
+ (packet -> interface -> hw_address.hlen - 1 !=
+ packet -> raw -> hlen) ||
+ (memcmp (&packet -> interface -> hw_address.hbuf [1],
+ packet -> raw -> chaddr, packet -> raw -> hlen))) {
+#if defined (DEBUG)
+ log_debug ("DHCPNAK in wrong transaction.");
+#endif
+ return;
+ }
+
+ if (client -> state != S_REBOOTING &&
+ client -> state != S_REQUESTING &&
+ client -> state != S_RENEWING &&
+ client -> state != S_REBINDING) {
+#if defined (DEBUG)
+ log_debug ("DHCPNAK in wrong state.");
+#endif
+ return;
+ }
+
+ log_info ("DHCPNAK from %s", piaddr (packet -> client_addr));
+
+ if (!client -> active) {
+#if defined (DEBUG)
+ log_info ("DHCPNAK with no active lease.\n");
+#endif
+ return;
+ }
+
+ /* If we get a DHCPNAK, we use the EXPIRE dhclient-script state
+ * to indicate that we want all old bindings to be removed. (It
+ * is possible that we may get a NAK while in the RENEW state,
+ * so we might have bindings active at that time)
+ */
+ script_init(client, "EXPIRE", NULL);
+ script_write_params(client, "old_", client->active);
+ if (client->alias)
+ script_write_params(client, "alias_", client->alias);
+ script_go(client);
+
+ destroy_client_lease (client -> active);
+ client -> active = (struct client_lease *)0;
+
+ /* Stop sending DHCPREQUEST packets... */
+ cancel_timeout (send_request, client);
+
+ /* On some scripts, 'EXPIRE' causes the interface to be ifconfig'd
+ * down (this expunges any routes and arp cache). This makes the
+ * interface unusable by state_init(), which we call next. So, we
+ * need to 'PREINIT' the interface to bring it back up.
+ */
+ script_init(client, "PREINIT", NULL);
+ if (client->alias)
+ script_write_params(client, "alias_", client->alias);
+ script_go(client);
+
+ client -> state = S_INIT;
+ state_init (client);
+}
+
+/* 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 (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ int result;
+ int interval;
+ int increase = 1;
+ struct timeval tv;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - 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 > client -> config -> timeout) {
+ state_panic (client);
+ 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 (!client -> offered_leases &&
+ client -> config -> media) {
+ int fail = 0;
+ again:
+ if (client -> medium) {
+ client -> medium = client -> medium -> next;
+ increase = 0;
+ }
+ if (!client -> medium) {
+ if (fail)
+ log_fatal ("No valid media types for %s!",
+ client -> interface -> name);
+ client -> medium =
+ client -> config -> media;
+ increase = 1;
+ }
+
+ log_info ("Trying medium \"%s\" %d",
+ client -> medium -> string, increase);
+ script_init (client, "MEDIUM", client -> medium);
+ if (script_go (client)) {
+ fail = 1;
+ 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 initial_interval; 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 (!client->interval)
+ client->interval = client->config->initial_interval;
+ else
+ client->interval += random() % (2 * client->interval);
+
+ /* Don't backoff past cutoff. */
+ if (client->interval > client->config->backoff_cutoff)
+ client->interval = (client->config->backoff_cutoff / 2)
+ + (random() % client->config->backoff_cutoff);
+ } else if (!client->interval)
+ client->interval = client->config->initial_interval;
+
+ /* If the backoff would take us to the panic timeout, just use that
+ as the interval. */
+ if (cur_time + client -> interval >
+ client -> first_sending + client -> config -> timeout)
+ client -> interval =
+ (client -> first_sending +
+ client -> config -> timeout) - cur_time + 1;
+
+ /* Record the number of seconds since we started sending. */
+ if (interval < 65536)
+ client -> packet.secs = htons (interval);
+ else
+ client -> packet.secs = htons (65535);
+ client -> secs = client -> packet.secs;
+
+ log_info ("DHCPDISCOVER on %s to %s port %d interval %ld",
+ client -> name ? client -> name : client -> interface -> name,
+ inet_ntoa (sockaddr_broadcast.sin_addr),
+ ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval));
+
+ /* Send out a packet. */
+ result = send_packet (client -> interface, (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ inaddr_any, &sockaddr_broadcast,
+ (struct hardware *)0);
+
+ /*
+ * If we used 0 microseconds here, and there were other clients on the
+ * same network with a synchronized local clock (ntp), and a similar
+ * zero-microsecond-scheduler behavior, then we could be participating
+ * in a sub-second DOS ttck.
+ */
+ tv.tv_sec = cur_tv.tv_sec + client->interval;
+ tv.tv_usec = client->interval > 1 ? random() % 1000000 : cur_tv.tv_usec;
+ add_timeout(&tv, send_discover, client, 0, 0);
+}
+
+/* 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 (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+ struct client_lease *loop;
+ struct client_lease *lp;
+ struct timeval tv;
+
+ loop = lp = client -> active;
+
+ log_info ("No DHCPOFFERS received.");
+
+ /* We may not have an active lease, but we may have some
+ predefined leases that we can try. */
+ if (!client -> active && client -> leases)
+ goto activate_next;
+
+ /* Run through the list of leases and see if one can be used. */
+ while (client -> active) {
+ if (client -> active -> expiry > cur_time) {
+ log_info ("Trying recorded lease %s",
+ piaddr (client -> active -> address));
+ /* Run the client script with the existing
+ parameters. */
+ script_init (client, "TIMEOUT",
+ client -> active -> medium);
+ script_write_params (client, "new_", client -> active);
+ if (client -> alias)
+ script_write_params (client, "alias_",
+ 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 (client)) {
+ if (cur_time < client -> active -> renewal) {
+ client -> state = S_BOUND;
+ log_info ("bound: renewal in %ld %s.",
+ (long)(client -> active -> renewal -
+ cur_time), "seconds");
+ tv.tv_sec = client->active->renewal;
+ tv.tv_usec = ((client->active->renewal -
+ cur_time) > 1) ?
+ random() % 1000000 :
+ cur_tv.tv_usec;
+ add_timeout(&tv, state_bound, client, 0, 0);
+ } else {
+ client -> state = S_BOUND;
+ log_info ("bound: immediate renewal.");
+ state_bound (client);
+ }
+ reinitialize_interfaces ();
+ go_daemon ();
+ return;
+ }
+ }
+
+ /* If there are no other leases, give up. */
+ if (!client -> leases) {
+ client -> leases = client -> active;
+ client -> active = (struct client_lease *)0;
+ break;
+ }
+
+ activate_next:
+ /* Otherwise, put the active lease at the end of the
+ lease list, and try another lease.. */
+ for (lp = client -> leases; lp -> next; lp = lp -> next)
+ ;
+ lp -> next = client -> active;
+ if (lp -> next) {
+ lp -> next -> next = (struct client_lease *)0;
+ }
+ client -> active = client -> leases;
+ client -> leases = 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 (client -> active == loop)
+ break;
+ else if (!loop)
+ loop = 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. */
+ if (onetry) {
+ if (!quiet)
+ log_info ("Unable to obtain a lease on first try.%s",
+ " Exiting.");
+ exit (2);
+ }
+
+ log_info ("No working leases in persistent database - sleeping.");
+ script_init (client, "FAIL", (struct string_list *)0);
+ if (client -> alias)
+ script_write_params (client, "alias_", client -> alias);
+ script_go (client);
+ client -> state = S_INIT;
+ tv.tv_sec = cur_tv.tv_sec + ((client->config->retry_interval + 1) / 2 +
+ (random() % client->config->retry_interval));
+ tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
+ random() % 1000000 : cur_tv.tv_usec;
+ add_timeout(&tv, state_init, client, 0, 0);
+ go_daemon ();
+}
+
+void send_request (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ int result;
+ int interval;
+ struct sockaddr_in destination;
+ struct in_addr from;
+ struct timeval tv;
+
+ /* Figure out how long it's been since we started transmitting. */
+ interval = cur_time - 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 ((client -> state == S_REBOOTING ||
+ client -> state == S_REQUESTING) &&
+ interval > client -> config -> reboot_timeout) {
+ cancel:
+ client -> state = S_INIT;
+ cancel_timeout (send_request, client);
+ state_init (client);
+ return;
+ }
+
+ /* If we're in the reboot state, make sure the media is set up
+ correctly. */
+ if (client -> state == S_REBOOTING &&
+ !client -> medium &&
+ client -> active -> medium ) {
+ script_init (client, "MEDIUM", client -> active -> medium);
+
+ /* If the medium we chose won't fly, go to INIT state. */
+ if (script_go (client))
+ goto cancel;
+
+ /* Record the medium. */
+ client -> medium = client -> active -> medium;
+ }
+
+ /* If the lease has expired, relinquish the address and go back
+ to the INIT state. */
+ if (client -> state != S_REQUESTING &&
+ cur_time > client -> active -> expiry) {
+ /* Run the client script with the new parameters. */
+ script_init (client, "EXPIRE", (struct string_list *)0);
+ script_write_params (client, "old_", client -> active);
+ if (client -> alias)
+ script_write_params (client, "alias_",
+ client -> alias);
+ script_go (client);
+
+ /* Now do a preinit on the interface so that we can
+ discover a new address. */
+ script_init (client, "PREINIT", (struct string_list *)0);
+ if (client -> alias)
+ script_write_params (client, "alias_",
+ client -> alias);
+ script_go (client);
+
+ client -> state = S_INIT;
+ state_init (client);
+ return;
+ }
+
+ /* Do the exponential backoff... */
+ if (!client -> interval)
+ client -> interval = client -> config -> initial_interval;
+ else {
+ client -> interval += ((random () >> 2) %
+ (2 * client -> interval));
+ }
+
+ /* Don't backoff past cutoff. */
+ if (client -> interval >
+ client -> config -> backoff_cutoff)
+ client -> interval =
+ ((client -> config -> backoff_cutoff / 2)
+ + ((random () >> 2) %
+ client -> config -> backoff_cutoff));
+
+ /* If the backoff would take us to the expiry time, just set the
+ timeout to the expiry time. */
+ if (client -> state != S_REQUESTING &&
+ cur_time + client -> interval > client -> active -> expiry)
+ client -> interval =
+ 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. */
+ if (client -> state == S_REQUESTING ||
+ client -> state == S_REBOOTING ||
+ cur_time > client -> active -> rebind)
+ destination.sin_addr = sockaddr_broadcast.sin_addr;
+ else
+ memcpy (&destination.sin_addr.s_addr,
+ client -> destination.iabuf,
+ sizeof destination.sin_addr.s_addr);
+ destination.sin_port = remote_port;
+ destination.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ destination.sin_len = sizeof destination;
+#endif
+
+ if (client -> state == S_RENEWING ||
+ client -> state == S_REBINDING)
+ memcpy (&from, client -> active -> address.iabuf,
+ sizeof from);
+ else
+ from.s_addr = INADDR_ANY;
+
+ /* Record the number of seconds since we started sending. */
+ if (client -> state == S_REQUESTING)
+ client -> packet.secs = client -> secs;
+ else {
+ if (interval < 65536)
+ client -> packet.secs = htons (interval);
+ else
+ client -> packet.secs = htons (65535);
+ }
+
+ log_info ("DHCPREQUEST on %s to %s port %d",
+ client -> name ? client -> name : client -> interface -> name,
+ inet_ntoa (destination.sin_addr),
+ ntohs (destination.sin_port));
+
+ if (destination.sin_addr.s_addr != INADDR_BROADCAST &&
+ fallback_interface)
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+ else
+ /* Send out a packet. */
+ result = send_packet (client -> interface, (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+
+ tv.tv_sec = cur_tv.tv_sec + client->interval;
+ tv.tv_usec = ((tv.tv_sec - cur_tv.tv_sec) > 1) ?
+ random() % 1000000 : cur_tv.tv_usec;
+ add_timeout(&tv, send_request, client, 0, 0);
+}
+
+void send_decline (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ int result;
+
+ log_info ("DHCPDECLINE on %s to %s port %d",
+ client -> name ? client -> name : client -> interface -> name,
+ inet_ntoa (sockaddr_broadcast.sin_addr),
+ ntohs (sockaddr_broadcast.sin_port));
+
+ /* Send out a packet. */
+ result = send_packet (client -> interface, (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ inaddr_any, &sockaddr_broadcast,
+ (struct hardware *)0);
+}
+
+void send_release (cpp)
+ void *cpp;
+{
+ struct client_state *client = cpp;
+
+ int result;
+ struct sockaddr_in destination;
+ struct in_addr from;
+
+ memcpy (&from, client -> active -> address.iabuf,
+ sizeof from);
+ memcpy (&destination.sin_addr.s_addr,
+ client -> destination.iabuf,
+ sizeof destination.sin_addr.s_addr);
+ destination.sin_port = remote_port;
+ destination.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ destination.sin_len = sizeof destination;
+#endif
+
+ /* Set the lease to end now, so that we don't accidentally
+ reuse it if we restart before the old expiry time. */
+ client -> active -> expiry =
+ client -> active -> renewal =
+ client -> active -> rebind = cur_time;
+ if (!write_client_lease (client, client -> active, 1, 1)) {
+ log_error ("Can't release lease: lease write failed.");
+ return;
+ }
+
+ log_info ("DHCPRELEASE on %s to %s port %d",
+ client -> name ? client -> name : client -> interface -> name,
+ inet_ntoa (destination.sin_addr),
+ ntohs (destination.sin_port));
+
+ if (fallback_interface)
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+ else
+ /* Send out a packet. */
+ result = send_packet (client -> interface, (struct packet *)0,
+ &client -> packet,
+ client -> packet_length,
+ from, &destination,
+ (struct hardware *)0);
+}
+
+void
+make_client_options(struct client_state *client, struct client_lease *lease,
+ u_int8_t *type, struct option_cache *sid,
+ struct iaddr *rip, struct option **prl,
+ struct option_state **op)
+{
+ unsigned i;
+ struct option_cache *oc;
+ struct option *option = NULL;
+ struct buffer *bp = (struct buffer *)0;
+
+ /* If there are any leftover options, get rid of them. */
+ if (*op)
+ option_state_dereference (op, MDL);
+
+ /* Allocate space for options. */
+ option_state_allocate (op, MDL);
+
+ /* Send the server identifier if provided. */
+ if (sid)
+ save_option (&dhcp_universe, *op, sid);
+
+ oc = (struct option_cache *)0;
+
+ /* Send the requested address if provided. */
+ if (rip) {
+ client -> requested_address = *rip;
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash,
+ &i, 0, MDL) &&
+ make_const_option_cache(&oc, NULL, rip->iabuf, rip->len,
+ option, MDL)))
+ log_error ("can't make requested address cache.");
+ else {
+ save_option (&dhcp_universe, *op, oc);
+ option_cache_dereference (&oc, MDL);
+ }
+ option_dereference(&option, MDL);
+ } else {
+ client -> requested_address.len = 0;
+ }
+
+ i = DHO_DHCP_MESSAGE_TYPE;
+ if (!(option_code_hash_lookup(&option, dhcp_universe.code_hash, &i, 0,
+ MDL) &&
+ make_const_option_cache(&oc, NULL, type, 1, option, MDL)))
+ log_error ("can't make message type.");
+ else {
+ save_option (&dhcp_universe, *op, oc);
+ option_cache_dereference (&oc, MDL);
+ }
+ option_dereference(&option, MDL);
+
+ if (prl) {
+ int len;
+
+ /* Probe the length of the list. */
+ len = 0;
+ for (i = 0 ; prl[i] != NULL ; i++)
+ if (prl[i]->universe == &dhcp_universe)
+ len++;
+
+ if (!buffer_allocate (&bp, len, MDL))
+ log_error ("can't make parameter list buffer.");
+ else {
+ unsigned code = DHO_DHCP_PARAMETER_REQUEST_LIST;
+
+ len = 0;
+ for (i = 0 ; prl[i] != NULL ; i++)
+ if (prl[i]->universe == &dhcp_universe)
+ bp->data[len++] = prl[i]->code;
+
+ if (!(option_code_hash_lookup(&option,
+ dhcp_universe.code_hash,
+ &code, 0, MDL) &&
+ make_const_option_cache(&oc, &bp, NULL, len,
+ option, MDL)))
+ log_error ("can't make option cache");
+ else {
+ save_option (&dhcp_universe, *op, oc);
+ option_cache_dereference (&oc, MDL);
+ }
+ option_dereference(&option, MDL);
+ }
+ }
+
+ /* Run statements that need to be run on transmission. */
+ if (client -> config -> on_transmission)
+ execute_statements_in_scope
+ ((struct binding_value **)0,
+ (struct packet *)0, (struct lease *)0, client,
+ (lease ? lease -> options : (struct option_state *)0),
+ *op, &global_scope,
+ client -> config -> on_transmission,
+ (struct group *)0);
+}
+
+void make_discover (client, lease)
+ struct client_state *client;
+ struct client_lease *lease;
+{
+ unsigned char discover = DHCPDISCOVER;
+ struct option_state *options = (struct option_state *)0;
+
+ memset (&client -> packet, 0, sizeof (client -> packet));
+
+ make_client_options (client,
+ lease, &discover, (struct option_cache *)0,
+ lease ? &lease -> address : (struct iaddr *)0,
+ client -> config -> requested_options,
+ &options);
+
+ /* Set up the option buffer... */
+ client -> packet_length =
+ cons_options ((struct packet *)0, &client -> packet,
+ (struct lease *)0, client,
+ /* maximum packet size */1500,
+ (struct option_state *)0,
+ options,
+ /* scope */ &global_scope,
+ /* overload */ 0,
+ /* terminate */0,
+ /* bootpp */0,
+ (struct data_string *)0,
+ client -> config -> vendor_space_name);
+
+ option_state_dereference (&options, MDL);
+ if (client -> packet_length < BOOTP_MIN_LEN)
+ client -> packet_length = BOOTP_MIN_LEN;
+
+ client -> packet.op = BOOTREQUEST;
+ client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+ client -> packet.hops = 0;
+ client -> packet.xid = random ();
+ client -> packet.secs = 0; /* filled in by send_discover. */
+
+ if (can_receive_unicast_unconfigured (client -> interface))
+ client -> packet.flags = 0;
+ else
+ client -> packet.flags = htons (BOOTP_BROADCAST);
+
+ memset (&(client -> packet.ciaddr),
+ 0, sizeof client -> packet.ciaddr);
+ memset (&(client -> packet.yiaddr),
+ 0, sizeof client -> packet.yiaddr);
+ memset (&(client -> packet.siaddr),
+ 0, sizeof client -> packet.siaddr);
+ client -> packet.giaddr = giaddr;
+ if (client -> interface -> hw_address.hlen > 0)
+ memcpy (client -> packet.chaddr,
+ &client -> interface -> hw_address.hbuf [1],
+ (unsigned)(client -> interface -> hw_address.hlen - 1));
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
+#endif
+}
+
+
+void make_request (client, lease)
+ struct client_state *client;
+ struct client_lease *lease;
+{
+ unsigned char request = DHCPREQUEST;
+ struct option_cache *oc;
+
+ memset (&client -> packet, 0, sizeof (client -> packet));
+
+ if (client -> state == S_REQUESTING)
+ oc = lookup_option (&dhcp_universe, lease -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ else
+ oc = (struct option_cache *)0;
+
+ if (client -> sent_options)
+ option_state_dereference (&client -> sent_options, MDL);
+
+ make_client_options (client, lease, &request, oc,
+ ((client -> state == S_REQUESTING ||
+ client -> state == S_REBOOTING)
+ ? &lease -> address
+ : (struct iaddr *)0),
+ client -> config -> requested_options,
+ &client -> sent_options);
+
+ /* Set up the option buffer... */
+ client -> packet_length =
+ cons_options ((struct packet *)0, &client -> packet,
+ (struct lease *)0, client,
+ /* maximum packet size */1500,
+ (struct option_state *)0,
+ client -> sent_options,
+ /* scope */ &global_scope,
+ /* overload */ 0,
+ /* terminate */0,
+ /* bootpp */0,
+ (struct data_string *)0,
+ client -> config -> vendor_space_name);
+
+ if (client -> packet_length < BOOTP_MIN_LEN)
+ client -> packet_length = BOOTP_MIN_LEN;
+
+ client -> packet.op = BOOTREQUEST;
+ client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+ client -> packet.hops = 0;
+ client -> packet.xid = client -> xid;
+ 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 (client -> state == S_BOUND ||
+ client -> state == S_RENEWING ||
+ client -> state == S_REBINDING) {
+ memcpy (&client -> packet.ciaddr,
+ lease -> address.iabuf, lease -> address.len);
+ client -> packet.flags = 0;
+ } else {
+ memset (&client -> packet.ciaddr, 0,
+ sizeof client -> packet.ciaddr);
+ if (can_receive_unicast_unconfigured (client -> interface))
+ client -> packet.flags = 0;
+ else
+ client -> packet.flags = htons (BOOTP_BROADCAST);
+ }
+
+ memset (&client -> packet.yiaddr, 0,
+ sizeof client -> packet.yiaddr);
+ memset (&client -> packet.siaddr, 0,
+ sizeof client -> packet.siaddr);
+ if (client -> state != S_BOUND &&
+ client -> state != S_RENEWING)
+ client -> packet.giaddr = giaddr;
+ else
+ memset (&client -> packet.giaddr, 0,
+ sizeof client -> packet.giaddr);
+ if (client -> interface -> hw_address.hlen > 0)
+ memcpy (client -> packet.chaddr,
+ &client -> interface -> hw_address.hbuf [1],
+ (unsigned)(client -> interface -> hw_address.hlen - 1));
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
+#endif
+}
+
+void make_decline (client, lease)
+ struct client_state *client;
+ struct client_lease *lease;
+{
+ unsigned char decline = DHCPDECLINE;
+ struct option_cache *oc;
+
+ struct option_state *options = (struct option_state *)0;
+
+ /* Create the options cache. */
+ oc = lookup_option (&dhcp_universe, lease -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ make_client_options(client, lease, &decline, oc, &lease->address,
+ NULL, &options);
+
+ /* Consume the options cache into the option buffer. */
+ memset (&client -> packet, 0, sizeof (client -> packet));
+ client -> packet_length =
+ cons_options ((struct packet *)0, &client -> packet,
+ (struct lease *)0, client, 0,
+ (struct option_state *)0, options,
+ &global_scope, 0, 0, 0, (struct data_string *)0,
+ client -> config -> vendor_space_name);
+
+ /* Destroy the options cache. */
+ option_state_dereference (&options, MDL);
+
+ if (client -> packet_length < BOOTP_MIN_LEN)
+ client -> packet_length = BOOTP_MIN_LEN;
+
+ client -> packet.op = BOOTREQUEST;
+ client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+ client -> packet.hops = 0;
+ client -> packet.xid = client -> xid;
+ client -> packet.secs = 0; /* Filled in by send_request. */
+ if (can_receive_unicast_unconfigured (client -> interface))
+ client -> packet.flags = 0;
+ else
+ client -> packet.flags = htons (BOOTP_BROADCAST);
+
+ /* ciaddr must always be zero. */
+ memset (&client -> packet.ciaddr, 0,
+ sizeof client -> packet.ciaddr);
+ memset (&client -> packet.yiaddr, 0,
+ sizeof client -> packet.yiaddr);
+ memset (&client -> packet.siaddr, 0,
+ sizeof client -> packet.siaddr);
+ client -> packet.giaddr = giaddr;
+ memcpy (client -> packet.chaddr,
+ &client -> interface -> hw_address.hbuf [1],
+ client -> interface -> hw_address.hlen);
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
+#endif
+}
+
+void make_release (client, lease)
+ struct client_state *client;
+ struct client_lease *lease;
+{
+ unsigned char request = DHCPRELEASE;
+ struct option_cache *oc;
+
+ struct option_state *options = (struct option_state *)0;
+
+ memset (&client -> packet, 0, sizeof (client -> packet));
+
+ oc = lookup_option (&dhcp_universe, lease -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ make_client_options(client, lease, &request, oc, NULL, NULL, &options);
+
+ /* Set up the option buffer... */
+ client -> packet_length =
+ cons_options ((struct packet *)0, &client -> packet,
+ (struct lease *)0, client,
+ /* maximum packet size */1500,
+ (struct option_state *)0,
+ options,
+ /* scope */ &global_scope,
+ /* overload */ 0,
+ /* terminate */0,
+ /* bootpp */0,
+ (struct data_string *)0,
+ client -> config -> vendor_space_name);
+
+ if (client -> packet_length < BOOTP_MIN_LEN)
+ client -> packet_length = BOOTP_MIN_LEN;
+ option_state_dereference (&options, MDL);
+
+ client -> packet.op = BOOTREQUEST;
+ client -> packet.htype = client -> interface -> hw_address.hbuf [0];
+ client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+ client -> packet.hops = 0;
+ client -> packet.xid = random ();
+ client -> packet.secs = 0;
+ client -> packet.flags = 0;
+ memcpy (&client -> packet.ciaddr,
+ lease -> address.iabuf, lease -> address.len);
+ memset (&client -> packet.yiaddr, 0,
+ sizeof client -> packet.yiaddr);
+ memset (&client -> packet.siaddr, 0,
+ sizeof client -> packet.siaddr);
+ client -> packet.giaddr = giaddr;
+ memcpy (client -> packet.chaddr,
+ &client -> interface -> hw_address.hbuf [1],
+ client -> interface -> hw_address.hlen);
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
+#endif
+}
+
+void destroy_client_lease (lease)
+ struct client_lease *lease;
+{
+ if (lease -> server_name)
+ dfree (lease -> server_name, MDL);
+ if (lease -> filename)
+ dfree (lease -> filename, MDL);
+ option_state_dereference (&lease -> options, MDL);
+ free_client_lease (lease, MDL);
+}
+
+FILE *leaseFile = NULL;
+int leases_written = 0;
+
+void rewrite_client_leases ()
+{
+ struct interface_info *ip;
+ struct client_state *client;
+ struct client_lease *lp;
+
+ if (leaseFile != NULL)
+ fclose (leaseFile);
+ leaseFile = fopen (path_dhclient_db, "w");
+ if (leaseFile == NULL) {
+ log_error ("can't create %s: %m", path_dhclient_db);
+ return;
+ }
+
+ /* If there is a default duid, write it out. */
+ if (default_duid.len != 0)
+ write_duid(&default_duid);
+
+ /* Write out all the leases attached to configured interfaces that
+ we know about. */
+ for (ip = interfaces; ip; ip = ip -> next) {
+ for (client = ip -> client; client; client = client -> next) {
+ for (lp = client -> leases; lp; lp = lp -> next) {
+ write_client_lease (client, lp, 1, 0);
+ }
+ if (client -> active)
+ write_client_lease (client,
+ client -> active, 1, 0);
+
+ if (client->active_lease != NULL)
+ write_client6_lease(client,
+ client->active_lease,
+ 1, 0);
+
+ /* Reset last_write after rewrites. */
+ client->last_write = 0;
+ }
+ }
+
+ /* Write out any leases that are attached to interfaces that aren't
+ currently configured. */
+ for (ip = dummy_interfaces; ip; ip = ip -> next) {
+ for (client = ip -> client; client; client = client -> next) {
+ for (lp = client -> leases; lp; lp = lp -> next) {
+ write_client_lease (client, lp, 1, 0);
+ }
+ if (client -> active)
+ write_client_lease (client,
+ client -> active, 1, 0);
+
+ if (client->active_lease != NULL)
+ write_client6_lease(client,
+ client->active_lease,
+ 1, 0);
+
+ /* Reset last_write after rewrites. */
+ client->last_write = 0;
+ }
+ }
+ fflush (leaseFile);
+}
+
+void write_lease_option (struct option_cache *oc,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff)
+{
+ const char *name, *dot;
+ struct data_string ds;
+ char *preamble = stuff;
+
+ memset (&ds, 0, sizeof ds);
+
+ if (u != &dhcp_universe) {
+ name = u -> name;
+ dot = ".";
+ } else {
+ name = "";
+ dot = "";
+ }
+ if (evaluate_option_cache (&ds, packet, lease, client_state,
+ in_options, cfg_options, scope, oc, MDL)) {
+ fprintf(leaseFile, "%soption %s%s%s %s;\n", preamble,
+ name, dot, oc->option->name,
+ pretty_print_option(oc->option, ds.data, ds.len,
+ 1, 1));
+ data_string_forget (&ds, MDL);
+ }
+}
+
+/* Write an option cache to the lease store. */
+static void
+write_options(struct client_state *client, struct option_state *options,
+ const char *preamble)
+{
+ int i;
+
+ for (i = 0; i < options->universe_count; i++) {
+ option_space_foreach(NULL, NULL, client, NULL, options,
+ &global_scope, universes[i],
+ (char *)preamble, write_lease_option);
+ }
+}
+
+/* Write the default DUID to the lease store. */
+static isc_result_t
+write_duid(struct data_string *duid)
+{
+ char *str;
+ int stat;
+
+ if ((duid == NULL) || (duid->len <= 2))
+ return DHCP_R_INVALIDARG;
+
+ if (leaseFile == NULL) { /* XXX? */
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (leaseFile == NULL) {
+ log_error("can't create %s: %m", path_dhclient_db);
+ return ISC_R_IOERROR;
+ }
+ }
+
+ /* It would make more sense to write this as a hex string,
+ * but our function to do that (print_hex_n) uses a fixed
+ * length buffer...and we can't guarantee a duid would be
+ * less than the fixed length.
+ */
+ str = quotify_buf(duid->data, duid->len, MDL);
+ if (str == NULL)
+ return ISC_R_NOMEMORY;
+
+ stat = fprintf(leaseFile, "default-duid \"%s\";\n", str);
+ dfree(str, MDL);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ if (fflush(leaseFile) != 0)
+ return ISC_R_IOERROR;
+
+ return ISC_R_SUCCESS;
+}
+
+/* Write a DHCPv6 lease to the store. */
+isc_result_t
+write_client6_lease(struct client_state *client, struct dhc6_lease *lease,
+ int rewrite, int sync)
+{
+ struct dhc6_ia *ia;
+ struct dhc6_addr *addr;
+ int stat;
+ const char *ianame;
+
+ /* This should include the current lease. */
+ if (!rewrite && (leases_written++ > 20)) {
+ rewrite_client_leases();
+ leases_written = 0;
+ return ISC_R_SUCCESS;
+ }
+
+ if (client == NULL || lease == NULL)
+ return DHCP_R_INVALIDARG;
+
+ if (leaseFile == NULL) { /* XXX? */
+ leaseFile = fopen(path_dhclient_db, "w");
+ if (leaseFile == NULL) {
+ log_error("can't create %s: %m", path_dhclient_db);
+ return ISC_R_IOERROR;
+ }
+ }
+
+ stat = fprintf(leaseFile, "lease6 {\n");
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ stat = fprintf(leaseFile, " interface \"%s\";\n",
+ client->interface->name);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ for (ia = lease->bindings ; ia != NULL ; ia = ia->next) {
+ switch (ia->ia_type) {
+ case D6O_IA_NA:
+ default:
+ ianame = "ia-na";
+ break;
+ case D6O_IA_TA:
+ ianame = "ia-ta";
+ break;
+ case D6O_IA_PD:
+ ianame = "ia-pd";
+ break;
+ }
+ stat = fprintf(leaseFile, " %s %s {\n",
+ ianame, print_hex_1(4, ia->iaid, 12));
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ if (ia->ia_type != D6O_IA_TA)
+ stat = fprintf(leaseFile, " starts %d;\n"
+ " renew %u;\n"
+ " rebind %u;\n",
+ (int)ia->starts, ia->renew, ia->rebind);
+ else
+ stat = fprintf(leaseFile, " starts %d;\n",
+ (int)ia->starts);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ for (addr = ia->addrs ; addr != NULL ; addr = addr->next) {
+ if (ia->ia_type != D6O_IA_PD)
+ stat = fprintf(leaseFile,
+ " iaaddr %s {\n",
+ piaddr(addr->address));
+ else
+ stat = fprintf(leaseFile,
+ " iaprefix %s/%d {\n",
+ piaddr(addr->address),
+ (int)addr->plen);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ stat = fprintf(leaseFile, " starts %d;\n"
+ " preferred-life %u;\n"
+ " max-life %u;\n",
+ (int)addr->starts, addr->preferred_life,
+ addr->max_life);
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ if (addr->options != NULL)
+ write_options(client, addr->options, " ");
+
+ stat = fprintf(leaseFile, " }\n");
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (ia->options != NULL)
+ write_options(client, ia->options, " ");
+
+ stat = fprintf(leaseFile, " }\n");
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (lease->released) {
+ stat = fprintf(leaseFile, " released;\n");
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (lease->options != NULL)
+ write_options(client, lease->options, " ");
+
+ stat = fprintf(leaseFile, "}\n");
+ if (stat <= 0)
+ return ISC_R_IOERROR;
+
+ if (fflush(leaseFile) != 0)
+ return ISC_R_IOERROR;
+
+ if (sync) {
+ if (fsync(fileno(leaseFile)) < 0) {
+ log_error("write_client_lease: fsync(): %m");
+ return ISC_R_IOERROR;
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+int write_client_lease (client, lease, rewrite, makesure)
+ struct client_state *client;
+ struct client_lease *lease;
+ int rewrite;
+ int makesure;
+{
+ struct data_string ds;
+ int errors = 0;
+ char *s;
+ const char *tval;
+
+ 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 1;
+
+ if (leaseFile == NULL) { /* XXX */
+ leaseFile = fopen (path_dhclient_db, "w");
+ if (leaseFile == NULL) {
+ log_error ("can't create %s: %m", path_dhclient_db);
+ return 0;
+ }
+ }
+
+ errno = 0;
+ fprintf (leaseFile, "lease {\n");
+ if (lease -> is_bootp) {
+ fprintf (leaseFile, " bootp;\n");
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ }
+ fprintf (leaseFile, " interface \"%s\";\n",
+ client -> interface -> name);
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ if (client -> name) {
+ fprintf (leaseFile, " name \"%s\";\n", client -> name);
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ }
+ fprintf (leaseFile, " fixed-address %s;\n",
+ piaddr (lease -> address));
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ if (lease -> filename) {
+ s = quotify_string (lease -> filename, MDL);
+ if (s) {
+ fprintf (leaseFile, " filename \"%s\";\n", s);
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ dfree (s, MDL);
+ } else
+ errors++;
+
+ }
+ if (lease->server_name != NULL) {
+ s = quotify_string(lease->server_name, MDL);
+ if (s != NULL) {
+ fprintf(leaseFile, " server-name \"%s\";\n", s);
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ dfree(s, MDL);
+ } else
+ ++errors;
+ }
+ if (lease -> medium) {
+ s = quotify_string (lease -> medium -> string, MDL);
+ if (s) {
+ fprintf (leaseFile, " medium \"%s\";\n", s);
+ if (errno) {
+ ++errors;
+ errno = 0;
+ }
+ dfree (s, MDL);
+ } else
+ errors++;
+ }
+ if (errno != 0) {
+ errors++;
+ errno = 0;
+ }
+
+ memset (&ds, 0, sizeof ds);
+
+ write_options(client, lease->options, " ");
+
+ tval = print_time(lease->renewal);
+ if (tval == NULL ||
+ fprintf(leaseFile, " renew %s\n", tval) < 0)
+ errors++;
+
+ tval = print_time(lease->rebind);
+ if (tval == NULL ||
+ fprintf(leaseFile, " rebind %s\n", tval) < 0)
+ errors++;
+
+ tval = print_time(lease->expiry);
+ if (tval == NULL ||
+ fprintf(leaseFile, " expire %s\n", tval) < 0)
+ errors++;
+
+ if (fprintf(leaseFile, "}\n") < 0)
+ errors++;
+
+ if (fflush(leaseFile) != 0)
+ errors++;
+
+ client->last_write = cur_time;
+
+ if (!errors && makesure) {
+ if (fsync (fileno (leaseFile)) < 0) {
+ log_info ("write_client_lease: %m");
+ return 0;
+ }
+ }
+
+ return errors ? 0 : 1;
+}
+
+/* Variables holding name of script and file pointer for writing to
+ script. Needless to say, this is not reentrant - only one script
+ can be invoked at a time. */
+char scriptName [256];
+FILE *scriptFile;
+
+void script_init (client, reason, medium)
+ struct client_state *client;
+ const char *reason;
+ struct string_list *medium;
+{
+ struct string_list *sl, *next;
+
+ if (client) {
+ for (sl = client -> env; sl; sl = next) {
+ next = sl -> next;
+ dfree (sl, MDL);
+ }
+ client -> env = (struct string_list *)0;
+ client -> envc = 0;
+
+ if (client -> interface) {
+ client_envadd (client, "", "interface", "%s",
+ client -> interface -> name);
+ }
+ if (client -> name)
+ client_envadd (client,
+ "", "client", "%s", client -> name);
+ if (medium)
+ client_envadd (client,
+ "", "medium", "%s", medium -> string);
+
+ client_envadd (client, "", "reason", "%s", reason);
+ client_envadd (client, "", "pid", "%ld", (long int)getpid ());
+ }
+}
+
+void client_option_envadd (struct option_cache *oc,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff)
+{
+ struct envadd_state *es = stuff;
+ struct data_string data;
+ memset (&data, 0, sizeof data);
+
+ if (evaluate_option_cache (&data, packet, lease, client_state,
+ in_options, cfg_options, scope, oc, MDL)) {
+ if (data.len) {
+ char name [256];
+ if (dhcp_option_ev_name (name, sizeof name,
+ oc->option)) {
+ const char *value;
+ size_t length;
+ value = pretty_print_option(oc->option,
+ data.data,
+ data.len, 0, 0);
+ length = strlen(value);
+
+ if (check_option_values(oc->option->universe,
+ oc->option->code,
+ value, length) == 0) {
+ client_envadd(es->client, es->prefix,
+ name, "%s", value);
+ } else {
+ log_error("suspect value in %s "
+ "option - discarded",
+ name);
+ }
+ data_string_forget (&data, MDL);
+ }
+ }
+ }
+}
+
+void script_write_params (client, prefix, lease)
+ struct client_state *client;
+ const char *prefix;
+ struct client_lease *lease;
+{
+ int i;
+ struct data_string data;
+ struct option_cache *oc;
+ struct envadd_state es;
+
+ es.client = client;
+ es.prefix = prefix;
+
+ client_envadd (client,
+ prefix, "ip_address", "%s", piaddr (lease -> address));
+
+ /* For the benefit of Linux (and operating systems which may
+ have similar needs), compute the network address based on
+ the supplied ip address and netmask, if provided. Also
+ compute the broadcast address (the host address all ones
+ broadcast address, not the host address all zeroes
+ broadcast address). */
+
+ memset (&data, 0, sizeof data);
+ oc = lookup_option (&dhcp_universe, lease -> options, DHO_SUBNET_MASK);
+ if (oc && evaluate_option_cache (&data, (struct packet *)0,
+ (struct lease *)0, client,
+ (struct option_state *)0,
+ lease -> options,
+ &global_scope, oc, MDL)) {
+ if (data.len > 3) {
+ struct iaddr netmask, subnet, broadcast;
+
+ /*
+ * No matter the length of the subnet-mask option,
+ * use only the first four octets. Note that
+ * subnet-mask options longer than 4 octets are not
+ * in conformance with RFC 2132, but servers with this
+ * flaw do exist.
+ */
+ memcpy(netmask.iabuf, data.data, 4);
+ netmask.len = 4;
+ data_string_forget (&data, MDL);
+
+ subnet = subnet_number (lease -> address, netmask);
+ if (subnet.len) {
+ client_envadd (client, prefix, "network_number",
+ "%s", piaddr (subnet));
+
+ oc = lookup_option (&dhcp_universe,
+ lease -> options,
+ DHO_BROADCAST_ADDRESS);
+ if (!oc ||
+ !(evaluate_option_cache
+ (&data, (struct packet *)0,
+ (struct lease *)0, client,
+ (struct option_state *)0,
+ lease -> options,
+ &global_scope, oc, MDL))) {
+ broadcast = broadcast_addr (subnet, netmask);
+ if (broadcast.len) {
+ client_envadd (client,
+ prefix, "broadcast_address",
+ "%s", piaddr (broadcast));
+ }
+ }
+ }
+ }
+ data_string_forget (&data, MDL);
+ }
+
+ if (lease->filename) {
+ if (check_option_values(NULL, DHO_ROOT_PATH,
+ lease->filename,
+ strlen(lease->filename)) == 0) {
+ client_envadd(client, prefix, "filename",
+ "%s", lease->filename);
+ } else {
+ log_error("suspect value in %s "
+ "option - discarded",
+ lease->filename);
+ }
+ }
+
+ if (lease->server_name) {
+ if (check_option_values(NULL, DHO_HOST_NAME,
+ lease->server_name,
+ strlen(lease->server_name)) == 0 ) {
+ client_envadd (client, prefix, "server_name",
+ "%s", lease->server_name);
+ } else {
+ log_error("suspect value in %s "
+ "option - discarded",
+ lease->server_name);
+ }
+ }
+
+
+ for (i = 0; i < lease -> options -> universe_count; i++) {
+ option_space_foreach ((struct packet *)0, (struct lease *)0,
+ client, (struct option_state *)0,
+ lease -> options, &global_scope,
+ universes [i],
+ &es, client_option_envadd);
+ }
+ client_envadd (client, prefix, "expiry", "%d", (int)(lease -> expiry));
+}
+
+int script_go (client)
+ struct client_state *client;
+{
+ char *scriptName;
+ char *argv [2];
+ char **envp;
+ char reason [] = "REASON=NBI";
+ static char client_path [] = CLIENT_PATH;
+ int i;
+ struct string_list *sp, *next;
+ int pid, wpid, wstatus;
+
+ if (client)
+ scriptName = client -> config -> script_name;
+ else
+ scriptName = top_level_config.script_name;
+
+ envp = dmalloc (((client ? client -> envc : 2) +
+ client_env_count + 2) * sizeof (char *), MDL);
+ if (!envp) {
+ log_error ("No memory for client script environment.");
+ return 0;
+ }
+ i = 0;
+ /* Copy out the environment specified on the command line,
+ if any. */
+ for (sp = client_env; sp; sp = sp -> next) {
+ envp [i++] = sp -> string;
+ }
+ /* Copy out the environment specified by dhclient. */
+ if (client) {
+ for (sp = client -> env; sp; sp = sp -> next) {
+ envp [i++] = sp -> string;
+ }
+ } else {
+ envp [i++] = reason;
+ }
+ /* Set $PATH. */
+ envp [i++] = client_path;
+ envp [i] = (char *)0;
+
+ argv [0] = scriptName;
+ argv [1] = (char *)0;
+
+ pid = fork ();
+ if (pid < 0) {
+ log_error ("fork: %m");
+ wstatus = 0;
+ } else if (pid) {
+ do {
+ wpid = wait (&wstatus);
+ } while (wpid != pid && wpid > 0);
+ if (wpid < 0) {
+ log_error ("wait: %m");
+ wstatus = 0;
+ }
+ } else {
+ /* We don't want to pass an open file descriptor for
+ * dhclient.leases when executing dhclient-script.
+ */
+ if (leaseFile != NULL)
+ fclose(leaseFile);
+ execve (scriptName, argv, envp);
+ log_error ("execve (%s, ...): %m", scriptName);
+ exit (0);
+ }
+
+ if (client) {
+ for (sp = client -> env; sp; sp = next) {
+ next = sp -> next;
+ dfree (sp, MDL);
+ }
+ client -> env = (struct string_list *)0;
+ client -> envc = 0;
+ }
+ dfree (envp, MDL);
+ gettimeofday(&cur_tv, NULL);
+ return (WIFEXITED (wstatus) ?
+ WEXITSTATUS (wstatus) : -WTERMSIG (wstatus));
+}
+
+void client_envadd (struct client_state *client,
+ const char *prefix, const char *name, const char *fmt, ...)
+{
+ char spbuf [1024];
+ char *s;
+ unsigned len;
+ struct string_list *val;
+ va_list list;
+
+ va_start (list, fmt);
+ len = vsnprintf (spbuf, sizeof spbuf, fmt, list);
+ va_end (list);
+
+ val = dmalloc (strlen (prefix) + strlen (name) + 1 /* = */ +
+ len + sizeof *val, MDL);
+ if (!val)
+ return;
+ s = val -> string;
+ strcpy (s, prefix);
+ strcat (s, name);
+ s += strlen (s);
+ *s++ = '=';
+ if (len >= sizeof spbuf) {
+ va_start (list, fmt);
+ vsnprintf (s, len + 1, fmt, list);
+ va_end (list);
+ } else
+ strcpy (s, spbuf);
+ val -> next = client -> env;
+ client -> env = val;
+ client -> envc++;
+}
+
+int dhcp_option_ev_name (buf, buflen, option)
+ char *buf;
+ size_t buflen;
+ struct option *option;
+{
+ int i, j;
+ const char *s;
+
+ j = 0;
+ if (option -> universe != &dhcp_universe) {
+ s = option -> universe -> name;
+ i = 0;
+ } else {
+ s = option -> name;
+ i = 1;
+ }
+
+ do {
+ while (*s) {
+ if (j + 1 == buflen)
+ return 0;
+ if (*s == '-')
+ buf [j++] = '_';
+ else
+ buf [j++] = *s;
+ ++s;
+ }
+ if (!i) {
+ s = option -> name;
+ if (j + 1 == buflen)
+ return 0;
+ buf [j++] = '_';
+ }
+ ++i;
+ } while (i != 2);
+
+ buf [j] = 0;
+ return 1;
+}
+
+void go_daemon ()
+{
+ static int state = 0;
+ int pid;
+
+ /* Don't become a daemon if the user requested otherwise. */
+ if (no_daemon) {
+ write_client_pid_file ();
+ return;
+ }
+
+ /* Only do it once. */
+ if (state)
+ return;
+ state = 1;
+
+ /* Stop logging to stderr... */
+ log_perror = 0;
+
+ /* Become a daemon... */
+ if ((pid = fork ()) < 0)
+ log_fatal ("Can't fork daemon: %m");
+ else if (pid)
+ exit (0);
+ /* Become session leader and get pid... */
+ pid = setsid ();
+
+ /* Close standard I/O descriptors. */
+ close(0);
+ close(1);
+ close(2);
+
+ /* Reopen them on /dev/null. */
+ open("/dev/null", O_RDWR);
+ open("/dev/null", O_RDWR);
+ open("/dev/null", O_RDWR);
+
+ write_client_pid_file ();
+
+ IGNORE_RET (chdir("/"));
+}
+
+void write_client_pid_file ()
+{
+ FILE *pf;
+ int pfdesc;
+
+ /* nothing to do if the user doesn't want a pid file */
+ if (no_pid_file == ISC_TRUE) {
+ return;
+ }
+
+ pfdesc = open (path_dhclient_pid, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+
+ if (pfdesc < 0) {
+ log_error ("Can't create %s: %m", path_dhclient_pid);
+ return;
+ }
+
+ pf = fdopen (pfdesc, "w");
+ if (!pf) {
+ close(pfdesc);
+ log_error ("Can't fdopen %s: %m", path_dhclient_pid);
+ } else {
+ fprintf (pf, "%ld\n", (long)getpid ());
+ fclose (pf);
+ }
+}
+
+void client_location_changed ()
+{
+ struct interface_info *ip;
+ struct client_state *client;
+
+ for (ip = interfaces; ip; ip = ip -> next) {
+ for (client = ip -> client; client; client = client -> next) {
+ switch (client -> state) {
+ case S_SELECTING:
+ cancel_timeout (send_discover, client);
+ break;
+
+ case S_BOUND:
+ cancel_timeout (state_bound, client);
+ break;
+
+ case S_REBOOTING:
+ case S_REQUESTING:
+ case S_RENEWING:
+ cancel_timeout (send_request, client);
+ break;
+
+ case S_INIT:
+ case S_REBINDING:
+ case S_STOPPED:
+ break;
+ }
+ client -> state = S_INIT;
+ state_reboot (client);
+ }
+ }
+}
+
+void do_release(client)
+ struct client_state *client;
+{
+ struct data_string ds;
+ struct option_cache *oc;
+
+ /* Pick a random xid. */
+ client -> xid = random ();
+
+ /* is there even a lease to release? */
+ if (client -> active) {
+ /* Make a DHCPRELEASE packet, and set appropriate per-interface
+ flags. */
+ make_release (client, client -> active);
+
+ memset (&ds, 0, sizeof ds);
+ oc = lookup_option (&dhcp_universe,
+ client -> active -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ if (oc &&
+ evaluate_option_cache (&ds, (struct packet *)0,
+ (struct lease *)0, client,
+ (struct option_state *)0,
+ client -> active -> options,
+ &global_scope, oc, MDL)) {
+ if (ds.len > 3) {
+ memcpy (client -> destination.iabuf,
+ ds.data, 4);
+ client -> destination.len = 4;
+ } else
+ client -> destination = iaddr_broadcast;
+
+ data_string_forget (&ds, MDL);
+ } else
+ client -> destination = iaddr_broadcast;
+ client -> first_sending = cur_time;
+ client -> interval = client -> config -> initial_interval;
+
+ /* Zap the medium list... */
+ client -> medium = (struct string_list *)0;
+
+ /* Send out the first and only DHCPRELEASE packet. */
+ send_release (client);
+
+ /* Do the client script RELEASE operation. */
+ script_init (client,
+ "RELEASE", (struct string_list *)0);
+ if (client -> alias)
+ script_write_params (client, "alias_",
+ client -> alias);
+ script_write_params (client, "old_", client -> active);
+ script_go (client);
+ }
+
+ /* Cancel any timeouts. */
+ cancel_timeout (state_bound, client);
+ cancel_timeout (send_discover, client);
+ cancel_timeout (state_init, client);
+ cancel_timeout (send_request, client);
+ cancel_timeout (state_reboot, client);
+ client -> state = S_STOPPED;
+}
+
+int dhclient_interface_shutdown_hook (struct interface_info *interface)
+{
+ do_release (interface -> client);
+
+ return 1;
+}
+
+int dhclient_interface_discovery_hook (struct interface_info *tmp)
+{
+ struct interface_info *last, *ip;
+ /* See if we can find the client from dummy_interfaces */
+ last = 0;
+ for (ip = dummy_interfaces; ip; ip = ip -> next) {
+ if (!strcmp (ip -> name, tmp -> name)) {
+ /* Remove from dummy_interfaces */
+ if (last) {
+ ip = (struct interface_info *)0;
+ interface_reference (&ip, last -> next, MDL);
+ interface_dereference (&last -> next, MDL);
+ if (ip -> next) {
+ interface_reference (&last -> next,
+ ip -> next, MDL);
+ interface_dereference (&ip -> next,
+ MDL);
+ }
+ } else {
+ ip = (struct interface_info *)0;
+ interface_reference (&ip,
+ dummy_interfaces, MDL);
+ interface_dereference (&dummy_interfaces, MDL);
+ if (ip -> next) {
+ interface_reference (&dummy_interfaces,
+ ip -> next, MDL);
+ interface_dereference (&ip -> next,
+ MDL);
+ }
+ }
+ /* Copy "client" to tmp */
+ if (ip -> client) {
+ tmp -> client = ip -> client;
+ tmp -> client -> interface = tmp;
+ }
+ interface_dereference (&ip, MDL);
+ break;
+ }
+ last = ip;
+ }
+ return 1;
+}
+
+isc_result_t dhclient_interface_startup_hook (struct interface_info *interface)
+{
+ struct interface_info *ip;
+ struct client_state *client;
+
+ /* This code needs some rethinking. It doesn't test against
+ a signal name, and it just kind of bulls into doing something
+ that may or may not be appropriate. */
+
+ if (interfaces) {
+ interface_reference (&interface -> next, interfaces, MDL);
+ interface_dereference (&interfaces, MDL);
+ }
+ interface_reference (&interfaces, interface, MDL);
+
+ discover_interfaces (DISCOVER_UNCONFIGURED);
+
+ for (ip = interfaces; ip; ip = ip -> next) {
+ /* If interfaces were specified, don't configure
+ interfaces that weren't specified! */
+ if (ip -> flags & INTERFACE_RUNNING ||
+ (ip -> flags & (INTERFACE_REQUESTED |
+ INTERFACE_AUTOMATIC)) !=
+ INTERFACE_REQUESTED)
+ continue;
+ script_init (ip -> client,
+ "PREINIT", (struct string_list *)0);
+ if (ip -> client -> alias)
+ script_write_params (ip -> client, "alias_",
+ ip -> client -> alias);
+ script_go (ip -> client);
+ }
+
+ discover_interfaces (interfaces_requested != 0
+ ? DISCOVER_REQUESTED
+ : DISCOVER_RUNNING);
+
+ for (ip = interfaces; ip; ip = ip -> next) {
+ if (ip -> flags & INTERFACE_RUNNING)
+ continue;
+ ip -> flags |= INTERFACE_RUNNING;
+ for (client = ip->client ; client ; client = client->next) {
+ client->state = S_INIT;
+ state_reboot(client);
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* The client should never receive a relay agent information option,
+ so if it does, log it and discard it. */
+
+int parse_agent_information_option (packet, len, data)
+ struct packet *packet;
+ int len;
+ u_int8_t *data;
+{
+ return 1;
+}
+
+/* The client never sends relay agent information options. */
+
+unsigned cons_agent_information_options (cfg_options, outpacket,
+ agentix, length)
+ struct option_state *cfg_options;
+ struct dhcp_packet *outpacket;
+ unsigned agentix;
+ unsigned length;
+{
+ return length;
+}
+
+static void shutdown_exit (void *foo)
+{
+ exit (0);
+}
+
+#if defined (NSUPDATE)
+/*
+ * If the first query fails, the updater MUST NOT delete the DNS name. It
+ * may be that the host whose lease on the server has expired has moved
+ * to another network and obtained a lease from a different server,
+ * which has caused the client's A RR to be replaced. It may also be
+ * that some other client has been configured with a name that matches
+ * the name of the DHCP client, and the policy was that the last client
+ * to specify the name would get the name. In this case, the DHCID RR
+ * will no longer match the updater's notion of the client-identity of
+ * the host pointed to by the DNS name.
+ * -- "Interaction between DHCP and DNS"
+ */
+
+/* The first and second stages are pretty similar so we combine them */
+void
+client_dns_remove_action(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+
+ isc_result_t result;
+
+ if ((eresult == ISC_R_SUCCESS) &&
+ (ddns_cb->state == DDNS_STATE_REM_FW_YXDHCID)) {
+ /* Do the second stage of the FWD removal */
+ ddns_cb->state = DDNS_STATE_REM_FW_NXRR;
+
+ result = ddns_modify_fwd(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+
+ /* If we are done or have an error clean up */
+ ddns_cb_free(ddns_cb, MDL);
+ return;
+}
+
+void
+client_dns_remove(struct client_state *client,
+ struct iaddr *addr)
+{
+ dhcp_ddns_cb_t *ddns_cb;
+ isc_result_t result;
+
+ /* if we have an old ddns request for this client, cancel it */
+ if (client->ddns_cb != NULL) {
+ ddns_cancel(client->ddns_cb);
+ client->ddns_cb = NULL;
+ }
+
+ ddns_cb = ddns_cb_alloc(MDL);
+ if (ddns_cb != NULL) {
+ ddns_cb->address = *addr;
+ ddns_cb->timeout = 0;
+
+ ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID;
+ ddns_cb->flags = DDNS_UPDATE_ADDR;
+ ddns_cb->cur_func = client_dns_remove_action;
+
+ result = client_dns_update(client, ddns_cb);
+
+ if (result != ISC_R_TIMEDOUT) {
+ ddns_cb_free(ddns_cb, MDL);
+ }
+ }
+}
+#endif
+
+isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
+ control_object_state_t newstate)
+{
+ struct interface_info *ip;
+ struct client_state *client;
+ struct timeval tv;
+
+ /* Do the right thing for each interface. */
+ for (ip = interfaces; ip; ip = ip -> next) {
+ for (client = ip -> client; client; client = client -> next) {
+ switch (newstate) {
+ case server_startup:
+ return ISC_R_SUCCESS;
+
+ case server_running:
+ return ISC_R_SUCCESS;
+
+ case server_shutdown:
+ if (client -> active &&
+ client -> active -> expiry > cur_time) {
+#if defined (NSUPDATE)
+ if (client->config->do_forward_update) {
+ client_dns_remove(client,
+ &client->active->address);
+ }
+#endif
+ do_release (client);
+ }
+ break;
+
+ case server_hibernate:
+ state_stop (client);
+ break;
+
+ case server_awaken:
+ state_reboot (client);
+ break;
+ }
+ }
+ }
+
+ if (newstate == server_shutdown) {
+ tv.tv_sec = cur_tv.tv_sec;
+ tv.tv_usec = cur_tv.tv_usec + 1;
+ add_timeout(&tv, shutdown_exit, 0, 0, 0);
+ }
+ return ISC_R_SUCCESS;
+}
+
+#if defined (NSUPDATE)
+/*
+ * Called after a timeout if the DNS update failed on the previous try.
+ * Starts the retry process. If the retry times out it will schedule
+ * this routine to run again after a 10x wait.
+ */
+void
+client_dns_update_timeout (void *cp)
+{
+ dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)cp;
+ struct client_state *client = (struct client_state *)ddns_cb->lease;
+ isc_result_t status = ISC_R_FAILURE;
+
+ if ((client != NULL) &&
+ ((client->active != NULL) ||
+ (client->active_lease != NULL)))
+ status = client_dns_update(client, ddns_cb);
+
+ /*
+ * A status of timedout indicates that we started the update and
+ * have released control of the control block. Any other status
+ * indicates that we should clean up the control block. We either
+ * got a success which indicates that we didn't really need to
+ * send an update or some other error in which case we weren't able
+ * to start the update process. In both cases we still own
+ * the control block and should free it.
+ */
+ if (status != ISC_R_TIMEDOUT) {
+ if (client != NULL) {
+ client->ddns_cb = NULL;
+ }
+ ddns_cb_free(ddns_cb, MDL);
+ }
+}
+
+/*
+ * If the first query succeeds, the updater can conclude that it
+ * has added a new name whose only RRs are the A and DHCID RR records.
+ * The A RR update is now complete (and a client updater is finished,
+ * while a server might proceed to perform a PTR RR update).
+ * -- "Interaction between DHCP and DNS"
+ *
+ * If the second query succeeds, the updater can conclude that the current
+ * client was the last client associated with the domain name, and that
+ * the name now contains the updated A RR. The A RR update is now
+ * complete (and a client updater is finished, while a server would
+ * then proceed to perform a PTR RR update).
+ * -- "Interaction between DHCP and DNS"
+ *
+ * If the second query fails with NXRRSET, the updater must conclude
+ * that the client's desired name is in use by another host. At this
+ * juncture, the updater can decide (based on some administrative
+ * configuration outside of the scope of this document) whether to let
+ * the existing owner of the name keep that name, and to (possibly)
+ * perform some name disambiguation operation on behalf of the current
+ * client, or to replace the RRs on the name with RRs that represent
+ * the current client. If the configured policy allows replacement of
+ * existing records, the updater submits a query that deletes the
+ * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
+ * represent the IP address and client-identity of the new client.
+ * -- "Interaction between DHCP and DNS"
+ */
+
+/* The first and second stages are pretty similar so we combine them */
+void
+client_dns_update_action(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result;
+ struct timeval tv;
+
+ switch(eresult) {
+ case ISC_R_SUCCESS:
+ default:
+ /* Either we succeeded or broke in a bad way, clean up */
+ break;
+
+ case DNS_R_YXRRSET:
+ /*
+ * This is the only difference between the two stages,
+ * check to see if it is the first stage, in which case
+ * start the second stage
+ */
+ if (ddns_cb->state == DDNS_STATE_ADD_FW_NXDOMAIN) {
+ ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
+ ddns_cb->cur_func = client_dns_update_action;
+
+ result = ddns_modify_fwd(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+ break;
+
+ case ISC_R_TIMEDOUT:
+ /*
+ * We got a timeout response from the DNS module. Schedule
+ * another attempt for later. We forget the name, dhcid and
+ * zone so if it gets changed we will get the new information.
+ */
+ data_string_forget(&ddns_cb->fwd_name, MDL);
+ data_string_forget(&ddns_cb->dhcid, MDL);
+ if (ddns_cb->zone != NULL) {
+ forget_zone((struct dns_zone **)&ddns_cb->zone);
+ }
+
+ /* Reset to doing the first stage */
+ ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
+ ddns_cb->cur_func = client_dns_update_action;
+
+ /* and update our timer */
+ if (ddns_cb->timeout < 3600)
+ ddns_cb->timeout *= 10;
+ tv.tv_sec = cur_tv.tv_sec + ddns_cb->timeout;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout(&tv, client_dns_update_timeout,
+ ddns_cb, NULL, NULL);
+ return;
+ }
+
+ ddns_cb_free(ddns_cb, MDL);
+ return;
+}
+
+/* See if we should do a DNS update, and if so, do it. */
+
+isc_result_t
+client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
+{
+ struct data_string client_identifier;
+ struct option_cache *oc;
+ int ignorep;
+ int result;
+ isc_result_t rcode;
+
+ /* If we didn't send an FQDN option, we certainly aren't going to
+ be doing an update. */
+ if (!client -> sent_options)
+ return ISC_R_SUCCESS;
+
+ /* If we don't have a lease, we can't do an update. */
+ if ((client->active == NULL) && (client->active_lease == NULL))
+ return ISC_R_SUCCESS;
+
+ /* If we set the no client update flag, don't do the update. */
+ if ((oc = lookup_option (&fqdn_universe, client -> sent_options,
+ FQDN_NO_CLIENT_UPDATE)) &&
+ evaluate_boolean_option_cache (&ignorep, (struct packet *)0,
+ (struct lease *)0, client,
+ client -> sent_options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL))
+ return ISC_R_SUCCESS;
+
+ /* If we set the "server, please update" flag, or didn't set it
+ to false, don't do the update. */
+ if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
+ FQDN_SERVER_UPDATE)) ||
+ evaluate_boolean_option_cache (&ignorep, (struct packet *)0,
+ (struct lease *)0, client,
+ client -> sent_options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL))
+ return ISC_R_SUCCESS;
+
+ /* If no FQDN option was supplied, don't do the update. */
+ if (!(oc = lookup_option (&fqdn_universe, client -> sent_options,
+ FQDN_FQDN)) ||
+ !evaluate_option_cache (&ddns_cb->fwd_name, (struct packet *)0,
+ (struct lease *)0, client,
+ client -> sent_options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL))
+ return ISC_R_SUCCESS;
+
+ /* If this is a DHCPv6 client update, make a dhcid string out of
+ * the DUID. If this is a DHCPv4 client update, choose either
+ * the client identifier, if there is one, or the interface's
+ * MAC address.
+ */
+ result = 0;
+ memset(&client_identifier, 0, sizeof(client_identifier));
+ if (client->active_lease != NULL) {
+ if (((oc =
+ lookup_option(&dhcpv6_universe, client->sent_options,
+ D6O_CLIENTID)) != NULL) &&
+ evaluate_option_cache(&client_identifier, NULL, NULL,
+ client, client->sent_options, NULL,
+ &global_scope, oc, MDL)) {
+ /* RFC4701 defines type '2' as being for the DUID
+ * field. We aren't using RFC4701 DHCID RR's yet,
+ * but this is as good a value as any.
+ */
+ result = get_dhcid(&ddns_cb->dhcid, 2,
+ client_identifier.data,
+ client_identifier.len);
+ data_string_forget(&client_identifier, MDL);
+ } else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ } else {
+ if (((oc =
+ lookup_option(&dhcp_universe, client->sent_options,
+ DHO_DHCP_CLIENT_IDENTIFIER)) != NULL) &&
+ evaluate_option_cache(&client_identifier, NULL, NULL,
+ client, client->sent_options, NULL,
+ &global_scope, oc, MDL)) {
+ result = get_dhcid(&ddns_cb->dhcid,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ client_identifier.data,
+ client_identifier.len);
+ data_string_forget(&client_identifier, MDL);
+ } else
+ result = get_dhcid(&ddns_cb->dhcid, 0,
+ client->interface->hw_address.hbuf,
+ client->interface->hw_address.hlen);
+ }
+ if (!result) {
+ return ISC_R_SUCCESS;
+ }
+
+ /*
+ * Perform updates.
+ */
+ if (ddns_cb->fwd_name.len && ddns_cb->dhcid.len) {
+ rcode = ddns_modify_fwd(ddns_cb);
+ } else
+ rcode = ISC_R_FAILURE;
+
+ /*
+ * A success from the modify routine means we are performing
+ * async processing, for which we use the timedout error message.
+ */
+ if (rcode == ISC_R_SUCCESS) {
+ rcode = ISC_R_TIMEDOUT;
+ }
+
+ return rcode;
+}
+
+
+/*
+ * Schedule the first update. They will continue to retry occasionally
+ * until they no longer time out (or fail).
+ */
+void
+dhclient_schedule_updates(struct client_state *client,
+ struct iaddr *addr,
+ int offset)
+{
+ dhcp_ddns_cb_t *ddns_cb;
+ struct timeval tv;
+
+ if (!client->config->do_forward_update)
+ return;
+
+ /* cancel any outstanding ddns requests */
+ if (client->ddns_cb != NULL) {
+ ddns_cancel(client->ddns_cb);
+ client->ddns_cb = NULL;
+ }
+
+ ddns_cb = ddns_cb_alloc(MDL);
+
+ if (ddns_cb != NULL) {
+ ddns_cb->lease = (void *)client;
+ ddns_cb->address = *addr;
+ ddns_cb->timeout = 1;
+
+ /*
+ * XXX: DNS TTL is a problem we need to solve properly.
+ * Until that time, 300 is a placeholder default for
+ * something that is less insane than a value scaled
+ * by lease timeout.
+ */
+ ddns_cb->ttl = 300;
+
+ ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
+ ddns_cb->cur_func = client_dns_update_action;
+ ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_INCLUDE_RRSET;
+
+ client->ddns_cb = ddns_cb;
+
+ tv.tv_sec = cur_tv.tv_sec + offset;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout(&tv, client_dns_update_timeout,
+ ddns_cb, NULL, NULL);
+ } else {
+ log_error("Unable to allocate dns update state for %s",
+ piaddr(*addr));
+ }
+}
+#endif
+
+void
+dhcpv4_client_assignments(void)
+{
+ struct servent *ent;
+
+ if (path_dhclient_pid == NULL)
+ path_dhclient_pid = _PATH_DHCLIENT_PID;
+ if (path_dhclient_db == NULL)
+ path_dhclient_db = _PATH_DHCLIENT_DB;
+
+ /* Default to the DHCP/BOOTP port. */
+ if (!local_port) {
+ /* If we're faking a relay agent, and we're not using loopback,
+ use the server port, not the client port. */
+ if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
+ local_port = htons(67);
+ } else {
+ ent = getservbyname ("dhcpc", "udp");
+ if (!ent)
+ local_port = htons (68);
+ else
+ local_port = ent -> s_port;
+#ifndef __CYGWIN32__
+ endservent ();
+#endif
+ }
+ }
+
+ /* If we're faking a relay agent, and we're not using loopback,
+ we're using the server port, not the client port. */
+ if (mockup_relay && giaddr.s_addr != htonl (INADDR_LOOPBACK)) {
+ remote_port = local_port;
+ } else
+ remote_port = htons (ntohs (local_port) - 1); /* XXX */
+}
+
+/*
+ * The following routines are used to check that certain
+ * strings are reasonable before we pass them to the scripts.
+ * This avoids some problems with scripts treating the strings
+ * as commands - see ticket 23722
+ * The domain checking code should be done as part of assembling
+ * the string but we are doing it here for now due to time
+ * constraints.
+ */
+
+static int check_domain_name(const char *ptr, size_t len, int dots)
+{
+ const char *p;
+
+ /* not empty or complete length not over 255 characters */
+ if ((len == 0) || (len > 256))
+ return(-1);
+
+ /* consists of [[:alnum:]-]+ labels separated by [.] */
+ /* a [_] is against RFC but seems to be "widely used"... */
+ for (p=ptr; (*p != 0) && (len-- > 0); p++) {
+ if ((*p == '-') || (*p == '_')) {
+ /* not allowed at begin or end of a label */
+ if (((p - ptr) == 0) || (len == 0) || (p[1] == '.'))
+ return(-1);
+ } else if (*p == '.') {
+ /* each label has to be 1-63 characters;
+ we allow [.] at the end ('foo.bar.') */
+ size_t d = p - ptr;
+ if ((d <= 0) || (d >= 64))
+ return(-1);
+ ptr = p + 1; /* jump to the next label */
+ if ((dots > 0) && (len > 0))
+ dots--;
+ } else if (isalnum((unsigned char)*p) == 0) {
+ /* also numbers at the begin are fine */
+ return(-1);
+ }
+ }
+ return(dots ? -1 : 0);
+}
+
+static int check_domain_name_list(const char *ptr, size_t len, int dots)
+{
+ const char *p;
+ int ret = -1; /* at least one needed */
+
+ if ((ptr == NULL) || (len == 0))
+ return(-1);
+
+ for (p=ptr; (*p != 0) && (len > 0); p++, len--) {
+ if (*p != ' ')
+ continue;
+ if (p > ptr) {
+ if (check_domain_name(ptr, p - ptr, dots) != 0)
+ return(-1);
+ ret = 0;
+ }
+ ptr = p + 1;
+ }
+ if (p > ptr)
+ return(check_domain_name(ptr, p - ptr, dots));
+ else
+ return(ret);
+}
+
+static int check_option_values(struct universe *universe,
+ unsigned int opt,
+ const char *ptr,
+ size_t len)
+{
+ if (ptr == NULL)
+ return(-1);
+
+ /* just reject options we want to protect, will be escaped anyway */
+ if ((universe == NULL) || (universe == &dhcp_universe)) {
+ switch(opt) {
+ case DHO_DOMAIN_NAME:
+#ifdef ACCEPT_LIST_IN_DOMAIN_NAME
+ return check_domain_name_list(ptr, len, 0);
+#else
+ return check_domain_name(ptr, len, 0);
+#endif
+ case DHO_HOST_NAME:
+ case DHO_NIS_DOMAIN:
+ case DHO_NETBIOS_SCOPE:
+ return check_domain_name(ptr, len, 0);
+ break;
+ case DHO_DOMAIN_SEARCH:
+ return check_domain_name_list(ptr, len, 0);
+ break;
+ case DHO_ROOT_PATH:
+ if (len == 0)
+ return(-1);
+ for (; (*ptr != 0) && (len-- > 0); ptr++) {
+ if(!(isalnum((unsigned char)*ptr) ||
+ *ptr == '#' || *ptr == '%' ||
+ *ptr == '+' || *ptr == '-' ||
+ *ptr == '_' || *ptr == ':' ||
+ *ptr == '.' || *ptr == ',' ||
+ *ptr == '@' || *ptr == '~' ||
+ *ptr == '\\' || *ptr == '/' ||
+ *ptr == '[' || *ptr == ']' ||
+ *ptr == '=' || *ptr == ' '))
+ return(-1);
+ }
+ return(0);
+ break;
+ }
+ }
+
+#ifdef DHCPv6
+ if (universe == &dhcpv6_universe) {
+ switch(opt) {
+ case D6O_SIP_SERVERS_DNS:
+ case D6O_DOMAIN_SEARCH:
+ case D6O_NIS_DOMAIN_NAME:
+ case D6O_NISP_DOMAIN_NAME:
+ return check_domain_name_list(ptr, len, 0);
+ break;
+ }
+ }
+#endif
+
+ return(0);
+}
+
+static void
+add_reject(struct packet *packet) {
+ struct iaddrmatchlist *list;
+
+ list = dmalloc(sizeof(struct iaddrmatchlist), MDL);
+ if (!list)
+ log_fatal ("no memory for reject list!");
+
+ /*
+ * client_addr is misleading - it is set to source address in common
+ * code.
+ */
+ list->match.addr = packet->client_addr;
+ /* Set mask to indicate host address. */
+ list->match.mask.len = list->match.addr.len;
+ memset(list->match.mask.iabuf, 0xff, sizeof(list->match.mask.iabuf));
+
+ /* Append to reject list for the source interface. */
+ list->next = packet->interface->client->config->reject_list;
+ packet->interface->client->config->reject_list = list;
+
+ /*
+ * We should inform user that we won't be accepting this server
+ * anymore.
+ */
+ log_info("Server added to list of rejected servers.");
+}
diff --git a/client/dhclient.conf b/client/dhclient.conf
new file mode 100644
index 0000000..b8354de
--- /dev/null
+++ b/client/dhclient.conf
@@ -0,0 +1,36 @@
+send host-name = pick-first-value(gethostname(), "ISC-dhclient");
+send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+send dhcp-lease-time 3600;
+supersede domain-search "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/client/dhclient.conf.5 b/client/dhclient.conf.5
new file mode 100644
index 0000000..1e376ca
--- /dev/null
+++ b/client/dhclient.conf.5
@@ -0,0 +1,739 @@
+.\" $Id: dhclient.conf.5,v 1.25.24.7 2011-04-21 14:08:14 tomasz Exp $
+.\"
+.\" Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Software Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH dhclient.conf 5
+.SH NAME
+dhclient.conf - DHCP client configuration file
+.SH DESCRIPTION
+The dhclient.conf file contains configuration information for
+.IR dhclient,
+the Internet Systems Consortium DHCP Client.
+.PP
+The dhclient.conf file is a free-form ASCII text file. It is parsed by
+the recursive-descent parser built into dhclient. 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 # character and
+end at the end of the line.
+.PP
+The dhclient.conf 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:
+.PP
+.I The
+.B timeout
+.I statement
+.PP
+.B timeout
+.I time
+.B ;
+.PP
+The
+.I 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.
+.PP
+.I The
+.B retry
+.I statement
+.PP
+ \fBretry \fItime\fR\fB;\fR
+.PP
+The
+.I 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.
+.PP
+.I The
+.B select-timeout
+.I statement
+.PP
+ \fBselect-timeout \fItime\fR\fB;\fR
+.PP
+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
+.I 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
+.I select-timeout
+has expired, the client will accept the first offer that arrives.
+.PP
+By default, the select-timeout is zero seconds - that is, the client
+will take the first offer it sees.
+.PP
+.I The
+.B reboot
+.I statement
+.PP
+ \fBreboot \fItime\fR\fB;\fR
+.PP
+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
+.I 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.
+.PP
+.I The
+.B backoff-cutoff
+.I statement
+.PP
+ \fBbackoff-cutoff \fItime\fR\fB;\fR
+.PP
+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
+.I backoff-cutoff
+statement determines the maximum amount of time that the client is
+allowed to back off, the actual value will be evaluated randomly between
+1/2 to 1 1/2 times the \fItime\fR specified. It defaults to two minutes.
+.PP
+.I The
+.B initial-interval
+.I statement
+.PP
+ \fBinitial-interval \fItime\fR\fB;\fR
+.PP
+The
+.I 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.
+.PP
+.I The initial-delay
+.I statement
+.PP
+ \fBinitial-delay \fItime\fR\fB;\fR
+.PP
+.I initial-delay
+parameter sets the maximum time client can wait after start before
+commencing first transmission.
+According to RFC2131 Section 4.4.1, client should wait a random time between
+startup and the actual first transmission. Previous versions of ISC DHCP
+client used to wait random time up to 5 seconds, but that was unwanted
+due to impact on startup time. As such, new versions have the default
+initial delay set to 0. To restore old behavior, please set initial-delay
+to 5.
+.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 \fIDHCP Options\fR. DHCP Options are defined in
+ \fBdhcp-options(5)\fR.
+.PP
+.I The
+.B request
+.I statement
+.PP
+ \fB[ also ] request [ [ \fIoption-space\fR . ] \fIoption\fR ] [\fB,\fI ... ]\fB;\fR
+.PP
+The 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. By default, the DHCPv4 client
+requests the subnet-mask, broadcast-address, time-offset, routers,
+domain-name, domain-name-servers and host-name options while the DHCPv6
+client requests the dhcp6 name-servers and domain-search options. Note
+that if you enter a \'request\' statement, you over-ride these defaults
+and these options will not be requested.
+.PP
+In some cases, it may be desirable to send no parameter request list
+at all. To do this, simply write the request statement but specify
+no parameters:
+.PP
+.nf
+ request;
+.fi
+.PP
+In most cases, it is desirable to simply add one option to the request
+list which is of interest to the client in question. In this case, it
+is best to \'also request\' the additional options:
+.PP
+.nf
+ also request domain-search, dhcp6.sip-servers-addresses;
+.fi
+.PP
+.I The
+.B require
+.I statement
+.PP
+ \fB[ also ] require [ [ \fIoption-space\fR . ] \fIoption\fR ] [\fB,\fI ... ]\fB;\fR
+.PP
+The 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. There is no default require list.
+.PP
+.nf
+ require name-servers;
+
+ interface eth0 {
+ also require domain-search;
+ }
+.PP
+.I The
+.B send
+.I statement
+.PP
+ \fBsend { [ \fIoption declaration\fR ]
+[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR
+.PP
+The 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 \fBdhcp-options(5)\fR. Options that are
+always sent in the DHCP protocol should not be specified here, except
+that the client can specify a requested \fBdhcp-lease-time\fR 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.
+.SH DYNAMIC DNS
+The client now has some very limited support for doing DNS updates
+when a lease is acquired. This is prototypical, and probably doesn't
+do what you want. It also only works if you happen to have control
+over your DNS server, which isn't very likely.
+.PP
+Note that everything in this section is true whether you are using DHCPv4
+or DHCPv6. The exact same syntax is used for both.
+.PP
+To make it work, you have to declare a key and zone as in the DHCP
+server (see \fBdhcpd.conf\fR(5) for details). You also need to
+configure the fqdn option on the client, as follows:
+.PP
+.nf
+ send fqdn.fqdn "grosse.fugue.com.";
+ send fqdn.encoded on;
+ send fqdn.server-update off;
+ also request fqdn, dhcp6.fqdn;
+.fi
+.PP
+The \fIfqdn.fqdn\fR option \fBMUST\fR be a fully-qualified domain
+name. You \fBMUST\fR define a zone statement for the zone to be
+updated. The \fIfqdn.encoded\fR option may need to be set to
+\fIon\fR or \fIoff\fR, depending on the DHCP server you are using.
+.PP
+.I The
+.B do-forward-updates
+.I statement
+.PP
+ \fBdo-forward-updates [ \fIflag\fR ] \fB;\fR
+.PP
+If you want to do DNS updates in the DHCP client
+script (see \fBdhclient-script(8)\fR) rather than having the
+DHCP client do the update directly (for example, if you want to
+use SIG(0) authentication, which is not supported directly by the
+DHCP client, you can instruct the client not to do the update using
+the \fBdo-forward-updates\fR statement. \fIFlag\fR should be \fBtrue\fR
+if you want the DHCP client to do the update, and \fBfalse\fR if
+you don't want the DHCP client to do the update. By default, the DHCP
+client will do the DNS update.
+.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.
+.PP
+.I The
+.B default
+.I statement
+.PP
+ \fBdefault [ \fIoption declaration\fR ] \fB;\fR
+.PP
+If for some option 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
+.B default
+statement.
+.PP
+.I The
+.B supersede
+.I statement
+.PP
+ \fBsupersede [ \fIoption declaration\fR ] \fB;\fR
+.PP
+If for some option the client should always use a locally-configured
+value or values rather than whatever is supplied by the server, these
+values can be defined in the
+.B supersede
+statement.
+.PP
+.I The
+.B prepend
+.I statement
+.PP
+ \fBprepend [ \fIoption declaration\fR ] \fB;\fR
+.PP
+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
+.B prepend
+statement. The
+.B prepend
+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.
+.PP
+.I The
+.B append
+.I statement
+.PP
+ \fBappend [ \fIoption declaration\fR ] \fB;\fR
+.PP
+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
+.B append
+statement. The
+.B 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.
+.SH LEASE DECLARATIONS
+.PP
+.I The
+.B lease
+.I declaration
+.PP
+ \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR
+.PP
+The DHCP client may decide after some period of time (see \fBPROTOCOL
+TIMING\fR) 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 \fIfixed\fR 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
+.B lease
+statement.
+.PP
+NOTE: the lease statement is also used in the 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
+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:
+.PP
+ \fBbootp;\fR
+.PP
+The
+.B 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.
+.PP
+ \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR
+.PP
+The
+.B 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 dhclient.conf file, the
+interface should also be specified, although this is not required.
+.PP
+ \fBfixed-address\fR \fIip-address\fR\fB;\fR
+.PP
+The
+.B 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).
+.PP
+ \fBfilename "\fR\fIstring\fR\fB";\fR
+.PP
+The
+.B 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.
+.PP
+ \fBserver-name "\fR\fIstring\fR\fB";\fR
+.PP
+The
+.B server-name
+statement specifies the name of the boot server name to use. This is
+also not used by the standard client configuration script.
+.PP
+ \fBoption\fR \fIoption-declaration\fR\fB;\fR
+.PP
+The
+.B option
+statement is used to specify the value of an option supplied by the
+server, or, in the case of predefined leases declared in
+dhclient.conf, the value that the user wishes the client configuration
+script to use if the predefined lease is used.
+.PP
+ \fBscript "\fIscript-name\fB";\fR
+.PP
+The
+.B 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
+.B dhclient-script(8).
+.PP
+ \fBvendor option space "\fIname\fB";\fR
+.PP
+The
+.B vendor option space
+statement is used to specify which option space should be used for
+decoding the vendor-encapsulate-options option if one is received.
+The \fIdhcp-vendor-identifier\fR can be used to request a specific
+class of vendor options from the server. See
+.B dhcp-options(5)
+for details.
+.PP
+ \fBmedium "\fImedia setup\fB";\fR
+.PP
+The
+.B 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 Unix 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 uses a
+media type (see the
+.B 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.
+.PP
+ \fBrenew\fR \fIdate\fB;\fR
+.PP
+ \fBrebind\fR \fIdate\fB;\fR
+.PP
+ \fBexpire\fR \fIdate\fB;\fR
+.PP
+The \fBrenew\fR 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 \fBrebind\fR statement defines the time at which the dhcp
+client should begin to try to contact \fIany\fR dhcp server in order
+to renew its lease. The \fBexpire\fR 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.
+.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 in one of two ways. The software will output times in
+these two formats depending on if the \fBdb-time-format\fR configuration
+parameter has been set to \fIdefault\fR or \fIlocal\fR.
+.PP
+If it is set to \fIdefault\fR, then \fIdate\fR values appear as follows:
+.PP
+ \fI<weekday> <year>\fB/\fI<month>\fB/\fI<day>
+<hour>\fB:\fI<minute>\fB:\fI<second>\fR
+.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.
+.PP
+If the \fBdb-time-format\fR configuration was set to \fIlocal\fR, then
+the \fIdate\fR values appear as follows:
+.PP
+ \fBepoch\fR \fI<seconds-since-epoch>\fR\fB; #\fR \fI<day-name> <month-name>
+<day-number> <hours>\fR\fB:\fR\fI<minutes>\fR\fB:\fR\fI<seconds> <year>\fR
+.PP
+The \fIseconds-since-epoch\fR is as according to the system's local clock (often
+referred to as "unix time"). The \fB#\fR symbol supplies a comment that
+describes what actual time this is as according to the system's configured
+timezone, at the time the value was written. It is provided only for human
+inspection, the epoch time is the only recommended value for machine
+inspection.
+.PP
+Note that when defining a static lease, one may use either time format one
+wishes, and need not include the comment or values after it.
+.PP
+If the time is infinite in duration, then the \fIdate\fR is \fBnever\fR
+instead of an actual date.
+.SH ALIAS DECLARATIONS
+ \fBalias { \fI declarations ... \fB}\fR
+.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 Systems
+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
+.B alias
+declaration.
+.PP
+The 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
+ \fBdb-time-format\fR [ \fIdefault\fR | \fIlocal\fR ] \fB;\fR
+.PP
+The \fBdb-time-format\fR option determines which of two output methods are
+used for printing times in leases files. The \fIdefault\fR format provides
+day-and-time in UTC, whereas \fIlocal\fR uses a seconds-since-epoch to store
+the time value, and helpfully places a local timezone time in a comment on
+the same line. The formats are described in detail in this manpage, whithin
+the LEASE DECLARATIONS section.
+.PP
+ \fBreject \fIcidr-ip-address\fR [\fB,\fR \fI...\fB \fIcidr-ip-address\fR ] \fB;\fR
+.PP
+The
+.B reject
+statement causes the DHCP client to reject offers from
+servers whose server identifier matches any of the specified hosts or
+subnets. 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.
+.PP
+The \fIcidr-ip-address\fR configuration type is of the
+form \fIip-address\fR[\fB/\fIprefixlen\fR], where \fIip-address\fR is a
+dotted quad IP address, and \fRprefixlen\fR is the CIDR prefix length of
+the subnet, counting the number of significant bits in the netmask starting
+from the leftmost end. Example configuration syntax:
+.PP
+.I \fIreject\fR 192.168.0.0\fB/\fR16\fB,\fR 10.0.0.5\fB;\fR
+.PP
+The above example would cause offers from any server identifier in the
+entire RFC 1918 "Class C" network 192.168.0.0/16, or the specific
+single address 10.0.0.5, to be rejected.
+.PP
+ \fBinterface "\fIname\fB" { \fIdeclarations ... \fB }
+.PP
+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.
+.PP
+.B Note well:
+ISC dhclient only maintains one list of interfaces, which is either
+determined at startup from command line arguments, or otherwise is
+autodetected. If you supplied the list of interfaces on the command
+line, this configuration clause will add the named interface to the
+list in such a way that will cause it to be configured by DHCP. Which
+may not be the result you had intended. This is an undesirable side
+effect that will be addressed in a future release.
+.PP
+ \fBpseudo "\fIname\fR" "\fIreal-name\fB" { \fIdeclarations ... \fB }
+.PP
+Under some circumstances it can be useful to declare a pseudo-interface
+and have the DHCP client acquire a configuration for that interface.
+Each interface that the DHCP client is supporting normally has a DHCP
+client state machine running on it to acquire and maintain its lease.
+A pseudo-interface is just another state machine running on the
+interface named \fIreal-name\fR, with its own lease and its own
+state. If you use this feature, you must provide a client identifier
+for both the pseudo-interface and the actual interface, and the two
+identifiers must be different. You must also provide a separate
+client script for the pseudo-interface to do what you want with the IP
+address. For example:
+.PP
+.nf
+ interface "ep0" {
+ send dhcp-client-identifier "my-client-ep0";
+ }
+ pseudo "secondary" "ep0" {
+ send dhcp-client-identifier "my-client-ep0-secondary";
+ script "/etc/dhclient-secondary";
+ }
+.fi
+.PP
+The client script for the pseudo-interface should not configure the
+interface up or down - essentially, all it needs to handle are the
+states where a lease has been acquired or renewed, and the states
+where a lease has expired. See \fBdhclient-script(8)\fR for more
+information.
+.PP
+ \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR
+.PP
+The
+.B 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.
+.PP
+ \fBhardware\fR \fIlink-type mac-address\fR\fB;\fR
+.PP
+The
+.B hardware
+statement defines the hardware MAC address to use for this interface,
+for DHCP servers or relays to direct their replies. dhclient will determine
+the interface's MAC address automatically, so use of this parameter
+is not recommended. The \fIlink-type\fR corresponds to the interface's
+link layer type (example: \'ethernet\'), while the \fImac-address\fR is
+a string of colon-separated hexadecimal values for octets.
+.PP
+ \fBanycast-mac\fR \fIlink-type mac-address\fR\fB;\fR
+.PP
+The
+.B anycast-mac
+statement over-rides the all-ones broadcast MAC address dhclient will use
+when it is transmitting packets to the all-ones limited broadcast IPv4
+address. This configuration parameter is useful to reduce the number of
+broadcast packets transmitted by DHCP clients, but is only useful if you
+know the DHCP service(s) anycast MAC address prior to configuring your
+client. The \fIlink-type\fR and \fImac-address\fR parameters are configured
+in a similar manner to the \fBhardware\fR statement.
+.PP
+.SH SAMPLE
+The following configuration file is used on a laptop running NetBSD
+1.3. The laptop 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.
+
+.nf
+
+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";
+ hardware ethernet 00:a0:24:ab:fb:9c;
+ send dhcp-client-identifier 1:0:a0:24:ab:fb:9c;
+ send dhcp-lease-time 3600;
+ supersede domain-search "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 "CLIENTBINDIR/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;
+}
+.fi
+This is a very complicated dhclient.conf file - in general, yours
+should be much simpler. In many cases, it's sufficient to just
+create an empty dhclient.conf file - the defaults are usually fine.
+.SH SEE ALSO
+dhcp-options(5), dhcp-eval(5), dhclient.leases(5), dhcpd(8), dhcpd.conf(5),
+RFC2132, RFC2131.
+.SH AUTHOR
+.B dhclient(8)
+was written by Ted Lemon
+under a contract with Vixie Labs. Funding
+for this project was provided by Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at
+.B https://www.isc.org.
diff --git a/client/dhclient.leases.5 b/client/dhclient.leases.5
new file mode 100644
index 0000000..971ca1e
--- /dev/null
+++ b/client/dhclient.leases.5
@@ -0,0 +1,57 @@
+.\" $Id: dhclient.leases.5,v 1.5.24.3 2011-02-23 23:52:21 sar Exp $
+.\"
+.\" Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1997-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.\" $Id: dhclient.leases.5,v 1.5.24.3 2011-02-23 23:52:21 sar Exp $
+.\"
+.TH dhclient.leases 5
+.SH NAME
+dhclient.leases - DHCP client lease database
+.SH DESCRIPTION
+The Internet Systems 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 format of the lease declarations is described in
+.B dhclient.conf(5).
+.SH FILES
+.B DBDIR/dhclient.leases
+.SH SEE ALSO
+dhclient(8), dhcp-options(5), dhclient.conf(5), dhcpd(8),
+dhcpd.conf(5), RFC2132, RFC2131.
+.SH AUTHOR
+.B dhclient(8)
+was written by Ted Lemon
+under a contract with Vixie Labs. Funding
+for this project was provided by Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at
+.B https://www.isc.org.
diff --git a/client/scripts/bsdos b/client/scripts/bsdos
new file mode 100755
index 0000000..2cbcdfb
--- /dev/null
+++ b/client/scripts/bsdos
@@ -0,0 +1,324 @@
+#!/bin/sh
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ if [ "x$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ "x$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >> /etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient6
+ chmod 644 /etc/resolv.conf.dhclient6
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6
+ done
+
+ mv /etc/resolv.conf.dhclient6 /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$new_network_number != x ]; then
+ echo New Network Number: $new_network_number
+fi
+
+if [ x$new_broadcast_address != x ]; then
+ echo New Broadcast Address: $new_broadcast_address
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_netmask_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_netmask_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ exit_with_hooks 0
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \
+ broadcast 255.255.255.255 up
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0;
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_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
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]
+ then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ if [ "$new_static_routes" != "" ]; then
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; 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
+ if [ x$old_ip_address != x ]; then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ sleep 1
+ if [ "$new_routers" != "" ]; then
+ set $new_routers
+ if ping -q -c 1 -w 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $0 $1
+ shift; shift
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ fi
+ eval "ifconfig $interface inet -alias $new_ip_address $medium"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ ${reason} = PREINIT6 ] ; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # XXX: Remove any stale addresses from aborted clients.
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = BOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 add ${new_ip6_address}/${new_ip6_prefixlen}
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = DEPREF6 ] ; then
+ if [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ # XXX:
+ # There doesn't appear to be a way to update an addr to indicate
+ # preference.
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 delete ${old_ip6_address}/${old_ip6_prefixlen}
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/freebsd b/client/scripts/freebsd
new file mode 100755
index 0000000..a2aa4a1
--- /dev/null
+++ b/client/scripts/freebsd
@@ -0,0 +1,392 @@
+#!/bin/sh
+#
+# $Id: freebsd,v 1.23.54.1 2011-05-18 20:01:54 sar Exp $
+#
+# $FreeBSD$
+
+if [ -x /usr/bin/logger ]; then
+ LOGGER="/usr/bin/logger -s -p user.notice -t dhclient"
+else
+ LOGGER=echo
+fi
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ ( cat /dev/null > /etc/resolv.conf.dhclient )
+ exit_status=$?
+ if [ $exit_status -ne 0 ]; then
+ $LOGGER "Unable to create /etc/resolv.conf.dhclient: Error $exit_status"
+ else
+ if [ "x$new_domain_search" != x ]; then
+ ( echo search $new_domain_search >> /etc/resolv.conf.dhclient )
+ exit_status=$?
+ elif [ "x$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ ( echo search $new_domain_name >> /etc/resolv.conf.dhclient )
+ exit_status=$?
+ fi
+ for nameserver in $new_domain_name_servers; do
+ if [ $exit_status -ne 0 ]; then
+ break
+ fi
+ ( echo nameserver $nameserver >>/etc/resolv.conf.dhclient )
+ exit_status=$?
+ done
+
+ # If there were no errors, attempt to mv the new file into place.
+ if [ $exit_status -eq 0 ]; then
+ ( mv /etc/resolv.conf.dhclient /etc/resolv.conf )
+ exit_status=$?
+ fi
+
+ if [ $exit_status -ne 0 ]; then
+ $LOGGER "Error while writing new /etc/resolv.conf."
+ fi
+ fi
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ ( cat /dev/null > /etc/resolv.conf.dhclient6 )
+ exit_status=$?
+ if [ $exit_status -ne 0 ] ; then
+ $LOGGER "Unable to create /etc/resolv.conf.dhclient6: Error $exit_status"
+ else
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ ( echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6 )
+ exit_status=$?
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ if [ $exit_status -ne 0 ] ; then
+ break
+ fi
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ ( echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 )
+ exit_status=$?
+ done
+
+ if [ $exit_status -eq 0 ] ; then
+ ( mv /etc/resolv.conf.dhclient6 /etc/resolv.conf )
+ exit_status=$?
+ fi
+
+ if [ $exit_status -ne 0 ] ; then
+ $LOGGER "Error while writing new /etc/resolv.conf."
+ fi
+ fi
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$new_network_number != x ]; then
+ $LOGGER New Network Number: $new_network_number
+fi
+
+if [ x$new_broadcast_address != x ]; then
+ $LOGGER New Broadcast Address: $new_broadcast_address
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_netmask_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_netmask_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ exit_with_hooks 0
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \
+ broadcast 255.255.255.255 up
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0;
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`/bin/hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ $LOGGER "New Hostname: $new_host_name"
+ hostname $new_host_name
+ fi
+ fi
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_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
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]
+ then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ -n "$old_static_routes" ]; then
+ set -- $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' |sh
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ $LOGGER "New IP Address ($interface): $new_ip_address"
+ $LOGGER "New Subnet Mask ($interface): $new_subnet_mask"
+ $LOGGER "New Broadcast Address ($interface): $new_broadcast_address"
+ if [ -n "$new_routers" ]; then
+ $LOGGER "New Routers: $new_routers"
+ fi
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ # If the subnet is captive, eg the netmask is /32 but the default
+ # gateway is (obviously) outside of this, then we need to produce a
+ # host route to reach the gateway.
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router -interface $interface
+ fi
+ route add default $router >/dev/null 2>&1
+ done
+ if [ -n "$new_static_routes" ]; then
+ $LOGGER "New Static Routes: $new_static_routes"
+ set -- $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ for router in $new_routers; do
+ # If the subnet is captive, eg the netmask is /32 but the default
+ # gateway is (obviously) outside of this, then we need to produce a
+ # host route to reach the gateway.
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router -interface $interface
+ fi
+ route add default $router >/dev/null 2>&1
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; 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
+ if [ x$old_ip_address != x ]; then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ -n "$old_static_routes" ]; then
+ set -- $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \
+ |sh >/dev/null 2>&1
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ $LOGGER "New IP Address ($interface): $new_ip_address"
+ $LOGGER "New Subnet Mask ($interface): $new_subnet_mask"
+ $LOGGER "New Broadcast Address ($interface): $new_broadcast_address"
+ sleep 1
+ if [ -n "$new_routers" ]; then
+ $LOGGER "New Routers: $new_routers"
+ set -- $new_routers
+ if ping -q -c 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router -interface $interface
+ fi
+ route add default $router >/dev/null 2>&1
+ done
+ set -- $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ fi
+ eval "ifconfig $interface inet -alias $new_ip_address $medium"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ -n "$old_static_routes" ]; then
+ set -- $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -d \1/p' \
+ |sh >/dev/null 2>&1
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ ${reason} = PREINIT6 ] ; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # XXX: Remove any stale addresses from aborted clients.
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = BOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = DEPREF6 ] ; then
+ if [ x${new_ip6_address} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address} deprecated
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${old_ip6_address}/${old_ip6_prefixlen} -alias
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/linux b/client/scripts/linux
new file mode 100755
index 0000000..14655f0
--- /dev/null
+++ b/client/scripts/linux
@@ -0,0 +1,318 @@
+#!/bin/bash
+# dhclient-script for Linux. Dan Halbert, March, 1997.
+# Updated for Linux 2.[12] by Brian J. Murrell, January 1999.
+# No guarantees about this. I'm a novice at the details of Linux
+# networking.
+
+# Notes:
+
+# 0. This script is based on the netbsd script supplied with dhcp-970306.
+
+# 1. ifconfig down apparently deletes all relevant routes and flushes
+# the arp cache, so this doesn't need to be done explicitly.
+
+# 2. The alias address handling here has not been tested AT ALL.
+# I'm just going by the doc of modern Linux ip aliasing, which uses
+# notations like eth0:0, eth0:1, for each alias.
+
+# 3. I have to calculate the network address, and calculate the broadcast
+# address if it is not supplied. This might be much more easily done
+# by the dhclient C code, and passed on.
+
+# 4. TIMEOUT not tested. ping has a flag I don't know, and I'm suspicious
+# of the $1 in its args.
+
+# 'ip' just looks too weird. /sbin/ip looks less weird.
+ip=/sbin/ip
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ chmod 644 /etc/resolv.conf.dhclient
+ if [ x"$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ x"$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient6
+ chmod 644 /etc/resolv.conf.dhclient6
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6
+ fi
+ shopt -s nocasematch
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ if [[ "$nameserver" =~ ^fe80:: ]]
+ then
+ zone_id="%$interface"
+ else
+ zone_id=
+ fi
+ echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6
+ done
+ shopt -u nocasematch
+
+ mv /etc/resolv.conf.dhclient6 /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$new_broadcast_address != x ]; then
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_subnet_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_subnet_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ # Linux doesn't do mediums (ok, ok, media).
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; then
+ # Bring down alias interface. Its routes will disappear too.
+ ifconfig $interface:0- inet 0
+ fi
+ ifconfig $interface 0 up
+
+ # We need to give the kernel some time to get the interface up.
+ sleep 1
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = "x(none)" ] || \
+ [ x$current_hostname = xlocalhost ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$new_host_name != x$old_host_name ]; then
+ hostname "$new_host_name"
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_ip_address ]; then
+ # Possible new alias. Remove old alias.
+ ifconfig $interface:0- inet 0
+ fi
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
+ # IP address changed. Bringing down the interface will delete all routes,
+ # and clear the ARP cache.
+ ifconfig $interface inet 0 down
+
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+
+ ifconfig $interface inet $new_ip_address $new_subnet_arg \
+ $new_broadcast_arg $mtu_arg
+ # Add a network route to the computed network address.
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router $metric_arg dev $interface
+ done
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ for router in $old_routers; do
+ route del default gw $router
+ done
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router $metric_arg dev $interface
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface:0- inet 0
+ ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address $interface:0
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; then
+ # Turn off alias interface.
+ ifconfig $interface:0- inet 0
+ fi
+ if [ x$old_ip_address != x ]; then
+ # Shut down interface, which will delete routes and clear arp cache.
+ ifconfig $interface inet 0 down
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address $interface:0
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0- inet 0
+ fi
+ ifconfig $interface inet $new_ip_address $new_subnet_arg \
+ $new_broadcast_arg $mtu_arg
+ set $new_routers
+ if ping -q -c 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0 inet $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address dev $interface:0
+ fi
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router $metric_arg dev $interface
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ ifconfig $interface inet 0 down
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ x$reason = xPREINIT6 ] ; then
+ # Ensure interface is up.
+ ${ip} link set ${interface} up
+
+ # Remove any stale addresses from aborted clients.
+ ${ip} -f inet6 addr flush dev ${interface} scope global permanent
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
+ dev ${interface} scope global
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ${ip} -f inet6 addr add ${new_ip6_address}/${new_ip6_prefixlen} \
+ dev ${interface} scope global
+
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xDEPREF6 ] ; then
+ if [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ${ip} -f inet6 addr change ${new_ip6_address}/${new_ip6_prefixlen} \
+ dev ${interface} scope global preferred_lft 0
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ${ip} -f inet6 addr del ${old_ip6_address}/${old_ip6_prefixlen} \
+ dev ${interface}
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/macos b/client/scripts/macos
new file mode 100755
index 0000000..cb467d1
--- /dev/null
+++ b/client/scripts/macos
@@ -0,0 +1,202 @@
+#!/bin/sh
+#
+# $Id: macos,v 1.2.108.1 2011-05-18 20:01:54 sar Exp $
+#
+# automous run of this script will commit the DNS setting
+#
+
+if [ -x /usr/bin/logger ]; then
+ LOGGER="/usr/bin/logger -s -p user.notice -t dhclient"
+else
+ LOGGER=echo
+fi
+
+to_commit="yes"
+
+make_resolv_conf() {
+ to_commit="no"
+ if [ "x${new_dhcp6_name_servers}" != x ]; then
+ ( cat /dev/null > /var/run/resolv.conf.dhclient6 )
+ exit_status=$?
+ if [ $exit_status -ne 0 ]; then
+ $LOGGER "Unable to create /var/run/resolv.conf.dhclient6: Error $exit_status"
+ else
+ if [ "x${new_dhcp6_domain_search}" != x ]; then
+ ( echo search ${new_dhcp6_domain_search} >> /var/run/resolv.conf.dhclient6 )
+ exit_status=$?
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ if [ $exit_status -ne 0 ]; then
+ break
+ fi
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ ( echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6 )
+ exit_status=$?
+ done
+
+ if [ $exit_status -eq 0 ]; then
+ to_commit="force"
+ commit_resolv_conf
+ fi
+ fi
+ fi
+}
+
+# Try to commit /var/run/resolv.conf.dhclient6 contents to
+# SystemConfiguration Dynamic Store
+# Note this will be cleared by the next location change
+commit_resolv_conf() {
+ if [ -f /var/run/resolv.conf.dhclient6 ]; then
+ if [ -x /usr/sbin/scutil ]; then
+ serviceID=`echo show State:/Network/Global/IPv6 | \
+ /usr/sbin/scutil | \
+ awk '/PrimaryService/ { print $3 }'`
+ echo $serviceID
+ if [ x$serviceID = x ]; then
+ $LOGGER "Can't find the primary IPv6 service"
+ else
+ tmp=`mktemp SC_dhclient6.XXXXXXXXXX`
+ echo list | /usr/sbin/scutil > /tmp/$tmp
+ grep -q State:/Network/Service/$serviceID/DNS /tmp/$tmp
+ grep_status=$?
+ if [ $grep_status -eq 0 ]; then
+ $LOGGER "DNS service already set in primary IPv6 service"
+ rm /tmp/$tmp
+ else
+ res=/var/run/resolv.conf.dhclient6
+ cp /dev/null /tmp/$tmp
+ grep -q '^nameserver' $res
+ grep_status=$?
+ if [ $grep_status -eq 0 ]; then
+ echo d.add ServerAddresses '*' \
+ `awk 'BEGIN { n="" } \
+ /^nameserver/ { n=n " " $2 } \
+ END { print n}' < $res` >> /tmp/$tmp
+ fi
+ grep -q '^search' $res
+ grep_status=$?
+ if [ $grep_status -eq 0 ]; then
+ echo d.add SearchDomains '*' \
+ `sed 's/^search//' < $res` >> /tmp/$tmp
+ fi
+ echo set State:/Network/Service/$serviceID/DNS >> /tmp/$tmp
+ echo quit >> /tmp/$tmp
+ cat /tmp/$tmp
+ /usr/sbin/scutil < /tmp/$tmp
+ rm /tmp/$tmp
+ fi
+ fi
+ else
+ $LOGGER "Can't find SystemConfiguration tools."
+ fi
+ else
+ if [ $to_commit = force ]; then
+ $LOGGER "Can't find /var/run/resolv.conf.dhclient6"
+ fi
+ fi
+ to_commit="done"
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ exit_with_hooks 0
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ x$reason = xPREINIT6 ]; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # XXX: Remove any stale addresses from aborted clients.
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ]; then
+ echo Prefix $reason old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND6 ]; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ]; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ]; then
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ]; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xDEPREF6 ]; then
+ if [ x${new_ip6_address} = x ]; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address} deprecated
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ]; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ]; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${old_ip6_address}/${old_ip6_prefixlen} -alias
+
+ exit_with_hooks 0
+fi
+
+if [ $to_commit = yes ]; then
+ commit_resolv_conf
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/netbsd b/client/scripts/netbsd
new file mode 100755
index 0000000..77b0fba
--- /dev/null
+++ b/client/scripts/netbsd
@@ -0,0 +1,324 @@
+#!/bin/sh
+
+make_resolv_conf() {
+ if [ "x$new_domain_name" != x ] && [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ if [ "x$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ "x$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient6
+ chmod 644 /etc/resolv.conf.dhclient6
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6
+ done
+
+ mv /etc/resolv.conf.dhclient6 /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$new_network_number != x ]; then
+ echo New Network Number: $new_network_number
+fi
+
+if [ x$new_broadcast_address != x ]; then
+ echo New Broadcast Address: $new_broadcast_address
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_netmask_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_netmask_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+ if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+ fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ exit_with_hooks 0
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \
+ broadcast 255.255.255.255 up
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_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
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]
+ then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ if [ "$new_static_routes" != "" ]; then
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; 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
+ if [ x$old_ip_address != x ]; then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ sleep 1
+ if [ "$new_routers" != "" ]; then
+ set $new_routers
+ if ping -q -c 1 -w 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $0 $1
+ shift; shift
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ fi
+ eval "ifconfig $interface inet -alias $new_ip_address $medium"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ ${reason} = PREINIT6 ] ; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # XXX: Remove any stale addresses from aborted clients.
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = BOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 add ${new_ip6_address}/${new_ip6_prefixlen}
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = DEPREF6 ] ; then
+ if [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ # XXX:
+ # There doesn't appear to be a way to update an addr to indicate
+ # preference.
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 delete ${old_ip6_address}/${old_ip6_prefixlen}
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/nextstep b/client/scripts/nextstep
new file mode 100644
index 0000000..9273c5a
--- /dev/null
+++ b/client/scripts/nextstep
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# simplified dhclient-script for NeXTSTEP/OPENSTEP
+#
+# removed a lot of the cruft from the netbsd version since NeXTSTEP doesn't
+# support aliases and lots of things were breaking for no good reason
+#
+# 14 Sep 1997, David W. Young
+#
+if [ x$reason = xPREINIT ]; then
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 up >/dev/null 2>&1
+ exit 0
+fi
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]
+ then
+ ifconfig $interface $new_ip_address netmask $new_subnet_mask \
+ >/dev/null 2>&1
+ route add $new_ip_address 127.1 0 >/dev/null 2>&1
+ for router in $new_routers ; do
+ route add default $router 1 >/dev/null 2>&1
+ done
+ fi
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ if [ "x$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ "x$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ fi
+ exit 0
+fi
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$old_ip_address != x ]; then
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for $router in $old_routers ; do
+ route delete default $router >/dev/null 2>&1
+ done
+ fi
+ exit 0
+fi
diff --git a/client/scripts/openbsd b/client/scripts/openbsd
new file mode 100644
index 0000000..ce22d75
--- /dev/null
+++ b/client/scripts/openbsd
@@ -0,0 +1,318 @@
+#!/bin/sh
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ if [ x"$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ x"$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient6
+ chmod 644 /etc/resolv.conf.dhclient6
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6
+ done
+
+ mv /etc/resolv.conf.dhclient6 /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$new_network_number != x ]; then
+ echo New Network Number: $new_network_number
+fi
+
+if [ x$new_broadcast_address != x ]; then
+ echo New Broadcast Address: $new_broadcast_address
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_netmask_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_netmask_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ eval "ifconfig $interface $medium"
+ eval "ifconfig $interface inet -alias 0.0.0.0 $medium" >/dev/null 2>&1
+ sleep 1
+ exit_with_hooks 0
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \
+ broadcast 255.255.255.255 up
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0;
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_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
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]
+ then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' |sh
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $medium"
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ if [ "$new_static_routes" != "" ]; then
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $1 $2
+ shift; shift
+ done
+ fi
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; 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
+ if [ x$old_ip_address != x ]; then
+ eval "ifconfig $interface inet -alias $old_ip_address $medium"
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; 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
+ eval "ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $medium"
+ sleep 1
+ if [ "$new_routers" != "" ]; then
+ set $new_routers
+ if ping -q -c 1 -w 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface inet alias $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1
+ fi
+ route add $new_ip_address 127.1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router >/dev/null 2>&1
+ done
+ set $new_static_routes
+ while [ $# -gt 1 ]; do
+ route add $0 $1
+ shift; shift
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ fi
+ eval "ifconfig $interface inet -alias $new_ip_address $medium"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ if [ "$old_static_routes" != "" ]; then
+ set $old_static_routes
+ while [ $# -gt 1 ]; do
+ route delete $1 $2
+ shift; shift
+ done
+ fi
+ arp -n -a | sed -n -e 's/^.*(\(.*\)) at .*$/arp -n -d \1/p' \
+ |sh >/dev/null 2>&1
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ ${reason} = PREINIT6 ] ; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # XXX: Remove any stale addresses from aborted clients.
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = BOUND6 ] ; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 add ${new_ip6_address}/${new_ip6_prefixlen}
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = RENEW6 ] || [ ${reason} = REBIND6 ] ; then
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = DEPREF6 ] ; then
+ if [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ # XXX:
+ # There doesn't appear to be a way to update an addr to indicate
+ # preference.
+
+ exit_with_hooks 0
+fi
+
+if [ ${reason} = EXPIRE6 -o ${reason} = RELEASE6 -o ${reason} = STOP6 ] ; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 delete ${old_ip6_address}/${old_ip6_prefixlen}
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/openwrt b/client/scripts/openwrt
new file mode 100755
index 0000000..9d434ad
--- /dev/null
+++ b/client/scripts/openwrt
@@ -0,0 +1,279 @@
+#!/bin/sh
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ chmod 644 /etc/resolv.conf.dhclient
+ if [ x"$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ x"$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ elif [ "x${new_dhcp6_name_servers}" != x ] ; then
+ cat /dev/null > /etc/resolv.conf.dhclient6
+ chmod 644 /etc/resolv.conf.dhclient6
+
+ if [ "x${new_dhcp6_domain_search}" != x ] ; then
+ echo search ${new_dhcp6_domain_search} >> /etc/resolv.conf.dhclient6
+ fi
+ for nameserver in ${new_dhcp6_name_servers} ; do
+ # If the nameserver has a link-local address
+ # add a <zone_id> (interface name) to it.
+ case $nameserver in
+ fe80:*) zone_id="%$interface";;
+ FE80:*) zone_id="%$interface";;
+ *) zone_id='';;
+ esac
+ echo nameserver ${nameserver}$zone_id >> /etc/resolv.conf.dhclient6
+ done
+
+ mv /etc/resolv.conf.dhclient6 /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+###
+### DHCPv4 Handlers
+###
+
+if [ x$new_broadcast_address != x ]; then
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_subnet_arg="netmask $new_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+ if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+ fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+if [ x$reason = xMEDIUM ]; then
+ # Linux doesn't do mediums (ok, ok, media).
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; then
+ # Bring down alias interface. Its routes will disappear too.
+ ifconfig $interface:0- 0.0.0.0
+ fi
+ ifconfig $interface 0.0.0.0 up
+
+ # We need to give the kernel some time to get the interface up.
+ sleep 1
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_ip_address ]; then
+ # Possible new alias. Remove old alias.
+ ifconfig $interface:0- 0.0.0.0
+ fi
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
+ # IP address changed. Bringing down the interface will delete all routes,
+ # and clear the ARP cache.
+ ifconfig $interface 0.0.0.0 down
+
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+
+ ifconfig $interface $new_ip_address $new_subnet_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router
+ done
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router
+ done
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ ifconfig $interface:0- 0.0.0.0
+ ifconfig $interface:0 $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address $interface:0
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; then
+ # Turn off alias interface.
+ ifconfig $interface:0- 0.0.0.0
+ fi
+ if [ x$old_ip_address != x ]; then
+ # Shut down interface, which will delete routes and clear arp cache.
+ ifconfig $interface 0.0.0.0 down
+ fi
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0 $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address $interface:0
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0- 0.0.0.0
+ fi
+ ifconfig $interface $new_ip_address $new_subnet_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg
+ set $new_routers
+ if ping -q -c 1 $1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ ifconfig $interface:0 $alias_ip_address $alias_subnet_arg
+ route add -host $alias_ip_address dev $interface:0
+ fi
+ for router in $new_routers; do
+ if [ "x$new_subnet_mask" = "x255.255.255.255" ] ; then
+ route add -host $router dev $interface
+ fi
+ route add default gw $router
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ ifconfig $interface 0.0.0.0 down
+ exit_with_hooks 1
+fi
+
+###
+### DHCPv6 Handlers
+###
+
+if [ x$reason = xPREINIT6 ]; then
+ # Ensure interface is up.
+ ifconfig ${interface} up
+
+ # Remove any stale addresses from aborted clients.
+ ip -f inet6 addr flush dev ${interface} scope global
+
+ exit_with_hooks 0
+fi
+
+if [ x${old_ip6_prefix} != x ] || [ x${new_ip6_prefix} != x ] ; then
+ echo Prefix ${reason} old=${old_ip6_prefix} new=${new_ip6_prefix}
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xBOUND6 ]; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias
+
+ # Check for nameserver options.
+ make_resolv_conf
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xRENEW6 ] || [ x$reason = xREBIND6 ]; then
+ if [ x${new_ip6_address} = x ] || [ x${new_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address}/${new_ip6_prefixlen} alias
+
+ # Make sure nothing has moved around on us.
+
+ # Nameservers/domains/etc.
+ if [ "x${new_dhcp6_name_servers}" != "x${old_dhcp6_name_servers}" ] ||
+ [ "x${new_dhcp6_domain_search}" != "x${old_dhcp6_domain_search}" ] ; then
+ make_resolv_conf
+ fi
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xDEPREF6 ]; then
+ if [ x${new_ip6_address} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${new_ip6_address} deprecated
+
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE6 -o x$reason = xRELEASE6 -o x$reason = xSTOP6 ]; then
+ if [ x${old_ip6_address} = x ] || [ x${old_ip6_prefixlen} = x ] ; then
+ exit_with_hooks 2;
+ fi
+
+ ifconfig ${interface} inet6 ${old_ip6_address}/${old_ip6_prefixlen} -alias
+
+ exit_with_hooks 0
+fi
+
+exit_with_hooks 0
diff --git a/client/scripts/solaris b/client/scripts/solaris
new file mode 100755
index 0000000..af553b9
--- /dev/null
+++ b/client/scripts/solaris
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+make_resolv_conf() {
+ if [ x"$new_domain_name_servers" != x ]; then
+ cat /dev/null > /etc/resolv.conf.dhclient
+ if [ x"$new_domain_search" != x ]; then
+ echo search $new_domain_search >> /etc/resolv.conf.dhclient
+ elif [ x"$new_domain_name" != x ]; then
+ # Note that the DHCP 'Domain Name Option' is really just a domain
+ # name, and that this practice of using the domain name option as
+ # a search path is both nonstandard and deprecated.
+ echo search $new_domain_name >> /etc/resolv.conf.dhclient
+ fi
+ for nameserver in $new_domain_name_servers; do
+ echo nameserver $nameserver >>/etc/resolv.conf.dhclient
+ done
+
+ mv /etc/resolv.conf.dhclient /etc/resolv.conf
+ fi
+}
+
+# Must be used on exit. Invokes the local dhcp client exit hooks, if any.
+exit_with_hooks() {
+ exit_status=$1
+ if [ -f /etc/dhclient-exit-hooks ]; then
+ . /etc/dhclient-exit-hooks
+ fi
+# probably should do something with exit status of the local script
+ exit $exit_status
+}
+
+# Invoke the local dhcp client enter hooks, if they exist.
+if [ -f /etc/dhclient-enter-hooks ]; then
+ exit_status=0
+ . /etc/dhclient-enter-hooks
+ # allow the local script to abort processing of this state
+ # local script must set exit_status variable to nonzero.
+ if [ $exit_status -ne 0 ]; then
+ exit $exit_status
+ fi
+fi
+
+if [ x$new_broadcast_address != x ]; then
+ new_broadcast_arg="broadcast $new_broadcast_address"
+fi
+if [ x$old_broadcast_address != x ]; then
+ old_broadcast_arg="broadcast $old_broadcast_address"
+fi
+if [ x$new_subnet_mask != x ]; then
+ new_netmask_arg="netmask $new_subnet_mask"
+fi
+if [ x$old_subnet_mask != x ]; then
+ old_netmask_arg="netmask $old_subnet_mask"
+fi
+if [ x$alias_subnet_mask != x ]; then
+ alias_subnet_arg="netmask $alias_subnet_mask"
+fi
+ if [ x$new_interface_mtu != x ]; then
+ mtu_arg="mtu $new_interface_mtu"
+ fi
+if [ x$IF_METRIC != x ]; then
+ metric_arg="metric $IF_METRIC"
+fi
+
+ifconfig=/sbin/ifconfig
+
+release=`uname -r`
+release=`expr $release : '\(.*\)\..*'`
+relmajor=`echo $release |sed -e 's/^\([^\.]*\)\..*$/\1/'`
+relminor=`echo $release |sed -e 's/^.*\.\([^\.]*\)$/\1/'`
+
+if [ x$reason = xMEDIUM ]; then
+ eval "$ifconfig $interface $medium"
+ $ifconfig $interface
+ sleep 1
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xPREINIT ]; then
+ if [ x$alias_ip_address != x ]; then
+ $ifconfig ${interface}:1 0 down > /dev/null 2>&1
+ route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1
+ fi
+ if [ $relmajor -gt 5 ] || ( [ $relmajor -eq 5 ] && [ $relminor -ge 5 ] )
+ then
+ # Turn the interface on
+ $ifconfig $interface plumb
+ $ifconfig $interface up
+ else
+ $ifconfig $interface inet 0.0.0.0 netmask 0.0.0.0 \
+ broadcast 255.255.255.255 up
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xARPCHECK ] || [ x$reason = xARPSEND ]; then
+ exit_with_hooks 0;
+fi
+
+if [ x$reason = xBOUND ] || [ x$reason = xRENEW ] || \
+ [ x$reason = xREBIND ] || [ x$reason = xREBOOT ]; then
+ current_hostname=`hostname`
+ if [ x$current_hostname = x ] || \
+ [ x$current_hostname = x$old_host_name ]; then
+ if [ x$current_hostname = x ] || \
+ [ x$new_host_name != x$old_host_name ]; then
+ hostname $new_host_name
+ fi
+ fi
+
+ if [ x$old_ip_address != x ] && [ x$alias_ip_address != x ] && \
+ [ x$alias_ip_address != x$old_ip_address ]; then
+ $ifconfig ${interface}:1 inet 0 down > /dev/null 2>&1
+ route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1
+ fi
+ if [ x$old_ip_address != x ] && [ x$old_ip_address != x$new_ip_address ]; then
+ $ifconfig ${interface} inet 0 down
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ fi
+ if [ x$old_ip_address = x ] || [ x$old_ip_address != x$new_ip_address ] || \
+ [ x$reason = xBOUND ] || [ x$reason = xREBOOT ]; then
+ eval "$ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ route add $new_ip_address 127.1 1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router 1 >/dev/null 2>&1
+ done
+ else
+ # we haven't changed the address, have we changed other options
+ # that we wish to update?
+ if [ x$new_routers != x ] && [ x$new_routers != x$old_routers ] ; then
+ # if we've changed routers delete the old and add the new.
+ $LOGGER "New Routers: $new_routers"
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ for router in $new_routers; do
+ route add default $router 1 >/dev/null 2>&1
+ done
+ fi
+ fi
+ if [ x$new_ip_address != x$alias_ip_address ] && [ x$alias_ip_address != x ];
+ then
+ $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1 1
+ fi
+ make_resolv_conf
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xEXPIRE ] || [ x$reason = xFAIL ] || [ x$reason = xRELEASE ] \
+ || [ x$reason = xSTOP ]; then
+ if [ x$alias_ip_address != x ]; then
+ $ifconfig ${interface}:1 0 down > /dev/null 2>&1
+ route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1
+ fi
+ if [ x$old_ip_address != x ]; then
+ $ifconfig $interface inet 0 down
+ route delete $old_ip_address 127.1 >/dev/null 2>&1
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ fi
+ if [ x$alias_ip_address != x ]; then
+ $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1 1
+ fi
+ exit_with_hooks 0
+fi
+
+if [ x$reason = xTIMEOUT ]; then
+ if [ x$alias_ip_address != x ]; then
+ $ifconfig ${interface}:1 0 down > /dev/null 2>&1
+ route delete $alias_ip_address 127.0.0.1 > /dev/null 2>&1
+ fi
+ eval "$ifconfig $interface inet $new_ip_address $new_netmask_arg \
+ $new_broadcast_arg $mtu_arg $metric_arg $medium"
+ sleep 1
+ set $new_routers
+ if ping -s -n -I 1 $1 64 1; then
+ if [ x$new_ip_address != x$alias_ip_address ] && \
+ [ x$alias_ip_address != x ]; then
+ $ifconfig ${interface}:1 inet $alias_ip_address $alias_subnet_arg
+ route add $alias_ip_address 127.0.0.1 1
+ fi
+ route add $new_ip_address 127.1 1 >/dev/null 2>&1
+ for router in $new_routers; do
+ route add default $router 1 >/dev/null 2>&1
+ done
+ make_resolv_conf
+ exit_with_hooks 0
+ fi
+ $ifconfig $interface inet 0 down
+ for router in $old_routers; do
+ route delete default $router >/dev/null 2>&1
+ done
+ exit_with_hooks 1
+fi
+
+exit_with_hooks 0
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 0000000..e4d4f9c
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,13 @@
+AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"'
+AM_CFLAGS = $(LDAP_CFLAGS)
+
+noinst_LIBRARIES = libdhcp.a
+libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c discover.c \
+ dispatch.c dlpi.c dns.c ethernet.c execute.c fddi.c \
+ icmp.c inet.c lpf.c memory.c nit.c ns_name.c options.c \
+ packet.c parse.c print.c raw.c resolv.c socket.c \
+ tables.c tr.c tree.c upf.c
+man_MANS = dhcp-eval.5 dhcp-options.5
+EXTRA_DIST = $(man_MANS)
+
+SUBDIRS = tests
diff --git a/common/Makefile.in b/common/Makefile.in
new file mode 100644
index 0000000..487f629
--- /dev/null
+++ b/common/Makefile.in
@@ -0,0 +1,605 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = common
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libdhcp_a_AR = $(AR) $(ARFLAGS)
+libdhcp_a_LIBADD =
+am_libdhcp_a_OBJECTS = alloc.$(OBJEXT) bpf.$(OBJEXT) comapi.$(OBJEXT) \
+ conflex.$(OBJEXT) ctrace.$(OBJEXT) discover.$(OBJEXT) \
+ dispatch.$(OBJEXT) dlpi.$(OBJEXT) dns.$(OBJEXT) \
+ ethernet.$(OBJEXT) execute.$(OBJEXT) fddi.$(OBJEXT) \
+ icmp.$(OBJEXT) inet.$(OBJEXT) lpf.$(OBJEXT) memory.$(OBJEXT) \
+ nit.$(OBJEXT) ns_name.$(OBJEXT) options.$(OBJEXT) \
+ packet.$(OBJEXT) parse.$(OBJEXT) print.$(OBJEXT) raw.$(OBJEXT) \
+ resolv.$(OBJEXT) socket.$(OBJEXT) tables.$(OBJEXT) \
+ tr.$(OBJEXT) tree.$(OBJEXT) upf.$(OBJEXT)
+libdhcp_a_OBJECTS = $(am_libdhcp_a_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libdhcp_a_SOURCES)
+DIST_SOURCES = $(libdhcp_a_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+man5dir = $(mandir)/man5
+am__installdirs = "$(DESTDIR)$(man5dir)"
+NROFF = nroff
+MANS = $(man_MANS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"'
+AM_CFLAGS = $(LDAP_CFLAGS)
+noinst_LIBRARIES = libdhcp.a
+libdhcp_a_SOURCES = alloc.c bpf.c comapi.c conflex.c ctrace.c discover.c \
+ dispatch.c dlpi.c dns.c ethernet.c execute.c fddi.c \
+ icmp.c inet.c lpf.c memory.c nit.c ns_name.c options.c \
+ packet.c parse.c print.c raw.c resolv.c socket.c \
+ tables.c tr.c tree.c upf.c
+
+man_MANS = dhcp-eval.5 dhcp-options.5
+EXTRA_DIST = $(man_MANS)
+SUBDIRS = tests
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign common/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign common/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libdhcp.a: $(libdhcp_a_OBJECTS) $(libdhcp_a_DEPENDENCIES)
+ -rm -f libdhcp.a
+ $(libdhcp_a_AR) libdhcp.a $(libdhcp_a_OBJECTS) $(libdhcp_a_LIBADD)
+ $(RANLIB) libdhcp.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bpf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/comapi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conflex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctrace.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/discover.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlpi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ethernet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execute.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fddi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icmp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lpf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ns_name.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tables.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upf.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man5: $(man5_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LIBRARIES) $(MANS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(man5dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man: install-man5
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-man
+
+uninstall-man: uninstall-man5
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic \
+ clean-noinstLIBRARIES ctags ctags-recursive distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-man5 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-man uninstall-man5
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/common/alloc.c b/common/alloc.c
new file mode 100644
index 0000000..f2ddc8a
--- /dev/null
+++ b/common/alloc.c
@@ -0,0 +1,1304 @@
+/* alloc.c
+
+ Memory allocation... */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+
+struct dhcp_packet *dhcp_free_list;
+struct packet *packet_free_list;
+
+int option_chain_head_allocate (ptr, file, line)
+ struct option_chain_head **ptr;
+ const char *file;
+ int line;
+{
+ struct option_chain_head *h;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_chain_head *)0;
+#endif
+ }
+
+ h = dmalloc (sizeof *h, file, line);
+ if (h) {
+ memset (h, 0, sizeof *h);
+ return option_chain_head_reference (ptr, h, file, line);
+ }
+ return 0;
+}
+
+int option_chain_head_reference (ptr, bp, file, line)
+ struct option_chain_head **ptr;
+ struct option_chain_head *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_chain_head *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int option_chain_head_dereference (ptr, file, line)
+ struct option_chain_head **ptr;
+ const char *file;
+ int line;
+{
+ struct option_chain_head *option_chain_head;
+ pair car, cdr;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ option_chain_head = *ptr;
+ *ptr = (struct option_chain_head *)0;
+ --option_chain_head -> refcnt;
+ rc_register (file, line, ptr, option_chain_head,
+ option_chain_head -> refcnt, 1, RC_MISC);
+ if (option_chain_head -> refcnt > 0)
+ return 1;
+
+ if (option_chain_head -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (option_chain_head);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ /* If there are any options on this head, free them. */
+ for (car = option_chain_head -> first; car; car = cdr) {
+ cdr = car -> cdr;
+ if (car -> car)
+ option_cache_dereference ((struct option_cache **)
+ (&car -> car), MDL);
+ dfree (car, MDL);
+ car = cdr;
+ }
+
+ dfree (option_chain_head, file, line);
+ return 1;
+}
+
+int group_allocate (ptr, file, line)
+ struct group **ptr;
+ const char *file;
+ int line;
+{
+ struct group *g;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct group *)0;
+#endif
+ }
+
+ g = dmalloc (sizeof *g, file, line);
+ if (g) {
+ memset (g, 0, sizeof *g);
+ return group_reference (ptr, g, file, line);
+ }
+ return 0;
+}
+
+int group_reference (ptr, bp, file, line)
+ struct group **ptr;
+ struct group *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct group *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int group_dereference (ptr, file, line)
+ struct group **ptr;
+ const char *file;
+ int line;
+{
+ struct group *group;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ group = *ptr;
+ *ptr = (struct group *)0;
+ --group -> refcnt;
+ rc_register (file, line, ptr, group, group -> refcnt, 1, RC_MISC);
+ if (group -> refcnt > 0)
+ return 1;
+
+ if (group -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (group);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (group -> object)
+ group_object_dereference (&group -> object, file, line);
+ if (group -> subnet)
+ subnet_dereference (&group -> subnet, file, line);
+ if (group -> shared_network)
+ shared_network_dereference (&group -> shared_network,
+ file, line);
+ if (group -> statements)
+ executable_statement_dereference (&group -> statements,
+ file, line);
+ if (group -> next)
+ group_dereference (&group -> next, file, line);
+ dfree (group, file, line);
+ return 1;
+}
+
+struct dhcp_packet *new_dhcp_packet (file, line)
+ const char *file;
+ int line;
+{
+ struct dhcp_packet *rval;
+ rval = (struct dhcp_packet *)dmalloc (sizeof (struct dhcp_packet),
+ file, line);
+ return rval;
+}
+
+struct protocol *new_protocol (file, line)
+ const char *file;
+ int line;
+{
+ struct protocol *rval = dmalloc (sizeof (struct protocol), file, line);
+ return rval;
+}
+
+struct domain_search_list *new_domain_search_list (file, line)
+ const char *file;
+ int line;
+{
+ struct domain_search_list *rval =
+ dmalloc (sizeof (struct domain_search_list), file, line);
+ return rval;
+}
+
+struct name_server *new_name_server (file, line)
+ const char *file;
+ int line;
+{
+ struct name_server *rval =
+ dmalloc (sizeof (struct name_server), file, line);
+ return rval;
+}
+
+void free_name_server (ptr, file, line)
+ struct name_server *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((void *)ptr, file, line);
+}
+
+struct option *new_option (name, file, line)
+ const char *name;
+ const char *file;
+ int line;
+{
+ struct option *rval;
+ int len;
+
+ len = strlen(name);
+
+ rval = dmalloc(sizeof(struct option) + len + 1, file, line);
+
+ if(rval) {
+ memcpy(rval + 1, name, len);
+ rval->name = (char *)(rval + 1);
+ }
+
+ return rval;
+}
+
+struct universe *new_universe (file, line)
+ const char *file;
+ int line;
+{
+ struct universe *rval =
+ dmalloc (sizeof (struct universe), file, line);
+ return rval;
+}
+
+void free_universe (ptr, file, line)
+ struct universe *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((void *)ptr, file, line);
+}
+
+void free_domain_search_list (ptr, file, line)
+ struct domain_search_list *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((void *)ptr, file, line);
+}
+
+void free_protocol (ptr, file, line)
+ struct protocol *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((void *)ptr, file, line);
+}
+
+void free_dhcp_packet (ptr, file, line)
+ struct dhcp_packet *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((void *)ptr, file, line);
+}
+
+struct client_lease *new_client_lease (file, line)
+ const char *file;
+ int line;
+{
+ return (struct client_lease *)dmalloc (sizeof (struct client_lease),
+ file, line);
+}
+
+void free_client_lease (lease, file, line)
+ struct client_lease *lease;
+ const char *file;
+ int line;
+{
+ dfree (lease, file, line);
+}
+
+pair free_pairs;
+
+pair new_pair (file, line)
+ const char *file;
+ int line;
+{
+ pair foo;
+
+ if (free_pairs) {
+ foo = free_pairs;
+ free_pairs = foo -> cdr;
+ memset (foo, 0, sizeof *foo);
+ dmalloc_reuse (foo, file, line, 0);
+ return foo;
+ }
+
+ foo = dmalloc (sizeof *foo, file, line);
+ if (!foo)
+ return foo;
+ memset (foo, 0, sizeof *foo);
+ return foo;
+}
+
+void free_pair (foo, file, line)
+ pair foo;
+ const char *file;
+ int line;
+{
+ foo -> cdr = free_pairs;
+ free_pairs = foo;
+ dmalloc_reuse (free_pairs, __FILE__, __LINE__, 0);
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_pairs ()
+{
+ pair pf, pc;
+
+ for (pf = free_pairs; pf; pf = pc) {
+ pc = pf -> cdr;
+ dfree (pf, MDL);
+ }
+ free_pairs = (pair)0;
+}
+#endif
+
+struct expression *free_expressions;
+
+int expression_allocate (cptr, file, line)
+ struct expression **cptr;
+ const char *file;
+ int line;
+{
+ struct expression *rval;
+
+ if (free_expressions) {
+ rval = free_expressions;
+ free_expressions = rval -> data.not;
+ dmalloc_reuse (rval, file, line, 1);
+ } else {
+ rval = dmalloc (sizeof (struct expression), file, line);
+ if (!rval)
+ return 0;
+ }
+ memset (rval, 0, sizeof *rval);
+ return expression_reference (cptr, rval, file, line);
+}
+
+int expression_reference (ptr, src, file, line)
+ struct expression **ptr;
+ struct expression *src;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct expression *)0;
+#endif
+ }
+ *ptr = src;
+ src -> refcnt++;
+ rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+void free_expression (expr, file, line)
+ struct expression *expr;
+ const char *file;
+ int line;
+{
+ expr -> data.not = free_expressions;
+ free_expressions = expr;
+ dmalloc_reuse (free_expressions, __FILE__, __LINE__, 0);
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_expressions ()
+{
+ struct expression *e, *n;
+
+ for (e = free_expressions; e; e = n) {
+ n = e -> data.not;
+ dfree (e, MDL);
+ }
+ free_expressions = (struct expression *)0;
+}
+#endif
+
+struct binding_value *free_binding_values;
+
+int binding_value_allocate (cptr, file, line)
+ struct binding_value **cptr;
+ const char *file;
+ int line;
+{
+ struct binding_value *rval;
+
+ if (free_binding_values) {
+ rval = free_binding_values;
+ free_binding_values = rval -> value.bv;
+ dmalloc_reuse (rval, file, line, 1);
+ } else {
+ rval = dmalloc (sizeof (struct binding_value), file, line);
+ if (!rval)
+ return 0;
+ }
+ memset (rval, 0, sizeof *rval);
+ return binding_value_reference (cptr, rval, file, line);
+}
+
+int binding_value_reference (ptr, src, file, line)
+ struct binding_value **ptr;
+ struct binding_value *src;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct binding_value *)0;
+#endif
+ }
+ *ptr = src;
+ src -> refcnt++;
+ rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+void free_binding_value (bv, file, line)
+ struct binding_value *bv;
+ const char *file;
+ int line;
+{
+ bv -> value.bv = free_binding_values;
+ free_binding_values = bv;
+ dmalloc_reuse (free_binding_values, (char *)0, 0, 0);
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_binding_values ()
+{
+ struct binding_value *b, *n;
+
+ for (b = free_binding_values; b; b = n) {
+ n = b -> value.bv;
+ dfree (b, MDL);
+ }
+ free_binding_values = (struct binding_value *)0;
+}
+#endif
+
+int fundef_allocate (cptr, file, line)
+ struct fundef **cptr;
+ const char *file;
+ int line;
+{
+ struct fundef *rval;
+
+ rval = dmalloc (sizeof (struct fundef), file, line);
+ if (!rval)
+ return 0;
+ memset (rval, 0, sizeof *rval);
+ return fundef_reference (cptr, rval, file, line);
+}
+
+int fundef_reference (ptr, src, file, line)
+ struct fundef **ptr;
+ struct fundef *src;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct fundef *)0;
+#endif
+ }
+ *ptr = src;
+ src -> refcnt++;
+ rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+struct option_cache *free_option_caches;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_option_caches ()
+{
+ struct option_cache *o, *n;
+
+ for (o = free_option_caches; o; o = n) {
+ n = (struct option_cache *)(o -> expression);
+ dfree (o, MDL);
+ }
+ free_option_caches = (struct option_cache *)0;
+}
+#endif
+
+int option_cache_allocate (cptr, file, line)
+ struct option_cache **cptr;
+ const char *file;
+ int line;
+{
+ struct option_cache *rval;
+
+ if (free_option_caches) {
+ rval = free_option_caches;
+ free_option_caches =
+ (struct option_cache *)(rval -> expression);
+ dmalloc_reuse (rval, file, line, 0);
+ } else {
+ rval = dmalloc (sizeof (struct option_cache), file, line);
+ if (!rval)
+ return 0;
+ }
+ memset (rval, 0, sizeof *rval);
+ return option_cache_reference (cptr, rval, file, line);
+}
+
+int option_cache_reference (ptr, src, file, line)
+ struct option_cache **ptr;
+ struct option_cache *src;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_cache *)0;
+#endif
+ }
+ *ptr = src;
+ src -> refcnt++;
+ rc_register (file, line, ptr, src, src -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int buffer_allocate (ptr, len, file, line)
+ struct buffer **ptr;
+ unsigned len;
+ const char *file;
+ int line;
+{
+ struct buffer *bp;
+
+ /* XXXSK: should check for bad ptr values, otherwise we
+ leak memory if they are wrong */
+ bp = dmalloc (len + sizeof *bp, file, line);
+ if (!bp)
+ return 0;
+ /* XXXSK: both of these initializations are unnecessary */
+ memset (bp, 0, sizeof *bp);
+ bp -> refcnt = 0;
+ return buffer_reference (ptr, bp, file, line);
+}
+
+int buffer_reference (ptr, bp, file, line)
+ struct buffer **ptr;
+ struct buffer *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct buffer *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int buffer_dereference (ptr, file, line)
+ struct buffer **ptr;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (!*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ (*ptr) -> refcnt--;
+ rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
+ if (!(*ptr) -> refcnt) {
+ dfree ((*ptr), file, line);
+ } else if ((*ptr) -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*ptr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ *ptr = (struct buffer *)0;
+ return 1;
+}
+
+int dns_host_entry_allocate (ptr, hostname, file, line)
+ struct dns_host_entry **ptr;
+ const char *hostname;
+ const char *file;
+ int line;
+{
+ struct dns_host_entry *bp;
+
+ bp = dmalloc (strlen (hostname) + sizeof *bp, file, line);
+ if (!bp)
+ return 0;
+ memset (bp, 0, sizeof *bp);
+ bp -> refcnt = 0;
+ strcpy (bp -> hostname, hostname);
+ return dns_host_entry_reference (ptr, bp, file, line);
+}
+
+int dns_host_entry_reference (ptr, bp, file, line)
+ struct dns_host_entry **ptr;
+ struct dns_host_entry *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct dns_host_entry *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int dns_host_entry_dereference (ptr, file, line)
+ struct dns_host_entry **ptr;
+ const char *file;
+ int line;
+{
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ (*ptr) -> refcnt--;
+ rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
+ if (!(*ptr) -> refcnt)
+ dfree ((*ptr), file, line);
+ if ((*ptr) -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*ptr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ *ptr = (struct dns_host_entry *)0;
+ return 1;
+}
+
+int option_state_allocate (ptr, file, line)
+ struct option_state **ptr;
+ const char *file;
+ int line;
+{
+ unsigned size;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_state *)0;
+#endif
+ }
+
+ size = sizeof **ptr + (universe_count - 1) * sizeof (void *);
+ *ptr = dmalloc (size, file, line);
+ if (*ptr) {
+ memset (*ptr, 0, size);
+ (*ptr) -> universe_count = universe_count;
+ (*ptr) -> refcnt = 1;
+ rc_register (file, line,
+ ptr, *ptr, (*ptr) -> refcnt, 0, RC_MISC);
+ return 1;
+ }
+ return 0;
+}
+
+int option_state_reference (ptr, bp, file, line)
+ struct option_state **ptr;
+ struct option_state *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_state *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int option_state_dereference (ptr, file, line)
+ struct option_state **ptr;
+ const char *file;
+ int line;
+{
+ int i;
+ struct option_state *options;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ options = *ptr;
+ *ptr = (struct option_state *)0;
+ --options -> refcnt;
+ rc_register (file, line, ptr, options, options -> refcnt, 1, RC_MISC);
+ if (options -> refcnt > 0)
+ return 1;
+
+ if (options -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (options);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ /* Loop through the per-universe state. */
+ for (i = 0; i < options -> universe_count; i++)
+ if (options -> universes [i] &&
+ universes [i] -> option_state_dereference)
+ ((*(universes [i] -> option_state_dereference))
+ (universes [i], options, file, line));
+
+ dfree (options, file, line);
+ return 1;
+}
+
+int executable_statement_allocate (ptr, file, line)
+ struct executable_statement **ptr;
+ const char *file;
+ int line;
+{
+ struct executable_statement *bp;
+
+ bp = dmalloc (sizeof *bp, file, line);
+ if (!bp)
+ return 0;
+ memset (bp, 0, sizeof *bp);
+ return executable_statement_reference (ptr, bp, file, line);
+}
+
+int executable_statement_reference (ptr, bp, file, line)
+ struct executable_statement **ptr;
+ struct executable_statement *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct executable_statement *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+static struct packet *free_packets;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_packets ()
+{
+ struct packet *p, *n;
+ for (p = free_packets; p; p = n) {
+ n = (struct packet *)(p -> raw);
+ dfree (p, MDL);
+ }
+ free_packets = (struct packet *)0;
+}
+#endif
+
+int packet_allocate (ptr, file, line)
+ struct packet **ptr;
+ const char *file;
+ int line;
+{
+ struct packet *p;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct packet *)0;
+#endif
+ }
+
+ if (free_packets) {
+ p = free_packets;
+ free_packets = (struct packet *)(p -> raw);
+ dmalloc_reuse (p, file, line, 1);
+ } else {
+ p = dmalloc (sizeof *p, file, line);
+ }
+ if (p) {
+ memset (p, 0, sizeof *p);
+ return packet_reference (ptr, p, file, line);
+ }
+ return 0;
+}
+
+int packet_reference (ptr, bp, file, line)
+ struct packet **ptr;
+ struct packet *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct packet *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int packet_dereference (ptr, file, line)
+ struct packet **ptr;
+ const char *file;
+ int line;
+{
+ int i;
+ struct packet *packet;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ packet = *ptr;
+ *ptr = (struct packet *)0;
+ --packet -> refcnt;
+ rc_register (file, line, ptr, packet, packet -> refcnt, 1, RC_MISC);
+ if (packet -> refcnt > 0)
+ return 1;
+
+ if (packet -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (packet);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (packet -> options)
+ option_state_dereference (&packet -> options, file, line);
+ if (packet -> interface)
+ interface_dereference (&packet -> interface, MDL);
+ if (packet -> shared_network)
+ shared_network_dereference (&packet -> shared_network, MDL);
+ for (i = 0; i < packet -> class_count && i < PACKET_MAX_CLASSES; i++) {
+ if (packet -> classes [i])
+ omapi_object_dereference ((omapi_object_t **)
+ &packet -> classes [i], MDL);
+ }
+ packet -> raw = (struct dhcp_packet *)free_packets;
+ free_packets = packet;
+ dmalloc_reuse (free_packets, __FILE__, __LINE__, 0);
+ return 1;
+}
+
+int dns_zone_allocate (ptr, file, line)
+ struct dns_zone **ptr;
+ const char *file;
+ int line;
+{
+ struct dns_zone *d;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct dns_zone *)0;
+#endif
+ }
+
+ d = dmalloc (sizeof *d, file, line);
+ if (d) {
+ memset (d, 0, sizeof *d);
+ return dns_zone_reference (ptr, d, file, line);
+ }
+ return 0;
+}
+
+int dns_zone_reference (ptr, bp, file, line)
+ struct dns_zone **ptr;
+ struct dns_zone *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct dns_zone *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+int binding_scope_allocate (ptr, file, line)
+ struct binding_scope **ptr;
+ const char *file;
+ int line;
+{
+ struct binding_scope *bp;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ bp = dmalloc (sizeof *bp, file, line);
+ if (!bp)
+ return 0;
+ memset (bp, 0, sizeof *bp);
+ binding_scope_reference (ptr, bp, file, line);
+ return 1;
+}
+
+int binding_scope_reference (ptr, bp, file, line)
+ struct binding_scope **ptr;
+ struct binding_scope *bp;
+ const char *file;
+ int line;
+{
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (*ptr) {
+ log_error ("%s(%d): non-null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct binding_scope *)0;
+#endif
+ }
+ *ptr = bp;
+ bp -> refcnt++;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 0, RC_MISC);
+ return 1;
+}
+
+/* Make a copy of the data in data_string, upping the buffer reference
+ count if there's a buffer. */
+
+void
+data_string_copy(struct data_string *dest, const struct data_string *src,
+ const char *file, int line)
+{
+ if (src -> buffer) {
+ buffer_reference (&dest -> buffer, src -> buffer, file, line);
+ } else {
+ dest->buffer = NULL;
+ }
+ dest -> data = src -> data;
+ dest -> terminated = src -> terminated;
+ dest -> len = src -> len;
+}
+
+/* Release the reference count to a data string's buffer (if any) and
+ zero out the other information, yielding the null data string. */
+
+void data_string_forget (data, file, line)
+ struct data_string *data;
+ const char *file;
+ int line;
+{
+ if (data -> buffer)
+ buffer_dereference (&data -> buffer, file, line);
+ memset (data, 0, sizeof *data);
+}
+
+/* If the data_string is larger than the specified length, reduce
+ the data_string to the specified size. */
+
+void data_string_truncate (dp, len)
+ struct data_string *dp;
+ int len;
+{
+ /* XXX: do we need to consider the "terminated" flag in the check? */
+ if (len < dp -> len) {
+ dp -> terminated = 0;
+ dp -> len = len;
+ }
+}
diff --git a/common/bpf.c b/common/bpf.c
new file mode 100644
index 0000000..b0ef657
--- /dev/null
+++ b/common/bpf.c
@@ -0,0 +1,609 @@
+/* bpf.c
+
+ BPF socket interface code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software was contributed to Internet Systems Consortium
+ * by Archie Cobbs.
+ *
+ * Patches for FDDI support on Digital Unix were written by Bill
+ * Stapleton, and maintained for a while by Mike Meredith before he
+ * managed to get me to integrate them.
+ */
+
+#include "dhcpd.h"
+#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE) \
+ || defined (USE_LPF_RECEIVE)
+# if defined (USE_LPF_RECEIVE)
+# include <asm/types.h>
+# include <linux/filter.h>
+# define bpf_insn sock_filter /* Linux: dare to be gratuitously different. */
+# else
+# include <sys/ioctl.h>
+# include <sys/uio.h>
+# include <net/bpf.h>
+# include <net/if_types.h>
+# if defined (NEED_OSF_PFILT_HACKS)
+# include <net/pfilt.h>
+# endif
+# endif
+
+#include <netinet/in_systm.h>
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+#endif
+
+#ifdef USE_BPF_RECEIVE
+#include <ifaddrs.h>
+#endif
+
+#include <errno.h>
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#ifdef USE_BPF_SEND
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+#ifdef USE_BPF_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+/* 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. */
+
+#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
+int if_register_bpf (info)
+ struct interface_info *info;
+{
+ int sock;
+ char filename[50];
+ int b;
+
+ /* Open a BPF device */
+ for (b = 0; 1; b++) {
+ /* %Audit% 31 bytes max. %2004.06.17,Safe% */
+ sprintf(filename, BPF_FORMAT, b);
+ sock = open (filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY) {
+ continue;
+ } else {
+ if (!b)
+ log_fatal ("No bpf devices.%s%s%s",
+ " Please read the README",
+ " section for your operating",
+ " system.");
+ log_fatal ("Can't find free bpf: %m");
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* Set the BPF device to point at this interface. */
+ if (ioctl (sock, BIOCSETIF, info -> ifp) < 0)
+ log_fatal ("Can't attach interface %s to bpf device %s: %m",
+ info -> name, filename);
+
+ get_hw_addr(info->name, &info->hw_address);
+
+ return sock;
+}
+#endif /* USE_BPF_SEND || USE_BPF_RECEIVE */
+
+#ifdef USE_BPF_SEND
+void if_register_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the bpf API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_BPF_RECEIVE
+ info -> wfdesc = if_register_bpf (info, interface);
+#else
+ info -> wfdesc = info -> rfdesc;
+#endif
+ if (!quiet_interface_discovery)
+ log_info ("Sending on BPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the bpf API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_BPF_RECEIVE
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on BPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_BPF_SEND */
+
+#if defined (USE_BPF_RECEIVE) || defined (USE_LPF_RECEIVE)
+/* 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! XXX */
+
+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),
+};
+
+#if defined (DEC_FDDI)
+struct bpf_insn *bpf_fddi_filter;
+#endif
+
+int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
+#if defined (HAVE_TR_SUPPORT)
+struct bpf_insn dhcp_bpf_tr_filter [] = {
+ /* accept all token ring packets due to variable length header */
+ /* if we want to get clever, insert the program here */
+
+ /* 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_tr_filter_len = (sizeof dhcp_bpf_tr_filter /
+ sizeof (struct bpf_insn));
+#endif /* HAVE_TR_SUPPORT */
+#endif /* USE_LPF_RECEIVE || USE_BPF_RECEIVE */
+
+#if defined (USE_BPF_RECEIVE)
+void if_register_receive (info)
+ struct interface_info *info;
+{
+ int flag = 1;
+ struct bpf_version v;
+ struct bpf_program p;
+#ifdef NEED_OSF_PFILT_HACKS
+ u_int32_t bits;
+#endif
+#ifdef DEC_FDDI
+ int link_layer;
+#endif /* DEC_FDDI */
+
+ /* 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)
+ log_fatal ("Can't get BPF version: %m");
+
+ if (v.bv_major != BPF_MAJOR_VERSION ||
+ v.bv_minor < BPF_MINOR_VERSION)
+ log_fatal ("BPF version mismatch - recompile DHCP!");
+
+ /* 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)
+ log_fatal ("Can't set immediate mode on bpf device: %m");
+
+#ifdef NEED_OSF_PFILT_HACKS
+ /* Allow the copyall flag to be set... */
+ if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0)
+ log_fatal ("Can't set ALLOWCOPYALL: %m");
+
+ /* Clear all the packet filter mode bits first... */
+ bits = 0;
+ if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
+ log_fatal ("Can't clear pfilt bits: %m");
+
+ /* Set the ENBATCH, ENCOPYALL, ENBPFHDR bits... */
+ bits = ENBATCH | ENCOPYALL | ENBPFHDR;
+ if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
+ log_fatal ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m");
+#endif
+ /* Get the required BPF buffer length from the kernel. */
+ if (ioctl (info -> rfdesc, BIOCGBLEN, &info -> rbuf_max) < 0)
+ log_fatal ("Can't get bpf buffer length: %m");
+ info -> rbuf = dmalloc (info -> rbuf_max, MDL);
+ if (!info -> rbuf)
+ log_fatal ("Can't allocate %ld bytes for bpf input buffer.",
+ (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;
+
+#ifdef DEC_FDDI
+ /* See if this is an FDDI interface, flag it for later. */
+ if (ioctl(info -> rfdesc, BIOCGDLT, &link_layer) >= 0 &&
+ link_layer == DLT_FDDI) {
+ if (!bpf_fddi_filter) {
+ bpf_fddi_filter = dmalloc (sizeof bpf_fddi_filter,
+ MDL);
+ if (!bpf_fddi_filter)
+ log_fatal ("No memory for FDDI filter.");
+ memcpy (bpf_fddi_filter,
+ dhcp_bpf_filter, sizeof dhcp_bpf_filter);
+ /* Patch the BPF program to account for the difference
+ in length between ethernet headers (14), FDDI and
+ 802.2 headers (16 +8=24, +10).
+ XXX changes to filter program may require changes to
+ XXX the insn number(s) used below! */
+ bpf_fddi_filter[0].k += 10;
+ bpf_fddi_filter[2].k += 10;
+ bpf_fddi_filter[4].k += 10;
+ bpf_fddi_filter[6].k += 10;
+ bpf_fddi_filter[7].k += 10;
+ }
+ p.bf_insns = bpf_fddi_filter;
+ } else
+#endif /* DEC_FDDI */
+ 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! XXX */
+ dhcp_bpf_filter [8].k = ntohs (local_port);
+
+ if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0)
+ log_fatal ("Can't install packet filter program: %m");
+ if (!quiet_interface_discovery)
+ log_info ("Listening on BPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+ close (info -> rfdesc);
+ info -> rfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on BPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_BPF_RECEIVE */
+
+#ifdef USE_BPF_SEND
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ unsigned hbufp = 0, ibufp = 0;
+ double hw [4];
+ double ip [32];
+ struct iovec iov [3];
+ int result;
+
+ if (!strcmp (interface -> name, "fallback"))
+ return send_fallback (interface, packet, raw,
+ len, from, to, hto);
+
+ if (hto == NULL && interface->anycast_mac_addr.hlen)
+ hto = &interface->anycast_mac_addr;
+
+ /* Assemble the headers... */
+ assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto);
+ assemble_udp_ip_header (interface,
+ (unsigned char *)ip, &ibufp, from.s_addr,
+ to -> sin_addr.s_addr, to -> sin_port,
+ (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov [0].iov_base = ((char *)hw);
+ iov [0].iov_len = hbufp;
+ iov [1].iov_base = ((char *)ip);
+ iov [1].iov_len = ibufp;
+ iov [2].iov_base = (char *)raw;
+ iov [2].iov_len = len;
+
+ result = writev(interface -> wfdesc, iov, 3);
+ if (result < 0)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_BPF_SEND */
+
+#ifdef USE_BPF_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+ int length = 0;
+ int offset = 0;
+ struct bpf_hdr hdr;
+ unsigned paylen;
+
+ /* 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,
+ (size_t)interface -> rbuf_max);
+ if (length <= 0) {
+#ifdef __FreeBSD__
+ if (errno == ENXIO) {
+#else
+ if (errno == EIO) {
+#endif
+ dhcp_interface_remove
+ ((omapi_object_t *)interface,
+ (omapi_object_t *)0);
+ }
+ return length;
+ }
+ interface -> rbuf_offset = 0;
+ interface -> rbuf_len = BPF_WORDALIGN (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 =
+ BPF_WORDALIGN (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,
+ 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 =
+ BPF_WORDALIGN (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,
+ interface -> rbuf,
+ interface -> rbuf_offset,
+ from, hdr.bh_caplen, &paylen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+ interface -> rbuf_offset = 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 =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
+ continue;
+ }
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, interface->rbuf + interface->rbuf_offset, paylen);
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
+ return paylen;
+ } while (!length);
+ return 0;
+}
+
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+void maybe_setup_fallback ()
+{
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ if_register_fallback (fbi);
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+}
+
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+ struct ifaddrs *ifa;
+ struct ifaddrs *p;
+ struct sockaddr_dl *sa;
+
+ if (getifaddrs(&ifa) != 0) {
+ log_fatal("Error getting interface information; %m");
+ }
+
+ /*
+ * Loop through our interfaces finding a match.
+ */
+ sa = NULL;
+ for (p=ifa; (p != NULL) && (sa == NULL); p = p->ifa_next) {
+ if ((p->ifa_addr->sa_family == AF_LINK) &&
+ !strcmp(p->ifa_name, name)) {
+ sa = (struct sockaddr_dl *)p->ifa_addr;
+ }
+ }
+ if (sa == NULL) {
+ log_fatal("No interface called '%s'", name);
+ }
+
+ /*
+ * Pull out the appropriate information.
+ */
+ switch (sa->sdl_type) {
+ case IFT_ETHER:
+ hw->hlen = sa->sdl_alen + 1;
+ hw->hbuf[0] = HTYPE_ETHER;
+ memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen);
+ break;
+ case IFT_ISO88023:
+ case IFT_ISO88024: /* "token ring" */
+ case IFT_ISO88025:
+ case IFT_ISO88026:
+ hw->hlen = sa->sdl_alen + 1;
+ hw->hbuf[0] = HTYPE_IEEE802;
+ memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen);
+ break;
+#ifdef IFT_FDDI
+ case IFT_FDDI:
+ hw->hlen = sa->sdl_alen + 1;
+ hw->hbuf[0] = HTYPE_FDDI;
+ memcpy(&hw->hbuf[1], LLADDR(sa), sa->sdl_alen);
+ break;
+#endif /* IFT_FDDI */
+ default:
+ log_fatal("Unsupported device type %d for \"%s\"",
+ sa->sdl_type, name);
+ }
+
+ freeifaddrs(ifa);
+}
+#endif
diff --git a/common/comapi.c b/common/comapi.c
new file mode 100644
index 0000000..c24b4a6
--- /dev/null
+++ b/common/comapi.c
@@ -0,0 +1,922 @@
+/* omapi.c
+
+ OMAPI object interfaces for the DHCP server. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* Many, many thanks to Brian Murrell and BCtel for this code - BCtel
+ provided the funding that resulted in this code and the entire
+ OMAPI support library being written, and Brian helped brainstorm
+ and refine the requirements. To the extent that this code is
+ useful, you have Brian and BCtel to thank. Any limitations in the
+ code are a result of mistakes on my part. -- Ted Lemon */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+
+OMAPI_OBJECT_ALLOC (subnet, struct subnet, dhcp_type_subnet)
+OMAPI_OBJECT_ALLOC (shared_network, struct shared_network,
+ dhcp_type_shared_network)
+OMAPI_OBJECT_ALLOC (group_object, struct group_object, dhcp_type_group)
+OMAPI_OBJECT_ALLOC (dhcp_control, dhcp_control_object_t, dhcp_type_control)
+
+omapi_object_type_t *dhcp_type_interface;
+omapi_object_type_t *dhcp_type_group;
+omapi_object_type_t *dhcp_type_shared_network;
+omapi_object_type_t *dhcp_type_subnet;
+omapi_object_type_t *dhcp_type_control;
+dhcp_control_object_t *dhcp_control_object;
+
+void dhcp_common_objects_setup ()
+{
+ isc_result_t status;
+
+ status = omapi_object_type_register (&dhcp_type_control,
+ "control",
+ dhcp_control_set_value,
+ dhcp_control_get_value,
+ dhcp_control_destroy,
+ dhcp_control_signal_handler,
+ dhcp_control_stuff_values,
+ dhcp_control_lookup,
+ dhcp_control_create,
+ dhcp_control_remove, 0, 0, 0,
+ sizeof (dhcp_control_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register control object type: %s",
+ isc_result_totext (status));
+ status = dhcp_control_allocate (&dhcp_control_object, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't make initial control object: %s",
+ isc_result_totext (status));
+ dhcp_control_object -> state = server_startup;
+
+ status = omapi_object_type_register (&dhcp_type_group,
+ "group",
+ dhcp_group_set_value,
+ dhcp_group_get_value,
+ dhcp_group_destroy,
+ dhcp_group_signal_handler,
+ dhcp_group_stuff_values,
+ dhcp_group_lookup,
+ dhcp_group_create,
+ dhcp_group_remove, 0, 0, 0,
+ sizeof (struct group_object), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register group object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_subnet,
+ "subnet",
+ dhcp_subnet_set_value,
+ dhcp_subnet_get_value,
+ dhcp_subnet_destroy,
+ dhcp_subnet_signal_handler,
+ dhcp_subnet_stuff_values,
+ dhcp_subnet_lookup,
+ dhcp_subnet_create,
+ dhcp_subnet_remove, 0, 0, 0,
+ sizeof (struct subnet), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register subnet object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register
+ (&dhcp_type_shared_network,
+ "shared-network",
+ dhcp_shared_network_set_value,
+ dhcp_shared_network_get_value,
+ dhcp_shared_network_destroy,
+ dhcp_shared_network_signal_handler,
+ dhcp_shared_network_stuff_values,
+ dhcp_shared_network_lookup,
+ dhcp_shared_network_create,
+ dhcp_shared_network_remove, 0, 0, 0,
+ sizeof (struct shared_network), 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register shared network object type: %s",
+ isc_result_totext (status));
+
+ interface_setup ();
+}
+
+isc_result_t dhcp_group_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct group_object *group;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)h;
+
+ /* XXX For now, we can only set these values on new group objects.
+ XXX Soon, we need to be able to update group objects. */
+ if (!omapi_ds_strcmp (name, "name")) {
+ if (group -> name)
+ return ISC_R_EXISTS;
+ if (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string) {
+ group -> name = dmalloc (value -> u.buffer.len + 1,
+ MDL);
+ if (!group -> name)
+ return ISC_R_NOMEMORY;
+ memcpy (group -> name,
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ group -> name [value -> u.buffer.len] = 0;
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "statements")) {
+ if (group -> group && group -> group -> statements)
+ return ISC_R_EXISTS;
+ if (!group -> group) {
+ if (!clone_group (&group -> group, root_group, MDL))
+ return ISC_R_NOMEMORY;
+ }
+ if (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string) {
+ struct parse *parse;
+ int lose = 0;
+ parse = NULL;
+ status = new_parse(&parse, -1,
+ (char *) value->u.buffer.value,
+ value->u.buffer.len,
+ "network client", 0);
+ if (status != ISC_R_SUCCESS || parse == NULL)
+ return status;
+ if (!(parse_executable_statements
+ (&group -> group -> statements, parse, &lose,
+ context_any))) {
+ end_parse (&parse);
+ return DHCP_R_BADPARSE;
+ }
+ end_parse (&parse);
+ return ISC_R_SUCCESS;
+ } else
+ return DHCP_R_INVALIDARG;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_group_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct group_object *group;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)h;
+
+ if (!omapi_ds_strcmp (name, "name"))
+ return omapi_make_string_value (value,
+ name, group -> name, MDL);
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_group_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct group_object *group, *t;
+
+ if (h -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)h;
+
+ if (group -> name) {
+ if (group_name_hash) {
+ t = (struct group_object *)0;
+ if (group_hash_lookup (&t, group_name_hash,
+ group -> name,
+ strlen (group -> name), MDL)) {
+ group_hash_delete (group_name_hash,
+ group -> name,
+ strlen (group -> name),
+ MDL);
+ group_object_dereference (&t, MDL);
+ }
+ }
+ dfree (group -> name, file, line);
+ group -> name = (char *)0;
+ }
+ if (group -> group)
+ group_dereference (&group -> group, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_group_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct group_object *group;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)h;
+
+ if (!strcmp (name, "updated")) {
+ /* A group object isn't valid if a subgroup hasn't yet been
+ associated with it. */
+ if (!group -> group)
+ return DHCP_R_INVALIDARG;
+
+ /* Group objects always have to have names. */
+ if (!group -> name) {
+ char hnbuf [64];
+ sprintf (hnbuf, "ng%08lx%08lx",
+ (unsigned long)cur_time,
+ (unsigned long)group);
+ group -> name = dmalloc (strlen (hnbuf) + 1, MDL);
+ if (!group -> name)
+ return ISC_R_NOMEMORY;
+ strcpy (group -> name, hnbuf);
+ }
+
+ supersede_group (group, 1);
+ updatep = 1;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ if (updatep)
+ return ISC_R_SUCCESS;
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_group_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct group_object *group;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)h;
+
+ /* Write out all the values. */
+ if (group -> name) {
+ status = omapi_connection_put_name (c, "name");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_string (c, group -> name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_group_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct group_object *group;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ /* First see if we were sent a handle. */
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (lp, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*lp) -> type != dhcp_type_group) {
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ }
+
+ /* Now look for a name. */
+ status = omapi_get_value_str (ref, id, "name", &tv);
+ if (status == ISC_R_SUCCESS) {
+ group = (struct group_object *)0;
+ if (group_name_hash &&
+ group_hash_lookup (&group, group_name_hash,
+ (const char *)
+ tv -> value -> u.buffer.value,
+ tv -> value -> u.buffer.len, MDL)) {
+ omapi_value_dereference (&tv, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)group) {
+ group_object_dereference (&group, MDL);
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)group,
+ MDL);
+ group_object_dereference (&group, MDL);
+ }
+ } else if (!*lp)
+ return ISC_R_NOTFOUND;
+ }
+
+ /* If we get to here without finding a group, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+
+ if (((struct group_object *)(*lp)) -> flags & GROUP_OBJECT_DELETED) {
+ omapi_object_dereference (lp, MDL);
+ return ISC_R_NOTFOUND;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_group_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ struct group_object *group;
+ isc_result_t status;
+ group = (struct group_object *)0;
+
+ status = group_object_allocate (&group, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ group -> flags = GROUP_OBJECT_DYNAMIC;
+ status = omapi_object_reference (lp, (omapi_object_t *)group, MDL);
+ group_object_dereference (&group, MDL);
+ return status;
+}
+
+isc_result_t dhcp_group_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ struct group_object *group;
+ isc_result_t status;
+ if (lp -> type != dhcp_type_group)
+ return DHCP_R_INVALIDARG;
+ group = (struct group_object *)lp;
+
+ group -> flags |= GROUP_OBJECT_DELETED;
+ if (group_write_hook) {
+ if (!(*group_write_hook) (group))
+ return ISC_R_IOERROR;
+ }
+
+ status = dhcp_group_destroy ((omapi_object_t *)group, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_control_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ dhcp_control_object_t *control;
+ isc_result_t status;
+ unsigned long newstate;
+
+ if (h -> type != dhcp_type_control)
+ return DHCP_R_INVALIDARG;
+ control = (dhcp_control_object_t *)h;
+
+ if (!omapi_ds_strcmp (name, "state")) {
+ status = omapi_get_int_value (&newstate, value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = dhcp_set_control_state (control -> state, newstate);
+ if (status == ISC_R_SUCCESS)
+ control -> state = value -> u.integer;
+ return status;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_control_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ dhcp_control_object_t *control;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_control)
+ return DHCP_R_INVALIDARG;
+ control = (dhcp_control_object_t *)h;
+
+ if (!omapi_ds_strcmp (name, "state"))
+ return omapi_make_int_value (value,
+ name, (int)control -> state, MDL);
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_control_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ if (h -> type != dhcp_type_control)
+ return DHCP_R_INVALIDARG;
+
+ /* Can't destroy the control object. */
+ return ISC_R_NOPERM;
+}
+
+isc_result_t dhcp_control_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ dhcp_control_object_t *control;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_control)
+ return DHCP_R_INVALIDARG;
+ control = (dhcp_control_object_t *)h;
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_control_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ dhcp_control_object_t *control;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_control)
+ return DHCP_R_INVALIDARG;
+ control = (dhcp_control_object_t *)h;
+
+ /* Write out all the values. */
+ status = omapi_connection_put_name (c, "state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, control -> state);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_control_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+
+ /* First see if we were sent a handle. */
+ if (ref) {
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (lp, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*lp) -> type != dhcp_type_control) {
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ }
+ }
+
+ /* Otherwise, stop playing coy - there's only one control object,
+ so we can just return it. */
+ dhcp_control_reference ((dhcp_control_object_t **)lp,
+ dhcp_control_object, MDL);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_control_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ /* Can't create a control object - there can be only one. */
+ return ISC_R_NOPERM;
+}
+
+isc_result_t dhcp_control_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ /* Form is emptiness; emptiness form. The control object
+ cannot go out of existance. */
+ return ISC_R_NOPERM;
+}
+
+isc_result_t dhcp_subnet_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct subnet *subnet;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_subnet)
+ return DHCP_R_INVALIDARG;
+ subnet = (struct subnet *)h;
+
+ /* No values to set yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_subnet_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct subnet *subnet;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_subnet)
+ return DHCP_R_INVALIDARG;
+ subnet = (struct subnet *)h;
+
+ /* No values to get yet. */
+
+ /* Try to find some inner object that can provide the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_subnet_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct subnet *subnet;
+
+ if (h -> type != dhcp_type_subnet)
+ return DHCP_R_INVALIDARG;
+ subnet = (struct subnet *)h;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ if (subnet -> next_subnet)
+ subnet_dereference (&subnet -> next_subnet, file, line);
+ if (subnet -> next_sibling)
+ subnet_dereference (&subnet -> next_sibling, file, line);
+ if (subnet -> shared_network)
+ shared_network_dereference (&subnet -> shared_network,
+ file, line);
+ if (subnet -> interface)
+ interface_dereference (&subnet -> interface, file, line);
+ if (subnet -> group)
+ group_dereference (&subnet -> group, file, line);
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_subnet_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct subnet *subnet;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_subnet)
+ return DHCP_R_INVALIDARG;
+ subnet = (struct subnet *)h;
+
+ /* Can't write subnets yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ if (updatep)
+ return ISC_R_SUCCESS;
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_subnet_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct subnet *subnet;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_subnet)
+ return DHCP_R_INVALIDARG;
+ subnet = (struct subnet *)h;
+
+ /* Can't stuff subnet values yet. */
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_subnet_lookup (omapi_object_t **lp,
+ omapi_object_t *id,
+ omapi_object_t *ref)
+{
+ /* Can't look up subnets yet. */
+
+ /* If we get to here without finding a subnet, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_subnet_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_subnet_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_shared_network_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct shared_network *shared_network;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_shared_network)
+ return DHCP_R_INVALIDARG;
+ shared_network = (struct shared_network *)h;
+
+ /* No values to set yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_shared_network_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct shared_network *shared_network;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_shared_network)
+ return DHCP_R_INVALIDARG;
+ shared_network = (struct shared_network *)h;
+
+ /* No values to get yet. */
+
+ /* Try to find some inner object that can provide the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_shared_network_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ struct shared_network *shared_network;
+
+ if (h -> type != dhcp_type_shared_network)
+ return DHCP_R_INVALIDARG;
+ shared_network = (struct shared_network *)h;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ if (shared_network -> next)
+ shared_network_dereference (&shared_network -> next,
+ file, line);
+ if (shared_network -> name) {
+ dfree (shared_network -> name, file, line);
+ shared_network -> name = 0;
+ }
+ if (shared_network -> subnets)
+ subnet_dereference (&shared_network -> subnets, file, line);
+ if (shared_network -> interface)
+ interface_dereference (&shared_network -> interface,
+ file, line);
+ if (shared_network -> pools)
+ omapi_object_dereference ((omapi_object_t **)
+ &shared_network -> pools, file, line);
+ if (shared_network -> group)
+ group_dereference (&shared_network -> group, file, line);
+#if defined (FAILOVER_PROTOCOL)
+ if (shared_network -> failover_peer)
+ omapi_object_dereference ((omapi_object_t **)
+ &shared_network -> failover_peer,
+ file, line);
+#endif
+#endif /* DEBUG_MEMORY_LEAKAGE */
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *h,
+ const char *name,
+ va_list ap)
+{
+ struct shared_network *shared_network;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_shared_network)
+ return DHCP_R_INVALIDARG;
+ shared_network = (struct shared_network *)h;
+
+ /* Can't write shared_networks yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ if (updatep)
+ return ISC_R_SUCCESS;
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct shared_network *shared_network;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_shared_network)
+ return DHCP_R_INVALIDARG;
+ shared_network = (struct shared_network *)h;
+
+ /* Can't stuff shared_network values yet. */
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_shared_network_lookup (omapi_object_t **lp,
+ omapi_object_t *id,
+ omapi_object_t *ref)
+{
+ /* Can't look up shared_networks yet. */
+
+ /* If we get to here without finding a shared_network, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_shared_network_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_shared_network_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
diff --git a/common/conflex.c b/common/conflex.c
new file mode 100644
index 0000000..1ec2328
--- /dev/null
+++ b/common/conflex.c
@@ -0,0 +1,1523 @@
+/* conflex.c
+
+ Lexical scanner for dhcpd config file... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <ctype.h>
+
+static int get_char (struct parse *);
+static void unget_char(struct parse *, int);
+static void skip_to_eol (struct parse *);
+static enum dhcp_token read_whitespace(int c, struct parse *cfile);
+static enum dhcp_token read_string (struct parse *);
+static enum dhcp_token read_number (int, struct parse *);
+static enum dhcp_token read_num_or_name (int, struct parse *);
+static enum dhcp_token intern (char *, enum dhcp_token);
+
+isc_result_t new_parse (cfile, file, inbuf, buflen, name, eolp)
+ struct parse **cfile;
+ int file;
+ char *inbuf;
+ unsigned buflen;
+ const char *name;
+ int eolp;
+{
+ isc_result_t status = ISC_R_SUCCESS;
+ struct parse *tmp;
+
+ tmp = dmalloc(sizeof(struct parse), MDL);
+ if (tmp == NULL) {
+ return (ISC_R_NOMEMORY);
+ }
+
+ /*
+ * We don't need to initialize things to zero here, since
+ * dmalloc() returns memory that is set to zero.
+ */
+ tmp->tlname = name;
+ tmp->lpos = tmp -> line = 1;
+ tmp->cur_line = tmp->line1;
+ tmp->prev_line = tmp->line2;
+ tmp->token_line = tmp->cur_line;
+ tmp->cur_line[0] = tmp->prev_line[0] = 0;
+ tmp->file = file;
+ tmp->eol_token = eolp;
+
+ if (inbuf != NULL) {
+ tmp->inbuf = inbuf;
+ tmp->buflen = buflen;
+ tmp->bufsiz = 0;
+ } else {
+ struct stat sb;
+
+ if (fstat(file, &sb) < 0) {
+ status = ISC_R_IOERROR;
+ goto cleanup;
+ }
+
+ if (sb.st_size == 0)
+ goto cleanup;
+
+ tmp->bufsiz = tmp->buflen = (size_t) sb.st_size;
+ tmp->inbuf = mmap(NULL, tmp->bufsiz, PROT_READ, MAP_SHARED,
+ file, 0);
+
+ if (tmp->inbuf == MAP_FAILED) {
+ status = ISC_R_IOERROR;
+ goto cleanup;
+ }
+ }
+
+ *cfile = tmp;
+ return (ISC_R_SUCCESS);
+
+cleanup:
+ dfree(tmp, MDL);
+ return (status);
+}
+
+isc_result_t end_parse (cfile)
+ struct parse **cfile;
+{
+ /* "Memory" config files have no file. */
+ if ((*cfile)->file != -1) {
+ munmap((*cfile)->inbuf, (*cfile)->bufsiz);
+ close((*cfile)->file);
+ }
+
+ if ((*cfile)->saved_state != NULL) {
+ dfree((*cfile)->saved_state, MDL);
+ }
+
+ dfree(*cfile, MDL);
+ *cfile = NULL;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Save the current state of the parser.
+ *
+ * Only one state may be saved. Any previous saved state is
+ * lost.
+ */
+isc_result_t
+save_parse_state(struct parse *cfile) {
+ /*
+ * Free any previous saved state.
+ */
+ if (cfile->saved_state != NULL) {
+ dfree(cfile->saved_state, MDL);
+ }
+
+ /*
+ * Save our current state.
+ */
+ cfile->saved_state = dmalloc(sizeof(struct parse), MDL);
+ if (cfile->saved_state == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+ memcpy(cfile->saved_state, cfile, sizeof(*cfile));
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Return the parser to the previous saved state.
+ *
+ * You must call save_parse_state() before calling
+ * restore_parse_state(), but you can call restore_parse_state() any
+ * number of times after that.
+ */
+isc_result_t
+restore_parse_state(struct parse *cfile) {
+ struct parse *saved_state;
+
+ if (cfile->saved_state == NULL) {
+ return DHCP_R_NOTYET;
+ }
+
+ saved_state = cfile->saved_state;
+ memcpy(cfile, saved_state, sizeof(*cfile));
+ cfile->saved_state = saved_state;
+ return ISC_R_SUCCESS;
+}
+
+static int get_char (cfile)
+ struct parse *cfile;
+{
+ /* My kingdom for WITH... */
+ int c;
+
+ if (cfile->bufix == cfile->buflen) {
+#if !defined(LDAP_CONFIGURATION)
+ c = EOF;
+#else /* defined(LDAP_CONFIGURATION) */
+ if (cfile->read_function != NULL)
+ c = cfile->read_function(cfile);
+ else
+ c = EOF;
+#endif
+ } else {
+ c = cfile->inbuf [cfile->bufix];
+ cfile->bufix++;
+ }
+
+ if (!cfile->ugflag) {
+ if (c == EOL) {
+ if (cfile->cur_line == cfile->line1) {
+ cfile->cur_line = cfile->line2;
+ cfile->prev_line = cfile->line1;
+ } else {
+ cfile->cur_line = cfile->line1;
+ cfile->prev_line = cfile->line2;
+ }
+ cfile->line++;
+ cfile->lpos = 1;
+ cfile->cur_line [0] = 0;
+ } else if (c != EOF) {
+ if (cfile->lpos <= 80) {
+ cfile->cur_line [cfile->lpos - 1] = c;
+ cfile->cur_line [cfile->lpos] = 0;
+ }
+ cfile->lpos++;
+ }
+ } else
+ cfile->ugflag = 0;
+ return c;
+}
+
+/*
+ * Return a character to our input buffer.
+ */
+static void
+unget_char(struct parse *cfile, int c) {
+ if (c != EOF) {
+ cfile->bufix--;
+ cfile->ugflag = 1; /* do not put characters into
+ our error buffer on the next
+ call to get_char() */
+ }
+}
+
+/*
+ * GENERAL NOTE ABOUT TOKENS
+ *
+ * We normally only want non-whitespace tokens. There are some
+ * circumstances where we *do* want to see whitespace (for example
+ * when parsing IPv6 addresses).
+ *
+ * Generally we use the next_token() function to read tokens. This
+ * in turn calls get_next_token, which does *not* return tokens for
+ * whitespace. Rather, it skips these.
+ *
+ * When we need to see whitespace, we us next_raw_token(), which also
+ * returns the WHITESPACE token.
+ *
+ * The peek_token() and peek_raw_token() functions work as expected.
+ *
+ * Warning: if you invoke peek_token(), then if there is a whitespace
+ * token, it will be lost, and subsequent use of next_raw_token() or
+ * peek_raw_token() will NOT see it.
+ */
+
+static enum dhcp_token
+get_raw_token(struct parse *cfile) {
+ int c;
+ enum dhcp_token ttok;
+ static char tb [2];
+ int l, p;
+
+ do {
+ l = cfile -> line;
+ p = cfile -> lpos;
+
+ c = get_char (cfile);
+ if (!((c == '\n') && cfile->eol_token) &&
+ isascii(c) && isspace(c)) {
+ ttok = read_whitespace(c, cfile);
+ break;
+ }
+ if (c == '#') {
+ skip_to_eol (cfile);
+ continue;
+ }
+ if (c == '"') {
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
+ ttok = read_string (cfile);
+ break;
+ }
+ if ((isascii (c) && isdigit (c)) || c == '-') {
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
+ ttok = read_number (c, cfile);
+ break;
+ } else if (isascii (c) && isalpha (c)) {
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
+ ttok = read_num_or_name (c, cfile);
+ break;
+ } else if (c == EOF) {
+ ttok = END_OF_FILE;
+ cfile -> tlen = 0;
+ break;
+ } else {
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
+ tb [0] = c;
+ tb [1] = 0;
+ cfile -> tval = tb;
+ cfile -> tlen = 1;
+ ttok = c;
+ break;
+ }
+ } while (1);
+ return ttok;
+}
+
+/*
+ * The get_next_token() function consumes the next token and
+ * returns it to the caller.
+ *
+ * Since the code is almost the same for "normal" and "raw"
+ * input, we pass a flag to alter the way it works.
+ */
+
+static enum dhcp_token
+get_next_token(const char **rval, unsigned *rlen,
+ struct parse *cfile, isc_boolean_t raw) {
+ int rv;
+
+ if (cfile -> token) {
+ if (cfile -> lexline != cfile -> tline)
+ cfile -> token_line = cfile -> cur_line;
+ cfile -> lexchar = cfile -> tlpos;
+ cfile -> lexline = cfile -> tline;
+ rv = cfile -> token;
+ cfile -> token = 0;
+ } else {
+ rv = get_raw_token(cfile);
+ cfile -> token_line = cfile -> cur_line;
+ }
+
+ if (!raw) {
+ while (rv == WHITESPACE) {
+ rv = get_raw_token(cfile);
+ cfile->token_line = cfile->cur_line;
+ }
+ }
+
+ if (rval)
+ *rval = cfile -> tval;
+ if (rlen)
+ *rlen = cfile -> tlen;
+#ifdef DEBUG_TOKENS
+ fprintf (stderr, "%s:%d ", cfile -> tval, rv);
+#endif
+ return rv;
+}
+
+
+/*
+ * Get the next token from cfile and return it.
+ *
+ * If rval is non-NULL, set the pointer it contains to
+ * the contents of the token.
+ *
+ * If rlen is non-NULL, set the integer it contains to
+ * the length of the token.
+ */
+
+enum dhcp_token
+next_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return get_next_token(rval, rlen, cfile, ISC_FALSE);
+}
+
+
+/*
+ * The same as the next_token() function above, but will return space
+ * as the WHITESPACE token.
+ */
+
+enum dhcp_token
+next_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return get_next_token(rval, rlen, cfile, ISC_TRUE);
+}
+
+
+/*
+ * The do_peek_token() function checks the next token without
+ * consuming it, and returns it to the caller.
+ *
+ * Since the code is almost the same for "normal" and "raw"
+ * input, we pass a flag to alter the way it works. (See the
+ * warning in the GENERAL NOTES ABOUT TOKENS above though.)
+ */
+
+enum dhcp_token
+do_peek_token(const char **rval, unsigned int *rlen,
+ struct parse *cfile, isc_boolean_t raw) {
+ int x;
+
+ if (!cfile->token || (!raw && (cfile->token == WHITESPACE))) {
+ cfile -> tlpos = cfile -> lexchar;
+ cfile -> tline = cfile -> lexline;
+
+ do {
+ cfile->token = get_raw_token(cfile);
+ } while (!raw && (cfile->token == WHITESPACE));
+
+ if (cfile -> lexline != cfile -> tline)
+ cfile -> token_line = cfile -> prev_line;
+
+ x = cfile -> lexchar;
+ cfile -> lexchar = cfile -> tlpos;
+ cfile -> tlpos = x;
+
+ x = cfile -> lexline;
+ cfile -> lexline = cfile -> tline;
+ cfile -> tline = x;
+ }
+ if (rval)
+ *rval = cfile -> tval;
+ if (rlen)
+ *rlen = cfile -> tlen;
+#ifdef DEBUG_TOKENS
+ fprintf (stderr, "(%s:%d) ", cfile -> tval, cfile -> token);
+#endif
+ return cfile -> token;
+}
+
+
+/*
+ * Get the next token from cfile and return it, leaving it for a
+ * subsequent call to next_token().
+ *
+ * Note that it WILL consume whitespace tokens.
+ *
+ * If rval is non-NULL, set the pointer it contains to
+ * the contents of the token.
+ *
+ * If rlen is non-NULL, set the integer it contains to
+ * the length of the token.
+ */
+
+enum dhcp_token
+peek_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return do_peek_token(rval, rlen, cfile, ISC_FALSE);
+}
+
+
+/*
+ * The same as the peek_token() function above, but will return space
+ * as the WHITESPACE token.
+ */
+
+enum dhcp_token
+peek_raw_token(const char **rval, unsigned *rlen, struct parse *cfile) {
+ return do_peek_token(rval, rlen, cfile, ISC_TRUE);
+}
+
+static void skip_to_eol (cfile)
+ struct parse *cfile;
+{
+ int c;
+ do {
+ c = get_char (cfile);
+ if (c == EOF)
+ return;
+ if (c == EOL) {
+ return;
+ }
+ } while (1);
+}
+
+static enum dhcp_token
+read_whitespace(int c, struct parse *cfile) {
+ int ofs;
+
+ /*
+ * Read as much whitespace as we have available.
+ */
+ ofs = 0;
+ do {
+ cfile->tokbuf[ofs++] = c;
+ c = get_char(cfile);
+ } while (!((c == '\n') && cfile->eol_token) &&
+ isascii(c) && isspace(c));
+
+ /*
+ * Put the last (non-whitespace) character back.
+ */
+ unget_char(cfile, c);
+
+ /*
+ * Return our token.
+ */
+ cfile->tokbuf[ofs] = '\0';
+ cfile->tlen = ofs;
+ cfile->tval = cfile->tokbuf;
+ return WHITESPACE;
+}
+
+static enum dhcp_token read_string (cfile)
+ struct parse *cfile;
+{
+ int i;
+ int bs = 0;
+ int c;
+ int value = 0;
+ int hex = 0;
+
+ for (i = 0; i < sizeof cfile -> tokbuf; i++) {
+ again:
+ c = get_char (cfile);
+ if (c == EOF) {
+ parse_warn (cfile, "eof in string constant");
+ break;
+ }
+ if (bs == 1) {
+ switch (c) {
+ case 't':
+ cfile -> tokbuf [i] = '\t';
+ break;
+ case 'r':
+ cfile -> tokbuf [i] = '\r';
+ break;
+ case 'n':
+ cfile -> tokbuf [i] = '\n';
+ break;
+ case 'b':
+ cfile -> tokbuf [i] = '\b';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ hex = 0;
+ value = c - '0';
+ ++bs;
+ goto again;
+ case 'x':
+ hex = 1;
+ value = 0;
+ ++bs;
+ goto again;
+ default:
+ cfile -> tokbuf [i] = c;
+ bs = 0;
+ break;
+ }
+ bs = 0;
+ } else if (bs > 1) {
+ if (hex) {
+ if (c >= '0' && c <= '9') {
+ value = value * 16 + (c - '0');
+ } else if (c >= 'a' && c <= 'f') {
+ value = value * 16 + (c - 'a' + 10);
+ } else if (c >= 'A' && c <= 'F') {
+ value = value * 16 + (c - 'A' + 10);
+ } else {
+ parse_warn (cfile,
+ "invalid hex digit: %x",
+ c);
+ bs = 0;
+ continue;
+ }
+ if (++bs == 4) {
+ cfile -> tokbuf [i] = value;
+ bs = 0;
+ } else
+ goto again;
+ } else {
+ if (c >= '0' && c <= '7') {
+ value = value * 8 + (c - '0');
+ } else {
+ if (value != 0) {
+ parse_warn (cfile,
+ "invalid octal digit %x",
+ c);
+ continue;
+ } else
+ cfile -> tokbuf [i] = 0;
+ bs = 0;
+ }
+ if (++bs == 4) {
+ cfile -> tokbuf [i] = value;
+ bs = 0;
+ } else
+ goto again;
+ }
+ } else if (c == '\\') {
+ bs = 1;
+ goto again;
+ } else if (c == '"')
+ break;
+ else
+ cfile -> 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 cfile -> tokbuf) {
+ parse_warn (cfile,
+ "string constant larger than internal buffer");
+ --i;
+ }
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
+ return STRING;
+}
+
+static enum dhcp_token read_number (c, cfile)
+ int c;
+ struct parse *cfile;
+{
+ int i = 0;
+ int token = NUMBER;
+
+ cfile -> tokbuf [i++] = c;
+ for (; i < sizeof cfile -> tokbuf; i++) {
+ c = get_char (cfile);
+
+ /* Promote NUMBER -> NUMBER_OR_NAME -> NAME, never demote.
+ * Except in the case of '0x' syntax hex, which gets called
+ * a NAME at '0x', and returned to NUMBER_OR_NAME once it's
+ * verified to be at least 0xf or less.
+ */
+ switch(isascii(c) ? token : BREAK) {
+ case NUMBER:
+ if(isdigit(c))
+ break;
+ /* FALLTHROUGH */
+ case NUMBER_OR_NAME:
+ if(isxdigit(c)) {
+ token = NUMBER_OR_NAME;
+ break;
+ }
+ /* FALLTHROUGH */
+ case NAME:
+ if((i == 2) && isxdigit(c) &&
+ (cfile->tokbuf[0] == '0') &&
+ ((cfile->tokbuf[1] == 'x') ||
+ (cfile->tokbuf[1] == 'X'))) {
+ token = NUMBER_OR_NAME;
+ break;
+ } else if(((c == '-') || (c == '_') || isalnum(c))) {
+ token = NAME;
+ break;
+ }
+ /* FALLTHROUGH */
+ case BREAK:
+ /* At this point c is either EOF or part of the next
+ * token. If not EOF, rewind the file one byte so
+ * the next token is read from there.
+ */
+ unget_char(cfile, c);
+ goto end_read;
+
+ default:
+ log_fatal("read_number():%s:%d: impossible case", MDL);
+ }
+
+ cfile -> tokbuf [i] = c;
+ }
+
+ if (i == sizeof cfile -> tokbuf) {
+ parse_warn (cfile,
+ "numeric token larger than internal buffer");
+ --i;
+ }
+
+ end_read:
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
+
+ /*
+ * If this entire token from start to finish was "-", such as
+ * the middle parameter in "42 - 7", return just the MINUS token.
+ */
+ if ((i == 1) && (cfile->tokbuf[i] == '-'))
+ return MINUS;
+ else
+ return token;
+}
+
+static enum dhcp_token read_num_or_name (c, cfile)
+ int c;
+ struct parse *cfile;
+{
+ int i = 0;
+ enum dhcp_token rv = NUMBER_OR_NAME;
+ cfile -> tokbuf [i++] = c;
+ for (; i < sizeof cfile -> tokbuf; i++) {
+ c = get_char (cfile);
+ if (!isascii (c) ||
+ (c != '-' && c != '_' && !isalnum (c))) {
+ unget_char(cfile, c);
+ break;
+ }
+ if (!isxdigit (c))
+ rv = NAME;
+ cfile -> tokbuf [i] = c;
+ }
+ if (i == sizeof cfile -> tokbuf) {
+ parse_warn (cfile, "token larger than internal buffer");
+ --i;
+ }
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
+ return intern(cfile->tval, rv);
+}
+
+static enum dhcp_token
+intern(char *atom, enum dhcp_token dfv) {
+ if (!isascii(atom[0]))
+ return dfv;
+
+ switch (tolower((unsigned char)atom[0])) {
+ case '-':
+ if (atom [1] == 0)
+ return MINUS;
+ break;
+
+ case 'a':
+ if (!strcasecmp(atom + 1, "bandoned"))
+ return TOKEN_ABANDONED;
+ if (!strcasecmp(atom + 1, "ctive"))
+ return TOKEN_ACTIVE;
+ if (!strncasecmp(atom + 1, "dd", 2)) {
+ if (atom[3] == '\0')
+ return TOKEN_ADD;
+ else if (!strcasecmp(atom + 3, "ress"))
+ return ADDRESS;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "fter"))
+ return AFTER;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'l')) {
+ if (!strcasecmp(atom + 2, "gorithm"))
+ return ALGORITHM;
+ if (!strcasecmp(atom + 2, "ias"))
+ return ALIAS;
+ if (isascii(atom[2]) &&
+ (tolower((unsigned char)atom[2]) == 'l')) {
+ if (atom[3] == '\0')
+ return ALL;
+ else if (!strcasecmp(atom + 3, "ow"))
+ return ALLOW;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "so"))
+ return TOKEN_ALSO;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'n')) {
+ if (!strcasecmp(atom + 2, "d"))
+ return AND;
+ if (!strcasecmp(atom + 2, "ycast-mac"))
+ return ANYCAST_MAC;
+ break;
+ }
+ if (!strcasecmp(atom + 1, "ppend"))
+ return APPEND;
+ if (!strcasecmp(atom + 1, "rray"))
+ return ARRAY;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 't')) {
+ if (atom[2] == '\0')
+ return AT;
+ if (!strcasecmp(atom + 2, "sfp"))
+ return ATSFP;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ut", 2)) {
+ if (isascii(atom[3]) &&
+ (tolower((unsigned char)atom[3]) == 'h')) {
+ if (!strncasecmp(atom + 4, "enticat", 7)) {
+ if (!strcasecmp(atom + 11, "ed"))
+ return AUTHENTICATED;
+ if (!strcasecmp(atom + 11, "ion"))
+ return AUTHENTICATION;
+ break;
+ }
+ if (!strcasecmp(atom + 4, "oritative"))
+ return AUTHORITATIVE;
+ break;
+ }
+ if (!strcasecmp(atom + 3, "o-partner-down"))
+ return AUTO_PARTNER_DOWN;
+ break;
+ }
+ break;
+ case 'b':
+ if (!strcasecmp (atom + 1, "ackup"))
+ return TOKEN_BACKUP;
+ if (!strcasecmp (atom + 1, "ootp"))
+ return TOKEN_BOOTP;
+ if (!strcasecmp (atom + 1, "inding"))
+ return BINDING;
+ if (!strcasecmp (atom + 1, "inary-to-ascii"))
+ return BINARY_TO_ASCII;
+ if (!strcasecmp (atom + 1, "ackoff-cutoff"))
+ return BACKOFF_CUTOFF;
+ if (!strcasecmp (atom + 1, "ooting"))
+ return BOOTING;
+ if (!strcasecmp (atom + 1, "oot-unknown-clients"))
+ return BOOT_UNKNOWN_CLIENTS;
+ if (!strcasecmp (atom + 1, "reak"))
+ return BREAK;
+ if (!strcasecmp (atom + 1, "illing"))
+ return BILLING;
+ if (!strcasecmp (atom + 1, "oolean"))
+ return BOOLEAN;
+ if (!strcasecmp (atom + 1, "alance"))
+ return BALANCE;
+ if (!strcasecmp (atom + 1, "ound"))
+ return BOUND;
+ break;
+ case 'c':
+ if (!strcasecmp(atom + 1, "ase"))
+ return CASE;
+ if (!strcasecmp(atom + 1, "heck"))
+ return CHECK;
+ if (!strcasecmp(atom + 1, "iaddr"))
+ return CIADDR;
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'l') {
+ if (!strcasecmp(atom + 2, "ass"))
+ return CLASS;
+ if (!strncasecmp(atom + 2, "ient", 4)) {
+ if (!strcasecmp(atom + 6, "s"))
+ return CLIENTS;
+ if (atom[6] == '-') {
+ if (!strcasecmp(atom + 7, "hostname"))
+ return CLIENT_HOSTNAME;
+ if (!strcasecmp(atom + 7, "identifier"))
+ return CLIENT_IDENTIFIER;
+ if (!strcasecmp(atom + 7, "state"))
+ return CLIENT_STATE;
+ if (!strcasecmp(atom + 7, "updates"))
+ return CLIENT_UPDATES;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 2, "ose"))
+ return TOKEN_CLOSE;
+ if (!strcasecmp(atom + 2, "tt"))
+ return CLTT;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'o') {
+ if (!strcasecmp(atom + 2, "de"))
+ return CODE;
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'm') {
+ if (!strcasecmp(atom + 3, "mit"))
+ return COMMIT;
+ if (!strcasecmp(atom + 3,
+ "munications-interrupted"))
+ return COMMUNICATIONS_INTERRUPTED;
+ if (!strcasecmp(atom + 3, "pressed"))
+ return COMPRESSED;
+ break;
+ }
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'n') {
+ if (!strcasecmp(atom + 3, "cat"))
+ return CONCAT;
+ if (!strcasecmp(atom + 3, "fig-option"))
+ return CONFIG_OPTION;
+ if (!strcasecmp(atom + 3, "flict-done"))
+ return CONFLICT_DONE;
+ if (!strcasecmp(atom + 3, "nect"))
+ return CONNECT;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 1, "reate"))
+ return TOKEN_CREATE;
+ break;
+ case 'd':
+ if (!strcasecmp(atom + 1, "b-time-format"))
+ return DB_TIME_FORMAT;
+ if (!strcasecmp (atom + 1, "ns-update"))
+ return DNS_UPDATE;
+ if (!strcasecmp (atom + 1, "ns-delete"))
+ return DNS_DELETE;
+ if (!strcasecmp (atom + 1, "omain"))
+ return DOMAIN;
+ if (!strncasecmp (atom + 1, "omain-", 6)) {
+ if (!strcasecmp(atom + 7, "name"))
+ return DOMAIN_NAME;
+ if (!strcasecmp(atom + 7, "list"))
+ return DOMAIN_LIST;
+ }
+ if (!strcasecmp (atom + 1, "o-forward-update"))
+ return DO_FORWARD_UPDATE;
+ if (!strcasecmp (atom + 1, "ebug"))
+ return TOKEN_DEBUG;
+ if (!strcasecmp (atom + 1, "eny"))
+ return DENY;
+ if (!strcasecmp (atom + 1, "eleted"))
+ return TOKEN_DELETED;
+ if (!strcasecmp (atom + 1, "elete"))
+ return TOKEN_DELETE;
+ if (!strncasecmp (atom + 1, "efault", 6)) {
+ if (!atom [7])
+ return DEFAULT;
+ if (!strcasecmp(atom + 7, "-duid"))
+ return DEFAULT_DUID;
+ if (!strcasecmp (atom + 7, "-lease-time"))
+ return DEFAULT_LEASE_TIME;
+ break;
+ }
+ if (!strncasecmp (atom + 1, "ynamic", 6)) {
+ if (!atom [7])
+ return DYNAMIC;
+ if (!strncasecmp (atom + 7, "-bootp", 6)) {
+ 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;
+ }
+ }
+ if (!strcasecmp (atom + 1, "uplicates"))
+ return DUPLICATES;
+ if (!strcasecmp (atom + 1, "eclines"))
+ return DECLINES;
+ if (!strncasecmp (atom + 1, "efine", 5)) {
+ if (!strcasecmp (atom + 6, "d"))
+ return DEFINED;
+ if (!atom [6])
+ return DEFINE;
+ }
+ break;
+ case 'e':
+ if (isascii (atom [1]) &&
+ tolower((unsigned char)atom[1]) == 'x') {
+ if (!strcasecmp (atom + 2, "tract-int"))
+ return EXTRACT_INT;
+ if (!strcasecmp (atom + 2, "ists"))
+ return EXISTS;
+ if (!strcasecmp (atom + 2, "piry"))
+ return EXPIRY;
+ if (!strcasecmp (atom + 2, "pire"))
+ return EXPIRE;
+ if (!strcasecmp (atom + 2, "pired"))
+ return TOKEN_EXPIRED;
+ }
+ if (!strcasecmp (atom + 1, "ncode-int"))
+ return ENCODE_INT;
+ if (!strcasecmp(atom + 1, "poch"))
+ return EPOCH;
+ if (!strcasecmp (atom + 1, "thernet"))
+ return ETHERNET;
+ if (!strcasecmp (atom + 1, "nds"))
+ return ENDS;
+ if (!strncasecmp (atom + 1, "ls", 2)) {
+ if (!strcasecmp (atom + 3, "e"))
+ return ELSE;
+ if (!strcasecmp (atom + 3, "if"))
+ return ELSIF;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "rror"))
+ return ERROR;
+ if (!strcasecmp (atom + 1, "val"))
+ return EVAL;
+ if (!strcasecmp (atom + 1, "ncapsulate"))
+ return ENCAPSULATE;
+ if (!strcasecmp(atom + 1, "xecute"))
+ return EXECUTE;
+ if (!strcasecmp(atom+1, "n")) {
+ return EN;
+ }
+ break;
+ case 'f':
+ if (!strcasecmp (atom + 1, "atal"))
+ return FATAL;
+ if (!strcasecmp (atom + 1, "ilename"))
+ return FILENAME;
+ if (!strcasecmp (atom + 1, "ixed-address"))
+ return FIXED_ADDR;
+ if (!strcasecmp (atom + 1, "ixed-address6"))
+ return FIXED_ADDR6;
+ if (!strcasecmp (atom + 1, "ixed-prefix6"))
+ return FIXED_PREFIX6;
+ if (!strcasecmp (atom + 1, "ddi"))
+ return TOKEN_FDDI;
+ if (!strcasecmp (atom + 1, "ormerr"))
+ return NS_FORMERR;
+ if (!strcasecmp (atom + 1, "unction"))
+ return FUNCTION;
+ if (!strcasecmp (atom + 1, "ailover"))
+ return FAILOVER;
+ if (!strcasecmp (atom + 1, "ree"))
+ return TOKEN_FREE;
+ break;
+ case 'g':
+ if (!strncasecmp(atom + 1, "et", 2)) {
+ if (!strcasecmp(atom + 3, "-lease-hostnames"))
+ return GET_LEASE_HOSTNAMES;
+ if (!strcasecmp(atom + 3, "hostbyname"))
+ return GETHOSTBYNAME;
+ if (!strcasecmp(atom + 3, "hostname"))
+ return GETHOSTNAME;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return GIADDR;
+ if (!strcasecmp (atom + 1, "roup"))
+ return GROUP;
+ break;
+ case 'h':
+ if (!strcasecmp(atom + 1, "ash"))
+ return HASH;
+ if (!strcasecmp (atom + 1, "ba"))
+ return HBA;
+ if (!strcasecmp (atom + 1, "ost"))
+ return HOST;
+ if (!strcasecmp (atom + 1, "ost-decl-name"))
+ return HOST_DECL_NAME;
+ if (!strcasecmp(atom + 1, "ost-identifier"))
+ return HOST_IDENTIFIER;
+ if (!strcasecmp (atom + 1, "ardware"))
+ return HARDWARE;
+ if (!strcasecmp (atom + 1, "ostname"))
+ return HOSTNAME;
+ if (!strcasecmp (atom + 1, "elp"))
+ return TOKEN_HELP;
+ break;
+ case 'i':
+ if (!strcasecmp(atom+1, "a-na"))
+ return IA_NA;
+ if (!strcasecmp(atom+1, "a-ta"))
+ return IA_TA;
+ if (!strcasecmp(atom+1, "a-pd"))
+ return IA_PD;
+ if (!strcasecmp(atom+1, "aaddr"))
+ return IAADDR;
+ if (!strcasecmp(atom+1, "aprefix"))
+ return IAPREFIX;
+ if (!strcasecmp (atom + 1, "nclude"))
+ return INCLUDE;
+ if (!strcasecmp (atom + 1, "nteger"))
+ return INTEGER;
+ if (!strcasecmp (atom + 1, "nfinite"))
+ return INFINITE;
+ if (!strcasecmp (atom + 1, "nfo"))
+ return INFO;
+ if (!strcasecmp (atom + 1, "p-address"))
+ return IP_ADDRESS;
+ if (!strcasecmp (atom + 1, "p6-address"))
+ return IP6_ADDRESS;
+ if (!strcasecmp (atom + 1, "nitial-interval"))
+ return INITIAL_INTERVAL;
+ if (!strcasecmp (atom + 1, "nitial-delay"))
+ return INITIAL_DELAY;
+ if (!strcasecmp (atom + 1, "nterface"))
+ return INTERFACE;
+ if (!strcasecmp (atom + 1, "dentifier"))
+ return IDENTIFIER;
+ if (!strcasecmp (atom + 1, "f"))
+ return IF;
+ if (!strcasecmp (atom + 1, "s"))
+ return IS;
+ if (!strcasecmp (atom + 1, "gnore"))
+ return IGNORE;
+ break;
+ case 'k':
+ if (!strncasecmp (atom + 1, "nown", 4)) {
+ if (!strcasecmp (atom + 5, "-clients"))
+ return KNOWN_CLIENTS;
+ if (!atom[5])
+ return KNOWN;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "ey"))
+ return KEY;
+ break;
+ case 'l':
+ if (!strcasecmp (atom + 1, "case"))
+ return LCASE;
+ if (!strcasecmp (atom + 1, "ease"))
+ return LEASE;
+ if (!strcasecmp(atom + 1, "ease6"))
+ return LEASE6;
+ if (!strcasecmp (atom + 1, "eased-address"))
+ return LEASED_ADDRESS;
+ if (!strcasecmp (atom + 1, "ease-time"))
+ return LEASE_TIME;
+ if (!strcasecmp(atom + 1, "easequery"))
+ return LEASEQUERY;
+ if (!strcasecmp(atom + 1, "ength"))
+ return LENGTH;
+ if (!strcasecmp (atom + 1, "imit"))
+ return LIMIT;
+ if (!strcasecmp (atom + 1, "et"))
+ return LET;
+ if (!strcasecmp (atom + 1, "oad"))
+ return LOAD;
+ if (!strcasecmp(atom + 1, "ocal"))
+ return LOCAL;
+ if (!strcasecmp (atom + 1, "og"))
+ return LOG;
+ if (!strcasecmp(atom+1, "lt")) {
+ return LLT;
+ }
+ if (!strcasecmp(atom+1, "l")) {
+ return LL;
+ }
+ break;
+ case 'm':
+ if (!strncasecmp (atom + 1, "ax", 2)) {
+ if (!atom [3])
+ return TOKEN_MAX;
+ if (!strcasecmp (atom + 3, "-balance"))
+ return MAX_BALANCE;
+ if (!strncasecmp (atom + 3, "-lease-", 7)) {
+ if (!strcasecmp(atom + 10, "misbalance"))
+ return MAX_LEASE_MISBALANCE;
+ if (!strcasecmp(atom + 10, "ownership"))
+ return MAX_LEASE_OWNERSHIP;
+ if (!strcasecmp(atom + 10, "time"))
+ return MAX_LEASE_TIME;
+ }
+ if (!strcasecmp(atom + 3, "-life"))
+ return MAX_LIFE;
+ if (!strcasecmp (atom + 3, "-transmit-idle"))
+ return MAX_TRANSMIT_IDLE;
+ if (!strcasecmp (atom + 3, "-response-delay"))
+ return MAX_RESPONSE_DELAY;
+ if (!strcasecmp (atom + 3, "-unacked-updates"))
+ return MAX_UNACKED_UPDATES;
+ }
+ if (!strncasecmp (atom + 1, "in-", 3)) {
+ if (!strcasecmp (atom + 4, "balance"))
+ return MIN_BALANCE;
+ if (!strcasecmp (atom + 4, "lease-time"))
+ return MIN_LEASE_TIME;
+ if (!strcasecmp (atom + 4, "secs"))
+ return MIN_SECS;
+ break;
+ }
+ if (!strncasecmp (atom + 1, "edi", 3)) {
+ if (!strcasecmp (atom + 4, "a"))
+ return MEDIA;
+ if (!strcasecmp (atom + 4, "um"))
+ return MEDIUM;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "atch"))
+ return MATCH;
+ if (!strcasecmp (atom + 1, "embers"))
+ return MEMBERS;
+ if (!strcasecmp (atom + 1, "y"))
+ return MY;
+ if (!strcasecmp (atom + 1, "clt"))
+ return MCLT;
+ break;
+ case 'n':
+ if (!strcasecmp (atom + 1, "ormal"))
+ return NORMAL;
+ if (!strcasecmp (atom + 1, "ameserver"))
+ return NAMESERVER;
+ if (!strcasecmp (atom + 1, "etmask"))
+ return NETMASK;
+ if (!strcasecmp (atom + 1, "ever"))
+ return NEVER;
+ if (!strcasecmp (atom + 1, "ext-server"))
+ return NEXT_SERVER;
+ if (!strcasecmp (atom + 1, "ot"))
+ return TOKEN_NOT;
+ if (!strcasecmp (atom + 1, "o"))
+ return TOKEN_NO;
+ if (!strcasecmp (atom + 1, "s-update"))
+ return NS_UPDATE;
+ if (!strcasecmp (atom + 1, "oerror"))
+ return NS_NOERROR;
+ if (!strcasecmp (atom + 1, "otauth"))
+ return NS_NOTAUTH;
+ if (!strcasecmp (atom + 1, "otimp"))
+ return NS_NOTIMP;
+ if (!strcasecmp (atom + 1, "otzone"))
+ return NS_NOTZONE;
+ if (!strcasecmp (atom + 1, "xdomain"))
+ return NS_NXDOMAIN;
+ if (!strcasecmp (atom + 1, "xrrset"))
+ return NS_NXRRSET;
+ if (!strcasecmp (atom + 1, "ull"))
+ return TOKEN_NULL;
+ if (!strcasecmp (atom + 1, "ext"))
+ return TOKEN_NEXT;
+ if (!strcasecmp (atom + 1, "ew"))
+ return TOKEN_NEW;
+ break;
+ case 'o':
+ if (!strcasecmp (atom + 1, "mapi"))
+ return OMAPI;
+ if (!strcasecmp (atom + 1, "r"))
+ return OR;
+ if (!strcasecmp (atom + 1, "n"))
+ return ON;
+ if (!strcasecmp (atom + 1, "pen"))
+ return TOKEN_OPEN;
+ if (!strcasecmp (atom + 1, "ption"))
+ return OPTION;
+ if (!strcasecmp (atom + 1, "ne-lease-per-client"))
+ return ONE_LEASE_PER_CLIENT;
+ if (!strcasecmp (atom + 1, "f"))
+ return OF;
+ if (!strcasecmp (atom + 1, "wner"))
+ return OWNER;
+ break;
+ case 'p':
+ if (!strcasecmp (atom + 1, "repend"))
+ return PREPEND;
+ if (!strcasecmp(atom + 1, "referred-life"))
+ return PREFERRED_LIFE;
+ if (!strcasecmp (atom + 1, "acket"))
+ return PACKET;
+ if (!strcasecmp (atom + 1, "ool"))
+ return POOL;
+ if (!strcasecmp (atom + 1, "refix6"))
+ return PREFIX6;
+ if (!strcasecmp (atom + 1, "seudo"))
+ return PSEUDO;
+ if (!strcasecmp (atom + 1, "eer"))
+ return PEER;
+ if (!strcasecmp (atom + 1, "rimary"))
+ return PRIMARY;
+ if (!strncasecmp (atom + 1, "artner", 6)) {
+ if (!atom [7])
+ return PARTNER;
+ if (!strcasecmp (atom + 7, "-down"))
+ return PARTNER_DOWN;
+ }
+ if (!strcasecmp (atom + 1, "ort"))
+ return PORT;
+ if (!strcasecmp (atom + 1, "otential-conflict"))
+ return POTENTIAL_CONFLICT;
+ if (!strcasecmp (atom + 1, "ick-first-value") ||
+ !strcasecmp (atom + 1, "ick"))
+ return PICK;
+ if (!strcasecmp (atom + 1, "aused"))
+ return PAUSED;
+ break;
+ case 'r':
+ if (!strcasecmp(atom + 1, "ange"))
+ return RANGE;
+ if (!strcasecmp(atom + 1, "ange6"))
+ return RANGE6;
+ if (isascii(atom[1]) &&
+ (tolower((unsigned char)atom[1]) == 'e')) {
+ if (!strcasecmp(atom + 2, "bind"))
+ return REBIND;
+ if (!strcasecmp(atom + 2, "boot"))
+ return REBOOT;
+ if (!strcasecmp(atom + 2, "contact-interval"))
+ return RECONTACT_INTERVAL;
+ if (!strncasecmp(atom + 2, "cover", 5)) {
+ if (atom[7] == '\0')
+ return RECOVER;
+ if (!strcasecmp(atom + 7, "-done"))
+ return RECOVER_DONE;
+ if (!strcasecmp(atom + 7, "-wait"))
+ return RECOVER_WAIT;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "fresh"))
+ return REFRESH;
+ if (!strcasecmp(atom + 2, "fused"))
+ return NS_REFUSED;
+ if (!strcasecmp(atom + 2, "ject"))
+ return REJECT;
+ if (!strcasecmp(atom + 2, "lease"))
+ return RELEASE;
+ if (!strcasecmp(atom + 2, "leased"))
+ return TOKEN_RELEASED;
+ if (!strcasecmp(atom + 2, "move"))
+ return REMOVE;
+ if (!strcasecmp(atom + 2, "new"))
+ return RENEW;
+ if (!strcasecmp(atom + 2, "quest"))
+ return REQUEST;
+ if (!strcasecmp(atom + 2, "quire"))
+ return REQUIRE;
+ if (isascii(atom[2]) &&
+ (tolower((unsigned char)atom[2]) == 's')) {
+ if (!strcasecmp(atom + 3, "erved"))
+ return TOKEN_RESERVED;
+ if (!strcasecmp(atom + 3, "et"))
+ return TOKEN_RESET;
+ if (!strcasecmp(atom + 3,
+ "olution-interrupted"))
+ return RESOLUTION_INTERRUPTED;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "try"))
+ return RETRY;
+ if (!strcasecmp(atom + 2, "turn"))
+ return RETURN;
+ if (!strcasecmp(atom + 2, "verse"))
+ return REVERSE;
+ if (!strcasecmp(atom + 2, "wind"))
+ return REWIND;
+ break;
+ }
+ break;
+ case 's':
+ if (!strcasecmp(atom + 1, "cript"))
+ return SCRIPT;
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'e') {
+ if (!strcasecmp(atom + 2, "arch"))
+ return SEARCH;
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'c') {
+ if (!strncasecmp(atom + 3, "ond", 3)) {
+ if (!strcasecmp(atom + 6, "ary"))
+ return SECONDARY;
+ if (!strcasecmp(atom + 6, "s"))
+ return SECONDS;
+ break;
+ }
+ if (!strcasecmp(atom + 3, "ret"))
+ return SECRET;
+ break;
+ }
+ if (!strncasecmp(atom + 2, "lect", 4)) {
+ if (atom[6] == '\0')
+ return SELECT;
+ if (!strcasecmp(atom + 6, "-timeout"))
+ return SELECT_TIMEOUT;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "nd"))
+ return SEND;
+ if (!strncasecmp(atom + 2, "rv", 2)) {
+ if (!strncasecmp(atom + 4, "er", 2)) {
+ if (atom[6] == '\0')
+ return TOKEN_SERVER;
+ if (atom[6] == '-') {
+ if (!strcasecmp(atom + 7,
+ "duid"))
+ return SERVER_DUID;
+ if (!strcasecmp(atom + 7,
+ "name"))
+ return SERVER_NAME;
+ if (!strcasecmp(atom + 7,
+ "identifier"))
+ return SERVER_IDENTIFIER;
+ break;
+ }
+ break;
+ }
+ if (!strcasecmp(atom + 4, "fail"))
+ return NS_SERVFAIL;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "t"))
+ return TOKEN_SET;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'h') {
+ if (!strcasecmp(atom + 2, "ared-network"))
+ return SHARED_NETWORK;
+ if (!strcasecmp(atom + 2, "utdown"))
+ return SHUTDOWN;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'i') {
+ if (!strcasecmp(atom + 2, "addr"))
+ return SIADDR;
+ if (!strcasecmp(atom + 2, "gned"))
+ return SIGNED;
+ if (!strcasecmp(atom + 2, "ze"))
+ return SIZE;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'p') {
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'a') {
+ if (!strcasecmp(atom + 3, "ce"))
+ return SPACE;
+ if (!strcasecmp(atom + 3, "wn"))
+ return SPAWN;
+ break;
+ }
+ if (!strcasecmp(atom + 2, "lit"))
+ return SPLIT;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 't') {
+ if (isascii(atom[2]) &&
+ tolower((unsigned char)atom[2]) == 'a') {
+ if(!strncasecmp(atom + 3, "rt", 2)) {
+ if (!strcasecmp(atom + 5, "s"))
+ return STARTS;
+ if (!strcasecmp(atom + 5, "up"))
+ return STARTUP;
+ break;
+ }
+ if (isascii(atom[3]) &&
+ tolower((unsigned char)atom[3]) == 't') {
+ if (!strcasecmp(atom + 4, "e"))
+ return STATE;
+ if (!strcasecmp(atom + 4, "ic"))
+ return STATIC;
+ break;
+ }
+ }
+ if (!strcasecmp(atom + 2, "ring"))
+ return STRING_TOKEN;
+ break;
+ }
+ if (!strncasecmp(atom + 1, "ub", 2)) {
+ if (!strcasecmp(atom + 3, "class"))
+ return SUBCLASS;
+ if (!strcasecmp(atom + 3, "net"))
+ return SUBNET;
+ if (!strcasecmp(atom + 3, "net6"))
+ return SUBNET6;
+ if (!strcasecmp(atom + 3, "string"))
+ return SUBSTRING;
+ break;
+ }
+ if (isascii(atom[1]) &&
+ tolower((unsigned char)atom[1]) == 'u') {
+ if (!strcasecmp(atom + 2, "ffix"))
+ return SUFFIX;
+ if (!strcasecmp(atom + 2, "persede"))
+ return SUPERSEDE;
+ }
+ if (!strcasecmp(atom + 1, "witch"))
+ return SWITCH;
+ 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;
+ if (!strcasecmp (atom + 1, "ext"))
+ return TEXT;
+ if (!strcasecmp (atom + 1, "stp"))
+ return TSTP;
+ if (!strcasecmp (atom + 1, "sfp"))
+ return TSFP;
+ if (!strcasecmp (atom + 1, "ransmission"))
+ return TRANSMISSION;
+ if (!strcasecmp(atom + 1, "emporary"))
+ return TEMPORARY;
+ break;
+ case 'u':
+ if (!strcasecmp (atom + 1, "case"))
+ return UCASE;
+ if (!strcasecmp (atom + 1, "nset"))
+ return UNSET;
+ if (!strcasecmp (atom + 1, "nsigned"))
+ return UNSIGNED;
+ if (!strcasecmp (atom + 1, "id"))
+ return UID;
+ 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 (!strncasecmp (atom + 1, "nknown", 6)) {
+ if (!strcasecmp (atom + 7, "-clients"))
+ return UNKNOWN_CLIENTS;
+ if (!strcasecmp (atom + 7, "-state"))
+ return UNKNOWN_STATE;
+ if (!atom [7])
+ return UNKNOWN;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "nauthenticated"))
+ return UNAUTHENTICATED;
+ if (!strcasecmp (atom + 1, "pdated-dns-rr"))
+ return UPDATED_DNS_RR;
+ if (!strcasecmp (atom + 1, "pdate"))
+ return UPDATE;
+ break;
+ case 'v':
+ if (!strcasecmp (atom + 1, "endor-class"))
+ return VENDOR_CLASS;
+ if (!strcasecmp (atom + 1, "endor"))
+ return VENDOR;
+ break;
+ case 'w':
+ if (!strcasecmp (atom + 1, "ith"))
+ return WITH;
+ if (!strcasecmp(atom + 1, "idth"))
+ return WIDTH;
+ break;
+ case 'y':
+ if (!strcasecmp (atom + 1, "iaddr"))
+ return YIADDR;
+ if (!strcasecmp (atom + 1, "xdomain"))
+ return NS_YXDOMAIN;
+ if (!strcasecmp (atom + 1, "xrrset"))
+ return NS_YXRRSET;
+ break;
+ case 'z':
+ if (!strcasecmp (atom + 1, "erolen"))
+ return ZEROLEN;
+ if (!strcasecmp (atom + 1, "one"))
+ return ZONE;
+ break;
+ }
+ return dfv;
+}
+
diff --git a/common/ctrace.c b/common/ctrace.c
new file mode 100644
index 0000000..0bc133e
--- /dev/null
+++ b/common/ctrace.c
@@ -0,0 +1,295 @@
+/* trace.c
+
+ Subroutines that support dhcp tracing... */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
+ * about Internet Systems Consortium, see https://www.isc.org/. To
+ * learn more about Nominum, Inc., see ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#if defined (TRACING)
+void trace_interface_register (trace_type_t *ttype, struct interface_info *ip)
+{
+ trace_interface_packet_t tipkt;
+
+ if (trace_record ()) {
+ memset (&tipkt, 0, sizeof tipkt);
+ memcpy (&tipkt.hw_address,
+ &ip -> hw_address, sizeof ip -> hw_address);
+ if (ip->address_count)
+ memcpy(&tipkt.primary_address,
+ ip->addresses, sizeof(*ip->addresses));
+ memcpy (tipkt.name, ip -> name, sizeof ip -> name);
+ tipkt.index = htonl (ip -> index);
+
+ trace_write_packet (ttype, sizeof tipkt, (char *)&tipkt, MDL);
+ }
+}
+
+void trace_interface_input (trace_type_t *ttype, unsigned len, char *buf)
+{
+ trace_interface_packet_t *tipkt;
+ struct interface_info *ip;
+ struct sockaddr_in *sin;
+ struct iaddr addr;
+ isc_result_t status;
+
+ if (len != sizeof *tipkt) {
+ log_error ("trace interface packet size mismatch: %ld != %d",
+ (long)(sizeof *tipkt), len);
+ return;
+ }
+ tipkt = (trace_interface_packet_t *)buf;
+
+ ip = (struct interface_info *)0;
+ status = interface_allocate (&ip, MDL);
+ if (status != ISC_R_SUCCESS) {
+ foo:
+ log_error ("trace_interface_input: %s.",
+ isc_result_totext (status));
+ return;
+ }
+ ip -> ifp = dmalloc (sizeof *(ip -> ifp), MDL);
+ if (!ip -> ifp) {
+ interface_dereference (&ip, MDL);
+ status = ISC_R_NOMEMORY;
+ goto foo;
+ }
+
+ memcpy (&ip -> hw_address, &tipkt -> hw_address,
+ sizeof ip -> hw_address);
+ /* XXX: Without the full addresses state it's not quite a full
+ * trace.
+ */
+ ip->address_count = ip->address_max = 1;
+ ip->addresses = dmalloc(sizeof(*ip->addresses), MDL);
+ memcpy(ip->addresses, &tipkt->primary_address, sizeof(*ip->addresses));
+ memcpy (ip -> name, tipkt -> name, sizeof ip -> name);
+ ip -> index = ntohl (tipkt -> index);
+
+ interface_snorf (ip, 0);
+ if (dhcp_interface_discovery_hook)
+ (*dhcp_interface_discovery_hook) (ip);
+
+ /* Fake up an ifp. */
+ memcpy (ip -> ifp -> ifr_name, ip -> name, sizeof ip -> name);
+#ifdef HAVE_SA_LEN
+ ip -> ifp -> ifr_addr.sa_len = sizeof (struct sockaddr_in);
+#endif
+ sin = (struct sockaddr_in *)&ip -> ifp -> ifr_addr;
+ sin->sin_addr = ip->addresses[0];
+
+ addr.len = 4;
+ memcpy (addr.iabuf, &sin -> sin_addr.s_addr, addr.len);
+ if (dhcp_interface_setup_hook)
+ (*dhcp_interface_setup_hook) (ip, &addr);
+ interface_stash (ip);
+
+ if (!quiet_interface_discovery) {
+ log_info ("Listening on Trace/%s/%s%s%s",
+ ip -> name,
+ print_hw_addr (ip -> hw_address.hbuf [0],
+ ip -> hw_address.hlen - 1,
+ &ip -> hw_address.hbuf [1]),
+ (ip -> shared_network ? "/" : ""),
+ (ip -> shared_network ?
+ ip -> shared_network -> name : ""));
+ if (strcmp (ip -> name, "fallback")) {
+ log_info ("Sending on Trace/%s/%s%s%s",
+ ip -> name,
+ print_hw_addr (ip -> hw_address.hbuf [0],
+ ip -> hw_address.hlen - 1,
+ &ip -> hw_address.hbuf [1]),
+ (ip -> shared_network ? "/" : ""),
+ (ip -> shared_network ?
+ ip -> shared_network -> name : ""));
+ }
+ }
+ interface_dereference (&ip, MDL);
+}
+
+void trace_interface_stop (trace_type_t *ttype) {
+ /* XXX */
+}
+
+void trace_inpacket_stash (struct interface_info *interface,
+ struct dhcp_packet *packet,
+ unsigned len,
+ unsigned int from_port,
+ struct iaddr from,
+ struct hardware *hfrom)
+{
+ trace_inpacket_t tip;
+ trace_iov_t iov [2];
+
+ if (!trace_record ())
+ return;
+ tip.from_port = from_port;
+ tip.from = from;
+ tip.from.len = htonl (tip.from.len);
+ if (hfrom) {
+ tip.hfrom = *hfrom;
+ tip.havehfrom = 1;
+ } else {
+ memset (&tip.hfrom, 0, sizeof tip.hfrom);
+ tip.havehfrom = 0;
+ }
+ tip.index = htonl (interface -> index);
+
+ iov [0].buf = (char *)&tip;
+ iov [0].len = sizeof tip;
+ iov [1].buf = (char *)packet;
+ iov [1].len = len;
+ trace_write_packet_iov (inpacket_trace, 2, iov, MDL);
+}
+
+void trace_inpacket_input (trace_type_t *ttype, unsigned len, char *buf)
+{
+ trace_inpacket_t *tip;
+ int index;
+
+ if (len < sizeof *tip) {
+ log_error ("trace_input_packet: too short - %d", len);
+ return;
+ }
+ tip = (trace_inpacket_t *)buf;
+ index = ntohl (tip -> index);
+ tip -> from.len = ntohl (tip -> from.len);
+
+ if (index > interface_count ||
+ index < 0 ||
+ !interface_vector [index]) {
+ log_error ("trace_input_packet: unknown interface index %d",
+ index);
+ return;
+ }
+
+ if (!bootp_packet_handler) {
+ log_error ("trace_input_packet: no bootp packet handler.");
+ return;
+ }
+
+ (*bootp_packet_handler) (interface_vector [index],
+ (struct dhcp_packet *)(tip + 1),
+ len - sizeof *tip,
+ tip -> from_port,
+ tip -> from,
+ (tip -> havehfrom ?
+ &tip -> hfrom
+ : (struct hardware *)0));
+}
+
+void trace_inpacket_stop (trace_type_t *ttype) { }
+
+ssize_t trace_packet_send (struct interface_info *interface,
+ struct packet *packet,
+ struct dhcp_packet *raw,
+ size_t len,
+ struct in_addr from,
+ struct sockaddr_in *to,
+ struct hardware *hto)
+{
+ trace_outpacket_t tip;
+ trace_iov_t iov [2];
+
+ if (trace_record ()) {
+ if (hto) {
+ tip.hto = *hto;
+ tip.havehto = 1;
+ } else {
+ memset (&tip.hto, 0, sizeof tip.hto);
+ tip.havehto = 0;
+ }
+ tip.from.len = 4;
+ memcpy (tip.from.iabuf, &from, 4);
+ tip.to.len = 4;
+ memcpy (tip.to.iabuf, &to -> sin_addr, 4);
+ tip.to_port = to -> sin_port;
+ tip.index = htonl (interface -> index);
+
+ iov [0].buf = (char *)&tip;
+ iov [0].len = sizeof tip;
+ iov [1].buf = (char *)raw;
+ iov [1].len = len;
+ trace_write_packet_iov (outpacket_trace, 2, iov, MDL);
+ }
+ if (!trace_playback ()) {
+ return send_packet (interface, packet, raw, len,
+ from, to, hto);
+ }
+ return len;
+}
+
+void trace_outpacket_input (trace_type_t *ttype, unsigned len, char *buf)
+{
+ trace_outpacket_t *tip;
+ int index;
+
+ if (len < sizeof *tip) {
+ log_error ("trace_input_packet: too short - %d", len);
+ return;
+ }
+ tip = (trace_outpacket_t *)buf;
+ index = ntohl (tip -> index);
+
+ if (index > interface_count ||
+ index < 0 ||
+ !interface_vector [index]) {
+ log_error ("trace_input_packet: unknown interface index %d",
+ index);
+ return;
+ }
+
+ /* XXX would be nice to somehow take notice of these. */
+}
+
+void trace_outpacket_stop (trace_type_t *ttype) { }
+
+void trace_seed_stash (trace_type_t *ttype, unsigned seed)
+{
+ u_int32_t outseed;
+ if (!trace_record ())
+ return;
+ outseed = htonl (seed);
+ trace_write_packet (ttype, sizeof outseed, (char *)&outseed, MDL);
+ return;
+}
+
+void trace_seed_input (trace_type_t *ttype, unsigned length, char *buf)
+{
+ u_int32_t *seed;
+
+ if (length != sizeof seed) {
+ log_error ("trace_seed_input: wrong size (%d)", length);
+ }
+ seed = (u_int32_t *)buf;
+ srandom (ntohl (*seed));
+}
+
+void trace_seed_stop (trace_type_t *ttype) { }
+#endif /* TRACING */
diff --git a/common/dhcp-eval.5 b/common/dhcp-eval.5
new file mode 100644
index 0000000..7228929
--- /dev/null
+++ b/common/dhcp-eval.5
@@ -0,0 +1,552 @@
+.\" $Id: dhcp-eval.5,v 1.29.24.2 2010-07-06 19:03:11 sar Exp $
+.\"
+.\" Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH dhcp-eval 5
+.SH NAME
+dhcp-eval - ISC DHCP conditional evaluation
+.SH DESCRIPTION
+The Internet Systems Consortium DHCP client and server both provide
+the ability to perform conditional behavior depending on the contents
+of packets they receive. The syntax for specifying this conditional
+behaviour is documented here.
+.SH REFERENCE: CONDITIONAL BEHAVIOUR
+Conditional behaviour is specified using the if statement and the else
+or elsif statements. A conditional statement can appear anywhere
+that a regular statement (e.g., an option statement) can appear, and
+can enclose one or more such statements. A typical conditional
+statement in a server might be:
+.PP
+.nf
+if option dhcp-user-class = "accounting" {
+ max-lease-time 17600;
+ option domain-name "accounting.example.org";
+ option domain-name-servers ns1.accounting.example.org,
+ ns2.accounting.example.org;
+} elsif option dhcp-user-class = "sales" {
+ max-lease-time 17600;
+ option domain-name "sales.example.org";
+ option domain-name-servers ns1.sales.example.org,
+ ns2.sales.example.org;
+} elsif option dhcp-user-class = "engineering" {
+ max-lease-time 17600;
+ option domain-name "engineering.example.org";
+ option domain-name-servers ns1.engineering.example.org,
+ ns2.engineering.example.org;
+} else {
+ max-lease-time 600;
+ option domain-name "misc.example.org";
+ option domain-name-servers ns1.misc.example.org,
+ ns2.misc.example.org;
+}
+.fi
+.PP
+On the client side, an example of conditional evaluation might be:
+.PP
+.nf
+# example.org filters DNS at its firewall, so we have to use their DNS
+# servers when we connect to their network. If we are not at
+# example.org, prefer our own DNS server.
+if not option domain-name = "example.org" {
+ prepend domain-name-servers 127.0.0.1;
+}
+.fi
+.PP
+The
+.B if
+statement and the
+.B elsif
+continuation statement both take boolean expressions as their
+arguments. That is, they take expressions that, when evaluated,
+produce a boolean result. If the expression evaluates to true, then
+the statements enclosed in braces following the
+.B if
+statement are executed, and all subsequent
+.B elsif
+and
+.B else
+clauses are skipped. Otherwise, each subsequent
+.B elsif
+clause's expression is checked, until an elsif clause is encountered
+whose test evaluates to true. If such a clause is found, the
+statements in braces following it are executed, and then any
+subsequent
+.B elsif
+and
+.B else
+clauses are skipped. If all the
+.B if
+and
+.B elsif
+clauses are checked but none
+of their expressions evaluate true, then if there is an
+.B else
+clause, the statements enclosed in braces following the
+.B else
+are evaluated. Boolean expressions that evaluate to null are
+treated as false in conditionals.
+.SH BOOLEAN EXPRESSIONS
+The following is the current list of boolean expressions that are
+supported by the DHCP distribution.
+.PP
+.I data-expression-1 \fB=\fR \fIdata-expression-2\fR
+.RS 0.25i
+.PP
+The \fB=\fR operator compares the values of two data expressions,
+returning true if they are the same, false if they are not. If
+either the left-hand side or the right-hand side are null, the
+result is also null.
+.RE
+.PP
+.I data-expression-1 \fB~=\fR \fIdata-expression-2\fR
+.I data-expression-1 \fB~~\fR \fIdata-expression-2\fR
+.RS 0.25i
+.PP
+The \fB~=\fR and \fB~~\fR operators (not available on all systems) perform
+extended regex(7) matching of the values of two data expressions, returning
+true if \fIdata-expression-1\fR matches against the regular expression
+evaluated by \fIdata-expression-2\fR, or false if it does not match or
+encounters some error. If either the left-hand side or the right-hand side
+are null, the result is also false. The \fB~~\fR operator differs from the
+\fB~=\fR operator in that it is case-insensitive.
+.RE
+.PP
+.I boolean-expression-1 \fBand\fR \fIboolean-expression-2\fR
+.PP
+.RS 0.25i
+The \fBand\fR operator evaluates to true if the boolean expression on
+the left-hand side and the boolean expression on the right-hand side
+both evaluate to true. Otherwise, it evaluates to false. If either
+the expression on the left-hand side or the expression on the
+right-hand side are null, the result is null.
+.RE
+.PP
+.I boolean-expression-1 \fBor\fR \fIboolean-expression-2\fR
+.PP
+.RS 0.25i
+The \fBor\fR operator evaluates to true if either the boolean
+expression on the left-hand side or the boolean expression on the
+right-hand side evaluate to true. Otherwise, it evaluates to false.
+If either the expression on the left-hand side or the expression on
+the right-hand side are null, the result is null.
+.RE
+.PP
+.B not \fIboolean-expression
+.PP
+.RS 0.25i
+The \fBnot\fR operator evaluates to true if \fIboolean-expression\fR
+evaluates to false, and returns false if \fIboolean-expression\fR evaluates
+to true. If \fIboolean-expression\fR evaluates to null, the result
+is also null.
+.RE
+.PP
+.B exists \fIoption-name\fR
+.PP
+.RS 0.25i
+The \fBexists\fR expression returns true if the specified option
+exists in the incoming DHCP packet being processed.
+.RE
+.B known
+.PP
+.RS 0.25i
+The \fBknown\fR expression returns true if the client whose request is
+currently being processed is known - that is, if there's a host
+declaration for it.
+.RE
+.B static
+.PP
+.RS 0.25i
+The \fBstatic\fR expression returns true if the lease assigned to the
+client whose request is currently being processed is derived from a static
+address assignment.
+.RE
+.SH DATA EXPRESSIONS
+Several of the boolean expressions above depend on the results of
+evaluating data expressions. A list of these expressions is provided
+here.
+.PP
+.B substring (\fIdata-expr\fB, \fIoffset\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+The \fBsubstring\fR operator evaluates the data expression and returns
+the substring of the result of that evaluation that starts
+\fIoffset\fR bytes from the beginning, continuing for \fIlength\fR
+bytes. \fIOffset\fR and \fIlength\fR are both numeric expressions.
+If \fIdata-expr\fR, \fIoffset\fR or \fIlength\fR evaluate to null,
+then the result is also null. If \fIoffset\fR is greater than or
+equal to the length of the evaluated data, then a zero-length data
+string is returned. If \fIlength\fI is greater then the remaining
+length of the evaluated data after \fIoffset\fR, then a data string
+containing all data from \fIoffset\fR to the end of the evaluated data
+is returned.
+.RE
+.PP
+.B suffix (\fIdata-expr\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+The \fBsuffix\fR operator evaluates \fIdata-expr\fR and returns the
+last \fIlength\fR bytes of the result of that evaluation. \fILength\fR
+is a numeric expression. If \fIdata-expr\fR or \fIlength\fR evaluate
+to null, then the result is also null. If \fIsuffix\fR evaluates to a
+number greater than the length of the evaluated data, then the
+evaluated data is returned.
+.RE
+.PP
+.B lcase (\fIdata-expr\fB)\fR
+.PP
+.RS 0.25i
+The \fBlcase\fR function returns the result of evaluating
+\fIdata-expr\fR converted to lower case. If \fIdata-expr\fR evaluates
+to null, then the result is also null.
+.RE
+.PP
+.B ucase (\fIdata-expr\fB)\fR
+.PP
+.RS 0.25i
+The \fBucase\fR function returns the result of evaluating
+\fIdata-expr\fR converted to upper case. If \fIdata-expr\fR evaluates
+to null, then the result is also null.
+.RE
+.PP
+.B option \fIoption-name\fR
+.PP
+.RS 0.25i
+The \fBoption\fR operator returns the contents of the specified option in
+the packet to which the server is responding.
+.RE
+.PP
+.B config-option \fIoption-name\fR
+.PP
+.RS 0.25i
+The \fBconfig-option\fR operator returns the value for the specified option
+that the DHCP client or server has been configured to send.
+.RE
+.PP
+.B gethostname()
+.PP
+.RS 0.25i
+The \fBgethostname()\fR function returns a data string whose contents are a
+character string, the results of calling gethostname() on the local
+system with a size limit of 255 bytes (not including NULL terminator). This
+can be used for example to configure dhclient to send the local hostname
+without knowing the local hostname at the time dhclient.conf is written.
+.RE
+.PP
+.B hardware
+.PP
+.RS 0.25i
+The \fBhardware\fR operator returns a data string whose first element
+is the type of network interface indicated in packet being considered,
+and whose subsequent elements are client's link-layer address. If
+there is no packet, or if the RFC2131 \fIhlen\fR field is invalid,
+then the result is null. Hardware types include ethernet (1),
+token-ring (6), and fddi (8). Hardware types are specified by the
+IETF, and details on how the type numbers are defined can be found in
+RFC2131 (in the ISC DHCP distribution, this is included in the doc/
+subdirectory).
+.RE
+.PP
+.B packet (\fIoffset\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+The \fBpacket\fR operator returns the specified portion of the packet
+being considered, or null in contexts where no packet is being
+considered. \fIOffset\fR and \fIlength\fR are applied to the
+contents packet as in the \fBsubstring\fR operator.
+.RE
+.PP
+.I string
+.PP
+.RS 0.25i
+A string, enclosed in quotes, may be specified as a data expression,
+and returns the text between the quotes, encoded in ASCII. The
+backslash ('\\') character is treated specially, as in C programming: '\\t'
+means TAB, '\\r' means carriage return, '\\n' means newline, and '\\b' means
+bell. Any octal value can be specified with '\\nnn', where nnn is any
+positive octal number less than 0400. Any hexadecimal value can be
+specified with '\\xnn', where nn is any positive hexadecimal number less
+than or equal to 0xff.
+.RE
+.PP
+.I colon-separated hexadecimal list
+.PP
+.RS 0.25i
+A list of hexadecimal octet values, separated by colons, may be
+specified as a data expression.
+.RE
+.PP
+.B concat (\fIdata-expr1\fB, ..., \fIdata-exprN\fB)\fR
+.RS 0.25i
+The expressions are evaluated, and the results of each evaluation are
+concatenated in the sequence that the subexpressions are listed. If
+any subexpression evaluates to null, the result of the concatenation
+is null.
+.RE
+.PP
+.B reverse (\fInumeric-expr1\fB, \fIdata-expr2\fB)\fR
+.RS 0.25i
+The two expressions are evaluated, and then the result of evaluating
+the data expression is reversed in place, using hunks of the size
+specified in the numeric expression. For example, if the numeric
+expression evaluates to four, and the data expression evaluates to
+twelve bytes of data, then the reverse expression will evaluate to
+twelve bytes of data, consisting of the last four bytes of the the
+input data, followed by the middle four bytes, followed by the first
+four bytes.
+.RE
+.PP
+.B leased-address
+.RS 0.25i
+In any context where the client whose request is being processed has
+been assigned an IP address, this data expression returns that IP
+address. In any context where the client whose request is being
+processed has not been assigned an ip address, if this data expression
+is found in executable statements executed on that client's behalf,
+a log message indicating "there is no lease associated with this client"
+is syslogged to the debug level (this is considered dhcpd.conf debugging
+information).
+.RE
+.PP
+.B binary-to-ascii (\fInumeric-expr1\fB, \fInumeric-expr2\fB,
+.B \fIdata-expr1\fB,\fR \fIdata-expr2\fB)\fR
+.RS 0.25i
+Converts the result of evaluating data-expr2 into a text string
+containing one number for each element of the result of evaluating
+data-expr2. Each number is separated from the other by the result of
+evaluating data-expr1. The result of evaluating numeric-expr1
+specifies the base (2 through 16) into which the numbers should be
+converted. The result of evaluating numeric-expr2 specifies the
+width in bits of each number, which may be either 8, 16 or 32.
+.PP
+As an example of the preceding three types of expressions, to produce
+the name of a PTR record for the IP address being assigned to a
+client, one could write the following expression:
+.RE
+.PP
+.nf
+ concat (binary-to-ascii (10, 8, ".",
+ reverse (1, leased-address)),
+ ".in-addr.arpa.");
+
+.fi
+.RE
+.PP
+.B encode-int (\fInumeric-expr\fB, \fIwidth\fB)\fR
+.RS 0.25i
+Numeric-expr is evaluated and encoded as a data string of the
+specified width, in network byte order (most significant byte first).
+If the numeric expression evaluates to the null value, the result is
+also null.
+.RE
+.PP
+.B pick-first-value (\fIdata-expr1\fR [ ... \fIexpr\fRn ] \fB)\fR
+.RS 0.25i
+The pick-first-value function takes any number of data expressions as
+its arguments. Each expression is evaluated, starting with the first
+in the list, until an expression is found that does not evaluate to a
+null value. That expression is returned, and none of the subsequent
+expressions are evaluated. If all expressions evaluate to a null
+value, the null value is returned.
+.RE
+.PP
+.B host-decl-name
+.RS 0.25i
+The host-decl-name function returns the name of the host declaration
+that matched the client whose request is currently being processed, if
+any. If no host declaration matched, the result is the null value.
+.RE
+.SH NUMERIC EXPRESSIONS
+Numeric expressions are expressions that evaluate to an integer. In
+general, the maximum size of such an integer should not be assumed to
+be representable in fewer than 32 bits, but the precision of such
+integers may be more than 32 bits.
+.PP
+.B extract-int (\fIdata-expr\fB, \fIwidth\fB)\fR
+.PP
+.RS 0.25i
+The \fBextract-int\fR operator extracts an integer value in network
+byte order from the result of evaluating the specified data
+expression. Width is the width in bits of the integer to extract.
+Currently, the only supported widths are 8, 16 and 32. If the
+evaluation of the data expression doesn't provide sufficient bits to
+extract an integer of the specified size, the null value is returned.
+.RE
+.PP
+.B lease-time
+.PP
+.RS 0.25i
+The duration of the current lease - that is, the difference between
+the current time and the time that the lease expires.
+.RE
+.PP
+.I number
+.PP
+.RS 0.25i
+Any number between zero and the maximum representable size may be
+specified as a numeric expression.
+.RE
+.PP
+.B client-state
+.PP
+.RS 0.25i
+The current state of the client instance being processed. This is
+only useful in DHCP client configuration files. Possible values are:
+.TP 2
+.I \(bu
+Booting - DHCP client is in the INIT state, and does not yet have an
+IP address. The next message transmitted will be a DHCPDISCOVER,
+which will be broadcast.
+.TP
+.I \(bu
+Reboot - DHCP client is in the INIT-REBOOT state. It has an IP
+address, but is not yet using it. The next message to be transmitted
+will be a DHCPREQUEST, which will be broadcast. If no response is
+heard, the client will bind to its address and move to the BOUND state.
+.TP
+.I \(bu
+Select - DHCP client is in the SELECTING state - it has received at
+least one DHCPOFFER message, but is waiting to see if it may receive
+other DHCPOFFER messages from other servers. No messages are sent in
+the SELECTING state.
+.TP
+.I \(bu
+Request - DHCP client is in the REQUESTING state - it has received at
+least one DHCPOFFER message, and has chosen which one it will
+request. The next message to be sent will be a DHCPREQUEST message,
+which will be broadcast.
+.TP
+.I \(bu
+Bound - DHCP client is in the BOUND state - it has an IP address. No
+messages are transmitted in this state.
+.TP
+.I \(bu
+Renew - DHCP client is in the RENEWING state - it has an IP address,
+and is trying to contact the server to renew it. The next message to
+be sent will be a DHCPREQUEST message, which will be unicast directly
+to the server.
+.TP
+.I \(bu
+Rebind - DHCP client is in the REBINDING state - it has an IP address,
+and is trying to contact any server to renew it. The next message to
+be sent will be a DHCPREQUEST, which will be broadcast.
+.RE
+.SH REFERENCE: ACTION EXPRESSIONS
+.PP
+.B log (\fIpriority\fB, \fIdata-expr\fB)\fR
+.RS 0.25i
+.PP
+Logging statements may be used to send information to the standard logging
+channels. A logging statement includes an optional priority (\fBfatal\fR,
+\fBerror\fR, \fBinfo\fR, or \fBdebug\fR), and a data expression.
+.PP
+Logging statements take only a single data expression argument, so if you
+want to output multiple data values, you will need to use the \fBconcat\fR
+operator to concatenate them.
+.RE
+.PP
+.B execute (\fIcommand-path\fB [, \fIdata-expr1\fB, ... \fIdata-exprN\fB]);\fR
+.RS 0.25i
+.PP
+The \fBexecute\fR statement runs an external command. The first argument
+is a string literal containing the name or path of the command to run.
+The other arguments, if present, are either string literals or data-
+expressions which evaluate to text strings, to be passed as command-line
+arguments to the command.
+.PP
+\fBexecute\fR is synchronous; the program will block until the external
+command being run has finished. Please note that lengthy program
+execution (for example, in an "on commit" in dhcpd.conf) may result in
+bad performance and timeouts. Only external applications with very short
+execution times are suitable for use.
+.PP
+Passing user-supplied data to an external application might be dangerous.
+Make sure the external application checks input buffers for validity.
+Non-printable ASCII characters will be converted into dhcpd.conf language
+octal escapes ("\777"), make sure your external command handles them as
+such.
+.PP
+It is possible to use the execute statement in any context, not only
+on events. If you put it in a regular scope in the configuration file
+you will execute that command every time a scope is evaluated.
+.RE
+.SH REFERENCE: DYNAMIC DNS UPDATES
+.PP
+The DHCP client and server have the ability to dynamically update the
+Domain Name System. Within the configuration files, you can define
+how you want the Domain Name System to be updated. These updates are
+RFC 2136 compliant so any DNS server supporting RFC 2136 should be
+able to accept updates from the DHCP server.
+.SH SECURITY
+Support for TSIG and DNSSEC is not yet available. When you set your
+DNS server up to allow updates from the DHCP server or client, you may
+be exposing it to unauthorized updates. To avoid this, the best you
+can do right now is to use IP address-based packet filtering to
+prevent unauthorized hosts from submitting update requests.
+Obviously, there is currently no way to provide security for client
+updates - this will require TSIG or DNSSEC, neither of which is yet
+available in the DHCP distribution.
+.PP
+Dynamic DNS (DDNS) updates are performed by using the \fBdns-update\fR
+expression. The \fBdns-update\fR expression is a boolean expression
+that takes four parameters. If the update succeeds, the result is
+true. If it fails, the result is false. The four parameters that the
+are the resource record type (RR), the left hand side of the RR, the
+right hand side of the RR and the ttl that should be applied to the
+record. The simplest example of the use of the function can be found
+in the reference section of the dhcpd.conf file, where events are
+described. In this example several statements are being used to make
+the arguments to the \fBdns-update\fR.
+.PP
+In the example, the first argument to the first \f\Bdns-update\fR
+expression is a data expression that evaluates to the A RR type. The
+second argument is constructed by concatenating the DHCP host-name
+option with a text string containing the local domain, in this case
+"ssd.example.net". The third argument is constructed by converting
+the address the client has been assigned from a 32-bit number into an
+ascii string with each byte separated by a ".". The fourth argument,
+the TTL, specifies the amount of time remaining in the lease (note
+that this isn't really correct, since the DNS server will pass this
+TTL out whenever a request comes in, even if that is only a few
+seconds before the lease expires).
+.PP
+If the first \fBdns-update\fR statement succeeds, it is followed up
+with a second update to install a PTR RR. The installation of a PTR
+record is similar to installing an A RR except that the left hand side
+of the record is the leased address, reversed, with ".in-addr.arpa"
+concatenated. The right hand side is the fully qualified domain name
+of the client to which the address is being leased.
+.SH SEE ALSO
+dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-options(5), dhcpd(8),
+dhclient(8), RFC2132, RFC2131.
+.SH AUTHOR
+The Internet Systems Consortium DHCP Distribution was written by Ted
+Lemon under a contract with Vixie Labs. Funding for
+this project was provided through Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at
+.B https://www.isc.org.
diff --git a/common/dhcp-options.5 b/common/dhcp-options.5
new file mode 100644
index 0000000..a87d03f
--- /dev/null
+++ b/common/dhcp-options.5
@@ -0,0 +1,2141 @@
+.\" $Id: dhcp-options.5,v 1.45.18.6 2011-05-20 14:33:26 tomasz Exp $
+.\"
+.\" Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH dhcp-options 5
+.SH NAME
+dhcp-options - Dynamic Host Configuration Protocol options
+.SH DESCRIPTION
+The Dynamic Host Configuration protocol allows the client to receive
+.B options
+from the DHCP server describing the network configuration and various
+services that are available on the network. When configuring
+.B dhcpd(8)
+or
+.B 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
+.PP
+DHCP \fIoption\fR statements always start with the \fIoption\fR
+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
+.B 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). When entering a domain name, be sure that that
+domain name resolves to a single IP address.
+.PP
+The
+.B ip6-address
+data specifies an IPv6 address, like ::1 or 3ffe:bbbb:aaaa:aaaa::1.
+.PP
+The
+.B int32
+data type specifies a signed 32-bit integer. The
+.B uint32
+data type specifies an unsigned 32-bit integer. The
+.B int16
+and
+.B uint16
+data types specify signed and unsigned 16-bit integers. The
+.B int8
+and
+.B uint8
+data types specify signed and unsigned 8-bit integers.
+Unsigned 8-bit integers are also sometimes referred to as octets.
+.PP
+The
+.B text
+data type specifies an NVT ASCII string, which must be
+enclosed in double quotes - for example, to specify a root-path
+option, the syntax would be
+.nf
+.sp 1
+option root-path "10.0.1.4:/var/tmp/rootfs";
+.fi
+.PP
+The
+.B domain-name
+data type specifies a domain name, which must not be
+enclosed in double quotes. This data type is not used for any
+existing DHCP options. The domain name is stored just as if it were
+a text option.
+.PP
+The
+.B domain-list
+data type specifies a list of domain names, enclosed in double quotes and
+separated by commas ("example.com", "foo.example.com").
+.PP
+The
+.B 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
+.B string
+data type specifies either an NVT ASCII string
+enclosed in double quotes, or a series of octets specified in
+hexadecimal, separated by colons. For example:
+.nf
+.sp 1
+ option dhcp-client-identifier "CLIENT-FOO";
+or
+ option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
+.fi
+.SH SETTING OPTION VALUES USING EXPRESSIONS
+Sometimes it's helpful to be able to set the value of a DHCP option
+based on some value that the client has sent. To do this, you can
+use expression evaluation. The
+.B dhcp-eval(5)
+manual page describes how to write expressions. To assign the result
+of an evaluation to an option, define the option as follows:
+.nf
+.sp 1
+ \fBoption \fImy-option \fB= \fIexpression \fB;\fR
+.fi
+.PP
+For example:
+.nf
+.sp 1
+ option hostname = binary-to-ascii (16, 8, "-",
+ substring (hardware, 1, 6));
+.fi
+.SH STANDARD DHCPV4 OPTIONS
+The documentation for the various options mentioned below is taken
+from the latest IETF draft document on DHCP options. Options not
+listed below may not yet be implemented, but it is possible to use
+such options by defining them in the configuration file. Please see
+the DEFINING NEW OPTIONS heading later in this document for more
+information.
+.PP
+Some of the options documented here are automatically generated by
+the DHCP server or by clients, and cannot be configured by the user.
+The value of such an option can be used in the configuration file of
+the receiving DHCP protocol agent (server or client), for example in
+conditional expressions. However, the value of the option cannot be
+used in the configuration file of the sending agent, because the value
+is determined only \fIafter\fR the configuration file has been
+processed. In the following documentation, such options will be shown
+as "not user configurable"
+.PP
+The standard options are:
+.PP
+.B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+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 true indicates that all subnets share
+the same MTU. A value of false means that the client should assume that
+some subnets of the directly connected network may have smaller MTUs.
+.RE
+.PP
+.B option \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the timeout in seconds for ARP cache entries.
+.RE
+.PP
+.B option \fBbcms-controller-address\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+This option configures a list of IPv4 addresses for use as Broadcast and
+Multicast Controller Servers ("BCMS").
+.RE
+.PP
+.B option \fBbcms-controller-names\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+This option contains the domain names of local Broadcast and
+Multicast Controller Servers ("BCMS") controllers which the client
+may use.
+.RE
+.PP
+.B option \fBbootfile-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used to identify a bootstrap file. If supported by the
+client, it should have the same effect as the \fBfilename\fR
+declaration. BOOTP clients are unlikely to support this option. Some
+DHCP clients will support it, and others actually require it.
+.RE
+.PP
+.B option \fBboot-size\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the length in 512-octet blocks of the default
+boot image for the client.
+.RE
+.PP
+.B option \fBbroadcast-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+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 (RFC1122).
+.RE
+.PP
+.B option \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The cookie server option specifies a list of RFC 865 cookie
+servers available to the client. Servers should be listed in order
+of preference.
+.RE
+.PP
+.B option \fBdefault-ip-ttl\fR \fIuint8;\fR
+.RS 0.25i
+.PP
+This option specifies the default time-to-live that the client should
+use on outgoing datagrams.
+.RE
+.PP
+.B option \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the default TTL that the client should use when
+sending TCP segments. The minimum value is 1.
+.RE
+.PP
+.B option \fBdefault-url\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The format and meaning of this option is not described in any standards
+document, but is claimed to be in use by Apple Computer. It is not known
+what clients may reasonably do if supplied with this option. Use at your
+own risk.
+.RE
+.PP
+.B option \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option can be used to specify a DHCP client identifier in a
+host declaration, so that dhcpd can find the host record by matching
+against the client identifier.
+.PP
+Please be aware that some DHCP clients, when configured with client
+identifiers that are ASCII text, will prepend a zero to the ASCII
+text. So you may need to write:
+.nf
+
+ option dhcp-client-identifier "\\0foo";
+
+rather than:
+
+ option dhcp-client-identifier "foo";
+.fi
+.RE
+.PP
+.B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used in a client request (DHCPDISCOVER or DHCPREQUEST)
+to allow the client to request a lease time for the IP address. In a
+server reply (DHCPOFFER), a DHCP server uses this option to specify
+the lease time it is willing to offer.
+.PP
+This option is not directly user configurable in the server; refer to the
+\fImax-lease-time\fR and \fIdefault-lease-time\fR server options in
+.B dhcpd.conf(5).
+.RE
+.PP
+.B option \fBdhcp-max-message-size\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+This option, when sent by the client, specifies the maximum size of
+any response that the server sends to the client. When specified on
+the server, if the client did not send a dhcp-max-message-size option,
+the size specified on the server is used. This works for BOOTP as
+well as DHCP responses.
+.RE
+.PP
+.B option \fBdhcp-message\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used by a DHCP server to provide an error message to a
+DHCP client in a DHCPNAK message in the event of a failure. A client
+may use this option in a DHCPDECLINE message to indicate why the
+client declined the offered parameters.
+.PP
+This option is not user configurable.
+.RE
+.PP
+.B option \fBdhcp-message-type\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+This option, sent by both client and server, specifies the type of DHCP
+message contained in the DHCP packet. Possible values (taken directly from
+RFC2132) are:
+.PP
+.nf
+ 1 DHCPDISCOVER
+ 2 DHCPOFFER
+ 3 DHCPREQUEST
+ 4 DHCPDECLINE
+ 5 DHCPACK
+ 6 DHCPNAK
+ 7 DHCPRELEASE
+ 8 DHCPINFORM
+.fi
+.PP
+This option is not user configurable.
+.PP
+.RE
+.B option \fBdhcp-option-overload\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used to indicate that the DHCP \'sname\' or \'file\'
+fields are being overloaded by using them to carry DHCP options. A
+DHCP server inserts this option if the returned parameters will
+exceed the usual space allotted for options.
+.PP
+If this option is present, the client interprets the specified
+additional fields after it concludes interpretation of the standard
+option fields.
+.PP
+Legal values for this option are:
+.PP
+.nf
+ 1 the \'file\' field is used to hold options
+ 2 the \'sname\' field is used to hold options
+ 3 both fields are used to hold options
+.fi
+.PP
+This option is not user configurable.
+.PP
+.RE
+.PP
+.B option \fBdhcp-parameter-request-list\fR \fIuint16\fR [\fB,\fR
+\fIuint16\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+This option, when sent by the client, specifies which options the
+client wishes the server to return. Normally, in the ISC DHCP
+client, this is done using the \fIrequest\fR statement. If this
+option is not specified by the client, the DHCP server will normally
+return every option that is valid in scope and that fits into the
+reply. When this option is specified on the server, the server
+returns the specified options. This can be used to force a client to
+take options that it hasn't requested, and it can also be used to
+tailor the response of the DHCP server for clients that may need a
+more limited set of options than those the server would normally
+return.
+.RE
+.PP
+.B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the number of seconds from the time a client gets
+an address until the client transitions to the REBINDING state.
+.PP
+This option is user configurable, but it will be ignored if the value is
+greater than the lease time.
+.PP
+To make DHCPv4+DHCPv6 migration easier in the future, any value configured
+in this option is also used as a DHCPv6 "T1" (renew) time.
+.PP
+.RE
+.PP
+.B option \fBdhcp-renewal-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the number of seconds from the time a client gets
+an address until the client transitions to the RENEWING state.
+.PP
+This option is user configurable, but it will be ignored if the value is
+greater than the rebinding time, or lease time.
+.PP
+To make DHCPv4+DHCPv6 migration easier in the future, any value configured
+in this option is also used as a DHCPv6 "T2" (rebind) time.
+.PP
+.RE
+.PP
+.B option \fBdhcp-requested-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used by the client in a DHCPDISCOVER to
+request that a particular IP address be assigned.
+.PP
+This option is not user configurable.
+.PP
+.RE
+.PP
+.B option \fBdhcp-server-identifier\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used in DHCPOFFER and DHCPREQUEST messages, and may
+optionally be included in the DHCPACK and DHCPNAK messages. DHCP
+servers include this option in the DHCPOFFER in order to allow the
+client to distinguish between lease offers. DHCP clients use the
+contents of the \'server identifier\' field as the destination address
+for any DHCP messages unicast to the DHCP server. DHCP clients also
+indicate which of several lease offers is being accepted by including
+this option in a DHCPREQUEST message.
+.PP
+The value of this option is the IP address of the server.
+.PP
+This option is not directly user configurable. See the
+\fIserver-identifier\fR server option in
+.B \fIdhcpd.conf(5).
+.PP
+.RE
+.PP
+.B option \fBdomain-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the domain name that client should use when
+resolving hostnames via the Domain Name System.
+.RE
+.PP
+.B option \fBdomain-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The 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.
+.RE
+.PP
+.B option \fBdomain-search\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The domain-search option specifies a \'search list\' of Domain Names to be
+used by the client to locate not-fully-qualified domain names. The difference
+between this option and historic use of the domain-name option for the same
+ends is that this option is encoded in RFC1035 compressed labels on the wire.
+For example:
+.nf
+.sp 1
+ option domain-search "example.com", "sales.example.com",
+ "eng.example.com";
+.fi
+.RE
+.PP
+.B option \fBextensions-path\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the name of a file containing additional options
+to be interpreted according to the DHCP option format as specified in
+RFC2132.
+.RE
+.PP
+.B option \fBfinger-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The Finger server option specifies a list of Finger servers available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+This option specifies a list of X Window System Font servers available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBhost-name\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+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 domain-name option to specify the domain name). See RFC 1035 for
+character set restrictions. This option is only honored by
+.B dhclient-script(8)
+if the hostname for the client machine is not set.
+.RE
+.PP
+.B option \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+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 false indicates that the client
+should use RFC 894 encapsulation. A value of true means that the client
+should use RFC 1042 encapsulation.
+.RE
+.PP
+.B option \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+];
+.RS 0.25i
+.PP
+The ien116-name-servers option specifies a list of IEN 116 name servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The impress-server option specifies a list of Imagen Impress servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBinterface-mtu\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the MTU to use on this interface. The minimum
+legal value for the MTU is 68.
+.RE
+.PP
+.B option \fBip-forwarding\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies whether the client should configure its IP
+layer for packet forwarding. A value of false means disable IP
+forwarding, and a value of true means enable IP forwarding.
+.RE
+.PP
+.B option \fBirc-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The IRC server option specifies a list of IRC servers available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The log-server option specifies a list of MIT-LCS UDP log servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The LPR server option specifies a list of RFC 1179 line printer
+servers available to the client. Servers should be listed in order
+of preference.
+.RE
+.PP
+.B option \fBmask-supplier\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies whether or not the client should respond to
+subnet mask requests using ICMP. A value of false indicates that the
+client should not respond. A value of true means that the client should
+respond.
+.RE
+.PP
+.B option \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the maximum size datagram that the client
+should be prepared to reassemble. The minimum legal value is
+576.
+.RE
+.PP
+.B option \fBmerit-dump\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the path-name 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 NVT ASCII character set.
+.RE
+.PP
+.B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+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.
+.RE
+.PP
+.B option \fBnds-context\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The nds-context option specifies the name of the initial Netware
+Directory Service for an NDS client.
+.RE
+.PP
+.B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The nds-servers option specifies a list of IP addresses of NDS servers.
+.RE
+.PP
+.B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The nds-tree-name option specifies NDS tree name that the NDS client
+should use.
+.RE
+.PP
+.B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The NetBIOS datagram distribution server (NBDD) option specifies a
+list of RFC 1001/1002 NBDD servers listed in order of preference.
+.RE
+.PP
+.B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR
+.RS 0.25i
+.PP
+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 netbios-name-servers option.
+.RE
+.PP
+.B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+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:
+.PP
+.TP 5
+.I 1
+B-node: Broadcast - no WINS
+.TP
+.I 2
+P-node: Peer - WINS only
+.TP
+.I 4
+M-node: Mixed - broadcast, then WINS
+.TP
+.I 8
+H-node: Hybrid - WINS, then broadcast
+.RE
+.PP
+.B option \fBnetbios-scope\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The NetBIOS scope option specifies the NetBIOS over TCP/IP scope
+parameter for the client as specified in RFC 1001/1002. See RFC1001,
+RFC1002, and RFC1035 for character-set restrictions.
+.RE
+.PP
+.B option \fBnetinfo-server-address\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The \fBnetinfo-server-address\fR option has not been described in any
+RFC, but has been allocated (and is claimed to be in use) by Apple
+Computers. It's hard to say if the above is the correct format, or
+what clients might be expected to do if values were configured. Use
+at your own risk.
+.RE
+.PP
+.B option \fBnetinfo-server-tag\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBnetinfo-server-tag\fR option has not been described in any
+RFC, but has been allocated (and is claimed to be in use) by Apple
+Computers. It's hard to say if the above is the correct format,
+or what clients might be expected to do if values were configured. Use
+at your own risk.
+.RE
+.PP
+.B option \fBnis-domain\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+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 NVT ASCII character set.
+.RE
+.PP
+.B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+This option specifies a list of IP addresses indicating NIS servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBnisplus-domain\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the name of the client's NIS+ domain. The
+domain is formatted as a character string consisting of characters
+from the NVT ASCII character set.
+.RE
+.PP
+.B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+This option specifies a list of IP addresses indicating NIS+ servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The NNTP server option specifies a list of NNTP servesr available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBnon-local-source-routing\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+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 false means disallow forwarding of such datagrams, and a value of true
+means allow forwarding.
+.RE
+.PP
+.B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+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.
+.RE
+.PP
+.B option \fBnwip-domain\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The name of the NetWare/IP domain that a NetWare/IP client should
+use.
+.RE
+.PP
+.B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+A sequence of suboptions for NetWare/IP clients - see RFC2242 for
+details. Normally this option is set by specifying specific
+NetWare/IP suboptions - see the NETWARE/IP SUBOPTIONS section for more
+information.
+.RE
+.PP
+.B option \fBpath-mtu-aging-timeout\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the timeout (in seconds) to use when aging Path
+MTU values discovered by the mechanism defined in RFC 1191.
+.RE
+.PP
+.B option \fBpath-mtu-plateau-table\fR \fIuint16\fR [\fB,\fR \fIuint16\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+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.
+.RE
+.PP
+.B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies whether or not the client should perform subnet
+mask discovery using ICMP. A value of false indicates that the client
+should not perform mask discovery. A value of true means that the
+client should perform mask discovery.
+.RE
+.PP
+.nf
+.B option \fBpolicy-filter\fR \fIip-address ip-address\fR
+ [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR
+.RE
+.fi
+.RS 0.25i
+.PP
+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 (RFC1122) for further information.
+.RE
+.PP
+.B option \fBpop-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The POP3 server option specifies a list of POP3 servers available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBresource-location-servers\fR \fIip-address\fR
+ [\fB, \fR\fIip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+This option specifies a list of RFC 887 Resource Location
+servers available to the client. Servers should be listed in order
+of preference.
+.RE
+.PP
+.B option \fBroot-path\fR \fItext\fB;\fR\fR
+.RS 0.25i
+.PP
+This option specifies the path-name that contains the client's root
+disk. The path is formatted as a character string consisting of
+characters from the NVT ASCII character set.
+.RE
+.PP
+.B option \fBrouter-discovery\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies whether or not the client should solicit
+routers using the Router Discovery mechanism defined in RFC 1256.
+A value of false indicates that the client should not perform
+router discovery. A value of true means that the client should perform
+router discovery.
+.RE
+.PP
+.B option \fBrouter-solicitation-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the address to which the client should transmit
+router solicitation requests.
+.RE
+.PP
+.B option routers \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The routers option specifies a list of IP addresses for routers on the
+client's subnet. Routers should be listed in order of preference.
+.RE
+.PP
+.B option slp-directory-agent \fIboolean ip-address
+[\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+This option specifies two things: the IP addresses of one or more
+Service Location Protocol Directory Agents, and whether the use of
+these addresses is mandatory. If the initial boolean value is true,
+the SLP agent should just use the IP addresses given. If the value
+is false, the SLP agent may additionally do active or passive
+multicast discovery of SLP agents (see RFC2165 for details).
+.PP
+Please note that in this option and the slp-service-scope option, the
+term "SLP Agent" is being used to refer to a Service Location Protocol
+agent running on a machine that is being configured using the DHCP
+protocol.
+.PP
+Also, please be aware that some companies may refer to SLP as NDS.
+If you have an NDS directory agent whose address you need to
+configure, the slp-directory-agent option should work.
+.RE
+.PP
+.B option slp-service-scope \fIboolean text\fR\fB;\fR
+.RS 0.25i
+.PP
+The Service Location Protocol Service Scope Option specifies two
+things: a list of service scopes for SLP, and whether the use of this
+list is mandatory. If the initial boolean value is true, the SLP
+agent should only use the list of scopes provided in this option;
+otherwise, it may use its own static configuration in preference to
+the list provided in this option.
+.PP
+The text string should be a comma-separated list of scopes that the
+SLP agent should use. It may be omitted, in which case the SLP Agent
+will use the aggregated list of scopes of all directory agents known
+to the SLP agent.
+.RE
+.PP
+.B option \fBsmtp-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The SMTP server option specifies a list of SMTP servers available to
+the client. Servers should be listed in order of preference.
+.RE
+.PP
+.nf
+.B option \fBstatic-routes\fR \fIip-address ip-address\fR
+ [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+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
+.B routers
+option. Also, please note that this option is not intended for
+classless IP routing - it does not include a subnet mask. Since
+classless IP routing is now the most widely deployed routing standard,
+this option is virtually useless, and is not implemented by any of the
+popular DHCP clients, for example the Microsoft DHCP client.
+.RE
+.PP
+.nf
+.B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+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.
+.RE
+.PP
+.B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The StreetTalk server option specifies a list of StreetTalk servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option subnet-mask \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+The 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 dhcpd will use the subnet mask from the subnet declaration
+for the network on which an address is being assigned. However,
+.I any
+subnet-mask option declaration that is in scope for the address being
+assigned will override the subnet mask specified in the subnet
+declaration.
+.RE
+.PP
+.B option \fBsubnet-selection\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+Sent by the client if an address is required in a subnet other than the one
+that would normally be selected (based on the relaying address of the
+connected subnet the request is obtained from). See RFC3011. Note that the
+option number used by this server is 118; this has not always been the
+defined number, and some clients may use a different value. Use of this
+option should be regarded as slightly experimental!
+.RE
+.PP
+This option is not user configurable in the server.
+.PP
+.PP
+.B option \fBswap-server\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+This specifies the IP address of the client's swap server.
+.RE
+.PP
+.B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+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 false indicates that a garbage octet
+should not be sent. A value of true indicates that a garbage octet
+should be sent.
+.RE
+.PP
+.B option \fBtcp-keepalive-interval\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+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.
+.RE
+.PP
+.B option \fBtftp-server-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used to identify a TFTP server and, if supported by the
+client, should have the same effect as the \fBserver-name\fR
+declaration. BOOTP clients are unlikely to support this option.
+Some DHCP clients will support it, and others actually require it.
+.RE
+.PP
+.B option time-offset \fIint32\fR\fB;\fR
+.RS 0.25i
+.PP
+The time-offset option specifies the offset of the client's subnet in
+seconds from Coordinated Universal Time (UTC).
+.RE
+.PP
+.B option time-servers \fIip-address\fR [, \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+The time-server option specifies a list of RFC 868 time servers
+available to the client. Servers should be listed in order of
+preference.
+.RE
+.PP
+.B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+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 false indicates that the client should not attempt to use trailers. A
+value of true means that the client should attempt to use trailers.
+.RE
+.PP
+.B option \fBuap-servers\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies a list of URLs, each pointing to a user
+authentication service that is capable of processing authentication
+requests encapsulated in the User Authentication Protocol (UAP). UAP
+servers can accept either HTTP 1.1 or SSLv3 connections. If the list
+includes a URL that does not contain a port component, the normal
+default port is assumed (i.e., port 80 for http and port 443 for
+https). If the list includes a URL that does not contain a path
+component, the path /uap is assumed. If more than one URL is
+specified in this list, the URLs are separated by spaces.
+.RE
+.PP
+.B option \fBuser-class\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used by some DHCP clients as a way for users to
+specify identifying information to the client. This can be used in a
+similar way to the vendor-class-identifier option, but the value of
+the option is specified by the user, not the vendor. Most recent
+DHCP clients have a way in the user interface to specify the value for
+this identifier, usually as a text string.
+.RE
+.PP
+.B option \fBvendor-class-identifier\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option is used by some DHCP clients to identify the vendor
+type and possibly the configuration of a DHCP client. The information
+is a string of bytes whose contents are specific to the vendor and are
+not specified in a standard. To see what vendor class identifier
+clients are sending, you can write the following in your DHCP server
+configuration file:
+.nf
+.PP
+set vendor-string = option vendor-class-identifier;
+.fi
+.PP
+This will result in all entries in the DHCP server lease database file
+for clients that sent vendor-class-identifier options having a set
+statement that looks something like this:
+.nf
+.PP
+set vendor-string = "SUNW.Ultra-5_10";
+.fi
+.PP
+The vendor-class-identifier option is normally used by the DHCP server
+to determine the options that are returned in the
+.B vendor-encapsulated-options
+option. Please see the VENDOR ENCAPSULATED OPTIONS section later in this
+manual page for further information.
+.RE
+.PP
+.B option \fBvendor-encapsulated-options\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBvendor-encapsulated-options\fR option can contain either a
+single vendor-specific value or one or more vendor-specific
+suboptions. This option is not normally specified in the DHCP server
+configuration file - instead, a vendor class is defined for each
+vendor, vendor class suboptions are defined, values for those
+suboptions are defined, and the DHCP server makes up a response on
+that basis.
+.PP
+Some default behaviours for well-known DHCP client vendors (currently,
+the Microsoft Windows 2000 DHCP client) are configured automatically,
+but otherwise this must be configured manually - see the VENDOR
+ENCAPSULATED OPTIONS section later in this manual page for details.
+.RE
+.PP
+.B option \fBvivso\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBvivso\fR option can contain multiple separate options, one for
+each 32-bit Enterprise ID. Each Enterprise-ID discriminated option then
+contains additional options whose format is defined by the vendor who
+holds that ID. This option is usually not configured manually, but
+rather is configured via intervening option definitions. Please also
+see the VENDOR ENCAPSULATED OPTIONS section later in this manual page
+for details.
+.RE
+.PP
+.B option \fBwww-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+The WWW server option specifies a list of WWW servers available
+to the client. Servers should be listed in order of preference.
+.RE
+.PP
+.B option \fBx-display-manager\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+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.
+.RE
+.SH RELAY AGENT INFORMATION OPTION
+An IETF draft, draft-ietf-dhc-agent-options-11.txt, defines a series
+of encapsulated options that a relay agent can add to a DHCP packet
+when relaying it to the DHCP server. The server can then make
+address allocation decisions (or whatever other decisions it wants)
+based on these options. The server also returns these options in any
+replies it sends through the relay agent, so that the relay agent can
+use the information in these options for delivery or accounting
+purposes.
+.PP
+The current draft defines two options. To reference
+these options in the dhcp server, specify the option space name,
+"agent", followed by a period, followed by the option name. It is
+not normally useful to define values for these options in the server,
+although it is permissible. These options are not supported in the
+client.
+.PP
+.B option \fBagent.circuit-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The circuit-id suboption encodes an agent-local identifier of the
+circuit from which a DHCP client-to-server packet was received. It is
+intended for use by agents in relaying DHCP responses back to the
+proper circuit. The format of this option is currently defined to be
+vendor-dependent, and will probably remain that way, although the
+current draft allows for for the possibility of standardizing the
+format in the future.
+.RE
+.PP
+.B option \fBagent.remote-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The remote-id suboption encodes information about the remote host end
+of a circuit. Examples of what it might contain include caller ID
+information, username information, remote ATM address, cable modem ID,
+and similar things. In principal, the meaning is not well-specified,
+and it should generally be assumed to be an opaque object that is
+administratively guaranteed to be unique to a particular remote end of
+a circuit.
+.RE
+.PP
+.B option \fBagent.DOCSIS-device-class\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+The DOCSIS-device-class suboption is intended to convey information about
+the host endpoint, hardware, and software, that either the host operating
+system or the DHCP server may not otherwise be aware of (but the relay is
+able to distinguish). This is implemented as a 32-bit field (4 octets),
+each bit representing a flag describing the host in one of these ways.
+So far, only bit zero (being the least significant bit) is defined in
+RFC3256. If this bit is set to one, the host is considered a CPE
+Controlled Cable Modem (CCCM). All other bits are reserved.
+.RE
+.PP
+.B option \fBagent.link-selection\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+The link-selection suboption is provided by relay agents to inform servers
+what subnet the client is actually attached to. This is useful in those
+cases where the giaddr (where responses must be sent to the relay agent)
+is not on the same subnet as the client. When this option is present in
+a packet from a relay agent, the DHCP server will use its contents to find
+a subnet declared in configuration, and from here take one step further
+backwards to any shared-network the subnet may be defined within...the
+client may be given any address within that shared network, as normally
+appropriate.
+.RE
+.SH THE CLIENT FQDN SUBOPTIONS
+The Client FQDN option, currently defined in the Internet Draft
+draft-ietf-dhc-fqdn-option-00.txt is not a standard yet, but is in
+sufficiently wide use already that we have implemented it. Due to
+the complexity of the option format, we have implemented it as a
+suboption space rather than a single option. In general this
+option should not be configured by the user - instead it should be
+used as part of an automatic DNS update system.
+.PP
+.B option fqdn.no-client-update \fIflag\fB;
+.RS 0.25i
+.PP
+When the client sends this, if it is true, it means the client will not
+attempt to update its A record. When sent by the server to the client,
+it means that the client \fIshould not\fR update its own A record.
+.RE
+.PP
+.B option fqdn.server-update \fIflag\fB;
+.RS 0.25i
+.PP
+When the client sends this to the server, it is requesting that the server
+update its A record. When sent by the server, it means that the server
+has updated (or is about to update) the client's A record.
+.RE
+.PP
+.B option fqdn.encoded \fIflag\fB;
+.RS 0.25i
+.PP
+If true, this indicates that the domain name included in the option is
+encoded in DNS wire format, rather than as plain ASCII text. The client
+normally sets this to false if it doesn't support DNS wire format in the
+FQDN option. The server should always send back the same value that the
+client sent. When this value is set on the configuration side, it controls
+the format in which the \fIfqdn.fqdn\fR suboption is encoded.
+.RE
+.PP
+.B option fqdn.rcode1 \fIflag\fB;
+.PP
+.B option fqdn.rcode2 \fIflag\fB;
+.RS 0.25i
+.PP
+These options specify the result of the updates of the A and PTR records,
+respectively, and are only sent by the DHCP server to the DHCP client.
+The values of these fields are those defined in the DNS protocol specification.
+.RE
+.PP
+.B option fqdn.fqdn \fItext\fB;
+.RS 0.25i
+.PP
+Specifies the domain name that the client wishes to use. This can be a
+fully-qualified domain name, or a single label. If there is no trailing
+\'.\' character in the name, it is not fully-qualified, and the server will
+generally update that name in some locally-defined domain.
+.RE
+.PP
+.B option fqdn.hostname \fI--never set--\fB;
+.RS 0.25i
+.PP
+This option should never be set, but it can be read back using the \fBoption\fR
+and \fBconfig-option\fR operators in an expression, in which case it returns
+the first label in the \fBfqdn.fqdn\fR suboption - for example, if
+the value of \fBfqdn.fqdn\fR is "foo.example.com.", then \fBfqdn.hostname\fR
+will be "foo".
+.RE
+.PP
+.B option fqdn.domainname \fI--never set--\fB;
+.RS 0.25i
+.PP
+This option should never be set, but it can be read back using the \fBoption\fR
+and \fBconfig-option\fR operators in an expression, in which case it returns
+all labels after the first label in the \fBfqdn.fqdn\fR suboption - for
+example, if the value of \fBfqdn.fqdn\fR is "foo.example.com.",
+then \fBfqdn.hostname\fR will be "example.com.". If this suboption value
+is not set, it means that an unqualified name was sent in the fqdn option,
+or that no fqdn option was sent at all.
+.RE
+.PP
+If you wish to use any of these suboptions, we strongly recommend that you
+refer to the Client FQDN option draft (or standard, when it becomes a
+standard) - the documentation here is sketchy and incomplete in comparison,
+and is just intended for reference by people who already understand the
+Client FQDN option specification.
+.SH THE NETWARE/IP SUBOPTIONS
+RFC2242 defines a set of encapsulated options for Novell NetWare/IP
+clients. To use these options in the dhcp server, specify the option
+space name, "nwip", followed by a period, followed by the option name.
+The following options can be specified:
+.PP
+.B option \fBnwip.nsq-broadcast\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+If true, the client should use the NetWare Nearest Server Query to
+locate a NetWare/IP server. The behaviour of the Novell client if
+this suboption is false, or is not present, is not specified.
+.PP
+.RE
+.B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR
+.RS 0.25i
+.PP
+This suboption specifies a list of up to five IP addresses, each of
+which should be the IP address of a NetWare Domain SAP/RIP server
+(DSS).
+.RE
+.PP
+.B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fR\fB;\fR
+.RS 0.25i
+.PP
+This suboption specifies a list of up to five IP addresses, each of
+which should be the IP address of a Nearest NetWare IP server.
+.RE
+.PP
+.B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+Specifies the number of times that a NetWare/IP client should attempt
+to communicate with a given DSS server at startup.
+.RE
+.PP
+.B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+Specifies the number of seconds that a Netware/IP client should wait
+between retries when attempting to establish communications with a DSS
+server at startup.
+.RE
+.PP
+.B option \fBnwip.nwip-1-1\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+If true, the NetWare/IP client should support NetWare/IP version 1.1
+compatibility. This is only needed if the client will be contacting
+Netware/IP version 1.1 servers.
+.RE
+.PP
+.B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+Specifies the IP address of the Primary Domain SAP/RIP Service server
+(DSS) for this NetWare/IP domain. The NetWare/IP administration
+utility uses this value as Primary DSS server when configuring a
+secondary DSS server.
+.RE
+.SH STANDARD DHCPV6 OPTIONS
+DHCPv6 options differ from DHCPv4 options partially due to using
+16-bit code and length tags, but semantically zero-length options
+are legal in DHCPv6, and multiple options are treated differently.
+Whereas in DHCPv4 multiple options would be concatenated to form one
+option, in DHCPv6 they are expected to be individual instantiations.
+Understandably, many options are not "allowed" to have multiple
+instances in a packet - normally these are options which are digested
+by the DHCP protocol software, and not by users or applications.
+.PP
+.B option \fBdhcp6.client-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the client's DUID identifier. DUIDs are similar
+but different from DHCPv4 client identifiers - there are documented duid
+types:
+.PP
+.I duid-llt
+.PP
+.I duid-en
+.PP
+.I duid-ll
+.PP
+This value should not be configured, but rather is provided by clients
+and treated as an opaque identifier key blob by servers.
+.RE
+.PP
+.B option \fBdhcp6.server-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option specifies the server's DUID identifier. One may use this
+option to configure an opaque binary blob for your server's identifier.
+.RE
+.PP
+.B option \fBdhcp6.ia-na\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The Identity Association for Non-temporary Addresses (ia-na) carries
+assigned addresses that are not temporary addresses for use by the
+DHCPv6 client. This option is produced by the DHCPv6 server software,
+and should not be configured.
+.RE
+.PP
+.B option \fBdhcp6.ia-ta\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The Identity Association for Temporary Addresses (ia-ta) carries
+temporary addresses, which may change upon every renewal. There is
+no support for this in the current DHCPv6 software.
+.RE
+.PP
+.B option \fBdhcp6.ia-addr\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The Identity Association Address option is encapsulated inside ia-na
+or ia-ta options in order to represent addresses associated with those
+IA's. These options are manufactured by the software, so should not
+be configured.
+.RE
+.PP
+.B option \fBdhcp6.oro\fR \fIuint16\fR [ \fB,\fR \fIuint16\fR\fB,\fR ... ]\fB;\fR
+.RS 0.25i
+.PP
+The Option Request Option ("ORO") is the DHCPv6 equivalent of the
+parameter-request-list. Clients supply this option to ask servers
+to reply with options relevant to their needs and use. This option
+must not be directly configured, the request syntax in dhclient.conf (5)
+should be used instead.
+.RE
+.PP
+.B option \fBdhcp6.preference\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBpreference\fR option informs a DHCPv6 client which server is
+\'preferred\' for use on a given subnet. This preference is only
+applied during the initial stages of configuration - once a client
+is bound to an IA, it will remain bound to that IA until it is no
+longer valid or has expired. This value may be configured on the
+server, and is digested by the client software.
+.RE
+.PP
+.B option \fBdhcp6.elapsed-time\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBelapsed-time\fR option is constructed by the DHCPv6 client
+software, and is potentially consumed by intermediaries. This
+option should not be configured.
+.RE
+.PP
+.B option \fBdhcp6.relay-msg\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBrelay-msg\fR option is constructed by intervening DHCPv6
+relay agent software. This option is entirely used by protocol
+software, and is not meant for user configuration.
+.RE
+.PP
+.B option \fBdhcp6.unicast\fR \fIip6-address\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBunicast\fR option is provided by DHCPv6 servers which are
+willing (or prefer) to receive Renew packets from their clients
+by exchanging UDP unicasts with them. Normally, DHCPv6 clients
+will multicast their Renew messages. This may be configured on
+the server, and should be configured as an address the server
+is ready to reply to.
+.RE
+.PP
+.B option \fBdhcp6.status-code\fR \fIstatus-code\fR [ \fIstring\fR ] \fB;\fR
+.RS 0.25i
+.PP
+The \fBstatus-code\fR option is provided by DHCPv6 servers to inform
+clients of error conditions during protocol communication. This option
+is manufactured and digested by protocol software, and should not be
+configured.
+.RE
+.PP
+.B option \fBdhcp6.rapid-commit\fR \fB;\fR
+.RS 0.25i
+.PP
+The \fBrapid-commit\fR option is a zero-length option that clients use
+to indicate their desire to enter into rapid-commit with the server.
+.RE
+.PP
+.B option \fBdhcp6.vendor-opts\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBvendor-opts\fR option is actually an encapsulated sub-option space,
+in which each Vendor-specific Information Option (VSIO) is identified by
+a 32-bit Enterprise-ID number. The encapsulated option spaces within these
+options are defined by the vendors.
+.PP
+To make use of this option, the best way is to examine the section
+titled VENDOR ENCAPSULATED OPTIONS below, in particular the bits about
+the "vsio" option space.
+.RE
+.PP
+.B option \fBdhcp6.interface-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBinterface-id\fR option is manufactured by relay agents, and may
+be used to guide configuration differentiating clients by the interface
+they are remotely attached to. It does not make sense to configure a
+value for this option, but it may make sense to inspect its contents.
+.RE
+.PP
+.B option \fBdhcp6.reconf-msg\fR \fIdhcpv6-message\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBreconf-msg\fR option is manufactured by servers, and sent to
+clients in Reconfigure messages to inform them of what message
+the client should Reconfigure using. There is no support for
+DHCPv6 Reconfigure extensions, and this option is documented
+informationally only.
+.RE
+.PP
+.B option \fBdhcp6.reconf-accept ;\fR
+.RS 0.25i
+.PP
+The \fBreconf-accept\fR option is included by DHCPv6 clients that
+support the Reconfigure extentions, advertising that they will
+respond if the server were to ask them to Reconfigure. There is
+no support for DHCPv6 Reconfigure extensions, and this option is
+documented informationally only.
+.RE
+.PP
+.B option \fBdhcp6.sip-servers-names\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBsip-servers-names\fR option allows SIP clients to locate a
+local SIP server that is to be used for all outbound SIP requests, a
+so-called"outbound proxy server." If you wish to use manually entered
+IPv6 addresses instead, please see the \fBsip-servers-addresses\fR option
+below.
+.RE
+.PP
+.B option
+.B dhcp6.sip-servers-addresses
+.I ip6-address \fR[\fB,\fR
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBsip-servers-addresses\fR option allows SIP clients to locate
+a local SIP server that is to be used for all outbound SIP requests,
+a so-called "outbound proxy servers." If you wish to use domain names
+rather than IPv6 addresses, please see the \fBsip-servers-names\fR option
+above.
+.RE
+.PP
+.B option
+.B dhcp6.name-servers
+.I ip6-address \fR[\fB,\fR
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBname-servers\fR option instructs clients about locally available
+recursive DNS servers. It is easiest to describe this as the "nameserver"
+line in /etc/resolv.conf.
+.RE
+.PP
+.B option \fBdhcp6.domain-search\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBdomain-search\fR option specifies the client's domain search path
+to be applied to recursive DNS queries. It is easiest to describe this as
+the "search" line in /etc/resolv.conf.
+.RE
+.PP
+.B option \fBdhcp6.ia-pd\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBia-pd\fR option is manufactured by clients and servers to create a
+Prefix Delegation binding - to delegate an IPv6 prefix to the client. It is
+not directly edited in dhcpd.conf(5) or dhclient.conf(5), but rather is
+manufactured and consumed by the software.
+.RE
+.PP
+.B option \fBdhcp6.ia-prefix\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBia-prefix\fR option is placed inside \fBia-pd\fR options in order
+to identify the prefix(es) allocated to the client. It is not directly
+edited in dhcpd.conf(5) or dhclient.conf(5), but rather is
+manufactured and consumed by the software.
+.RE
+.PP
+.B option
+.B dhcp6.nis-servers
+.I ip6-address \fR[\fB,
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBnis-servers\fR option identifies, in order, NIS servers available
+to the client.
+.RE
+.PP
+.B option
+.B dhcp6.nisp-servers
+.I ip6-address \fR[\fB,
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBnisp-servers\fR option identifies, in order, NIS+ servers available
+to the client.
+.RE
+.PP
+.B option \fBnis-domain-name\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBnis-domain-name\fR option specifies the NIS domain name the client is
+expected to use, and is related to the \fBnis-servers\fR option.
+.RE
+.PP
+.B option \fBdhcp6.nis-domain-name\fR \fIdomain-name\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBdhcp6.nis-domain-name\fR option specfies NIS domain name the
+client is expected to use, and is related to \fBdhcp6.nis-servers\fR option.
+.RE
+.PP
+.B option \fBnisp-domain-name\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBnisp-domain-name\fR option specifies the NIS+ domain name the client
+is expected to use, and is related to the \fBnisp-servers\fR option.
+.RE
+.PP
+.B option \fBdhcp6.nisp-domain-name\fR \fIdomain-name\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBdhcp6.nis-domain-name\fR option specfies NIS+ domain name the
+client is expected to use, and is related to \fBdhcp6.nisp-servers\fR option.
+.RE
+.PP
+.B option
+.B dhcp6.sntp-servers
+.I ip6-address \fR[\fB,
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBsntp-servers\fR option specifies a list of local SNTP servers
+available for the client to synchronize their clocks.
+.RE
+.PP
+.B option \fBdhcp6.info-refresh-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBinfo-refresh-time\fR option gives DHCPv6 clients using
+Information-request messages a hint as to how long they should between
+refreshing the information they were given. Note that this option will
+only be delivered to the client, and be likely to affect the client's
+behaviour, if the client requested the option.
+.RE
+.PP
+.B option \fBdhcp6.bcms-server-d\fR \fIdomain-list\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBbcms-server-d\fR option contains the domain names of local BCMS
+(Broadcast and Multicast Control Services) controllers which the client
+may use.
+.RE
+.PP
+.B option
+.B dhcp6.bcms-server-a
+.I ip6-address \fR[\fB,
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBbcms-server-a\fR option contains the IPv6 addresses of local BCMS
+(Broadcast and Multicast Control Services) controllers which the client
+may use.
+.RE
+.PP
+.B option \fBdhcp6.remote-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBremote-id\fR option is constructed by relay agents, to inform the
+server of details pertaining to what the relay knows about the client (such
+as what port it is attached to, and so forth). The contents of this option
+have some vendor-specific structure (similar to VSIO), but we have chosen
+to treat this option as an opaque field.
+.RE
+.PP
+.B option \fBdhcp6.subscriber-id\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBsubscriber-id\fR option is an opaque field provided by the relay agent,
+which provides additional information about the subscriber in question. The
+exact contents of this option depend upon the vendor and/or the operator's
+configuration of the remote device, and as such is an opaque field.
+.RE
+.PP
+.B option \fBdhcp6.fqdn\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBfqdn\fR option is normally constructed by the client or server,
+and negotiates the client's Fully Qualified Domain Name, as well as which
+party is responsible for Dynamic DNS Updates. See the section on the
+Client FQDN SubOptions for full details (the DHCPv4 and DHCPv6 FQDN options
+use the same "fqdn." encapsulated space, so are in all ways identical).
+.RE
+.PP
+.B option \fBdhcp6.lq-query\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBlq-query\fR option is used internally by for lease query.
+.RE
+.PP
+.B option \fBdhcp6.client-data\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBclient-data\fR option is used internally by for lease query.
+.RE
+.PP
+.B option \fBdhcp6.clt-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBclt-time\fR option is used internally by for lease query.
+.RE
+.PP
+.B option \fBdhcp6.lq-relay-data\fR \fIip6-address string\fR\fB;\fR
+.RS 0.25i
+.PP
+The \fBlq-relay-data\fR option is used internally by for lease query.
+.RE
+.PP
+.B option
+.B dhcp6.lq-client-link
+.I ip6-address \fR[\fB,\fR
+.I ip6-address \fR... ]
+.B ;
+.RS 0.25i
+.PP
+The \fBlq-client-link\fR option is used internally by for lease query.
+.RE
+.PP
+.RE
+.SH DEFINING NEW OPTIONS
+The Internet Systems Consortium DHCP client and server provide the
+capability to define new options. Each DHCP option has a name, a
+code, and a structure. The name is used by you to refer to the
+option. The code is a number, used by the DHCP server and client to
+refer to an option. The structure describes what the contents of an
+option looks like.
+.PP
+To define a new option, you need to choose a name for it that is not
+in use for some other option - for example, you can't use "host-name"
+because the DHCP protocol already defines a host-name option, which is
+documented earlier in this manual page. If an option name doesn't
+appear in this manual page, you can use it, but it's probably a good
+idea to put some kind of unique string at the beginning so you can be
+sure that future options don't take your name. For example, you
+might define an option, "local-host-name", feeling some confidence
+that no official DHCP option name will ever start with "local".
+.PP
+Once you have chosen a name, you must choose a code. All codes between
+224 and 254 are reserved as \'site-local\' DHCP options, so you can pick
+any one of these for your site (not for your product/application). In
+RFC3942, site-local space was moved from starting at 128 to starting at
+224. In practice, some vendors have interpreted the protocol rather
+loosely and have used option code values greater than 128 themselves.
+There's no real way to avoid this problem, and it was thought to be
+unlikely to cause too much trouble in practice. If you come across
+a vendor-documented option code in either the new or old site-local
+spaces, please contact your vendor and inform them about rfc3942.
+.PP
+The structure of an option is simply the format in which the option
+data appears. The ISC DHCP server currently supports a few simple
+types, like integers, booleans, strings and IP addresses, and it also
+supports the ability to define arrays of single types or arrays of
+fixed sequences of types.
+.PP
+New options are declared as follows:
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.I definition
+.B ;
+.PP
+The values of
+.I new-name
+and
+.I new-code
+should be the name you have chosen for the new option and the code you
+have chosen. The
+.I definition
+should be the definition of the structure of the option.
+.PP
+The following simple option type definitions are supported:
+.PP
+.B BOOLEAN
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B boolean
+.B ;
+.PP
+An option of type boolean is a flag with a value of either on or off
+(or true or false). So an example use of the boolean type would be:
+.nf
+
+option use-zephyr code 180 = boolean;
+option use-zephyr on;
+
+.fi
+.B INTEGER
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.I sign
+.B integer
+.I width
+.B ;
+.PP
+The \fIsign\fR token should either be blank, \fIunsigned\fR
+or \fIsigned\fR. The width can be either 8, 16 or 32, and refers to
+the number of bits in the integer. So for example, the following two
+lines show a definition of the sql-connection-max option and its use:
+.nf
+
+option sql-connection-max code 192 = unsigned integer 16;
+option sql-connection-max 1536;
+
+.fi
+.B IP-ADDRESS
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B ip-address
+.B ;
+.PP
+An option whose structure is an IP address can be expressed either as
+a domain name or as a dotted quad. So the following is an example use
+of the ip-address type:
+.nf
+
+option sql-server-address code 193 = ip-address;
+option sql-server-address sql.example.com;
+
+.fi
+.B IP6-ADDRESS
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B ip6-address
+.B ;
+.PP
+An option whose structure is an IPv6 address must be expressed as
+a valid IPv6 address. The following is an example use of the
+ip6-address type:
+.nf
+
+option dhcp6.some-server code 1234 = array of ip6-address;
+option dhcp6.some-server 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2;
+
+.fi
+.PP
+.B TEXT
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B text
+.B ;
+.PP
+An option whose type is text will encode an ASCII text string. For
+example:
+.nf
+
+option sql-default-connection-name code 194 = text;
+option sql-default-connection-name "PRODZA";
+
+.fi
+.PP
+.B DATA STRING
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B string
+.B ;
+.PP
+An option whose type is a data string is essentially just a collection
+of bytes, and can be specified either as quoted text, like the text
+type, or as a list of hexadecimal contents separated by colons whose
+values must be between 0 and FF. For example:
+.nf
+
+option sql-identification-token code 195 = string;
+option sql-identification-token 17:23:19:a6:42:ea:99:7c:22;
+
+.fi
+.PP
+.B DOMAIN-LIST
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B domain-list
+.B [compressed]
+.B ;
+.PP
+An option whose type is \fBdomain-list\fR is an RFC1035 formatted (on the
+wire, "DNS Format") list of domain names, separated by root labels. The
+optional \fBcompressed\fR keyword indicates if the option should be
+compressed relative to the start of the option contents (not the packet
+contents).
+.PP
+When in doubt, omit the \fBcompressed\fR keyword. When the software recieves
+an option that is compressed and the \fBcompressed\fR keyword is omitted, it
+will still decompress the option (relative to the option contents field). The
+keyword only controls whether or not transmitted packets are compressed.
+.PP
+Note that when
+.B domain-list
+formatted options are output as environment variables to
+.B dhclient-script(8),
+the standard DNS \-escape mechanism is used: they are decimal. This is
+appropriate for direct use in eg /etc/resolv.conf.
+.nf
+
+.fi
+.PP
+.B ENCAPSULATION
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B encapsulate
+.I identifier
+.B ;
+.PP
+An option whose type is \fBencapsulate\fR will encapsulate the
+contents of the option space specified in \fIidentifier\fR. Examples
+of encapsulated options in the DHCP protocol as it currently exists
+include the vendor-encapsulated-options option, the netware-suboptions
+option and the relay-agent-information option.
+.nf
+
+option space local;
+option local.demo code 1 = text;
+option local-encapsulation code 197 = encapsulate local;
+option local.demo "demo";
+
+.fi
+.PP
+.B ARRAYS
+.PP
+Options can contain arrays of any of the above types except for the
+text and data string types, which aren't currently supported in
+arrays. An example of an array definition is as follows:
+.nf
+
+option kerberos-servers code 200 = array of ip-address;
+option kerberos-servers 10.20.10.1, 10.20.11.1;
+
+.fi
+.B RECORDS
+.PP
+Options can also contain data structures consisting of a sequence of
+data types, which is sometimes called a record type. For example:
+.nf
+
+option contrived-001 code 201 = { boolean, integer 32, text };
+option contrived-001 on 1772 "contrivance";
+
+.fi
+It's also possible to have options that are arrays of records, for
+example:
+.nf
+
+option new-static-routes code 201 = array of {
+ ip-address, ip-address, ip-address, integer 8 };
+option static-routes
+ 10.0.0.0 255.255.255.0 net-0-rtr.example.com 1,
+ 10.0.1.0 255.255.255.0 net-1-rtr.example.com 1,
+ 10.2.0.0 255.255.224.0 net-2-0-rtr.example.com 3;
+
+.fi
+.SH VENDOR ENCAPSULATED OPTIONS
+The DHCP protocol defines the \fBvendor-encapsulated-options\fR
+option, which allows vendors to define their own options that will be
+sent encapsulated in a standard DHCP option. It also defines
+the \fBVendor Identified Vendor Sub Options\fR option ("VIVSO"), and the
+DHCPv6 protocol defines the \fBVendor-specific Information Option\fR
+("VSIO"). The format of all of these options is usually internally a
+string of options, similarly to other normal DHCP options. The VIVSO
+and VSIO options differ in that that they contain options that correspond
+to vendor Enterprise-ID numbers (assigned by IANA), which then contain
+options according to each Vendor's specifications. You will need to refer
+to your vendor's documentation in order to form options to their
+specification.
+.PP
+The value of these options can be set in one of two ways. The first
+way is to simply specify the data directly, using a text string or a
+colon-separated list of hexadecimal values. For help in forming these
+strings, please refer to \fBRFC2132\fR for the DHCPv4 \fBVendor Specific
+Information Option\fR, \fBRFC3925\fR for the DHCPv4 \fBVendor Identified Vendor
+Sub Options\fR, or \fBRFC3315\fR for the DHCPv6 \fBVendor-specific Information
+Option\fR. For example:
+.PP
+.nf
+option vendor-encapsulated-options
+ 2:4:
+ AC:11:41:1:
+ 3:12:
+ 73:75:6e:64:68:63:70:2d:73:65:72:76:65:72:31:37:2d:31:
+ 4:12:
+ 2f:65:78:70:6f:72:74:2f:72:6f:6f:74:2f:69:38:36:70:63;
+option vivso
+ 00:00:09:bf:0E:
+ 01:0c:
+ 48:65:6c:6c:6f:20:77:6f:72:6c:64:21;
+option dhcp6.vendor-opts
+ 00:00:09:bf:
+ 00:01:00:0c:
+ 48:65:6c:6c:6f:20:77:6f:72:6c:64:21;
+.fi
+.PP
+The second way of setting the value of these options is to have the DHCP
+server generate a vendor-specific option buffer. To do this, you
+must do four things: define an option space, define some options in
+that option space, provide values for them, and specify that that
+option space should be used to generate the relevant option.
+.PP
+To define a new option space in which vendor options can be stored,
+use the \fRoption space\fP statement:
+.PP
+.B option
+.B space
+.I name
+.B [ [ code width
+.I number
+.B ] [ length width
+.I number
+.B ] [ hash size
+.I number
+.B ] ] ;
+.PP
+Where the numbers following \fBcode width\fR, \fBlength width\fR,
+and \fBhash size\fR respectively identify the number of bytes used to
+describe option codes, option lengths, and the size in buckets of the
+hash tables to hold options in this space (most DHCPv4 option spaces
+use 1 byte codes and lengths, which is the default, whereas most
+DHCPv6 option spaces use 2 byte codes and lengths).
+.PP
+The code and length widths are used in DHCP protocol - you must configure
+these numbers to match the applicable option space you are configuring.
+They each default to 1. Valid values for code widths are 1, 2 or 4.
+Valid values for length widths are 0, 1 or 2. Most DHCPv4 option spaces
+use 1 byte codes and lengths, which is the default, whereas most DHCPv6
+option spaces use 2 byte codes and lengths. A zero-byte length produces
+options similar to the DHCPv6 Vendor-specific Information Option - but
+not their contents!
+.PP
+The hash size defaults depend upon the \fBcode width\fR selected, and
+may be 254 or 1009. Valid values range between 1 and 65535. Note
+that the higher you configure this value, the more memory will be used. It
+is considered good practice to configure a value that is slightly larger
+than the estimated number of options you plan to configure within the
+space. Previous versions of ISC DHCP (up to and including DHCP 3.0.*),
+this value was fixed at 9973.
+.PP
+The name can then be used in option definitions, as described earlier in
+this document. For example:
+.nf
+
+option space SUNW code width 1 length width 1 hash size 3;
+option SUNW.server-address code 2 = ip-address;
+option SUNW.server-name code 3 = text;
+option SUNW.root-path code 4 = text;
+
+option space ISC code width 1 length width 1 hash size 3;
+option ISC.sample code 1 = text;
+option vendor.ISC code 2495 = encapsulate vivso-sample;
+option vendor-class.ISC code 2495 = text;
+
+option ISC.sample "configuration text here";
+option vendor-class.ISC "vendor class here";
+
+option space docsis code width 2 length width 2 hash size 17;
+option docsis.tftp-servers code 32 = array of ip6-address;
+option docsis.cablelabs-configuration-file code 33 = text;
+option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
+option docsis.device-id code 36 = string;
+option docsis.time-servers code 37 = array of ip6-address;
+option docsis.time-offset code 38 = signed integer 32;
+option vsio.docsis code 4491 = encapsulate docsis;
+
+.fi
+Once you have defined an option space and the format of some options,
+you can set up scopes that define values for those options, and you
+can say when to use them. For example, suppose you want to handle
+two different classes of clients. Using the option space definition
+shown in the previous example, you can send different option values to
+different clients based on the vendor-class-identifier option that the
+clients send, as follows:
+.PP
+.nf
+class "vendor-classes" {
+ match option vendor-class-identifier;
+}
+
+subclass "vendor-classes" "SUNW.Ultra-5_10" {
+ vendor-option-space SUNW;
+ option SUNW.root-path "/export/root/sparc";
+}
+
+subclass "vendor-classes" "SUNW.i86pc" {
+ vendor-option-space SUNW;
+ option SUNW.root-path "/export/root/i86pc";
+}
+
+option SUNW.server-address 172.17.65.1;
+option SUNW.server-name "sundhcp-server17-1";
+
+option vivso-sample.sample "Hello world!";
+
+option docsis.tftp-servers ::1;
+
+.fi
+.PP
+As you can see in the preceding example, regular scoping rules apply,
+so you can define values that are global in the global scope, and only
+define values that are specific to a particular class in the local
+scope. The \fBvendor-option-space\fR declaration tells the DHCP
+server to use options in the SUNW option space to construct the DHCPv4
+.B vendor-encapsulated-options
+option. This is a limitation of that option - the DHCPv4 VIVSO and the
+DHCPv6 VSIO options can have multiple vendor definitions all at once (even
+transmitted to the same client), so it is not necessary to configure this.
+.SH SEE ALSO
+dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8),
+dhclient(8), RFC2132, RFC2131, RFC3046, RFC3315.
+.SH AUTHOR
+The Internet Systems Consortium DHCP Distribution was written by Ted
+Lemon under a contract with Vixie Labs. Funding for
+this project was provided through Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at
+.B https://www.isc.org.
diff --git a/common/discover.c b/common/discover.c
new file mode 100644
index 0000000..1d84219
--- /dev/null
+++ b/common/discover.c
@@ -0,0 +1,1867 @@
+/* discover.c
+
+ Find and identify the network interfaces. */
+
+/*
+ * Copyright (c) 2004-2009,2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#define BSD_COMP /* needed on Solaris for SIOCGLIFNUM */
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#ifdef HAVE_NET_IF6_H
+# include <net/if6.h>
+#endif
+
+struct interface_info *interfaces, *dummy_interfaces, *fallback_interface;
+int interfaces_invalidated;
+int quiet_interface_discovery;
+u_int16_t local_port;
+u_int16_t remote_port;
+int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *);
+int (*dhcp_interface_discovery_hook) (struct interface_info *);
+isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
+int (*dhcp_interface_shutdown_hook) (struct interface_info *);
+
+struct in_addr limited_broadcast;
+
+int local_family = AF_INET;
+struct in_addr local_address;
+
+#ifdef DHCPv6
+struct in6_addr local_address6;
+#endif /* DHCPv6 */
+
+void (*bootp_packet_handler) (struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ unsigned int,
+ struct iaddr, struct hardware *);
+
+#ifdef DHCPv6
+void (*dhcpv6_packet_handler)(struct interface_info *,
+ const char *, int,
+ int, const struct iaddr *,
+ isc_boolean_t);
+#endif /* DHCPv6 */
+
+
+omapi_object_type_t *dhcp_type_interface;
+#if defined (TRACING)
+trace_type_t *interface_trace;
+trace_type_t *inpacket_trace;
+trace_type_t *outpacket_trace;
+#endif
+struct interface_info **interface_vector;
+int interface_count;
+int interface_max;
+
+OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface)
+
+isc_result_t interface_setup ()
+{
+ isc_result_t status;
+ status = omapi_object_type_register (&dhcp_type_interface,
+ "interface",
+ dhcp_interface_set_value,
+ dhcp_interface_get_value,
+ dhcp_interface_destroy,
+ dhcp_interface_signal_handler,
+ dhcp_interface_stuff_values,
+ dhcp_interface_lookup,
+ dhcp_interface_create,
+ dhcp_interface_remove,
+ 0, 0, 0,
+ sizeof (struct interface_info),
+ interface_initialize, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register interface object type: %s",
+ isc_result_totext (status));
+
+ return status;
+}
+
+#if defined (TRACING)
+void interface_trace_setup ()
+{
+ interface_trace = trace_type_register ("interface", (void *)0,
+ trace_interface_input,
+ trace_interface_stop, MDL);
+ inpacket_trace = trace_type_register ("inpacket", (void *)0,
+ trace_inpacket_input,
+ trace_inpacket_stop, MDL);
+ outpacket_trace = trace_type_register ("outpacket", (void *)0,
+ trace_outpacket_input,
+ trace_outpacket_stop, MDL);
+}
+#endif
+
+isc_result_t interface_initialize (omapi_object_t *ipo,
+ const char *file, int line)
+{
+ struct interface_info *ip = (struct interface_info *)ipo;
+ ip -> rfdesc = ip -> wfdesc = -1;
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Scanning for Interfaces
+ * -----------------------
+ *
+ * To find interfaces, we create an iterator that abstracts out most
+ * of the platform specifics. Use is fairly straightforward:
+ *
+ * - begin_iface_scan() starts the process.
+ * - Use next_iface() until it returns 0.
+ * - end_iface_scan() performs any necessary cleanup.
+ *
+ * We check for errors on each call to next_iface(), which returns a
+ * description of the error as a string if any occurs.
+ *
+ * We currently have code for Solaris and Linux. Other systems need
+ * to have code written.
+ *
+ * NOTE: the long-term goal is to use the interface code from BIND 9.
+ */
+
+#if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && defined(SIOCGLIFFLAGS)
+
+/* HP/UX doesn't define struct lifconf, instead they define struct
+ * if_laddrconf. Similarly, 'struct lifreq' and 'struct lifaddrreq'.
+ */
+#ifdef ISC_PLATFORM_HAVEIF_LADDRCONF
+# define lifc_len iflc_len
+# define lifc_buf iflc_buf
+# define lifc_req iflc_req
+# define LIFCONF if_laddrconf
+#else
+# define ISC_HAVE_LIFC_FAMILY 1
+# define ISC_HAVE_LIFC_FLAGS 1
+# define LIFCONF lifconf
+#endif
+
+#ifdef ISC_PLATFORM_HAVEIF_LADDRREQ
+# define lifr_addr iflr_addr
+# define lifr_name iflr_name
+# define lifr_dstaddr iflr_dstaddr
+# define lifr_flags iflr_flags
+# define sockaddr_storage sockaddr_ext
+# define ss_family sa_family
+# define LIFREQ if_laddrreq
+#else
+# define LIFREQ lifreq
+#endif
+
+#ifndef IF_NAMESIZE
+# if defined(LIFNAMSIZ)
+# define IF_NAMESIZE LIFNAMSIZ
+# elif defined(IFNAMSIZ)
+# define IF_NAMESIZE IFNAMSIZ
+# else
+# define IF_NAMESIZE 16
+# endif
+#endif
+#elif !defined(__linux) && !defined(HAVE_IFADDRS_H)
+# define SIOCGLIFCONF SIOCGIFCONF
+# define SIOCGLIFFLAGS SIOCGIFFLAGS
+# define LIFREQ ifreq
+# define LIFCONF ifconf
+# define lifr_name ifr_name
+# define lifr_addr ifr_addr
+# define lifr_flags ifr_flags
+# define lifc_len ifc_len
+# define lifc_buf ifc_buf
+# define lifc_req ifc_req
+#ifdef _AIX
+# define ss_family __ss_family
+#endif
+#endif
+
+#if defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
+/*
+ * Solaris support
+ * ---------------
+ *
+ * The SIOCGLIFCONF ioctl() are the extension that you need to use
+ * on Solaris to get information about IPv6 addresses.
+ *
+ * Solaris' extended interface is documented in the if_tcp man page.
+ */
+
+/*
+ * Structure holding state about the scan.
+ */
+struct iface_conf_list {
+ int sock; /* file descriptor used to get information */
+ int num; /* total number of interfaces */
+ struct LIFCONF conf; /* structure used to get information */
+ int next; /* next interface to retrieve when iterating */
+};
+
+/*
+ * Structure used to return information about a specific interface.
+ */
+struct iface_info {
+ char name[IF_NAMESIZE+1]; /* name of the interface, e.g. "bge0" */
+ struct sockaddr_storage addr; /* address information */
+ isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
+};
+
+/*
+ * Start a scan of interfaces.
+ *
+ * The iface_conf_list structure maintains state for this process.
+ */
+int
+begin_iface_scan(struct iface_conf_list *ifaces) {
+#ifdef ISC_PLATFORM_HAVELIFNUM
+ struct lifnum lifnum;
+#else
+ int lifnum;
+#endif
+
+ ifaces->sock = socket(local_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (ifaces->sock < 0) {
+ log_error("Error creating socket to list interfaces; %m");
+ return 0;
+ }
+
+ memset(&lifnum, 0, sizeof(lifnum));
+#ifdef ISC_PLATFORM_HAVELIFNUM
+ lifnum.lifn_family = AF_UNSPEC;
+#endif
+#ifdef SIOCGLIFNUM
+ if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) {
+ log_error("Error finding total number of interfaces; %m");
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ return 0;
+ }
+
+#ifdef ISC_PLATFORM_HAVELIFNUM
+ ifaces->num = lifnum.lifn_count;
+#else
+ ifaces->num = lifnum;
+#endif
+#else
+ ifaces->num = 64;
+#endif /* SIOCGLIFNUM */
+
+ memset(&ifaces->conf, 0, sizeof(ifaces->conf));
+#ifdef ISC_HAVE_LIFC_FAMILY
+ ifaces->conf.lifc_family = AF_UNSPEC;
+#endif
+ ifaces->conf.lifc_len = ifaces->num * sizeof(struct LIFREQ);
+ ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL);
+ if (ifaces->conf.lifc_buf == NULL) {
+ log_fatal("Out of memory getting interface list.");
+ }
+
+ if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) {
+ log_error("Error getting interfaces configuration list; %m");
+ dfree(ifaces->conf.lifc_buf, MDL);
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ return 0;
+ }
+
+ ifaces->next = 0;
+
+ return 1;
+}
+
+/*
+ * Retrieve the next interface.
+ *
+ * Returns information in the info structure.
+ * Sets err to 1 if there is an error, otherwise 0.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ struct LIFREQ *p;
+ struct LIFREQ tmp;
+ isc_boolean_t foundif;
+#if defined(sun) || defined(__linux)
+ /* Pointer used to remove interface aliases. */
+ char *s;
+#endif
+
+ do {
+ foundif = ISC_FALSE;
+
+ if (ifaces->next >= ifaces->num) {
+ *err = 0;
+ return 0;
+ }
+
+ p = ifaces->conf.lifc_req;
+ p += ifaces->next;
+
+ if (strlen(p->lifr_name) >= sizeof(info->name)) {
+ *err = 1;
+ log_error("Interface name '%s' too long", p->lifr_name);
+ return 0;
+ }
+
+ /* Reject if interface address family does not match */
+ if (p->lifr_addr.ss_family != local_family) {
+ ifaces->next++;
+ continue;
+ }
+
+ strcpy(info->name, p->lifr_name);
+ memset(&info->addr, 0, sizeof(info->addr));
+ memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr));
+
+#if defined(sun) || defined(__linux)
+ /* interface aliases look like "eth0:1" or "wlan1:3" */
+ s = strchr(info->name, ':');
+ if (s != NULL) {
+ *s = '\0';
+ }
+#endif /* defined(sun) || defined(__linux) */
+
+ foundif = ISC_TRUE;
+ } while ((foundif == ISC_FALSE) ||
+ (strncmp(info->name, "dummy", 5) == 0));
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.lifr_name, info->name);
+ if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m",
+ p->lifr_name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.lifr_flags;
+
+ ifaces->next++;
+ *err = 0;
+ return 1;
+}
+
+/*
+ * End scan of interfaces.
+ */
+void
+end_iface_scan(struct iface_conf_list *ifaces) {
+ dfree(ifaces->conf.lifc_buf, MDL);
+ close(ifaces->sock);
+ ifaces->sock = -1;
+}
+
+#elif __linux /* !HAVE_SIOCGLIFCONF */
+/*
+ * Linux support
+ * -------------
+ *
+ * In Linux, we use the /proc pseudo-filesystem to get information
+ * about interfaces, along with selected ioctl() calls.
+ *
+ * Linux low level access is documented in the netdevice man page.
+ */
+
+/*
+ * Structure holding state about the scan.
+ */
+struct iface_conf_list {
+ int sock; /* file descriptor used to get information */
+ FILE *fp; /* input from /proc/net/dev */
+#ifdef DHCPv6
+ FILE *fp6; /* input from /proc/net/if_inet6 */
+#endif
+};
+
+/*
+ * Structure used to return information about a specific interface.
+ */
+struct iface_info {
+ char name[IFNAMSIZ]; /* name of the interface, e.g. "eth0" */
+ struct sockaddr_storage addr; /* address information */
+ isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
+};
+
+/*
+ * Start a scan of interfaces.
+ *
+ * The iface_conf_list structure maintains state for this process.
+ */
+int
+begin_iface_scan(struct iface_conf_list *ifaces) {
+ char buf[256];
+ int len;
+ int i;
+
+ ifaces->fp = fopen("/proc/net/dev", "r");
+ if (ifaces->fp == NULL) {
+ log_error("Error opening '/proc/net/dev' to list interfaces");
+ return 0;
+ }
+
+ /*
+ * The first 2 lines are header information, so read and ignore them.
+ */
+ for (i=0; i<2; i++) {
+ if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
+ log_error("Error reading headers from '/proc/net/dev'");
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad header line in '/proc/net/dev'");
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ }
+
+ ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (ifaces->sock < 0) {
+ log_error("Error creating socket to list interfaces; %m");
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+
+#ifdef DHCPv6
+ if (local_family == AF_INET6) {
+ ifaces->fp6 = fopen("/proc/net/if_inet6", "r");
+ if (ifaces->fp6 == NULL) {
+ log_error("Error opening '/proc/net/if_inet6' to "
+ "list IPv6 interfaces; %m");
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ }
+#endif
+
+ return 1;
+}
+
+/*
+ * Read our IPv4 interfaces from /proc/net/dev.
+ *
+ * The file looks something like this:
+ *
+ * Inter-| Receive ...
+ * face |bytes packets errs drop fifo frame ...
+ * lo: 1580562 4207 0 0 0 0 ...
+ * eth0: 0 0 0 0 0 0 ...
+ * eth1:1801552440 37895 0 14 0 ...
+ *
+ * We only care about the interface name, which is at the start of
+ * each line.
+ *
+ * We use an ioctl() to get the address and flags for each interface.
+ */
+static int
+next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ char buf[256];
+ int len;
+ char *p;
+ char *name;
+ struct ifreq tmp;
+
+ /*
+ * Loop exits when we find an interface that has an address, or
+ * when we run out of interfaces.
+ */
+ for (;;) {
+ do {
+ /*
+ * Read the next line in the file.
+ */
+ if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
+ if (ferror(ifaces->fp)) {
+ *err = 1;
+ log_error("Error reading interface "
+ "information");
+ } else {
+ *err = 0;
+ }
+ return 0;
+ }
+
+ /*
+ * Make sure the line is a nice,
+ * newline-terminated line.
+ */
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad line reading interface "
+ "information");
+ *err = 1;
+ return 0;
+ }
+
+ /*
+ * Figure out our name.
+ */
+ p = strrchr(buf, ':');
+ if (p == NULL) {
+ log_error("Bad line reading interface "
+ "information (no colon)");
+ *err = 1;
+ return 0;
+ }
+ *p = '\0';
+ name = buf;
+ while (isspace(*name)) {
+ name++;
+ }
+
+ /*
+ * Copy our name into our interface structure.
+ */
+ len = p - name;
+ if (len >= sizeof(info->name)) {
+ *err = 1;
+ log_error("Interface name '%s' too long", name);
+ return 0;
+ }
+ strcpy(info->name, name);
+
+#ifdef ALIAS_NAMED_PERMUTED
+ /* interface aliases look like "eth0:1" or "wlan1:3" */
+ s = strchr(info->name, ':');
+ if (s != NULL) {
+ *s = '\0';
+ }
+#endif
+
+#ifdef SKIP_DUMMY_INTERFACES
+ } while (strncmp(info->name, "dummy", 5) == 0);
+#else
+ } while (0);
+#endif
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) {
+ if (errno == EADDRNOTAVAIL) {
+ continue;
+ }
+ log_error("Error getting interface address "
+ "for '%s'; %m", name);
+ *err = 1;
+ return 0;
+ }
+ memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr));
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m",
+ name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.ifr_flags;
+
+ *err = 0;
+ return 1;
+ }
+}
+
+#ifdef DHCPv6
+/*
+ * Read our IPv6 interfaces from /proc/net/if_inet6.
+ *
+ * The file looks something like this:
+ *
+ * fe80000000000000025056fffec00008 05 40 20 80 vmnet8
+ * 00000000000000000000000000000001 01 80 10 80 lo
+ * fe80000000000000025056fffec00001 06 40 20 80 vmnet1
+ * 200108881936000202166ffffe497d9b 03 40 00 00 eth1
+ * fe8000000000000002166ffffe497d9b 03 40 20 80 eth1
+ *
+ * We get IPv6 address from the start, the interface name from the end,
+ * and ioctl() to get flags.
+ */
+static int
+next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ char buf[256];
+ int len;
+ char *p;
+ char *name;
+ int i;
+ struct sockaddr_in6 addr;
+ struct ifreq tmp;
+
+ do {
+ /*
+ * Read the next line in the file.
+ */
+ if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) {
+ if (ferror(ifaces->fp6)) {
+ *err = 1;
+ log_error("Error reading IPv6 "
+ "interface information");
+ } else {
+ *err = 0;
+ }
+ return 0;
+ }
+
+ /*
+ * Make sure the line is a nice, newline-terminated line.
+ */
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad line reading IPv6 "
+ "interface information");
+ *err = 1;
+ return 0;
+ }
+
+ /*
+ * Figure out our name.
+ */
+ buf[--len] = '\0';
+ p = strrchr(buf, ' ');
+ if (p == NULL) {
+ log_error("Bad line reading IPv6 interface "
+ "information (no space)");
+ *err = 1;
+ return 0;
+ }
+ name = p+1;
+
+ /*
+ * Copy our name into our interface structure.
+ */
+ len = strlen(name);
+ if (len >= sizeof(info->name)) {
+ *err = 1;
+ log_error("IPv6 interface name '%s' too long", name);
+ return 0;
+ }
+ strcpy(info->name, name);
+
+#ifdef SKIP_DUMMY_INTERFACES
+ } while (strncmp(info->name, "dummy", 5) == 0);
+#else
+ } while (0);
+#endif
+
+ /*
+ * Double-check we start with the IPv6 address.
+ */
+ for (i=0; i<32; i++) {
+ if (!isxdigit(buf[i]) || isupper(buf[i])) {
+ *err = 1;
+ log_error("Bad line reading IPv6 interface address "
+ "for '%s'", name);
+ return 0;
+ }
+ }
+
+ /*
+ * Load our socket structure.
+ */
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ for (i=0; i<16; i++) {
+ unsigned char byte;
+ static const char hex[] = "0123456789abcdef";
+ byte = ((index(hex, buf[i * 2]) - hex) << 4) |
+ (index(hex, buf[i * 2 + 1]) - hex);
+ addr.sin6_addr.s6_addr[i] = byte;
+ }
+ memcpy(&info->addr, &addr, sizeof(addr));
+
+ /*
+ * Get our flags.
+ */
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m", name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.ifr_flags;
+
+ *err = 0;
+ return 1;
+}
+#endif /* DHCPv6 */
+
+/*
+ * Retrieve the next interface.
+ *
+ * Returns information in the info structure.
+ * Sets err to 1 if there is an error, otherwise 0.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ if (next_iface4(info, err, ifaces)) {
+ return 1;
+ }
+#ifdef DHCPv6
+ if (!(*err)) {
+ if (local_family == AF_INET6)
+ return next_iface6(info, err, ifaces);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * End scan of interfaces.
+ */
+void
+end_iface_scan(struct iface_conf_list *ifaces) {
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ close(ifaces->sock);
+ ifaces->sock = -1;
+#ifdef DHCPv6
+ if (local_family == AF_INET6) {
+ fclose(ifaces->fp6);
+ ifaces->fp6 = NULL;
+ }
+#endif
+}
+#else
+
+/*
+ * BSD support
+ * -----------
+ *
+ * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs()
+ * function.
+ *
+ * The getifaddrs() man page describes the use.
+ */
+
+#include <ifaddrs.h>
+
+/*
+ * Structure holding state about the scan.
+ */
+struct iface_conf_list {
+ struct ifaddrs *head; /* beginning of the list */
+ struct ifaddrs *next; /* current position in the list */
+};
+
+/*
+ * Structure used to return information about a specific interface.
+ */
+struct iface_info {
+ char name[IFNAMSIZ]; /* name of the interface, e.g. "bge0" */
+ struct sockaddr_storage addr; /* address information */
+ isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
+};
+
+/*
+ * Start a scan of interfaces.
+ *
+ * The iface_conf_list structure maintains state for this process.
+ */
+int
+begin_iface_scan(struct iface_conf_list *ifaces) {
+ if (getifaddrs(&ifaces->head) != 0) {
+ log_error("Error getting interfaces; %m");
+ return 0;
+ }
+ ifaces->next = ifaces->head;
+ return 1;
+}
+
+/*
+ * Retrieve the next interface.
+ *
+ * Returns information in the info structure.
+ * Sets err to 1 if there is an error, otherwise 0.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ if (ifaces->next == NULL) {
+ *err = 0;
+ return 0;
+ }
+ if (strlen(ifaces->next->ifa_name) >= sizeof(info->name)) {
+ log_error("Interface name '%s' too long",
+ ifaces->next->ifa_name);
+ *err = 1;
+ return 0;
+ }
+ strcpy(info->name, ifaces->next->ifa_name);
+ memcpy(&info->addr, ifaces->next->ifa_addr,
+ ifaces->next->ifa_addr->sa_len);
+ info->flags = ifaces->next->ifa_flags;
+ ifaces->next = ifaces->next->ifa_next;
+ *err = 0;
+ return 1;
+}
+
+/*
+ * End scan of interfaces.
+ */
+void
+end_iface_scan(struct iface_conf_list *ifaces) {
+ freeifaddrs(ifaces->head);
+ ifaces->head = NULL;
+ ifaces->next = NULL;
+}
+#endif
+
+/* XXX: perhaps create drealloc() rather than do it manually */
+void
+add_ipv4_addr_to_interface(struct interface_info *iface,
+ const struct in_addr *addr) {
+ /*
+ * We don't expect a lot of addresses per IPv4 interface, so
+ * we use 4, as our "chunk size" for collecting addresses.
+ */
+ if (iface->addresses == NULL) {
+ iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL);
+ if (iface->addresses == NULL) {
+ log_fatal("Out of memory saving IPv4 address "
+ "on interface.");
+ }
+ iface->address_count = 0;
+ iface->address_max = 4;
+ } else if (iface->address_count >= iface->address_max) {
+ struct in_addr *tmp;
+ int new_max;
+
+ new_max = iface->address_max + 4;
+ tmp = dmalloc(new_max * sizeof(struct in_addr), MDL);
+ if (tmp == NULL) {
+ log_fatal("Out of memory saving IPv4 address "
+ "on interface.");
+ }
+ memcpy(tmp,
+ iface->addresses,
+ iface->address_max * sizeof(struct in_addr));
+ dfree(iface->addresses, MDL);
+ iface->addresses = tmp;
+ iface->address_max = new_max;
+ }
+ iface->addresses[iface->address_count++] = *addr;
+}
+
+#ifdef DHCPv6
+/* XXX: perhaps create drealloc() rather than do it manually */
+void
+add_ipv6_addr_to_interface(struct interface_info *iface,
+ const struct in6_addr *addr) {
+ /*
+ * Each IPv6 interface will have at least two IPv6 addresses,
+ * and likely quite a few more. So we use 8, as our "chunk size" for
+ * collecting addresses.
+ */
+ if (iface->v6addresses == NULL) {
+ iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL);
+ if (iface->v6addresses == NULL) {
+ log_fatal("Out of memory saving IPv6 address "
+ "on interface.");
+ }
+ iface->v6address_count = 0;
+ iface->v6address_max = 8;
+ } else if (iface->v6address_count >= iface->v6address_max) {
+ struct in6_addr *tmp;
+ int new_max;
+
+ new_max = iface->v6address_max + 8;
+ tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL);
+ if (tmp == NULL) {
+ log_fatal("Out of memory saving IPv6 address "
+ "on interface.");
+ }
+ memcpy(tmp,
+ iface->v6addresses,
+ iface->v6address_max * sizeof(struct in6_addr));
+ dfree(iface->v6addresses, MDL);
+ iface->v6addresses = tmp;
+ iface->v6address_max = new_max;
+ }
+ iface->v6addresses[iface->v6address_count++] = *addr;
+}
+#endif /* DHCPv6 */
+
+/* Use the SIOCGIFCONF ioctl 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(int state) {
+ struct iface_conf_list ifaces;
+ struct iface_info info;
+ int err;
+
+ struct interface_info *tmp;
+ struct interface_info *last, *next;
+
+#ifdef DHCPv6
+ char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+#endif /* DHCPv6 */
+
+
+ struct subnet *subnet;
+ int ir;
+ isc_result_t status;
+ int wifcount = 0;
+
+ static int setup_fallback = 0;
+
+ if (!begin_iface_scan(&ifaces)) {
+ log_fatal("Can't get list of interfaces.");
+ }
+
+ /* If we already have a list of interfaces, and we're running as
+ a DHCP server, the interfaces were requested. */
+ if (interfaces && (state == DISCOVER_SERVER ||
+ state == DISCOVER_RELAY ||
+ state == DISCOVER_REQUESTED))
+ ir = 0;
+ else if (state == DISCOVER_UNCONFIGURED)
+ ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC;
+ else
+ ir = INTERFACE_REQUESTED;
+
+ /* Cycle through the list of interfaces looking for IP addresses. */
+ while (next_iface(&info, &err, &ifaces)) {
+
+ /* See if we've seen an interface that matches this one. */
+ for (tmp = interfaces; tmp; tmp = tmp->next) {
+ if (!strcmp(tmp->name, info.name))
+ break;
+ }
+
+ /* Skip non broadcast interfaces (plus loopback and
+ point-to-point in case an OS incorrectly marks them
+ as broadcast). Also skip down interfaces unless we're
+ trying to get a list of configurable interfaces. */
+ if ((((local_family == AF_INET &&
+ !(info.flags & IFF_BROADCAST)) ||
+#ifdef DHCPv6
+ (local_family == AF_INET6 &&
+ !(info.flags & IFF_MULTICAST)) ||
+#endif
+ info.flags & IFF_LOOPBACK ||
+ info.flags & IFF_POINTOPOINT) && !tmp) ||
+ (!(info.flags & IFF_UP) &&
+ state != DISCOVER_UNCONFIGURED))
+ continue;
+
+ /* If there isn't already an interface by this name,
+ allocate one. */
+ if (tmp == NULL) {
+ status = interface_allocate(&tmp, MDL);
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("Error allocating interface %s: %s",
+ info.name, isc_result_totext(status));
+ }
+ strcpy(tmp->name, info.name);
+ interface_snorf(tmp, ir);
+ interface_dereference(&tmp, MDL);
+ tmp = interfaces; /* XXX */
+ }
+
+ if (dhcp_interface_discovery_hook) {
+ (*dhcp_interface_discovery_hook)(tmp);
+ }
+
+ if ((info.addr.ss_family == AF_INET) &&
+ (local_family == AF_INET)) {
+ struct sockaddr_in *a = (struct sockaddr_in*)&info.addr;
+ struct iaddr addr;
+
+ /* We don't want the loopback interface. */
+ if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_SERVER))
+ continue;
+
+ /* If the only address we have is 0.0.0.0, we
+ shouldn't consider the interface configured. */
+ if (a->sin_addr.s_addr != htonl(INADDR_ANY))
+ tmp->configured = 1;
+
+ add_ipv4_addr_to_interface(tmp, &a->sin_addr);
+
+ /* invoke the setup hook */
+ addr.len = 4;
+ memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len);
+ if (dhcp_interface_setup_hook) {
+ (*dhcp_interface_setup_hook)(tmp, &addr);
+ }
+ }
+#ifdef DHCPv6
+ else if ((info.addr.ss_family == AF_INET6) &&
+ (local_family == AF_INET6)) {
+ struct sockaddr_in6 *a =
+ (struct sockaddr_in6*)&info.addr;
+ struct iaddr addr;
+
+ /* We don't want the loopback interface. */
+ if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_SERVER))
+ continue;
+
+ /* If the only address we have is 0.0.0.0, we
+ shouldn't consider the interface configured. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
+ tmp->configured = 1;
+
+ add_ipv6_addr_to_interface(tmp, &a->sin6_addr);
+
+ /* invoke the setup hook */
+ addr.len = 16;
+ memcpy(addr.iabuf, &a->sin6_addr, addr.len);
+ if (dhcp_interface_setup_hook) {
+ (*dhcp_interface_setup_hook)(tmp, &addr);
+ }
+ }
+#endif /* DHCPv6 */
+ }
+
+ if (err) {
+ log_fatal("Error getting interface information.");
+ }
+
+ end_iface_scan(&ifaces);
+
+
+ /* Mock-up an 'ifp' structure which is no longer used in the
+ * new interface-sensing code, but is used in higher layers
+ * (for example to sense fallback interfaces).
+ */
+ for (tmp = interfaces ; tmp != NULL ; tmp = tmp->next) {
+ if (tmp->ifp == NULL) {
+ struct ifreq *tif;
+
+ tif = (struct ifreq *)dmalloc(sizeof(struct ifreq),
+ MDL);
+ if (tif == NULL)
+ log_fatal("no space for ifp mockup.");
+ strcpy(tif->ifr_name, tmp->name);
+ tmp->ifp = tif;
+ }
+ }
+
+
+ /* If we're just trying to get a list of interfaces that we might
+ be able to configure, we can quit now. */
+ if (state == DISCOVER_UNCONFIGURED) {
+ return;
+ }
+
+ /* Weed out the interfaces that did not have IP addresses. */
+ tmp = last = next = NULL;
+ if (interfaces)
+ interface_reference (&tmp, interfaces, MDL);
+ while (tmp) {
+ if (next)
+ interface_dereference (&next, MDL);
+ if (tmp -> next)
+ interface_reference (&next, tmp -> next, MDL);
+ /* skip interfaces that are running already */
+ if (tmp -> flags & INTERFACE_RUNNING) {
+ interface_dereference(&tmp, MDL);
+ if(next)
+ interface_reference(&tmp, next, MDL);
+ continue;
+ }
+ if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_REQUESTED)
+ tmp -> flags &= ~(INTERFACE_AUTOMATIC |
+ INTERFACE_REQUESTED);
+
+#ifdef DHCPv6
+ if (!(tmp->flags & INTERFACE_REQUESTED)) {
+#else
+ if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
+#endif /* DHCPv6 */
+ if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
+ log_fatal ("%s: not found", tmp -> name);
+ if (!last) {
+ if (interfaces)
+ interface_dereference (&interfaces,
+ MDL);
+ if (next)
+ interface_reference (&interfaces, next, MDL);
+ } else {
+ interface_dereference (&last -> next, MDL);
+ if (next)
+ interface_reference (&last -> next,
+ next, MDL);
+ }
+ if (tmp -> next)
+ interface_dereference (&tmp -> next, MDL);
+
+ /* Remember the interface in case we need to know
+ about it later. */
+ if (dummy_interfaces) {
+ interface_reference (&tmp -> next,
+ dummy_interfaces, MDL);
+ interface_dereference (&dummy_interfaces, MDL);
+ }
+ interface_reference (&dummy_interfaces, tmp, MDL);
+ interface_dereference (&tmp, MDL);
+ if (next)
+ interface_reference (&tmp, next, MDL);
+ continue;
+ }
+ last = tmp;
+
+ /* We must have a subnet declaration for each interface. */
+ if (!tmp->shared_network && (state == DISCOVER_SERVER)) {
+ log_error("%s", "");
+ if (local_family == AF_INET) {
+ log_error("No subnet declaration for %s (%s).",
+ tmp->name,
+ (tmp->addresses == NULL) ?
+ "no IPv4 addresses" :
+ inet_ntoa(tmp->addresses[0]));
+#ifdef DHCPv6
+ } else {
+ if (tmp->v6addresses != NULL) {
+ inet_ntop(AF_INET6,
+ &tmp->v6addresses[0],
+ abuf,
+ sizeof(abuf));
+ } else {
+ strcpy(abuf, "no IPv6 addresses");
+ }
+ log_error("No subnet6 declaration for %s (%s).",
+ tmp->name,
+ abuf);
+#endif /* DHCPv6 */
+ }
+ if (supports_multiple_interfaces(tmp)) {
+ log_error ("** Ignoring requests on %s. %s",
+ tmp -> name, "If this is not what");
+ log_error (" you want, please write %s",
+#ifdef DHCPv6
+ (local_family != AF_INET) ?
+ "a subnet6 declaration" :
+#endif
+ "a subnet declaration");
+ log_error (" in your dhcpd.conf file %s",
+ "for the network segment");
+ log_error (" to %s %s %s",
+ "which interface",
+ tmp -> name, "is attached. **");
+ log_error ("%s", "");
+ goto next;
+ } else {
+ log_error ("You must write a %s",
+#ifdef DHCPv6
+ (local_family != AF_INET) ?
+ "subnet6 declaration for this" :
+#endif
+ "subnet declaration for this");
+ log_error ("subnet. You cannot prevent %s",
+ "the DHCP server");
+ log_error ("from listening on this subnet %s",
+ "because your");
+ log_fatal ("operating system does not %s.",
+ "support this capability");
+ }
+ }
+
+ /* Find subnets that don't have valid interface
+ addresses... */
+ for (subnet = (tmp -> shared_network
+ ? tmp -> shared_network -> subnets
+ : (struct subnet *)0);
+ subnet; subnet = subnet -> next_sibling) {
+ /* Set the interface address for this subnet
+ to the first address we found. */
+ if (subnet->interface_address.len == 0) {
+ if (tmp->address_count > 0) {
+ subnet->interface_address.len = 4;
+ memcpy(subnet->interface_address.iabuf,
+ &tmp->addresses[0].s_addr, 4);
+ } else if (tmp->v6address_count > 0) {
+ subnet->interface_address.len = 16;
+ memcpy(subnet->interface_address.iabuf,
+ &tmp->v6addresses[0].s6_addr,
+ 16);
+ } else {
+ /* XXX: should be one */
+ log_error("%s missing an interface "
+ "address", tmp->name);
+ continue;
+ }
+ }
+ }
+
+ /* Flag the index as not having been set, so that the
+ interface registerer can set it or not as it chooses. */
+ tmp -> index = -1;
+
+ /* Register the interface... */
+ if (local_family == AF_INET) {
+ if_register_receive(tmp);
+ if_register_send(tmp);
+#ifdef DHCPv6
+ } else {
+ if ((state == DISCOVER_SERVER) ||
+ (state == DISCOVER_RELAY)) {
+ if_register6(tmp, 1);
+ } else {
+ if_register6(tmp, 0);
+ }
+#endif /* DHCPv6 */
+ }
+
+ interface_stash (tmp);
+ wifcount++;
+#if defined (F_SETFD)
+ if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on %s: %m",
+ tmp -> name);
+ if (tmp -> rfdesc != tmp -> wfdesc) {
+ if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on %s: %m",
+ tmp -> name);
+ }
+#endif
+ next:
+ interface_dereference (&tmp, MDL);
+ if (next)
+ interface_reference (&tmp, next, MDL);
+ }
+
+ /*
+ * Now register all the remaining interfaces as protocols.
+ * We register with omapi to allow for control of the interface,
+ * we've already registered the fd or socket with the socket
+ * manager as part of if_register_receive().
+ */
+ for (tmp = interfaces; tmp; tmp = tmp -> next) {
+ /* not if it's been registered before */
+ if (tmp -> flags & INTERFACE_RUNNING)
+ continue;
+ if (tmp -> rfdesc == -1)
+ continue;
+ switch (local_family) {
+#ifdef DHCPv6
+ case AF_INET6:
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one_v6, 0, 0);
+ break;
+#endif /* DHCPv6 */
+ case AF_INET:
+ default:
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one, 0, 0);
+ break;
+ }
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ tmp -> name, isc_result_totext (status));
+
+#if defined(DHCPv6)
+ /* Only register the first interface for V6, since they all
+ * use the same socket. XXX: This has some messy side
+ * effects if we start dynamically adding and removing
+ * interfaces, but we're well beyond that point in terms of
+ * mess.
+ */
+ if (local_family == AF_INET6)
+ break;
+#endif
+ } /* for (tmp = interfaces; ... */
+
+ if (state == DISCOVER_SERVER && wifcount == 0) {
+ log_info ("%s", "");
+ log_fatal ("Not configured to listen on any interfaces!");
+ }
+
+ if ((local_family == AF_INET) && !setup_fallback) {
+ setup_fallback = 1;
+ maybe_setup_fallback();
+ }
+
+#if defined (F_SETFD)
+ if (fallback_interface) {
+ if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on fallback: %m");
+ if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) {
+ if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on fallback: %m");
+ }
+ }
+#endif /* F_SETFD */
+}
+
+int if_readsocket (h)
+ omapi_object_t *h;
+{
+ struct interface_info *ip;
+
+ if (h -> type != dhcp_type_interface)
+ return -1;
+ ip = (struct interface_info *)h;
+ return ip -> rfdesc;
+}
+
+int setup_fallback (struct interface_info **fp, const char *file, int line)
+{
+ isc_result_t status;
+
+ status = interface_allocate (&fallback_interface, file, line);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Error allocating fallback interface: %s",
+ isc_result_totext (status));
+ strcpy (fallback_interface -> name, "fallback");
+ if (dhcp_interface_setup_hook)
+ (*dhcp_interface_setup_hook) (fallback_interface,
+ (struct iaddr *)0);
+ status = interface_reference (fp, fallback_interface, file, line);
+
+ fallback_interface -> index = -1;
+ interface_stash (fallback_interface);
+ return status == ISC_R_SUCCESS;
+}
+
+void reinitialize_interfaces ()
+{
+ struct interface_info *ip;
+
+ for (ip = interfaces; ip; ip = ip -> next) {
+ if_reinitialize_receive (ip);
+ if_reinitialize_send (ip);
+ }
+
+ if (fallback_interface)
+ if_reinitialize_send (fallback_interface);
+
+ interfaces_invalidated = 1;
+}
+
+isc_result_t got_one (h)
+ omapi_object_t *h;
+{
+ struct sockaddr_in from;
+ struct hardware hfrom;
+ struct iaddr ifrom;
+ int result;
+ union {
+ unsigned char packbuf [4095]; /* Packet input buffer.
+ Must be as large as largest
+ possible MTU. */
+ struct dhcp_packet packet;
+ } u;
+ struct interface_info *ip;
+
+ if (h -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ ip = (struct interface_info *)h;
+
+ again:
+ if ((result =
+ receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) {
+ log_error ("receive_packet failed on %s: %m", ip -> name);
+ return ISC_R_UNEXPECTED;
+ }
+ if (result == 0)
+ return ISC_R_UNEXPECTED;
+
+ /*
+ * If we didn't at least get the fixed portion of the BOOTP
+ * packet, drop the packet.
+ * Previously we allowed packets with no sname or filename
+ * as we were aware of at least one client that did. But
+ * a bug caused short packets to not work and nobody has
+ * complained, it seems rational to tighten up that
+ * restriction.
+ */
+ if (result < DHCP_FIXED_NON_UDP)
+ return ISC_R_UNEXPECTED;
+
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ {
+ /* We retrieve the ifindex from the unused hfrom variable */
+ unsigned int ifindex;
+
+ memcpy(&ifindex, hfrom.hbuf, sizeof (ifindex));
+
+ /*
+ * Seek forward from the first interface to find the matching
+ * source interface by interface index.
+ */
+ ip = interfaces;
+ while ((ip != NULL) && (if_nametoindex(ip->name) != ifindex))
+ ip = ip->next;
+ if (ip == NULL)
+ return ISC_R_NOTFOUND;
+ }
+#endif
+
+ if (bootp_packet_handler) {
+ ifrom.len = 4;
+ memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
+
+ (*bootp_packet_handler) (ip, &u.packet, (unsigned)result,
+ from.sin_port, ifrom, &hfrom);
+ }
+
+ /* If there is buffered data, read again. This is for, e.g.,
+ bpf, which may return two packets at once. */
+ if (ip -> rbuf_offset != ip -> rbuf_len)
+ goto again;
+ return ISC_R_SUCCESS;
+}
+
+#ifdef DHCPv6
+isc_result_t
+got_one_v6(omapi_object_t *h) {
+ struct sockaddr_in6 from;
+ struct in6_addr to;
+ struct iaddr ifrom;
+ int result;
+ char buf[65536]; /* maximum size for a UDP packet is 65536 */
+ struct interface_info *ip;
+ int is_unicast;
+ unsigned int if_idx = 0;
+
+ if (h->type != dhcp_type_interface) {
+ return DHCP_R_INVALIDARG;
+ }
+ ip = (struct interface_info *)h;
+
+ result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf),
+ &from, &to, &if_idx);
+ if (result < 0) {
+ log_error("receive_packet6() failed on %s: %m", ip->name);
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* 0 is 'any' interface. */
+ if (if_idx == 0)
+ return ISC_R_NOTFOUND;
+
+ if (dhcpv6_packet_handler != NULL) {
+ /*
+ * If a packet is not multicast, we assume it is unicast.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&to)) {
+ is_unicast = ISC_FALSE;
+ } else {
+ is_unicast = ISC_TRUE;
+ }
+
+ ifrom.len = 16;
+ memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
+
+ /* Seek forward to find the matching source interface. */
+ ip = interfaces;
+ while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx))
+ ip = ip->next;
+
+ if (ip == NULL)
+ return ISC_R_NOTFOUND;
+
+ (*dhcpv6_packet_handler)(ip, buf,
+ result, from.sin6_port,
+ &ifrom, is_unicast);
+ }
+
+ return ISC_R_SUCCESS;
+}
+#endif /* DHCPv6 */
+
+isc_result_t dhcp_interface_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct interface_info *interface;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ interface = (struct interface_info *)h;
+
+ if (!omapi_ds_strcmp (name, "name")) {
+ if ((value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string) &&
+ value -> u.buffer.len < sizeof interface -> name) {
+ memcpy (interface -> name,
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ interface -> name [value -> u.buffer.len] = 0;
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_interface_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_interface_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ struct interface_info *interface;
+
+ if (h -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ interface = (struct interface_info *)h;
+
+ if (interface -> ifp) {
+ dfree (interface -> ifp, file, line);
+ interface -> ifp = 0;
+ }
+ if (interface -> next)
+ interface_dereference (&interface -> next, file, line);
+ if (interface -> rbuf) {
+ dfree (interface -> rbuf, file, line);
+ interface -> rbuf = (unsigned char *)0;
+ }
+ if (interface -> client)
+ interface -> client = (struct client_state *)0;
+
+ if (interface -> shared_network)
+ omapi_object_dereference ((omapi_object_t **)
+ &interface -> shared_network, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_interface_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct interface_info *ip, *interface;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ interface = (struct interface_info *)h;
+
+ /* If it's an update signal, see if the interface is dead right
+ now, or isn't known at all, and if that's the case, revive it. */
+ if (!strcmp (name, "update")) {
+ for (ip = dummy_interfaces; ip; ip = ip -> next)
+ if (ip == interface)
+ break;
+ if (ip && dhcp_interface_startup_hook)
+ return (*dhcp_interface_startup_hook) (ip);
+
+ for (ip = interfaces; ip; ip = ip -> next)
+ if (ip == interface)
+ break;
+ if (!ip && dhcp_interface_startup_hook)
+ return (*dhcp_interface_startup_hook) (ip);
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> signal_handler) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_interface_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct interface_info *interface;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ interface = (struct interface_info *)h;
+
+ /* Write out all the values. */
+
+ status = omapi_connection_put_name (c, "state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if ((interface->flags & INTERFACE_REQUESTED) != 0)
+ status = omapi_connection_put_string (c, "up");
+ else
+ status = omapi_connection_put_string (c, "down");
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_interface_lookup (omapi_object_t **ip,
+ omapi_object_t *id,
+ omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct interface_info *interface;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ /* First see if we were sent a handle. */
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (ip, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*ip) -> type != dhcp_type_interface) {
+ omapi_object_dereference (ip, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ }
+
+ /* Now look for an interface name. */
+ status = omapi_get_value_str (ref, id, "name", &tv);
+ if (status == ISC_R_SUCCESS) {
+ char *s;
+ unsigned len;
+ for (interface = interfaces; interface;
+ interface = interface -> next) {
+ s = memchr (interface -> name, 0, IFNAMSIZ);
+ if (s)
+ len = s - &interface -> name [0];
+ else
+ len = IFNAMSIZ;
+ if ((tv -> value -> u.buffer.len == len &&
+ !memcmp (interface -> name,
+ (char *)tv -> value -> u.buffer.value,
+ len)))
+ break;
+ }
+ if (!interface) {
+ for (interface = dummy_interfaces;
+ interface; interface = interface -> next) {
+ s = memchr (interface -> name, 0, IFNAMSIZ);
+ if (s)
+ len = s - &interface -> name [0];
+ else
+ len = IFNAMSIZ;
+ if ((tv -> value -> u.buffer.len == len &&
+ !memcmp (interface -> name,
+ (char *)
+ tv -> value -> u.buffer.value,
+ len)))
+ break;
+ }
+ }
+
+ omapi_value_dereference (&tv, MDL);
+ if (*ip && *ip != (omapi_object_t *)interface) {
+ omapi_object_dereference (ip, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!interface) {
+ if (*ip)
+ omapi_object_dereference (ip, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*ip)
+ omapi_object_reference (ip,
+ (omapi_object_t *)interface,
+ MDL);
+ }
+
+ /* If we get to here without finding an interface, no valid key was
+ specified. */
+ if (!*ip)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+/* actually just go discover the interface */
+isc_result_t dhcp_interface_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ struct interface_info *hp;
+ isc_result_t status;
+
+ hp = (struct interface_info *)0;
+ status = interface_allocate (&hp, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ hp -> flags = INTERFACE_REQUESTED;
+ status = interface_reference ((struct interface_info **)lp, hp, MDL);
+ interface_dereference (&hp, MDL);
+ return status;
+}
+
+isc_result_t dhcp_interface_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ struct interface_info *interface, *ip, *last;
+
+ interface = (struct interface_info *)lp;
+
+ /* remove from interfaces */
+ last = 0;
+ for (ip = interfaces; ip; ip = ip -> next) {
+ if (ip == interface) {
+ if (last) {
+ interface_dereference (&last -> next, MDL);
+ if (ip -> next)
+ interface_reference (&last -> next,
+ ip -> next, MDL);
+ } else {
+ interface_dereference (&interfaces, MDL);
+ if (ip -> next)
+ interface_reference (&interfaces,
+ ip -> next, MDL);
+ }
+ if (ip -> next)
+ interface_dereference (&ip -> next, MDL);
+ break;
+ }
+ last = ip;
+ }
+ if (!ip)
+ return ISC_R_NOTFOUND;
+
+ /* add the interface to the dummy_interface list */
+ if (dummy_interfaces) {
+ interface_reference (&interface -> next,
+ dummy_interfaces, MDL);
+ interface_dereference (&dummy_interfaces, MDL);
+ }
+ interface_reference (&dummy_interfaces, interface, MDL);
+
+ /* do a DHCPRELEASE */
+ if (dhcp_interface_shutdown_hook)
+ (*dhcp_interface_shutdown_hook) (interface);
+
+ /* remove the io object */
+ omapi_unregister_io_object ((omapi_object_t *)interface);
+
+ switch(local_family) {
+#ifdef DHCPv6
+ case AF_INET6:
+ if_deregister6(interface);
+ break;
+#endif /* DHCPv6 */
+ case AF_INET:
+ default:
+ if_deregister_send(interface);
+ if_deregister_receive(interface);
+ break;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void interface_stash (struct interface_info *tptr)
+{
+ struct interface_info **vec;
+ int delta;
+
+ /* If the registerer didn't assign an index, assign one now. */
+ if (tptr -> index == -1) {
+ tptr -> index = interface_count++;
+ while (tptr -> index < interface_max &&
+ interface_vector [tptr -> index])
+ tptr -> index = interface_count++;
+ }
+
+ if (interface_max <= tptr -> index) {
+ delta = tptr -> index - interface_max + 10;
+ vec = dmalloc ((interface_max + delta) *
+ sizeof (struct interface_info *), MDL);
+ if (!vec)
+ return;
+ memset (&vec [interface_max], 0,
+ (sizeof (struct interface_info *)) * delta);
+ interface_max += delta;
+ if (interface_vector) {
+ memcpy (vec, interface_vector,
+ (interface_count *
+ sizeof (struct interface_info *)));
+ dfree (interface_vector, MDL);
+ }
+ interface_vector = vec;
+ }
+ interface_reference (&interface_vector [tptr -> index], tptr, MDL);
+ if (tptr -> index >= interface_count)
+ interface_count = tptr -> index + 1;
+#if defined (TRACING)
+ trace_interface_register (interface_trace, tptr);
+#endif
+}
+
+void interface_snorf (struct interface_info *tmp, int ir)
+{
+ tmp -> circuit_id = (u_int8_t *)tmp -> name;
+ tmp -> circuit_id_len = strlen (tmp -> name);
+ tmp -> remote_id = 0;
+ tmp -> remote_id_len = 0;
+ tmp -> flags = ir;
+ if (interfaces) {
+ interface_reference (&tmp -> next,
+ interfaces, MDL);
+ interface_dereference (&interfaces, MDL);
+ }
+ interface_reference (&interfaces, tmp, MDL);
+}
diff --git a/common/dispatch.c b/common/dispatch.c
new file mode 100644
index 0000000..7c4434e
--- /dev/null
+++ b/common/dispatch.c
@@ -0,0 +1,416 @@
+/* dispatch.c
+
+ Network input dispatcher... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "dhcpd.h"
+
+#include <sys/time.h>
+
+struct timeout *timeouts;
+static struct timeout *free_timeouts;
+
+void set_time(TIME t)
+{
+ /* Do any outstanding timeouts. */
+ if (cur_tv . tv_sec != t) {
+ cur_tv . tv_sec = t;
+ cur_tv . tv_usec = 0;
+ process_outstanding_timeouts ((struct timeval *)0);
+ }
+}
+
+struct timeval *process_outstanding_timeouts (struct timeval *tvp)
+{
+ /* 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 . tv_sec < cur_tv . tv_sec) ||
+ ((timeouts -> when . tv_sec == cur_tv . tv_sec) &&
+ (timeouts -> when . tv_usec <= cur_tv . tv_usec))) {
+ t = timeouts;
+ timeouts = timeouts -> next;
+ (*(t -> func)) (t -> what);
+ if (t -> unref)
+ (*t -> unref) (&t -> what, MDL);
+ t -> next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+ if (tvp) {
+ tvp -> tv_sec = timeouts -> when . tv_sec;
+ tvp -> tv_usec = timeouts -> when . tv_usec;
+ }
+ return tvp;
+ } else
+ return (struct timeval *)0;
+}
+
+/* Wait for packets to come in using select(). When one does, 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. */
+
+/*
+ * Use the DHCP timeout list as a place to store DHCP specific
+ * information, but use the ISC timer system to actually dispatch
+ * the events.
+ *
+ * There are several things that the DHCP timer code does that the
+ * ISC code doesn't:
+ * 1) It allows for negative times
+ * 2) The cancel arguments are different. The DHCP code uses the
+ * function and data to find the proper timer to cancel while the
+ * ISC code uses a pointer to the timer.
+ * 3) The DHCP code includes provision for incrementing and decrementing
+ * a reference counter associated with the data.
+ * The first one is fairly easy to fix but will take some time to go throuh
+ * the callers and update them. The second is also not all that difficult
+ * in concept - add a pointer to the appropriate structures to hold a pointer
+ * to the timer and use that. The complications arise in trying to ensure
+ * that all of the corner cases are covered. The last one is potentially
+ * more painful and requires more investigation.
+ *
+ * The plan is continue with the older DHCP calls and timer list. The
+ * calls will continue to manipulate the list but will also pass a
+ * timer to the ISC timer code for the actual dispatch. Later, if desired,
+ * we can go back and modify the underlying calls to use the ISC
+ * timer functions directly without requiring all of the code to change
+ * at the same time.
+ */
+
+void
+dispatch(void)
+{
+ isc_result_t status;
+
+ status = isc_app_ctxrun(dhcp_gbl_ctx.actx);
+
+ log_fatal ("Dispatch routine failed: %s -- exiting",
+ isc_result_totext (status));
+}
+
+void
+isclib_timer_callback(isc_task_t *taskp,
+ isc_event_t *eventp)
+{
+ struct timeout *t = (struct timeout *)eventp->ev_arg;
+ struct timeout *q, *r;
+
+ /* Get the current time... */
+ gettimeofday (&cur_tv, (struct timezone *)0);
+
+ /*
+ * Find the timeout on the dhcp list and remove it.
+ * As the list isn't ordered we search the entire list
+ */
+
+ r = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q == t) {
+ if (r)
+ r->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ r = q;
+ }
+
+ /*
+ * The timer should always be on the list. If it is we do
+ * the work and detach the timer block, if not we log an error.
+ * In both cases we attempt free the ISC event and continue
+ * processing.
+ */
+
+ if (q != NULL) {
+ /* call the callback function */
+ (*(q->func)) (q->what);
+ if (q->unref) {
+ (*q->unref) (&q->what, MDL);
+ }
+ q->next = free_timeouts;
+ isc_timer_detach(&q->isc_timeout);
+ free_timeouts = q;
+ } else {
+ /*
+ * Hmm, we should clean up the timer structure but aren't
+ * sure about the pointer to the timer block we got so
+ * don't try to - may change this to a log_fatal
+ */
+ log_error("Error finding timer structure");
+ }
+
+ isc_event_free(&eventp);
+ return;
+}
+
+/* maximum value for usec */
+#define USEC_MAX 1000000
+#define DHCP_SEC_MAX 0xFFFFFFFF
+
+void add_timeout (when, where, what, ref, unref)
+ struct timeval *when;
+ void (*where) (void *);
+ void *what;
+ tvref_t ref;
+ tvunref_t unref;
+{
+ struct timeout *t, *q;
+ int usereset = 0;
+ isc_result_t status;
+ int64_t sec;
+ int usec;
+ isc_interval_t interval;
+ isc_time_t expires;
+
+ /* See if this timeout supersedes an existing timeout. */
+ t = (struct timeout *)0;
+ for (q = timeouts; q; q = q->next) {
+ if ((where == NULL || q->func == where) &&
+ q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ usereset = 1;
+ 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;
+ } else {
+ q = ((struct timeout *)
+ dmalloc(sizeof(struct timeout), MDL));
+ if (!q) {
+ log_fatal("add_timeout: no memory!");
+ }
+ }
+ memset(q, 0, sizeof *q);
+ q->func = where;
+ q->ref = ref;
+ q->unref = unref;
+ if (q->ref)
+ (*q->ref)(&q->what, what, MDL);
+ else
+ q->what = what;
+ }
+
+ /*
+ * The value passed in is a time from an epoch but we need a relative
+ * time so we need to do some math to try and recover the period.
+ * This is complicated by the fact that not all of the calls cared
+ * about the usec value, if it's zero we assume the caller didn't care.
+ *
+ * The ISC timer library doesn't seem to like negative values
+ * and can't accept any values above 4G-1 seconds so we limit
+ * the values to 0 <= value < 4G-1. We do it before
+ * checking the trace option so that both the trace code and
+ * the working code use the same values.
+ */
+
+ sec = when->tv_sec - cur_tv.tv_sec;
+ usec = when->tv_usec - cur_tv.tv_usec;
+
+ if ((when->tv_usec != 0) && (usec < 0)) {
+ sec--;
+ usec += USEC_MAX;
+ }
+
+ if (sec < 0) {
+ sec = 0;
+ usec = 0;
+ } else if (sec > DHCP_SEC_MAX) {
+ log_error("Timeout requested too large "
+ "reducing to 2^^32-1");
+ sec = DHCP_SEC_MAX;
+ usec = 0;
+ } else if (usec < 0) {
+ usec = 0;
+ } else if (usec >= USEC_MAX) {
+ usec = USEC_MAX - 1;
+ }
+
+ /*
+ * This is necessary for the tracing code but we put it
+ * here in case we want to compare timing information
+ * for some reason, like debugging.
+ */
+ q->when.tv_sec = cur_tv.tv_sec + (sec & DHCP_SEC_MAX);
+ q->when.tv_usec = usec;
+
+#if defined (TRACING)
+ if (trace_playback()) {
+ /*
+ * If we are doing playback we need to handle the timers
+ * within this code rather than having the isclib handle
+ * them for us. We need to keep the timer list in order
+ * to allow us to find the ones to timeout.
+ *
+ * By using a different timer setup in the playback we may
+ * have variations between the orginal and the playback but
+ * it's the best we can do for now.
+ */
+
+ /* Beginning of list? */
+ if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
+ ((timeouts->when.tv_sec == q->when.tv_sec) &&
+ (timeouts->when.tv_usec > q->when.tv_usec))) {
+ q->next = timeouts;
+ timeouts = q;
+ return;
+ }
+
+ /* Middle of list? */
+ for (t = timeouts; t->next; t = t->next) {
+ if ((t->next->when.tv_sec > q->when.tv_sec) ||
+ ((t->next->when.tv_sec == q->when.tv_sec) &&
+ (t->next->when.tv_usec > q->when.tv_usec))) {
+ q->next = t->next;
+ t->next = q;
+ return;
+ }
+ }
+
+ /* End of list. */
+ t->next = q;
+ q->next = (struct timeout *)0;
+ return;
+ }
+#endif
+ /*
+ * Don't bother sorting the DHCP list, just add it to the front.
+ * Eventually the list should be removed as we migrate the callers
+ * to the native ISC timer functions, if it becomes a performance
+ * problem before then we may need to order the list.
+ */
+ q->next = timeouts;
+ timeouts = q;
+
+ isc_interval_set(&interval, sec & DHCP_SEC_MAX, usec * 1000);
+ status = isc_time_nowplusinterval(&expires, &interval);
+ if (status != ISC_R_SUCCESS) {
+ /*
+ * The system time function isn't happy or returned
+ * a value larger than isc_time_t can hold.
+ */
+ log_fatal("Unable to set up timer: %s",
+ isc_result_totext(status));
+ }
+
+ if (usereset == 0) {
+ status = isc_timer_create(dhcp_gbl_ctx.timermgr,
+ isc_timertype_once, &expires,
+ NULL, dhcp_gbl_ctx.task,
+ isclib_timer_callback,
+ (void *)q, &q->isc_timeout);
+ } else {
+ status = isc_timer_reset(q->isc_timeout,
+ isc_timertype_once, &expires,
+ NULL, 0);
+ }
+
+ /* If it fails log an error and die */
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("Unable to add timeout to isclib\n");
+ }
+
+ return;
+}
+
+void cancel_timeout (where, what)
+ void (*where) (void *);
+ void *what;
+{
+ struct timeout *t, *q;
+
+ /* Look for this timeout on the list, and unlink it if we find it. */
+ t = (struct timeout *)0;
+ 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, cancel it and put it on the free list.
+ * The TRACING stuff is ugly but we don't add a timer when doing
+ * playback so we don't want to remove them then either.
+ */
+ if (q) {
+#if defined (TRACING)
+ if (!trace_playback()) {
+#endif
+ isc_timer_detach(&q->isc_timeout);
+#if defined (TRACING)
+ }
+#endif
+
+ if (q->unref)
+ (*q->unref) (&q->what, MDL);
+ q->next = free_timeouts;
+ free_timeouts = q;
+ }
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void cancel_all_timeouts ()
+{
+ struct timeout *t, *n;
+ for (t = timeouts; t; t = n) {
+ n = t->next;
+ isc_timer_detach(&t->isc_timeout);
+ if (t->unref && t->what)
+ (*t->unref) (&t->what, MDL);
+ t->next = free_timeouts;
+ free_timeouts = t;
+ }
+}
+
+void relinquish_timeouts ()
+{
+ struct timeout *t, *n;
+ for (t = free_timeouts; t; t = n) {
+ n = t->next;
+ dfree(t, MDL);
+ }
+}
+#endif
diff --git a/common/dlpi.c b/common/dlpi.c
new file mode 100644
index 0000000..8f2c73d
--- /dev/null
+++ b/common/dlpi.c
@@ -0,0 +1,1416 @@
+/* dlpi.c
+
+ Data Link Provider Interface (DLPI) network interface code. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software was written for Internet Systems Consortium
+ * by Eric James Negaard, <lmdejn@lmd.ericsson.se>. To learn more about
+ * Internet Systems Consortium, see ``https://www.isc.org''.
+ *
+ * Joost Mulders has also done considerable work in debugging the DLPI API
+ * support on Solaris and getting this code to work properly on a variety
+ * of different Solaris platforms.
+ */
+
+/*
+ * Based largely in part to the existing NIT code in nit.c.
+ *
+ * This code has been developed and tested on sparc-based machines running
+ * SunOS 5.5.1, with le and hme network interfaces. It should be pretty
+ * generic, though.
+ */
+
+/*
+ * Implementation notes:
+ *
+ * I first tried to write this code to the "vanilla" DLPI 2.0 API.
+ * It worked on a Sun Ultra-1 with a hme interface, but didn't work
+ * on Sun SparcStation 5's with "le" interfaces (the packets sent out
+ * via dlpiunitdatareq contained an Ethernet type of 0x0000 instead
+ * of the expected 0x0800).
+ *
+ * Therefore I added the "DLPI_RAW" code which is a Sun extension to
+ * the DLPI standard. This code works on both of the above machines.
+ * This is configurable in the OS-dependent include file by defining
+ * USE_DLPI_RAW.
+ *
+ * It quickly became apparant that I should also use the "pfmod"
+ * STREAMS module to cut down on the amount of user level packet
+ * processing. I don't know how widely available "pfmod" is, so it's
+ * use is conditionally included. This is configurable in the
+ * OS-dependent include file by defining USE_DLPI_PFMOD.
+ *
+ * A major quirk on the Sun's at least, is that no packets seem to get
+ * sent out the interface until six seconds after the interface is
+ * first "attached" to [per system reboot] (it's actually from when
+ * the interface is attached, not when it is plumbed, so putting a
+ * sleep into the dhclient-script at PREINIT time doesn't help). I
+ * HAVE tried, without success to poll the fd to see when it is ready
+ * for writing. This doesn't help at all. If the sleeps are not done,
+ * the initial DHCPREQUEST or DHCPDISCOVER never gets sent out, so
+ * I've put them here, when register_send and register_receive are
+ * called (split up into two three-second sleeps between the notices,
+ * so that it doesn't seem like so long when you're watching :-). The
+ * amount of time to sleep is configurable in the OS-dependent include
+ * file by defining DLPI_FIRST_SEND_WAIT to be the number of seconds
+ * to sleep.
+ */
+
+/*
+ * The Open Group Technical Standard can be found here:
+ * http://www.opengroup.org/onlinepubs/009618899/index.htm
+ *
+ * The HP DLPI Programmer's Guide can be found here:
+ * http://docs.hp.com/en/B2355-90139/index.html
+ */
+
+#include "dhcpd.h"
+
+#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) || \
+ defined(USE_DLPI_HWADDR)
+
+# include <sys/ioctl.h>
+# include <sys/time.h>
+# include <sys/dlpi.h>
+# include <stropts.h>
+# ifdef USE_DLPI_PFMOD
+# include <sys/pfmod.h>
+# endif
+#include <poll.h>
+#include <errno.h>
+
+# include <netinet/in_systm.h>
+# include "includes/netinet/ip.h"
+# include "includes/netinet/udp.h"
+# include "includes/netinet/if_ether.h"
+
+# ifdef USE_DLPI_PFMOD
+# ifdef USE_DLPI_RAW
+# define DLPI_MODNAME "DLPI+RAW+PFMOD"
+# else
+# define DLPI_MODNAME "DLPI+PFMOD"
+# endif
+# else
+# ifdef USE_DLPI_RAW
+# define DLPI_MODNAME "DLPI+RAW"
+# else
+# define DLPI_MODNAME "DLPI"
+# endif
+# endif
+
+# ifndef ABS
+# define ABS(x) ((x) >= 0 ? (x) : 0-(x))
+# endif
+
+#if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW)
+static int strioctl (int fd, int cmd, int timeout, int len, char *dp);
+#endif
+
+#define DLPI_MAXDLBUF 8192 /* Buffer size */
+#define DLPI_MAXDLADDR 1024 /* Max address size */
+#define DLPI_DEVDIR "/dev/" /* Device directory */
+
+static int dlpiopen(const char *ifname);
+static int dlpiunit (char *ifname);
+static int dlpiinforeq (int fd);
+static int dlpiphysaddrreq (int fd, unsigned long addrtype);
+static int dlpiattachreq (int fd, unsigned long ppa);
+static int dlpibindreq (int fd, unsigned long sap, unsigned long max_conind,
+ unsigned long service_mode, unsigned long conn_mgmt,
+ unsigned long xidtest);
+#if defined(UNUSED_DLPI_INTERFACE)
+/* These functions are unused at present, but may be used at a later date.
+ * defined out to avoid compiler warnings about unused static functions.
+ */
+static int dlpidetachreq (int fd);
+static int dlpiunbindreq (int fd);
+#endif
+static int dlpiokack (int fd, char *bufp);
+static int dlpiinfoack (int fd, char *bufp);
+static int dlpiphysaddrack (int fd, char *bufp);
+static int dlpibindack (int fd, char *bufp);
+#if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE)
+/* These functions are not used if we're only sourcing the get_hw_addr()
+ * function (for USE_SOCKETS).
+ */
+static int dlpiunitdatareq (int fd, unsigned char *addr, int addrlen,
+ unsigned long minpri, unsigned long maxpri,
+ unsigned char *data, int datalen);
+static int dlpiunitdataind (int fd,
+ unsigned char *dstaddr,
+ unsigned long *dstaddrlen,
+ unsigned char *srcaddr,
+ unsigned long *srcaddrlen,
+ unsigned long *grpaddr,
+ unsigned char *data,
+ int datalen);
+#endif /* !USE_DLPI_HWADDR: USE_DLPI_SEND || USE_DLPI_RECEIVE */
+static int expected (unsigned long prim, union DL_primitives *dlp,
+ int msgflags);
+static int strgetmsg (int fd, struct strbuf *ctlp, struct strbuf *datap,
+ int *flagsp, char *caller);
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#ifdef USE_DLPI_SEND
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+#ifdef USE_DLPI_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+/* 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_dlpi (info)
+ struct interface_info *info;
+{
+ int sock;
+ int unit;
+ long buf [DLPI_MAXDLBUF];
+ union DL_primitives *dlp;
+
+ dlp = (union DL_primitives *)buf;
+
+ /* Open a DLPI device */
+ if ((sock = dlpiopen (info -> name)) < 0) {
+ log_fatal ("Can't open DLPI device for %s: %m", info -> name);
+ }
+
+ /*
+ * Submit a DL_INFO_REQ request, to find the dl_mac_type and
+ * dl_provider_style
+ */
+ if (dlpiinforeq(sock) < 0 || dlpiinfoack(sock, (char *)buf) < 0) {
+ log_fatal ("Can't get DLPI MAC type for %s: %m", info -> name);
+ } else {
+ switch (dlp -> info_ack.dl_mac_type) {
+ case DL_CSMACD: /* IEEE 802.3 */
+ case DL_ETHER:
+ info -> hw_address.hbuf [0] = HTYPE_ETHER;
+ break;
+ /* adding token ring 5/1999 - mayer@ping.at */
+ case DL_TPR:
+ info -> hw_address.hbuf [0] = HTYPE_IEEE802;
+ break;
+ case DL_FDDI:
+ info -> hw_address.hbuf [0] = HTYPE_FDDI;
+ break;
+ default:
+ log_fatal("%s: unsupported DLPI MAC type %lu", info->name,
+ (unsigned long)dlp->info_ack.dl_mac_type);
+ break;
+ }
+ /*
+ * copy the sap length and broadcast address of this interface
+ * to interface_info. This fixes nothing but seemed nicer than to
+ * assume -2 and ffffff.
+ */
+ info -> dlpi_sap_length = dlp -> info_ack.dl_sap_length;
+ info -> dlpi_broadcast_addr.hlen =
+ dlp -> info_ack.dl_brdcst_addr_length;
+ memcpy (info -> dlpi_broadcast_addr.hbuf,
+ (char *)dlp + dlp -> info_ack.dl_brdcst_addr_offset,
+ dlp -> info_ack.dl_brdcst_addr_length);
+ }
+
+ if (dlp -> info_ack.dl_provider_style == DL_STYLE2) {
+ /*
+ * Attach to the device. If this fails, the device
+ * does not exist.
+ */
+ unit = dlpiunit (info -> name);
+
+ if (dlpiattachreq (sock, unit) < 0
+ || dlpiokack (sock, (char *)buf) < 0) {
+ log_fatal ("Can't attach DLPI device for %s: %m", info -> name);
+ }
+ }
+
+ /*
+ * Bind to the IP service access point (SAP), connectionless (CLDLS).
+ */
+ if (dlpibindreq (sock, ETHERTYPE_IP, 0, DL_CLDLS, 0, 0) < 0
+ || dlpibindack (sock, (char *)buf) < 0) {
+ log_fatal ("Can't bind DLPI device for %s: %m", info -> name);
+ }
+
+ /*
+ * Submit a DL_PHYS_ADDR_REQ request, to find
+ * the hardware address
+ */
+ if (dlpiphysaddrreq (sock, DL_CURR_PHYS_ADDR) < 0
+ || dlpiphysaddrack (sock, (char *)buf) < 0) {
+ log_fatal ("Can't get DLPI hardware address for %s: %m",
+ info -> name);
+ }
+
+ info -> hw_address.hlen = dlp -> physaddr_ack.dl_addr_length + 1;
+ memcpy (&info -> hw_address.hbuf [1],
+ (char *)buf + dlp -> physaddr_ack.dl_addr_offset,
+ dlp -> physaddr_ack.dl_addr_length);
+
+#ifdef USE_DLPI_RAW
+ if (strioctl (sock, DLIOCRAW, INFTIM, 0, 0) < 0) {
+ log_fatal ("Can't set DLPI RAW mode for %s: %m",
+ info -> name);
+ }
+#endif
+
+#ifdef USE_DLPI_PFMOD
+ if (ioctl (sock, I_PUSH, "pfmod") < 0) {
+ log_fatal ("Can't push packet filter onto DLPI for %s: %m",
+ info -> name);
+ }
+#endif
+
+ return sock;
+}
+
+#if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW)
+static int
+strioctl (fd, cmd, timeout, len, dp)
+int fd;
+int cmd;
+int timeout;
+int len;
+char *dp;
+{
+ struct strioctl sio;
+ int rslt;
+
+ sio.ic_cmd = cmd;
+ sio.ic_timout = timeout;
+ sio.ic_len = len;
+ sio.ic_dp = dp;
+
+ if ((rslt = ioctl (fd, I_STR, &sio)) < 0) {
+ return rslt;
+ } else {
+ return sio.ic_len;
+ }
+}
+#endif /* USE_DPI_PFMOD || USE_DLPI_RAW */
+
+#ifdef USE_DLPI_SEND
+void if_register_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the DLPI API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_DLPI_RECEIVE
+# ifdef USE_DLPI_PFMOD
+ struct packetfilt pf;
+# endif
+
+ info -> wfdesc = if_register_dlpi (info);
+
+# ifdef USE_DLPI_PFMOD
+ /* Set up an PFMOD filter that rejects everything... */
+ pf.Pf_Priority = 0;
+ pf.Pf_FilterLen = 1;
+ pf.Pf_Filter [0] = ENF_PUSHZERO;
+
+ /* Install the filter */
+ if (strioctl (info -> wfdesc, PFIOCSETF, INFTIM,
+ sizeof (pf), (char *)&pf) < 0) {
+ log_fatal ("Can't set PFMOD send filter on %s: %m", info -> name);
+ }
+
+# endif /* USE_DLPI_PFMOD */
+#else /* !defined (USE_DLPI_RECEIVE) */
+ /*
+ * If using DLPI for both send and receive, simply re-use
+ * the read file descriptor that was set up earlier.
+ */
+ info -> wfdesc = info -> rfdesc;
+#endif
+
+ if (!quiet_interface_discovery)
+ log_info ("Sending on DLPI/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+
+#ifdef DLPI_FIRST_SEND_WAIT
+/* See the implementation notes at the beginning of this file */
+# ifdef USE_DLPI_RECEIVE
+ sleep (DLPI_FIRST_SEND_WAIT - (DLPI_FIRST_SEND_WAIT / 2));
+# else
+ sleep (DLPI_FIRST_SEND_WAIT);
+# endif
+#endif
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the DLPI API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_DLPI_RECEIVE
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on DLPI/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_DLPI_SEND */
+
+#ifdef USE_DLPI_RECEIVE
+/* Packet filter program...
+ XXX Changes to the filter program may require changes to the constant
+ offsets used in if_register_send to patch the NIT program! XXX */
+
+void if_register_receive (info)
+ struct interface_info *info;
+{
+#ifdef USE_DLPI_PFMOD
+ struct packetfilt pf;
+ struct ip iphdr;
+ u_int16_t offset;
+#endif
+
+ /* Open a DLPI device and hang it on this interface... */
+ info -> rfdesc = if_register_dlpi (info);
+
+#ifdef USE_DLPI_PFMOD
+ /* Set up the PFMOD filter program. */
+ /* XXX Unlike the BPF filter program, this one won't work if the
+ XXX IP packet is fragmented or if there are options on the IP
+ XXX header. */
+ pf.Pf_Priority = 0;
+ pf.Pf_FilterLen = 0;
+
+#if defined (USE_DLPI_RAW)
+# define ETHER_H_PREFIX (14) /* sizeof (ethernet_header) */
+ /*
+ * ethertype == ETHERTYPE_IP
+ */
+ offset = 12;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
+# else
+# define ETHER_H_PREFIX (0)
+# endif /* USE_DLPI_RAW */
+ /*
+ * The packets that will be received on this file descriptor
+ * will be IP packets (due to the SAP that was specified in
+ * the dlbind call). There will be no ethernet header.
+ * Therefore, setup the packet filter to check the protocol
+ * field for UDP, and the destination port number equal
+ * to the local port. All offsets are relative to the start
+ * of an IP packet.
+ */
+
+ /*
+ * BOOTPS destination port
+ */
+ offset = ETHER_H_PREFIX + sizeof (iphdr) + sizeof (u_int16_t);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
+
+ /*
+ * protocol should be udp. this is a byte compare, test for
+ * endianess.
+ */
+ offset = ETHER_H_PREFIX + ((u_int8_t *)&(iphdr.ip_p) - (u_int8_t *)&iphdr);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + (offset / 2);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_AND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0x00FF);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT | ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
+
+ /* Install the filter... */
+ if (strioctl (info -> rfdesc, PFIOCSETF, INFTIM,
+ sizeof (pf), (char *)&pf) < 0) {
+ log_fatal ("Can't set PFMOD receive filter on %s: %m", info -> name);
+ }
+#endif /* USE_DLPI_PFMOD */
+
+ if (!quiet_interface_discovery)
+ log_info ("Listening on DLPI/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+
+#ifdef DLPI_FIRST_SEND_WAIT
+/* See the implementation notes at the beginning of this file */
+# ifdef USE_DLPI_SEND
+ sleep (DLPI_FIRST_SEND_WAIT / 2);
+# else
+ sleep (DLPI_FIRST_SEND_WAIT);
+# endif
+#endif
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+ /* If we're using the DLPI API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_DLPI_SEND
+ close (info -> rfdesc);
+#endif
+ info -> rfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on DLPI/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_DLPI_RECEIVE */
+
+#ifdef USE_DLPI_SEND
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+#ifdef USE_DLPI_RAW
+ double hh [32];
+#endif
+ double ih [1536 / sizeof (double)];
+ unsigned char *dbuf = (unsigned char *)ih;
+ unsigned dbuflen;
+ unsigned char dstaddr [DLPI_MAXDLADDR];
+ unsigned addrlen;
+ int result;
+ int fudge;
+
+ if (!strcmp (interface -> name, "fallback"))
+ return send_fallback (interface, packet, raw,
+ len, from, to, hto);
+
+ if (hto == NULL && interface->anycast_mac_addr.hlen)
+ hto = &interface->anycast_mac_addr;
+
+ dbuflen = 0;
+
+ /* Assemble the headers... */
+#ifdef USE_DLPI_RAW
+ assemble_hw_header (interface, (unsigned char *)hh, &dbuflen, hto);
+ if (dbuflen > sizeof hh)
+ log_fatal ("send_packet: hh buffer too small.\n");
+ fudge = dbuflen % 4; /* IP header must be word-aligned. */
+ memcpy (dbuf + fudge, (unsigned char *)hh, dbuflen);
+ dbuflen += fudge;
+#else
+ fudge = 0;
+#endif
+ assemble_udp_ip_header (interface, dbuf, &dbuflen, from.s_addr,
+ to -> sin_addr.s_addr, to -> sin_port,
+ (unsigned char *)raw, len);
+
+ /* Copy the data into the buffer (yuk). */
+ memcpy (dbuf + dbuflen, raw, len);
+ dbuflen += len;
+
+#ifdef USE_DLPI_RAW
+ result = write (interface -> wfdesc, dbuf + fudge, dbuflen - fudge);
+#else
+
+ /*
+ * Setup the destination address (DLSAP) in dstaddr
+ *
+ * If sap_length < 0 we must deliver the DLSAP as phys+sap.
+ * If sap_length > 0 we must deliver the DLSAP as sap+phys.
+ *
+ * sap = Service Access Point == ETHERTYPE_IP
+ * sap + datalink address is called DLSAP in dlpi speak.
+ */
+ { /* ENCODE DLSAP */
+ unsigned char phys [DLPI_MAXDLADDR];
+ unsigned char sap [4];
+ int sap_len = interface -> dlpi_sap_length;
+ int phys_len = interface -> hw_address.hlen - 1;
+
+ /* sap = htons (ETHERTYPE_IP) kludge */
+ memset (sap, 0, sizeof (sap));
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+ sap [0] = 0x00;
+ sap [1] = 0x08;
+# else
+ sap [0] = 0x08;
+ sap [1] = 0x00;
+# endif
+
+ if (hto && hto -> hlen == interface -> hw_address.hlen)
+ memcpy ( phys, (char *) &hto -> hbuf [1], phys_len);
+ else
+ memcpy ( phys, interface -> dlpi_broadcast_addr.hbuf,
+ interface -> dlpi_broadcast_addr.hlen);
+
+ if (sap_len < 0) {
+ memcpy ( dstaddr, phys, phys_len);
+ memcpy ( (char *) &dstaddr [phys_len], sap, ABS (sap_len));
+ }
+ else {
+ memcpy ( dstaddr, (void *) sap, sap_len);
+ memcpy ( (char *) &dstaddr [sap_len], phys, phys_len);
+ }
+ addrlen = phys_len + ABS (sap_len);
+ } /* ENCODE DLSAP */
+
+ result = dlpiunitdatareq (interface -> wfdesc, dstaddr, addrlen,
+ 0, 0, dbuf, dbuflen);
+#endif /* USE_DLPI_RAW */
+ if (result < 0)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_DLPI_SEND */
+
+#ifdef USE_DLPI_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+ unsigned char dbuf [1536];
+ unsigned char srcaddr [DLPI_MAXDLADDR];
+ unsigned long srcaddrlen;
+ int length = 0;
+ int offset = 0;
+ int bufix = 0;
+ unsigned paylen;
+
+#ifdef USE_DLPI_RAW
+ length = read (interface -> rfdesc, dbuf, sizeof (dbuf));
+#else
+ length = dlpiunitdataind (interface -> rfdesc, (unsigned char *)NULL,
+ (unsigned long *)NULL, srcaddr, &srcaddrlen,
+ (unsigned long *)NULL, dbuf, sizeof (dbuf));
+#endif
+
+ if (length <= 0) {
+ log_error("receive_packet: %m");
+ return length;
+ }
+
+# if !defined (USE_DLPI_RAW)
+ /*
+ * Copy the sender's hw address into hfrom
+ * If sap_len < 0 the DLSAP is as phys+sap.
+ * If sap_len > 0 the DLSAP is as sap+phys.
+ *
+ * sap is discarded here.
+ */
+ { /* DECODE DLSAP */
+ int sap_len = interface -> dlpi_sap_length;
+ int phys_len = interface -> hw_address.hlen - 1;
+
+ if (hfrom && (srcaddrlen == ABS (sap_len) + phys_len )) {
+ hfrom -> hbuf [0] = interface -> hw_address.hbuf [0];
+ hfrom -> hlen = interface -> hw_address.hlen;
+
+ if (sap_len < 0) {
+ memcpy ((char *) &hfrom -> hbuf [1], srcaddr, phys_len);
+ }
+ else {
+ memcpy((char *)&hfrom->hbuf[1], srcaddr + sap_len, phys_len);
+ }
+ }
+ else if (hfrom) {
+ memset (hfrom, '\0', sizeof *hfrom);
+ }
+ } /* DECODE_DLSAP */
+
+# endif /* !defined (USE_DLPI_RAW) */
+
+ /* Decode the IP and UDP headers... */
+ bufix = 0;
+#ifdef USE_DLPI_RAW
+ /* Decode the physical header... */
+ offset = decode_hw_header (interface, dbuf, bufix, hfrom);
+
+ /* If a physical layer checksum failed (dunno of any
+ physical layer that supports this, but WTH), skip this
+ packet. */
+ if (offset < 0) {
+ return 0;
+ }
+ bufix += offset;
+ length -= offset;
+#endif
+ offset = decode_udp_ip_header (interface, dbuf, bufix,
+ from, length, &paylen);
+
+ /*
+ * If the IP or UDP checksum was bad, skip the packet...
+ *
+ * Note: this happens all the time when writing packets via the
+ * fallback socket. The packet received by streams does not have
+ * the IP or UDP checksums filled in, as those are calculated by
+ * the hardware.
+ */
+ if (offset < 0) {
+ return 0;
+ }
+
+ bufix += offset;
+ length -= offset;
+
+ if (length < paylen)
+ log_fatal("Internal inconsistency at %s:%d.", MDL);
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, &dbuf [bufix], paylen);
+ return paylen;
+}
+#endif
+
+/* Common DLPI routines ...
+ *
+ * Written by Eric James Negaard, <lmdejn@lmd.ericsson.se>
+ *
+ * Based largely in part to the example code contained in the document
+ * "How to Use the STREAMS Data Link Provider Interface (DLPI)", written
+ * by Neal Nuckolls of SunSoft Internet Engineering.
+ *
+ * This code has been developed and tested on sparc-based machines running
+ * SunOS 5.5.1, with le and hme network interfaces. It should be pretty
+ * generic, though.
+ *
+ * The usual disclaimers apply. This code works for me. Don't blame me
+ * if it makes your machine or network go down in flames. That taken
+ * into consideration, use this code as you wish. If you make usefull
+ * modifications I'd appreciate hearing about it.
+ */
+
+#define DLPI_MAXWAIT 15 /* Max timeout */
+
+
+/*
+ * Parse an interface name and extract the unit number
+ */
+
+static int dlpiunit (ifname)
+ char *ifname;
+{
+ char *cp;
+ int unit;
+
+ if (!ifname) {
+ return 0;
+ }
+
+ /* Advance to the end of the name */
+ cp = ifname;
+ while (*cp) cp++;
+ /* Back up to the start of the first digit */
+ while ((*(cp-1) >= '0' && *(cp-1) <= '9') || *(cp - 1) == ':') cp--;
+
+ /* Convert the unit number */
+ unit = 0;
+ while (*cp >= '0' && *cp <= '9') {
+ unit *= 10;
+ unit += (*cp++ - '0');
+ }
+
+ return unit;
+}
+
+/*
+ * dlpiopen - open the DLPI device for a given interface name
+ */
+static int
+dlpiopen(const char *ifname) {
+ char devname [50];
+ char *dp;
+ const char *cp, *ep;
+
+ if (!ifname) {
+ return -1;
+ }
+
+ /* Open a DLPI device */
+ if (*ifname == '/') {
+ dp = devname;
+ } else {
+ /* Prepend the device directory */
+ memcpy (devname, DLPI_DEVDIR, strlen (DLPI_DEVDIR));
+ dp = &devname [strlen (DLPI_DEVDIR)];
+ }
+
+ /* Find the end of the interface name */
+ ep = cp = ifname;
+ while (*ep)
+ ep++;
+ /* And back up to the first digit (unit number) */
+ while ((*(ep - 1) >= '0' && *(ep - 1) <= '9') || *(ep - 1) == ':')
+ ep--;
+
+ /* Copy everything up to the unit number */
+ while (cp < ep) {
+ *dp++ = *cp++;
+ }
+ *dp = '\0';
+
+ return open (devname, O_RDWR, 0);
+}
+
+/*
+ * dlpiinforeq - request information about the data link provider.
+ */
+
+static int dlpiinforeq (fd)
+ int fd;
+{
+ dl_info_req_t info_req;
+ struct strbuf ctl;
+ int flags;
+
+ info_req.dl_primitive = DL_INFO_REQ;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (info_req);
+ ctl.buf = (char *)&info_req;
+
+ flags = RS_HIPRI;
+
+ return putmsg (fd, &ctl, (struct strbuf *)NULL, flags);
+}
+
+/*
+ * dlpiphysaddrreq - request the current physical address.
+ */
+static int dlpiphysaddrreq (fd, addrtype)
+ int fd;
+ unsigned long addrtype;
+{
+ dl_phys_addr_req_t physaddr_req;
+ struct strbuf ctl;
+ int flags;
+
+ physaddr_req.dl_primitive = DL_PHYS_ADDR_REQ;
+ physaddr_req.dl_addr_type = addrtype;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (physaddr_req);
+ ctl.buf = (char *)&physaddr_req;
+
+ flags = RS_HIPRI;
+
+ return putmsg (fd, &ctl, (struct strbuf *)NULL, flags);
+}
+
+/*
+ * dlpiattachreq - send a request to attach to a specific unit.
+ */
+static int dlpiattachreq (fd, ppa)
+ unsigned long ppa;
+ int fd;
+{
+ dl_attach_req_t attach_req;
+ struct strbuf ctl;
+ int flags;
+
+ attach_req.dl_primitive = DL_ATTACH_REQ;
+ attach_req.dl_ppa = ppa;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (attach_req);
+ ctl.buf = (char *)&attach_req;
+
+ flags = 0;
+
+ return putmsg (fd, &ctl, (struct strbuf*)NULL, flags);
+}
+
+/*
+ * dlpibindreq - send a request to bind to a specific SAP address.
+ */
+static int dlpibindreq (fd, sap, max_conind, service_mode, conn_mgmt, xidtest)
+ unsigned long sap;
+ unsigned long max_conind;
+ unsigned long service_mode;
+ unsigned long conn_mgmt;
+ unsigned long xidtest;
+ int fd;
+{
+ dl_bind_req_t bind_req;
+ struct strbuf ctl;
+ int flags;
+
+ bind_req.dl_primitive = DL_BIND_REQ;
+ bind_req.dl_sap = sap;
+ bind_req.dl_max_conind = max_conind;
+ bind_req.dl_service_mode = service_mode;
+ bind_req.dl_conn_mgmt = conn_mgmt;
+ bind_req.dl_xidtest_flg = xidtest;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (bind_req);
+ ctl.buf = (char *)&bind_req;
+
+ flags = 0;
+
+ return putmsg (fd, &ctl, (struct strbuf*)NULL, flags);
+}
+
+#if defined(UNUSED_DLPI_INTERFACE)
+/*
+ * dlpiunbindreq - send a request to unbind. This function is not actually
+ * used by ISC DHCP, but is included for completeness in case it is
+ * ever required for new work.
+ */
+static int dlpiunbindreq (fd)
+ int fd;
+{
+ dl_unbind_req_t unbind_req;
+ struct strbuf ctl;
+ int flags;
+
+ unbind_req.dl_primitive = DL_UNBIND_REQ;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (unbind_req);
+ ctl.buf = (char *)&unbind_req;
+
+ flags = 0;
+
+ return putmsg (fd, &ctl, (struct strbuf*)NULL, flags);
+}
+
+
+/*
+ * dlpidetachreq - send a request to detach. This function is not actually
+ * used by ISC DHCP, but is included for completeness in case it is
+ * ever required for new work.
+ */
+static int dlpidetachreq (fd)
+ int fd;
+{
+ dl_detach_req_t detach_req;
+ struct strbuf ctl;
+ int flags;
+
+ detach_req.dl_primitive = DL_DETACH_REQ;
+
+ ctl.maxlen = 0;
+ ctl.len = sizeof (detach_req);
+ ctl.buf = (char *)&detach_req;
+
+ flags = 0;
+
+ return putmsg (fd, &ctl, (struct strbuf*)NULL, flags);
+}
+#endif /* UNUSED_DLPI_INTERFACE */
+
+
+/*
+ * dlpibindack - receive an ack to a dlbindreq.
+ */
+static int dlpibindack (fd, bufp)
+ char *bufp;
+ int fd;
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+
+ ctl.maxlen = DLPI_MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ if (strgetmsg (fd, &ctl,
+ (struct strbuf*)NULL, &flags, "dlpibindack") < 0) {
+ return -1;
+ }
+
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if (expected (DL_BIND_ACK, dlp, flags) == -1) {
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_bind_ack_t)) {
+ /* Returned structure is too short */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * dlpiokack - general acknowledgement reception.
+ */
+static int dlpiokack (fd, bufp)
+ char *bufp;
+ int fd;
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+
+ ctl.maxlen = DLPI_MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ if (strgetmsg (fd, &ctl,
+ (struct strbuf*)NULL, &flags, "dlpiokack") < 0) {
+ return -1;
+ }
+
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if (expected (DL_OK_ACK, dlp, flags) == -1) {
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_ok_ack_t)) {
+ /* Returned structure is too short */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * dlpiinfoack - receive an ack to a dlinforeq.
+ */
+static int dlpiinfoack (fd, bufp)
+ char *bufp;
+ int fd;
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+
+ ctl.maxlen = DLPI_MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags,
+ "dlpiinfoack") < 0) {
+ return -1;
+ }
+
+ dlp = (union DL_primitives *) ctl.buf;
+
+ if (expected (DL_INFO_ACK, dlp, flags) == -1) {
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_info_ack_t)) {
+ /* Returned structure is too short */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq.
+ */
+int dlpiphysaddrack (fd, bufp)
+ char *bufp;
+ int fd;
+{
+ union DL_primitives *dlp;
+ struct strbuf ctl;
+ int flags;
+
+ ctl.maxlen = DLPI_MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = bufp;
+
+ if (strgetmsg (fd, &ctl, (struct strbuf *)NULL, &flags,
+ "dlpiphysaddrack") < 0) {
+ return -1;
+ }
+
+ dlp = (union DL_primitives *)ctl.buf;
+
+ if (expected (DL_PHYS_ADDR_ACK, dlp, flags) == -1) {
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_phys_addr_ack_t)) {
+ /* Returned structure is too short */
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE)
+int dlpiunitdatareq (fd, addr, addrlen, minpri, maxpri, dbuf, dbuflen)
+ int fd;
+ unsigned char *addr;
+ int addrlen;
+ unsigned long minpri;
+ unsigned long maxpri;
+ unsigned char *dbuf;
+ int dbuflen;
+{
+ long buf [DLPI_MAXDLBUF];
+ union DL_primitives *dlp;
+ struct strbuf ctl, data;
+
+ /* Set up the control information... */
+ dlp = (union DL_primitives *)buf;
+ dlp -> unitdata_req.dl_primitive = DL_UNITDATA_REQ;
+ dlp -> unitdata_req.dl_dest_addr_length = addrlen;
+ dlp -> unitdata_req.dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
+ dlp -> unitdata_req.dl_priority.dl_min = minpri;
+ dlp -> unitdata_req.dl_priority.dl_max = maxpri;
+
+ /* Append the destination address */
+ memcpy ((char *)buf + dlp -> unitdata_req.dl_dest_addr_offset,
+ addr, addrlen);
+
+ ctl.maxlen = 0;
+ ctl.len = dlp -> unitdata_req.dl_dest_addr_offset + addrlen;
+ ctl.buf = (char *)buf;
+
+ data.maxlen = 0;
+ data.buf = (char *)dbuf;
+ data.len = dbuflen;
+
+ /* Send the packet down the wire... */
+ return putmsg (fd, &ctl, &data, 0);
+}
+
+static int dlpiunitdataind (fd, daddr, daddrlen,
+ saddr, saddrlen, grpaddr, dbuf, dlen)
+ int fd;
+ unsigned char *daddr;
+ unsigned long *daddrlen;
+ unsigned char *saddr;
+ unsigned long *saddrlen;
+ unsigned long *grpaddr;
+ unsigned char *dbuf;
+ int dlen;
+{
+ long buf [DLPI_MAXDLBUF];
+ union DL_primitives *dlp;
+ struct strbuf ctl, data;
+ int flags = 0;
+ int result;
+
+ /* Set up the msg_buf structure... */
+ dlp = (union DL_primitives *)buf;
+ dlp -> unitdata_ind.dl_primitive = DL_UNITDATA_IND;
+
+ ctl.maxlen = DLPI_MAXDLBUF;
+ ctl.len = 0;
+ ctl.buf = (char *)buf;
+
+ data.maxlen = dlen;
+ data.len = 0;
+ data.buf = (char *)dbuf;
+
+ result = getmsg (fd, &ctl, &data, &flags);
+
+ if (result < 0) {
+ log_debug("dlpiunitdataind: %m");
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_unitdata_ind_t) ||
+ dlp -> unitdata_ind.dl_primitive != DL_UNITDATA_IND) {
+ return -1;
+ }
+
+ if (data.len <= 0) {
+ return data.len;
+ }
+
+ /* Copy sender info */
+ if (saddr) {
+ memcpy (saddr,
+ (char *)buf + dlp -> unitdata_ind.dl_src_addr_offset,
+ dlp -> unitdata_ind.dl_src_addr_length);
+ }
+ if (saddrlen) {
+ *saddrlen = dlp -> unitdata_ind.dl_src_addr_length;
+ }
+
+ /* Copy destination info */
+ if (daddr) {
+ memcpy (daddr,
+ (char *)buf + dlp -> unitdata_ind.dl_dest_addr_offset,
+ dlp -> unitdata_ind.dl_dest_addr_length);
+ }
+ if (daddrlen) {
+ *daddrlen = dlp -> unitdata_ind.dl_dest_addr_length;
+ }
+
+ if (grpaddr) {
+ *grpaddr = dlp -> unitdata_ind.dl_group_address;
+ }
+
+ return data.len;
+}
+#endif /* !USE_DLPI_HWADDR: USE_DLPI_RECEIVE || USE_DLPI_SEND */
+
+/*
+ * expected - see if we got what we wanted.
+ */
+static int expected (prim, dlp, msgflags)
+ unsigned long prim;
+ union DL_primitives *dlp;
+ int msgflags;
+{
+ if (msgflags != RS_HIPRI) {
+ /* Message was not M_PCPROTO */
+ return -1;
+ }
+
+ if (dlp->dl_primitive != prim) {
+ /* Incorrect/unexpected return message */
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * strgetmsg - get a message from a stream, with timeout.
+ */
+static int strgetmsg (fd, ctlp, datap, flagsp, caller)
+ struct strbuf *ctlp, *datap;
+ char *caller;
+ int *flagsp;
+ int fd;
+{
+ int result;
+ struct pollfd pfd;
+ int count;
+ time_t now;
+ time_t starttime;
+ int to_msec;
+
+ pfd.fd = fd;
+ pfd.events = POLLPRI; /* We're only interested in knowing
+ * when we can receive the next high
+ * priority message.
+ */
+ pfd.revents = 0;
+
+ now = time (&starttime);
+ while (now <= starttime + DLPI_MAXWAIT) {
+ to_msec = ((starttime + DLPI_MAXWAIT) - now) * 1000;
+ count = poll (&pfd, 1, to_msec);
+
+ if (count == 0) {
+ /* log_fatal ("strgetmsg: timeout"); */
+ return -1;
+ } else if (count < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ time (&now);
+ continue;
+ } else {
+ /* log_fatal ("poll: %m"); */
+ return -1;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /*
+ * Set flags argument and issue getmsg ().
+ */
+ *flagsp = 0;
+ if ((result = getmsg (fd, ctlp, datap, flagsp)) < 0) {
+ return result;
+ }
+
+ /*
+ * Check for MOREDATA and/or MORECTL.
+ */
+ if (result & (MORECTL|MOREDATA)) {
+ return -1;
+ }
+
+ /*
+ * Check for at least sizeof (long) control data portion.
+ */
+ if (ctlp -> len < sizeof (long)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined(USE_DLPI_SEND)
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+void maybe_setup_fallback ()
+{
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ if_register_fallback (fbi);
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+}
+#endif /* USE_DLPI_SEND */
+
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+ int sock, unit;
+ long buf[DLPI_MAXDLBUF];
+ union DL_primitives *dlp;
+
+ dlp = (union DL_primitives *)buf;
+
+ /*
+ * Open a DLPI device.
+ */
+ sock = dlpiopen(name);
+ if (sock < 0) {
+ log_fatal("Can't open DLPI device for %s: %m", name);
+ }
+
+ /*
+ * Submit a DL_INFO_REQ request, to find the dl_mac_type and
+ * dl_provider_style
+ */
+ if (dlpiinforeq(sock) < 0) {
+ log_fatal("Can't request DLPI MAC type for %s: %m", name);
+ }
+ if (dlpiinfoack(sock, (char *)buf) < 0) {
+ log_fatal("Can't get DLPI MAC type for %s: %m", name);
+ }
+ switch (dlp->info_ack.dl_mac_type) {
+ case DL_CSMACD: /* IEEE 802.3 */
+ case DL_ETHER:
+ hw->hbuf[0] = HTYPE_ETHER;
+ break;
+ case DL_TPR:
+ hw->hbuf[0] = HTYPE_IEEE802;
+ break;
+ case DL_FDDI:
+ hw->hbuf[0] = HTYPE_FDDI;
+ break;
+ default:
+ log_fatal("%s: unsupported DLPI MAC type %lu", name,
+ (unsigned long)dlp->info_ack.dl_mac_type);
+ }
+
+ if (dlp->info_ack.dl_provider_style == DL_STYLE2) {
+ /*
+ * Attach to the device. If this fails, the device
+ * does not exist.
+ */
+ unit = dlpiunit((char *)name);
+
+ if (dlpiattachreq(sock, unit) < 0 ||
+ dlpiokack(sock, (char *)buf) < 0) {
+ log_fatal("Can't attach DLPI device for %s: %m",
+ name);
+ }
+ }
+
+ /*
+ * Submit a DL_PHYS_ADDR_REQ request, to find
+ * the hardware address.
+ */
+ if (dlpiphysaddrreq(sock, DL_CURR_PHYS_ADDR) < 0) {
+ log_fatal("Can't request DLPI hardware address for %s: %m",
+ name);
+ }
+ if (dlpiphysaddrack(sock, (char *)buf) < 0) {
+ log_fatal("Can't get DLPI hardware address for %s: %m",
+ name);
+ }
+ if (dlp->physaddr_ack.dl_addr_length < sizeof(hw->hbuf)) {
+ memcpy(hw->hbuf+1,
+ (char *)buf + dlp->physaddr_ack.dl_addr_offset,
+ dlp->physaddr_ack.dl_addr_length);
+ hw->hlen = dlp->physaddr_ack.dl_addr_length + 1;
+ } else {
+ memcpy(hw->hbuf+1,
+ (char *)buf + dlp->physaddr_ack.dl_addr_offset,
+ sizeof(hw->hbuf)-1);
+ hw->hlen = sizeof(hw->hbuf);
+ }
+
+ close(sock);
+}
+#endif /* USE_DLPI_SEND || USE_DLPI_RECEIVE || USE_DLPI_HWADDR */
diff --git a/common/dns.c b/common/dns.c
new file mode 100644
index 0000000..5ecae67
--- /dev/null
+++ b/common/dns.c
@@ -0,0 +1,1706 @@
+/* dns.c
+
+ Domain Name Service subroutines. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * The original software was written for Internet Systems Consortium
+ * by Ted Lemon it has since been extensively modified to use the
+ * asynchronous DNS routines.
+ */
+
+#include "dhcpd.h"
+#include "arpa/nameser.h"
+#include <isc/md5.h>
+
+#include <dns/result.h>
+
+/*
+ * This file contains code to connect the DHCP code to the libdns modules.
+ * As part of that function it maintains a database of zone cuts that can
+ * be used to figure out which server should be contacted to update any
+ * given domain name. Included in the zone information may be a pointer
+ * to a key in which case that key is used for the update. If no zone
+ * is found then the DNS code determines the zone on its own.
+ *
+ * The way this works is that you define the domain name to which an
+ * SOA corresponds, and the addresses of some primaries for that domain name:
+ *
+ * zone FOO.COM {
+ * primary 10.0.17.1;
+ * secondary 10.0.22.1, 10.0.23.1;
+ * key "FOO.COM Key";
+ * }
+ *
+ * If an update is requested for GAZANGA.TOPANGA.FOO.COM, then the name
+ * server looks in its database for a zone record for "GAZANGA.TOPANGA.FOO.COM",
+ * doesn't find it, looks for one for "TOPANGA.FOO.COM", doesn't find *that*,
+ * looks for "FOO.COM", finds it. So it
+ * attempts the update to the primary for FOO.COM. If that times out, it
+ * tries the secondaries. You can list multiple primaries if you have some
+ * kind of magic name server that supports that. You shouldn't list
+ * secondaries that don't know how to forward updates (e.g., BIND 8 doesn't
+ * support update forwarding, AFAIK). If no TSIG key is listed, the update
+ * is attempted without TSIG.
+ *
+ * The DHCP server tries to find an existing zone for any given name by
+ * trying to look up a local zone structure for each domain containing
+ * that name, all the way up to '.'. If it finds one cached, it tries
+ * to use that one to do the update. That's why it tries to update
+ * "FOO.COM" above, even though theoretically it should try GAZANGA...
+ * and TOPANGA... first.
+ *
+ * If the update fails with a predefined zone the zone is marked as bad
+ * and another search of the predefined zones is done. If no predefined
+ * zone is found finding a zone is left to the DNS module via examination
+ * of SOA records. If the DNS module finds a zone it may cache the zone
+ * but the zone won't be cached here.
+ *
+ * TSIG updates are not performed on zones found by the DNS module - if
+ * you want TSIG updates you _must_ write a zone definition linking the
+ * key to the zone. In cases where you know for sure what the key is
+ * but do not want to hardcode the IP addresses of the primary or
+ * secondaries, a zone declaration can be made that doesn't include any
+ * primary or secondary declarations. When the DHCP server encounters
+ * this while hunting up a matching zone for a name, it looks up the SOA,
+ * fills in the IP addresses, and uses that record for the update.
+ * If the SOA lookup returns NXRRSET, a warning is printed and the zone is
+ * discarded, TSIG key and all. The search for the zone then continues
+ * as if the zone record hadn't been found. Zones without IP addresses
+ * don't match when initially hunting for a zone to update.
+ *
+ * When an update is attempted and no predefined zone is found
+ * that matches any enclosing domain of the domain being updated, the DHCP
+ * server goes through the same process that is done when the update to a
+ * predefined zone fails - starting with the most specific domain
+ * name (GAZANGA.TOPANGA.FOO.COM) and moving to the least specific (the root),
+ * it tries to look up an SOA record.
+ *
+ * TSIG keys are defined like this:
+ *
+ * key "FOO.COM Key" {
+ * algorithm HMAC-MD5.SIG-ALG.REG.INT;
+ * secret <Base64>;
+ * }
+ *
+ * <Base64> is a number expressed in base64 that represents the key.
+ * It's also permissible to use a quoted string here - this will be
+ * translated as the ASCII bytes making up the string, and will not
+ * include any NUL termination. The key name can be any text string,
+ * and the key type must be one of the key types defined in the draft
+ * or by the IANA. Currently only the HMAC-MD5... key type is
+ * supported.
+ *
+ * The DDNS processing has been split into two areas. One is the
+ * control code that determines what should be done. That code is found
+ * in the client or server directories. The other is the common code
+ * that performs functions such as properly formatting the arguments.
+ * That code is found in this file. The basic processing flow for a
+ * DDNS update is:
+ * In the client or server code determine what needs to be done and
+ * collect the necesary information then pass it to a function from
+ * this file.
+ * In this code lookup the zone and extract the zone and key information
+ * (if available) and prepare the arguments for the DNS module.
+ * When the DNS module completes its work (times out or gets a reply)
+ * it will trigger another function here which does generic processing
+ * and then passes control back to the code from the server or client.
+ * The server or client code then determines the next step which may
+ * result in another call to this module in which case the process repeats.
+ */
+
+dns_zone_hash_t *dns_zone_hash;
+
+/*
+ * DHCP dns structures
+ * Normally the relationship between these structures isn't one to one
+ * but in the DHCP case it (mostly) is. To make the allocations, frees,
+ * and passing of the memory easier we make a single structure with all
+ * the pieces.
+ *
+ * The maximum size of the data buffer should be large enough for any
+ * items DHCP will generate
+ */
+
+typedef struct dhcp_ddns_rdata {
+ dns_rdata_t rdata;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+} dhcp_ddns_data_t;
+
+#if defined (NSUPDATE)
+
+void ddns_interlude(isc_task_t *, isc_event_t *);
+
+
+#if defined (TRACING)
+/*
+ * Code to support tracing DDNS packets. We trace packets going to and
+ * coming from the libdns code but don't try to track the packets
+ * exchanged between the libdns code and the dns server(s) it contacts.
+ *
+ * The code is split into two sets of routines
+ * input refers to messages received from the dns module
+ * output refers to messages sent to the dns module
+ * Currently there are three routines in each set
+ * write is used to write information about the message to the trace file
+ * this routine is called directly from the proper place in the code.
+ * read is used to read information about a message from the trace file
+ * this routine is called from the trace loop as it reads through
+ * the file and is registered via the trace_type_register routine.
+ * When playing back a trace file we shall absorb records of output
+ * messages as part of processing the write function, therefore
+ * any output messages we encounter are flagged as errors.
+ * stop isn't currently used in this code but is needed for the register
+ * routine.
+ *
+ * We pass a pointer to a control block to the dns module which it returns
+ * to use as part of the result. As the pointer may vary between traces
+ * we need to map between those from the trace file and the new ones during
+ * playback.
+ *
+ * The mapping is complicated a little as a pointer could be 4 or 8 bytes
+ * long. We treat the old pointer as an 8 byte quantity and pad and compare
+ * as necessary.
+ */
+
+/*
+ * Structure used to map old pointers to new pointers.
+ * Old pointers are 8 bytes long as we don't know if the trace was
+ * done on a 64 bit or 32 bit machine.
+ */
+#define TRACE_PTR_LEN 8
+
+typedef struct dhcp_ddns_map {
+ char old_pointer[TRACE_PTR_LEN];
+ void *new_pointer;
+ struct dhcp_ddns_map *next;
+} dhcp_ddns_map_t;
+
+/* The starting point for the map structure */
+static dhcp_ddns_map_t *ddns_map;
+
+trace_type_t *trace_ddns_input;
+trace_type_t *trace_ddns_output;
+
+/*
+ * The data written to the trace file is:
+ * 32 bits result from dns
+ * 64 bits pointer of cb
+ */
+
+void
+trace_ddns_input_write(dhcp_ddns_cb_t *ddns_cb, isc_result_t result)
+{
+ trace_iov_t iov[2];
+ u_int32_t old_result;
+ char old_pointer[TRACE_PTR_LEN];
+
+ old_result = htonl((u_int32_t)result);
+ memset(old_pointer, 0, TRACE_PTR_LEN);
+ memcpy(old_pointer, &ddns_cb, sizeof(ddns_cb));
+
+ iov[0].len = sizeof(old_result);
+ iov[0].buf = (char *)&old_result;
+ iov[1].len = TRACE_PTR_LEN;
+ iov[1].buf = old_pointer;
+ trace_write_packet_iov(trace_ddns_input, 2, iov, MDL);
+}
+
+/*
+ * Process the result and pointer from the trace file.
+ * We use the pointer map to find the proper pointer for this instance.
+ * Then we need to construct an event to pass along to the interlude
+ * function.
+ */
+static void
+trace_ddns_input_read(trace_type_t *ttype, unsigned length,
+ char *buf)
+{
+ u_int32_t old_result;
+ char old_pointer[TRACE_PTR_LEN];
+ dns_clientupdateevent_t *eventp;
+ void *new_pointer;
+ dhcp_ddns_map_t *ddns_map_ptr;
+
+ if (length < (sizeof(old_result) + TRACE_PTR_LEN)) {
+ log_error("trace_ddns_input_read: data too short");
+ return;
+ }
+
+ memcpy(&old_result, buf, sizeof(old_result));
+ memcpy(old_pointer, buf + sizeof(old_result), TRACE_PTR_LEN);
+
+ /* map the old pointer to a new pointer */
+ for (ddns_map_ptr = ddns_map;
+ ddns_map_ptr != NULL;
+ ddns_map_ptr = ddns_map_ptr->next) {
+ if ((ddns_map_ptr->new_pointer != NULL) &&
+ memcmp(ddns_map_ptr->old_pointer,
+ old_pointer, TRACE_PTR_LEN) == 0) {
+ new_pointer = ddns_map_ptr->new_pointer;
+ ddns_map_ptr->new_pointer = NULL;
+ memset(ddns_map_ptr->old_pointer, 0, TRACE_PTR_LEN);
+ break;
+ }
+ }
+ if (ddns_map_ptr == NULL) {
+ log_error("trace_dns_input_read: unable to map cb pointer");
+ return;
+ }
+
+ eventp = (dns_clientupdateevent_t *)
+ isc_event_allocate(dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.task,
+ 0,
+ ddns_interlude,
+ new_pointer,
+ sizeof(dns_clientupdateevent_t));
+ if (eventp == NULL) {
+ log_error("trace_ddns_input_read: unable to allocate event");
+ return;
+ }
+ eventp->result = ntohl(old_result);
+
+
+ ddns_interlude(dhcp_gbl_ctx.task, (isc_event_t *)eventp);
+
+ return;
+}
+
+static void
+trace_ddns_input_stop(trace_type_t *ttype)
+{
+}
+
+/*
+ * We use the same arguments as for the dns startupdate function to
+ * allows us to choose between the two via a macro. If tracing isn't
+ * in use we simply call the dns function directly.
+ *
+ * If we are doing playback we read the next packet from the file
+ * and compare the type. If it matches we extract the results and pointer
+ * from the trace file. The results are returned to the caller as if
+ * they had called the dns routine. The pointer is used to construct a
+ * map for when the "reply" is processed.
+ *
+ * The data written to trace file is:
+ * 32 bits result
+ * 64 bits pointer of cb (DDNS Control block)
+ * contents of cb
+ */
+
+isc_result_t
+trace_ddns_output_write(dns_client_t *client, dns_rdataclass_t rdclass,
+ dns_name_t *zonename, dns_namelist_t *prerequisites,
+ dns_namelist_t *updates, isc_sockaddrlist_t *servers,
+ dns_tsec_t *tsec, unsigned int options,
+ isc_task_t *task, isc_taskaction_t action, void *arg,
+ dns_clientupdatetrans_t **transp)
+{
+ isc_result_t result;
+ u_int32_t old_result;
+ char old_pointer[TRACE_PTR_LEN];
+ dhcp_ddns_map_t *ddns_map_ptr;
+
+ if (trace_playback() != 0) {
+ /* We are doing playback, extract the entry from the file */
+ unsigned buflen = 0;
+ char *inbuf = NULL;
+
+ result = trace_get_packet(&trace_ddns_output,
+ &buflen, &inbuf);
+ if (result != ISC_R_SUCCESS) {
+ log_error("trace_ddns_output_write: no input found");
+ return (ISC_R_FAILURE);
+ }
+ if (buflen < (sizeof(old_result) + TRACE_PTR_LEN)) {
+ log_error("trace_ddns_output_write: data too short");
+ dfree(inbuf, MDL);
+ return (ISC_R_FAILURE);
+ }
+ memcpy(&old_result, inbuf, sizeof(old_result));
+ result = ntohl(old_result);
+ memcpy(old_pointer, inbuf + sizeof(old_result), TRACE_PTR_LEN);
+ dfree(inbuf, MDL);
+
+ /* add the pointer to the pointer map */
+ for (ddns_map_ptr = ddns_map;
+ ddns_map_ptr != NULL;
+ ddns_map_ptr = ddns_map_ptr->next) {
+ if (ddns_map_ptr->new_pointer == NULL) {
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find an empty entry, allocate an entry and
+ * link it into the list. The list isn't ordered.
+ */
+ if (ddns_map_ptr == NULL) {
+ ddns_map_ptr = dmalloc(sizeof(*ddns_map_ptr), MDL);
+ if (ddns_map_ptr == NULL) {
+ log_error("trace_ddns_output_write: "
+ "unable to allocate map entry");
+ return(ISC_R_FAILURE);
+ }
+ ddns_map_ptr->next = ddns_map;
+ ddns_map = ddns_map_ptr;
+ }
+
+ memcpy(ddns_map_ptr->old_pointer, old_pointer, TRACE_PTR_LEN);
+ ddns_map_ptr->new_pointer = arg;
+ }
+ else {
+ /* We aren't doing playback, make the actual call */
+ result = dns_client_startupdate(client, rdclass, zonename,
+ prerequisites, updates,
+ servers, tsec, options,
+ task, action, arg, transp);
+ }
+
+ if (trace_record() != 0) {
+ /* We are recording, save the information to the file */
+ trace_iov_t iov[3];
+ old_result = htonl((u_int32_t)result);
+ memset(old_pointer, 0, TRACE_PTR_LEN);
+ memcpy(old_pointer, &arg, sizeof(arg));
+ iov[0].len = sizeof(old_result);
+ iov[0].buf = (char *)&old_result;
+ iov[1].len = TRACE_PTR_LEN;
+ iov[1].buf = old_pointer;
+
+ /* Write out the entire cb, in case we want to look at it */
+ iov[2].len = sizeof(dhcp_ddns_cb_t);
+ iov[2].buf = (char *)arg;
+
+ trace_write_packet_iov(trace_ddns_output, 3, iov, MDL);
+ }
+
+ return(result);
+}
+
+static void
+trace_ddns_output_read(trace_type_t *ttype, unsigned length,
+ char *buf)
+{
+ log_error("unaccounted for ddns output.");
+}
+
+static void
+trace_ddns_output_stop(trace_type_t *ttype)
+{
+}
+
+void
+trace_ddns_init()
+{
+ trace_ddns_output = trace_type_register("ddns-output", NULL,
+ trace_ddns_output_read,
+ trace_ddns_output_stop, MDL);
+ trace_ddns_input = trace_type_register("ddns-input", NULL,
+ trace_ddns_input_read,
+ trace_ddns_input_stop, MDL);
+ ddns_map = NULL;
+}
+
+#define ddns_update trace_ddns_output_write
+#else
+#define ddns_update dns_client_startupdate
+#endif /* TRACING */
+
+/*
+ * Code to allocate and free a dddns control block. This block is used
+ * to pass and track the information associated with a DDNS update request.
+ */
+dhcp_ddns_cb_t *
+ddns_cb_alloc(const char *file, int line)
+{
+ dhcp_ddns_cb_t *ddns_cb;
+ int i;
+
+ ddns_cb = dmalloc(sizeof(*ddns_cb), file, line);
+ if (ddns_cb != NULL) {
+ ISC_LIST_INIT(ddns_cb->zone_server_list);
+ for (i = 0; i < DHCP_MAXNS; i++) {
+ ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
+ }
+ }
+
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("%s(%d): Allocating ddns_cb=%p", file, line, ddns_cb);
+#endif
+
+ return(ddns_cb);
+}
+
+void
+ddns_cb_free(dhcp_ddns_cb_t *ddns_cb, const char *file, int line)
+{
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("%s(%d): freeing ddns_cb=%p", file, line, ddns_cb);
+#endif
+
+ data_string_forget(&ddns_cb->fwd_name, file, line);
+ data_string_forget(&ddns_cb->rev_name, file, line);
+ data_string_forget(&ddns_cb->dhcid, file, line);
+
+ if (ddns_cb->zone != NULL) {
+ forget_zone((struct dns_zone **)&ddns_cb->zone);
+ }
+
+ /* Should be freed by now, check just in case. */
+ if (ddns_cb->transaction != NULL)
+ log_error("Impossible memory leak at %s:%d (attempt to free "
+ "DDNS Control Block before transaction).", MDL);
+
+ dfree(ddns_cb, file, line);
+}
+
+void
+ddns_cb_forget_zone(dhcp_ddns_cb_t *ddns_cb)
+{
+ int i;
+
+ forget_zone(&ddns_cb->zone);
+ ddns_cb->zone_name[0] = 0;
+ ISC_LIST_INIT(ddns_cb->zone_server_list);
+ for (i = 0; i < DHCP_MAXNS; i++) {
+ ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
+ }
+}
+
+isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
+ struct dns_zone *zone)
+{
+ ns_tsig_key *tkey;
+
+ if (!zone)
+ return ISC_R_NOTFOUND;
+
+ if (!zone -> key) {
+ return DHCP_R_KEY_UNKNOWN;
+ }
+
+ if ((!zone -> key -> name ||
+ strlen (zone -> key -> name) > NS_MAXDNAME) ||
+ (!zone -> key -> algorithm ||
+ strlen (zone -> key -> algorithm) > NS_MAXDNAME) ||
+ (!zone -> key) ||
+ (!zone -> key -> key) ||
+ (zone -> key -> key -> len == 0)) {
+ return DHCP_R_INVALIDKEY;
+ }
+ tkey = dmalloc (sizeof *tkey, MDL);
+ if (!tkey) {
+ nomem:
+ return ISC_R_NOMEMORY;
+ }
+ memset (tkey, 0, sizeof *tkey);
+ tkey -> data = dmalloc (zone -> key -> key -> len, MDL);
+ if (!tkey -> data) {
+ dfree (tkey, MDL);
+ goto nomem;
+ }
+ strcpy (tkey -> name, zone -> key -> name);
+ strcpy (tkey -> alg, zone -> key -> algorithm);
+ memcpy (tkey -> data,
+ zone -> key -> key -> value, zone -> key -> key -> len);
+ tkey -> len = zone -> key -> key -> len;
+ *key = tkey;
+ return ISC_R_SUCCESS;
+}
+
+void tkey_free (ns_tsig_key **key)
+{
+ if ((*key) -> data)
+ dfree ((*key) -> data, MDL);
+ dfree ((*key), MDL);
+ *key = (ns_tsig_key *)0;
+}
+#endif
+
+isc_result_t enter_dns_zone (struct dns_zone *zone)
+{
+ struct dns_zone *tz = (struct dns_zone *)0;
+
+ if (dns_zone_hash) {
+ dns_zone_hash_lookup (&tz,
+ dns_zone_hash, zone -> name, 0, MDL);
+ if (tz == zone) {
+ dns_zone_dereference (&tz, MDL);
+ return ISC_R_SUCCESS;
+ }
+ if (tz) {
+ dns_zone_hash_delete (dns_zone_hash,
+ zone -> name, 0, MDL);
+ dns_zone_dereference (&tz, MDL);
+ }
+ } else {
+ if (!dns_zone_new_hash(&dns_zone_hash, DNS_HASH_SIZE, MDL))
+ return ISC_R_NOMEMORY;
+ }
+
+ dns_zone_hash_add (dns_zone_hash, zone -> name, 0, zone, MDL);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dns_zone_lookup (struct dns_zone **zone, const char *name)
+{
+ int len;
+ char *tname = (char *)0;
+ isc_result_t status;
+
+ if (!dns_zone_hash)
+ return ISC_R_NOTFOUND;
+
+ len = strlen (name);
+ if (name [len - 1] != '.') {
+ tname = dmalloc ((unsigned)len + 2, MDL);
+ if (!tname)
+ return ISC_R_NOMEMORY;
+ strcpy (tname, name);
+ tname [len] = '.';
+ tname [len + 1] = 0;
+ name = tname;
+ }
+ if (!dns_zone_hash_lookup (zone, dns_zone_hash, name, 0, MDL))
+ status = ISC_R_NOTFOUND;
+ else
+ status = ISC_R_SUCCESS;
+
+ if (tname)
+ dfree (tname, MDL);
+ return status;
+}
+
+int dns_zone_dereference (ptr, file, line)
+ struct dns_zone **ptr;
+ const char *file;
+ int line;
+{
+ struct dns_zone *dns_zone;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ dns_zone = *ptr;
+ *ptr = (struct dns_zone *)0;
+ --dns_zone -> refcnt;
+ rc_register (file, line, ptr, dns_zone, dns_zone -> refcnt, 1, RC_MISC);
+ if (dns_zone -> refcnt > 0)
+ return 1;
+
+ if (dns_zone -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (dns_zone);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (dns_zone -> name)
+ dfree (dns_zone -> name, file, line);
+ if (dns_zone -> key)
+ omapi_auth_key_dereference (&dns_zone -> key, file, line);
+ if (dns_zone -> primary)
+ option_cache_dereference (&dns_zone -> primary, file, line);
+ if (dns_zone -> secondary)
+ option_cache_dereference (&dns_zone -> secondary, file, line);
+ dfree (dns_zone, file, line);
+ return 1;
+}
+
+#if defined (NSUPDATE)
+isc_result_t
+find_cached_zone(dhcp_ddns_cb_t *ddns_cb, int direction)
+{
+ isc_result_t status = ISC_R_NOTFOUND;
+ const char *np;
+ struct dns_zone *zone = (struct dns_zone *)0;
+ struct data_string nsaddrs;
+ struct in_addr zone_addr;
+ int ix;
+
+ if (direction == FIND_FORWARD) {
+ np = (const char *)ddns_cb->fwd_name.data;
+ } else {
+ np = (const char *)ddns_cb->rev_name.data;
+ }
+
+ /* We can't look up a null zone. */
+ if ((np == NULL) || (*np == '\0')) {
+ return DHCP_R_INVALIDARG;
+ }
+
+ /*
+ * For each subzone, try to find a cached zone.
+ */
+ for (;;) {
+ status = dns_zone_lookup (&zone, np);
+ if (status == ISC_R_SUCCESS)
+ break;
+
+ np = strchr(np, '.');
+ if (np == NULL)
+ break;
+ np++;
+ }
+
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Make sure the zone is valid. */
+ if (zone -> timeout && zone -> timeout < cur_time) {
+ dns_zone_dereference (&zone, MDL);
+ return ISC_R_CANCELED;
+ }
+
+ /* Make sure the zone name will fit. */
+ if (strlen(zone->name) > sizeof(ddns_cb->zone_name)) {
+ dns_zone_dereference (&zone, MDL);
+ return ISC_R_NOSPACE;
+ }
+ strcpy((char *)&ddns_cb->zone_name[0], zone->name);
+
+ memset (&nsaddrs, 0, sizeof nsaddrs);
+ ix = 0;
+
+ if (zone -> primary) {
+ if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ zone -> primary, MDL)) {
+ int ip = 0;
+ while (ix < DHCP_MAXNS) {
+ if (ip + 4 > nsaddrs.len)
+ break;
+ memcpy(&zone_addr, &nsaddrs.data[ip], 4);
+ isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
+ &zone_addr,
+ NS_DEFAULTPORT);
+ ISC_LIST_APPEND(ddns_cb->zone_server_list,
+ &ddns_cb->zone_addrs[ix],
+ link);
+ ip += 4;
+ ix++;
+ }
+ data_string_forget (&nsaddrs, MDL);
+ }
+ }
+ if (zone -> secondary) {
+ if (evaluate_option_cache (&nsaddrs, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ zone -> secondary, MDL)) {
+ int ip = 0;
+ while (ix < DHCP_MAXNS) {
+ if (ip + 4 > nsaddrs.len)
+ break;
+ memcpy(&zone_addr, &nsaddrs.data[ip], 4);
+ isc_sockaddr_fromin(&ddns_cb->zone_addrs[ix],
+ &zone_addr,
+ NS_DEFAULTPORT);
+ ISC_LIST_APPEND(ddns_cb->zone_server_list,
+ &ddns_cb->zone_addrs[ix],
+ link);
+ ip += 4;
+ ix++;
+ }
+ data_string_forget (&nsaddrs, MDL);
+ }
+ }
+
+ dns_zone_reference(&ddns_cb->zone, zone, MDL);
+ dns_zone_dereference (&zone, MDL);
+ return ISC_R_SUCCESS;
+}
+
+void forget_zone (struct dns_zone **zone)
+{
+ dns_zone_dereference (zone, MDL);
+}
+
+void repudiate_zone (struct dns_zone **zone)
+{
+ /* XXX Currently we're not differentiating between a cached
+ XXX zone and a zone that's been repudiated, which means
+ XXX that if we reap cached zones, we blow away repudiated
+ XXX zones. This isn't a big problem since we're not yet
+ XXX caching zones... :'} */
+
+ (*zone) -> timeout = cur_time - 1;
+ dns_zone_dereference (zone, MDL);
+}
+
+/* Have to use TXT records for now. */
+#define T_DHCID T_TXT
+
+int get_dhcid (struct data_string *id,
+ int type, const u_int8_t *data, unsigned len)
+{
+ unsigned char buf[ISC_MD5_DIGESTLENGTH];
+ isc_md5_t md5;
+ int i;
+
+ /* Types can only be 0..(2^16)-1. */
+ if (type < 0 || type > 65535)
+ return 0;
+
+ /*
+ * Hexadecimal MD5 digest plus two byte type, NUL,
+ * and one byte for length for dns.
+ */
+ if (!buffer_allocate (&id -> buffer,
+ (ISC_MD5_DIGESTLENGTH * 2) + 4, MDL))
+ return 0;
+ id -> data = id -> buffer -> data;
+
+ /*
+ * DHCP clients and servers should use the following forms of client
+ * identification, starting with the most preferable, and finishing
+ * with the least preferable. If the client does not send any of these
+ * forms of identification, the DHCP/DDNS interaction is not defined by
+ * this specification. The most preferable form of identification is
+ * the Globally Unique Identifier Option [TBD]. Next is the DHCP
+ * Client Identifier option. Last is the client's link-layer address,
+ * as conveyed in its DHCPREQUEST message. Implementors should note
+ * that the link-layer address cannot be used if there are no
+ * significant bytes in the chaddr field of the DHCP client's request,
+ * because this does not constitute a unique identifier.
+ * -- "Interaction between DHCP and DNS"
+ * <draft-ietf-dhc-dhcp-dns-12.txt>
+ * M. Stapp, Y. Rekhter
+ *
+ * We put the length into the first byte to turn
+ * this into a dns text string. This avoid needing to
+ * copy the string to add the byte later.
+ */
+ id->buffer->data[0] = ISC_MD5_DIGESTLENGTH * 2 + 2;
+
+ /* Put the type in the next two bytes. */
+ id->buffer->data[1] = "0123456789abcdef"[(type >> 4) & 0xf];
+ /* This should have been [type & 0xf] but now that
+ * it is in use we need to leave it this way in order
+ * to avoid disturbing customer's lease files
+ */
+ id->buffer->data[2] = "0123456789abcdef"[type % 15];
+
+ /* Mash together an MD5 hash of the identifier. */
+ isc_md5_init(&md5);
+ isc_md5_update(&md5, data, len);
+ isc_md5_final(&md5, buf);
+
+ /* Convert into ASCII. */
+ for (i = 0; i < ISC_MD5_DIGESTLENGTH; i++) {
+ id->buffer->data[i * 2 + 3] =
+ "0123456789abcdef"[(buf[i] >> 4) & 0xf];
+ id->buffer->data[i * 2 + 4] =
+ "0123456789abcdef"[buf[i] & 0xf];
+ }
+
+ id->len = ISC_MD5_DIGESTLENGTH * 2 + 3;
+ id->buffer->data[id->len] = 0;
+ id->terminated = 1;
+
+ return 1;
+}
+
+/*
+ * The dhcid (text version) that we pass to DNS includes a length byte
+ * at the start but the text we store in the lease doesn't include the
+ * length byte. The following routines are to convert between the two
+ * styles.
+ *
+ * When converting from a dhcid to a leaseid we reuse the buffer and
+ * simply adjust the data pointer and length fields in the data string.
+ * This avoids any prolems with allocating space.
+ */
+
+void
+dhcid_tolease(struct data_string *dhcid,
+ struct data_string *leaseid)
+{
+ /* copy the data string then update the fields */
+ data_string_copy(leaseid, dhcid, MDL);
+ leaseid->data++;
+ leaseid->len--;
+}
+
+isc_result_t
+dhcid_fromlease(struct data_string *dhcid,
+ struct data_string *leaseid)
+{
+ if (!buffer_allocate(&dhcid->buffer, leaseid->len + 2, MDL)) {
+ return(ISC_R_FAILURE);
+ }
+
+ dhcid->data = dhcid->buffer->data;
+
+ dhcid->buffer->data[0] = leaseid->len;
+ memcpy(dhcid->buffer->data + 1, leaseid->data, leaseid->len);
+ dhcid->len = leaseid->len + 1;
+ if (leaseid->terminated == 1) {
+ dhcid->buffer->data[dhcid->len] = 0;
+ dhcid->terminated = 1;
+ }
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * Construct the dataset for this item.
+ * This is a fairly simple arrangement as the operations we do are simple.
+ * If there is data we simply have the rdata point to it - the formatting
+ * must be correct already. We then link the rdatalist to the rdata and
+ * create a rdataset from the rdatalist.
+ */
+
+static isc_result_t
+make_dns_dataset(dns_rdataclass_t dataclass,
+ dns_rdatatype_t datatype,
+ dhcp_ddns_data_t *dataspace,
+ unsigned char *data,
+ int datalen,
+ int ttl)
+{
+ dns_rdata_t *rdata = &dataspace->rdata;
+ dns_rdatalist_t *rdatalist = &dataspace->rdatalist;
+ dns_rdataset_t *rdataset = &dataspace->rdataset;
+
+ isc_region_t region;
+
+ /* set up the rdata */
+ dns_rdata_init(rdata);
+
+ if (data == NULL) {
+ /* No data, set up the rdata fields we care about */
+ rdata->flags = DNS_RDATA_UPDATE;
+ rdata->type = datatype;
+ rdata->rdclass = dataclass;
+ } else {
+ switch(datatype) {
+ case dns_rdatatype_a:
+ case dns_rdatatype_aaaa:
+ case dns_rdatatype_txt:
+ case dns_rdatatype_dhcid:
+ case dns_rdatatype_ptr:
+ /* The data must be in the right format we simply
+ * need to supply it via the correct structure */
+ region.base = data;
+ region.length = datalen;
+ dns_rdata_fromregion(rdata, dataclass, datatype,
+ &region);
+ break;
+ default:
+ return(DHCP_R_INVALIDARG);
+ break;
+ }
+ }
+
+ /* setup the datalist and attach the rdata to it */
+ dns_rdatalist_init(rdatalist);
+ rdatalist->type = datatype;
+ rdatalist->rdclass = dataclass;
+ rdatalist->ttl = ttl;
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+
+ /* convert the datalist to a dataset */
+ dns_rdataset_init(rdataset);
+ dns_rdatalist_tordataset(rdatalist, rdataset);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * When a DHCP client or server intends to update an A RR, it first
+ * prepares a DNS UPDATE query which includes as a prerequisite the
+ * assertion that the name does not exist. The update section of the
+ * query attempts to add the new name and its IP address mapping (an A
+ * RR), and the DHCID RR with its unique client-identity.
+ * -- "Interaction between DHCP and DNS"
+ *
+ * There are two cases, one for the server and one for the client.
+ *
+ * For the server the first step will have a request of:
+ * The name is not in use
+ * Add an A RR
+ * Add a DHCID RR (currently txt)
+ *
+ * For the client the first step will have a request of:
+ * The A RR does not exist
+ * Add an A RR
+ * Add a DHCID RR (currently txt)
+ */
+
+static isc_result_t
+ddns_modify_fwd_add1(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+
+ /* Construct the prerequisite list */
+ if ((ddns_cb->flags & DDNS_INCLUDE_RRSET) != 0) {
+ /* The A RR shouldn't exist */
+ result = make_dns_dataset(dns_rdataclass_none,
+ ddns_cb->address_type,
+ dataspace, NULL, 0, 0);
+ } else {
+ /* The name is not in use */
+ result = make_dns_dataset(dns_rdataclass_none,
+ dns_rdatatype_any,
+ dataspace, NULL, 0, 0);
+ }
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Construct the update list */
+ /* Add the A RR */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add the DHCID RR */
+ result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * If the first update operation fails with YXDOMAIN, the updater can
+ * conclude that the intended name is in use. The updater then
+ * attempts to confirm that the DNS name is not being used by some
+ * other host. The updater prepares a second UPDATE query in which the
+ * prerequisite is that the desired name has attached to it a DHCID RR
+ * whose contents match the client identity. The update section of
+ * this query deletes the existing A records on the name, and adds the
+ * A record that matches the DHCP binding and the DHCID RR with the
+ * client identity.
+ * -- "Interaction between DHCP and DNS"
+ *
+ * The message for the second step depends on if we are doing conflict
+ * resolution. If we are we include a prerequisite. If not we delete
+ * the DHCID in addition to all A rrsets.
+ *
+ * Conflict resolution:
+ * DHCID RR exists, and matches client identity.
+ * Delete A RRset.
+ * Add A RR.
+ *
+ * Conflict override:
+ * Delete DHCID RRs.
+ * Add DHCID RR
+ * Delete A RRset.
+ * Add A RR.
+ */
+
+static isc_result_t
+ddns_modify_fwd_add2(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+
+ /*
+ * If we are doing conflict resolution (unset) we use a prereq list.
+ * If not we delete the DHCID in addition to all A rrsets.
+ */
+ if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
+ /* Construct the prereq list */
+ /* The DHCID RR exists and matches the client identity */
+ result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+ } else {
+ /* Start constructing the update list.
+ * Conflict detection override: delete DHCID RRs */
+ result = make_dns_dataset(dns_rdataclass_any,
+ dns_rdatatype_txt,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add current DHCID RR */
+ result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+ }
+
+ /* Start or continue constructing the update list */
+ /* Delete the A RRset */
+ result = make_dns_dataset(dns_rdataclass_any, ddns_cb->address_type,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Add the A RR */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * The entity chosen to handle the A record for this client (either the
+ * client or the server) SHOULD delete the A record that was added when
+ * the lease was made to the client.
+ *
+ * In order to perform this delete, the updater prepares an UPDATE
+ * query which contains two prerequisites. The first prerequisite
+ * asserts that the DHCID RR exists whose data is the client identity
+ * described in Section 4.3. The second prerequisite asserts that the
+ * data in the A RR contains the IP address of the lease that has
+ * expired or been released.
+ * -- "Interaction between DHCP and DNS"
+ *
+ * First try has:
+ * DHCID RR exists, and matches client identity.
+ * A RR matches the expiring lease.
+ * Delete appropriate A RR.
+ */
+
+static isc_result_t
+ddns_modify_fwd_rem1(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+
+ /* Consruct the prereq list */
+ /* The DHCID RR exists and matches the client identity */
+ result = make_dns_dataset(dns_rdataclass_in, dns_rdatatype_txt,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* The A RR matches the expiring lease */
+ result = make_dns_dataset(dns_rdataclass_in, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Construct the update list */
+ /* Delete A RRset */
+ result = make_dns_dataset(dns_rdataclass_none, ddns_cb->address_type,
+ dataspace,
+ (unsigned char *)ddns_cb->address.iabuf,
+ ddns_cb->address.len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * If the deletion of the A succeeded, and there are no A or AAAA
+ * records left for this domain, then we can blow away the DHCID
+ * record as well. We can't blow away the DHCID record above
+ * because it's possible that more than one record has been added
+ * to this domain name.
+ *
+ * Second query has:
+ * A RR does not exist.
+ * AAAA RR does not exist.
+ * Delete appropriate DHCID RR.
+ */
+
+static isc_result_t
+ddns_modify_fwd_rem2(dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_data_t *dataspace,
+ dns_name_t *pname,
+ dns_name_t *uname)
+{
+ isc_result_t result;
+
+ /* Construct the prereq list */
+ /* The A RR does not exist */
+ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_a,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* The AAAA RR does not exist */
+ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_aaaa,
+ dataspace, NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(pname->list, &dataspace->rdataset, link);
+ dataspace++;
+
+ /* Construct the update list */
+ /* Delete DHCID RR */
+ result = make_dns_dataset(dns_rdataclass_none, dns_rdatatype_txt,
+ dataspace,
+ (unsigned char *)ddns_cb->dhcid.data,
+ ddns_cb->dhcid.len, 0);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace->rdataset, link);
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * This routine converts from the task action call into something
+ * easier to work with. It also handles the common case of a signature
+ * or zone not being correct.
+ */
+void ddns_interlude(isc_task_t *taskp,
+ isc_event_t *eventp)
+{
+ dhcp_ddns_cb_t *ddns_cb = (dhcp_ddns_cb_t *)eventp->ev_arg;
+ dns_clientupdateevent_t *ddns_event = (dns_clientupdateevent_t *)eventp;
+ isc_result_t eresult = ddns_event->result;
+ isc_result_t result;
+
+ /* We've extracted the information we want from it, get rid of
+ * the event block.*/
+ isc_event_free(&eventp);
+
+#if defined (TRACING)
+ if (trace_record()) {
+ trace_ddns_input_write(ddns_cb, eresult);
+ }
+#endif
+
+#if defined (DEBUG_DNS_UPDATES)
+ print_dns_status(DDNS_PRINT_INBOUND, ddns_cb, eresult);
+#endif
+
+ /* This transaction is complete, clear the value */
+ dns_client_destroyupdatetrans(&ddns_cb->transaction);
+
+ /* If we cancelled or tried to cancel the operation we just
+ * need to clean up. */
+ if ((eresult == ISC_R_CANCELED) ||
+ ((ddns_cb->flags & DDNS_ABORT) != 0)) {
+ if (ddns_cb->next_op != NULL) {
+ /* if necessary cleanup up next op block */
+ ddns_cb_free(ddns_cb->next_op, MDL);
+ }
+ ddns_cb_free(ddns_cb, MDL);
+ return;
+ }
+
+ /* If we had a problem with our key or zone try again */
+ if ((eresult == DNS_R_NOTAUTH) ||
+ (eresult == DNS_R_NOTZONE)) {
+ int i;
+ /* Our zone information was questionable,
+ * repudiate it and try again */
+ repudiate_zone(&ddns_cb->zone);
+ ddns_cb->zone_name[0] = 0;
+ ISC_LIST_INIT(ddns_cb->zone_server_list);
+ for (i = 0; i < DHCP_MAXNS; i++) {
+ ISC_LINK_INIT(&ddns_cb->zone_addrs[i], link);
+ }
+
+ if ((ddns_cb->state &
+ (DDNS_STATE_ADD_PTR | DDNS_STATE_REM_PTR)) != 0) {
+ result = ddns_modify_ptr(ddns_cb);
+ } else {
+ result = ddns_modify_fwd(ddns_cb);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ /* if we couldn't redo the query toss it */
+ if (ddns_cb->next_op != NULL) {
+ /* cleanup up next op block */
+ ddns_cb_free(ddns_cb->next_op, MDL);
+ }
+ ddns_cb_free(ddns_cb, MDL);
+ }
+ return;
+ } else {
+ /* pass it along to be processed */
+ ddns_cb->cur_func(ddns_cb, eresult);
+ }
+
+ return;
+}
+
+/*
+ * This routine does the generic work for sending a ddns message to
+ * modify the forward record (A or AAAA) and calls one of a set of
+ * routines to build the specific message.
+ */
+
+isc_result_t
+ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb)
+{
+ isc_result_t result;
+ dns_tsec_t *tsec_key = NULL;
+
+ unsigned char *clientname;
+ dhcp_ddns_data_t *dataspace = NULL;
+ dns_namelist_t prereqlist, updatelist;
+ dns_fixedname_t zname0, pname0, uname0;
+ dns_name_t *zname = NULL, *pname, *uname;
+
+ isc_sockaddrlist_t *zlist = NULL;
+
+ /* Get a pointer to the clientname to make things easier. */
+ clientname = (unsigned char *)ddns_cb->fwd_name.data;
+
+ /* Extract and validate the type of the address. */
+ if (ddns_cb->address.len == 4) {
+ ddns_cb->address_type = dns_rdatatype_a;
+ } else if (ddns_cb->address.len == 16) {
+ ddns_cb->address_type = dns_rdatatype_aaaa;
+ } else {
+ return DHCP_R_INVALIDARG;
+ }
+
+ /*
+ * If we already have a zone use it, otherwise try to lookup the
+ * zone in our cache. If we find one we will have a pointer to
+ * the zone that needs to be dereferenced when we are done with it.
+ * If we don't find one that is okay we'll let the DNS code try and
+ * find the information for us.
+ */
+
+ if (ddns_cb->zone == NULL) {
+ result = find_cached_zone(ddns_cb, FIND_FORWARD);
+ }
+
+ /*
+ * If we have a zone try to get any information we need
+ * from it - name, addresses and the key. The address
+ * and key may be empty the name can't be.
+ */
+ if (ddns_cb->zone) {
+ /* Set up the zone name for use by DNS */
+ result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
+ if (result != ISC_R_SUCCESS) {
+ log_error("Unable to build name for zone for "
+ "fwd update: %s %s",
+ ddns_cb->zone_name,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
+ /* If we have any addresses get them */
+ zlist = &ddns_cb->zone_server_list;
+ }
+
+
+ if (ddns_cb->zone->key != NULL) {
+ /*
+ * Not having a key is fine, having a key
+ * but not a tsec is odd so we warn the user.
+ */
+ /*sar*/
+ /* should we do the warning? */
+ tsec_key = ddns_cb->zone->key->tsec_key;
+ if (tsec_key == NULL) {
+ log_error("No tsec for use with key %s",
+ ddns_cb->zone->key->name);
+ }
+ }
+ }
+
+ /* Set up the DNS names for the prereq and update lists */
+ if (((result = dhcp_isc_name(clientname, &pname0, &pname))
+ != ISC_R_SUCCESS) ||
+ ((result = dhcp_isc_name(clientname, &uname0, &uname))
+ != ISC_R_SUCCESS)) {
+ log_error("Unable to build name for fwd update: %s %s",
+ clientname, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /* Allocate the various isc dns library structures we may require. */
+ dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 4);
+ if (dataspace == NULL) {
+ log_error("Unable to allocate memory for fwd update");
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ ISC_LIST_INIT(prereqlist);
+ ISC_LIST_INIT(updatelist);
+
+ switch(ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ result = ddns_modify_fwd_add1(ddns_cb, dataspace,
+ pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ break;
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ result = ddns_modify_fwd_add2(ddns_cb, dataspace,
+ pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* If we aren't doing conflict override we have entries
+ * in the pname list and we need to attach it to the
+ * prereqlist */
+
+ if ((ddns_cb->flags & DDNS_CONFLICT_OVERRIDE) == 0) {
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ }
+
+ break;
+ case DDNS_STATE_REM_FW_YXDHCID:
+ result = ddns_modify_fwd_rem1(ddns_cb, dataspace,
+ pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ break;
+ case DDNS_STATE_REM_FW_NXRR:
+ result = ddns_modify_fwd_rem2(ddns_cb, dataspace,
+ pname, uname);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(prereqlist, pname, link);
+ break;
+
+ default:
+ log_error("Invalid operation in ddns code.");
+ result = DHCP_R_INVALIDARG;
+ goto cleanup;
+ break;
+ }
+
+ /*
+ * We always have an update list but may not have a prereqlist
+ * if we are doing conflict override.
+ */
+ ISC_LIST_APPEND(updatelist, uname, link);
+
+ /* send the message, cleanup and return the result */
+ result = ddns_update(dhcp_gbl_ctx.dnsclient,
+ dns_rdataclass_in, zname,
+ &prereqlist, &updatelist,
+ zlist, tsec_key,
+ DNS_CLIENTRESOPT_ALLOWRUN,
+ dhcp_gbl_ctx.task,
+ ddns_interlude,
+ (void *)ddns_cb,
+ &ddns_cb->transaction);
+ if (result == ISC_R_FAMILYNOSUPPORT) {
+ log_info("Unable to perform DDNS update, "
+ "address family not supported");
+ }
+
+#if defined (DEBUG_DNS_UPDATES)
+ print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
+#endif
+
+ cleanup:
+ if (dataspace != NULL) {
+ isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
+ sizeof(*dataspace) * 4);
+ }
+ return(result);
+}
+
+
+isc_result_t
+ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb)
+{
+ isc_result_t result;
+ dns_tsec_t *tsec_key = NULL;
+ unsigned char *ptrname;
+ dhcp_ddns_data_t *dataspace = NULL;
+ dns_namelist_t updatelist;
+ dns_fixedname_t zname0, uname0;
+ dns_name_t *zname = NULL, *uname;
+ isc_sockaddrlist_t *zlist = NULL;
+ unsigned char buf[256];
+ int buflen;
+
+ /*
+ * Try to lookup the zone in the zone cache. As with the forward
+ * case it's okay if we don't have one, the DNS code will try to
+ * find something also if we succeed we will need to dereference
+ * the zone later. Unlike with the forward case we assume we won't
+ * have a pre-existing zone.
+ */
+ result = find_cached_zone(ddns_cb, FIND_REVERSE);
+ if ((result == ISC_R_SUCCESS) &&
+ !(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
+ /* Set up the zone name for use by DNS */
+ result = dhcp_isc_name(ddns_cb->zone_name, &zname0, &zname);
+ if (result != ISC_R_SUCCESS) {
+ log_error("Unable to build name for zone for "
+ "fwd update: %s %s",
+ ddns_cb->zone_name,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ /* If we have any addresses get them */
+ if (!(ISC_LIST_EMPTY(ddns_cb->zone_server_list))) {
+ zlist = &ddns_cb->zone_server_list;
+ }
+
+ /*
+ * If we now have a zone try to get the key, NULL is okay,
+ * having a key but not a tsec is odd so we warn.
+ */
+ /*sar*/
+ /* should we do the warning if we have a key but no tsec? */
+ if ((ddns_cb->zone != NULL) && (ddns_cb->zone->key != NULL)) {
+ tsec_key = ddns_cb->zone->key->tsec_key;
+ if (tsec_key == NULL) {
+ log_error("No tsec for use with key %s",
+ ddns_cb->zone->key->name);
+ }
+ }
+ }
+
+ /* We must have a name for the update list */
+ /* Get a pointer to the ptrname to make things easier. */
+ ptrname = (unsigned char *)ddns_cb->rev_name.data;
+
+ if ((result = dhcp_isc_name(ptrname, &uname0, &uname))
+ != ISC_R_SUCCESS) {
+ log_error("Unable to build name for fwd update: %s %s",
+ ptrname, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ /*
+ * Allocate the various isc dns library structures we may require.
+ * Allocating one blob avoids being halfway through the process
+ * and being unable to allocate as well as making the free easy.
+ */
+ dataspace = isc_mem_get(dhcp_gbl_ctx.mctx, sizeof(*dataspace) * 2);
+ if (dataspace == NULL) {
+ log_error("Unable to allocate memory for fwd update");
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ ISC_LIST_INIT(updatelist);
+
+ /*
+ * Construct the update list
+ * We always delete what's currently there
+ * Delete PTR RR.
+ */
+ result = make_dns_dataset(dns_rdataclass_any, dns_rdatatype_ptr,
+ &dataspace[0], NULL, 0, 0);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace[0].rdataset, link);
+
+ /*
+ * If we are updating the pointer we then add the new one
+ * Add PTR RR.
+ */
+ if (ddns_cb->state == DDNS_STATE_ADD_PTR) {
+#if 0
+ /*
+ * I've left this dead code in the file for now in case
+ * we decide to try and get rid of the ns_name functions.
+ * sar
+ */
+
+ /*
+ * Need to convert pointer into on the wire representation
+ * We replace the '.' characters with the lengths of the
+ * next name and add a length to the beginning for the first
+ * name.
+ */
+ if (ddns_cb->fwd_name.len == 1) {
+ /* the root */
+ buf[0] = 0;
+ buflen = 1;
+ } else {
+ unsigned char *cp;
+ buf[0] = '.';
+ memcpy(&buf[1], ddns_cb->fwd_name.data,
+ ddns_cb->fwd_name.len);
+ for(cp = buf + ddns_cb->fwd_name.len, buflen = 0;
+ cp != buf;
+ cp--) {
+ if (*cp == '.') {
+ *cp = buflen;
+ buflen = 0;
+ } else {
+ buflen++;
+ }
+ }
+ *cp = buflen;
+ buflen = ddns_cb->fwd_name.len + 1;
+ }
+#endif
+ /*
+ * Need to convert pointer into on the wire representation
+ */
+ if (MRns_name_pton((char *)ddns_cb->fwd_name.data,
+ buf, 256) == -1) {
+ goto cleanup;
+ }
+ buflen = 0;
+ while (buf[buflen] != 0) {
+ buflen += buf[buflen] + 1;
+ }
+ buflen++;
+
+ result = make_dns_dataset(dns_rdataclass_in,
+ dns_rdatatype_ptr,
+ &dataspace[1],
+ buf, buflen, ddns_cb->ttl);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ ISC_LIST_APPEND(uname->list, &dataspace[1].rdataset, link);
+ }
+
+ ISC_LIST_APPEND(updatelist, uname, link);
+
+ /*sar*/
+ /*
+ * for now I'll cleanup the dataset immediately, it would be
+ * more efficient to keep it around in case the signaturure failed
+ * and we wanted to retry it.
+ */
+ /* send the message, cleanup and return the result */
+ result = ddns_update((dns_client_t *)dhcp_gbl_ctx.dnsclient,
+ dns_rdataclass_in, zname,
+ NULL, &updatelist,
+ zlist, tsec_key,
+ DNS_CLIENTRESOPT_ALLOWRUN,
+ dhcp_gbl_ctx.task,
+ ddns_interlude, (void *)ddns_cb,
+ &ddns_cb->transaction);
+ if (result == ISC_R_FAMILYNOSUPPORT) {
+ log_info("Unable to perform DDNS update, "
+ "address family not supported");
+ }
+
+#if defined (DEBUG_DNS_UPDATES)
+ print_dns_status(DDNS_PRINT_OUTBOUND, ddns_cb, result);
+#endif
+
+ cleanup:
+ if (dataspace != NULL) {
+ isc_mem_put(dhcp_gbl_ctx.mctx, dataspace,
+ sizeof(*dataspace) * 2);
+ }
+ return(result);
+}
+
+void
+ddns_cancel(dhcp_ddns_cb_t *ddns_cb) {
+ ddns_cb->flags |= DDNS_ABORT;
+ if (ddns_cb->transaction != NULL) {
+ dns_client_cancelupdate((dns_clientupdatetrans_t *)
+ ddns_cb->transaction);
+ }
+ ddns_cb->lease = NULL;
+}
+
+#endif /* NSUPDATE */
+
+HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
+ dns_zone_reference, dns_zone_dereference, do_case_hash)
diff --git a/common/ethernet.c b/common/ethernet.c
new file mode 100644
index 0000000..7b8a22c
--- /dev/null
+++ b/common/ethernet.c
@@ -0,0 +1,93 @@
+/* ethernet.c
+
+ Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
+#include "includes/netinet/if_ether.h"
+#endif /* PACKET_ASSEMBLY || PACKET_DECODING */
+
+#if defined (PACKET_ASSEMBLY)
+/* Assemble an hardware header... */
+
+void assemble_ethernet_header (interface, buf, bufix, to)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned *bufix;
+ struct hardware *to;
+{
+ struct isc_ether_header eh;
+
+ if (to && to -> hlen == 7) /* XXX */
+ memcpy (eh.ether_dhost, &to -> hbuf [1],
+ sizeof eh.ether_dhost);
+ else
+ memset (eh.ether_dhost, 0xff, sizeof (eh.ether_dhost));
+ if (interface -> hw_address.hlen - 1 == sizeof (eh.ether_shost))
+ memcpy (eh.ether_shost, &interface -> hw_address.hbuf [1],
+ 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;
+}
+#endif /* PACKET_ASSEMBLY */
+
+#ifdef PACKET_DECODING
+/* Decode a hardware header... */
+
+ssize_t decode_ethernet_header (interface, buf, bufix, from)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned bufix;
+ struct hardware *from;
+{
+ struct isc_ether_header eh;
+
+ memcpy (&eh, buf + bufix, ETHER_HEADER_SIZE);
+
+#ifdef USERLAND_FILTER
+ if (ntohs (eh.ether_type) != ETHERTYPE_IP)
+ return -1;
+#endif
+ memcpy (&from -> hbuf [1], eh.ether_shost, sizeof (eh.ether_shost));
+ from -> hbuf [0] = ARPHRD_ETHER;
+ from -> hlen = (sizeof eh.ether_shost) + 1;
+
+ return ETHER_HEADER_SIZE;
+}
+#endif /* PACKET_DECODING */
diff --git a/common/execute.c b/common/execute.c
new file mode 100644
index 0000000..9d08207
--- /dev/null
+++ b/common/execute.c
@@ -0,0 +1,1152 @@
+/* execute.c
+
+ Support for executable statements. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+int execute_statements (result, packet, lease, client_state,
+ in_options, out_options, scope, statements)
+ struct binding_value **result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *out_options;
+ struct binding_scope **scope;
+ struct executable_statement *statements;
+{
+ struct executable_statement *r, *e, *next;
+ int rc;
+ int status;
+ struct binding *binding;
+ struct data_string ds;
+ struct binding_scope *ns;
+
+ if (!statements)
+ return 1;
+
+ r = (struct executable_statement *)0;
+ next = (struct executable_statement *)0;
+ e = (struct executable_statement *)0;
+ executable_statement_reference (&r, statements, MDL);
+ while (r && !(result && *result)) {
+ if (r -> next)
+ executable_statement_reference (&next, r -> next, MDL);
+ switch (r -> op) {
+ case statements_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: statements");
+#endif
+ status = execute_statements (result, packet, lease,
+ client_state, in_options,
+ out_options, scope,
+ r -> data.statements);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: statements returns %d", status);
+#endif
+ if (!status)
+ return 0;
+ break;
+
+ case on_statement:
+ if (lease) {
+ if (r -> data.on.evtypes & ON_EXPIRY) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on expiry");
+#endif
+ if (lease -> on_expiry)
+ executable_statement_dereference
+ (&lease -> on_expiry, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_expiry,
+ r -> data.on.statements, MDL);
+ }
+ if (r -> data.on.evtypes & ON_RELEASE) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on release");
+#endif
+ if (lease -> on_release)
+ executable_statement_dereference
+ (&lease -> on_release, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_release,
+ r -> data.on.statements, MDL);
+ }
+ if (r -> data.on.evtypes & ON_COMMIT) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: on commit");
+#endif
+ if (lease -> on_commit)
+ executable_statement_dereference
+ (&lease -> on_commit, MDL);
+ if (r -> data.on.statements)
+ executable_statement_reference
+ (&lease -> on_commit,
+ r -> data.on.statements, MDL);
+ }
+ }
+ break;
+
+ case switch_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: switch");
+#endif
+ status = (find_matching_case
+ (&e, packet, lease, client_state,
+ in_options, out_options, scope,
+ r -> data.s_switch.expr,
+ r -> data.s_switch.statements));
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: switch: case %lx", (unsigned long)e);
+#endif
+ if (status) {
+ if (!(execute_statements
+ (result, packet, lease, client_state,
+ in_options, out_options, scope, e))) {
+ executable_statement_dereference
+ (&e, MDL);
+ return 0;
+ }
+ executable_statement_dereference (&e, MDL);
+ }
+ break;
+
+ /* These have no effect when executed. */
+ case case_statement:
+ case default_statement:
+ break;
+
+ case if_statement:
+ status = (evaluate_boolean_expression
+ (&rc, packet,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.ie.expr));
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: if %s", (status
+ ? (rc ? "true" : "false")
+ : "NULL"));
+#endif
+ /* XXX Treat NULL as false */
+ if (!status)
+ rc = 0;
+ if (!execute_statements
+ (result, packet, lease, client_state,
+ in_options, out_options, scope,
+ rc ? r -> data.ie.tc : r -> data.ie.fc))
+ return 0;
+ break;
+
+ case eval_statement:
+ status = evaluate_expression
+ ((struct binding_value **)0,
+ packet, lease, client_state, in_options,
+ out_options, scope, r -> data.eval, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: evaluate: %s",
+ (status ? "succeeded" : "failed"));
+#endif
+ break;
+
+ case execute_statement: {
+#ifdef ENABLE_EXECUTE
+ struct expression *expr;
+ char **argv;
+ int i, argc = r->data.execute.argc;
+ pid_t p;
+
+ /* save room for the command and the NULL terminator */
+ argv = dmalloc((argc + 2) * sizeof(*argv), MDL);
+ if (!argv)
+ break;
+
+ argv[0] = dmalloc(strlen(r->data.execute.command) + 1,
+ MDL);
+ if (argv[0]) {
+ strcpy(argv[0], r->data.execute.command);
+ } else {
+ goto execute_out;
+ }
+
+ log_debug("execute_statement argv[0] = %s", argv[0]);
+
+ for (i = 1, expr = r->data.execute.arglist; expr;
+ expr = expr->data.arg.next, i++) {
+ memset (&ds, 0, sizeof(ds));
+ status = (evaluate_data_expression
+ (&ds, packet,
+ lease, client_state, in_options,
+ out_options, scope,
+ expr->data.arg.val, MDL));
+ if (status) {
+ argv[i] = dmalloc(ds.len + 1, MDL);
+ if (argv[i]) {
+ memcpy(argv[i], ds.data,
+ ds.len);
+ argv[i][ds.len] = 0;
+ log_debug("execute_statement argv[%d] = %s", i, argv[i]);
+ }
+ data_string_forget (&ds, MDL);
+ if (!argv[i]) {
+ log_debug("execute_statement failed argv[%d]", i);
+ goto execute_out;
+ }
+ } else {
+ log_debug("execute: bad arg %d", i);
+ goto execute_out;
+ }
+ }
+ argv[i] = NULL;
+
+ if ((p = fork()) > 0) {
+ int status;
+ waitpid(p, &status, 0);
+
+ if (status) {
+ log_error("execute: %s exit status %d",
+ argv[0], status);
+ }
+ } else if (p == 0) {
+ execvp(argv[0], argv);
+ log_error("Unable to execute %s: %m", argv[0]);
+ _exit(127);
+ } else {
+ log_error("execute: fork() failed");
+ }
+
+ execute_out:
+ for (i = 0; i <= argc; i++) {
+ if(argv[i])
+ dfree(argv[i], MDL);
+ }
+
+ dfree(argv, MDL);
+#else /* !ENABLE_EXECUTE */
+ log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE "
+ "is not defined).", MDL);
+#endif /* ENABLE_EXECUTE */
+ break;
+ }
+
+ case return_statement:
+ status = evaluate_expression
+ (result, packet,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.retval, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: return: %s",
+ (status ? "succeeded" : "failed"));
+#endif
+ break;
+
+ case add_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: add %s", (r -> data.add -> name
+ ? r -> data.add -> name
+ : "<unnamed class>"));
+#endif
+ classify (packet, r -> data.add);
+ break;
+
+ case break_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: break");
+#endif
+ return 1;
+
+ case supersede_option_statement:
+ case send_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: %s option %s.%s",
+ (r -> op == supersede_option_statement
+ ? "supersede" : "send"),
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case default_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: default option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case append_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: append option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ goto option_statement;
+#endif
+ case prepend_option_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: prepend option %s.%s",
+ r -> data.option -> option -> universe -> name,
+ r -> data.option -> option -> name);
+ option_statement:
+#endif
+ set_option (r -> data.option -> option -> universe,
+ out_options, r -> data.option, r -> op);
+ break;
+
+ case set_statement:
+ case define_statement:
+ if (!scope) {
+ log_error ("set %s: no scope",
+ r -> data.set.name);
+ status = 0;
+ break;
+ }
+ if (!*scope) {
+ if (!binding_scope_allocate (scope, MDL)) {
+ log_error ("set %s: can't allocate scope",
+ r -> data.set.name);
+ status = 0;
+ break;
+ }
+ }
+ binding = find_binding (*scope, r -> data.set.name);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: set %s", r -> data.set.name);
+#endif
+ if (!binding) {
+ binding = dmalloc (sizeof *binding, MDL);
+ if (binding) {
+ memset (binding, 0, sizeof *binding);
+ binding -> name =
+ dmalloc (strlen
+ (r -> data.set.name) + 1,
+ MDL);
+ if (binding -> name) {
+ strcpy (binding -> name,
+ r -> data.set.name);
+ binding -> next = (*scope) -> bindings;
+ (*scope) -> bindings = binding;
+ } else {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ }
+ }
+ }
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ if (r -> op == set_statement) {
+ status = (evaluate_expression
+ (&binding -> value, packet,
+ lease, client_state,
+ in_options, out_options,
+ scope, r -> data.set.expr,
+ MDL));
+ } else {
+ if (!(binding_value_allocate
+ (&binding -> value, MDL))) {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ }
+ if (binding -> value) {
+ binding -> value -> type =
+ binding_function;
+ (fundef_reference
+ (&binding -> value -> value.fundef,
+ r -> data.set.expr -> data.func,
+ MDL));
+ }
+ }
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: set %s%s", r -> data.set.name,
+ (binding && status ? "" : " (failed)"));
+#endif
+ break;
+
+ case unset_statement:
+ if (!scope || !*scope) {
+ status = 0;
+ break;
+ }
+ binding = find_binding (*scope, r -> data.unset);
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ status = 1;
+ } else
+ status = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: unset %s: %s", r -> data.unset,
+ (status ? "found" : "not found"));
+#endif
+ break;
+
+ case let_statement:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: let %s", r -> data.let.name);
+#endif
+ ns = (struct binding_scope *)0;
+ binding_scope_allocate (&ns, MDL);
+ e = r;
+
+ next_let:
+ if (ns) {
+ binding = dmalloc (sizeof *binding, MDL);
+ memset (binding, 0, sizeof *binding);
+ if (!binding) {
+ blb:
+ binding_scope_dereference (&ns, MDL);
+ } else {
+ binding -> name =
+ dmalloc (strlen
+ (e -> data.let.name + 1),
+ MDL);
+ if (binding -> name)
+ strcpy (binding -> name,
+ e -> data.let.name);
+ else {
+ dfree (binding, MDL);
+ binding = (struct binding *)0;
+ goto blb;
+ }
+ }
+ } else
+ binding = NULL;
+
+ if (ns && binding) {
+ status = (evaluate_expression
+ (&binding -> value, packet, lease,
+ client_state,
+ in_options, out_options,
+ scope, e -> data.set.expr, MDL));
+ binding -> next = ns -> bindings;
+ ns -> bindings = binding;
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: let %s%s", e -> data.let.name,
+ (binding && status ? "" : "failed"));
+#endif
+ if (!e -> data.let.statements) {
+ } else if (e -> data.let.statements -> op ==
+ let_statement) {
+ e = e -> data.let.statements;
+ goto next_let;
+ } else if (ns) {
+ if (scope && *scope)
+ binding_scope_reference (&ns -> outer,
+ *scope, MDL);
+ execute_statements
+ (result, packet, lease,
+ client_state,
+ in_options, out_options,
+ &ns, e -> data.let.statements);
+ }
+ if (ns)
+ binding_scope_dereference (&ns, MDL);
+ break;
+
+ case log_statement:
+ memset (&ds, 0, sizeof ds);
+ status = (evaluate_data_expression
+ (&ds, packet,
+ lease, client_state, in_options,
+ out_options, scope, r -> data.log.expr,
+ MDL));
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("exec: log");
+#endif
+
+ if (status) {
+ switch (r -> data.log.priority) {
+ case log_priority_fatal:
+ log_fatal ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_error:
+ log_error ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_debug:
+ log_debug ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ case log_priority_info:
+ log_info ("%.*s", (int)ds.len,
+ ds.data);
+ break;
+ }
+ data_string_forget (&ds, MDL);
+ }
+
+ break;
+
+ default:
+ log_error ("bogus statement type %d", r -> op);
+ break;
+ }
+ executable_statement_dereference (&r, MDL);
+ if (next) {
+ executable_statement_reference (&r, next, MDL);
+ executable_statement_dereference (&next, MDL);
+ }
+ }
+
+ return 1;
+}
+
+/* Execute all the statements in a particular scope, and all statements in
+ scopes outer from that scope, but if a particular limiting scope is
+ reached, do not execute statements in that scope or in scopes outer
+ from it. More specific scopes need to take precedence over less
+ specific scopes, so we recursively traverse the scope list, executing
+ the most outer scope first. */
+
+void execute_statements_in_scope (result, packet,
+ lease, client_state, in_options, out_options,
+ scope, group, limiting_group)
+ struct binding_value **result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *out_options;
+ struct binding_scope **scope;
+ struct group *group;
+ struct group *limiting_group;
+{
+ struct group *limit;
+
+ /* If we've recursed as far as we can, return. */
+ if (!group)
+ return;
+
+ /* As soon as we get to a scope that is outer than the limiting
+ scope, we are done. This is so that if somebody does something
+ like this, it does the expected thing:
+
+ domain-name "fugue.com";
+ shared-network FOO {
+ host bar {
+ domain-name "othello.fugue.com";
+ fixed-address 10.20.30.40;
+ }
+ subnet 10.20.30.0 netmask 255.255.255.0 {
+ domain-name "manhattan.fugue.com";
+ }
+ }
+
+ The problem with the above arrangement is that the host's
+ group nesting will be host -> shared-network -> top-level,
+ and the limiting scope when we evaluate the host's scope
+ will be the subnet -> shared-network -> top-level, so we need
+ to know when we evaluate the host's scope to stop before we
+ evaluate the shared-networks scope, because it's outer than
+ the limiting scope, which means we've already evaluated it. */
+
+ for (limit = limiting_group; limit; limit = limit -> next) {
+ if (group == limit)
+ return;
+ }
+
+ if (group -> next)
+ execute_statements_in_scope (result, packet,
+ lease, client_state,
+ in_options, out_options, scope,
+ group -> next, limiting_group);
+ execute_statements (result, packet, lease, client_state, in_options,
+ out_options, scope, group -> statements);
+}
+
+/* Dereference or free any subexpressions of a statement being freed. */
+
+int executable_statement_dereference (ptr, file, line)
+ struct executable_statement **ptr;
+ const char *file;
+ int line;
+{
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ (*ptr) -> refcnt--;
+ rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
+ if ((*ptr) -> refcnt > 0) {
+ *ptr = (struct executable_statement *)0;
+ return 1;
+ }
+
+ if ((*ptr) -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*ptr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if ((*ptr) -> next)
+ executable_statement_dereference (&(*ptr) -> next, file, line);
+
+ switch ((*ptr) -> op) {
+ case statements_statement:
+ if ((*ptr) -> data.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.statements, file, line);
+ break;
+
+ case on_statement:
+ if ((*ptr) -> data.on.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.on.statements, file, line);
+ break;
+
+ case switch_statement:
+ if ((*ptr) -> data.s_switch.statements)
+ executable_statement_dereference
+ (&(*ptr) -> data.on.statements, file, line);
+ if ((*ptr) -> data.s_switch.expr)
+ expression_dereference (&(*ptr) -> data.s_switch.expr,
+ file, line);
+ break;
+
+ case case_statement:
+ if ((*ptr) -> data.s_switch.expr)
+ expression_dereference (&(*ptr) -> data.c_case,
+ file, line);
+ break;
+
+ case if_statement:
+ if ((*ptr) -> data.ie.expr)
+ expression_dereference (&(*ptr) -> data.ie.expr,
+ file, line);
+ if ((*ptr) -> data.ie.tc)
+ executable_statement_dereference
+ (&(*ptr) -> data.ie.tc, file, line);
+ if ((*ptr) -> data.ie.fc)
+ executable_statement_dereference
+ (&(*ptr) -> data.ie.fc, file, line);
+ break;
+
+ case eval_statement:
+ if ((*ptr) -> data.eval)
+ expression_dereference (&(*ptr) -> data.eval,
+ file, line);
+ break;
+
+ case return_statement:
+ if ((*ptr) -> data.eval)
+ expression_dereference (&(*ptr) -> data.eval,
+ file, line);
+ break;
+
+ case set_statement:
+ if ((*ptr)->data.set.name)
+ dfree ((*ptr)->data.set.name, file, line);
+ if ((*ptr)->data.set.expr)
+ expression_dereference (&(*ptr) -> data.set.expr,
+ file, line);
+ break;
+
+ case unset_statement:
+ if ((*ptr)->data.unset)
+ dfree ((*ptr)->data.unset, file, line);
+ break;
+
+ case execute_statement:
+ if ((*ptr)->data.execute.command)
+ dfree ((*ptr)->data.execute.command, file, line);
+ if ((*ptr)->data.execute.arglist)
+ expression_dereference (&(*ptr) -> data.execute.arglist,
+ file, line);
+ break;
+
+ case supersede_option_statement:
+ case send_option_statement:
+ case default_option_statement:
+ case append_option_statement:
+ case prepend_option_statement:
+ if ((*ptr) -> data.option)
+ option_cache_dereference (&(*ptr) -> data.option,
+ file, line);
+ break;
+
+ default:
+ /* Nothing to do. */
+ break;
+ }
+
+ dfree ((*ptr), file, line);
+ *ptr = (struct executable_statement *)0;
+ return 1;
+}
+
+void write_statements (file, statements, indent)
+ FILE *file;
+ struct executable_statement *statements;
+ int indent;
+{
+#if defined ENABLE_EXECUTE
+ struct expression *expr;
+#endif
+ struct executable_statement *r, *x;
+ const char *s, *t, *dot;
+ int col;
+
+ if (!statements)
+ return;
+
+ for (r = statements; r; r = r -> next) {
+ switch (r -> op) {
+ case statements_statement:
+ write_statements (file, r -> data.statements, indent);
+ break;
+
+ case on_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "on ");
+ s = "";
+ if (r -> data.on.evtypes & ON_EXPIRY) {
+ fprintf (file, "%sexpiry", s);
+ s = " or ";
+ }
+ if (r -> data.on.evtypes & ON_COMMIT) {
+ fprintf (file, "%scommit", s);
+ s = "or";
+ }
+ if (r -> data.on.evtypes & ON_RELEASE) {
+ fprintf (file, "%srelease", s);
+ s = "or";
+ }
+ if (r -> data.on.statements) {
+ fprintf (file, " {");
+ write_statements (file,
+ r -> data.on.statements,
+ indent + 2);
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ } else {
+ fprintf (file, ";");
+ }
+ break;
+
+ case switch_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "switch (");
+ col = write_expression (file,
+ r -> data.s_switch.expr,
+ indent + 7, indent + 7, 1);
+ col = token_print_indent (file, col, indent + 7,
+ "", "", ")");
+ token_print_indent (file,
+ col, indent, " ", "", "{");
+ write_statements (file, r -> data.s_switch.statements,
+ indent + 2);
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ break;
+
+ case case_statement:
+ indent_spaces (file, indent - 1);
+ fprintf (file, "case ");
+ col = write_expression (file,
+ r -> data.s_switch.expr,
+ indent + 5, indent + 5, 1);
+ token_print_indent (file, col, indent + 5,
+ "", "", ":");
+ break;
+
+ case default_statement:
+ indent_spaces (file, indent - 1);
+ fprintf (file, "default: ");
+ break;
+
+ case if_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "if ");
+ x = r;
+ col = write_expression (file,
+ x -> data.ie.expr,
+ indent + 3, indent + 3, 1);
+ else_if:
+ token_print_indent (file, col, indent, " ", "", "{");
+ write_statements (file, x -> data.ie.tc, indent + 2);
+ if (x -> data.ie.fc &&
+ x -> data.ie.fc -> op == if_statement &&
+ !x -> data.ie.fc -> next) {
+ indent_spaces (file, indent);
+ fprintf (file, "} elsif ");
+ x = x -> data.ie.fc;
+ col = write_expression (file,
+ x -> data.ie.expr,
+ indent + 6,
+ indent + 6, 1);
+ goto else_if;
+ }
+ if (x -> data.ie.fc) {
+ indent_spaces (file, indent);
+ fprintf (file, "} else {");
+ write_statements (file, x -> data.ie.fc,
+ indent + 2);
+ }
+ indent_spaces (file, indent);
+ fprintf (file, "}");
+ break;
+
+ case eval_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "eval ");
+ col = write_expression (file, r -> data.eval,
+ indent + 5, indent + 5, 1);
+ fprintf (file, ";");
+ break;
+
+ case return_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "return;");
+ break;
+
+ case add_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "add \"%s\"", r -> data.add -> name);
+ break;
+
+ case break_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "break;");
+ break;
+
+ case supersede_option_statement:
+ case send_option_statement:
+ s = "supersede";
+ goto option_statement;
+
+ case default_option_statement:
+ s = "default";
+ goto option_statement;
+
+ case append_option_statement:
+ s = "append";
+ goto option_statement;
+
+ case prepend_option_statement:
+ s = "prepend";
+ option_statement:
+ /* Note: the reason we don't try to pretty print
+ the option here is that the format of the option
+ may change in dhcpd.conf, and then when this
+ statement was read back, it would cause a syntax
+ error. */
+ if (r -> data.option -> option -> universe ==
+ &dhcp_universe) {
+ t = "";
+ dot = "";
+ } else {
+ t = (r -> data.option -> option ->
+ universe -> name);
+ dot = ".";
+ }
+ indent_spaces (file, indent);
+ fprintf (file, "%s %s%s%s = ", s, t, dot,
+ r -> data.option -> option -> name);
+ col = (indent + strlen (s) + strlen (t) +
+ strlen (dot) + strlen (r -> data.option ->
+ option -> name) + 4);
+ if (r -> data.option -> expression)
+ write_expression
+ (file,
+ r -> data.option -> expression,
+ col, indent + 8, 1);
+ else
+ token_indent_data_string
+ (file, col, indent + 8, "", "",
+ &r -> data.option -> data);
+
+ fprintf (file, ";"); /* XXX */
+ break;
+
+ case set_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "set ");
+ col = token_print_indent (file, indent + 4, indent + 4,
+ "", "", r -> data.set.name);
+ col = token_print_indent (file, col, indent + 4,
+ " ", " ", "=");
+ col = write_expression (file, r -> data.set.expr,
+ indent + 3, indent + 3, 0);
+ col = token_print_indent (file, col, indent + 4,
+ " ", "", ";");
+ break;
+
+ case unset_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "unset ");
+ col = token_print_indent (file, indent + 6, indent + 6,
+ "", "", r -> data.set.name);
+ col = token_print_indent (file, col, indent + 6,
+ " ", "", ";");
+ break;
+
+ case log_statement:
+ indent_spaces (file, indent);
+ fprintf (file, "log ");
+ col = token_print_indent (file, indent + 4, indent + 4,
+ "", "", "(");
+ switch (r -> data.log.priority) {
+ case log_priority_fatal:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "fatal,");
+ break;
+ case log_priority_error:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "error,");
+ break;
+ case log_priority_debug:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "debug,");
+ break;
+ case log_priority_info:
+ col = token_print_indent
+ (file, col, indent + 4, "",
+ " ", "info,");
+ break;
+ }
+ col = write_expression (file, r -> data.log.expr,
+ indent + 4, indent + 4, 0);
+ col = token_print_indent (file, col, indent + 4,
+ "", "", ");");
+
+ break;
+
+ case execute_statement:
+#ifdef ENABLE_EXECUTE
+ indent_spaces (file, indent);
+ col = token_print_indent(file, indent + 4, indent + 4,
+ "", "", "execute");
+ col = token_print_indent(file, col, indent + 4, " ", "",
+ "(");
+ col = token_print_indent(file, col, indent + 4, "\"", "\"", r->data.execute.command);
+ for (expr = r->data.execute.arglist; expr; expr = expr->data.arg.next) {
+ col = token_print_indent(file, col, indent + 4, "", " ", ",");
+ col = write_expression (file, expr->data.arg.val, col, indent + 4, 0);
+ }
+ col = token_print_indent(file, col, indent + 4, "", "", ");");
+#else /* !ENABLE_EXECUTE */
+ log_fatal("Impossible case at %s:%d (ENABLE_EXECUTE "
+ "is not defined).", MDL);
+#endif /* ENABLE_EXECUTE */
+ break;
+
+ default:
+ log_fatal ("bogus statement type %d\n", r -> op);
+ }
+ }
+}
+
+/* Find a case statement in the sequence of executable statements that
+ matches the expression, and if found, return the following statement.
+ If no case statement matches, try to find a default statement and
+ return that (the default statement can precede all the case statements).
+ Otherwise, return the null statement. */
+
+int find_matching_case (struct executable_statement **ep,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *out_options,
+ struct binding_scope **scope,
+ struct expression *expr,
+ struct executable_statement *stmt)
+{
+ int status, sub;
+ struct executable_statement *s;
+
+ if (is_data_expression (expr)) {
+ struct data_string cd, ds;
+ memset (&ds, 0, sizeof ds);
+ memset (&cd, 0, sizeof cd);
+
+ status = (evaluate_data_expression (&ds, packet, lease,
+ client_state, in_options,
+ out_options, scope, expr,
+ MDL));
+ if (status) {
+ for (s = stmt; s; s = s -> next) {
+ if (s -> op == case_statement) {
+ sub = (evaluate_data_expression
+ (&cd, packet, lease, client_state,
+ in_options, out_options,
+ scope, s -> data.c_case, MDL));
+ if (sub && cd.len == ds.len &&
+ !memcmp (cd.data, ds.data, cd.len))
+ {
+ data_string_forget (&cd, MDL);
+ data_string_forget (&ds, MDL);
+ executable_statement_reference
+ (ep, s -> next, MDL);
+ return 1;
+ }
+ data_string_forget (&cd, MDL);
+ }
+ }
+ data_string_forget (&ds, MDL);
+ }
+ } else {
+ unsigned long n, c;
+ status = evaluate_numeric_expression (&n, packet, lease,
+ client_state,
+ in_options, out_options,
+ scope, expr);
+
+ if (status) {
+ for (s = stmt; s; s = s -> next) {
+ if (s -> op == case_statement) {
+ sub = (evaluate_numeric_expression
+ (&c, packet, lease, client_state,
+ in_options, out_options,
+ scope, s -> data.c_case));
+ if (sub && n == c) {
+ executable_statement_reference
+ (ep, s -> next, MDL);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ /* If we didn't find a matching case statement, look for a default
+ statement and return the statement following it. */
+ for (s = stmt; s; s = s -> next)
+ if (s -> op == default_statement)
+ break;
+ if (s) {
+ executable_statement_reference (ep, s -> next, MDL);
+ return 1;
+ }
+ return 0;
+}
+
+int executable_statement_foreach (struct executable_statement *stmt,
+ int (*callback) (struct
+ executable_statement *,
+ void *, int),
+ void *vp, int condp)
+{
+ struct executable_statement *foo;
+ int ok = 0;
+
+ for (foo = stmt; foo; foo = foo -> next) {
+ if ((*callback) (foo, vp, condp) != 0)
+ ok = 1;
+ switch (foo -> op) {
+ case null_statement:
+ break;
+ case if_statement:
+ if (executable_statement_foreach (foo -> data.ie.tc,
+ callback, vp, 1))
+ ok = 1;
+ if (executable_statement_foreach (foo -> data.ie.fc,
+ callback, vp, 1))
+ ok = 1;
+ break;
+ case add_statement:
+ break;
+ case eval_statement:
+ break;
+ case break_statement:
+ break;
+ case default_option_statement:
+ break;
+ case supersede_option_statement:
+ break;
+ case append_option_statement:
+ break;
+ case prepend_option_statement:
+ break;
+ case send_option_statement:
+ break;
+ case statements_statement:
+ if ((executable_statement_foreach
+ (foo -> data.statements, callback, vp, condp)))
+ ok = 1;
+ break;
+ case on_statement:
+ if ((executable_statement_foreach
+ (foo -> data.on.statements, callback, vp, 1)))
+ ok = 1;
+ break;
+ case switch_statement:
+ if ((executable_statement_foreach
+ (foo -> data.s_switch.statements, callback, vp, 1)))
+ ok = 1;
+ break;
+ case case_statement:
+ break;
+ case default_statement:
+ break;
+ case set_statement:
+ break;
+ case unset_statement:
+ break;
+ case let_statement:
+ if ((executable_statement_foreach
+ (foo -> data.let.statements, callback, vp, 0)))
+ ok = 1;
+ break;
+ case define_statement:
+ break;
+ case log_statement:
+ case return_statement:
+ case execute_statement:
+ break;
+ }
+ }
+ return ok;
+}
diff --git a/common/fddi.c b/common/fddi.c
new file mode 100644
index 0000000..eef85c2
--- /dev/null
+++ b/common/fddi.c
@@ -0,0 +1,92 @@
+/* fddi.c
+
+ Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#if defined (DEC_FDDI)
+#include <netinet/if_fddi.h>
+#include <net/if_llc.h>
+
+#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
+#include "includes/netinet/if_ether.h"
+#endif /* PACKET_ASSEMBLY || PACKET_DECODING */
+
+#if defined (PACKET_ASSEMBLY)
+/* Assemble an hardware header... */
+
+void assemble_fddi_header (interface, buf, bufix, to)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned *bufix;
+ struct hardware *to;
+{
+ struct fddi_header fh;
+ struct llc lh;
+
+ if (to && to -> hlen == 7)
+ memcpy (fh.fddi_dhost, &to -> hbuf [1],
+ sizeof (fh.fddi_dhost));
+ memcpy (fh.fddi_shost,
+ &interface -> hw_address.hbuf [1], sizeof (fh.fddi_shost));
+ fh.fddi_fc = FDDIFC_LLC_ASYNC;
+ memcpy (&buf [*bufix], &fh, sizeof fh);
+ *bufix += sizeof fh;
+
+ lh.llc_dsap = LLC_SNAP_LSAP;
+ lh.llc_ssap = LLC_SNAP_LSAP;
+ lh.llc_un.type_snap.control = LLC_UI;
+ lh.llc_un.type_snap.ether_type = htons (ETHERTYPE_IP);
+ memcpy (&buf [*bufix], &lh, LLC_SNAP_LEN);
+ *bufix += LLC_SNAP_LEN;
+}
+#endif /* PACKET_ASSEMBLY */
+
+#ifdef PACKET_DECODING
+/* Decode a hardware header... */
+
+ssize_t decode_fddi_header (interface, buf, bufix, from)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned bufix;
+ struct hardware *from;
+{
+ struct fddi_header fh;
+ struct llc lh;
+
+ from -> hbuf [0] = HTYPE_FDDI;
+ memcpy (&from -> hbuf [1], fh.fddi_shost, sizeof fh.fddi_shost);
+ return FDDI_HEADER_SIZE + LLC_SNAP_LEN;
+}
+#endif /* PACKET_DECODING */
+#endif /* DEC_FDDI */
diff --git a/common/icmp.c b/common/icmp.c
new file mode 100644
index 0000000..cf9a745
--- /dev/null
+++ b/common/icmp.c
@@ -0,0 +1,308 @@
+/* dhcp.c
+
+ ICMP Protocol engine - for sending out pings and receiving
+ responses. */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include "netinet/ip.h"
+#include "netinet/ip_icmp.h"
+
+struct icmp_state *icmp_state;
+static omapi_object_type_t *dhcp_type_icmp;
+static int no_icmp;
+
+OMAPI_OBJECT_ALLOC (icmp_state, struct icmp_state, dhcp_type_icmp)
+
+#if defined (TRACING)
+trace_type_t *trace_icmp_input;
+trace_type_t *trace_icmp_output;
+#endif
+
+/* Initialize the ICMP protocol. */
+
+void icmp_startup (routep, handler)
+ int routep;
+ void (*handler) (struct iaddr, u_int8_t *, int);
+{
+ struct protoent *proto;
+ int protocol = 1;
+ int state;
+ isc_result_t result;
+
+ /* Only initialize icmp once. */
+ if (dhcp_type_icmp)
+ log_fatal ("attempted to reinitialize icmp protocol");
+
+ result = omapi_object_type_register (&dhcp_type_icmp, "icmp",
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ sizeof (struct icmp_state),
+ 0, RC_MISC);
+
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Can't register icmp object type: %s",
+ isc_result_totext (result));
+
+ icmp_state_allocate (&icmp_state, MDL);
+ icmp_state -> icmp_handler = handler;
+
+#if defined (TRACING)
+ trace_icmp_input = trace_type_register ("icmp-input", (void *)0,
+ trace_icmp_input_input,
+ trace_icmp_input_stop, MDL);
+ trace_icmp_output = trace_type_register ("icmp-output", (void *)0,
+ trace_icmp_output_input,
+ trace_icmp_output_stop, MDL);
+
+ /* If we're playing back a trace file, don't create the socket
+ or set up the callback. */
+ if (!trace_playback ()) {
+#endif
+ /* Get the protocol number (should be 1). */
+ proto = getprotobyname ("icmp");
+ if (proto)
+ protocol = proto -> p_proto;
+
+ /* Get a raw socket for the ICMP protocol. */
+ icmp_state -> socket = socket (AF_INET, SOCK_RAW, protocol);
+ if (icmp_state -> socket < 0) {
+ no_icmp = 1;
+ log_error ("unable to create icmp socket: %m");
+ return;
+ }
+
+#if defined (HAVE_SETFD)
+ if (fcntl (icmp_state -> socket, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on icmp: %m");
+#endif
+
+ /* Make sure it does routing... */
+ state = 0;
+ if (setsockopt (icmp_state -> socket, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&state, sizeof state) < 0)
+ log_fatal ("Can't disable SO_DONTROUTE on ICMP: %m");
+
+ result = (omapi_register_io_object
+ ((omapi_object_t *)icmp_state,
+ icmp_readsocket, 0, icmp_echoreply, 0, 0));
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Can't register icmp handle: %s",
+ isc_result_totext (result));
+#if defined (TRACING)
+ }
+#endif
+}
+
+int icmp_readsocket (h)
+ omapi_object_t *h;
+{
+ struct icmp_state *state;
+
+ state = (struct icmp_state *)h;
+ return state -> socket;
+}
+
+int icmp_echorequest (addr)
+ struct iaddr *addr;
+{
+ struct sockaddr_in to;
+ struct icmp icmp;
+ int status;
+#if defined (TRACING)
+ trace_iov_t iov [2];
+#endif
+
+ if (no_icmp)
+ return 1;
+ if (!icmp_state)
+ log_fatal ("ICMP protocol used before initialization.");
+
+ memset (&to, 0, sizeof(to));
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+ to.sin_family = AF_INET;
+ to.sin_port = 0; /* unused. */
+ memcpy (&to.sin_addr, addr -> iabuf, sizeof to.sin_addr); /* XXX */
+
+ icmp.icmp_type = ICMP_ECHO;
+ icmp.icmp_code = 0;
+ icmp.icmp_cksum = 0;
+ icmp.icmp_seq = 0;
+#if SIZEOF_STRUCT_IADDR_P == 8
+ icmp.icmp_id = (((u_int32_t)(u_int64_t)addr) ^
+ (u_int32_t)(((u_int64_t)addr) >> 32));
+#else
+ icmp.icmp_id = (u_int32_t)addr;
+#endif
+ memset (&icmp.icmp_dun, 0, sizeof icmp.icmp_dun);
+
+ icmp.icmp_cksum = wrapsum (checksum ((unsigned char *)&icmp,
+ sizeof icmp, 0));
+
+#if defined (TRACING)
+ if (trace_playback ()) {
+ char *buf = (char *)0;
+ unsigned buflen = 0;
+
+ /* Consume the ICMP event. */
+ status = trace_get_packet (&trace_icmp_output, &buflen, &buf);
+ if (status != ISC_R_SUCCESS)
+ log_error ("icmp_echorequest: %s",
+ isc_result_totext (status));
+ if (buf)
+ dfree (buf, MDL);
+ } else {
+ if (trace_record ()) {
+ iov [0].buf = (char *)addr;
+ iov [0].len = sizeof *addr;
+ iov [1].buf = (char *)&icmp;
+ iov [1].len = sizeof icmp;
+ trace_write_packet_iov (trace_icmp_output,
+ 2, iov, MDL);
+ }
+#endif
+ /* Send the ICMP packet... */
+ status = sendto (icmp_state -> socket,
+ (char *)&icmp, sizeof icmp, 0,
+ (struct sockaddr *)&to, sizeof to);
+ if (status < 0)
+ log_error ("icmp_echorequest %s: %m",
+ inet_ntoa(to.sin_addr));
+
+ if (status != sizeof icmp)
+ return 0;
+#if defined (TRACING)
+ }
+#endif
+ return 1;
+}
+
+isc_result_t icmp_echoreply (h)
+ omapi_object_t *h;
+{
+ struct icmp *icfrom;
+ struct ip *ip;
+ struct sockaddr_in from;
+ u_int8_t icbuf [1500];
+ int status;
+ SOCKLEN_T sl;
+ int hlen, len;
+ struct iaddr ia;
+ struct icmp_state *state;
+#if defined (TRACING)
+ trace_iov_t iov [2];
+#endif
+
+ state = (struct icmp_state *)h;
+
+ sl = sizeof from;
+ status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0,
+ (struct sockaddr *)&from, &sl);
+ if (status < 0) {
+ log_error ("icmp_echoreply: %m");
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Find the IP header length... */
+ ip = (struct ip *)icbuf;
+ hlen = IP_HL (ip);
+
+ /* Short packet? */
+ if (status < hlen + (sizeof *icfrom)) {
+ return ISC_R_SUCCESS;
+ }
+
+ len = status - hlen;
+ icfrom = (struct icmp *)(icbuf + hlen);
+
+ /* Silently discard ICMP packets that aren't echoreplies. */
+ if (icfrom -> icmp_type != ICMP_ECHOREPLY) {
+ return ISC_R_SUCCESS;
+ }
+
+ /* If we were given a second-stage handler, call it. */
+ if (state -> icmp_handler) {
+ memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr);
+ ia.len = sizeof from.sin_addr;
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ ia.len = htonl(ia.len);
+ iov [0].buf = (char *)&ia;
+ iov [0].len = sizeof ia;
+ iov [1].buf = (char *)icbuf;
+ iov [1].len = len;
+ trace_write_packet_iov (trace_icmp_input, 2, iov, MDL);
+ ia.len = ntohl(ia.len);
+ }
+#endif
+ (*state -> icmp_handler) (ia, icbuf, len);
+ }
+ return ISC_R_SUCCESS;
+}
+
+#if defined (TRACING)
+void trace_icmp_input_input (trace_type_t *ttype, unsigned length, char *buf)
+{
+ struct iaddr *ia;
+ u_int8_t *icbuf;
+ ia = (struct iaddr *)buf;
+ ia->len = ntohl(ia->len);
+ icbuf = (u_int8_t *)(ia + 1);
+ if (icmp_state -> icmp_handler)
+ (*icmp_state -> icmp_handler) (*ia, icbuf,
+ (int)(length - sizeof ia));
+}
+
+void trace_icmp_input_stop (trace_type_t *ttype) { }
+
+void trace_icmp_output_input (trace_type_t *ttype, unsigned length, char *buf)
+{
+ struct icmp *icmp;
+ struct iaddr ia;
+
+ if (length != (sizeof (*icmp) + (sizeof ia))) {
+ log_error ("trace_icmp_output_input: data size mismatch %d:%d",
+ length, (int)((sizeof (*icmp)) + (sizeof ia)));
+ return;
+ }
+ ia.len = 4;
+ memcpy (ia.iabuf, buf, 4);
+ icmp = (struct icmp *)(buf + 1);
+
+ log_error ("trace_icmp_output_input: unsent ping to %s", piaddr (ia));
+}
+
+void trace_icmp_output_stop (trace_type_t *ttype) { }
+#endif /* TRACING */
diff --git a/common/inet.c b/common/inet.c
new file mode 100644
index 0000000..39845a8
--- /dev/null
+++ b/common/inet.c
@@ -0,0 +1,631 @@
+/* inet.c
+
+ Subroutines to manipulate internet addresses and ports in a safely portable
+ way... */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2007-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2005 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+/* Return just the network number of an internet address... */
+
+struct iaddr subnet_number (addr, mask)
+ struct iaddr addr;
+ struct iaddr mask;
+{
+ int i;
+ struct iaddr rv;
+
+ if (addr.len > sizeof(addr.iabuf))
+ log_fatal("subnet_number():%s:%d: Invalid addr length.", MDL);
+ if (addr.len != mask.len)
+ log_fatal("subnet_number():%s:%d: Addr/mask length mismatch.",
+ MDL);
+
+ 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;
+}
+
+/* Combine a network number and a integer to produce an internet address.
+ This won't work for subnets with more than 32 bits of host address, but
+ maybe this isn't a problem. */
+
+struct iaddr ip_addr (subnet, mask, host_address)
+ struct iaddr subnet;
+ struct iaddr mask;
+ u_int32_t host_address;
+{
+ int i, j, k;
+ u_int32_t swaddr;
+ struct iaddr rv;
+ unsigned char habuf [sizeof swaddr];
+
+ if (subnet.len > sizeof(subnet.iabuf))
+ log_fatal("ip_addr():%s:%d: Invalid addr length.", MDL);
+ if (subnet.len != mask.len)
+ log_fatal("ip_addr():%s:%d: Addr/mask length mismatch.",
+ MDL);
+
+ swaddr = htonl (host_address);
+ memcpy (habuf, &swaddr, sizeof swaddr);
+
+ /* Combine the subnet address and the host address. If
+ the host address is bigger than can fit in the subnet,
+ return a zero-length iaddr structure. */
+ rv = subnet;
+ j = rv.len - sizeof habuf;
+ for (i = sizeof habuf - 1; i >= 0; i--) {
+ if (mask.iabuf [i + j]) {
+ if (habuf [i] > (mask.iabuf [i + j] ^ 0xFF)) {
+ rv.len = 0;
+ return rv;
+ }
+ for (k = i - 1; k >= 0; k--) {
+ if (habuf [k]) {
+ rv.len = 0;
+ return rv;
+ }
+ }
+ rv.iabuf [i + j] |= habuf [i];
+ break;
+ } else
+ rv.iabuf [i + j] = habuf [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 (subnet, mask)
+ struct iaddr subnet;
+ struct iaddr mask;
+{
+ int i;
+ struct iaddr rv;
+
+ if (subnet.len > sizeof(subnet.iabuf))
+ log_fatal("broadcast_addr():%s:%d: Invalid addr length.", MDL);
+ if (subnet.len != mask.len)
+ log_fatal("broadcast_addr():%s:%d: Addr/mask length mismatch.",
+ MDL);
+
+ 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;
+}
+
+u_int32_t host_addr (addr, mask)
+ struct iaddr addr;
+ struct iaddr mask;
+{
+ int i;
+ u_int32_t swaddr;
+ struct iaddr rv;
+
+ if (addr.len > sizeof(addr.iabuf))
+ log_fatal("host_addr():%s:%d: Invalid addr length.", MDL);
+ if (addr.len != mask.len)
+ log_fatal("host_addr():%s:%d: Addr/mask length mismatch.",
+ MDL);
+
+ rv.len = 0;
+
+ /* Mask out the network bits... */
+ rv.len = addr.len;
+ for (i = 0; i < rv.len; i++)
+ rv.iabuf [i] = addr.iabuf [i] & ~mask.iabuf [i];
+
+ /* Copy out up to 32 bits... */
+ memcpy (&swaddr, &rv.iabuf [rv.len - sizeof swaddr], sizeof swaddr);
+
+ /* Swap it and return it. */
+ return ntohl (swaddr);
+}
+
+int addr_eq (addr1, addr2)
+ struct iaddr addr1, addr2;
+{
+ if (addr1.len > sizeof(addr1.iabuf))
+ log_fatal("addr_eq():%s:%d: Invalid addr length.", MDL);
+
+ if (addr1.len != addr2.len)
+ return 0;
+ return memcmp (addr1.iabuf, addr2.iabuf, addr1.len) == 0;
+}
+
+/* addr_match
+ *
+ * compares an IP address against a network/mask combination
+ * by ANDing the IP with the mask and seeing whether the result
+ * matches the masked network value.
+ */
+int
+addr_match(addr, match)
+ struct iaddr *addr;
+ struct iaddrmatch *match;
+{
+ int i;
+
+ if (addr->len != match->addr.len)
+ return 0;
+
+ i = 0;
+ for (i = 0 ; i < addr->len ; i++) {
+ if ((addr->iabuf[i] & match->mask.iabuf[i]) !=
+ match->addr.iabuf[i])
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Compares the addresses a1 and a2.
+ *
+ * If a1 < a2, returns -1.
+ * If a1 == a2, returns 0.
+ * If a1 > a2, returns 1.
+ *
+ * WARNING: if a1 and a2 differ in length, returns 0.
+ */
+int
+addr_cmp(const struct iaddr *a1, const struct iaddr *a2) {
+ int i;
+
+ if (a1->len != a2->len) {
+ return 0;
+ }
+
+ for (i=0; i<a1->len; i++) {
+ if (a1->iabuf[i] < a2->iabuf[i]) {
+ return -1;
+ }
+ if (a1->iabuf[i] > a2->iabuf[i]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Performs a bitwise-OR of two addresses.
+ *
+ * Returns 1 if the result is non-zero, or 0 otherwise.
+ *
+ * WARNING: if a1 and a2 differ in length, returns 0.
+ */
+int
+addr_or(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) {
+ int i;
+ int all_zero;
+
+ if (a1->len != a2->len) {
+ return 0;
+ }
+
+ all_zero = 1;
+
+ result->len = a1->len;
+ for (i=0; i<a1->len; i++) {
+ result->iabuf[i] = a1->iabuf[i] | a2->iabuf[i];
+ if (result->iabuf[i] != 0) {
+ all_zero = 0;
+ }
+ }
+
+ return !all_zero;
+}
+
+/*
+ * Performs a bitwise-AND of two addresses.
+ *
+ * Returns 1 if the result is non-zero, or 0 otherwise.
+ *
+ * WARNING: if a1 and a2 differ in length, returns 0.
+ */
+int
+addr_and(struct iaddr *result, const struct iaddr *a1, const struct iaddr *a2) {
+ int i;
+ int all_zero;
+
+ if (a1->len != a2->len) {
+ return 0;
+ }
+
+ all_zero = 1;
+
+ result->len = a1->len;
+ for (i=0; i<a1->len; i++) {
+ result->iabuf[i] = a1->iabuf[i] & a2->iabuf[i];
+ if (result->iabuf[i] != 0) {
+ all_zero = 0;
+ }
+ }
+
+ return !all_zero;
+}
+
+/*
+ * Check if a bitmask of the given length is valid for the address.
+ * This is not the case if any bits longer than the bitmask are 1.
+ *
+ * So, this is valid:
+ *
+ * 127.0.0.0/8
+ *
+ * But this is not:
+ *
+ * 127.0.0.1/8
+ *
+ * Because the final ".1" would get masked out by the /8.
+ */
+isc_boolean_t
+is_cidr_mask_valid(const struct iaddr *addr, int bits) {
+ int zero_bits;
+ int zero_bytes;
+ int i;
+ char byte;
+ int shift_bits;
+
+ /*
+ * Check our bit boundaries.
+ */
+ if (bits < 0) {
+ return ISC_FALSE;
+ }
+ if (bits > (addr->len * 8)) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Figure out how many low-order bits need to be zero.
+ */
+ zero_bits = (addr->len * 8) - bits;
+ zero_bytes = zero_bits / 8;
+
+ /*
+ * Check to make sure the low-order bytes are zero.
+ */
+ for (i=1; i<=zero_bytes; i++) {
+ if (addr->iabuf[addr->len-i] != 0) {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * Look to see if any bits not in right-hand bytes are
+ * non-zero, by making a byte that has these bits set to zero
+ * comparing to the original byte. If these two values are
+ * equal, then the right-hand bits are zero, and we are
+ * happy.
+ */
+ shift_bits = zero_bits % 8;
+ if (shift_bits == 0) return ISC_TRUE;
+ byte = addr->iabuf[addr->len-zero_bytes-1];
+ return (((byte >> shift_bits) << shift_bits) == byte);
+}
+
+/*
+ * range2cidr
+ *
+ * Converts a range of IP addresses to a set of CIDR networks.
+ *
+ * Examples:
+ * 192.168.0.0 - 192.168.0.255 = 192.168.0.0/24
+ * 10.0.0.0 - 10.0.1.127 = 10.0.0.0/24, 10.0.1.0/25
+ * 255.255.255.32 - 255.255.255.255 = 255.255.255.32/27, 255.255.255.64/26,
+ * 255.255.255.128/25
+ */
+isc_result_t
+range2cidr(struct iaddrcidrnetlist **result,
+ const struct iaddr *lo, const struct iaddr *hi) {
+ struct iaddr addr;
+ struct iaddr mask;
+ int bit;
+ struct iaddr end_addr;
+ struct iaddr dummy;
+ int ofs, val;
+ struct iaddrcidrnetlist *net;
+ int tmp;
+
+ if (result == NULL) {
+ return DHCP_R_INVALIDARG;
+ }
+ if (*result != NULL) {
+ return DHCP_R_INVALIDARG;
+ }
+ if ((lo == NULL) || (hi == NULL) || (lo->len != hi->len)) {
+ return DHCP_R_INVALIDARG;
+ }
+
+ /*
+ * Put our start and end in the right order, if reversed.
+ */
+ if (addr_cmp(lo, hi) > 0) {
+ const struct iaddr *tmp;
+ tmp = lo;
+ lo = hi;
+ hi = tmp;
+ }
+
+ /*
+ * Theory of operation:
+ *
+ * -------------------
+ * Start at the low end, and keep trying larger networks
+ * until we get one that is too big (explained below).
+ *
+ * We keep a "mask", which is the ones-complement of a
+ * normal netmask. So, a /23 has a netmask of 255.255.254.0,
+ * and a mask of 0.0.1.255.
+ *
+ * We know when a network is too big when we bitwise-AND the
+ * mask with the starting address and we get a non-zero
+ * result, like this:
+ *
+ * addr: 192.168.1.0, mask: 0.0.1.255
+ * bitwise-AND: 0.0.1.0
+ *
+ * A network is also too big if the bitwise-OR of the mask
+ * with the starting address is larger than the end address,
+ * like this:
+ *
+ * start: 192.168.1.0, mask: 0.0.1.255, end: 192.168.0.255
+ * bitwise-OR: 192.168.1.255
+ *
+ * -------------------
+ * Once we have found a network that is too big, we add the
+ * appropriate CIDR network to our list of found networks.
+ *
+ * We then use the next IP address as our low address, and
+ * begin the process of searching for a network that is
+ * too big again, starting with an empty mask.
+ */
+ addr = *lo;
+ bit = 0;
+ memset(&mask, 0, sizeof(mask));
+ mask.len = addr.len;
+ while (addr_cmp(&addr, hi) <= 0) {
+ /*
+ * Bitwise-OR mask with (1 << bit)
+ */
+ ofs = addr.len - (bit / 8) - 1;
+ val = 1 << (bit % 8);
+ if (ofs >= 0) {
+ mask.iabuf[ofs] |= val;
+ }
+
+ /*
+ * See if we're too big, and save this network if so.
+ */
+ addr_or(&end_addr, &addr, &mask);
+ if ((ofs < 0) ||
+ (addr_cmp(&end_addr, hi) > 0) ||
+ addr_and(&dummy, &addr, &mask)) {
+ /*
+ * Add a new prefix to our list.
+ */
+ net = dmalloc(sizeof(*net), MDL);
+ if (net == NULL) {
+ while (*result != NULL) {
+ net = (*result)->next;
+ dfree(*result, MDL);
+ *result = net;
+ }
+ return ISC_R_NOMEMORY;
+ }
+ net->cidrnet.lo_addr = addr;
+ net->cidrnet.bits = (addr.len * 8) - bit;
+ net->next = *result;
+ *result = net;
+
+ /*
+ * Figure out our new starting address,
+ * by adding (1 << bit) to our previous
+ * starting address.
+ */
+ tmp = addr.iabuf[ofs] + val;
+ while ((ofs >= 0) && (tmp > 255)) {
+ addr.iabuf[ofs] = tmp - 256;
+ ofs--;
+ tmp = addr.iabuf[ofs] + 1;
+ }
+ if (ofs < 0) {
+ /* Gone past last address, we're done. */
+ break;
+ }
+ addr.iabuf[ofs] = tmp;
+
+ /*
+ * Reset our bit and mask.
+ */
+ bit = 0;
+ memset(mask.iabuf, 0, sizeof(mask.iabuf));
+ memset(end_addr.iabuf, 0, sizeof(end_addr.iabuf));
+ } else {
+ /*
+ * If we're not too big, increase our network size.
+ */
+ bit++;
+ }
+ }
+
+ /*
+ * We're done.
+ */
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Free a list of CIDR networks, such as returned from range2cidr().
+ */
+isc_result_t
+free_iaddrcidrnetlist(struct iaddrcidrnetlist **result) {
+ struct iaddrcidrnetlist *p;
+
+ if (result == NULL) {
+ return DHCP_R_INVALIDARG;
+ }
+ if (*result == NULL) {
+ return DHCP_R_INVALIDARG;
+ }
+
+ while (*result != NULL) {
+ p = *result;
+ *result = p->next;
+ dfree(p, MDL);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/* piaddr() turns an iaddr structure into a printable address. */
+/* XXX: should use a const pointer rather than passing the structure */
+const char *
+piaddr(const struct iaddr addr) {
+ static char
+ pbuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ /* "255.255.255.255" */
+
+ /* INSIST((addr.len == 0) || (addr.len == 4) || (addr.len == 16)); */
+
+ if (addr.len == 0) {
+ return "<null address>";
+ }
+ if (addr.len == 4) {
+ return inet_ntop(AF_INET, addr.iabuf, pbuf, sizeof(pbuf));
+ }
+ if (addr.len == 16) {
+ return inet_ntop(AF_INET6, addr.iabuf, pbuf, sizeof(pbuf));
+ }
+
+ log_fatal("piaddr():%s:%d: Invalid address length %d.", MDL,
+ addr.len);
+ /* quell compiler warnings */
+ return NULL;
+}
+
+/* piaddrmask takes an iaddr structure mask, determines the bitlength of
+ * the mask, and then returns the printable CIDR notation of the two.
+ */
+char *
+piaddrmask(struct iaddr *addr, struct iaddr *mask) {
+ int mw;
+ unsigned int oct, bit;
+
+ if ((addr->len != 4) && (addr->len != 16))
+ log_fatal("piaddrmask():%s:%d: Address length %d invalid",
+ MDL, addr->len);
+ if (addr->len != mask->len)
+ log_fatal("piaddrmask():%s:%d: Address and mask size mismatch",
+ MDL);
+
+ /* Determine netmask width in bits. */
+ for (mw = (mask->len * 8) ; mw > 0 ; ) {
+ oct = (mw - 1) / 8;
+ bit = 0x80 >> ((mw - 1) % 8);
+ if (!mask->iabuf[oct])
+ mw -= 8;
+ else if (mask->iabuf[oct] & bit)
+ break;
+ else
+ mw--;
+ }
+
+ if (mw < 0)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ return piaddrcidr(addr, mw);
+}
+
+/* Format an address and mask-length into printable CIDR notation. */
+char *
+piaddrcidr(const struct iaddr *addr, unsigned int bits) {
+ static char
+ ret[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128")];
+ /* "255.255.255.255/32" */
+
+ /* INSIST(addr != NULL); */
+ /* INSIST((addr->len == 4) || (addr->len == 16)); */
+ /* INSIST(bits <= (addr->len * 8)); */
+
+ if (bits > (addr->len * 8))
+ return NULL;
+
+ sprintf(ret, "%s/%d", piaddr(*addr), bits);
+
+ return ret;
+}
+
+/* Validate that the string represents a valid port number and
+ * return it in network byte order
+ */
+
+u_int16_t
+validate_port(char *port) {
+ long local_port = 0;
+ long lower = 1;
+ long upper = 65535;
+ char *endptr;
+
+ errno = 0;
+ local_port = strtol(port, &endptr, 10);
+
+ if ((*endptr != '\0') || (errno == ERANGE) || (errno == EINVAL))
+ log_fatal ("Invalid port number specification: %s", port);
+
+ if (local_port < lower || local_port > upper)
+ log_fatal("Port number specified is out of range (%ld-%ld).",
+ lower, upper);
+
+ return htons((u_int16_t)local_port);
+}
diff --git a/common/lpf.c b/common/lpf.c
new file mode 100644
index 0000000..16eecc9
--- /dev/null
+++ b/common/lpf.c
@@ -0,0 +1,470 @@
+/* lpf.c
+
+ Linux packet filter code, contributed by Brian Murrel at Interlinx
+ Support Services in Vancouver, B.C. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#include "dhcpd.h"
+#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#include <asm/types.h>
+#include <linux/filter.h>
+#include <linux/if_ether.h>
+#include <netinet/in_systm.h>
+#include <net/if_packet.h>
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+#include <net/if.h>
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#ifdef USE_LPF_SEND
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+#ifdef USE_LPF_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+/* 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_lpf (info)
+ struct interface_info *info;
+{
+ int sock;
+ struct sockaddr sa;
+
+ /* Make an LPF socket. */
+ if ((sock = socket(PF_PACKET, SOCK_PACKET,
+ htons((short)ETH_P_ALL))) < 0) {
+ if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
+ errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
+ errno == EAFNOSUPPORT || errno == EINVAL) {
+ log_error ("socket: %m - make sure");
+ log_error ("CONFIG_PACKET (Packet socket) %s",
+ "and CONFIG_FILTER");
+ log_error ("(Socket Filtering) are enabled %s",
+ "in your kernel");
+ log_fatal ("configuration!");
+ }
+ log_fatal ("Open a socket for LPF: %m");
+ }
+
+ /* Bind to the interface name */
+ memset (&sa, 0, sizeof sa);
+ sa.sa_family = AF_PACKET;
+ strncpy (sa.sa_data, (const char *)info -> ifp, sizeof sa.sa_data);
+ if (bind (sock, &sa, sizeof sa)) {
+ if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
+ errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
+ errno == EAFNOSUPPORT || errno == EINVAL) {
+ log_error ("socket: %m - make sure");
+ log_error ("CONFIG_PACKET (Packet socket) %s",
+ "and CONFIG_FILTER");
+ log_error ("(Socket Filtering) are enabled %s",
+ "in your kernel");
+ log_fatal ("configuration!");
+ }
+ log_fatal ("Bind socket to interface: %m");
+ }
+
+ get_hw_addr(info->name, &info->hw_address);
+
+ return sock;
+}
+#endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
+
+#ifdef USE_LPF_SEND
+void if_register_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the lpf API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_LPF_RECEIVE
+ info -> wfdesc = if_register_lpf (info);
+#else
+ info -> wfdesc = info -> rfdesc;
+#endif
+ if (!quiet_interface_discovery)
+ log_info ("Sending on LPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+ /* don't need to close twice if we are using lpf for sending and
+ receiving */
+#ifndef USE_LPF_RECEIVE
+ /* for LPF this is simple, packet filters are removed when sockets
+ are closed */
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on LPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_LPF_SEND */
+
+#ifdef USE_LPF_RECEIVE
+/* Defined in bpf.c. We can't extern these in dhcpd.h without pulling
+ in bpf includes... */
+extern struct sock_filter dhcp_bpf_filter [];
+extern int dhcp_bpf_filter_len;
+
+#if defined (HAVE_TR_SUPPORT)
+extern struct sock_filter dhcp_bpf_tr_filter [];
+extern int dhcp_bpf_tr_filter_len;
+static void lpf_tr_filter_setup (struct interface_info *);
+#endif
+
+static void lpf_gen_filter_setup (struct interface_info *);
+
+void if_register_receive (info)
+ struct interface_info *info;
+{
+ /* Open a LPF device and hang it on this interface... */
+ info -> rfdesc = if_register_lpf (info);
+
+#if defined (HAVE_TR_SUPPORT)
+ if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
+ lpf_tr_filter_setup (info);
+ else
+#endif
+ lpf_gen_filter_setup (info);
+
+ if (!quiet_interface_discovery)
+ log_info ("Listening on LPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+ /* for LPF this is simple, packet filters are removed when sockets
+ are closed */
+ close (info -> rfdesc);
+ info -> rfdesc = -1;
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on LPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+static void lpf_gen_filter_setup (info)
+ struct interface_info *info;
+{
+ struct sock_fprog p;
+
+ memset(&p, 0, sizeof(p));
+
+ /* Set up the bpf filter program structure. This is defined in
+ bpf.c */
+ p.len = dhcp_bpf_filter_len;
+ p.filter = dhcp_bpf_filter;
+
+ /* Patch the server port into the LPF program...
+ XXX changes to filter program may require changes
+ to the insn number(s) used below! XXX */
+ dhcp_bpf_filter [8].k = ntohs ((short)local_port);
+
+ if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
+ sizeof p) < 0) {
+ if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
+ errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
+ errno == EAFNOSUPPORT) {
+ log_error ("socket: %m - make sure");
+ log_error ("CONFIG_PACKET (Packet socket) %s",
+ "and CONFIG_FILTER");
+ log_error ("(Socket Filtering) are enabled %s",
+ "in your kernel");
+ log_fatal ("configuration!");
+ }
+ log_fatal ("Can't install packet filter program: %m");
+ }
+}
+
+#if defined (HAVE_TR_SUPPORT)
+static void lpf_tr_filter_setup (info)
+ struct interface_info *info;
+{
+ struct sock_fprog p;
+
+ memset(&p, 0, sizeof(p));
+
+ /* Set up the bpf filter program structure. This is defined in
+ bpf.c */
+ p.len = dhcp_bpf_tr_filter_len;
+ p.filter = dhcp_bpf_tr_filter;
+
+ /* Patch the server port into the LPF program...
+ XXX changes to filter program may require changes
+ XXX to the insn number(s) used below!
+ XXX Token ring filter is null - when/if we have a filter
+ XXX that's not, we'll need this code.
+ XXX dhcp_bpf_filter [?].k = ntohs (local_port); */
+
+ if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
+ sizeof p) < 0) {
+ if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
+ errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
+ errno == EAFNOSUPPORT) {
+ log_error ("socket: %m - make sure");
+ log_error ("CONFIG_PACKET (Packet socket) %s",
+ "and CONFIG_FILTER");
+ log_error ("(Socket Filtering) are enabled %s",
+ "in your kernel");
+ log_fatal ("configuration!");
+ }
+ log_fatal ("Can't install packet filter program: %m");
+ }
+}
+#endif /* HAVE_TR_SUPPORT */
+#endif /* USE_LPF_RECEIVE */
+
+#ifdef USE_LPF_SEND
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ unsigned hbufp = 0, ibufp = 0;
+ double hh [16];
+ double ih [1536 / sizeof (double)];
+ unsigned char *buf = (unsigned char *)ih;
+ struct sockaddr_pkt sa;
+ int result;
+ int fudge;
+
+ if (!strcmp (interface -> name, "fallback"))
+ return send_fallback (interface, packet, raw,
+ len, from, to, hto);
+
+ if (hto == NULL && interface->anycast_mac_addr.hlen)
+ hto = &interface->anycast_mac_addr;
+
+ /* Assemble the headers... */
+ assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto);
+ fudge = hbufp % 4; /* IP header must be word-aligned. */
+ memcpy (buf + fudge, (unsigned char *)hh, hbufp);
+ ibufp = hbufp + fudge;
+ assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
+ to -> sin_addr.s_addr, to -> sin_port,
+ (unsigned char *)raw, len);
+ memcpy (buf + ibufp, raw, len);
+
+ /* For some reason, SOCK_PACKET sockets can't be connected,
+ so we have to do a sentdo every time. */
+ memset (&sa, 0, sizeof sa);
+ sa.spkt_family = AF_PACKET;
+ strncpy ((char *)sa.spkt_device,
+ (const char *)interface -> ifp, sizeof sa.spkt_device);
+ sa.spkt_protocol = htons(ETH_P_IP);
+
+ result = sendto (interface -> wfdesc,
+ buf + fudge, ibufp + len - fudge, 0,
+ (const struct sockaddr *)&sa, sizeof sa);
+ if (result < 0)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_LPF_SEND */
+
+#ifdef USE_LPF_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+ int length = 0;
+ int offset = 0;
+ unsigned char ibuf [1536];
+ unsigned bufix = 0;
+ unsigned paylen;
+
+ length = read (interface -> rfdesc, ibuf, sizeof ibuf);
+ if (length <= 0)
+ return length;
+
+ bufix = 0;
+ /* Decode the physical header... */
+ offset = decode_hw_header (interface, ibuf, bufix, hfrom);
+
+ /* If a physical layer checksum failed (dunno of any
+ physical layer that supports this, but WTH), skip this
+ packet. */
+ if (offset < 0) {
+ return 0;
+ }
+
+ bufix += offset;
+ length -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header (interface, ibuf, bufix, from,
+ (unsigned)length, &paylen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0)
+ return 0;
+
+ bufix += offset;
+ length -= offset;
+
+ if (length < paylen)
+ log_fatal("Internal inconsistency at %s:%d.", MDL);
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, &ibuf[bufix], paylen);
+ return paylen;
+}
+
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+void maybe_setup_fallback ()
+{
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ if_register_fallback (fbi);
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for \"%s\": %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+}
+
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+ int sock;
+ struct ifreq tmp;
+ struct sockaddr *sa;
+
+ if (strlen(name) >= sizeof(tmp.ifr_name)) {
+ log_fatal("Device name too long: \"%s\"", name);
+ }
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ log_fatal("Can't create socket for \"%s\": %m", name);
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.ifr_name, name);
+ if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) {
+ log_fatal("Error getting hardware address for \"%s\": %m",
+ name);
+ }
+
+ sa = &tmp.ifr_hwaddr;
+ switch (sa->sa_family) {
+ case ARPHRD_ETHER:
+ hw->hlen = 7;
+ hw->hbuf[0] = HTYPE_ETHER;
+ memcpy(&hw->hbuf[1], sa->sa_data, 6);
+ break;
+ case ARPHRD_IEEE802:
+#ifdef ARPHRD_IEEE802_TR
+ case ARPHRD_IEEE802_TR:
+#endif /* ARPHRD_IEEE802_TR */
+ hw->hlen = 7;
+ hw->hbuf[0] = HTYPE_IEEE802;
+ memcpy(&hw->hbuf[1], sa->sa_data, 6);
+ break;
+ case ARPHRD_FDDI:
+ hw->hlen = 17;
+ hw->hbuf[0] = HTYPE_FDDI;
+ memcpy(&hw->hbuf[1], sa->sa_data, 16);
+ break;
+ default:
+ log_fatal("Unsupported device type %ld for \"%s\"",
+ (long int)sa->sa_family, name);
+ }
+
+ close(sock);
+}
+#endif
diff --git a/common/memory.c b/common/memory.c
new file mode 100644
index 0000000..0e74159
--- /dev/null
+++ b/common/memory.c
@@ -0,0 +1,154 @@
+/* memory.c
+
+ Memory-resident database... */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+struct group *root_group;
+group_hash_t *group_name_hash;
+int (*group_write_hook) (struct group_object *);
+
+isc_result_t delete_group (struct group_object *group, int writep)
+{
+ struct group_object *d;
+
+ /* The group should exist and be hashed - if not, it's invalid. */
+ if (group_name_hash) {
+ d = (struct group_object *)0;
+ group_hash_lookup (&d, group_name_hash, group -> name,
+ strlen (group -> name), MDL);
+ } else
+ return DHCP_R_INVALIDARG;
+ if (!d)
+ return DHCP_R_INVALIDARG;
+
+ /* Also not okay to delete a group that's not the one in
+ the hash table. */
+ if (d != group)
+ return DHCP_R_INVALIDARG;
+
+ /* If it's dynamic, and we're deleting it, we can just blow away the
+ hash table entry. */
+ if ((group -> flags & GROUP_OBJECT_DYNAMIC) &&
+ !(group -> flags & GROUP_OBJECT_STATIC)) {
+ group_hash_delete (group_name_hash,
+ group -> name, strlen (group -> name), MDL);
+ } else {
+ group -> flags |= GROUP_OBJECT_DELETED;
+ if (group -> group)
+ group_dereference (&group -> group, MDL);
+ }
+
+ /* Store the group declaration in the lease file. */
+ if (writep && group_write_hook) {
+ if (!(*group_write_hook) (group))
+ return ISC_R_IOERROR;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t supersede_group (struct group_object *group, int writep)
+{
+ struct group_object *t;
+
+ /* Register the group in the group name hash table,
+ so we can look it up later. */
+ if (group_name_hash) {
+ t = (struct group_object *)0;
+ group_hash_lookup (&t, group_name_hash,
+ group -> name,
+ strlen (group -> name), MDL);
+ if (t && t != group) {
+ /* If this isn't a dynamic entry, then we need to flag
+ the replacement as not dynamic either - otherwise,
+ if the dynamic entry is deleted later, the static
+ entry will come back next time the server is stopped
+ and restarted. */
+ if (!(t -> flags & GROUP_OBJECT_DYNAMIC))
+ group -> flags |= GROUP_OBJECT_STATIC;
+
+ /* Delete the old object if it hasn't already been
+ deleted. If it has already been deleted, get rid of
+ the hash table entry. This is a legitimate
+ situation - a deleted static object needs to be kept
+ around so we remember it's deleted. */
+ if (!(t -> flags & GROUP_OBJECT_DELETED))
+ delete_group (t, 0);
+ else {
+ group_hash_delete (group_name_hash,
+ group -> name,
+ strlen (group -> name),
+ MDL);
+ group_object_dereference (&t, MDL);
+ }
+ }
+ } else {
+ group_new_hash(&group_name_hash, GROUP_HASH_SIZE, MDL);
+ t = (struct group_object *)0;
+ }
+
+ /* Add the group to the group name hash if it's not
+ already there, and also thread it into the list of
+ dynamic groups if appropriate. */
+ if (!t) {
+ group_hash_add (group_name_hash, group -> name,
+ strlen (group -> name), group, MDL);
+ }
+
+ /* Store the group declaration in the lease file. */
+ if (writep && group_write_hook) {
+ if (!(*group_write_hook) (group))
+ return ISC_R_IOERROR;
+ }
+ return ISC_R_SUCCESS;
+}
+
+int clone_group (struct group **gp, struct group *group,
+ const char *file, int line)
+{
+ struct group *g = (struct group *)0;
+
+ /* Normally gp should contain the null pointer, but for convenience
+ it's permissible to clone a group into itself. */
+ if (*gp && *gp != group)
+ return 0;
+ if (!group_allocate (&g, file, line))
+ return 0;
+ if (group == *gp)
+ *gp = (struct group *)0;
+ group_reference (gp, g, file, line);
+ g -> authoritative = group -> authoritative;
+ group_reference (&g -> next, group, file, line);
+ group_dereference (&g, file, line);
+ return 1;
+}
diff --git a/common/nit.c b/common/nit.c
new file mode 100644
index 0000000..3822206
--- /dev/null
+++ b/common/nit.c
@@ -0,0 +1,422 @@
+/* nit.c
+
+ Network Interface Tap (NIT) network interface code, by Ted Lemon
+ with one crucial tidbit of help from Stu Grossmen. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <sys/time.h>
+#include <net/nit.h>
+#include <net/nit_if.h>
+#include <net/nit_pf.h>
+#include <net/nit_buf.h>
+#include <sys/stropts.h>
+#include <net/packetfilt.h>
+
+#include <netinet/in_systm.h>
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#ifdef USE_NIT_SEND
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+#ifdef USE_NIT_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+/* 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_nit (info)
+ struct interface_info *info;
+{
+ int sock;
+ char filename[50];
+ struct ifreq ifr;
+ struct strioctl sio;
+
+ /* Open a NIT device */
+ sock = open ("/dev/nit", O_RDWR);
+ if (sock < 0)
+ log_fatal ("Can't open NIT device for %s: %m", info -> name);
+
+ /* Set the NIT device to point at this interface. */
+ sio.ic_cmd = NIOCBIND;
+ sio.ic_len = sizeof *(info -> ifp);
+ sio.ic_dp = (char *)(info -> ifp);
+ sio.ic_timout = INFTIM;
+ if (ioctl (sock, I_STR, &sio) < 0)
+ log_fatal ("Can't attach interface %s to nit device: %m",
+ info -> name);
+
+ /* Get the low-level address... */
+ sio.ic_cmd = SIOCGIFADDR;
+ sio.ic_len = sizeof ifr;
+ sio.ic_dp = (char *)&ifr;
+ sio.ic_timout = INFTIM;
+ if (ioctl (sock, I_STR, &sio) < 0)
+ log_fatal ("Can't get physical layer address for %s: %m",
+ info -> name);
+
+ /* XXX code below assumes ethernet interface! */
+ info -> hw_address.hlen = 7;
+ info -> hw_address.hbuf [0] = ARPHRD_ETHER;
+ memcpy (&info -> hw_address.hbuf [1],
+ ifr.ifr_ifru.ifru_addr.sa_data, 6);
+
+ if (ioctl (sock, I_PUSH, "pf") < 0)
+ log_fatal ("Can't push packet filter onto NIT for %s: %m",
+ info -> name);
+
+ return sock;
+}
+#endif /* USE_NIT_SEND || USE_NIT_RECEIVE */
+
+#ifdef USE_NIT_SEND
+void if_register_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the nit API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_NIT_RECEIVE
+ struct packetfilt pf;
+ struct strioctl sio;
+
+ info -> wfdesc = if_register_nit (info);
+
+ pf.Pf_Priority = 0;
+ pf.Pf_FilterLen = 1;
+ pf.Pf_Filter [0] = ENF_PUSHZERO;
+
+ /* Set up an NIT filter that rejects everything... */
+ sio.ic_cmd = NIOCSETF;
+ sio.ic_len = sizeof pf;
+ sio.ic_dp = (char *)&pf;
+ sio.ic_timout = INFTIM;
+ if (ioctl (info -> wfdesc, I_STR, &sio) < 0)
+ log_fatal ("Can't set NIT filter: %m");
+#else
+ info -> wfdesc = info -> rfdesc;
+#endif
+ if (!quiet_interface_discovery)
+ log_info ("Sending on NIT/%s%s%s",
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the nit API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_NIT_RECEIVE
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on NIT/%s%s%s",
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_NIT_SEND */
+
+#ifdef USE_NIT_RECEIVE
+/* Packet filter program...
+ XXX Changes to the filter program may require changes to the constant
+ offsets used in if_register_send to patch the NIT program! XXX */
+
+void if_register_receive (info)
+ struct interface_info *info;
+{
+ int flag = 1;
+ u_int32_t x;
+ struct packetfilt pf;
+ struct strioctl sio;
+ u_int16_t addr [2];
+ struct timeval t;
+
+ /* Open a NIT device and hang it on this interface... */
+ info -> rfdesc = if_register_nit (info);
+
+ /* Set the snap length to 0, which means always take the whole
+ packet. */
+ x = 0;
+ if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0)
+ log_fatal ("Can't set NIT snap length on %s: %m", info -> name);
+
+ /* Set the stream to byte stream mode */
+ if (ioctl (info -> rfdesc, I_SRDOPT, RMSGN) != 0)
+ log_info ("I_SRDOPT failed on %s: %m", info -> name);
+
+#if 0
+ /* Push on the chunker... */
+ if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0)
+ log_fatal ("Can't push chunker onto NIT STREAM: %m");
+
+ /* Set the timeout to zero. */
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+ if (ioctl (info -> rfdesc, NIOCSTIME, &t) < 0)
+ log_fatal ("Can't set chunk timeout: %m");
+#endif
+
+ /* Ask for no header... */
+ x = 0;
+ if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0)
+ log_fatal ("Can't set NIT flags on %s: %m", info -> name);
+
+ /* Set up the NIT filter program. */
+ /* XXX Unlike the BPF filter program, this one won't work if the
+ XXX IP packet is fragmented or if there are options on the IP
+ XXX header. */
+ pf.Pf_Priority = 0;
+ pf.Pf_FilterLen = 0;
+
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 6;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (ETHERTYPE_IP);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (IPPROTO_UDP);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 11;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = htons (0xFF);
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHWORD + 18;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+ pf.Pf_Filter [pf.Pf_FilterLen++] = local_port;
+
+ /* Install the filter... */
+ sio.ic_cmd = NIOCSETF;
+ sio.ic_len = sizeof pf;
+ sio.ic_dp = (char *)&pf;
+ sio.ic_timout = INFTIM;
+ if (ioctl (info -> rfdesc, I_STR, &sio) < 0)
+ log_fatal ("Can't set NIT filter on %s: %m", info -> name);
+
+ if (!quiet_interface_discovery)
+ log_info ("Listening on NIT/%s%s%s",
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+ /* If we're using the nit API for sending and receiving,
+ we don't need to register this interface twice. */
+ close (info -> rfdesc);
+ info -> rfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on NIT/%s%s%s",
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_NIT_RECEIVE */
+
+#ifdef USE_NIT_SEND
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ unsigned hbufp, ibufp;
+ double hh [16];
+ double ih [1536 / sizeof (double)];
+ unsigned char *buf = (unsigned char *)ih;
+ struct sockaddr *junk;
+ struct strbuf ctl, data;
+ struct sockaddr_in foo;
+ int result;
+
+ if (!strcmp (interface -> name, "fallback"))
+ return send_fallback (interface, packet, raw,
+ len, from, to, hto);
+
+ if (hto == NULL && interface->anycast_mac_addr.hlen)
+ hto = &interface->anycast_mac_addr;
+
+ /* Start with the sockaddr struct... */
+ junk = (struct sockaddr *)&hh [0];
+ hbufp = (((unsigned char *)&junk -> sa_data [0]) -
+ (unsigned char *)&hh[0]);
+ ibufp = 0;
+
+ /* Assemble the headers... */
+ assemble_hw_header (interface, (unsigned char *)junk, &hbufp, hto);
+ assemble_udp_ip_header (interface, buf, &ibufp,
+ from.s_addr, to -> sin_addr.s_addr,
+ to -> sin_port, (unsigned char *)raw, len);
+
+ /* Copy the data into the buffer (yuk). */
+ memcpy (buf + ibufp, raw, len);
+
+ /* Set up the sockaddr structure... */
+#if USE_SIN_LEN
+ junk -> sa_len = hbufp - 2; /* XXX */
+#endif
+ junk -> sa_family = AF_UNSPEC;
+
+ /* Set up the msg_buf structure... */
+ ctl.buf = (char *)&hh [0];
+ ctl.maxlen = ctl.len = hbufp;
+ data.buf = (char *)&ih [0];
+ data.maxlen = data.len = ibufp + len;
+
+ result = putmsg (interface -> wfdesc, &ctl, &data, 0);
+ if (result < 0)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_NIT_SEND */
+
+#ifdef USE_NIT_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+ int nread;
+ int length = 0;
+ int offset = 0;
+ unsigned char ibuf [1536];
+ int bufix = 0;
+ unsigned paylen;
+
+ length = read (interface -> rfdesc, ibuf, sizeof ibuf);
+ if (length <= 0)
+ return length;
+
+ /* Decode the physical header... */
+ offset = decode_hw_header (interface, ibuf, bufix, hfrom);
+
+ /* If a physical layer checksum failed (dunno of any
+ physical layer that supports this, but WTH), skip this
+ packet. */
+ if (offset < 0) {
+ return 0;
+ }
+
+ bufix += offset;
+ length -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header (interface, ibuf, bufix,
+ from, length, &paylen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0)
+ return 0;
+
+ bufix += offset;
+ length -= offset;
+
+ if (length < paylen)
+ log_fatal("Internal inconsistency at %s:%d.", MDL);
+
+ /* Copy out the data in the packet... */
+ memcpy(buf, &ibuf[bufix], paylen);
+ return paylen;
+}
+
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+void maybe_setup_fallback ()
+{
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ if_register_fallback (fbi);
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+}
+#endif
diff --git a/common/ns_name.c b/common/ns_name.c
new file mode 100644
index 0000000..74de868
--- /dev/null
+++ b/common/ns_name.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: ns_name.c,v 1.2 2009-10-28 04:12:29 sar Exp $";
+#endif
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "minires.h"
+#include "arpa/nameser.h"
+
+/* Data. */
+
+static const char digits[] = "0123456789";
+
+/* Forward. */
+
+static int special(int);
+static int printable(int);
+static int dn_find(const u_char *, const u_char *,
+ const u_char * const *,
+ const u_char * const *);
+
+/* Public. */
+
+/*
+ * MRns_name_ntop(src, dst, dstsiz)
+ * Convert an encoded domain name to printable ascii as per RFC1035.
+ * return:
+ * Number of bytes written to buffer, or -1 (with errno set)
+ * notes:
+ * The root is returned as "."
+ * All other domains are returned in non absolute form
+ */
+int
+MRns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
+ const u_char *cp;
+ char *dn, *eom;
+ u_char c;
+ u_int n;
+
+ cp = src;
+ dn = dst;
+ eom = dst + dstsiz;
+
+ while ((n = *cp++) != 0) {
+ if ((n & NS_CMPRSFLGS) != 0) {
+ /* Some kind of compression pointer. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dn != dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if (dn + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ for ((void)NULL; n > 0; n--) {
+ c = *cp++;
+ if (special(c)) {
+ if (dn + 1 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = (char)c;
+ } else if (!printable(c)) {
+ if (dn + 3 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = digits[c / 100];
+ *dn++ = digits[(c % 100) / 10];
+ *dn++ = digits[c % 10];
+ } else {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = (char)c;
+ }
+ }
+ }
+ if (dn == dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\0';
+ return (dn - dst);
+}
+
+/*
+ * MRns_name_pton(src, dst, dstsiz)
+ * Convert a ascii string into an encoded domain name as per RFC1035.
+ * return:
+ * -1 if it fails
+ * 1 if string was fully qualified
+ * 0 is string was not fully qualified
+ * notes:
+ * Enforces label and domain length limits.
+ */
+
+int
+MRns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
+ u_char *label, *bp, *eom;
+ int c, n, escaped;
+ char *cp;
+
+ escaped = 0;
+ bp = dst;
+ eom = dst + dstsiz;
+ label = bp++;
+
+ while ((c = *src++) != 0) {
+ if (escaped) {
+ if ((cp = strchr(digits, c)) != NULL) {
+ n = (cp - digits) * 100;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits) * 10;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits);
+ if (n > 255) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ c = n;
+ }
+ escaped = 0;
+ } else if (c == '\\') {
+ escaped = 1;
+ continue;
+ } else if (c == '.') {
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ /* Fully qualified ? */
+ if (*src == '\0') {
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = '\0';
+ }
+ if ((bp - dst) > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (1);
+ }
+ if (c == 0 || *src == '.') {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ label = bp++;
+ continue;
+ }
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = (u_char)c;
+ }
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = 0;
+ }
+ if ((bp - dst) > MAXCDNAME) { /* src too big */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * MRns_name_ntol(src, dst, dstsiz)
+ * Convert a network strings labels into all lowercase.
+ * return:
+ * Number of bytes written to buffer, or -1 (with errno set)
+ * notes:
+ * Enforces label and domain length limits.
+ */
+
+int
+MRns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
+ const u_char *cp;
+ u_char *dn, *eom;
+ u_char c;
+ u_int n;
+
+ cp = src;
+ dn = dst;
+ eom = dst + dstsiz;
+
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ while ((n = *cp++) != 0) {
+ if ((n & NS_CMPRSFLGS) != 0) {
+ /* Some kind of compression pointer. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = n;
+ if (dn + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ for ((void)NULL; n > 0; n--) {
+ c = *cp++;
+ if (isupper(c))
+ *dn++ = tolower(c);
+ else
+ *dn++ = c;
+ }
+ }
+ *dn++ = '\0';
+ return (dn - dst);
+}
+
+/*
+ * MRns_name_unpack(msg, eom, src, dst, dstsiz)
+ * Unpack a domain name from a message, source may be compressed.
+ * return:
+ * -1 if it fails, or consumed octets if it succeeds.
+ */
+int
+MRns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
+ u_char *dst, size_t dstsiz)
+{
+ const u_char *srcp, *dstlim;
+ u_char *dstp;
+ unsigned n;
+ int len;
+ int checked;
+
+ len = -1;
+ checked = 0;
+ dstp = dst;
+ srcp = src;
+ dstlim = dst + dstsiz;
+ if (srcp < msg || srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ /* Fetch next label in domain name. */
+ while ((n = *srcp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0:
+ /* Limit checks. */
+ if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += n + 1;
+ *dstp++ = n;
+ memcpy(dstp, srcp, n);
+ dstp += n;
+ srcp += n;
+ break;
+
+ case NS_CMPRSFLGS:
+ if (srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (len < 0)
+ len = srcp - src + 1;
+ srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
+ if (srcp < msg || srcp >= eom) { /* Out of range. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += 2;
+ /*
+ * Check for loops in the compressed name;
+ * if we've looked at the whole message,
+ * there must be a loop.
+ */
+ if (checked >= eom - msg) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+
+ default:
+ errno = EMSGSIZE;
+ return (-1); /* flag error */
+ }
+ }
+ *dstp = '\0';
+ if (len < 0)
+ len = srcp - src;
+ return (len);
+}
+
+/*
+ * MRns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
+ * Pack domain name 'domain' into 'comp_dn'.
+ * return:
+ * Size of the compressed name, or -1.
+ * notes:
+ * 'dnptrs' is an array of pointers to previous compressed names.
+ * dnptrs[0] is a pointer to the beginning of the message. The array
+ * ends with NULL.
+ * 'lastdnptr' is a pointer to the end of the array pointed to
+ * by 'dnptrs'.
+ * Side effects:
+ * The list of pointers in dnptrs is updated for labels inserted into
+ * the message as we compress the name. If 'dnptr' is NULL, we don't
+ * try to compress names. If 'lastdnptr' is NULL, we don't update the
+ * list.
+ */
+int
+MRns_name_pack(const u_char *src, u_char *dst, unsigned dstsiz,
+ const u_char **dnptrs, const u_char **lastdnptr)
+{
+ u_char *dstp;
+ const u_char **cpp, **lpp, *eob, *msg;
+ const u_char *srcp;
+ unsigned n;
+ int l;
+
+ srcp = src;
+ dstp = dst;
+ eob = dstp + dstsiz;
+ lpp = cpp = NULL;
+ if (dnptrs != NULL) {
+ if ((msg = *dnptrs++) != NULL) {
+ for (cpp = dnptrs; *cpp != NULL; cpp++)
+ (void)NULL;
+ lpp = cpp; /* end of list to search */
+ }
+ } else
+ msg = NULL;
+
+ /* make sure the domain we are about to add is legal */
+ l = 0;
+ do {
+ n = *srcp;
+ if ((n & NS_CMPRSFLGS) != 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ l += n + 1;
+ if (l > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ srcp += n + 1;
+ } while (n != 0);
+
+ /* from here on we need to reset compression pointer array on error */
+ srcp = src;
+ do {
+ /* Look to see if we can use pointers. */
+ n = *srcp;
+ if (n != 0 && msg != NULL) {
+ l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
+ (const u_char * const *)lpp);
+ if (l >= 0) {
+ if (dstp + 1 >= eob) {
+ goto cleanup;
+ }
+ *dstp++ = (l >> 8) | NS_CMPRSFLGS;
+ *dstp++ = l % 256;
+ return (dstp - dst);
+ }
+ /* Not found, save it. */
+ if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
+ (dstp - msg) < 0x4000) {
+ *cpp++ = dstp;
+ *cpp = NULL;
+ }
+ }
+ /* copy label to buffer */
+ if (n & NS_CMPRSFLGS) { /* Should not happen. */
+ goto cleanup;
+ }
+ if (dstp + 1 + n >= eob) {
+ goto cleanup;
+ }
+ memcpy(dstp, srcp, n + 1);
+ srcp += n + 1;
+ dstp += n + 1;
+ } while (n != 0);
+
+ if (dstp > eob) {
+cleanup:
+ if (msg != NULL)
+ *lpp = NULL;
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (dstp - dst);
+}
+
+/*
+ * MRns_name_uncompress(msg, eom, src, dst, dstsiz)
+ * Expand compressed domain name to presentation format.
+ * return:
+ * Number of bytes read out of `src', or -1 (with errno set).
+ * note:
+ * Root domain returns as "." not "".
+ */
+int
+MRns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
+ char *dst, size_t dstsiz)
+{
+ u_char tmp[NS_MAXCDNAME];
+ int n;
+
+ if ((n = MRns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
+ return (-1);
+ if (MRns_name_ntop(tmp, dst, dstsiz) == -1)
+ return (-1);
+ return (n);
+}
+
+/*
+ * MRns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
+ * Compress a domain name into wire format, using compression pointers.
+ * return:
+ * Number of bytes consumed in `dst' or -1 (with errno set).
+ * notes:
+ * 'dnptrs' is an array of pointers to previous compressed names.
+ * dnptrs[0] is a pointer to the beginning of the message.
+ * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
+ * array pointed to by 'dnptrs'. Side effect is to update the list of
+ * pointers for labels inserted into the message as we compress the name.
+ * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ * is NULL, we don't update the list.
+ */
+int
+MRns_name_compress(const char *src, u_char *dst, size_t dstsiz,
+ const u_char **dnptrs, const u_char **lastdnptr)
+{
+ u_char tmp[NS_MAXCDNAME];
+
+ if (MRns_name_pton(src, tmp, sizeof tmp) == -1)
+ return (-1);
+ return (MRns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
+}
+
+/*
+ * MRns_name_skip(ptrptr, eom)
+ * Advance *ptrptr to skip over the compressed name it points at.
+ * return:
+ * 0 on success, -1 (with errno set) on failure.
+ */
+int
+MRns_name_skip(const u_char **ptrptr, const u_char *eom) {
+ const u_char *cp;
+ u_int n;
+
+ cp = *ptrptr;
+ while (cp < eom && (n = *cp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /* normal case, n == len */
+ cp += n;
+ continue;
+ case NS_CMPRSFLGS: /* indirection */
+ cp++;
+ break;
+ default: /* illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+ }
+ if (cp > eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *ptrptr = cp;
+ return (0);
+}
+
+/* Private. */
+
+/*
+ * special(ch)
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this characted special ("in need of quoting") ?
+ * return:
+ * boolean.
+ */
+static int
+special(int ch) {
+ switch (ch) {
+ case 0x22: /* '"' */
+ case 0x2E: /* '.' */
+ case 0x3B: /* ';' */
+ case 0x5C: /* '\\' */
+ /* Special modifiers in zone files. */
+ case 0x40: /* '@' */
+ case 0x24: /* '$' */
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * printable(ch)
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this character visible and not a space when printed ?
+ * return:
+ * boolean.
+ */
+static int
+printable(int ch) {
+ return (ch > 0x20 && ch < 0x7f);
+}
+
+/*
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * convert this character to lower case if it's upper case.
+ */
+static int
+mklower(int ch) {
+ if (ch >= 0x41 && ch <= 0x5A)
+ return (ch + 0x20);
+ return (ch);
+}
+
+/*
+ * dn_find(domain, msg, dnptrs, lastdnptr)
+ * Search for the counted-label name in an array of compressed names.
+ * return:
+ * offset from msg if found, or -1.
+ * notes:
+ * dnptrs is the pointer to the first name on the list,
+ * not the pointer to the start of the message.
+ */
+static int
+dn_find(const u_char *domain, const u_char *msg,
+ const u_char * const *dnptrs,
+ const u_char * const *lastdnptr)
+{
+ const u_char *dn, *cp, *sp;
+ const u_char * const *cpp;
+ u_int n;
+
+ for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+ dn = domain;
+ sp = cp = *cpp;
+ while ((n = *cp++) != 0) {
+ /*
+ * check for indirection
+ */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /* normal case, n == len */
+ if (n != *dn++)
+ goto next;
+ for ((void)NULL; n > 0; n--)
+ if (mklower(*dn++) != mklower(*cp++))
+ goto next;
+ /* Is next root for both ? */
+ if (*dn == '\0' && *cp == '\0')
+ return (sp - msg);
+ if (*dn)
+ continue;
+ goto next;
+
+ case NS_CMPRSFLGS: /* indirection */
+ cp = msg + (((n & 0x3f) << 8) | *cp);
+ break;
+
+ default: /* illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ }
+ next: ;
+ }
+ errno = ENOENT;
+ return (-1);
+}
diff --git a/common/options.c b/common/options.c
new file mode 100644
index 0000000..80fd8db
--- /dev/null
+++ b/common/options.c
@@ -0,0 +1,4079 @@
+/* options.c
+
+ DHCP options parsing and reassembly. */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#define DHCP_OPTION_DATA
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <limits.h>
+
+struct option *vendor_cfg_option;
+
+static int pretty_text(char **, char *, const unsigned char **,
+ const unsigned char *, int);
+static int pretty_domain(char **, char *, const unsigned char **,
+ const unsigned char *);
+static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
+ unsigned char *buffer, unsigned length,
+ unsigned code, int terminatep,
+ struct option_cache **opp);
+
+/* Parse all available options out of the specified packet. */
+
+int parse_options (packet)
+ struct packet *packet;
+{
+ struct option_cache *op = (struct option_cache *)0;
+
+ /* Allocate a new option state. */
+ if (!option_state_allocate (&packet -> options, MDL)) {
+ packet -> options_valid = 0;
+ return 0;
+ }
+
+ /* 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 1;
+ }
+
+ /* Go through the options field, up to the end of the packet
+ or the End field. */
+ if (!parse_option_buffer (packet -> options,
+ &packet -> raw -> options [4],
+ (packet -> packet_length -
+ DHCP_FIXED_NON_UDP - 4),
+ &dhcp_universe)) {
+
+ /* STSN servers have a bug where they send a mangled
+ domain-name option, and whatever is beyond that in
+ the packet is junk. Microsoft clients accept this,
+ which is probably why whoever implemented the STSN
+ server isn't aware of the problem yet. To work around
+ this, we will accept corrupt packets from the server if
+ they contain a valid DHCP_MESSAGE_TYPE option, but
+ will not accept any corrupt client packets (the ISC DHCP
+ server is sufficiently widely used that it is probably
+ beneficial for it to be picky) and will not accept
+ packets whose type can't be determined. */
+
+ if ((op = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_MESSAGE_TYPE))) {
+ if (!op -> data.data ||
+ (op -> data.data [0] != DHCPOFFER &&
+ op -> data.data [0] != DHCPACK &&
+ op -> data.data [0] != DHCPNAK))
+ return 0;
+ } else
+ return 0;
+ }
+
+ /* If we parsed a DHCP Option Overload option, parse more
+ options out of the buffer(s) containing them. */
+ if ((op = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_OPTION_OVERLOAD))) {
+ if (op -> data.data [0] & 1) {
+ if (!parse_option_buffer
+ (packet -> options,
+ (unsigned char *)packet -> raw -> file,
+ sizeof packet -> raw -> file,
+ &dhcp_universe))
+ return 0;
+ }
+ if (op -> data.data [0] & 2) {
+ if (!parse_option_buffer
+ (packet -> options,
+ (unsigned char *)packet -> raw -> sname,
+ sizeof packet -> raw -> sname,
+ &dhcp_universe))
+ return 0;
+ }
+ }
+ packet -> options_valid = 1;
+ return 1;
+}
+
+/* Parse options out of the specified buffer, storing addresses of option
+ * values in packet->options.
+ */
+int parse_option_buffer (options, buffer, length, universe)
+ struct option_state *options;
+ const unsigned char *buffer;
+ unsigned length;
+ struct universe *universe;
+{
+ unsigned len, offset;
+ unsigned code;
+ struct option_cache *op = NULL, *nop = NULL;
+ struct buffer *bp = (struct buffer *)0;
+ struct option *option = NULL;
+ char *reason = "general failure";
+
+ if (!buffer_allocate (&bp, length, MDL)) {
+ log_error ("no memory for option buffer.");
+ return 0;
+ }
+ memcpy (bp -> data, buffer, length);
+
+ for (offset = 0;
+ (offset + universe->tag_size) <= length &&
+ (code = universe->get_tag(buffer + offset)) != universe->end; ) {
+ offset += universe->tag_size;
+
+ /* Pad options don't have a length - just skip them. */
+ if (code == DHO_PAD)
+ continue;
+
+ /* Don't look for length if the buffer isn't that big. */
+ if ((offset + universe->length_size) > length) {
+ reason = "code tag at end of buffer - missing "
+ "length field";
+ goto bogus;
+ }
+
+ /* All other fields (except PAD and END handled above)
+ * have a length field, unless it's a DHCPv6 zero-length
+ * options space (eg any of the enterprise-id'd options).
+ *
+ * Zero-length-size option spaces basically consume the
+ * entire options buffer, so have at it.
+ */
+ if (universe->get_length != NULL)
+ len = universe->get_length(buffer + offset);
+ else if (universe->length_size == 0)
+ len = length - universe->tag_size;
+ else {
+ log_fatal("Improperly configured option space(%s): "
+ "may not have a nonzero length size "
+ "AND a NULL get_length function.",
+ universe->name);
+
+ /* Silence compiler warnings. */
+ return 0;
+ }
+
+ offset += universe->length_size;
+
+ option_code_hash_lookup(&option, universe->code_hash, &code,
+ 0, MDL);
+
+ /* If the length is outrageous, the options are bad. */
+ if (offset + len > length) {
+ reason = "option length exceeds option buffer length";
+ bogus:
+ log_error("parse_option_buffer: malformed option "
+ "%s.%s (code %u): %s.", universe->name,
+ option ? option->name : "<unknown>",
+ code, reason);
+ buffer_dereference (&bp, MDL);
+ return 0;
+ }
+
+ /* If the option contains an encapsulation, parse it. If
+ the parse fails, or the option isn't an encapsulation (by
+ far the most common case), or the option isn't entirely
+ an encapsulation, keep the raw data as well. */
+ if (!(option &&
+ (option->format[0] == 'e' ||
+ option->format[0] == 'E') &&
+ (parse_encapsulated_suboptions(options, option,
+ bp->data + offset, len,
+ universe, NULL)))) {
+ op = lookup_option(universe, options, code);
+
+ if (op != NULL && universe->concat_duplicates) {
+ struct data_string new;
+ memset(&new, 0, sizeof new);
+ if (!buffer_allocate(&new.buffer,
+ op->data.len + len,
+ MDL)) {
+ log_error("parse_option_buffer: "
+ "No memory.");
+ buffer_dereference(&bp, MDL);
+ return 0;
+ }
+ /* Copy old option to new data object. */
+ memcpy(new.buffer->data, op->data.data,
+ op->data.len);
+ /* Concat new option behind old. */
+ memcpy(new.buffer->data + op->data.len,
+ bp->data + offset, len);
+ new.len = op->data.len + len;
+ new.data = new.buffer->data;
+ /* Save new concat'd object. */
+ data_string_forget(&op->data, MDL);
+ data_string_copy(&op->data, &new, MDL);
+ data_string_forget(&new, MDL);
+ } else if (op != NULL) {
+ /* We must append this statement onto the
+ * end of the list.
+ */
+ while (op->next != NULL)
+ op = op->next;
+
+ if (!option_cache_allocate(&nop, MDL)) {
+ log_error("parse_option_buffer: "
+ "No memory.");
+ buffer_dereference(&bp, MDL);
+ return 0;
+ }
+
+ option_reference(&nop->option, op->option, MDL);
+
+ nop->data.buffer = NULL;
+ buffer_reference(&nop->data.buffer, bp, MDL);
+ nop->data.data = bp->data + offset;
+ nop->data.len = len;
+
+ option_cache_reference(&op->next, nop, MDL);
+ option_cache_dereference(&nop, MDL);
+ } else {
+ save_option_buffer(universe, options, bp,
+ bp->data + offset, len,
+ code, 1);
+ }
+ }
+ option_dereference(&option, MDL);
+ offset += len;
+ }
+ buffer_dereference (&bp, MDL);
+ return 1;
+}
+
+/* If an option in an option buffer turns out to be an encapsulation,
+ figure out what to do. If we don't know how to de-encapsulate it,
+ or it's not well-formed, return zero; otherwise, return 1, indicating
+ that we succeeded in de-encapsulating it. */
+
+struct universe *find_option_universe (struct option *eopt, const char *uname)
+{
+ int i;
+ char *s, *t;
+ struct universe *universe = (struct universe *)0;
+
+ /* Look for the E option in the option format. */
+ s = strchr (eopt -> format, 'E');
+ if (!s) {
+ log_error ("internal encapsulation format error 1.");
+ return 0;
+ }
+ /* Look for the universe name in the option format. */
+ t = strchr (++s, '.');
+ /* If there was no trailing '.', or there's something after the
+ trailing '.', the option is bogus and we can't use it. */
+ if (!t || t [1]) {
+ log_error ("internal encapsulation format error 2.");
+ return 0;
+ }
+ if (t == s && uname) {
+ for (i = 0; i < universe_count; i++) {
+ if (!strcmp (universes [i] -> name, uname)) {
+ universe = universes [i];
+ break;
+ }
+ }
+ } else if (t != s) {
+ for (i = 0; i < universe_count; i++) {
+ if (strlen (universes [i] -> name) == t - s &&
+ !memcmp (universes [i] -> name,
+ s, (unsigned)(t - s))) {
+ universe = universes [i];
+ break;
+ }
+ }
+ }
+ return universe;
+}
+
+/* If an option in an option buffer turns out to be an encapsulation,
+ figure out what to do. If we don't know how to de-encapsulate it,
+ or it's not well-formed, return zero; otherwise, return 1, indicating
+ that we succeeded in de-encapsulating it. */
+
+int parse_encapsulated_suboptions (struct option_state *options,
+ struct option *eopt,
+ const unsigned char *buffer,
+ unsigned len, struct universe *eu,
+ const char *uname)
+{
+ int i;
+ struct universe *universe = find_option_universe (eopt, uname);
+
+ /* If we didn't find the universe, we can't do anything with it
+ right now (e.g., we can't decode vendor options until we've
+ decoded the packet and executed the scopes that it matches). */
+ if (!universe)
+ return 0;
+
+ /* If we don't have a decoding function for it, we can't decode
+ it. */
+ if (!universe -> decode)
+ return 0;
+
+ i = (*universe -> decode) (options, buffer, len, universe);
+
+ /* If there is stuff before the suboptions, we have to keep it. */
+ if (eopt -> format [0] != 'E')
+ return 0;
+ /* Otherwise, return the status of the decode function. */
+ return i;
+}
+
+int fqdn_universe_decode (struct option_state *options,
+ const unsigned char *buffer,
+ unsigned length, struct universe *u)
+{
+ struct buffer *bp = (struct buffer *)0;
+
+ /* FQDN options have to be at least four bytes long. */
+ if (length < 3)
+ return 0;
+
+ /* Save the contents of the option in a buffer. */
+ if (!buffer_allocate (&bp, length + 4, MDL)) {
+ log_error ("no memory for option buffer.");
+ return 0;
+ }
+ memcpy (&bp -> data [3], buffer + 1, length - 1);
+
+ if (buffer [0] & 4) /* encoded */
+ bp -> data [0] = 1;
+ else
+ bp -> data [0] = 0;
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ bp->data, 1, FQDN_ENCODED, 0)) {
+ bad:
+ buffer_dereference (&bp, MDL);
+ return 0;
+ }
+
+ if (buffer [0] & 1) /* server-update */
+ bp -> data [2] = 1;
+ else
+ bp -> data [2] = 0;
+ if (buffer [0] & 2) /* no-client-update */
+ bp -> data [1] = 1;
+ else
+ bp -> data [1] = 0;
+
+ /* XXX Ideally we should store the name in DNS format, so if the
+ XXX label isn't in DNS format, we convert it to DNS format,
+ XXX rather than converting labels specified in DNS format to
+ XXX the plain ASCII representation. But that's hard, so
+ XXX not now. */
+
+ /* Not encoded using DNS format? */
+ if (!bp -> data [0]) {
+ unsigned i;
+
+ /* Some broken clients NUL-terminate this option. */
+ if (buffer [length - 1] == 0) {
+ --length;
+ bp -> data [1] = 1;
+ }
+
+ /* Determine the length of the hostname component of the
+ name. If the name contains no '.' character, it
+ represents a non-qualified label. */
+ for (i = 3; i < length && buffer [i] != '.'; i++);
+ i -= 3;
+
+ /* Note: If the client sends a FQDN, the first '.' will
+ be used as a NUL terminator for the hostname. */
+ if (i && (!save_option_buffer(&fqdn_universe, options, bp,
+ &bp->data[5], i,
+ FQDN_HOSTNAME, 0)))
+ goto bad;
+ /* Note: If the client sends a single label, the
+ FQDN_DOMAINNAME option won't be set. */
+ if (length > 4 + i &&
+ (!save_option_buffer(&fqdn_universe, options, bp,
+ &bp -> data[6 + i], length - 4 - i,
+ FQDN_DOMAINNAME, 1)))
+ goto bad;
+ /* Also save the whole name. */
+ if (length > 3) {
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ &bp -> data [5], length - 3,
+ FQDN_FQDN, 1))
+ goto bad;
+ }
+ } else {
+ unsigned len;
+ unsigned total_len = 0;
+ unsigned first_len = 0;
+ int terminated = 0;
+ unsigned char *s;
+
+ s = &bp -> data[5];
+
+ while (s < &bp -> data[0] + length + 2) {
+ len = *s;
+ if (len > 63) {
+ log_info ("fancy bits in fqdn option");
+ return 0;
+ }
+ if (len == 0) {
+ terminated = 1;
+ break;
+ }
+ if (s + len > &bp -> data [0] + length + 3) {
+ log_info ("fqdn tag longer than buffer");
+ return 0;
+ }
+
+ if (first_len == 0) {
+ first_len = len;
+ }
+
+ *s = '.';
+ s += len + 1;
+ total_len += len + 1;
+ }
+
+ /* We wind up with a length that's one too many because
+ we shouldn't increment for the last label, but there's
+ no way to tell we're at the last label until we exit
+ the loop. :'*/
+ if (total_len > 0)
+ total_len--;
+
+ if (!terminated) {
+ first_len = total_len;
+ }
+
+ if (first_len > 0 &&
+ !save_option_buffer(&fqdn_universe, options, bp,
+ &bp -> data[6], first_len,
+ FQDN_HOSTNAME, 0))
+ goto bad;
+ if (total_len > 0 && first_len != total_len) {
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ &bp->data[6 + first_len],
+ total_len - first_len,
+ FQDN_DOMAINNAME, 1))
+ goto bad;
+ }
+ if (total_len > 0)
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [6], total_len,
+ FQDN_FQDN, 1))
+ goto bad;
+ }
+
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [1], 1,
+ FQDN_NO_CLIENT_UPDATE, 0))
+ goto bad;
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [2], 1,
+ FQDN_SERVER_UPDATE, 0))
+ goto bad;
+
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [3], 1,
+ FQDN_RCODE1, 0))
+ goto bad;
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [4], 1,
+ FQDN_RCODE2, 0))
+ goto bad;
+
+ buffer_dereference (&bp, MDL);
+ return 1;
+}
+
+/*
+ * Load all options into a buffer, and then split them out into the three
+ * separate fields in the dhcp packet (options, file, and sname) where
+ * options can be stored.
+ */
+int
+cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
+ struct lease *lease, struct client_state *client_state,
+ int mms, struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ int overload_avail, int terminate, int bootpp,
+ struct data_string *prl, const char *vuname)
+{
+#define PRIORITY_COUNT 300
+ unsigned priority_list[PRIORITY_COUNT];
+ int priority_len;
+ unsigned char buffer[4096], agentopts[1024];
+ unsigned index = 0;
+ unsigned mb_size = 0, mb_max = 0;
+ unsigned option_size = 0, agent_size = 0;
+ unsigned length;
+ int i;
+ struct option_cache *op;
+ struct data_string ds;
+ pair pp, *hash;
+ int overload_used = 0;
+ int of1 = 0, of2 = 0;
+
+ memset(&ds, 0, sizeof ds);
+
+ /*
+ * If there's a Maximum Message Size option in the incoming packet
+ * and no alternate maximum message size has been specified, or
+ * if the one specified in the packet is shorter than the
+ * alternative, take the one in the packet.
+ */
+
+ if (inpacket &&
+ (op = lookup_option(&dhcp_universe, inpacket->options,
+ DHO_DHCP_MAX_MESSAGE_SIZE))) {
+ evaluate_option_cache(&ds, inpacket,
+ lease, client_state, in_options,
+ cfg_options, scope, op, MDL);
+ if (ds.len >= sizeof (u_int16_t)) {
+ i = getUShort(ds.data);
+ if(!mms || (i < mms))
+ mms = i;
+ }
+ data_string_forget(&ds, MDL);
+ }
+
+ /*
+ * If the client has provided a maximum DHCP message size,
+ * use that, up to the MTU limit. 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) {
+ if (mms < DHCP_MTU_MIN)
+ /* Enforce minimum packet size, per RFC 2132 */
+ mb_size = DHCP_MIN_OPTION_LEN;
+ else if (mms > DHCP_MTU_MAX)
+ /*
+ * TODO: Packets longer than 1500 bytes really
+ * should be allowed, but it requires upstream
+ * changes to the way the packet is allocated. For
+ * now, we forbid them. They won't be needed very
+ * often anyway.
+ */
+ mb_size = DHCP_MAX_OPTION_LEN;
+ else
+ mb_size = mms - DHCP_FIXED_LEN;
+ } else if (bootpp) {
+ mb_size = 64;
+ if (inpacket != NULL &&
+ (inpacket->packet_length >= 64 + DHCP_FIXED_NON_UDP))
+ mb_size = inpacket->packet_length - DHCP_FIXED_NON_UDP;
+ } else
+ mb_size = DHCP_MIN_OPTION_LEN;
+
+ /*
+ * If answering a client message, see whether any relay agent
+ * options were included with the message. If so, save them
+ * to copy back in later, and make space in the main buffer
+ * to accommodate them
+ */
+ if (client_state == NULL) {
+ priority_list[0] = DHO_DHCP_AGENT_OPTIONS;
+ priority_len = 1;
+ agent_size = store_options(NULL, agentopts, 0,
+ sizeof(agentopts),
+ inpacket, lease, client_state,
+ in_options, cfg_options, scope,
+ priority_list, priority_len,
+ 0, 0, 0, NULL);
+
+ mb_size += agent_size;
+ if (mb_size > DHCP_MAX_OPTION_LEN)
+ mb_size = DHCP_MAX_OPTION_LEN;
+ }
+
+ /*
+ * Set offsets for buffer data to be copied into filename
+ * and servername fields
+ */
+ mb_max = mb_size;
+
+ if (overload_avail & 1) {
+ of1 = mb_max;
+ mb_max += DHCP_FILE_LEN;
+ }
+
+ if (overload_avail & 2) {
+ of2 = mb_max;
+ mb_max += DHCP_SNAME_LEN;
+ }
+
+ /*
+ * Preload the option priority list with protocol-mandatory options.
+ * This effectively gives these options the highest priority.
+ * This provides the order for any available options, the option
+ * must be in the option cache in order to actually be included.
+ */
+ 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_RENEWAL_TIME;
+ priority_list[priority_len++] = DHO_DHCP_REBINDING_TIME;
+ priority_list[priority_len++] = DHO_DHCP_MESSAGE;
+ priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
+ priority_list[priority_len++] = DHO_ASSOCIATED_IP;
+
+ if (prl != NULL && prl->len > 0) {
+ if ((op = lookup_option(&dhcp_universe, cfg_options,
+ DHO_SUBNET_SELECTION))) {
+ if (priority_len < PRIORITY_COUNT)
+ priority_list[priority_len++] =
+ DHO_SUBNET_SELECTION;
+ }
+
+ data_string_truncate(prl, (PRIORITY_COUNT - priority_len));
+
+ /*
+ * Copy the client's PRL onto the priority_list after our high
+ * priority header.
+ */
+ for (i = 0; i < prl->len; i++) {
+ /*
+ * Prevent client from changing order of delivery
+ * of relay agent information option.
+ */
+ if (prl->data[i] != DHO_DHCP_AGENT_OPTIONS)
+ priority_list[priority_len++] = prl->data[i];
+ }
+
+ /*
+ * If the client doesn't request the FQDN option explicitly,
+ * to indicate priority, consider it lowest priority. Fit
+ * in the packet if there is space. Note that the option
+ * may only be included if the client supplied one.
+ */
+ if ((priority_len < PRIORITY_COUNT) &&
+ (lookup_option(&fqdn_universe, inpacket->options,
+ FQDN_ENCODED) != NULL))
+ priority_list[priority_len++] = DHO_FQDN;
+
+ /*
+ * Some DHCP Servers will give the subnet-mask option if
+ * it is not on the parameter request list - so some client
+ * implementations have come to rely on this - so we will
+ * also make sure we supply this, at lowest priority.
+ *
+ * This is only done in response to DHCPDISCOVER or
+ * DHCPREQUEST messages, to avoid providing the option on
+ * DHCPINFORM or DHCPLEASEQUERY responses (if the client
+ * didn't request it).
+ */
+ if ((priority_len < PRIORITY_COUNT) &&
+ ((inpacket->packet_type == DHCPDISCOVER) ||
+ (inpacket->packet_type == DHCPREQUEST)))
+ priority_list[priority_len++] = DHO_SUBNET_MASK;
+ } else {
+ /*
+ * First, hardcode some more options that ought to be
+ * sent first...these are high priority to have in the
+ * packet.
+ */
+ priority_list[priority_len++] = DHO_SUBNET_MASK;
+ priority_list[priority_len++] = DHO_ROUTERS;
+ priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
+ priority_list[priority_len++] = DHO_HOST_NAME;
+ priority_list[priority_len++] = DHO_FQDN;
+
+ /*
+ * Append a list of the standard DHCP options from the
+ * standard DHCP option space. Actually, if a site
+ * option space hasn't been specified, we wind up
+ * treating the dhcp option space as the site option
+ * space, and the first for loop is skipped, because
+ * it's slightly more general to do it this way,
+ * taking the 1Q99 DHCP futures work into account.
+ */
+ if (cfg_options->site_code_min) {
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ hash = cfg_options->universes[dhcp_universe.index];
+ if (hash) {
+ for (pp = hash[i]; pp; pp = pp->cdr) {
+ op = (struct option_cache *)(pp->car);
+ if (op->option->code <
+ cfg_options->site_code_min &&
+ priority_len < PRIORITY_COUNT &&
+ op->option->code != DHO_DHCP_AGENT_OPTIONS)
+ priority_list[priority_len++] =
+ op->option->code;
+ }
+ }
+ }
+ }
+
+ /*
+ * Now cycle through the site option space, or if there
+ * is no site option space, we'll be cycling through the
+ * dhcp option space.
+ */
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ hash = cfg_options->universes[cfg_options->site_universe];
+ if (hash != NULL)
+ for (pp = hash[i]; pp; pp = pp->cdr) {
+ op = (struct option_cache *)(pp->car);
+ if (op->option->code >=
+ cfg_options->site_code_min &&
+ priority_len < PRIORITY_COUNT &&
+ op->option->code != DHO_DHCP_AGENT_OPTIONS)
+ priority_list[priority_len++] =
+ op->option->code;
+ }
+ }
+
+ /*
+ * Put any spaces that are encapsulated on the list,
+ * sort out whether they contain values later.
+ */
+ for (i = 0; i < cfg_options->universe_count; i++) {
+ if (universes[i]->enc_opt &&
+ priority_len < PRIORITY_COUNT &&
+ universes[i]->enc_opt->universe == &dhcp_universe) {
+ if (universes[i]->enc_opt->code !=
+ DHO_DHCP_AGENT_OPTIONS)
+ priority_list[priority_len++] =
+ universes[i]->enc_opt->code;
+ }
+ }
+
+ /*
+ * The vendor option space can't stand on its own, so always
+ * add it to the list.
+ */
+ if (priority_len < PRIORITY_COUNT)
+ priority_list[priority_len++] =
+ DHO_VENDOR_ENCAPSULATED_OPTIONS;
+ }
+
+ /* Put the cookie up front... */
+ memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
+ index += 4;
+
+ /* Copy the options into the big buffer... */
+ option_size = store_options(&overload_used, buffer, index, mb_max,
+ inpacket, lease, client_state,
+ in_options, cfg_options, scope,
+ priority_list, priority_len,
+ of1, of2, terminate, vuname);
+
+ /* If store_options() failed */
+ if (option_size == 0)
+ return 0;
+
+ /* How much was stored in the main buffer? */
+ index += option_size;
+
+ /*
+ * If we're going to have to overload, store the overload
+ * option first.
+ */
+ if (overload_used) {
+ if (mb_size - agent_size - index < 3)
+ return 0;
+
+ buffer[index++] = DHO_DHCP_OPTION_OVERLOAD;
+ buffer[index++] = 1;
+ buffer[index++] = overload_used;
+
+ if (overload_used & 1)
+ memcpy(outpacket->file, &buffer[of1], DHCP_FILE_LEN);
+
+ if (overload_used & 2)
+ memcpy(outpacket->sname, &buffer[of2], DHCP_SNAME_LEN);
+ }
+
+ /* Now copy in preserved agent options, if any */
+ if (agent_size) {
+ if (mb_size - index >= agent_size) {
+ memcpy(&buffer[index], agentopts, agent_size);
+ index += agent_size;
+ } else
+ log_error("Unable to store relay agent information "
+ "in reply packet.");
+ }
+
+ /* Tack a DHO_END option onto the packet if we need to. */
+ if (index < mb_size)
+ buffer[index++] = DHO_END;
+
+ /* Copy main buffer into the options buffer of the packet */
+ memcpy(outpacket->options, buffer, index);
+
+ /* Figure out the length. */
+ length = DHCP_FIXED_NON_UDP + index;
+ return length;
+}
+
+/*
+ * XXX: We currently special case collecting VSIO options.
+ * We should be able to handle this in a more generic fashion, by
+ * including any encapsulated options that are present and desired.
+ * This will look something like the VSIO handling VSIO code.
+ * We may also consider handling the ORO-like options within
+ * encapsulated spaces.
+ */
+
+struct vsio_state {
+ char *buf;
+ int buflen;
+ int bufpos;
+};
+
+static void
+vsio_options(struct option_cache *oc,
+ struct packet *packet,
+ struct lease *dummy_lease,
+ struct client_state *dummy_client_state,
+ struct option_state *dummy_opt_state,
+ struct option_state *opt_state,
+ struct binding_scope **dummy_binding_scope,
+ struct universe *universe,
+ void *void_vsio_state) {
+ struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
+ struct data_string ds;
+ int total_len;
+
+ memset(&ds, 0, sizeof(ds));
+ if (evaluate_option_cache(&ds, packet, NULL,
+ NULL, opt_state, NULL,
+ &global_scope, oc, MDL)) {
+ total_len = ds.len + universe->tag_size + universe->length_size;
+ if (total_len <= (vs->buflen - vs->bufpos)) {
+ if (universe->tag_size == 1) {
+ vs->buf[vs->bufpos++] = oc->option->code;
+ } else if (universe->tag_size == 2) {
+ putUShort((unsigned char *)vs->buf+vs->bufpos,
+ oc->option->code);
+ vs->bufpos += 2;
+ } else if (universe->tag_size == 4) {
+ putULong((unsigned char *)vs->buf+vs->bufpos,
+ oc->option->code);
+ vs->bufpos += 4;
+ }
+ if (universe->length_size == 1) {
+ vs->buf[vs->bufpos++] = ds.len;
+ } else if (universe->length_size == 2) {
+ putUShort((unsigned char *)vs->buf+vs->bufpos,
+ ds.len);
+ vs->bufpos += 2;
+ } else if (universe->length_size == 4) {
+ putULong((unsigned char *)vs->buf+vs->bufpos,
+ ds.len);
+ vs->bufpos += 4;
+ }
+ memcpy(vs->buf + vs->bufpos, ds.data, ds.len);
+ vs->bufpos += ds.len;
+ } else {
+ log_debug("No space for option %d in VSIO space %s.",
+ oc->option->code, universe->name);
+ }
+ data_string_forget(&ds, MDL);
+ } else {
+ log_error("Error evaluating option %d in VSIO space %s.",
+ oc->option->code, universe->name);
+ }
+}
+
+/*
+ * Stores the options from the DHCPv6 universe into the buffer given.
+ *
+ * Required options are given as a 0-terminated list of option codes.
+ * Once those are added, the ORO is consulted.
+ */
+
+int
+store_options6(char *buf, int buflen,
+ struct option_state *opt_state,
+ struct packet *packet,
+ const int *required_opts,
+ struct data_string *oro) {
+ int i, j;
+ struct option_cache *oc;
+ struct option *o;
+ struct data_string ds;
+ int bufpos;
+ int oro_size;
+ u_int16_t code;
+ int in_required_opts;
+ int vsio_option_code;
+ int vsio_wanted;
+ struct vsio_state vs;
+ unsigned char *tmp;
+
+ bufpos = 0;
+ vsio_wanted = 0;
+
+ /*
+ * Find the option code for the VSIO universe.
+ */
+ vsio_option_code = 0;
+ o = vsio_universe.enc_opt;
+ while (o != NULL) {
+ if (o->universe == &dhcpv6_universe) {
+ vsio_option_code = o->code;
+ break;
+ }
+ o = o->universe->enc_opt;
+ }
+ if (vsio_option_code == 0) {
+ log_fatal("No VSIO option code found.");
+ }
+
+ if (required_opts != NULL) {
+ for (i=0; required_opts[i] != 0; i++) {
+ if (required_opts[i] == vsio_option_code) {
+ vsio_wanted = 1;
+ }
+
+ oc = lookup_option(&dhcpv6_universe,
+ opt_state, required_opts[i]);
+ if (oc == NULL) {
+ continue;
+ }
+ memset(&ds, 0, sizeof(ds));
+ for (; oc != NULL ; oc = oc->next) {
+ if (evaluate_option_cache(&ds, packet, NULL,
+ NULL, opt_state,
+ NULL, &global_scope,
+ oc, MDL)) {
+ if ((ds.len + 4) <=
+ (buflen - bufpos)) {
+ tmp = (unsigned char *)buf;
+ tmp += bufpos;
+ /* option tag */
+ putUShort(tmp,
+ required_opts[i]);
+ /* option length */
+ putUShort(tmp+2, ds.len);
+ /* option data */
+ memcpy(tmp+4, ds.data, ds.len);
+ /* update position */
+ bufpos += (4 + ds.len);
+ } else {
+ log_debug("No space for "
+ "option %d",
+ required_opts[i]);
+ }
+ data_string_forget(&ds, MDL);
+ } else {
+ log_error("Error evaluating option %d",
+ required_opts[i]);
+ }
+ }
+ }
+ }
+
+ if (oro == NULL) {
+ oro_size = 0;
+ } else {
+ oro_size = oro->len / 2;
+ }
+ for (i=0; i<oro_size; i++) {
+ memcpy(&code, oro->data+(i*2), 2);
+ code = ntohs(code);
+
+ /*
+ * See if we've already included this option because
+ * it is required.
+ */
+ in_required_opts = 0;
+ if (required_opts != NULL) {
+ for (j=0; required_opts[j] != 0; j++) {
+ if (required_opts[j] == code) {
+ in_required_opts = 1;
+ break;
+ }
+ }
+ }
+ if (in_required_opts) {
+ continue;
+ }
+
+ /*
+ * See if this is the VSIO option.
+ */
+ if (code == vsio_option_code) {
+ vsio_wanted = 1;
+ }
+
+ /*
+ * Not already added, find this option.
+ */
+ oc = lookup_option(&dhcpv6_universe, opt_state, code);
+ memset(&ds, 0, sizeof(ds));
+ for (; oc != NULL ; oc = oc->next) {
+ if (evaluate_option_cache(&ds, packet, NULL, NULL,
+ opt_state, NULL,
+ &global_scope, oc, MDL)) {
+ if ((ds.len + 4) <= (buflen - bufpos)) {
+ tmp = (unsigned char *)buf + bufpos;
+ /* option tag */
+ putUShort(tmp, code);
+ /* option length */
+ putUShort(tmp+2, ds.len);
+ /* option data */
+ memcpy(tmp+4, ds.data, ds.len);
+ /* update position */
+ bufpos += (4 + ds.len);
+ } else {
+ log_debug("No space for option %d",
+ code);
+ }
+ data_string_forget(&ds, MDL);
+ } else {
+ log_error("Error evaluating option %d", code);
+ }
+ }
+ }
+
+ if (vsio_wanted) {
+ for (i=0; i < opt_state->universe_count; i++) {
+ if (opt_state->universes[i] != NULL) {
+ o = universes[i]->enc_opt;
+ if ((o != NULL) &&
+ (o->universe == &vsio_universe)) {
+ /*
+ * Add the data from this VSIO option.
+ */
+ vs.buf = buf;
+ vs.buflen = buflen;
+ vs.bufpos = bufpos+8;
+ option_space_foreach(packet, NULL,
+ NULL,
+ NULL, opt_state,
+ NULL,
+ universes[i],
+ (void *)&vs,
+ vsio_options);
+
+ /*
+ * If there was actually data here,
+ * add the "header".
+ */
+ if (vs.bufpos > bufpos+8) {
+ tmp = (unsigned char *)buf +
+ bufpos;
+ putUShort(tmp,
+ vsio_option_code);
+ putUShort(tmp+2,
+ vs.bufpos-bufpos-4);
+ putULong(tmp+4, o->code);
+
+ bufpos = vs.bufpos;
+ }
+ }
+ }
+ }
+ }
+
+ return bufpos;
+}
+
+/*
+ * Store all the requested options into the requested buffer.
+ * XXX: ought to be static
+ */
+int
+store_options(int *ocount,
+ unsigned char *buffer, unsigned index, unsigned buflen,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ unsigned *priority_list, int priority_len,
+ unsigned first_cutoff, int second_cutoff, int terminate,
+ const char *vuname)
+{
+ int bufix = 0, six = 0, tix = 0;
+ int i;
+ int ix;
+ int tto;
+ int bufend, sbufend;
+ struct data_string od;
+ struct option_cache *oc;
+ struct option *option = NULL;
+ unsigned code;
+
+ /*
+ * These arguments are relative to the start of the buffer, so
+ * reduce them by the current buffer index, and advance the
+ * buffer pointer to where we're going to start writing.
+ */
+ buffer = &buffer[index];
+ buflen -= index;
+ if (first_cutoff)
+ first_cutoff -= index;
+ if (second_cutoff)
+ second_cutoff -= index;
+
+ /* Calculate the start and end of each section of the buffer */
+ bufend = sbufend = buflen;
+ if (first_cutoff) {
+ if (first_cutoff >= buflen)
+ log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL);
+ bufend = first_cutoff;
+
+ if (second_cutoff) {
+ if (second_cutoff >= buflen)
+ log_fatal("%s:%d:store_options: Invalid second cutoff.",
+ MDL);
+ sbufend = second_cutoff;
+ }
+ } else if (second_cutoff) {
+ if (second_cutoff >= buflen)
+ log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL);
+ bufend = second_cutoff;
+ }
+
+ memset (&od, 0, sizeof od);
+
+ /* Eliminate duplicate options from the parameter request list.
+ * Enforce RFC-mandated ordering of options that are present.
+ */
+ for (i = 0; i < priority_len - 1; i++) {
+ /* Eliminate duplicates. */
+ tto = 0;
+ for (ix = i + 1; ix < priority_len + tto; ix++) {
+ if (tto)
+ priority_list [ix - tto] =
+ priority_list [ix];
+ if (priority_list [i] == priority_list [ix]) {
+ tto++;
+ priority_len--;
+ }
+ }
+
+ /* Enforce ordering of SUBNET_MASK options, according to
+ * RFC2132 Section 3.3:
+ *
+ * If both the subnet mask and the router option are
+ * specified in a DHCP reply, the subnet mask option MUST
+ * be first.
+ *
+ * This guidance does not specify what to do if the client
+ * PRL explicitly requests the options out of order, it is
+ * a general statement.
+ */
+ if (priority_list[i] == DHO_SUBNET_MASK) {
+ for (ix = i - 1 ; ix >= 0 ; ix--) {
+ if (priority_list[ix] == DHO_ROUTERS) {
+ /* swap */
+ priority_list[ix] = DHO_SUBNET_MASK;
+ priority_list[i] = DHO_ROUTERS;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Copy out the options in the order that they appear in the
+ priority list... */
+ for (i = 0; i < priority_len; i++) {
+ /* Number of bytes left to store (some may already
+ have been stored by a previous pass). */
+ unsigned length;
+ int optstart, soptstart, toptstart;
+ struct universe *u;
+ int have_encapsulation = 0;
+ struct data_string encapsulation;
+ int splitup;
+
+ memset (&encapsulation, 0, sizeof encapsulation);
+ have_encapsulation = 0;
+
+ if (option != NULL)
+ option_dereference(&option, MDL);
+
+ /* Code for next option to try to store. */
+ code = priority_list [i];
+
+ /* Look up the option in the site option space if the code
+ is above the cutoff, otherwise in the DHCP option space. */
+ if (code >= cfg_options -> site_code_min)
+ u = universes [cfg_options -> site_universe];
+ else
+ u = &dhcp_universe;
+
+ oc = lookup_option (u, cfg_options, code);
+
+ if (oc && oc->option)
+ option_reference(&option, oc->option, MDL);
+ else
+ option_code_hash_lookup(&option, u->code_hash, &code, 0, MDL);
+
+ /* If it's a straight encapsulation, and the user supplied a
+ * value for the entire option, use that. Otherwise, search
+ * the encapsulated space.
+ *
+ * If it's a limited encapsulation with preceding data, and the
+ * user supplied values for the preceding bytes, search the
+ * encapsulated space.
+ */
+ if ((option != NULL) &&
+ (((oc == NULL) && (option->format[0] == 'E')) ||
+ ((oc != NULL) && (option->format[0] == 'e')))) {
+ static char *s, *t;
+ struct option_cache *tmp;
+ struct data_string name;
+
+ s = strchr (option->format, 'E');
+ if (s)
+ t = strchr (++s, '.');
+ if (s && t) {
+ memset (&name, 0, sizeof name);
+
+ /* A zero-length universe name means the vendor
+ option space, if one is defined. */
+ if (t == s) {
+ if (vendor_cfg_option) {
+ tmp = lookup_option (vendor_cfg_option -> universe,
+ cfg_options,
+ vendor_cfg_option -> code);
+ if (tmp)
+ evaluate_option_cache (&name, packet, lease,
+ client_state,
+ in_options,
+ cfg_options,
+ scope, tmp, MDL);
+ } else if (vuname) {
+ name.data = (unsigned char *)s;
+ name.len = strlen (s);
+ }
+ } else {
+ name.data = (unsigned char *)s;
+ name.len = t - s;
+ }
+
+ /* If we found a universe, and there are options configured
+ for that universe, try to encapsulate it. */
+ if (name.len) {
+ have_encapsulation =
+ (option_space_encapsulate
+ (&encapsulation, packet, lease, client_state,
+ in_options, cfg_options, scope, &name));
+ data_string_forget (&name, MDL);
+ }
+ }
+ }
+
+ /* In order to avoid memory leaks, we have to get to here
+ with any option cache that we allocated in tmp not being
+ referenced by tmp, and whatever option cache is referenced
+ by oc being an actual reference. lookup_option doesn't
+ generate a reference (this needs to be fixed), so the
+ preceding goop ensures that if we *didn't* generate a new
+ option cache, oc still winds up holding an actual reference. */
+
+ /* If no data is available for this option, skip it. */
+ if (!oc && !have_encapsulation) {
+ continue;
+ }
+
+ /* Find the value of the option... */
+ od.len = 0;
+ if (oc) {
+ evaluate_option_cache (&od, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, MDL);
+
+ /* If we have encapsulation for this option, and an oc
+ * lookup succeeded, but the evaluation failed, it is
+ * either because this is a complex atom (atoms before
+ * E on format list) and the top half of the option is
+ * not configured, or this is a simple encapsulated
+ * space and the evaluator is giving us a NULL. Prefer
+ * the evaluator's opinion over the subspace.
+ */
+ if (!od.len) {
+ data_string_forget (&encapsulation, MDL);
+ data_string_forget (&od, MDL);
+ continue;
+ }
+ }
+
+ /* We should now have a constant length for the option. */
+ length = od.len;
+ if (have_encapsulation) {
+ length += encapsulation.len;
+
+ /* od.len can be nonzero if we got here without an
+ * oc (cache lookup failed), but did have an encapsulated
+ * simple encapsulation space.
+ */
+ if (!od.len) {
+ data_string_copy (&od, &encapsulation, MDL);
+ data_string_forget (&encapsulation, MDL);
+ } else {
+ struct buffer *bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, length, MDL)) {
+ option_cache_dereference (&oc, MDL);
+ data_string_forget (&od, MDL);
+ data_string_forget (&encapsulation, MDL);
+ continue;
+ }
+ memcpy (&bp -> data [0], od.data, od.len);
+ memcpy (&bp -> data [od.len], encapsulation.data,
+ encapsulation.len);
+ data_string_forget (&od, MDL);
+ data_string_forget (&encapsulation, MDL);
+ od.data = &bp -> data [0];
+ buffer_reference (&od.buffer, bp, MDL);
+ buffer_dereference (&bp, MDL);
+ od.len = length;
+ od.terminated = 0;
+ }
+ }
+
+ /* Do we add a NUL? */
+ if (terminate && option && format_has_text(option->format)) {
+ 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. */
+
+ if (length > 255)
+ splitup = 1;
+ else
+ splitup = 0;
+
+ ix = 0;
+ optstart = bufix;
+ soptstart = six;
+ toptstart = tix;
+ while (length) {
+ unsigned incr = length;
+ int *pix;
+ unsigned char *base;
+
+ /* Try to fit it in the options buffer. */
+ if (!splitup &&
+ ((!six && !tix && (i == priority_len - 1) &&
+ (bufix + 2 + length < bufend)) ||
+ (bufix + 5 + length < bufend))) {
+ base = buffer;
+ pix = &bufix;
+ /* Try to fit it in the second buffer. */
+ } else if (!splitup && first_cutoff &&
+ (first_cutoff + six + 3 + length < sbufend)) {
+ base = &buffer[first_cutoff];
+ pix = &six;
+ /* Try to fit it in the third buffer. */
+ } else if (!splitup && second_cutoff &&
+ (second_cutoff + tix + 3 + length < buflen)) {
+ base = &buffer[second_cutoff];
+ pix = &tix;
+ /* Split the option up into the remaining space. */
+ } else {
+ splitup = 1;
+
+ /* Use any remaining options space. */
+ if (bufix + 6 < bufend) {
+ incr = bufend - bufix - 5;
+ base = buffer;
+ pix = &bufix;
+ /* Use any remaining first_cutoff space. */
+ } else if (first_cutoff &&
+ (first_cutoff + six + 4 < sbufend)) {
+ incr = sbufend - (first_cutoff + six) - 3;
+ base = &buffer[first_cutoff];
+ pix = &six;
+ /* Use any remaining second_cutoff space. */
+ } else if (second_cutoff &&
+ (second_cutoff + tix + 4 < buflen)) {
+ incr = buflen - (second_cutoff + tix) - 3;
+ base = &buffer[second_cutoff];
+ pix = &tix;
+ /* Give up, roll back this option. */
+ } else {
+ bufix = optstart;
+ six = soptstart;
+ tix = toptstart;
+ break;
+ }
+ }
+
+ if (incr > length)
+ incr = length;
+ if (incr > 255)
+ incr = 255;
+
+ /* Everything looks good - copy it in! */
+ base [*pix] = code;
+ base [*pix + 1] = (unsigned char)incr;
+ if (tto && incr == length) {
+ if (incr > 1)
+ memcpy (base + *pix + 2,
+ od.data + ix, (unsigned)(incr - 1));
+ base [*pix + 2 + incr - 1] = 0;
+ } else {
+ memcpy (base + *pix + 2,
+ od.data + ix, (unsigned)incr);
+ }
+ length -= incr;
+ ix += incr;
+ *pix += 2 + incr;
+ }
+ data_string_forget (&od, MDL);
+ }
+
+ if (option != NULL)
+ option_dereference(&option, MDL);
+
+ /* If we can overload, and we have, then PAD and END those spaces. */
+ if (first_cutoff && six) {
+ if ((first_cutoff + six + 1) < sbufend)
+ memset (&buffer[first_cutoff + six + 1], DHO_PAD,
+ sbufend - (first_cutoff + six + 1));
+ else if (first_cutoff + six >= sbufend)
+ log_fatal("Second buffer overflow in overloaded options.");
+
+ buffer[first_cutoff + six] = DHO_END;
+ if (ocount != NULL)
+ *ocount |= 1; /* So that caller knows there's data there. */
+ }
+
+ if (second_cutoff && tix) {
+ if (second_cutoff + tix + 1 < buflen) {
+ memset (&buffer[second_cutoff + tix + 1], DHO_PAD,
+ buflen - (second_cutoff + tix + 1));
+ } else if (second_cutoff + tix >= buflen)
+ log_fatal("Third buffer overflow in overloaded options.");
+
+ buffer[second_cutoff + tix] = DHO_END;
+ if (ocount != NULL)
+ *ocount |= 2; /* So that caller knows there's data there. */
+ }
+
+ if ((six || tix) && (bufix + 3 > bufend))
+ log_fatal("Not enough space for option overload option.");
+
+ return bufix;
+}
+
+/* Return true if the format string has a variable length text option
+ * ("t"), return false otherwise.
+ */
+
+int
+format_has_text(format)
+ const char *format;
+{
+ const char *p;
+
+ p = format;
+ while (*p != '\0') {
+ switch (*p++) {
+ case 'd':
+ case 't':
+ return 1;
+
+ /* These symbols are arbitrary, not fixed or
+ * determinable length...text options with them is
+ * invalid (whatever the case, they are never NULL
+ * terminated).
+ */
+ case 'A':
+ case 'a':
+ case 'X':
+ case 'x':
+ case 'D':
+ return 0;
+
+ case 'c':
+ /* 'c' only follows 'D' atoms, and indicates that
+ * compression may be used. If there was a 'D'
+ * atom already, we would have returned. So this
+ * is an error, but continue looking for 't' anyway.
+ */
+ log_error("format_has_text(%s): 'c' atoms are illegal "
+ "except after 'D' atoms.", format);
+ break;
+
+ /* 'E' is variable length, but not arbitrary...you
+ * can find its length if you can find an END option.
+ * N is (n)-byte in length but trails a name of a
+ * space defining the enumeration values. So treat
+ * both the same - valid, fixed-length fields.
+ */
+ case 'E':
+ case 'N':
+ /* Consume the space name. */
+ while ((*p != '\0') && (*p++ != '.'))
+ ;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Determine the minimum length of a DHCP option prior to any variable
+ * or inconsistent length formats, according to its configured format
+ * variable (and possibly from supplied option cache contents for variable
+ * length format symbols).
+ */
+
+int
+format_min_length(format, oc)
+ const char *format;
+ struct option_cache *oc;
+{
+ const char *p, *name;
+ int min_len = 0;
+ int last_size = 0;
+ struct enumeration *espace;
+
+ p = format;
+ while (*p != '\0') {
+ switch (*p++) {
+ case '6': /* IPv6 Address */
+ min_len += 16;
+ last_size = 16;
+ break;
+
+ case 'I': /* IPv4 Address */
+ case 'l': /* int32_t */
+ case 'L': /* uint32_t */
+ case 'T': /* Lease Time, uint32_t equivalent */
+ min_len += 4;
+ last_size = 4;
+ break;
+
+ case 's': /* int16_t */
+ case 'S': /* uint16_t */
+ min_len += 2;
+ last_size = 2;
+ break;
+
+ case 'N': /* Enumeration value. */
+ /* Consume space name. */
+ name = p;
+ p = strchr(p, '.');
+ if (p == NULL)
+ log_fatal("Corrupt format: %s", format);
+
+ espace = find_enumeration(name, p - name);
+ if (espace == NULL) {
+ log_error("Unknown enumeration: %s", format);
+ /* Max is safest value to return. */
+ return INT_MAX;
+ }
+
+ min_len += espace->width;
+ last_size = espace->width;
+ p++;
+
+ break;
+
+ case 'b': /* int8_t */
+ case 'B': /* uint8_t */
+ case 'F': /* Flag that is always true. */
+ case 'f': /* Flag */
+ min_len++;
+ last_size = 1;
+ break;
+
+ case 'o': /* Last argument is optional. */
+ min_len -= last_size;
+
+ /* XXX: It MAY be possible to sense the end of an
+ * encapsulated space, but right now this is too
+ * hard to support. Return a safe value.
+ */
+ case 'e': /* Encapsulation hint (there is an 'E' later). */
+ case 'E': /* Encapsulated options. */
+ return min_len;
+
+ case 'd': /* "Domain name" */
+ case 'D': /* "rfc1035 formatted names" */
+ case 't': /* "ASCII Text" */
+ case 'X': /* "ASCII or Hex Conditional */
+ case 'x': /* "Hex" */
+ case 'A': /* Array of all that precedes. */
+ case 'a': /* Array of preceding symbol. */
+ case 'Z': /* nothing. */
+ return min_len;
+
+ case 'c': /* Compress flag for D atom. */
+ log_error("format_min_length(%s): 'c' atom is illegal "
+ "except after 'D' atom.", format);
+ return INT_MAX;
+
+ default:
+ /* No safe value is known. */
+ log_error("format_min_length(%s): No safe value "
+ "for unknown format symbols.", format);
+ return INT_MAX;
+ }
+ }
+
+ return min_len;
+}
+
+
+/* Format the specified option so that a human can easily read it. */
+
+const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
+ struct option *option;
+ const unsigned char *data;
+ unsigned len;
+ int emit_commas;
+ int emit_quotes;
+{
+ static char optbuf [32768]; /* XXX */
+ static char *endbuf = &optbuf[sizeof(optbuf)];
+ int hunksize = 0;
+ int opthunk = 0;
+ int hunkinc = 0;
+ int numhunk = -1;
+ int numelem = 0;
+ int count;
+ int i, j, k, l;
+ char fmtbuf[32] = "";
+ struct iaddr iaddr;
+ struct enumeration *enumbuf[32]; /* MUST be same as fmtbuf */
+ char *op = optbuf;
+ const unsigned char *dp = data;
+ char comma;
+ unsigned long tval;
+
+ if (emit_commas)
+ comma = ',';
+ else
+ comma = ' ';
+
+ memset (enumbuf, 0, sizeof enumbuf);
+
+ /* Figure out the size of the data. */
+ for (l = i = 0; option -> format [i]; i++, l++) {
+ if (l >= sizeof(fmtbuf) - 1)
+ log_fatal("Bounds failure on internal buffer at "
+ "%s:%d", MDL);
+
+ if (!numhunk) {
+ log_error ("%s: Extra codes in format string: %s",
+ option -> name,
+ &(option -> format [i]));
+ break;
+ }
+ numelem++;
+ fmtbuf [l] = option -> format [i];
+ switch (option -> format [i]) {
+ case 'a':
+ case 'A':
+ --numelem;
+ fmtbuf [l] = 0;
+ numhunk = 0;
+ break;
+ case 'E':
+ /* Skip the universe name. */
+ while (option -> format [i] &&
+ option -> format [i] != '.')
+ i++;
+ /* Fall Through! */
+ case 'X':
+ for (k = 0; k < len; k++) {
+ if (!isascii (data [k]) ||
+ !isprint (data [k]))
+ break;
+ }
+ /* If we found no bogus characters, or the bogus
+ character we found is a trailing NUL, it's
+ okay to print this option as text. */
+ if (k == len || (k + 1 == len && data [k] == 0)) {
+ fmtbuf [l] = 't';
+ numhunk = -2;
+ } else {
+ fmtbuf [l] = 'x';
+ hunksize++;
+ comma = ':';
+ numhunk = 0;
+ }
+ fmtbuf [l + 1] = 0;
+ break;
+ case 'c':
+ /* The 'c' atom is a 'D' modifier only. */
+ log_error("'c' atom not following D atom in format "
+ "string: %s", option->format);
+ break;
+ case 'D':
+ /*
+ * Skip the 'c' atom, if present. It does not affect
+ * how we convert wire->text format (if compression is
+ * present either way, we still process it).
+ */
+ if (option->format[i+1] == 'c')
+ i++;
+ fmtbuf[l + 1] = 0;
+ numhunk = -2;
+ break;
+ case 'd':
+ fmtbuf[l] = 't';
+ /* Fall Through ! */
+ case 't':
+ fmtbuf[l + 1] = 0;
+ numhunk = -2;
+ break;
+ case 'N':
+ k = i;
+ while (option -> format [i] &&
+ option -> format [i] != '.')
+ i++;
+ enumbuf [l] =
+ find_enumeration (&option -> format [k] + 1,
+ i - k - 1);
+ if (enumbuf[l] == NULL) {
+ hunksize += 1;
+ hunkinc = 1;
+ } else {
+ hunksize += enumbuf[l]->width;
+ hunkinc = enumbuf[l]->width;
+ }
+ break;
+ case '6':
+ hunksize += 16;
+ hunkinc = 16;
+ break;
+ case 'I':
+ case 'l':
+ case 'L':
+ case 'T':
+ hunksize += 4;
+ hunkinc = 4;
+ break;
+ case 's':
+ case 'S':
+ hunksize += 2;
+ hunkinc = 2;
+ break;
+ case 'b':
+ case 'B':
+ case 'f':
+ case 'F':
+ hunksize++;
+ hunkinc = 1;
+ break;
+ case 'e':
+ case 'Z':
+ break;
+ case 'o':
+ opthunk += hunkinc;
+ break;
+ default:
+ log_error ("%s: garbage in format string: %s",
+ option -> name,
+ &(option -> format [i]));
+ break;
+ }
+ }
+
+ /* Check for too few bytes... */
+ if (hunksize - opthunk > len) {
+ log_error ("%s: expecting at least %d bytes; got %d",
+ option -> name,
+ hunksize, len);
+ return "<error>";
+ }
+ /* Check for too many bytes... */
+ if (numhunk == -1 && hunksize < len)
+ log_error ("%s: %d extra bytes",
+ option -> 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)
+ log_error ("%s: %d extra bytes at end of array\n",
+ option -> 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++) {
+ switch (fmtbuf [j]) {
+ case 't':
+ /* endbuf-1 leaves room for NULL. */
+ k = pretty_text(&op, endbuf - 1, &dp,
+ data + len, emit_quotes);
+ if (k == -1) {
+ log_error("Error printing text.");
+ break;
+ }
+ *op = 0;
+ break;
+ case 'D': /* RFC1035 format name list */
+ for( ; dp < (data + len) ; dp += k) {
+ unsigned char nbuff[NS_MAXCDNAME];
+ const unsigned char *nbp, *nend;
+
+ nend = &nbuff[sizeof(nbuff)];
+
+ /* If this is for ISC DHCP consumption
+ * (emit_quotes), lay it out as a list
+ * of STRING tokens. Otherwise, it is
+ * a space-separated list of DNS-
+ * escaped names as /etc/resolv.conf
+ * might digest.
+ */
+ if (dp != data) {
+ if (op + 2 > endbuf)
+ break;
+
+ if (emit_quotes)
+ *op++ = ',';
+ *op++ = ' ';
+ }
+
+ /* XXX: if fmtbuf[j+1] != 'c', we
+ * should warn if the data was
+ * compressed anyway.
+ */
+ k = MRns_name_unpack(data,
+ data + len,
+ dp, nbuff,
+ sizeof(nbuff));
+
+ if (k == -1) {
+ log_error("Invalid domain "
+ "list.");
+ break;
+ }
+
+ /* If emit_quotes, then use ISC DHCP
+ * escapes. Otherwise, rely only on
+ * ns_name_ntop().
+ */
+ if (emit_quotes) {
+ nbp = nbuff;
+ pretty_domain(&op, endbuf-1,
+ &nbp, nend);
+ } else {
+ /* ns_name_ntop() includes
+ * a trailing NUL in its
+ * count.
+ */
+ count = MRns_name_ntop(
+ nbuff, op,
+ (endbuf-op)-1);
+
+ if (count <= 0) {
+ log_error("Invalid "
+ "domain name.");
+ break;
+ }
+
+ /* Consume all but the trailing
+ * NUL.
+ */
+ op += count - 1;
+
+ /* Replace the trailing NUL
+ * with the implicit root
+ * (in the unlikely event the
+ * domain name /is/ the root).
+ */
+ *op++ = '.';
+ }
+ }
+ *op = '\0';
+ break;
+ /* pretty-printing an array of enums is
+ going to get ugly. */
+ case 'N':
+ if (!enumbuf [j]) {
+ tval = *dp++;
+ goto enum_as_num;
+ }
+
+ switch (enumbuf[j]->width) {
+ case 1:
+ tval = getUChar(dp);
+ break;
+
+ case 2:
+ tval = getUShort(dp);
+ break;
+
+ case 4:
+ tval = getULong(dp);
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d.",
+ MDL);
+ return "<double impossible condition>";
+ }
+
+ for (i = 0; ;i++) {
+ if (!enumbuf [j] -> values [i].name)
+ goto enum_as_num;
+ if (enumbuf [j] -> values [i].value ==
+ tval)
+ break;
+ }
+ strcpy (op, enumbuf [j] -> values [i].name);
+ dp += enumbuf[j]->width;
+ break;
+
+ enum_as_num:
+ sprintf(op, "%lu", tval);
+ break;
+
+ case 'I':
+ iaddr.len = 4;
+ memcpy(iaddr.iabuf, dp, 4);
+ strcpy(op, piaddr(iaddr));
+ dp += 4;
+ break;
+ case '6':
+ iaddr.len = 16;
+ memcpy(iaddr.iabuf, dp, 16);
+ strcpy(op, piaddr(iaddr));
+ dp += 16;
+ break;
+ case 'l':
+ sprintf (op, "%ld", (long)getLong (dp));
+ dp += 4;
+ break;
+ case 'T':
+ tval = getULong (dp);
+ if (tval == -1)
+ sprintf (op, "%s", "infinite");
+ else
+ sprintf(op, "%lu", tval);
+ break;
+ case 'L':
+ sprintf(op, "%lu",
+ (unsigned long)getULong(dp));
+ dp += 4;
+ break;
+ case 's':
+ sprintf (op, "%d", (int)getShort (dp));
+ dp += 2;
+ break;
+ case 'S':
+ sprintf(op, "%u", (unsigned)getUShort(dp));
+ dp += 2;
+ break;
+ case 'b':
+ sprintf (op, "%d", *(const char *)dp++);
+ break;
+ case 'B':
+ sprintf (op, "%d", *dp++);
+ break;
+ case 'X':
+ case 'x':
+ sprintf (op, "%x", *dp++);
+ break;
+ case 'f':
+ strcpy (op, *dp++ ? "true" : "false");
+ break;
+ case 'F':
+ strcpy (op, "true");
+ break;
+ case 'e':
+ case 'Z':
+ *op = '\0';
+ break;
+ default:
+ log_error ("Unexpected format code %c",
+ fmtbuf [j]);
+ }
+ op += strlen (op);
+ if (dp == data + len)
+ break;
+ if (j + 1 < numelem && comma != ':')
+ *op++ = ' ';
+ }
+ if (i + 1 < numhunk) {
+ *op++ = comma;
+ }
+ if (dp == data + len)
+ break;
+ }
+ return optbuf;
+}
+
+int get_option (result, universe, packet, lease, client_state,
+ in_options, cfg_options, options, scope, code, file, line)
+ struct data_string *result;
+ struct universe *universe;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct option_state *options;
+ struct binding_scope **scope;
+ unsigned code;
+ const char *file;
+ int line;
+{
+ struct option_cache *oc;
+
+ if (!universe -> lookup_func)
+ return 0;
+ oc = ((*universe -> lookup_func) (universe, options, code));
+ if (!oc)
+ return 0;
+ if (!evaluate_option_cache (result, packet, lease, client_state,
+ in_options, cfg_options, scope, oc,
+ file, line))
+ return 0;
+ return 1;
+}
+
+void set_option (universe, options, option, op)
+ struct universe *universe;
+ struct option_state *options;
+ struct option_cache *option;
+ enum statement_op op;
+{
+ struct option_cache *oc, *noc;
+
+ switch (op) {
+ case if_statement:
+ case add_statement:
+ case eval_statement:
+ case break_statement:
+ default:
+ log_error ("bogus statement type in set_option.");
+ break;
+
+ case default_option_statement:
+ oc = lookup_option (universe, options,
+ option -> option -> code);
+ if (oc)
+ break;
+ save_option (universe, options, option);
+ break;
+
+ case supersede_option_statement:
+ case send_option_statement:
+ /* Install the option, replacing any existing version. */
+ save_option (universe, options, option);
+ break;
+
+ case append_option_statement:
+ case prepend_option_statement:
+ oc = lookup_option (universe, options,
+ option -> option -> code);
+ if (!oc) {
+ save_option (universe, options, option);
+ break;
+ }
+ /* If it's not an expression, make it into one. */
+ if (!oc -> expression && oc -> data.len) {
+ if (!expression_allocate (&oc -> expression, MDL)) {
+ log_error ("Can't allocate const expression.");
+ break;
+ }
+ oc -> expression -> op = expr_const_data;
+ data_string_copy
+ (&oc -> expression -> data.const_data,
+ &oc -> data, MDL);
+ data_string_forget (&oc -> data, MDL);
+ }
+ noc = (struct option_cache *)0;
+ if (!option_cache_allocate (&noc, MDL))
+ break;
+ if (op == append_option_statement) {
+ if (!make_concat (&noc -> expression,
+ oc -> expression,
+ option -> expression)) {
+ option_cache_dereference (&noc, MDL);
+ break;
+ }
+ } else {
+ if (!make_concat (&noc -> expression,
+ option -> expression,
+ oc -> expression)) {
+ option_cache_dereference (&noc, MDL);
+ break;
+ }
+ }
+ option_reference(&(noc->option), oc->option, MDL);
+ save_option (universe, options, noc);
+ option_cache_dereference (&noc, MDL);
+ break;
+ }
+}
+
+struct option_cache *lookup_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ unsigned code;
+{
+ if (!options)
+ return (struct option_cache *)0;
+ if (universe -> lookup_func)
+ return (*universe -> lookup_func) (universe, options, code);
+ else
+ log_error ("can't look up options in %s space.",
+ universe -> name);
+ return (struct option_cache *)0;
+}
+
+struct option_cache *lookup_hashed_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ unsigned code;
+{
+ int hashix;
+ pair bptr;
+ pair *hash;
+
+ /* Make sure there's a hash table. */
+ if (universe -> index >= options -> universe_count ||
+ !(options -> universes [universe -> index]))
+ return (struct option_cache *)0;
+
+ hash = options -> universes [universe -> index];
+
+ hashix = compute_option_hash (code);
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)(bptr -> car)) -> option -> code ==
+ code)
+ return (struct option_cache *)(bptr -> car);
+ }
+ return (struct option_cache *)0;
+}
+
+/* Save a specified buffer into an option cache. */
+int
+save_option_buffer(struct universe *universe, struct option_state *options,
+ struct buffer *bp, unsigned char *buffer, unsigned length,
+ unsigned code, int terminatep)
+{
+ struct option_cache *op = NULL;
+ int status = 1;
+
+ status = prepare_option_buffer(universe, bp, buffer, length, code,
+ terminatep, &op);
+
+ if (status == 0)
+ goto cleanup;
+
+ save_option(universe, options, op);
+
+ cleanup:
+ if (op != NULL)
+ option_cache_dereference(&op, MDL);
+
+ return status;
+}
+
+/* Append a specified buffer onto the tail of an option cache. */
+int
+append_option_buffer(struct universe *universe, struct option_state *options,
+ struct buffer *bp, unsigned char *buffer, unsigned length,
+ unsigned code, int terminatep)
+{
+ struct option_cache *op = NULL;
+ int status = 1;
+
+ status = prepare_option_buffer(universe, bp, buffer, length, code,
+ terminatep, &op);
+
+ if (status == 0)
+ goto cleanup;
+
+ also_save_option(universe, options, op);
+
+ cleanup:
+ if (op != NULL)
+ option_cache_dereference(&op, MDL);
+
+ return status;
+}
+
+/* Create/copy a buffer into a new option cache. */
+static int
+prepare_option_buffer(struct universe *universe, struct buffer *bp,
+ unsigned char *buffer, unsigned length, unsigned code,
+ int terminatep, struct option_cache **opp)
+{
+ struct buffer *lbp = NULL;
+ struct option *option = NULL;
+ struct option_cache *op;
+ int status = 1;
+
+ /* Code sizes of 8, 16, and 32 bits are allowed. */
+ switch(universe->tag_size) {
+ case 1:
+ if (code > 0xff)
+ return 0;
+ break;
+ case 2:
+ if (code > 0xffff)
+ return 0;
+ break;
+ case 4:
+ if (code > 0xffffffff)
+ return 0;
+ break;
+
+ default:
+ log_fatal("Inconsistent universe tag size at %s:%d.", MDL);
+ }
+
+ option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL);
+
+ /* If we created an option structure for each option a client
+ * supplied, it's possible we may create > 2^32 option structures.
+ * That's not feasible. So by failing to enter these option
+ * structures into the code and name hash tables, references will
+ * never be more than 1 - when the option cache is destroyed, this
+ * will be cleaned up.
+ */
+ if (!option) {
+ char nbuf[sizeof("unknown-4294967295")];
+
+ sprintf(nbuf, "unknown-%u", code);
+
+ option = new_option(nbuf, MDL);
+
+ if (!option)
+ return 0;
+
+ option->format = default_option_format;
+ option->universe = universe;
+ option->code = code;
+
+ /* new_option() doesn't set references, pretend. */
+ option->refcnt = 1;
+ }
+
+ if (!option_cache_allocate (opp, MDL)) {
+ log_error("No memory for option code %s.%s.",
+ universe->name, option->name);
+ status = 0;
+ goto cleanup;
+ }
+
+ /* Pointer rather than double pointer makes for less parens. */
+ op = *opp;
+
+ option_reference(&op->option, option, MDL);
+
+ /* If we weren't passed a buffer in which the data are saved and
+ refcounted, allocate one now. */
+ if (!bp) {
+ if (!buffer_allocate (&lbp, length + terminatep, MDL)) {
+ log_error ("no memory for option buffer.");
+
+ status = 0;
+ goto cleanup;
+ }
+ memcpy (lbp -> data, buffer, length + terminatep);
+ bp = lbp;
+ buffer = &bp -> data [0]; /* Refer to saved buffer. */
+ }
+
+ /* Reference buffer copy to option cache. */
+ op -> data.buffer = (struct buffer *)0;
+ buffer_reference (&op -> data.buffer, bp, MDL);
+
+ /* Point option cache into buffer. */
+ op -> data.data = buffer;
+ op -> data.len = length;
+
+ if (terminatep) {
+ /* NUL terminate (we can get away with this because we (or
+ the caller!) allocated one more than the buffer size, and
+ because the byte following the end of an option is always
+ the code of the next option, which the caller is getting
+ out of the *original* buffer. */
+ buffer [length] = 0;
+ op -> data.terminated = 1;
+ } else
+ op -> data.terminated = 0;
+
+ /* If this option is ultimately a text option, null determinate to
+ * comply with RFC2132 section 2. Mark a flag so this can be sensed
+ * later to echo NULLs back to clients that supplied them (they
+ * probably expect them).
+ */
+ if (format_has_text(option->format)) {
+ int min_len = format_min_length(option->format, op);
+
+ while ((op->data.len > min_len) &&
+ (op->data.data[op->data.len-1] == '\0')) {
+ op->data.len--;
+ op->flags |= OPTION_HAD_NULLS;
+ }
+ }
+
+ /* And let go of our references. */
+ cleanup:
+ option_dereference(&option, MDL);
+
+ return 1;
+}
+
+static void
+count_options(struct option_cache *dummy_oc,
+ struct packet *dummy_packet,
+ struct lease *dummy_lease,
+ struct client_state *dummy_client_state,
+ struct option_state *dummy_opt_state,
+ struct option_state *opt_state,
+ struct binding_scope **dummy_binding_scope,
+ struct universe *dummy_universe,
+ void *void_accumulator) {
+ int *accumulator = (int *)void_accumulator;
+
+ *accumulator += 1;
+}
+
+static void
+collect_oro(struct option_cache *oc,
+ struct packet *dummy_packet,
+ struct lease *dummy_lease,
+ struct client_state *dummy_client_state,
+ struct option_state *dummy_opt_state,
+ struct option_state *opt_state,
+ struct binding_scope **dummy_binding_scope,
+ struct universe *dummy_universe,
+ void *void_oro) {
+ struct data_string *oro = (struct data_string *)void_oro;
+
+ putUShort(oro->buffer->data + oro->len, oc->option->code);
+ oro->len += 2;
+}
+
+/* build_server_oro() is presently unusued, but may be used at a future date
+ * with support for Reconfigure messages (as a hint to the client about new
+ * option value contents).
+ */
+void
+build_server_oro(struct data_string *server_oro,
+ struct option_state *options,
+ const char *file, int line) {
+ int num_opts;
+ int i;
+ struct option *o;
+
+ /*
+ * Count the number of options, so we can allocate enough memory.
+ * We want to mention sub-options too, so check all universes.
+ */
+ num_opts = 0;
+ option_space_foreach(NULL, NULL, NULL, NULL, options,
+ NULL, &dhcpv6_universe, (void *)&num_opts,
+ count_options);
+ for (i=0; i < options->universe_count; i++) {
+ if (options->universes[i] != NULL) {
+ o = universes[i]->enc_opt;
+ while (o != NULL) {
+ if (o->universe == &dhcpv6_universe) {
+ num_opts++;
+ break;
+ }
+ o = o->universe->enc_opt;
+ }
+ }
+ }
+
+ /*
+ * Allocate space.
+ */
+ memset(server_oro, 0, sizeof(*server_oro));
+ if (!buffer_allocate(&server_oro->buffer, num_opts * 2, MDL)) {
+ log_fatal("no memory to build server ORO");
+ }
+ server_oro->data = server_oro->buffer->data;
+
+ /*
+ * Copy the data in.
+ * We want to mention sub-options too, so check all universes.
+ */
+ server_oro->len = 0; /* gets set in collect_oro */
+ option_space_foreach(NULL, NULL, NULL, NULL, options,
+ NULL, &dhcpv6_universe, (void *)server_oro,
+ collect_oro);
+ for (i=0; i < options->universe_count; i++) {
+ if (options->universes[i] != NULL) {
+ o = universes[i]->enc_opt;
+ while (o != NULL) {
+ if (o->universe == &dhcpv6_universe) {
+ unsigned char *tmp;
+ tmp = server_oro->buffer->data;
+ putUShort(tmp + server_oro->len,
+ o->code);
+ server_oro->len += 2;
+ break;
+ }
+ o = o->universe->enc_opt;
+ }
+ }
+ }
+}
+
+/* Wrapper function to put an option cache into an option state. */
+void
+save_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc)
+{
+ if (universe->save_func)
+ (*universe->save_func)(universe, options, oc, ISC_FALSE);
+ else
+ log_error("can't store options in %s space.", universe->name);
+}
+
+/* Wrapper function to append an option cache into an option state's list. */
+void
+also_save_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc)
+{
+ if (universe->save_func)
+ (*universe->save_func)(universe, options, oc, ISC_TRUE);
+ else
+ log_error("can't store options in %s space.", universe->name);
+}
+
+void
+save_hashed_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc, isc_boolean_t appendp)
+{
+ int hashix;
+ pair bptr;
+ pair *hash = options -> universes [universe -> index];
+ struct option_cache **ocloc;
+
+ if (oc -> refcnt == 0)
+ abort ();
+
+ /* Compute the hash. */
+ hashix = compute_option_hash (oc -> option -> code);
+
+ /* If there's no hash table, make one. */
+ if (!hash) {
+ hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL);
+ if (!hash) {
+ log_error ("no memory to store %s.%s",
+ universe -> name, oc -> option -> name);
+ return;
+ }
+ memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash);
+ options -> universes [universe -> index] = (void *)hash;
+ } else {
+ /* Try to find an existing option matching the new one. */
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)
+ (bptr -> car)) -> option -> code ==
+ oc -> option -> code)
+ break;
+ }
+
+ /* Deal with collisions on the hash list. */
+ if (bptr) {
+ ocloc = (struct option_cache **)&bptr->car;
+
+ /*
+ * If appendp is set, append it onto the tail of the
+ * ->next list. If it is not set, rotate it into
+ * position at the head of the list.
+ */
+ if (appendp) {
+ do {
+ ocloc = &(*ocloc)->next;
+ } while (*ocloc != NULL);
+ } else {
+ option_cache_dereference(ocloc, MDL);
+ }
+
+ option_cache_reference(ocloc, oc, MDL);
+ return;
+ }
+ }
+
+ /* Otherwise, just put the new one at the head of the list. */
+ bptr = new_pair (MDL);
+ if (!bptr) {
+ log_error ("No memory for option_cache reference.");
+ return;
+ }
+ bptr -> cdr = hash [hashix];
+ bptr -> car = 0;
+ option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL);
+ hash [hashix] = bptr;
+}
+
+void delete_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ int code;
+{
+ if (universe -> delete_func)
+ (*universe -> delete_func) (universe, options, code);
+ else
+ log_error ("can't delete options from %s space.",
+ universe -> name);
+}
+
+void delete_hashed_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ int code;
+{
+ int hashix;
+ pair bptr, prev = (pair)0;
+ pair *hash = options -> universes [universe -> index];
+
+ /* There may not be any options in this space. */
+ if (!hash)
+ return;
+
+ /* Try to find an existing option matching the new one. */
+ hashix = compute_option_hash (code);
+ for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
+ if (((struct option_cache *)(bptr -> car)) -> option -> code
+ == code)
+ break;
+ prev = bptr;
+ }
+ /* If we found one, wipe it out... */
+ if (bptr) {
+ if (prev)
+ prev -> cdr = bptr -> cdr;
+ else
+ hash [hashix] = bptr -> cdr;
+ option_cache_dereference
+ ((struct option_cache **)(&bptr -> car), MDL);
+ free_pair (bptr, MDL);
+ }
+}
+
+extern struct option_cache *free_option_caches; /* XXX */
+
+int option_cache_dereference (ptr, file, line)
+ struct option_cache **ptr;
+ const char *file;
+ int line;
+{
+ if (!ptr || !*ptr) {
+ log_error ("Null pointer in option_cache_dereference: %s(%d)",
+ file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ (*ptr) -> refcnt--;
+ rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
+ if (!(*ptr) -> refcnt) {
+ if ((*ptr) -> data.buffer)
+ data_string_forget (&(*ptr) -> data, file, line);
+ if ((*ptr)->option)
+ option_dereference(&(*ptr)->option, MDL);
+ if ((*ptr) -> expression)
+ expression_dereference (&(*ptr) -> expression,
+ file, line);
+ if ((*ptr) -> next)
+ option_cache_dereference (&((*ptr) -> next),
+ file, line);
+ /* Put it back on the free list... */
+ (*ptr) -> expression = (struct expression *)free_option_caches;
+ free_option_caches = *ptr;
+ dmalloc_reuse (free_option_caches, (char *)0, 0, 0);
+ }
+ if ((*ptr) -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*ptr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ *ptr = (struct option_cache *)0;
+ return 0;
+#endif
+ }
+ *ptr = (struct option_cache *)0;
+ return 1;
+
+}
+
+int hashed_option_state_dereference (universe, state, file, line)
+ struct universe *universe;
+ struct option_state *state;
+ const char *file;
+ int line;
+{
+ pair *heads;
+ pair cp, next;
+ int i;
+
+ /* Get the pointer to the array of hash table bucket heads. */
+ heads = (pair *)(state -> universes [universe -> index]);
+ if (!heads)
+ return 0;
+
+ /* For each non-null head, loop through all the buckets dereferencing
+ the attached option cache structures and freeing the buckets. */
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ for (cp = heads [i]; cp; cp = next) {
+ next = cp -> cdr;
+ option_cache_dereference
+ ((struct option_cache **)&cp -> car,
+ file, line);
+ free_pair (cp, file, line);
+ }
+ }
+
+ dfree (heads, file, line);
+ state -> universes [universe -> index] = (void *)0;
+ return 1;
+}
+
+/* The 'data_string' primitive doesn't have an appension mechanism.
+ * This function must then append a new option onto an existing buffer
+ * by first duplicating the original buffer and appending the desired
+ * values, followed by coping the new value into place.
+ */
+int
+append_option(struct data_string *dst, struct universe *universe,
+ struct option *option, struct data_string *src)
+{
+ struct data_string tmp;
+
+ if (src->len == 0 && option->format[0] != 'Z')
+ return 0;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ /* Allocate a buffer to hold existing data, the current option's
+ * tag and length, and the option's content.
+ */
+ if (!buffer_allocate(&tmp.buffer,
+ (dst->len + universe->length_size +
+ universe->tag_size + src->len), MDL)) {
+ /* XXX: This kills all options presently stored in the
+ * destination buffer. This is the way the original code
+ * worked, and assumes an 'all or nothing' approach to
+ * eg encapsulated option spaces. It may or may not be
+ * desirable.
+ */
+ data_string_forget(dst, MDL);
+ return 0;
+ }
+ tmp.data = tmp.buffer->data;
+
+ /* Copy the existing data off the destination. */
+ if (dst->len != 0)
+ memcpy(tmp.buffer->data, dst->data, dst->len);
+ tmp.len = dst->len;
+
+ /* Place the new option tag and length. */
+ (*universe->store_tag)(tmp.buffer->data + tmp.len, option->code);
+ tmp.len += universe->tag_size;
+ (*universe->store_length)(tmp.buffer->data + tmp.len, src->len);
+ tmp.len += universe->length_size;
+
+ /* Copy the option contents onto the end. */
+ memcpy(tmp.buffer->data + tmp.len, src->data, src->len);
+ tmp.len += src->len;
+
+ /* Play the shell game. */
+ data_string_forget(dst, MDL);
+ data_string_copy(dst, &tmp, MDL);
+ data_string_forget(&tmp, MDL);
+ return 1;
+}
+
+int
+store_option(struct data_string *result, struct universe *universe,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options, struct option_state *cfg_options,
+ struct binding_scope **scope, struct option_cache *oc)
+{
+ struct data_string tmp;
+ struct universe *subu=NULL;
+ int status;
+ char *start, *end;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ if (evaluate_option_cache(&tmp, packet, lease, client_state,
+ in_options, cfg_options, scope, oc, MDL)) {
+ /* If the option is an extended 'e'ncapsulation (not a
+ * direct 'E'ncapsulation), append the encapsulated space
+ * onto the currently prepared value.
+ */
+ do {
+ if (oc->option->format &&
+ oc->option->format[0] == 'e') {
+ /* Skip forward to the universe name. */
+ start = strchr(oc->option->format, 'E');
+ if (start == NULL)
+ break;
+
+ /* Locate the name-terminating '.'. */
+ end = strchr(++start, '.');
+
+ /* A zero-length name is not allowed in
+ * these kinds of encapsulations.
+ */
+ if (end == NULL || start == end)
+ break;
+
+ universe_hash_lookup(&subu, universe_hash,
+ start, end - start, MDL);
+
+ if (subu == NULL) {
+ log_error("store_option: option %d "
+ "refers to unknown "
+ "option space '%.*s'.",
+ oc->option->code,
+ (int)(end - start), start);
+ break;
+ }
+
+ /* Append encapsulations, if any. We
+ * already have the prepended values, so
+ * we send those even if there are no
+ * encapsulated options (and ->encapsulate()
+ * returns zero).
+ */
+ subu->encapsulate(&tmp, packet, lease,
+ client_state, in_options,
+ cfg_options, scope, subu);
+ subu = NULL;
+ }
+ } while (ISC_FALSE);
+
+ status = append_option(result, universe, oc->option, &tmp);
+ data_string_forget(&tmp, MDL);
+
+ return status;
+ }
+
+ return 0;
+}
+
+int option_space_encapsulate (result, packet, lease, client_state,
+ in_options, cfg_options, scope, name)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct data_string *name;
+{
+ struct universe *u = NULL;
+ int status = 0;
+
+ universe_hash_lookup(&u, universe_hash,
+ (const char *)name->data, name->len, MDL);
+ if (u == NULL) {
+ log_error("option_space_encapsulate: option space '%.*s' does "
+ "not exist, but is configured.",
+ (int)name->len, name->data);
+ return status;
+ }
+
+ if (u->encapsulate != NULL) {
+ if (u->encapsulate(result, packet, lease, client_state,
+ in_options, cfg_options, scope, u))
+ status = 1;
+ } else
+ log_error("encapsulation requested for '%s' with no support.",
+ name->data);
+
+ return status;
+}
+
+/* Attempt to store any 'E'ncapsulated options that have not yet been
+ * placed on the option buffer by the above (configuring a value in
+ * the space over-rides any values in the child universe).
+ *
+ * Note that there are far fewer universes than there will ever be
+ * options in any universe. So it is faster to traverse the
+ * configured universes, checking if each is encapsulated in the
+ * current universe, and if so attempting to do so.
+ *
+ * For each configured universe for this configuration option space,
+ * which is encapsulated within the current universe, can not be found
+ * by the lookup function (the universe-specific encapsulation
+ * functions would already have stored such a value), and encapsulates
+ * at least one option, append it.
+ */
+static int
+search_subencapsulation(struct data_string *result, struct packet *packet,
+ struct lease *lease, struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *universe)
+{
+ struct data_string sub;
+ struct universe *subu;
+ int i, status = 0;
+
+ memset(&sub, 0, sizeof(sub));
+ for (i = 0 ; i < cfg_options->universe_count ; i++) {
+ subu = universes[i];
+
+ if (subu == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (subu->enc_opt != NULL &&
+ subu->enc_opt->universe == universe &&
+ subu->enc_opt->format != NULL &&
+ subu->enc_opt->format[0] == 'E' &&
+ lookup_option(universe, cfg_options,
+ subu->enc_opt->code) == NULL &&
+ subu->encapsulate(&sub, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, subu)) {
+ if (append_option(result, universe,
+ subu->enc_opt, &sub))
+ status = 1;
+
+ data_string_forget(&sub, MDL);
+ }
+ }
+
+ return status;
+}
+
+int hashed_option_space_encapsulate (result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ pair p, *hash;
+ int status;
+ int i;
+
+ if (universe -> index >= cfg_options -> universe_count)
+ return 0;
+
+ hash = cfg_options -> universes [universe -> index];
+ if (!hash)
+ return 0;
+
+ /* For each hash bucket, and each configured option cache within
+ * that bucket, append the option onto the buffer in encapsulated
+ * format appropriate to the universe.
+ */
+ status = 0;
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ for (p = hash [i]; p; p = p -> cdr) {
+ if (store_option(result, universe, packet, lease,
+ client_state, in_options, cfg_options,
+ scope, (struct option_cache *)p->car))
+ status = 1;
+ }
+ }
+
+ if (search_subencapsulation(result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe))
+ status = 1;
+
+ return status;
+}
+
+int nwip_option_space_encapsulate (result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ pair ocp;
+ int status;
+ static struct option_cache *no_nwip;
+ struct data_string ds;
+ struct option_chain_head *head;
+
+ if (universe -> index >= cfg_options -> universe_count)
+ return 0;
+ head = ((struct option_chain_head *)
+ cfg_options -> universes [nwip_universe.index]);
+ if (!head)
+ return 0;
+
+ status = 0;
+ for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
+ if (store_option (result, universe, packet,
+ lease, client_state, in_options,
+ cfg_options, scope,
+ (struct option_cache *)ocp -> car))
+ status = 1;
+ }
+
+ /* If there's no data, the nwip suboption is supposed to contain
+ a suboption saying there's no data. */
+ if (!status) {
+ if (!no_nwip) {
+ unsigned one = 1;
+ static unsigned char nni [] = { 1, 0 };
+
+ memset (&ds, 0, sizeof ds);
+ ds.data = nni;
+ ds.len = 2;
+ if (option_cache_allocate (&no_nwip, MDL))
+ data_string_copy (&no_nwip -> data, &ds, MDL);
+ if (!option_code_hash_lookup(&no_nwip->option,
+ nwip_universe.code_hash,
+ &one, 0, MDL))
+ log_fatal("Nwip option hash does not contain "
+ "1 (%s:%d).", MDL);
+ }
+ if (no_nwip) {
+ if (store_option (result, universe, packet, lease,
+ client_state, in_options,
+ cfg_options, scope, no_nwip))
+ status = 1;
+ }
+ } else {
+ memset (&ds, 0, sizeof ds);
+
+ /* If we have nwip options, the first one has to be the
+ nwip-exists-in-option-area option. */
+ if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) {
+ data_string_forget (result, MDL);
+ return 0;
+ }
+ ds.data = &ds.buffer -> data [0];
+ ds.buffer -> data [0] = 2;
+ ds.buffer -> data [1] = 0;
+ memcpy (&ds.buffer -> data [2], result -> data, result -> len);
+ data_string_forget (result, MDL);
+ data_string_copy (result, &ds, MDL);
+ data_string_forget (&ds, MDL);
+ }
+
+ return status;
+}
+
+/* We don't want to use ns_name_pton()...it doesn't tell us how many bytes
+ * it has consumed, and it plays havoc with our escapes.
+ *
+ * So this function does DNS encoding, and returns either the number of
+ * octects consumed (on success), or -1 on failure.
+ */
+static int
+fqdn_encode(unsigned char *dst, int dstlen, const unsigned char *src,
+ int srclen)
+{
+ unsigned char *out;
+ int i, j, len, outlen=0;
+
+ out = dst;
+ for (i = 0, j = 0 ; i < srclen ; i = j) {
+ while ((j < srclen) && (src[j] != '.') && (src[j] != '\0'))
+ j++;
+
+ len = j - i;
+ if ((outlen + 1 + len) > dstlen)
+ return -1;
+
+ *out++ = len;
+ outlen++;
+
+ /* We only do one FQDN, ending in one root label. */
+ if (len == 0)
+ return outlen;
+
+ memcpy(out, src + i, len);
+ out += len;
+ outlen += len;
+
+ /* Advance past the root label. */
+ j++;
+ }
+
+ if ((outlen + 1) > dstlen)
+ return -1;
+
+ /* Place the root label. */
+ *out++ = 0;
+ outlen++;
+
+ return outlen;
+}
+
+int fqdn_option_space_encapsulate (result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ pair ocp;
+ struct data_string results [FQDN_SUBOPTION_COUNT + 1];
+ int status = 1;
+ int i;
+ unsigned len;
+ struct buffer *bp = (struct buffer *)0;
+ struct option_chain_head *head;
+
+ /* If there's no FQDN universe, don't encapsulate. */
+ if (fqdn_universe.index >= cfg_options -> universe_count)
+ return 0;
+ head = ((struct option_chain_head *)
+ cfg_options -> universes [fqdn_universe.index]);
+ if (!head)
+ return 0;
+
+ /* Figure out the values of all the suboptions. */
+ memset (results, 0, sizeof results);
+ for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
+ struct option_cache *oc = (struct option_cache *)(ocp -> car);
+ if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
+ continue;
+ evaluate_option_cache (&results [oc -> option -> code],
+ packet, lease, client_state, in_options,
+ cfg_options, scope, oc, MDL);
+ }
+ /* We add a byte for the flags field.
+ * We add two bytes for the two RCODE fields.
+ * We add a byte because we will prepend a label count.
+ * We add a byte because the input len doesn't count null termination,
+ * and we will add a root label.
+ */
+ len = 5 + results [FQDN_FQDN].len;
+ /* Save the contents of the option in a buffer. */
+ if (!buffer_allocate (&bp, len, MDL)) {
+ log_error ("no memory for option buffer.");
+ status = 0;
+ goto exit;
+ }
+ buffer_reference (&result -> buffer, bp, MDL);
+ result -> len = 3;
+ result -> data = &bp -> data [0];
+
+ memset (&bp -> data [0], 0, len);
+ /* XXX: The server should set bit 4 (yes, 4, not 3) to 1 if it is
+ * not going to perform any ddns updates. The client should set the
+ * bit if it doesn't want the server to perform any updates.
+ * The problem is at this layer of abstraction we have no idea if
+ * the caller is a client or server.
+ *
+ * See RFC4702, Section 3.1, 'The "N" bit'.
+ *
+ * if (?)
+ * bp->data[0] |= 8;
+ */
+ if (results [FQDN_NO_CLIENT_UPDATE].len &&
+ results [FQDN_NO_CLIENT_UPDATE].data [0])
+ bp -> data [0] |= 2;
+ if (results [FQDN_SERVER_UPDATE].len &&
+ results [FQDN_SERVER_UPDATE].data [0])
+ bp -> data [0] |= 1;
+ if (results [FQDN_RCODE1].len)
+ bp -> data [1] = results [FQDN_RCODE1].data [0];
+ if (results [FQDN_RCODE2].len)
+ bp -> data [2] = results [FQDN_RCODE2].data [0];
+
+ if (results [FQDN_ENCODED].len &&
+ results [FQDN_ENCODED].data [0]) {
+ bp->data[0] |= 4;
+ if (results [FQDN_FQDN].len) {
+ i = fqdn_encode(&bp->data[3], len - 3,
+ results[FQDN_FQDN].data,
+ results[FQDN_FQDN].len);
+
+ if (i < 0) {
+ status = 0;
+ goto exit;
+ }
+
+ result->len += i;
+ result->terminated = 0;
+ }
+ } else {
+ if (results [FQDN_FQDN].len) {
+ memcpy (&bp -> data [3], results [FQDN_FQDN].data,
+ results [FQDN_FQDN].len);
+ result -> len += results [FQDN_FQDN].len;
+ result -> terminated = 0;
+ }
+ }
+ exit:
+ for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
+ if (results [i].len)
+ data_string_forget (&results [i], MDL);
+ }
+ buffer_dereference (&bp, MDL);
+ if (!status)
+ data_string_forget(result, MDL);
+ return status;
+}
+
+/*
+ * Trap invalid attempts to inspect FQND6 contents.
+ */
+struct option_cache *
+lookup_fqdn6_option(struct universe *universe, struct option_state *options,
+ unsigned code)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ return NULL;
+}
+
+/*
+ * Trap invalid attempts to save options directly to FQDN6 rather than FQDN.
+ */
+void
+save_fqdn6_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc, isc_boolean_t appendp)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+}
+
+/*
+ * Trap invalid attempts to delete an option out of the FQDN6 universe.
+ */
+void
+delete_fqdn6_option(struct universe *universe, struct option_state *options,
+ int code)
+{
+ log_fatal("Impossible condition at %s:%d.", MDL);
+}
+
+/* Shill to the DHCPv4 fqdn option cache any attempts to traverse the
+ * V6's option cache entry.
+ *
+ * This function is called speculatively by dhclient to setup
+ * environment variables. But it would have already called the
+ * foreach on the normal fqdn universe, so this is superfluous.
+ */
+void
+fqdn6_option_space_foreach(struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func)(struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ /* Pretend it is empty. */
+ return;
+}
+
+/* Turn the FQDN option space into a DHCPv6 FQDN option buffer.
+ */
+int
+fqdn6_option_space_encapsulate(struct data_string *result,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *universe)
+{
+ pair ocp;
+ struct option_chain_head *head;
+ struct option_cache *oc;
+ unsigned char *data;
+ int i, len, rval = 0, count;
+ struct data_string results[FQDN_SUBOPTION_COUNT + 1];
+
+ if (fqdn_universe.index >= cfg_options->universe_count)
+ return 0;
+ head = ((struct option_chain_head *)
+ cfg_options->universes[fqdn_universe.index]);
+ if (head == NULL)
+ return 0;
+
+ memset(results, 0, sizeof(results));
+ for (ocp = head->first ; ocp != NULL ; ocp = ocp->cdr) {
+ oc = (struct option_cache *)(ocp->car);
+ if (oc->option->code > FQDN_SUBOPTION_COUNT)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ evaluate_option_cache(&results[oc->option->code], packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, MDL);
+ }
+
+ /* We add a byte for the flags field at the start of the option.
+ * We add a byte because we will prepend a label count.
+ * We add a byte because the input length doesn't include a trailing
+ * NULL, and we will add a root label.
+ */
+ len = results[FQDN_FQDN].len + 3;
+ if (!buffer_allocate(&result->buffer, len, MDL)) {
+ log_error("No memory for virtual option buffer.");
+ goto exit;
+ }
+ data = result->buffer->data;
+ result->data = data;
+
+ /* The first byte is the flags field. */
+ result->len = 1;
+ data[0] = 0;
+ /* XXX: The server should set bit 3 (yes, 3, not 4) to 1 if we
+ * are not going to perform any DNS updates. The problem is
+ * that at this layer of abstraction, we do not know if the caller
+ * is the client or the server.
+ *
+ * See RFC4704 Section 4.1, 'The "N" bit'.
+ *
+ * if (?)
+ * data[0] |= 4;
+ */
+ if (results[FQDN_NO_CLIENT_UPDATE].len &&
+ results[FQDN_NO_CLIENT_UPDATE].data[0])
+ data[0] |= 2;
+ if (results[FQDN_SERVER_UPDATE].len &&
+ results[FQDN_SERVER_UPDATE].data[0])
+ data[0] |= 1;
+
+ /* If there is no name, we're done. */
+ if (results[FQDN_FQDN].len == 0) {
+ rval = 1;
+ goto exit;
+ }
+
+ /* Convert textual representation to DNS format. */
+ count = fqdn_encode(data + 1, len - 1,
+ results[FQDN_FQDN].data, results[FQDN_FQDN].len);
+
+ if (count < 0) {
+ rval = 0;
+ data_string_forget(result, MDL);
+ goto exit;
+ }
+
+ result->len += count;
+ result->terminated = 0;
+
+ /* Success! */
+ rval = 1;
+
+ exit:
+ for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
+ if (result[i].len)
+ data_string_forget(&results[i], MDL);
+ }
+
+ return rval;
+}
+
+/* Read the DHCPv6 FQDN option's contents into the FQDN virtual space.
+ */
+int
+fqdn6_universe_decode(struct option_state *options,
+ const unsigned char *buffer, unsigned length,
+ struct universe *u)
+{
+ struct buffer *bp = NULL;
+ unsigned char *first_dot;
+ int len, hlen, dlen;
+
+ /* The FQDN option has to be at least 1 byte long. */
+ if (length < 1)
+ return 0;
+
+ /* Save the contents of the option in a buffer. There are 3
+ * one-byte values we record from the packet, so we go ahead
+ * and allocate a bigger buffer to accommodate them. But the
+ * 'length' we got (because it is a DNS encoded string) is
+ * one longer than we need...so we only add two extra octets.
+ */
+ if (!buffer_allocate(&bp, length + 2, MDL)) {
+ log_error("No memory for dhcp6.fqdn option buffer.");
+ return 0;
+ }
+
+ /* The v6 FQDN is always 'encoded' per DNS. */
+ bp->data[0] = 1;
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ bp->data, 1, FQDN_ENCODED, 0))
+ goto error;
+
+ /* XXX: We need to process 'The "N" bit'. */
+
+ if (buffer[0] & 1) /* server-update. */
+ bp->data[2] = 1;
+ else
+ bp->data[2] = 0;
+
+ if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 2, 1,
+ FQDN_SERVER_UPDATE, 0))
+ goto error;
+
+ if (buffer[0] & 2) /* no-client-update. */
+ bp->data[1] = 1;
+ else
+ bp->data[1] = 0;
+
+ if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 1, 1,
+ FQDN_NO_CLIENT_UPDATE, 0))
+ goto error;
+
+ /* Convert the domain name to textual representation for config. */
+ len = MRns_name_ntop(buffer + 1, (char *)bp->data + 3, length - 1);
+ if (len == -1) {
+ log_error("Unable to convert dhcp6.fqdn domain name to "
+ "printable form.");
+ goto error;
+ }
+
+ /* Save the domain name. */
+ if (len > 0) {
+ unsigned char *fqdn_start = bp->data + 3;
+
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ fqdn_start, len, FQDN_FQDN, 1))
+ goto error;
+
+ first_dot = (unsigned char *)strchr((char *)fqdn_start, '.');
+
+ if (first_dot != NULL) {
+ hlen = first_dot - fqdn_start;
+ dlen = len - hlen;
+ } else {
+ hlen = len;
+ dlen = 0;
+ }
+
+ if (!save_option_buffer(&fqdn_universe, options, bp,
+ fqdn_start, len, FQDN_FQDN, 1) ||
+ ((hlen > 0) &&
+ !save_option_buffer(&fqdn_universe, options, bp,
+ fqdn_start, hlen,
+ FQDN_HOSTNAME, 0)) ||
+ ((dlen > 0) &&
+ !save_option_buffer(&fqdn_universe, options, bp,
+ first_dot, dlen, FQDN_DOMAINNAME, 0)))
+ goto error;
+ }
+
+ buffer_dereference(&bp, MDL);
+ return 1;
+
+ error:
+ buffer_dereference(&bp, MDL);
+ return 0;
+}
+
+void option_space_foreach (struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func) (struct option_cache *,
+ struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ if (u -> foreach)
+ (*u -> foreach) (packet, lease, client_state, in_options,
+ cfg_options, scope, u, stuff, func);
+}
+
+void suboption_foreach (struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func) (struct option_cache *,
+ struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *),
+ struct option_cache *oc,
+ const char *vsname)
+{
+ struct universe *universe = find_option_universe (oc -> option,
+ vsname);
+ if (universe -> foreach)
+ (*universe -> foreach) (packet, lease, client_state,
+ in_options, cfg_options,
+ scope, universe, stuff, func);
+}
+
+void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func) (struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ pair *hash;
+ int i;
+ struct option_cache *oc;
+
+ if (cfg_options -> universe_count <= u -> index)
+ return;
+
+ hash = cfg_options -> universes [u -> index];
+ if (!hash)
+ return;
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ pair p;
+ /* XXX save _all_ options! XXX */
+ for (p = hash [i]; p; p = p -> cdr) {
+ oc = (struct option_cache *)p -> car;
+ (*func) (oc, packet, lease, client_state,
+ in_options, cfg_options, scope, u, stuff);
+ }
+ }
+}
+
+void
+save_linked_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc, isc_boolean_t appendp)
+{
+ pair *tail;
+ struct option_chain_head *head;
+ struct option_cache **ocloc;
+
+ if (universe -> index >= options -> universe_count)
+ return;
+ head = ((struct option_chain_head *)
+ options -> universes [universe -> index]);
+ if (!head) {
+ if (!option_chain_head_allocate (((struct option_chain_head **)
+ &options -> universes
+ [universe -> index]), MDL))
+ return;
+ head = ((struct option_chain_head *)
+ options -> universes [universe -> index]);
+ }
+
+ /* Find the tail of the list. */
+ for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
+ ocloc = (struct option_cache **)&(*tail)->car;
+
+ if (oc->option->code == (*ocloc)->option->code) {
+ if (appendp) {
+ do {
+ ocloc = &(*ocloc)->next;
+ } while (*ocloc != NULL);
+ } else {
+ option_cache_dereference(ocloc, MDL);
+ }
+ option_cache_reference(ocloc, oc, MDL);
+ return;
+ }
+ }
+
+ *tail = cons (0, 0);
+ if (*tail) {
+ option_cache_reference ((struct option_cache **)
+ (&(*tail) -> car), oc, MDL);
+ }
+}
+
+int linked_option_space_encapsulate (result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ int status = 0;
+ pair oc;
+ struct option_chain_head *head;
+
+ if (universe -> index >= cfg_options -> universe_count)
+ return status;
+ head = ((struct option_chain_head *)
+ cfg_options -> universes [universe -> index]);
+ if (!head)
+ return status;
+
+ for (oc = head -> first; oc; oc = oc -> cdr) {
+ if (store_option (result, universe, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, (struct option_cache *)(oc -> car)))
+ status = 1;
+ }
+
+ if (search_subencapsulation(result, packet, lease, client_state,
+ in_options, cfg_options, scope, universe))
+ status = 1;
+
+ return status;
+}
+
+void delete_linked_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ int code;
+{
+ pair *tail, tmp = (pair)0;
+ struct option_chain_head *head;
+
+ if (universe -> index >= options -> universe_count)
+ return;
+ head = ((struct option_chain_head *)
+ options -> universes [universe -> index]);
+ if (!head)
+ return;
+
+ for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
+ if (code ==
+ ((struct option_cache *)(*tail) -> car) -> option -> code)
+ {
+ tmp = (*tail) -> cdr;
+ option_cache_dereference ((struct option_cache **)
+ (&(*tail) -> car), MDL);
+ dfree (*tail, MDL);
+ (*tail) = tmp;
+ break;
+ }
+ }
+}
+
+struct option_cache *lookup_linked_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ unsigned code;
+{
+ pair oc;
+ struct option_chain_head *head;
+
+ if (universe -> index >= options -> universe_count)
+ return 0;
+ head = ((struct option_chain_head *)
+ options -> universes [universe -> index]);
+ if (!head)
+ return 0;
+
+ for (oc = head -> first; oc; oc = oc -> cdr) {
+ if (code ==
+ ((struct option_cache *)(oc -> car)) -> option -> code) {
+ return (struct option_cache *)(oc -> car);
+ }
+ }
+
+ return (struct option_cache *)0;
+}
+
+int linked_option_state_dereference (universe, state, file, line)
+ struct universe *universe;
+ struct option_state *state;
+ const char *file;
+ int line;
+{
+ return (option_chain_head_dereference
+ ((struct option_chain_head **)
+ (&state -> universes [universe -> index]), MDL));
+}
+
+void linked_option_space_foreach (struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func) (struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ pair car;
+ struct option_chain_head *head;
+
+ if (u -> index >= cfg_options -> universe_count)
+ return;
+ head = ((struct option_chain_head *)
+ cfg_options -> universes [u -> index]);
+ if (!head)
+ return;
+ for (car = head -> first; car; car = car -> cdr) {
+ (*func) ((struct option_cache *)(car -> car),
+ packet, lease, client_state,
+ in_options, cfg_options, scope, u, stuff);
+ }
+}
+
+void do_packet (interface, packet, len, from_port, from, hfrom)
+ struct interface_info *interface;
+ struct dhcp_packet *packet;
+ unsigned len;
+ unsigned int from_port;
+ struct iaddr from;
+ struct hardware *hfrom;
+{
+ struct option_cache *op;
+ struct packet *decoded_packet;
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ unsigned long previous_outstanding = dmalloc_outstanding;
+#endif
+
+#if defined (TRACING)
+ trace_inpacket_stash (interface, packet, len, from_port, from, hfrom);
+#endif
+
+ decoded_packet = (struct packet *)0;
+ if (!packet_allocate (&decoded_packet, MDL)) {
+ log_error ("do_packet: no memory for incoming packet!");
+ return;
+ }
+ decoded_packet -> raw = packet;
+ decoded_packet -> packet_length = len;
+ decoded_packet -> client_port = from_port;
+ decoded_packet -> client_addr = from;
+ interface_reference (&decoded_packet -> interface, interface, MDL);
+ decoded_packet -> haddr = hfrom;
+
+ if (packet -> hlen > sizeof packet -> chaddr) {
+ packet_dereference (&decoded_packet, MDL);
+ log_info ("Discarding packet with bogus hlen.");
+ return;
+ }
+
+ /* If there's an option buffer, try to parse it. */
+ if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) {
+ if (!parse_options (decoded_packet)) {
+ if (decoded_packet -> options)
+ option_state_dereference
+ (&decoded_packet -> options, MDL);
+ packet_dereference (&decoded_packet, MDL);
+ return;
+ }
+
+ if (decoded_packet -> options_valid &&
+ (op = lookup_option (&dhcp_universe,
+ decoded_packet -> options,
+ DHO_DHCP_MESSAGE_TYPE))) {
+ struct data_string dp;
+ memset (&dp, 0, sizeof dp);
+ evaluate_option_cache (&dp, decoded_packet,
+ (struct lease *)0,
+ (struct client_state *)0,
+ decoded_packet -> options,
+ (struct option_state *)0,
+ (struct binding_scope **)0,
+ op, MDL);
+ if (dp.len > 0)
+ decoded_packet -> packet_type = dp.data [0];
+ else
+ decoded_packet -> packet_type = 0;
+ data_string_forget (&dp, MDL);
+ }
+ }
+
+ if (decoded_packet -> packet_type)
+ dhcp (decoded_packet);
+ else
+ bootp (decoded_packet);
+
+ /* If the caller kept the packet, they'll have upped the refcnt. */
+ packet_dereference (&decoded_packet, MDL);
+
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
+ dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm);
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ dmalloc_dump_outstanding ();
+#endif
+#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
+ dump_rc_history (0);
+#endif
+}
+
+int
+packet6_len_okay(const char *packet, int len) {
+ if (len < 1) {
+ return 0;
+ }
+ if ((packet[0] == DHCPV6_RELAY_FORW) ||
+ (packet[0] == DHCPV6_RELAY_REPL)) {
+ if (len >= offsetof(struct dhcpv6_relay_packet, options)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ if (len >= offsetof(struct dhcpv6_packet, options)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+#ifdef DHCPv6
+void
+do_packet6(struct interface_info *interface, const char *packet,
+ int len, int from_port, const struct iaddr *from,
+ isc_boolean_t was_unicast) {
+ unsigned char msg_type;
+ const struct dhcpv6_packet *msg;
+ const struct dhcpv6_relay_packet *relay;
+ struct packet *decoded_packet;
+
+ if (!packet6_len_okay(packet, len)) {
+ log_info("do_packet6: "
+ "short packet from %s port %d, len %d, dropped",
+ piaddr(*from), from_port, len);
+ return;
+ }
+
+ decoded_packet = NULL;
+ if (!packet_allocate(&decoded_packet, MDL)) {
+ log_error("do_packet6: no memory for incoming packet.");
+ return;
+ }
+
+ if (!option_state_allocate(&decoded_packet->options, MDL)) {
+ log_error("do_packet6: no memory for options.");
+ packet_dereference(&decoded_packet, MDL);
+ return;
+ }
+
+ /* IPv4 information, already set to 0 */
+ /* decoded_packet->packet_type = 0; */
+ /* memset(&decoded_packet->haddr, 0, sizeof(decoded_packet->haddr)); */
+ /* decoded_packet->circuit_id = NULL; */
+ /* decoded_packet->circuit_id_len = 0; */
+ /* decoded_packet->remote_id = NULL; */
+ /* decoded_packet->remote_id_len = 0; */
+ decoded_packet->raw = (struct dhcp_packet *) packet;
+ decoded_packet->packet_length = (unsigned) len;
+ decoded_packet->client_port = from_port;
+ decoded_packet->client_addr = *from;
+ interface_reference(&decoded_packet->interface, interface, MDL);
+
+ decoded_packet->unicast = was_unicast;
+
+ msg_type = packet[0];
+ if ((msg_type == DHCPV6_RELAY_FORW) ||
+ (msg_type == DHCPV6_RELAY_REPL)) {
+ relay = (const struct dhcpv6_relay_packet *)packet;
+ decoded_packet->dhcpv6_msg_type = relay->msg_type;
+
+ /* relay-specific data */
+ decoded_packet->dhcpv6_hop_count = relay->hop_count;
+ memcpy(&decoded_packet->dhcpv6_link_address,
+ relay->link_address, sizeof(relay->link_address));
+ memcpy(&decoded_packet->dhcpv6_peer_address,
+ relay->peer_address, sizeof(relay->peer_address));
+
+ if (!parse_option_buffer(decoded_packet->options,
+ relay->options, len-sizeof(*relay),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&decoded_packet, MDL);
+ return;
+ }
+ } else {
+ msg = (const struct dhcpv6_packet *)packet;
+ decoded_packet->dhcpv6_msg_type = msg->msg_type;
+
+ /* message-specific data */
+ memcpy(decoded_packet->dhcpv6_transaction_id,
+ msg->transaction_id,
+ sizeof(decoded_packet->dhcpv6_transaction_id));
+
+ if (!parse_option_buffer(decoded_packet->options,
+ msg->options, len-sizeof(*msg),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ packet_dereference(&decoded_packet, MDL);
+ return;
+ }
+ }
+
+ dhcpv6(decoded_packet);
+
+ packet_dereference(&decoded_packet, MDL);
+}
+#endif /* DHCPv6 */
+
+int
+pretty_escape(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send)
+{
+ int count = 0;
+
+ /* If there aren't as many bytes left as there are in the source
+ * buffer, don't even bother entering the loop.
+ */
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || *src == NULL || (*dst >= dend) || (*src > send) ||
+ ((send - *src) > (dend - *dst)))
+ return -1;
+
+ for ( ; *src < send ; (*src)++) {
+ if (!isascii (**src) || !isprint (**src)) {
+ /* Skip trailing NUL. */
+ if ((*src + 1) != send || **src != '\0') {
+ if (*dst + 4 > dend)
+ return -1;
+
+ sprintf(*dst, "\\%03o",
+ **src);
+ (*dst) += 4;
+ count += 4;
+ }
+ } else if (**src == '"' || **src == '\'' || **src == '$' ||
+ **src == '`' || **src == '\\' || **src == '|' ||
+ **src == '&') {
+ if (*dst + 2 > dend)
+ return -1;
+
+ **dst = '\\';
+ (*dst)++;
+ **dst = **src;
+ (*dst)++;
+ count += 2;
+ } else {
+ if (*dst + 1 > dend)
+ return -1;
+
+ **dst = **src;
+ (*dst)++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static int
+pretty_text(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send, int emit_quotes)
+{
+ int count;
+
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || *src == NULL ||
+ ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send))
+ return -1;
+
+ if (emit_quotes) {
+ **dst = '"';
+ (*dst)++;
+ }
+
+ /* dend-1 leaves 1 byte for the closing quote. */
+ count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send);
+
+ if (count == -1)
+ return -1;
+
+ if (emit_quotes && (*dst < dend)) {
+ **dst = '"';
+ (*dst)++;
+
+ /* Includes quote prior to pretty_escape(); */
+ count += 2;
+ }
+
+ return count;
+}
+
+static int
+pretty_domain(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send)
+{
+ const unsigned char *tend;
+ int count = 2;
+ int tsiz, status;
+
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || *src == NULL ||
+ ((*dst + 2) > dend) || (*src >= send))
+ return -1;
+
+ **dst = '"';
+ (*dst)++;
+
+ do {
+ /* Continue loop until end of src buffer. */
+ if (*src >= send)
+ break;
+
+ /* Consume tag size. */
+ tsiz = **src;
+ (*src)++;
+
+ /* At root, finis. */
+ if (tsiz == 0)
+ break;
+
+ tend = (*src) + tsiz;
+
+ /* If the tag exceeds the source buffer, it's illegal.
+ * This should also trap compression pointers (which should
+ * not be in these buffers).
+ */
+ if (tend > send)
+ return -1;
+
+ /* dend-2 leaves room for a trailing dot and quote. */
+ status = pretty_escape(dst, dend-2, src, tend);
+
+ if ((status == -1) || ((*dst + 2) > dend))
+ return -1;
+
+ **dst = '.';
+ (*dst)++;
+ count += status + 1;
+ }
+ while(1);
+
+ **dst = '"';
+ (*dst)++;
+
+ return count;
+}
+
+/*
+ * Add the option identified with the option number and data to the
+ * options state.
+ */
+int
+add_option(struct option_state *options,
+ unsigned int option_num,
+ void *data,
+ unsigned int data_len)
+{
+ struct option_cache *oc;
+ struct option *option;
+
+ /* INSIST(options != NULL); */
+ /* INSIST(data != NULL); */
+
+ option = NULL;
+ if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
+ &option_num, 0, MDL)) {
+ log_error("Attempting to add unknown option %d.", option_num);
+ return 0;
+ }
+
+ oc = NULL;
+ if (!option_cache_allocate(&oc, MDL)) {
+ log_error("No memory for option cache adding %s (option %d).",
+ option->name, option_num);
+ return 0;
+ }
+
+ if (!make_const_data(&oc->expression,
+ data,
+ data_len,
+ 0,
+ 0,
+ MDL)) {
+ log_error("No memory for constant data adding %s (option %d).",
+ option->name, option_num);
+ option_cache_dereference(&oc, MDL);
+ return 0;
+ }
+
+ option_reference(&(oc->option), option, MDL);
+ save_option(&dhcp_universe, options, oc);
+ option_cache_dereference(&oc, MDL);
+
+ return 1;
+}
+
+
diff --git a/common/packet.c b/common/packet.c
new file mode 100644
index 0000000..42bca69
--- /dev/null
+++ b/common/packet.c
@@ -0,0 +1,346 @@
+/* packet.c
+
+ Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This code was originally contributed by Archie Cobbs, and is still
+ * very similar to that contribution, although the packet checksum code
+ * has been hacked significantly with the help of quite a few ISC DHCP
+ * users, without whose gracious and thorough help the checksum code would
+ * still be disabled.
+ */
+
+#include "dhcpd.h"
+
+#if defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING)
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+#endif /* PACKET_ASSEMBLY || PACKET_DECODING */
+
+/* Compute the easy part of the checksum on a range of bytes. */
+
+u_int32_t checksum (buf, nbytes, sum)
+ unsigned char *buf;
+ unsigned nbytes;
+ u_int32_t sum;
+{
+ unsigned i;
+
+#ifdef DEBUG_CHECKSUM
+ log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
+#endif
+
+ /* Checksum all the pairs of bytes first... */
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
+#ifdef DEBUG_CHECKSUM_VERBOSE
+ log_debug ("sum = %x", sum);
+#endif
+ sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
+ /* Add carry. */
+ 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) {
+#ifdef DEBUG_CHECKSUM_VERBOSE
+ log_debug ("sum = %x", sum);
+#endif
+ sum += buf [i] << 8;
+ /* Add carry. */
+ if (sum > 0xFFFF)
+ sum -= 0xFFFF;
+ }
+
+ return sum;
+}
+
+/* Finish computing the checksum, and then put it into network byte order. */
+
+u_int32_t wrapsum (sum)
+ u_int32_t sum;
+{
+#ifdef DEBUG_CHECKSUM
+ log_debug ("wrapsum (%x)", sum);
+#endif
+
+ sum = ~sum & 0xFFFF;
+#ifdef DEBUG_CHECKSUM_VERBOSE
+ log_debug ("sum = %x", sum);
+#endif
+
+#ifdef DEBUG_CHECKSUM
+ log_debug ("wrapsum returns %x", htons (sum));
+#endif
+ return htons(sum);
+}
+
+#ifdef PACKET_ASSEMBLY
+void assemble_hw_header (interface, buf, bufix, to)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned *bufix;
+ struct hardware *to;
+{
+#if defined (HAVE_TR_SUPPORT)
+ if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
+ assemble_tr_header (interface, buf, bufix, to);
+ else
+#endif
+#if defined (DEC_FDDI)
+ if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
+ assemble_fddi_header (interface, buf, bufix, to);
+ else
+#endif
+ assemble_ethernet_header (interface, buf, bufix, to);
+
+}
+
+/* UDP header and IP header assembled together for convenience. */
+
+void assemble_udp_ip_header (interface, buf, bufix,
+ from, to, port, data, len)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned *bufix;
+ u_int32_t from;
+ u_int32_t to;
+ u_int32_t port;
+ unsigned char *data;
+ unsigned len;
+{
+ struct ip ip;
+ struct udphdr udp;
+
+ memset (&ip, 0, sizeof ip);
+
+ /* Fill out the IP header */
+ IP_V_SET (&ip, 4);
+ IP_HL_SET (&ip, 20);
+ 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 = 128;
+ ip.ip_p = IPPROTO_UDP;
+ ip.ip_sum = 0;
+ ip.ip_src.s_addr = from;
+ ip.ip_dst.s_addr = to;
+
+ /* Checksum the IP header... */
+ ip.ip_sum = wrapsum (checksum ((unsigned char *)&ip, sizeof ip, 0));
+
+ /* Copy the ip header into the buffer... */
+ memcpy (&buf [*bufix], &ip, sizeof ip);
+ *bufix += sizeof ip;
+
+ /* Fill out the UDP header */
+ udp.uh_sport = local_port; /* XXX */
+ udp.uh_dport = port; /* XXX */
+ udp.uh_ulen = htons(sizeof(udp) + len);
+ memset (&udp.uh_sum, 0, sizeof udp.uh_sum);
+
+ /* Compute UDP checksums, including the ``pseudo-header'', the UDP
+ header and the data. */
+
+ 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)))));
+
+ /* Copy the udp header into the buffer... */
+ memcpy (&buf [*bufix], &udp, sizeof udp);
+ *bufix += sizeof udp;
+}
+#endif /* PACKET_ASSEMBLY */
+
+#ifdef PACKET_DECODING
+/* Decode a hardware header... */
+/* XXX currently only supports ethernet; doesn't check for other types. */
+
+ssize_t decode_hw_header (interface, buf, bufix, from)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned bufix;
+ struct hardware *from;
+{
+#if defined (HAVE_TR_SUPPORT)
+ if (interface -> hw_address.hbuf [0] == HTYPE_IEEE802)
+ return decode_tr_header (interface, buf, bufix, from);
+ else
+#endif
+#if defined (DEC_FDDI)
+ if (interface -> hw_address.hbuf [0] == HTYPE_FDDI)
+ return decode_fddi_header (interface, buf, bufix, from);
+ else
+#endif
+ return decode_ethernet_header (interface, buf, bufix, from);
+}
+
+/* UDP header and IP header decoded together for convenience. */
+
+ssize_t
+decode_udp_ip_header(struct interface_info *interface,
+ unsigned char *buf, unsigned bufix,
+ struct sockaddr_in *from, unsigned buflen,
+ unsigned *rbuflen)
+{
+ unsigned char *data;
+ struct ip ip;
+ struct udphdr udp;
+ unsigned char *upp, *endbuf;
+ u_int32_t ip_len, ulen, pkt_len;
+ 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;
+ unsigned len;
+
+ /* Designate the end of the input buffer for bounds checks. */
+ endbuf = buf + bufix + buflen;
+
+ /* Assure there is at least an IP header there. */
+ if ((buf + bufix + sizeof(ip)) > endbuf)
+ return -1;
+
+ /* Copy the IP header into a stack aligned structure for inspection.
+ * There may be bits in the IP header that we're not decoding, so we
+ * copy out the bits we grok and skip ahead by ip.ip_hl * 4.
+ */
+ upp = buf + bufix;
+ memcpy(&ip, upp, sizeof(ip));
+ ip_len = (*upp & 0x0f) << 2;
+ upp += ip_len;
+
+ /* Check the IP packet length. */
+ pkt_len = ntohs(ip.ip_len);
+ if (pkt_len > buflen)
+ return -1;
+
+ /* Assure after ip_len bytes that there is enough room for a UDP header. */
+ if ((upp + sizeof(udp)) > endbuf)
+ return -1;
+
+ /* Copy the UDP header into a stack aligned structure for inspection. */
+ memcpy(&udp, upp, sizeof(udp));
+
+#ifdef USERLAND_FILTER
+ /* Is it a UDP packet? */
+ if (ip.ip_p != IPPROTO_UDP)
+ return -1;
+
+ /* Is it to the port we're serving? */
+ if (udp.uh_dport != local_port)
+ return -1;
+#endif /* USERLAND_FILTER */
+
+ ulen = ntohs(udp.uh_ulen);
+ if (ulen < sizeof(udp))
+ return -1;
+
+ udp_packets_length_checked++;
+ if ((upp + ulen) > endbuf) {
+ udp_packets_length_overflow++;
+ if ((udp_packets_length_checked > 4) &&
+ ((udp_packets_length_checked /
+ udp_packets_length_overflow) < 2)) {
+ log_info("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
+ udp_packets_length_overflow = 0;
+ udp_packets_length_checked = 0;
+ }
+ return -1;
+ }
+
+ if ((ulen < sizeof(udp)) || ((upp + ulen) > endbuf))
+ return -1;
+
+ /* Check the IP header checksum - it should be zero. */
+ ++ip_packets_seen;
+ if (wrapsum (checksum (buf + bufix, ip_len, 0))) {
+ ++ip_packets_bad_checksum;
+ if (ip_packets_seen > 4 &&
+ (ip_packets_seen / ip_packets_bad_checksum) < 2) {
+ log_info ("%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;
+ }
+
+ /* Copy out the IP source address... */
+ 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. */
+
+ data = upp + sizeof(udp);
+ len = ulen - sizeof(udp);
+
+ usum = udp.uh_sum;
+ udp.uh_sum = 0;
+
+ /* XXX: We have to pass &udp, because we have to zero the checksum
+ * field before calculating the sum...'upp' isn't zeroed.
+ */
+ sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
+ checksum(data, len,
+ checksum((unsigned char *)&ip.ip_src,
+ 8, IPPROTO_UDP + 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) {
+ log_info ("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return -1;
+ }
+
+ /* Copy out the port... */
+ memcpy (&from -> sin_port, &udp.uh_sport, sizeof udp.uh_sport);
+
+ /* Save the length of the UDP payload. */
+ if (rbuflen != NULL)
+ *rbuflen = len;
+
+ /* Return the index to the UDP payload. */
+ return ip_len + sizeof udp;
+}
+#endif /* PACKET_DECODING */
diff --git a/common/parse.c b/common/parse.c
new file mode 100644
index 0000000..049ce7b
--- /dev/null
+++ b/common/parse.c
@@ -0,0 +1,5898 @@
+/* parse.c
+
+ Common parser code for dhcpd and dhclient. */
+
+/*
+ * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <syslog.h>
+
+/* Enumerations can be specified in option formats, and are used for
+ parsing, so we define the routines that manage them here. */
+
+struct enumeration *enumerations;
+
+void add_enumeration (struct enumeration *enumeration)
+{
+ enumeration -> next = enumerations;
+ enumerations = enumeration;
+}
+
+struct enumeration *find_enumeration (const char *name, int length)
+{
+ struct enumeration *e;
+
+ for (e = enumerations; e; e = e -> next)
+ if (strlen (e -> name) == length &&
+ !memcmp (e -> name, name, (unsigned)length))
+ return e;
+ return (struct enumeration *)0;
+}
+
+struct enumeration_value *find_enumeration_value (const char *name,
+ int length,
+ unsigned *widthp,
+ const char *value)
+{
+ struct enumeration *e;
+ int i;
+
+ e = find_enumeration (name, length);
+ if (e) {
+ if (widthp != NULL)
+ *widthp = e->width;
+ for (i = 0; e -> values [i].name; i++) {
+ if (!strcmp (value, e -> values [i].name))
+ return &e -> values [i];
+ }
+ }
+ return (struct enumeration_value *)0;
+}
+
+/* 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 (cfile)
+ struct parse *cfile;
+{
+ skip_to_rbrace (cfile, 0);
+}
+
+void skip_to_rbrace (cfile, brace_count)
+ struct parse *cfile;
+ int brace_count;
+{
+ enum dhcp_token token;
+ const char *val;
+
+#if defined (DEBUG_TOKEN)
+ log_error ("skip_to_rbrace: %d\n", brace_count);
+#endif
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (brace_count) {
+ if (!--brace_count)
+ return;
+ } else
+ return;
+ } else if (token == LBRACE) {
+ brace_count++;
+ } else if (token == SEMI && !brace_count) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ return;
+ } else if (token == EOL) {
+ /* 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, (unsigned *)0, cfile);
+ return;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token != END_OF_FILE);
+}
+
+int parse_semi (cfile)
+ struct parse *cfile;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return 1;
+}
+
+/* string-parameter :== STRING SEMI */
+
+int parse_string (cfile, sptr, lptr)
+ struct parse *cfile;
+ char **sptr;
+ unsigned *lptr;
+{
+ const char *val;
+ enum dhcp_token token;
+ char *s;
+ unsigned len;
+
+ token = next_token (&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting a string");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ s = (char *)dmalloc (len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for string %s.", val);
+ memcpy (s, val, len + 1);
+
+ if (!parse_semi (cfile)) {
+ dfree (s, MDL);
+ return 0;
+ }
+ if (sptr)
+ *sptr = s;
+ else
+ dfree (s, MDL);
+ if (lptr)
+ *lptr = len;
+ return 1;
+}
+
+/*
+ * hostname :== IDENTIFIER
+ * | IDENTIFIER DOT
+ * | hostname DOT IDENTIFIER
+ */
+
+char *parse_host_name (cfile)
+ struct parse *cfile;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned len = 0;
+ char *s;
+ char *t;
+ pair c = (pair)0;
+ int ltid = 0;
+
+ /* Read a dotted hostname... */
+ do {
+ /* Read a token, which should be an identifier. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER)
+ break;
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Store this identifier... */
+ if (!(s = (char *)dmalloc (strlen (val) + 1, MDL)))
+ log_fatal ("can't allocate temp space for hostname.");
+ strcpy (s, val);
+ c = cons ((caddr_t)s, c);
+ len += strlen (s) + 1;
+ /* Look for a dot; if it's there, keep going, otherwise
+ we're done. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == DOT) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ ltid = 1;
+ } else
+ ltid = 0;
+ } while (token == DOT);
+
+ /* Should be at least one token. */
+ if (!len)
+ return (char *)0;
+
+ /* Assemble the hostname together into a string. */
+ if (!(s = (char *)dmalloc (len + ltid, MDL)))
+ log_fatal ("can't allocate space for hostname.");
+ t = s + len + ltid;
+ *--t = 0;
+ if (ltid)
+ *--t = '.';
+ while (c) {
+ pair cdr = c -> cdr;
+ unsigned l = strlen ((char *)(c -> car));
+ t -= l;
+ memcpy (t, (char *)(c -> car), l);
+ /* Free up temp space. */
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
+ c = cdr;
+ if (t != s)
+ *--t = '.';
+ }
+ return s;
+}
+
+/* ip-addr-or-hostname :== ip-address | hostname
+ ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+
+ Parse an ip address or a hostname. If uniform is zero, put in
+ an expr_substring node to limit hostnames that evaluate to more
+ than one IP address.
+
+ Note that RFC1123 permits hostnames to consist of all digits,
+ making it difficult to quickly disambiguate them from ip addresses.
+*/
+
+int parse_ip_addr_or_hostname (expr, cfile, uniform)
+ struct expression **expr;
+ struct parse *cfile;
+ int uniform;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char addr [4];
+ unsigned len = sizeof addr;
+ char *name;
+ struct expression *x = (struct expression *)0;
+ int ipaddr = 0;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+
+ if (token == NUMBER) {
+ /*
+ * a hostname may be numeric, but domain names must
+ * start with a letter, so we can disambiguate by
+ * looking ahead a few tokens. we save the parse
+ * context first, and restore it after we know what
+ * we're dealing with.
+ */
+ save_parse_state(cfile);
+ (void) next_token(NULL, NULL, cfile);
+ if (next_token(NULL, NULL, cfile) == DOT &&
+ next_token(NULL, NULL, cfile) == NUMBER)
+ ipaddr = 1;
+ restore_parse_state(cfile);
+
+ if (ipaddr &&
+ parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return make_const_data (expr, addr, len, 0, 1, MDL);
+
+ }
+
+ if (is_identifier (token) || token == NUMBER) {
+ name = parse_host_name (cfile);
+ if (!name)
+ return 0;
+ if (!make_host_lookup (expr, name)) {
+ dfree(name, MDL);
+ return 0;
+ }
+ dfree(name, MDL);
+ if (!uniform) {
+ if (!make_limit (&x, *expr, 4))
+ return 0;
+ expression_dereference (expr, MDL);
+ *expr = x;
+ }
+ } else {
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "%s (%d): expecting IP address or hostname",
+ val, token);
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ip-address :== NUMBER DOT NUMBER DOT NUMBER DOT NUMBER
+ */
+
+int parse_ip_addr (cfile, addr)
+ struct parse *cfile;
+ struct iaddr *addr;
+{
+ addr -> len = 4;
+ if (parse_numeric_aggregate (cfile, addr -> iabuf,
+ &addr -> len, DOT, 10, 8))
+ return 1;
+ return 0;
+}
+
+/*
+ * Return true if every character in the string is hexadecimal.
+ */
+static int
+is_hex_string(const char *s) {
+ while (*s != '\0') {
+ if (!isxdigit((int)*s)) {
+ return 0;
+ }
+ s++;
+ }
+ return 1;
+}
+
+/*
+ * ip-address6 :== (complicated set of rules)
+ *
+ * See section 2.2 of RFC 1884 for details.
+ *
+ * We are lazy for this. We pull numbers, names, colons, and dots
+ * together and then throw the resulting string at the inet_pton()
+ * function.
+ */
+
+int
+parse_ip6_addr(struct parse *cfile, struct iaddr *addr) {
+ enum dhcp_token token;
+ const char *val;
+ int val_len;
+
+ char v6[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ int v6_len;
+
+ /*
+ * First token is non-raw. This way we eat any whitespace before
+ * our IPv6 address begins, like one would expect.
+ */
+ token = peek_token(&val, NULL, cfile);
+
+ /*
+ * Gather symbols.
+ */
+ v6_len = 0;
+ for (;;) {
+ if ((((token == NAME) || (token == NUMBER_OR_NAME)) &&
+ is_hex_string(val)) ||
+ (token == NUMBER) ||
+ (token == DOT) ||
+ (token == COLON)) {
+
+ next_raw_token(&val, NULL, cfile);
+ val_len = strlen(val);
+ if ((v6_len + val_len) >= sizeof(v6)) {
+ parse_warn(cfile, "Invalid IPv6 address.");
+ skip_to_semi(cfile);
+ return 0;
+ }
+ memcpy(v6+v6_len, val, val_len);
+ v6_len += val_len;
+
+ } else {
+ break;
+ }
+ token = peek_raw_token(&val, NULL, cfile);
+ }
+ v6[v6_len] = '\0';
+
+ /*
+ * Use inet_pton() for actual work.
+ */
+ if (inet_pton(AF_INET6, v6, addr->iabuf) <= 0) {
+ parse_warn(cfile, "Invalid IPv6 address.");
+ skip_to_semi(cfile);
+ return 0;
+ }
+ addr->len = 16;
+ return 1;
+}
+
+/*
+ * Same as parse_ip6_addr() above, but returns the value in the
+ * expression rather than in an address structure.
+ */
+int
+parse_ip6_addr_expr(struct expression **expr,
+ struct parse *cfile) {
+ struct iaddr addr;
+
+ if (!parse_ip6_addr(cfile, &addr)) {
+ return 0;
+ }
+ return make_const_data(expr, addr.iabuf, addr.len, 0, 1, MDL);
+}
+
+/*
+ * ip6-prefix :== ip6-address "/" NUMBER
+ */
+int
+parse_ip6_prefix(struct parse *cfile, struct iaddr *addr, u_int8_t *plen) {
+ enum dhcp_token token;
+ const char *val;
+ int n;
+
+ if (!parse_ip6_addr(cfile, addr)) {
+ return 0;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != SLASH) {
+ parse_warn(cfile, "Slash expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return 0;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "Number expected.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return 0;
+ }
+ n = atoi(val);
+ if ((n < 0) || (n > 128)) {
+ parse_warn(cfile, "Invalid IPv6 prefix length.");
+ skip_to_semi(cfile);
+ return 0;
+ }
+ if (!is_cidr_mask_valid(addr, n)) {
+ parse_warn(cfile, "network mask too short.");
+ skip_to_semi(cfile);
+ return 0;
+ }
+ *plen = n;
+ return 1;
+}
+
+/*
+ * ip-address-with-subnet :== ip-address |
+ * ip-address "/" NUMBER
+ */
+
+int
+parse_ip_addr_with_subnet(cfile, match)
+ struct parse *cfile;
+ struct iaddrmatch *match;
+{
+ const char *val, *orig;
+ enum dhcp_token token;
+ int prefixlen;
+ int fflen;
+ unsigned char newval, warnmask=0;
+
+ if (parse_ip_addr(cfile, &match->addr)) {
+ /* default to host mask */
+ prefixlen = match->addr.len * 8;
+
+ token = peek_token(&val, NULL, cfile);
+
+ if (token == SLASH) {
+ next_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+
+ if (token != NUMBER) {
+ parse_warn(cfile, "Invalid CIDR prefix length:"
+ " expecting a number.");
+ return 0;
+ }
+
+ prefixlen = atoi(val);
+
+ if (prefixlen < 0 ||
+ prefixlen > (match->addr.len * 8)) {
+ parse_warn(cfile, "subnet prefix is out of "
+ "range [0..%d].",
+ match->addr.len * 8);
+ return 0;
+ }
+ }
+
+ /* construct a suitable mask field */
+
+ /* copy length */
+ match->mask.len = match->addr.len;
+
+ /* count of 0xff bytes in mask */
+ fflen = prefixlen / 8;
+
+ /* set leading mask */
+ memset(match->mask.iabuf, 0xff, fflen);
+
+ /* set zeroes */
+ if (fflen < match->mask.len) {
+ match->mask.iabuf[fflen] =
+ "\x00\x80\xc0\xe0\xf0\xf8\xfc\xfe"[prefixlen % 8];
+
+ memset(match->mask.iabuf+fflen+1, 0x00,
+ match->mask.len - fflen - 1);
+
+ /* AND-out insignificant bits from supplied netmask. */
+ orig = piaddr(match->addr);
+ do {
+ newval = match->addr.iabuf[fflen] &
+ match->mask.iabuf[fflen];
+
+ if (newval != match->addr.iabuf[fflen]) {
+ warnmask = 1;
+ match->addr.iabuf[fflen] = newval;
+ }
+ } while (++fflen < match->mask.len);
+
+ if (warnmask) {
+ log_error("Warning: Extraneous bits removed "
+ "in address component of %s/%d.",
+ orig, prefixlen);
+ log_error("New value: %s/%d.",
+ piaddr(match->addr), prefixlen);
+ }
+ }
+
+ return 1;
+ }
+
+ parse_warn(cfile,
+ "expecting ip-address or ip-address/prefixlen");
+
+ return 0; /* let caller pick up pieces */
+}
+
+/*
+ * hardware-parameter :== HARDWARE hardware-type colon-separated-hex-list SEMI
+ * hardware-type :== ETHERNET | TOKEN_RING | TOKEN_FDDI
+ */
+
+void parse_hardware_param (cfile, hardware)
+ struct parse *cfile;
+ struct hardware *hardware;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned hlen;
+ unsigned char *t;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case ETHERNET:
+ hardware -> hbuf [0] = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ hardware -> hbuf [0] = HTYPE_IEEE802;
+ break;
+ case TOKEN_FDDI:
+ hardware -> hbuf [0] = HTYPE_FDDI;
+ break;
+ default:
+ if (!strncmp (val, "unknown-", 8)) {
+ hardware -> hbuf [0] = atoi (&val [8]);
+ } else {
+ parse_warn (cfile,
+ "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;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ hardware -> hlen = 1;
+ goto out;
+ }
+ t = parse_numeric_aggregate (cfile, (unsigned char *)0, &hlen,
+ COLON, 16, 8);
+ if (!t) {
+ hardware -> hlen = 1;
+ return;
+ }
+ if (hlen + 1 > sizeof hardware -> hbuf) {
+ dfree (t, MDL);
+ parse_warn (cfile, "hardware address too long");
+ } else {
+ hardware -> hlen = hlen + 1;
+ memcpy ((unsigned char *)&hardware -> hbuf [1], t, hlen);
+ if (hlen + 1 < sizeof hardware -> hbuf)
+ memset (&hardware -> hbuf [hlen + 1], 0,
+ (sizeof hardware -> hbuf) - hlen - 1);
+ dfree (t, MDL);
+ }
+
+ out:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ }
+}
+
+/* lease-time :== NUMBER SEMI */
+
+void parse_lease_time (cfile, timep)
+ struct parse *cfile;
+ TIME *timep;
+{
+ const char *val;
+ enum dhcp_token token;
+ u_int32_t num;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "Expecting numeric lease time");
+ skip_to_semi (cfile);
+ return;
+ }
+ convert_num(cfile, (unsigned char *)&num, val, 10, 32);
+ /* Unswap the number - convert_num returns stuff in NBO. */
+ *timep = ntohl(num);
+
+ 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 (cfile, buf,
+ max, separator, base, size)
+ struct parse *cfile;
+ unsigned char *buf;
+ unsigned *max;
+ int separator;
+ int base;
+ unsigned size;
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char *bufp = buf, *s, *t;
+ unsigned count = 0;
+ pair c = (pair)0;
+
+ if (!bufp && *max) {
+ bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
+ if (!bufp)
+ log_fatal ("no space for numeric aggregate");
+ s = 0;
+ } else
+ s = bufp;
+
+ do {
+ if (count) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != separator) {
+ if (!*max)
+ break;
+ if (token != RBRACE && token != LBRACE)
+ token = next_token (&val,
+ (unsigned *)0,
+ cfile);
+ parse_warn (cfile, "too few numbers.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return (unsigned char *)0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == END_OF_FILE) {
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ }
+
+ /* Allow NUMBER_OR_NAME if base is 16. */
+ if (token != NUMBER &&
+ (base != 16 || token != NUMBER_OR_NAME)) {
+ parse_warn (cfile, "expecting numeric value.");
+ skip_to_semi (cfile);
+ return (unsigned char *)0;
+ }
+ /* If we can, convert the number now; otherwise, build
+ a linked list of all the numbers. */
+ if (s) {
+ convert_num (cfile, s, val, base, size);
+ s += size / 8;
+ } else {
+ t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
+ if (!t)
+ log_fatal ("no temp space for number.");
+ strcpy ((char *)t, val);
+ c = cons ((caddr_t)t, c);
+ }
+ } while (++count != *max);
+
+ /* If we had to cons up a list, convert it now. */
+ if (c) {
+ bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
+ if (!bufp)
+ log_fatal ("no space for numeric aggregate.");
+ s = bufp + count - size / 8;
+ *max = count;
+ }
+ while (c) {
+ pair cdr = c -> cdr;
+ convert_num (cfile, s, (char *)(c -> car), base, size);
+ s -= size / 8;
+ /* Free up temp space. */
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
+ c = cdr;
+ }
+ return bufp;
+}
+
+void convert_num (cfile, buf, str, base, size)
+ struct parse *cfile;
+ unsigned char *buf;
+ const char *str;
+ int base;
+ unsigned size;
+{
+ const unsigned char *ptr = (const unsigned char *)str;
+ int negative = 0;
+ u_int32_t val = 0;
+ int tval;
+ int max;
+
+ 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 {
+ parse_warn (cfile, "Bogus number: %s.", str);
+ break;
+ }
+ if (tval >= base) {
+ parse_warn (cfile,
+ "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:
+ parse_warn (cfile,
+ "%s%lo exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ case 16:
+ parse_warn (cfile,
+ "%s%lx exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ default:
+ parse_warn (cfile,
+ "%s%lu exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
+ break;
+ }
+ }
+
+ if (negative) {
+ switch (size) {
+ case 8:
+ *buf = -(unsigned long)val;
+ break;
+ case 16:
+ putShort (buf, -(long)val);
+ break;
+ case 32:
+ putLong (buf, -(long)val);
+ break;
+ default:
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", 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:
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", size);
+ break;
+ }
+ }
+}
+
+/*
+ * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER |
+ * NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER NUMBER |
+ * EPOCH NUMBER |
+ * NEVER
+ *
+ * Dates are stored in UTC or with a timezone offset; first number is day
+ * of week; next is year/month/day; next is hours:minutes:seconds on a
+ * 24-hour clock, followed by the timezone offset in seconds, which is
+ * optional.
+ */
+
+/*
+ * just parse the date
+ * any trailing semi must be consumed by the caller of this routine
+ */
+TIME
+parse_date_core(cfile)
+ struct parse *cfile;
+{
+ int guess;
+ int tzoff, wday, year, mon, mday, hour, min, sec;
+ const char *val;
+ enum dhcp_token token;
+ static int months[11] = { 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+
+ /* "never", "epoch" or day of week */
+ token = peek_token(&val, NULL, cfile);
+ if (token == NEVER) {
+ token = next_token(&val, NULL, cfile); /* consume NEVER */
+ return(MAX_TIME);
+ }
+
+ /* This indicates 'local' time format. */
+ if (token == EPOCH) {
+ token = next_token(&val, NULL, cfile); /* consume EPOCH */
+ token = peek_token(&val, NULL, cfile);
+
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "Seconds since epoch expected.");
+ return((TIME)0);
+ }
+
+ token = next_token(&val, NULL, cfile); /* consume number */
+ guess = atoi(val);
+
+ return((TIME)guess);
+ }
+
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric day of week expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume day of week */
+ wday = atoi(val);
+
+ /* Year... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric year expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume year */
+
+ /* Note: the following is not a Y2K bug - it's a Y1.9K bug. Until
+ somebody invents a time machine, I think we can safely disregard
+ it. This actually works around a stupid Y2K bug that was present
+ in a very early beta release of dhcpd. */
+ year = atoi(val);
+ if (year > 1900)
+ year -= 1900;
+
+ /* Slash separating year from month... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != SLASH) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile,
+ "expected slash separating year from month.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume SLASH */
+
+ /* Month... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric month expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume month */
+ mon = atoi(val) - 1;
+
+ /* Slash separating month from day... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != SLASH) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile,
+ "expected slash separating month from day.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume SLASH */
+
+ /* Day of month... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric day of month expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume day of month */
+ mday = atoi(val);
+
+ /* Hour... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric hour expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume hour */
+ hour = atoi(val);
+
+ /* Colon separating hour from minute... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != COLON) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile,
+ "expected colon separating hour from minute.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume colon */
+
+ /* Minute... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric minute expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume minute */
+ min = atoi(val);
+
+ /* Colon separating minute from second... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != COLON) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile,
+ "expected colon separating minute from second.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume colon */
+
+ /* Second... */
+ token = peek_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ if (token != SEMI)
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile, "numeric second expected.");
+ return((TIME)0);
+ }
+ token = next_token(&val, NULL, cfile); /* consume second */
+ sec = atoi(val);
+
+ tzoff = 0;
+ token = peek_token(&val, NULL, cfile);
+ if (token == NUMBER) {
+ token = next_token(&val, NULL, cfile); /* consume tzoff */
+ tzoff = atoi(val);
+ } else if (token != SEMI) {
+ token = next_token(&val, NULL, cfile);
+ parse_warn(cfile,
+ "Time zone offset or semicolon expected.");
+ return((TIME)0);
+ }
+
+ /* Guess the time value... */
+ guess = ((((((365 * (year - 70) + /* Days in years since '70 */
+ (year - 69) / 4 + /* Leap days since '70 */
+ (mon /* Days in months this year */
+ ? months [mon - 1]
+ : 0) +
+ (mon > 1 && /* Leap day this year */
+ !((year - 72) & 3)) +
+ mday - 1) * 24) + /* Day of month */
+ hour) * 60) +
+ min) * 60) + sec + tzoff;
+
+ /* 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((TIME)guess);
+}
+
+/*
+ * Wrapper to consume the semicolon after the date
+ * :== date semi
+ */
+
+TIME
+parse_date(cfile)
+ struct parse *cfile;
+{
+ TIME guess;
+ guess = parse_date_core(cfile);
+
+ /* Make sure the date ends in a semicolon... */
+ if (!parse_semi(cfile))
+ return((TIME)0);
+ return(guess);
+}
+
+
+
+/*
+ * option-name :== IDENTIFIER |
+ IDENTIFIER . IDENTIFIER
+ */
+
+isc_result_t
+parse_option_name (cfile, allocate, known, opt)
+ struct parse *cfile;
+ int allocate;
+ int *known;
+ struct option **opt;
+{
+ const char *val;
+ enum dhcp_token token;
+ char *uname;
+ struct universe *universe;
+ struct option *option;
+ unsigned code;
+
+ if (opt == NULL)
+ return DHCP_R_INVALIDARG;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting identifier after option keyword.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return DHCP_R_BADPARSE;
+ }
+ uname = dmalloc (strlen (val) + 1, MDL);
+ if (!uname)
+ log_fatal ("no memory for uname information.");
+ strcpy (uname, val);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == DOT) {
+ /* Go ahead and take the DOT token... */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* The next token should be an identifier... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "expecting identifier after '.'");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return DHCP_R_BADPARSE;
+ }
+
+ /* Look up the option name hash table for the specified
+ uname. */
+ universe = (struct universe *)0;
+ if (!universe_hash_lookup (&universe, universe_hash,
+ uname, 0, MDL)) {
+ parse_warn (cfile, "no option space named %s.", uname);
+ skip_to_semi (cfile);
+ return ISC_R_NOTFOUND;
+ }
+ } else {
+ /* Use the default hash table, which contains all the
+ standard dhcp option names. */
+ val = uname;
+ universe = &dhcp_universe;
+ }
+
+ /* Look up the actual option info... */
+ option_name_hash_lookup(opt, universe->name_hash, val, 0, MDL);
+ option = *opt;
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (option) {
+ if (known)
+ *known = 1;
+ /* If the option name is of the form unknown-[decimal], use
+ * the trailing decimal value to find the option definition.
+ * If there is no definition, construct one. This is to
+ * support legacy use of unknown options in config files or
+ * lease databases.
+ */
+ } else if (strncasecmp(val, "unknown-", 8) == 0) {
+ code = atoi(val+8);
+
+ /* Option code 0 is always illegal for us, thanks
+ * to the option decoder.
+ */
+ if (code == 0 || code == universe->end) {
+ parse_warn(cfile, "Option codes 0 and %u are illegal "
+ "in the %s space.", universe->end,
+ universe->name);
+ skip_to_semi(cfile);
+ dfree(uname, MDL);
+ return ISC_R_FAILURE;
+ }
+
+ /* It's odd to think of unknown option codes as
+ * being known, but this means we know what the
+ * parsed name is talking about.
+ */
+ if (known)
+ *known = 1;
+
+ option_code_hash_lookup(opt, universe->code_hash,
+ &code, 0, MDL);
+ option = *opt;
+
+ /* If we did not find an option of that code,
+ * manufacture an unknown-xxx option definition.
+ * Its single reference will ensure that it is
+ * deleted once the option is recycled out of
+ * existence (by the parent).
+ */
+ if (option == NULL) {
+ option = new_option(val, MDL);
+ option->universe = universe;
+ option->code = code;
+ option->format = default_option_format;
+ option_reference(opt, option, MDL);
+ } else
+ log_info("option %s has been redefined as option %s. "
+ "Please update your configs if neccessary.",
+ val, option->name);
+ /* If we've been told to allocate, that means that this
+ * (might) be an option code definition, so we'll create
+ * an option structure and return it for the parent to
+ * decide.
+ */
+ } else if (allocate) {
+ option = new_option(val, MDL);
+ option -> universe = universe;
+ option_reference(opt, option, MDL);
+ } else {
+ parse_warn(cfile, "no option named %s in space %s",
+ val, universe->name);
+ skip_to_semi (cfile);
+ dfree(uname, MDL);
+ return ISC_R_NOTFOUND;
+ }
+
+ /* Free the initial identifier token. */
+ dfree (uname, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/* IDENTIFIER [WIDTHS] SEMI
+ * WIDTHS ~= LENGTH WIDTH NUMBER
+ * CODE WIDTH NUMBER
+ */
+
+void parse_option_space_decl (cfile)
+ struct parse *cfile;
+{
+ int token;
+ const char *val;
+ struct universe **ua, *nu;
+ char *nu_name;
+ int tsize=1, lsize=1, hsize = 0;
+
+ next_token (&val, (unsigned *)0, cfile); /* Discard the SPACE token,
+ which was checked by the
+ caller. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile, "expecting identifier.");
+ skip_to_semi (cfile);
+ return;
+ }
+ nu = new_universe (MDL);
+ if (!nu)
+ log_fatal ("No memory for new option space.");
+
+ /* Set up the server option universe... */
+ nu_name = dmalloc (strlen (val) + 1, MDL);
+ if (!nu_name)
+ log_fatal ("No memory for new option space name.");
+ strcpy (nu_name, val);
+ nu -> name = nu_name;
+
+ do {
+ token = next_token(&val, NULL, cfile);
+ switch(token) {
+ case SEMI:
+ break;
+
+ case CODE:
+ token = next_token(&val, NULL, cfile);
+ if (token != WIDTH) {
+ parse_warn(cfile, "expecting width token.");
+ goto bad;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting number 1, 2, 4.");
+ goto bad;
+ }
+
+ tsize = atoi(val);
+
+
+ switch (tsize) {
+ case 1:
+ if (!hsize)
+ hsize = BYTE_NAME_HASH_SIZE;
+ break;
+ case 2:
+ if (!hsize)
+ hsize = WORD_NAME_HASH_SIZE;
+ break;
+ case 4:
+ if (!hsize)
+ hsize = QUAD_NAME_HASH_SIZE;
+ break;
+ default:
+ parse_warn(cfile, "invalid code width (%d), "
+ "expecting a 1, 2 or 4.",
+ tsize);
+ goto bad;
+ }
+ break;
+
+ case LENGTH:
+ token = next_token(&val, NULL, cfile);
+ if (token != WIDTH) {
+ parse_warn(cfile, "expecting width token.");
+ goto bad;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting number 1 or 2.");
+ goto bad;
+ }
+
+ lsize = atoi(val);
+ if (lsize != 1 && lsize != 2) {
+ parse_warn(cfile, "invalid length width (%d) "
+ "expecting 1 or 2.", lsize);
+ goto bad;
+ }
+
+ break;
+
+ case HASH:
+ token = next_token(&val, NULL, cfile);
+ if (token != SIZE) {
+ parse_warn(cfile, "expecting size token.");
+ goto bad;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting a 10base number");
+ goto bad;
+ }
+
+ /* (2^31)-1 is the highest Mersenne prime we should
+ * probably allow...
+ */
+ hsize = atoi(val);
+ if (hsize < 0 || hsize > 0x7FFFFFFF) {
+ parse_warn(cfile, "invalid hash length: %d",
+ hsize);
+ goto bad;
+ }
+
+ break;
+
+ default:
+ parse_warn(cfile, "Unexpected token.");
+ }
+ } while (token != SEMI);
+
+ if (!hsize)
+ hsize = DEFAULT_SPACE_HASH_SIZE;
+
+ nu -> lookup_func = lookup_hashed_option;
+ nu -> option_state_dereference = hashed_option_state_dereference;
+ nu -> foreach = hashed_option_space_foreach;
+ nu -> save_func = save_hashed_option;
+ nu -> delete_func = delete_hashed_option;
+ nu -> encapsulate = hashed_option_space_encapsulate;
+ nu -> decode = parse_option_buffer;
+ nu -> length_size = lsize;
+ nu -> tag_size = tsize;
+ switch(tsize) {
+ case 1:
+ nu->get_tag = getUChar;
+ nu->store_tag = putUChar;
+ break;
+ case 2:
+ nu->get_tag = getUShort;
+ nu->store_tag = putUShort;
+ break;
+ case 4:
+ nu->get_tag = getULong;
+ nu->store_tag = putULong;
+ break;
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ switch(lsize) {
+ case 0:
+ nu->get_length = NULL;
+ nu->store_length = NULL;
+ break;
+ case 1:
+ nu->get_length = getUChar;
+ nu->store_length = putUChar;
+ break;
+ case 2:
+ nu->get_length = getUShort;
+ nu->store_length = putUShort;
+ break;
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ nu -> index = universe_count++;
+ if (nu -> index >= universe_max) {
+ ua = dmalloc (universe_max * 2 * sizeof *ua, MDL);
+ if (!ua)
+ log_fatal ("No memory to expand option space array.");
+ memcpy (ua, universes, universe_max * sizeof *ua);
+ universe_max *= 2;
+ dfree (universes, MDL);
+ universes = ua;
+ }
+ universes [nu -> index] = nu;
+ if (!option_name_new_hash(&nu->name_hash, hsize, MDL) ||
+ !option_code_new_hash(&nu->code_hash, hsize, MDL))
+ log_fatal("Can't allocate %s option hash table.", nu->name);
+ universe_hash_add (universe_hash, nu -> name, 0, nu, MDL);
+ return;
+
+ bad:
+ dfree(nu_name, MDL);
+ dfree(nu, MDL);
+}
+
+/* This is faked up to look good right now. Ideally, this should do a
+ recursive parse and allow arbitrary data structure definitions, but for
+ now it just allows you to specify a single type, an array of single types,
+ a sequence of types, or an array of sequences of types.
+
+ ocd :== NUMBER EQUALS ocsd SEMI
+
+ ocsd :== ocsd_type |
+ ocsd_type_sequence |
+ ARRAY OF ocsd_simple_type_sequence
+
+ ocsd_type_sequence :== LBRACE ocsd_types RBRACE
+
+ ocsd_simple_type_sequence :== LBRACE ocsd_simple_types RBRACE
+
+ ocsd_types :== ocsd_type |
+ ocsd_types ocsd_type
+
+ ocsd_type :== ocsd_simple_type |
+ ARRAY OF ocsd_simple_type
+
+ ocsd_simple_types :== ocsd_simple_type |
+ ocsd_simple_types ocsd_simple_type
+
+ ocsd_simple_type :== BOOLEAN |
+ INTEGER NUMBER |
+ SIGNED INTEGER NUMBER |
+ UNSIGNED INTEGER NUMBER |
+ IP-ADDRESS |
+ TEXT |
+ STRING |
+ ENCAPSULATE identifier */
+
+int parse_option_code_definition (cfile, option)
+ struct parse *cfile;
+ struct option *option;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct option *oldopt;
+ unsigned arrayp = 0;
+ int recordp = 0;
+ int no_more_in_record = 0;
+ char tokbuf [128];
+ unsigned tokix = 0;
+ char type;
+ int is_signed;
+ char *s;
+ int has_encapsulation = 0;
+ struct universe *encapsulated;
+
+ /* Parse the option code. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting option code number.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ option -> code = atoi (val);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL) {
+ parse_warn (cfile, "expecting \"=\"");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ /* See if this is an array. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == ARRAY) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != OF) {
+ parse_warn (cfile, "expecting \"of\".");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ }
+
+ if (token == LBRACE) {
+ recordp = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ }
+
+ /* At this point we're expecting a data type. */
+ next_type:
+ if (has_encapsulation) {
+ parse_warn (cfile,
+ "encapsulate must always be the last item.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ switch (token) {
+ case ARRAY:
+ if (arrayp) {
+ parse_warn (cfile, "no nested arrays.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != OF) {
+ parse_warn (cfile, "expecting \"of\".");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = recordp + 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if ((recordp) && (token == LBRACE)) {
+ parse_warn (cfile,
+ "only uniform array inside record.");
+ skip_to_rbrace (cfile, recordp + 1);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ goto next_type;
+ case BOOLEAN:
+ type = 'f';
+ break;
+ case INTEGER:
+ is_signed = 1;
+ parse_integer:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting number.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ switch (atoi (val)) {
+ case 8:
+ type = is_signed ? 'b' : 'B';
+ break;
+ case 16:
+ type = is_signed ? 's' : 'S';
+ break;
+ case 32:
+ type = is_signed ? 'l' : 'L';
+ break;
+ default:
+ parse_warn (cfile,
+ "%s bit precision is not supported.", val);
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ break;
+ case SIGNED:
+ is_signed = 1;
+ parse_signed:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != INTEGER) {
+ parse_warn (cfile, "expecting \"integer\" keyword.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ goto parse_integer;
+ case UNSIGNED:
+ is_signed = 0;
+ goto parse_signed;
+
+ case IP_ADDRESS:
+ type = 'I';
+ break;
+ case IP6_ADDRESS:
+ type = '6';
+ break;
+ case DOMAIN_NAME:
+ type = 'd';
+ goto no_arrays;
+ case DOMAIN_LIST:
+ /* Consume optional compression indicator. */
+ token = peek_token(&val, NULL, cfile);
+ if (token == COMPRESSED) {
+ token = next_token(&val, NULL, cfile);
+ tokbuf[tokix++] = 'D';
+ type = 'c';
+ } else
+ type = 'D';
+ goto no_arrays;
+ case TEXT:
+ type = 't';
+ no_arrays:
+ if (arrayp) {
+ parse_warn (cfile, "arrays of text strings not %s",
+ "yet supported.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ no_more_in_record = 1;
+ break;
+ case STRING_TOKEN:
+ type = 'X';
+ goto no_arrays;
+
+ case ENCAPSULATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting option space identifier");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ encapsulated = NULL;
+ if (!universe_hash_lookup(&encapsulated, universe_hash,
+ val, strlen(val), MDL)) {
+ parse_warn(cfile, "unknown option space %s", val);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (strlen (val) + tokix + 2 > sizeof (tokbuf))
+ goto toobig;
+ tokbuf [tokix++] = 'E';
+ strcpy (&tokbuf [tokix], val);
+ tokix += strlen (val);
+ type = '.';
+ has_encapsulation = 1;
+ break;
+
+ case ZEROLEN:
+ type = 'Z';
+ if (arrayp) {
+ parse_warn (cfile, "array incompatible with zerolen.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ no_more_in_record = 1;
+ break;
+
+ default:
+ parse_warn (cfile, "unknown data type %s", val);
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ if (tokix == sizeof tokbuf) {
+ toobig:
+ parse_warn (cfile, "too many types in record.");
+ skip_to_rbrace (cfile, recordp);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ tokbuf [tokix++] = type;
+
+ if (recordp) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (arrayp > recordp) {
+ if (tokix == sizeof tokbuf) {
+ parse_warn (cfile,
+ "too many types in record.");
+ skip_to_rbrace (cfile, 1);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ arrayp = 0;
+ tokbuf[tokix++] = 'a';
+ }
+ if (token == COMMA) {
+ if (no_more_in_record) {
+ parse_warn (cfile,
+ "%s must be at end of record.",
+ type == 't' ? "text" : "string");
+ skip_to_rbrace (cfile, 1);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ goto next_type;
+ }
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ skip_to_rbrace (cfile, 1);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ }
+ if (!parse_semi (cfile)) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ if (recordp)
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (has_encapsulation && arrayp) {
+ parse_warn (cfile,
+ "Arrays of encapsulations don't make sense.");
+ return 0;
+ }
+ s = dmalloc(tokix + (arrayp ? 1 : 0) + 1, MDL);
+ if (s == NULL) {
+ log_fatal("no memory for option format.");
+ }
+ memcpy(s, tokbuf, tokix);
+ if (arrayp) {
+ s[tokix++] = (arrayp > recordp) ? 'a' : 'A';
+ }
+ s[tokix] = '\0';
+
+ option -> format = s;
+
+ oldopt = NULL;
+ option_code_hash_lookup(&oldopt, option->universe->code_hash,
+ &option->code, 0, MDL);
+ if (oldopt != NULL) {
+ /*
+ * XXX: This illegalizes a configuration syntax that was
+ * valid in 3.0.x, where multiple name->code mappings are
+ * given, but only one code->name mapping survives. It is
+ * unclear what can or should be done at this point, but it
+ * seems best to retain 3.0.x behaviour for upgrades to go
+ * smoothly.
+ *
+ option_name_hash_delete(option->universe->name_hash,
+ oldopt->name, 0, MDL);
+ */
+ option_code_hash_delete(option->universe->code_hash,
+ &oldopt->code, 0, MDL);
+
+ option_dereference(&oldopt, MDL);
+ }
+ option_code_hash_add(option->universe->code_hash, &option->code, 0,
+ option, MDL);
+ option_name_hash_add(option->universe->name_hash, option->name, 0,
+ option, MDL);
+ if (has_encapsulation) {
+ /* INSIST(tokbuf[0] == 'E'); */
+ /* INSIST(encapsulated != NULL); */
+ if (!option_code_hash_lookup(&encapsulated->enc_opt,
+ option->universe->code_hash,
+ &option->code, 0, MDL)) {
+ log_fatal("error finding encapsulated option (%s:%d)",
+ MDL);
+ }
+ }
+ return 1;
+}
+
+/*
+ * base64 :== NUMBER_OR_STRING
+ */
+
+int parse_base64 (data, cfile)
+ struct data_string *data;
+ struct parse *cfile;
+{
+ enum dhcp_token token;
+ const char *val;
+ int i, j, k;
+ unsigned acc = 0;
+ static unsigned char
+ from64 [] = {64, 64, 64, 64, 64, 64, 64, 64, /* \"#$%&' */
+ 64, 64, 64, 62, 64, 64, 64, 63, /* ()*+,-./ */
+ 52, 53, 54, 55, 56, 57, 58, 59, /* 01234567 */
+ 60, 61, 64, 64, 64, 64, 64, 64, /* 89:;<=>? */
+ 64, 0, 1, 2, 3, 4, 5, 6, /* @ABCDEFG */
+ 7, 8, 9, 10, 11, 12, 13, 14, /* HIJKLMNO */
+ 15, 16, 17, 18, 19, 20, 21, 22, /* PQRSTUVW */
+ 23, 24, 25, 64, 64, 64, 64, 64, /* XYZ[\]^_ */
+ 64, 26, 27, 28, 29, 30, 31, 32, /* 'abcdefg */
+ 33, 34, 35, 36, 37, 38, 39, 40, /* hijklmno */
+ 41, 42, 43, 44, 45, 46, 47, 48, /* pqrstuvw */
+ 49, 50, 51, 64, 64, 64, 64, 64}; /* xyz{|}~ */
+ struct string_list *bufs = (struct string_list *)0,
+ *last = (struct string_list *)0,
+ *t;
+ int cc = 0;
+ int terminated = 0;
+
+ /* It's possible for a + or a / to cause a base64 quantity to be
+ tokenized into more than one token, so we have to parse them all
+ in before decoding. */
+ do {
+ unsigned l;
+
+ token = next_token (&val, &l, cfile);
+ t = dmalloc (l + sizeof *t, MDL);
+ if (!t)
+ log_fatal ("no memory for base64 buffer.");
+ memset (t, 0, (sizeof *t) - 1);
+ memcpy (t -> string, val, l + 1);
+ cc += l;
+ if (last)
+ last -> next = t;
+ else
+ bufs = t;
+ last = t;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ } while (token == NUMBER_OR_NAME || token == NAME || token == EQUAL ||
+ token == NUMBER || token == PLUS || token == SLASH ||
+ token == STRING);
+
+ data -> len = cc;
+ data -> len = (data -> len * 3) / 4;
+ if (!buffer_allocate (&data -> buffer, data -> len, MDL)) {
+ parse_warn (cfile, "can't allocate buffer for base64 data.");
+ data -> len = 0;
+ data -> data = (unsigned char *)0;
+ return 0;
+ }
+
+ j = k = 0;
+ for (t = bufs; t; t = t -> next) {
+ for (i = 0; t -> string [i]; i++) {
+ unsigned foo = t -> string [i];
+ if (terminated && foo != '=') {
+ parse_warn (cfile,
+ "stuff after base64 '=' terminator: %s.",
+ &t -> string [i]);
+ goto bad;
+ }
+ if (foo < ' ' || foo > 'z') {
+ bad64:
+ parse_warn (cfile,
+ "invalid base64 character %d.",
+ t -> string [i]);
+ bad:
+ data_string_forget (data, MDL);
+ goto out;
+ }
+ if (foo == '=')
+ terminated = 1;
+ else {
+ foo = from64 [foo - ' '];
+ if (foo == 64)
+ goto bad64;
+ acc = (acc << 6) + foo;
+ switch (k % 4) {
+ case 0:
+ break;
+ case 1:
+ data -> buffer -> data [j++] = (acc >> 4);
+ acc = acc & 0x0f;
+ break;
+
+ case 2:
+ data -> buffer -> data [j++] = (acc >> 2);
+ acc = acc & 0x03;
+ break;
+ case 3:
+ data -> buffer -> data [j++] = acc;
+ acc = 0;
+ break;
+ }
+ }
+ k++;
+ }
+ }
+ if (k % 4) {
+ if (acc) {
+ parse_warn (cfile,
+ "partial base64 value left over: %d.",
+ acc);
+ }
+ }
+ data -> len = j;
+ data -> data = data -> buffer -> data;
+ out:
+ for (t = bufs; t; t = last) {
+ last = t -> next;
+ dfree (t, MDL);
+ }
+ if (data -> len)
+ return 1;
+ else
+ return 0;
+}
+
+
+/*
+ * colon-separated-hex-list :== NUMBER |
+ * NUMBER COLON colon-separated-hex-list
+ */
+
+int parse_cshl (data, cfile)
+ struct data_string *data;
+ struct parse *cfile;
+{
+ u_int8_t ibuf [128];
+ unsigned ilen = 0;
+ unsigned tlen = 0;
+ struct option_tag *sl = (struct option_tag *)0;
+ struct option_tag *next, **last = &sl;
+ enum dhcp_token token;
+ const char *val;
+ unsigned char *rvp;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn (cfile, "expecting hexadecimal number.");
+ skip_to_semi (cfile);
+ for (; sl; sl = next) {
+ next = sl -> next;
+ dfree (sl, MDL);
+ }
+ return 0;
+ }
+ if (ilen == sizeof ibuf) {
+ next = (struct option_tag *)
+ dmalloc (ilen - 1 +
+ sizeof (struct option_tag), MDL);
+ if (!next)
+ log_fatal ("no memory for string list.");
+ memcpy (next -> data, ibuf, ilen);
+ *last = next;
+ last = &next -> next;
+ tlen += ilen;
+ ilen = 0;
+ }
+ convert_num (cfile, &ibuf [ilen++], val, 16, 8);
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != COLON)
+ break;
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (1);
+
+ if (!buffer_allocate (&data -> buffer, tlen + ilen, MDL))
+ log_fatal ("no memory to store octet data.");
+ data -> data = &data -> buffer -> data [0];
+ data -> len = tlen + ilen;
+ data -> terminated = 0;
+
+ rvp = &data -> buffer -> data [0];
+ while (sl) {
+ next = sl -> next;
+ memcpy (rvp, sl -> data, sizeof ibuf);
+ rvp += sizeof ibuf;
+ dfree (sl, MDL);
+ sl = next;
+ }
+
+ memcpy (rvp, ibuf, ilen);
+ return 1;
+}
+
+/*
+ * executable-statements :== executable-statement executable-statements |
+ * executable-statement
+ *
+ * executable-statement :==
+ * IF if-statement |
+ * ADD class-name SEMI |
+ * BREAK SEMI |
+ * OPTION option-parameter SEMI |
+ * SUPERSEDE option-parameter SEMI |
+ * PREPEND option-parameter SEMI |
+ * APPEND option-parameter SEMI
+ */
+
+int parse_executable_statements (statements, cfile, lose, case_context)
+ struct executable_statement **statements;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+ struct executable_statement **next;
+
+ next = statements;
+ while (parse_executable_statement (next, cfile, lose, case_context))
+ next = &((*next) -> next);
+ if (!*lose)
+ return 1;
+ return 0;
+}
+
+int parse_executable_statement (result, cfile, lose, case_context)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+#if defined(ENABLE_EXECUTE)
+ unsigned len;
+ struct expression **ep;
+#endif
+ enum dhcp_token token;
+ const char *val;
+ struct class *cta;
+ struct option *option=NULL;
+ struct option_cache *cache;
+ int known;
+ int flag;
+ int i;
+ struct dns_zone *zone;
+ isc_result_t status;
+ char *s;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case DB_TIME_FORMAT:
+ next_token(&val, NULL, cfile);
+
+ token = next_token(&val, NULL, cfile);
+ if (token == DEFAULT) {
+ db_time_format = DEFAULT_TIME_FORMAT;
+ } else if (token == LOCAL) {
+ db_time_format = LOCAL_TIME_FORMAT;
+ } else {
+ parse_warn(cfile, "Expecting 'local' or 'default'.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "Expecting a semicolon.");
+ *lose = 1;
+ return 0;
+ }
+
+ /* We're done here. */
+ return 1;
+
+ case IF:
+ next_token (&val, (unsigned *)0, cfile);
+ return parse_if_statement (result, cfile, lose);
+
+ case TOKEN_ADD:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting class name.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ cta = (struct class *)0;
+ status = find_class (&cta, val, MDL);
+ if (status != ISC_R_SUCCESS) {
+ parse_warn (cfile, "class %s: %s",
+ val, isc_result_totext (status));
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = add_statement;
+ (*result) -> data.add = cta;
+ break;
+
+ case BREAK:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = break_statement;
+ break;
+
+ case SEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ *lose = 1;
+ return 0;
+ }
+ status = parse_option_statement(result, cfile, 1, option,
+ send_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+
+ case SUPERSEDE:
+ case OPTION:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ *lose = 1;
+ return 0;
+ }
+ status = parse_option_statement(result, cfile, 1, option,
+ supersede_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+
+ case ALLOW:
+ flag = 1;
+ goto pad;
+ case DENY:
+ flag = 0;
+ goto pad;
+ case IGNORE:
+ flag = 2;
+ pad:
+ token = next_token (&val, (unsigned *)0, cfile);
+ cache = (struct option_cache *)0;
+ if (!parse_allow_deny (&cache, cfile, flag))
+ return 0;
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = supersede_option_statement;
+ (*result) -> data.option = cache;
+ break;
+
+ case DEFAULT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == COLON)
+ goto switch_default;
+ known = 0;
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ *lose = 1;
+ return 0;
+ }
+ status = parse_option_statement(result, cfile, 1, option,
+ default_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+
+ case PREPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ *lose = 1;
+ return 0;
+ }
+ status = parse_option_statement(result, cfile, 1, option,
+ prepend_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+
+ case APPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL) {
+ *lose = 1;
+ return 0;
+ }
+ status = parse_option_statement(result, cfile, 1, option,
+ append_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+
+ case ON:
+ token = next_token (&val, (unsigned *)0, cfile);
+ return parse_on_statement (result, cfile, lose);
+
+ case SWITCH:
+ token = next_token (&val, (unsigned *)0, cfile);
+ return parse_switch_statement (result, cfile, lose);
+
+ case CASE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (case_context == context_any) {
+ parse_warn (cfile,
+ "case statement in inappropriate scope.");
+ *lose = 1;
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return parse_case_statement (result,
+ cfile, lose, case_context);
+
+ switch_default:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (case_context == context_any) {
+ parse_warn (cfile, "switch default statement in %s",
+ "inappropriate scope.");
+
+ *lose = 1;
+ return 0;
+ } else {
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for default statement.");
+ (*result) -> op = default_statement;
+ return 1;
+ }
+
+ case DEFINE:
+ case TOKEN_SET:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == DEFINE)
+ flag = 1;
+ else
+ flag = 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "%s can't be a variable name", val);
+ badset:
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for set statement.");
+ (*result) -> op = flag ? define_statement : set_statement;
+ (*result) -> data.set.name = dmalloc (strlen (val) + 1, MDL);
+ if (!(*result)->data.set.name)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*result) -> data.set.name, val);
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == LPAREN) {
+ struct string_list *head, *cur, *new;
+ struct expression *expr;
+ head = cur = (struct string_list *)0;
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token == RPAREN)
+ break;
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "expecting argument name");
+ skip_to_rbrace (cfile, 0);
+ *lose = 1;
+ executable_statement_dereference
+ (result, MDL);
+ return 0;
+ }
+ new = ((struct string_list *)
+ dmalloc (sizeof (struct string_list) +
+ strlen (val), MDL));
+ if (!new)
+ log_fatal ("can't allocate string.");
+ memset (new, 0, sizeof *new);
+ strcpy (new -> string, val);
+ if (cur) {
+ cur -> next = new;
+ cur = new;
+ } else {
+ head = cur = new;
+ }
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token == COMMA);
+
+ if (token != RPAREN) {
+ parse_warn (cfile, "expecting right paren.");
+ badx:
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace.");
+ goto badx;
+ }
+
+ expr = (struct expression *)0;
+ if (!(expression_allocate (&expr, MDL)))
+ log_fatal ("can't allocate expression.");
+ expr -> op = expr_function;
+ if (!fundef_allocate (&expr -> data.func, MDL))
+ log_fatal ("can't allocate fundef.");
+ expr -> data.func -> args = head;
+ (*result) -> data.set.expr = expr;
+
+ if (!(parse_executable_statements
+ (&expr -> data.func -> statements, cfile, lose,
+ case_context))) {
+ if (*lose)
+ goto badx;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting rigt brace.");
+ goto badx;
+ }
+ } else {
+ if (token != EQUAL) {
+ parse_warn (cfile,
+ "expecting '=' in %s statement.",
+ flag ? "define" : "set");
+ goto badset;
+ }
+
+ if (!parse_expression (&(*result) -> data.set.expr,
+ cfile, lose, context_any,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ break;
+
+ case UNSET:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "%s can't be a variable name", val);
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for set statement.");
+ (*result) -> op = unset_statement;
+ (*result) -> data.unset = dmalloc (strlen (val) + 1, MDL);
+ if (!(*result)->data.unset)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*result) -> data.unset, val);
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+
+ case EVAL:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for eval statement.");
+ (*result) -> op = eval_statement;
+
+ if (!parse_expression (&(*result) -> data.eval,
+ cfile, lose, context_data, /* XXX */
+ (struct expression **)0, expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting data expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ }
+ break;
+
+ case EXECUTE:
+#ifdef ENABLE_EXECUTE
+ token = next_token(&val, NULL, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for execute statement.");
+ (*result)->op = execute_statement;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LPAREN) {
+ parse_warn(cfile, "left parenthesis expected.");
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "Expecting a quoted string.");
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ (*result)->data.execute.command = dmalloc(len + 1, MDL);
+ if ((*result)->data.execute.command == NULL)
+ log_fatal("can't allocate command name");
+ strcpy((*result)->data.execute.command, val);
+
+ ep = &(*result)->data.execute.arglist;
+ (*result)->data.execute.argc = 0;
+
+ while((token = next_token(&val, NULL, cfile)) == COMMA) {
+ if (!expression_allocate(ep, MDL))
+ log_fatal ("can't allocate expression");
+
+ if (!parse_data_expression (&(*ep) -> data.arg.val,
+ cfile, lose)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting expression.");
+ *lose = 1;
+ }
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+ ep = &(*ep)->data.arg.next;
+ (*result)->data.execute.argc++;
+ }
+
+ if (token != RPAREN) {
+ parse_warn(cfile, "right parenthesis expected.");
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ }
+#else /* ! ENABLE_EXECUTE */
+ parse_warn(cfile, "define ENABLE_EXECUTE in site.h to "
+ "enable execute(); expressions.");
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+#endif /* ENABLE_EXECUTE */
+ break;
+
+ case RETURN:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for return statement.");
+ (*result) -> op = return_statement;
+
+ if (!parse_expression (&(*result) -> data.retval,
+ cfile, lose, context_data,
+ (struct expression **)0, expr_none)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting data expression.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+
+ case LOG:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for log statement.");
+ (*result) -> op = log_statement;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ i = 1;
+ if (token == FATAL) {
+ (*result) -> data.log.priority = log_priority_fatal;
+ } else if (token == ERROR) {
+ (*result) -> data.log.priority = log_priority_error;
+ } else if (token == TOKEN_DEBUG) {
+ (*result) -> data.log.priority = log_priority_debug;
+ } else if (token == INFO) {
+ (*result) -> data.log.priority = log_priority_info;
+ } else {
+ (*result) -> data.log.priority = log_priority_debug;
+ i = 0;
+ }
+ if (i) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ }
+
+ if (!(parse_data_expression
+ (&(*result) -> data.log.expr, cfile, lose))) {
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ /* Not really a statement, but we parse it here anyway
+ because it's appropriate for all DHCP agents with
+ parsers. */
+ case ZONE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ zone = (struct dns_zone *)0;
+ if (!dns_zone_allocate (&zone, MDL))
+ log_fatal ("no memory for new zone.");
+ zone -> name = parse_host_name (cfile);
+ if (!zone -> name) {
+ parse_warn (cfile, "expecting hostname.");
+ badzone:
+ *lose = 1;
+ skip_to_semi (cfile);
+ dns_zone_dereference (&zone, MDL);
+ return 0;
+ }
+ i = strlen (zone -> name);
+ if (zone -> name [i - 1] != '.') {
+ s = dmalloc ((unsigned)i + 2, MDL);
+ if (!s) {
+ parse_warn (cfile, "no trailing '.' on zone");
+ goto badzone;
+ }
+ strcpy (s, zone -> name);
+ s [i] = '.';
+ s [i + 1] = 0;
+ dfree (zone -> name, MDL);
+ zone -> name = s;
+ }
+ if (!parse_zone (zone, cfile))
+ goto badzone;
+ status = enter_dns_zone (zone);
+ if (status != ISC_R_SUCCESS) {
+ parse_warn (cfile, "dns zone key %s: %s",
+ zone -> name, isc_result_totext (status));
+ dns_zone_dereference (&zone, MDL);
+ return 0;
+ }
+ dns_zone_dereference (&zone, MDL);
+ return 1;
+
+ /* Also not really a statement, but same idea as above. */
+ case KEY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_key (cfile)) {
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+
+ default:
+ if (config_universe && is_identifier (token)) {
+ option = (struct option *)0;
+ option_name_hash_lookup(&option,
+ config_universe->name_hash,
+ val, 0, MDL);
+ if (option) {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ status = parse_option_statement
+ (result, cfile, 1, option,
+ supersede_option_statement);
+ option_dereference(&option, MDL);
+ return status;
+ }
+ }
+
+ if (token == NUMBER_OR_NAME || token == NAME) {
+ /* This is rather ugly. Since function calls are
+ data expressions, fake up an eval statement. */
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for eval statement.");
+ (*result) -> op = eval_statement;
+
+ if (!parse_expression (&(*result) -> data.eval,
+ cfile, lose, context_data,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose)
+ parse_warn (cfile, "expecting "
+ "function call.");
+ else
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_semi (cfile)) {
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ break;
+ }
+
+ *lose = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+/* zone-statements :== zone-statement |
+ zone-statement zone-statements
+ zone-statement :==
+ PRIMARY ip-addresses SEMI |
+ SECONDARY ip-addresses SEMI |
+ key-reference SEMI
+ ip-addresses :== ip-addr-or-hostname |
+ ip-addr-or-hostname COMMA ip-addresses
+ key-reference :== KEY STRING |
+ KEY identifier */
+
+int parse_zone (struct dns_zone *zone, struct parse *cfile)
+{
+ int token;
+ const char *val;
+ char *key_name;
+ struct option_cache *oc;
+ int done = 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ return 0;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case PRIMARY:
+ if (zone -> primary) {
+ parse_warn (cfile,
+ "more than one primary.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (!option_cache_allocate (&zone -> primary, MDL))
+ log_fatal ("can't allocate primary option cache.");
+ oc = zone -> primary;
+ goto consemup;
+
+ case SECONDARY:
+ if (zone -> secondary) {
+ parse_warn (cfile, "more than one secondary.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (!option_cache_allocate (&zone -> secondary, MDL))
+ log_fatal ("can't allocate secondary.");
+ oc = zone -> secondary;
+ consemup:
+ token = next_token (&val, (unsigned *)0, cfile);
+ do {
+ struct expression *expr = (struct expression *)0;
+ if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) {
+ parse_warn (cfile,
+ "expecting IP addr or hostname.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (oc -> expression) {
+ struct expression *old =
+ (struct expression *)0;
+ expression_reference (&old,
+ oc -> expression,
+ MDL);
+ expression_dereference (&oc -> expression,
+ MDL);
+ if (!make_concat (&oc -> expression,
+ old, expr))
+ log_fatal ("no memory for concat.");
+ expression_dereference (&expr, MDL);
+ expression_dereference (&old, MDL);
+ } else {
+ expression_reference (&oc -> expression,
+ expr, MDL);
+ expression_dereference (&expr, MDL);
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+ if (token != SEMI) {
+ parse_warn (cfile, "expecting semicolon.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ break;
+
+ case KEY:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ key_name = (char *)0;
+ } else {
+ key_name = parse_host_name (cfile);
+ if (!key_name) {
+ parse_warn (cfile, "expecting key name.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ val = key_name;
+ }
+ if (omapi_auth_key_lookup_name (&zone -> key, val) !=
+ ISC_R_SUCCESS)
+ parse_warn (cfile, "unknown key %s", val);
+ if (key_name)
+ dfree (key_name, MDL);
+ if (!parse_semi (cfile))
+ return 0;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ } while (!done);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ return 0;
+ }
+ return 1;
+}
+
+/* key-statements :== key-statement |
+ key-statement key-statements
+ key-statement :==
+ ALGORITHM host-name SEMI |
+ secret-definition SEMI
+ secret-definition :== SECRET base64val |
+ SECRET STRING */
+
+int parse_key (struct parse *cfile)
+{
+ int token;
+ const char *val;
+ int done = 0;
+ struct auth_key *key;
+ struct data_string ds;
+ isc_result_t status;
+ char *s;
+
+ key = (struct auth_key *)0;
+ if (omapi_auth_key_new (&key, MDL) != ISC_R_SUCCESS)
+ log_fatal ("no memory for key");
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ key -> name = dmalloc (strlen (val) + 1, MDL);
+ if (!key -> name)
+ log_fatal ("no memory for key name.");
+ strcpy (key -> name, val);
+
+ } else {
+ key -> name = parse_host_name (cfile);
+ if (!key -> name) {
+ parse_warn (cfile, "expecting key name.");
+ skip_to_semi (cfile);
+ goto bad;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ goto bad;
+ }
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case ALGORITHM:
+ if (key -> algorithm) {
+ parse_warn (cfile,
+ "key %s: too many algorithms",
+ key -> name);
+ goto rbad;
+ }
+ key -> algorithm = parse_host_name (cfile);
+ if (!key -> algorithm) {
+ parse_warn (cfile,
+ "expecting key algorithm name.");
+ goto rbad;
+ }
+ if (!parse_semi (cfile))
+ goto rbad;
+ /* If the algorithm name isn't an FQDN, tack on
+ the .SIG-ALG.REG.NET. domain. */
+ s = strrchr (key -> algorithm, '.');
+ if (!s) {
+ static char add [] = ".SIG-ALG.REG.INT.";
+ s = dmalloc (strlen (key -> algorithm) +
+ sizeof (add), MDL);
+ if (!s) {
+ log_error ("no memory for key %s.",
+ "algorithm");
+ goto rbad;
+ }
+ strcpy (s, key -> algorithm);
+ strcat (s, add);
+ dfree (key -> algorithm, MDL);
+ key -> algorithm = s;
+ } else if (s [1]) {
+ /* If there is no trailing '.', hack one in. */
+ s = dmalloc (strlen (key -> algorithm) + 2, MDL);
+ if (!s) {
+ log_error ("no memory for key %s.",
+ key -> algorithm);
+ goto rbad;
+ }
+ strcpy (s, key -> algorithm);
+ strcat (s, ".");
+ dfree (key -> algorithm, MDL);
+ key -> algorithm = s;
+ }
+ break;
+
+ case SECRET:
+ if (key -> key) {
+ parse_warn (cfile, "key %s: too many secrets",
+ key -> name);
+ goto rbad;
+ }
+
+ memset (&ds, 0, sizeof(ds));
+ if (!parse_base64 (&ds, cfile))
+ goto rbad;
+ status = omapi_data_string_new (&key -> key, ds.len,
+ MDL);
+ if (status != ISC_R_SUCCESS)
+ goto rbad;
+ memcpy (key -> key -> value,
+ ds.buffer -> data, ds.len);
+ data_string_forget (&ds, MDL);
+
+ if (!parse_semi (cfile))
+ goto rbad;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+ } while (!done);
+ if (token != RBRACE) {
+ parse_warn (cfile, "expecting right brace.");
+ goto rbad;
+ }
+ /* Allow the BIND 8 syntax, which has a semicolon after each
+ closing brace. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI)
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Remember the key. */
+ status = omapi_auth_key_enter (key);
+ if (status != ISC_R_SUCCESS) {
+ parse_warn (cfile, "tsig key %s: %s",
+ key -> name, isc_result_totext (status));
+ goto bad;
+ }
+ omapi_auth_key_dereference (&key, MDL);
+ return 1;
+
+ rbad:
+ skip_to_rbrace (cfile, 1);
+ bad:
+ omapi_auth_key_dereference (&key, MDL);
+ return 0;
+}
+
+/*
+ * on-statement :== event-types LBRACE executable-statements RBRACE
+ * event-types :== event-type OR event-types |
+ * event-type
+ * event-type :== EXPIRY | COMMIT | RELEASE
+ */
+
+int parse_on_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = on_statement;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case EXPIRY:
+ (*result) -> data.on.evtypes |= ON_EXPIRY;
+ break;
+
+ case COMMIT:
+ (*result) -> data.on.evtypes |= ON_COMMIT;
+ break;
+
+ case RELEASE:
+ (*result) -> data.on.evtypes |= ON_RELEASE;
+ break;
+
+ case TRANSMISSION:
+ (*result) -> data.on.evtypes |= ON_TRANSMISSION;
+ break;
+
+ default:
+ parse_warn (cfile, "expecting a lease event type");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == OR);
+
+ /* Semicolon means no statements. */
+ if (token == SEMI)
+ return 1;
+
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_executable_statements (&(*result) -> data.on.statements,
+ cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE
+ *
+ */
+
+int parse_switch_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = switch_statement;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "expecting left brace.");
+ pfui:
+ *lose = 1;
+ skip_to_semi (cfile);
+ gnorf:
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ if (!parse_expression (&(*result) -> data.s_switch.expr,
+ cfile, lose, context_data_or_numeric,
+ (struct expression **)0, expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data or numeric expression.");
+ goto pfui;
+ }
+ goto gnorf;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right paren expected.");
+ goto pfui;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ goto pfui;
+ }
+ if (!(parse_executable_statements
+ (&(*result) -> data.s_switch.statements, cfile, lose,
+ (is_data_expression ((*result) -> data.s_switch.expr)
+ ? context_data : context_numeric)))) {
+ if (*lose) {
+ skip_to_rbrace (cfile, 1);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ goto pfui;
+ }
+ return 1;
+}
+
+/*
+ * case-statement :== CASE expr COLON
+ *
+ */
+
+int parse_case_statement (result, cfile, lose, case_context)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context case_context;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for new statement.");
+ (*result) -> op = case_statement;
+
+ if (!parse_expression (&(*result) -> data.c_case,
+ cfile, lose, case_context,
+ (struct expression **)0, expr_none))
+ {
+ if (!*lose) {
+ parse_warn (cfile, "expecting %s expression.",
+ (case_context == context_data
+ ? "data" : "numeric"));
+ }
+ pfui:
+ *lose = 1;
+ skip_to_semi (cfile);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COLON) {
+ parse_warn (cfile, "colon expected.");
+ goto pfui;
+ }
+ return 1;
+}
+
+/*
+ * if-statement :== boolean-expression LBRACE executable-statements RBRACE
+ * else-statement
+ *
+ * else-statement :== <null> |
+ * ELSE LBRACE executable-statements RBRACE |
+ * ELSE IF if-statement |
+ * ELSIF if-statement
+ */
+
+int parse_if_statement (result, cfile, lose)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int *lose;
+{
+ enum dhcp_token token;
+ const char *val;
+ int parenp;
+
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for if statement.");
+
+ (*result) -> op = if_statement;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == LPAREN) {
+ parenp = 1;
+ next_token (&val, (unsigned *)0, cfile);
+ } else
+ parenp = 0;
+
+
+ if (!parse_boolean_expression (&(*result) -> data.ie.expr,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile, "boolean expression expected.");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSION_PARSE)
+ print_expression ("if condition", (*result) -> data.ie.expr);
+#endif
+ if (parenp) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "expecting right paren.");
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "left brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ if (!parse_executable_statements (&(*result) -> data.ie.tc,
+ cfile, lose, context_any)) {
+ if (*lose) {
+ /* Try to even things up. */
+ do {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token != END_OF_FILE && token != RBRACE);
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == ELSE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == IF) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_if_statement (&(*result) -> data.ie.fc,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting if statement");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+ } else if (token != LBRACE) {
+ parse_warn (cfile, "left brace or if expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ } else {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!(parse_executable_statements
+ (&(*result) -> data.ie.fc,
+ cfile, lose, context_any))) {
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RBRACE) {
+ parse_warn (cfile, "right brace expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ }
+ } else if (token == ELSIF) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_if_statement (&(*result) -> data.ie.fc,
+ cfile, lose)) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting conditional.");
+ executable_statement_dereference (result, MDL);
+ *lose = 1;
+ return 0;
+ }
+ } else
+ (*result) -> data.ie.fc = (struct executable_statement *)0;
+
+ return 1;
+}
+
+/*
+ * boolean_expression :== CHECK STRING |
+ * NOT boolean-expression |
+ * data-expression EQUAL data-expression |
+ * data-expression BANG EQUAL data-expression |
+ * data-expression REGEX_MATCH data-expression |
+ * boolean-expression AND boolean-expression |
+ * boolean-expression OR boolean-expression
+ * EXISTS OPTION-NAME
+ */
+
+int parse_boolean_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_boolean,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_boolean_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ parse_warn (cfile, "Expecting a boolean expression.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ return 1;
+}
+
+/* boolean :== ON SEMI | OFF SEMI | TRUE SEMI | FALSE SEMI */
+
+int parse_boolean (cfile)
+ struct parse *cfile;
+{
+ enum dhcp_token token;
+ const char *val;
+ int rv;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ rv = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ rv = 0;
+ else {
+ parse_warn (cfile,
+ "boolean value (true/false/on/off) expected");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ parse_semi (cfile);
+ return rv;
+}
+
+
+/*
+ * data_expression :== SUBSTRING LPAREN data-expression COMMA
+ * numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * CONCAT LPAREN data-expression COMMA
+ * data-expression RPAREN
+ * SUFFIX LPAREN data_expression COMMA
+ * numeric-expression RPAREN |
+ * LCASE LPAREN data_expression RPAREN |
+ * UCASE LPAREN data_expression RPAREN |
+ * OPTION option_name |
+ * HARDWARE |
+ * PACKET LPAREN numeric-expression COMMA
+ * numeric-expression RPAREN |
+ * STRING |
+ * colon_separated_hex_list
+ */
+
+int parse_data_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_data,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_data_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "Expecting a data expression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * numeric-expression :== EXTRACT_INT LPAREN data-expression
+ * COMMA number RPAREN |
+ * NUMBER
+ */
+
+int parse_numeric_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_numeric,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_numeric_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "Expecting a numeric expression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+#if defined (NSUPDATE_OLD)
+/*
+ * dns-expression :==
+ * UPDATE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression COMMA numeric-expression RPAREN
+ * DELETE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * NOT EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA
+ * data-expression RPAREN
+ * ns-class :== IN | CHAOS | HS | NUMBER
+ * ns-type :== A | PTR | MX | TXT | NUMBER
+ */
+
+int parse_dns_expression (expr, cfile, lose)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+{
+ /* Parse an expression... */
+ if (!parse_expression (expr, cfile, lose, context_dns,
+ (struct expression **)0, expr_none))
+ return 0;
+
+ if (!is_dns_expression (*expr) &&
+ (*expr) -> op != expr_variable_reference &&
+ (*expr) -> op != expr_funcall) {
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "Expecting a dns update subexpression.");
+ *lose = 1;
+ return 0;
+ }
+ return 1;
+}
+#endif /* NSUPDATE_OLD */
+/* Parse a subexpression that does not contain a binary operator. */
+
+int parse_non_binary (expr, cfile, lose, context)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context context;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct collection *col;
+ struct expression *nexp, **ep;
+ int known;
+ char *cptr;
+#if defined (NSUPDATE_OLD)
+ enum expr_op opcode;
+ const char *s;
+ unsigned long u;
+#endif
+ isc_result_t status;
+ unsigned len;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+
+ /* Check for unary operators... */
+ switch (token) {
+ case CHECK:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "string expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+ for (col = collections; col; col = col -> next)
+ if (!strcmp (col -> name, val))
+ break;
+ if (!col) {
+ parse_warn (cfile, "unknown collection.");
+ *lose = 1;
+ return 0;
+ }
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_check;
+ (*expr) -> data.check = col;
+ break;
+
+ case TOKEN_NOT:
+ token = next_token (&val, (unsigned *)0, cfile);
+#if defined(NSUPDATE_OLD)
+ if (context == context_dns) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ goto not_exists;
+ }
+#endif
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_not;
+ if (!parse_non_binary (&(*expr) -> data.not,
+ cfile, lose, context_boolean)) {
+ if (!*lose) {
+ parse_warn (cfile, "expression expected");
+ skip_to_semi (cfile);
+ }
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ if (!is_boolean_expression ((*expr) -> data.not)) {
+ *lose = 1;
+ parse_warn (cfile, "boolean expression expected");
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case LPAREN:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_expression (expr, cfile, lose, context,
+ (struct expression **)0, expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile, "expression expected");
+ skip_to_semi (cfile);
+ }
+ *lose = 1;
+ return 0;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ *lose = 1;
+ parse_warn (cfile, "right paren expected");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ break;
+
+ case EXISTS:
+#if defined(NSUPDATE_OLD)
+ if (context == context_dns)
+ goto ns_exists;
+#endif
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_exists;
+ known = 0;
+ /* Pass reference directly to expression structure. */
+ status = parse_option_name(cfile, 0, &known,
+ &(*expr)->data.option);
+ if (status != ISC_R_SUCCESS ||
+ (*expr)->data.option == NULL) {
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case STATIC:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_static;
+ break;
+
+ case KNOWN:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_known;
+ break;
+
+ case SUBSTRING:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_substring;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ nolparen:
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!parse_data_expression (&(*expr) -> data.substring.expr,
+ cfile, lose)) {
+ nodata:
+ expression_dereference (expr, MDL);
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ nocomma:
+ expression_dereference (expr, MDL);
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+
+ return 0;
+ }
+
+ if (!parse_numeric_expression
+ (&(*expr) -> data.substring.offset,cfile, lose)) {
+ nonum:
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression
+ (&(*expr) -> data.substring.len, cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ norparen:
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case SUFFIX:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_suffix;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression (&(*expr) -> data.suffix.expr,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.suffix.len,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case LCASE:
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (!expression_allocate(expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr)->op = expr_lcase;
+
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression(&(*expr)->data.lcase, cfile, lose))
+ goto nodata;
+
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case UCASE:
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (!expression_allocate(expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr)->op = expr_ucase;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression(&(*expr)->data.ucase,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case CONCAT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_concat;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_data_expression (&(*expr) -> data.concat [0],
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ concat_another:
+ if (!parse_data_expression (&(*expr) -> data.concat [1],
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == COMMA) {
+ nexp = (struct expression *)0;
+ if (!expression_allocate (&nexp, MDL))
+ log_fatal ("can't allocate at CONCAT2");
+ nexp -> op = expr_concat;
+ expression_reference (&nexp -> data.concat [0],
+ *expr, MDL);
+ expression_dereference (expr, MDL);
+ expression_reference (expr, nexp, MDL);
+ expression_dereference (&nexp, MDL);
+ goto concat_another;
+ }
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case BINARY_TO_ASCII:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_binary_to_ascii;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_numeric_expression (&(*expr) -> data.b2a.base,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.b2a.width,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_data_expression (&(*expr) -> data.b2a.separator,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_data_expression (&(*expr) -> data.b2a.buffer,
+ cfile, lose))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case REVERSE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_reverse;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!(parse_numeric_expression
+ (&(*expr) -> data.reverse.width, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.reverse.buffer, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case PICK:
+ /* pick (a, b, c) actually produces an internal representation
+ that looks like pick (a, pick (b, pick (c, nil))). */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!(expression_allocate (expr, MDL)))
+ log_fatal ("can't allocate expression");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ nexp = (struct expression *)0;
+ expression_reference (&nexp, *expr, MDL);
+ do {
+ nexp -> op = expr_pick_first_value;
+ if (!(parse_data_expression
+ (&nexp -> data.pick_first_value.car,
+ cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == COMMA) {
+ struct expression *foo = (struct expression *)0;
+ if (!expression_allocate (&foo, MDL))
+ log_fatal ("can't allocate expr");
+ expression_reference
+ (&nexp -> data.pick_first_value.cdr, foo, MDL);
+ expression_dereference (&nexp, MDL);
+ expression_reference (&nexp, foo, MDL);
+ expression_dereference (&foo, MDL);
+ }
+ } while (token == COMMA);
+ expression_dereference (&nexp, MDL);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+#if defined(NSUPDATE_OLD)
+ /* dns-update and dns-delete are present for historical
+ purposes, but are deprecated in favor of ns-update
+ in combination with update, delete, exists and not
+ exists. */
+ case DNS_UPDATE:
+ case DNS_DELETE:
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == DNS_UPDATE)
+ opcode = expr_ns_add;
+ else
+ opcode = expr_ns_delete;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile,
+ "parse_expression: expecting string.");
+ badnsupdate:
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!strcasecmp (val, "a"))
+ u = T_A;
+ else if (!strcasecmp (val, "aaaa"))
+ u = T_AAAA;
+ else if (!strcasecmp (val, "ptr"))
+ u = T_PTR;
+ else if (!strcasecmp (val, "mx"))
+ u = T_MX;
+ else if (!strcasecmp (val, "cname"))
+ u = T_CNAME;
+ else if (!strcasecmp (val, "TXT"))
+ u = T_TXT;
+ else {
+ parse_warn (cfile, "unexpected rrtype: %s", val);
+ goto badnsupdate;
+ }
+
+ s = (opcode == expr_ns_add
+ ? "old-dns-update"
+ : "old-dns-delete");
+ cptr = dmalloc (strlen (s) + 1, MDL);
+ if (!cptr)
+ log_fatal ("can't allocate name for %s", s);
+ strcpy (cptr, s);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_funcall;
+ (*expr) -> data.funcall.name = cptr;
+
+ /* Fake up a function call. */
+ ep = &(*expr) -> data.funcall.arglist;
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!make_const_int (&(*ep) -> data.arg.val, u))
+ log_fatal ("can't allocate rrtype value.");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_data_expression (&(*ep) -> data.arg.val,
+ cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_data_expression (&(*ep) -> data.arg.val,
+ cfile, lose)))
+ goto nodata;
+
+ if (opcode == expr_ns_add) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ ep = &((*ep) -> data.arg.next);
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!(parse_numeric_expression (&(*ep) -> data.arg.val,
+ cfile, lose))) {
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ goto badnsupdate;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case NS_UPDATE:
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ nexp = *expr;
+ do {
+ nexp -> op = expr_dns_transaction;
+ if (!(parse_dns_expression
+ (&nexp -> data.dns_transaction.car,
+ cfile, lose)))
+ {
+ if (!*lose)
+ parse_warn
+ (cfile,
+ "expecting dns expression.");
+ expression_dereference (expr, MDL);
+ *lose = 1;
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (token == COMMA) {
+ if (!(expression_allocate
+ (&nexp -> data.dns_transaction.cdr,
+ MDL)))
+ log_fatal
+ ("can't allocate expression");
+ nexp = nexp -> data.dns_transaction.cdr;
+ }
+ } while (token == COMMA);
+
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* NOT EXISTS is special cased above... */
+ not_exists:
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != EXISTS) {
+ parse_warn (cfile, "expecting DNS prerequisite.");
+ *lose = 1;
+ return 0;
+ }
+ opcode = expr_ns_not_exists;
+ goto nsupdatecode;
+ case TOKEN_ADD:
+ opcode = expr_ns_add;
+ goto nsupdatecode;
+ case TOKEN_DELETE:
+ opcode = expr_ns_delete;
+ goto nsupdatecode;
+ ns_exists:
+ opcode = expr_ns_exists;
+ nsupdatecode:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+#if !defined (NSUPDATE)
+ parse_warn (cfile,
+ "Please rebuild dhcpd with --with-nsupdate.");
+#endif
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = opcode;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER) {
+ parse_warn (cfile, "expecting identifier or number.");
+ badnsop:
+ expression_dereference (expr, MDL);
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (token == NUMBER)
+ (*expr) -> data.ns_add.rrclass = atoi (val);
+ else if (!strcasecmp (val, "in"))
+ (*expr) -> data.ns_add.rrclass = C_IN;
+ else if (!strcasecmp (val, "chaos"))
+ (*expr) -> data.ns_add.rrclass = C_CHAOS;
+ else if (!strcasecmp (val, "hs"))
+ (*expr) -> data.ns_add.rrclass = C_HS;
+ else {
+ parse_warn (cfile, "unexpected rrclass: %s", val);
+ goto badnsop;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token) && token != NUMBER) {
+ parse_warn (cfile, "expecting identifier or number.");
+ goto badnsop;
+ }
+
+ if (token == NUMBER)
+ (*expr) -> data.ns_add.rrtype = atoi (val);
+ else if (!strcasecmp (val, "a"))
+ (*expr) -> data.ns_add.rrtype = T_A;
+ else if (!strcasecmp (val, "aaaa"))
+ (*expr) -> data.ns_add.rrtype = T_AAAA;
+ else if (!strcasecmp (val, "ptr"))
+ (*expr) -> data.ns_add.rrtype = T_PTR;
+ else if (!strcasecmp (val, "mx"))
+ (*expr) -> data.ns_add.rrtype = T_MX;
+ else if (!strcasecmp (val, "cname"))
+ (*expr) -> data.ns_add.rrtype = T_CNAME;
+ else if (!strcasecmp (val, "TXT"))
+ (*expr) -> data.ns_add.rrtype = T_TXT;
+ else {
+ parse_warn (cfile, "unexpected rrtype: %s", val);
+ goto badnsop;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.ns_add.rrname, cfile, lose)))
+ goto nodata;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_data_expression
+ (&(*expr) -> data.ns_add.rrdata, cfile, lose)))
+ goto nodata;
+
+ if (opcode == expr_ns_add) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!(parse_numeric_expression
+ (&(*expr) -> data.ns_add.ttl, cfile,
+ lose))) {
+ if (!*lose)
+ parse_warn (cfile,
+ "expecting numeric expression.");
+ goto badnsupdate;
+ }
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+#endif /* NSUPDATE_OLD */
+ case OPTION:
+ case CONFIG_OPTION:
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = (token == OPTION
+ ? expr_option
+ : expr_config_option);
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ /* Pass reference directly to expression structure. */
+ status = parse_option_name(cfile, 0, &known,
+ &(*expr)->data.option);
+ if (status != ISC_R_SUCCESS ||
+ (*expr)->data.option == NULL) {
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case HARDWARE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_hardware;
+ break;
+
+ case LEASED_ADDRESS:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_leased_address;
+ break;
+
+ case CLIENT_STATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_client_state;
+ break;
+
+ case FILENAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_filename;
+ break;
+
+ case SERVER_NAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_sname;
+ break;
+
+ case LEASE_TIME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_lease_time;
+ break;
+
+ case TOKEN_NULL:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_null;
+ break;
+
+ case HOST_DECL_NAME:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_host_decl_name;
+ break;
+
+#if defined(NSUPDATE_OLD)
+ case UPDATED_DNS_RR:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting string.");
+ bad_rrtype:
+ *lose = 1;
+ return 0;
+ }
+ if (!strcasecmp (val, "a"))
+ s = "ddns-fwd-name";
+ else if (!strcasecmp (val, "ptr"))
+ s = "ddns-rev-name";
+ else {
+ parse_warn (cfile, "invalid DNS rrtype: %s", val);
+ goto bad_rrtype;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_reference;
+ (*expr) -> data.variable =
+ dmalloc (strlen (s) + 1, MDL);
+ if (!(*expr) -> data.variable)
+ log_fatal ("can't allocate variable name.");
+ strcpy ((*expr) -> data.variable, s);
+ break;
+#endif /* NSUPDATE_OLD */
+ case PACKET:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_packet;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ if (!parse_numeric_expression (&(*expr) -> data.packet.offset,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA)
+ goto nocomma;
+
+ if (!parse_numeric_expression (&(*expr) -> data.packet.len,
+ cfile, lose))
+ goto nonum;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case STRING:
+ token = next_token (&val, &len, cfile);
+ if (!make_const_data (expr, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("can't make constant string expression.");
+ break;
+
+ case EXTRACT_INT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ if (!parse_data_expression (&(*expr) -> data.extract_int,
+ cfile, lose)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting data expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ }
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "number expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ switch (atoi (val)) {
+ case 8:
+ (*expr) -> op = expr_extract_int8;
+ break;
+
+ case 16:
+ (*expr) -> op = expr_extract_int16;
+ break;
+
+ case 32:
+ (*expr) -> op = expr_extract_int32;
+ break;
+
+ default:
+ parse_warn (cfile,
+ "unsupported integer size %d", atoi (val));
+ *lose = 1;
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case ENCODE_INT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ parse_warn (cfile, "left parenthesis expected.");
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ if (!parse_numeric_expression (&(*expr) -> data.encode_int,
+ cfile, lose)) {
+ parse_warn (cfile, "expecting numeric expression.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != COMMA) {
+ parse_warn (cfile, "comma expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "number expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ switch (atoi (val)) {
+ case 8:
+ (*expr) -> op = expr_encode_int8;
+ break;
+
+ case 16:
+ (*expr) -> op = expr_encode_int16;
+ break;
+
+ case 32:
+ (*expr) -> op = expr_encode_int32;
+ break;
+
+ default:
+ parse_warn (cfile,
+ "unsupported integer size %d", atoi (val));
+ *lose = 1;
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN) {
+ parse_warn (cfile, "right parenthesis expected.");
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case NUMBER:
+ /* If we're in a numeric context, this should just be a
+ number, by itself. */
+ if (context == context_numeric ||
+ context == context_data_or_numeric) {
+ next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = atoi (val);
+ break;
+ }
+
+ case NUMBER_OR_NAME:
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+
+ (*expr) -> op = expr_const_data;
+ if (!parse_cshl (&(*expr) -> data.const_data, cfile)) {
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+
+ case NS_FORMERR:
+ known = FORMERR;
+ goto ns_const;
+ ns_const:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = known;
+ break;
+
+ case NS_NOERROR:
+ known = ISC_R_SUCCESS;
+ goto ns_const;
+
+ case NS_NOTAUTH:
+ known = DHCP_R_NOTAUTH;
+ goto ns_const;
+
+ case NS_NOTIMP:
+ known = ISC_R_NOTIMPLEMENTED;
+ goto ns_const;
+
+ case NS_NOTZONE:
+ known = DHCP_R_NOTZONE;
+ goto ns_const;
+
+ case NS_NXDOMAIN:
+ known = DHCP_R_NXDOMAIN;
+ goto ns_const;
+
+ case NS_NXRRSET:
+ known = DHCP_R_NXRRSET;
+ goto ns_const;
+
+ case NS_REFUSED:
+ known = DHCP_R_REFUSED;
+ goto ns_const;
+
+ case NS_SERVFAIL:
+ known = DHCP_R_SERVFAIL;
+ goto ns_const;
+
+ case NS_YXDOMAIN:
+ known = DHCP_R_YXDOMAIN;
+ goto ns_const;
+
+ case NS_YXRRSET:
+ known = DHCP_R_YXRRSET;
+ goto ns_const;
+
+ case BOOTING:
+ known = S_INIT;
+ goto ns_const;
+
+ case REBOOT:
+ known = S_REBOOTING;
+ goto ns_const;
+
+ case SELECT:
+ known = S_SELECTING;
+ goto ns_const;
+
+ case REQUEST:
+ known = S_REQUESTING;
+ goto ns_const;
+
+ case BOUND:
+ known = S_BOUND;
+ goto ns_const;
+
+ case RENEW:
+ known = S_RENEWING;
+ goto ns_const;
+
+ case REBIND:
+ known = S_REBINDING;
+ goto ns_const;
+
+ case DEFINED:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile, "%s can't be a variable name", val);
+ skip_to_semi (cfile);
+ *lose = 1;
+ return 0;
+ }
+
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_exists;
+ (*expr) -> data.variable = dmalloc (strlen (val) + 1, MDL);
+ if (!(*expr)->data.variable)
+ log_fatal ("can't allocate variable name");
+ strcpy ((*expr) -> data.variable, val);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* This parses 'gethostname()'. */
+ case GETHOSTNAME:
+ token = next_token(&val, NULL, cfile);
+ if (!expression_allocate(expr, MDL))
+ log_fatal("can't allocate expression");
+ (*expr)->op = expr_gethostname;
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ case GETHOSTBYNAME:
+ token = next_token(&val, NULL, cfile);
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != LPAREN)
+ goto nolparen;
+
+ /* The argument is a quoted string. */
+ token = next_token(&val, NULL, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "Expecting quoted literal: "
+ "\"foo.example.com\"");
+ skip_to_semi(cfile);
+ *lose = 1;
+ return 0;
+ }
+ if (!make_host_lookup(expr, val))
+ log_fatal("Error creating gethostbyname() internal "
+ "record. (%s:%d)", MDL);
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != RPAREN)
+ goto norparen;
+ break;
+
+ /* Not a valid start to an expression... */
+ default:
+ if (token != NAME && token != NUMBER_OR_NAME)
+ return 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Save the name of the variable being referenced. */
+ cptr = dmalloc (strlen (val) + 1, MDL);
+ if (!cptr)
+ log_fatal ("can't allocate variable name");
+ strcpy (cptr, val);
+
+ /* Simple variable reference, as far as we can tell. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != LPAREN) {
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_variable_reference;
+ (*expr) -> data.variable = cptr;
+ break;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("can't allocate expression");
+ (*expr) -> op = expr_funcall;
+ (*expr) -> data.funcall.name = cptr;
+
+ /* Now parse the argument list. */
+ ep = &(*expr) -> data.funcall.arglist;
+ do {
+ if (!expression_allocate (ep, MDL))
+ log_fatal ("can't allocate expression");
+ (*ep) -> op = expr_arg;
+ if (!parse_expression (&(*ep) -> data.arg.val,
+ cfile, lose, context_any,
+ (struct expression **)0,
+ expr_none)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting expression.");
+ *lose = 1;
+ }
+ skip_to_semi (cfile);
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ ep = &((*ep) -> data.arg.next);
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token == COMMA);
+ if (token != RPAREN) {
+ parse_warn (cfile, "Right parenthesis expected.");
+ skip_to_semi (cfile);
+ *lose = 1;
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ break;
+ }
+ return 1;
+}
+
+/* Parse an expression. */
+
+int parse_expression (expr, cfile, lose, context, plhs, binop)
+ struct expression **expr;
+ struct parse *cfile;
+ int *lose;
+ enum expression_context context;
+ struct expression **plhs;
+ enum expr_op binop;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct expression *rhs = (struct expression *)0, *tmp;
+ struct expression *lhs = (struct expression *)0;
+ enum expr_op next_op;
+ enum expression_context
+ lhs_context = context_any,
+ rhs_context = context_any;
+
+ /* Consume the left hand side we were passed. */
+ if (plhs) {
+ expression_reference (&lhs, *plhs, MDL);
+ expression_dereference (plhs, MDL);
+ }
+
+ new_rhs:
+ if (!parse_non_binary (&rhs, cfile, lose, context)) {
+ /* If we already have a left-hand side, then it's not
+ okay for there not to be a right-hand side here, so
+ we need to flag it as an error. */
+ if (lhs) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting right-hand side.");
+ *lose = 1;
+ skip_to_semi (cfile);
+ }
+ expression_dereference (&lhs, MDL);
+ }
+ return 0;
+ }
+
+ /* At this point, rhs contains either an entire subexpression,
+ or at least a left-hand-side. If we do not see a binary token
+ as the next token, we're done with the expression. */
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case BANG:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL) {
+ parse_warn (cfile, "! in boolean context without =");
+ *lose = 1;
+ skip_to_semi (cfile);
+ if (lhs)
+ expression_dereference (&lhs, MDL);
+ return 0;
+ }
+ next_op = expr_not_equal;
+ context = expression_context (rhs);
+ break;
+
+ case EQUAL:
+ next_op = expr_equal;
+ context = expression_context (rhs);
+ break;
+
+ case TILDE:
+#ifdef HAVE_REGEX_H
+ token = next_token(&val, NULL, cfile);
+ token = peek_token(&val, NULL, cfile);
+
+ if (token == TILDE)
+ next_op = expr_iregex_match;
+ else if (token == EQUAL)
+ next_op = expr_regex_match;
+ else {
+ parse_warn(cfile, "expecting ~= or ~~ operator");
+ *lose = 1;
+ skip_to_semi(cfile);
+ if (lhs)
+ expression_dereference(&lhs, MDL);
+ return 0;
+ }
+
+ context = expression_context(rhs);
+#else
+ parse_warn(cfile, "No support for regex operator.");
+ *lose = 1;
+ skip_to_semi(cfile);
+ if (lhs != NULL)
+ expression_dereference(&lhs, MDL);
+ return 0;
+#endif
+ break;
+
+ case AND:
+ next_op = expr_and;
+ context = expression_context (rhs);
+ break;
+
+ case OR:
+ next_op = expr_or;
+ context = expression_context (rhs);
+ break;
+
+ case PLUS:
+ next_op = expr_add;
+ context = expression_context (rhs);
+ break;
+
+ case MINUS:
+ next_op = expr_subtract;
+ context = expression_context (rhs);
+ break;
+
+ case SLASH:
+ next_op = expr_divide;
+ context = expression_context (rhs);
+ break;
+
+ case ASTERISK:
+ next_op = expr_multiply;
+ context = expression_context (rhs);
+ break;
+
+ case PERCENT:
+ next_op = expr_remainder;
+ context = expression_context (rhs);
+ break;
+
+ case AMPERSAND:
+ next_op = expr_binary_and;
+ context = expression_context (rhs);
+ break;
+
+ case PIPE:
+ next_op = expr_binary_or;
+ context = expression_context (rhs);
+ break;
+
+ case CARET:
+ next_op = expr_binary_xor;
+ context = expression_context (rhs);
+ break;
+
+ default:
+ next_op = expr_none;
+ }
+
+ /* If we have no lhs yet, we just parsed it. */
+ if (!lhs) {
+ /* If there was no operator following what we just parsed,
+ then we're done - return it. */
+ if (next_op == expr_none) {
+ *expr = rhs;
+ return 1;
+ }
+ lhs = rhs;
+ rhs = (struct expression *)0;
+ binop = next_op;
+ next_token (&val, (unsigned *)0, cfile);
+ goto new_rhs;
+ }
+
+ /* If the next binary operator is of greater precedence than the
+ * current operator, then rhs we have parsed so far is actually
+ * the lhs of the next operator. To get this value, we have to
+ * recurse.
+ */
+ if (binop != expr_none && next_op != expr_none &&
+ op_precedence (binop, next_op) < 0) {
+
+ /* Eat the subexpression operator token, which we pass to
+ * parse_expression...we only peek()'d earlier.
+ */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Continue parsing of the right hand side with that token. */
+ tmp = rhs;
+ rhs = (struct expression *)0;
+ if (!parse_expression (&rhs, cfile, lose, op_context (next_op),
+ &tmp, next_op)) {
+ if (!*lose) {
+ parse_warn (cfile,
+ "expecting a subexpression");
+ *lose = 1;
+ }
+ return 0;
+ }
+ next_op = expr_none;
+ }
+
+ if (binop != expr_none) {
+ rhs_context = expression_context(rhs);
+ lhs_context = expression_context(lhs);
+
+ if ((rhs_context != context_any) && (lhs_context != context_any) &&
+ (rhs_context != lhs_context)) {
+ parse_warn (cfile, "illegal expression relating different types");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ expression_dereference (&lhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+
+ switch(binop) {
+ case expr_not_equal:
+ case expr_equal:
+ if ((rhs_context != context_data_or_numeric) &&
+ (rhs_context != context_data) &&
+ (rhs_context != context_numeric) &&
+ (rhs_context != context_any)) {
+ parse_warn (cfile, "expecting data/numeric expression");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ case expr_regex_match:
+#ifdef HAVE_REGEX_H
+ if (expression_context(rhs) != context_data) {
+ parse_warn(cfile, "expecting data expression");
+ skip_to_semi(cfile);
+ expression_dereference(&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+#else
+ /* It should not be possible to attempt to parse the right
+ * hand side of an operator there is no support for.
+ */
+ log_fatal("Impossible condition at %s:%d.", MDL);
+#endif
+ break;
+
+ case expr_and:
+ case expr_or:
+ if ((rhs_context != context_boolean) &&
+ (rhs_context != context_any)) {
+ parse_warn (cfile, "expecting boolean expressions");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ case expr_add:
+ case expr_subtract:
+ case expr_divide:
+ case expr_multiply:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ if ((rhs_context != context_numeric) &&
+ (rhs_context != context_any)) {
+ parse_warn (cfile, "expecting numeric expressions");
+ skip_to_semi (cfile);
+ expression_dereference (&rhs, MDL);
+ *lose = 1;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Now, if we didn't find a binary operator, we're done parsing
+ this subexpression, so combine it with the preceding binary
+ operator and return the result. */
+ if (next_op == expr_none) {
+ if (!expression_allocate (expr, MDL))
+ log_fatal ("Can't allocate expression!");
+
+ (*expr) -> op = binop;
+ /* All the binary operators' data union members
+ are the same, so we'll cheat and use the member
+ for the equals operator. */
+ (*expr) -> data.equal [0] = lhs;
+ (*expr) -> data.equal [1] = rhs;
+ return 1;
+ }
+
+ /* Eat the operator token - we now know it was a binary operator... */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Now combine the LHS and the RHS using binop. */
+ tmp = (struct expression *)0;
+ if (!expression_allocate (&tmp, MDL))
+ log_fatal ("No memory for equal precedence combination.");
+
+ /* Store the LHS and RHS. */
+ tmp -> data.equal [0] = lhs;
+ tmp -> data.equal [1] = rhs;
+ tmp -> op = binop;
+
+ lhs = tmp;
+ tmp = (struct expression *)0;
+ rhs = (struct expression *)0;
+
+ /* Recursions don't return until we have parsed the end of the
+ expression, so if we recursed earlier, we can now return what
+ we got. */
+ if (next_op == expr_none) {
+ *expr = lhs;
+ return 1;
+ }
+
+ binop = next_op;
+ goto new_rhs;
+}
+
+
+int parse_option_data (expr, cfile, lookups, option)
+struct expression **expr;
+struct parse *cfile;
+int lookups;
+struct option *option;
+{
+ const char *val;
+ const char *fmt = NULL;
+ struct expression *tmp;
+ enum dhcp_token token;
+
+ do {
+ /*
+ * Set a flag if this is an array of a simple type (i.e.,
+ * not an array of pairs of IP addresses, or something like
+ * that.
+ */
+ int uniform = 0;
+
+ and_again:
+ /* Set fmt to start of format for 'A' and one char back
+ * for 'a'.
+ */
+ if ((fmt != NULL) && (fmt != option->format) && (*fmt == 'a'))
+ fmt -= 1;
+ else if ((fmt == NULL) || (*fmt == 'A'))
+ fmt = option->format;
+
+ /* 'a' means always uniform */
+ if ((fmt[0] != 'Z') && (tolower((unsigned char)fmt[1]) == 'a'))
+ uniform = 1;
+
+ do {
+ if ((*fmt == 'A') || (*fmt == 'a'))
+ break;
+ if (*fmt == 'o') {
+ /* consume the optional flag */
+ fmt++;
+ continue;
+ }
+
+ if (fmt[1] == 'o') {
+ /*
+ * A value for the current format is
+ * optional - check to see if the next
+ * token is a semi-colon if so we don't
+ * need to parse it and doing so would
+ * consume the semi-colon which our
+ * caller is expecting to parse
+ */
+ token = peek_token(&val, (unsigned *)0,
+ cfile);
+ if (token == SEMI) {
+ fmt++;
+ continue;
+ }
+ }
+
+ tmp = *expr;
+ *expr = NULL;
+
+ if (!parse_option_token(expr, cfile, &fmt, tmp,
+ uniform, lookups)) {
+ if (fmt [1] != 'o') {
+ if (tmp)
+ expression_dereference (&tmp,
+ MDL);
+ return 0;
+ }
+ *expr = tmp;
+ tmp = NULL;
+ }
+ if (tmp)
+ expression_dereference (&tmp, MDL);
+
+ fmt++;
+ } while (*fmt != '\0');
+
+ if ((*fmt == 'A') || (*fmt == 'a')) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ /* Comma means: continue with next element in array */
+ if (token == COMMA) {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ continue;
+ }
+ /* no comma: end of array.
+ 'A' or end of string means: leave the loop */
+ if ((*fmt == 'A') || (fmt[1] == '\0'))
+ break;
+ /* 'a' means: go on with next char */
+ if (*fmt == 'a') {
+ fmt++;
+ goto and_again;
+ }
+ }
+ } while ((*fmt == 'A') || (*fmt == 'a'));
+
+ return 1;
+}
+
+/* option-statement :== identifier DOT identifier <syntax> SEMI
+ | identifier <syntax> SEMI
+
+ Option syntax is handled specially through format strings, so it
+ would be painful to come up with BNF for it. However, it always
+ starts as above and ends in a SEMI. */
+
+int parse_option_statement (result, cfile, lookups, option, op)
+ struct executable_statement **result;
+ struct parse *cfile;
+ int lookups;
+ struct option *option;
+ enum statement_op op;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct expression *expr = (struct expression *)0;
+ int lose;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if ((token == SEMI) && (option->format[0] != 'Z')) {
+ /* Eat the semicolon... */
+ /*
+ * XXXSK: I'm not sure why we should ever get here, but we
+ * do during our startup. This confuses things if
+ * we are parsing a zero-length option, so don't
+ * eat the semicolon token in that case.
+ */
+ token = next_token (&val, (unsigned *)0, cfile);
+ } else if (token == EQUAL) {
+ /* Eat the equals sign. */
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Parse a data expression and use its value for the data. */
+ if (!parse_data_expression (&expr, cfile, &lose)) {
+ /* In this context, we must have an executable
+ statement, so if we found something else, it's
+ still an error. */
+ if (!lose) {
+ parse_warn (cfile,
+ "expecting a data expression.");
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ } else {
+ if (! parse_option_data(&expr, cfile, lookups, option))
+ return 0;
+ }
+
+ if (!parse_semi (cfile))
+ return 0;
+ if (!executable_statement_allocate (result, MDL))
+ log_fatal ("no memory for option statement.");
+
+ (*result)->op = op;
+ if (expr && !option_cache (&(*result)->data.option,
+ NULL, expr, option, MDL))
+ log_fatal ("no memory for option cache");
+
+ if (expr)
+ expression_dereference (&expr, MDL);
+
+ return 1;
+}
+
+int parse_option_token (rv, cfile, fmt, expr, uniform, lookups)
+ struct expression **rv;
+ struct parse *cfile;
+ const char **fmt;
+ struct expression *expr;
+ int uniform;
+ int lookups;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct expression *t = (struct expression *)0;
+ unsigned char buf [4];
+ unsigned len;
+ struct iaddr addr;
+ int compress;
+ isc_boolean_t freeval = ISC_FALSE;
+ const char *f, *g;
+ struct enumeration_value *e;
+
+ switch (**fmt) {
+ case 'U':
+ token = next_token (&val, &len, cfile);
+ if (!is_identifier (token)) {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting identifier.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ if (!make_const_data (&t, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for %s", val);
+ break;
+
+ case 'E':
+ g = strchr (*fmt, '.');
+ if (!g) {
+ parse_warn (cfile,
+ "malformed encapsulation format (bug!)");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ *fmt = g;
+ case 'X':
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ if (!expression_allocate (&t, MDL))
+ return 0;
+ if (!parse_cshl (&t -> data.const_data, cfile)) {
+ expression_dereference (&t, MDL);
+ return 0;
+ }
+ t -> op = expr_const_data;
+ } else {
+ token = next_token (&val, &len, cfile);
+
+ if(token == STRING) {
+ if (!make_const_data (&t,
+ (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for \"%s\"", val);
+ } else {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting string "
+ "or hexadecimal data.");
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ }
+ break;
+
+ case 'D': /* Domain list... */
+ if ((*fmt)[1] == 'c') {
+ compress = 1;
+ /* Skip the compress-flag atom. */
+ (*fmt)++;
+ } else
+ compress = 0;
+
+ t = parse_domain_list(cfile, compress);
+
+ if (!t) {
+ if ((*fmt)[1] != 'o')
+ skip_to_semi(cfile);
+ return 0;
+ }
+
+ break;
+
+ case 'd': /* Domain name... */
+ val = parse_host_name (cfile);
+ if (!val) {
+ parse_warn (cfile, "not a valid domain name.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ len = strlen (val);
+ freeval = ISC_TRUE;
+ goto make_string;
+
+ case 't': /* Text string... */
+ token = next_token (&val, &len, cfile);
+ if (token != STRING && !is_identifier (token)) {
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting string.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ make_string:
+ if (!make_const_data (&t, (const unsigned char *)val,
+ len, 1, 1, MDL))
+ log_fatal ("No memory for concatenation");
+ if (freeval == ISC_TRUE) {
+ dfree((char *)val, MDL);
+ freeval = ISC_FALSE;
+ }
+ break;
+
+ case 'N':
+ f = (*fmt) + 1;
+ g = strchr (*fmt, '.');
+ if (!g) {
+ parse_warn (cfile, "malformed %s (bug!)",
+ "enumeration format");
+ foo:
+ skip_to_semi (cfile);
+ return 0;
+ }
+ *fmt = g;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "identifier expected");
+ goto foo;
+ }
+ e = find_enumeration_value (f, (*fmt) - f, &len, val);
+ if (!e) {
+ parse_warn (cfile, "unknown value");
+ goto foo;
+ }
+ if (!make_const_data (&t, &e -> value, len, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'I': /* IP address or hostname. */
+ if (lookups) {
+ if (!parse_ip_addr_or_hostname (&t, cfile, uniform))
+ return 0;
+ } else {
+ if (!parse_ip_addr (cfile, &addr))
+ return 0;
+ if (!make_const_data (&t, addr.iabuf, addr.len,
+ 0, 1, MDL))
+ return 0;
+ }
+ break;
+
+ case '6': /* IPv6 address. */
+ if (!parse_ip6_addr(cfile, &addr)) {
+ return 0;
+ }
+ if (!make_const_data(&t, addr.iabuf, addr.len, 0, 1, MDL)) {
+ return 0;
+ }
+ break;
+
+ case 'T': /* Lease interval. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != INFINITE)
+ goto check_number;
+ putLong (buf, -1);
+ if (!make_const_data (&t, buf, 4, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ check_number:
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME)) {
+ need_number:
+ if ((*fmt) [1] != 'o') {
+ parse_warn (cfile, "expecting number.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ convert_num (cfile, buf, val, 0, 32);
+ if (!make_const_data (&t, buf, 4, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 's': /* Signed 16-bit integer. */
+ case 'S': /* Unsigned 16-bit integer. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num (cfile, buf, val, 0, 16);
+ if (!make_const_data (&t, buf, 2, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'b': /* Signed 8-bit integer. */
+ case 'B': /* Unsigned 8-bit integer. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if ((token != NUMBER) && (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num (cfile, buf, val, 0, 8);
+ if (!make_const_data (&t, buf, 1, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'f': /* Boolean flag. */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ if ((*fmt) [1] != 'o')
+ parse_warn (cfile, "expecting identifier.");
+ bad_flag:
+ if ((*fmt) [1] != 'o') {
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ return 0;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else if (!strcasecmp (val, "ignore"))
+ buf [0] = 2;
+ else {
+ if ((*fmt) [1] != 'o')
+ parse_warn (cfile, "expecting boolean.");
+ goto bad_flag;
+ }
+ if (!make_const_data (&t, buf, 1, 0, 1, MDL))
+ return 0;
+ break;
+
+ case 'Z': /* Zero-length option. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "semicolon expected.");
+ skip_to_semi(cfile);
+ }
+ buf[0] = '\0';
+ if (!make_const_data(&t, /* expression */
+ buf, /* buffer */
+ 0, /* length */
+ 0, /* terminated */
+ 1, /* allocate */
+ MDL))
+ return 0;
+ break;
+
+ default:
+ parse_warn (cfile, "Bad format '%c' in parse_option_token.",
+ **fmt);
+ skip_to_semi (cfile);
+ return 0;
+ }
+ if (expr) {
+ if (!make_concat (rv, expr, t))
+ return 0;
+ } else
+ expression_reference (rv, t, MDL);
+ expression_dereference (&t, MDL);
+ return 1;
+}
+
+int parse_option_decl (oc, cfile)
+ struct option_cache **oc;
+ struct parse *cfile;
+{
+ const char *val;
+ int token;
+ u_int8_t buf [4];
+ u_int8_t hunkbuf [1024];
+ unsigned hunkix = 0;
+ const char *fmt, *f;
+ struct option *option=NULL;
+ struct iaddr ip_addr;
+ u_int8_t *dp;
+ const u_int8_t *cdp;
+ unsigned len;
+ int nul_term = 0;
+ struct buffer *bp;
+ int known = 0;
+ int compress;
+ struct expression *express = NULL;
+ struct enumeration_value *e;
+ isc_result_t status;
+
+ status = parse_option_name (cfile, 0, &known, &option);
+ if (status != ISC_R_SUCCESS || option == NULL)
+ return 0;
+
+ /* Parse the option data... */
+ do {
+ for (fmt = option -> format; *fmt; fmt++) {
+ if (*fmt == 'A')
+ break;
+ if (*fmt == 'o' && fmt != option -> format)
+ continue;
+ switch (*fmt) {
+ case 'E':
+ fmt = strchr (fmt, '.');
+ if (!fmt) {
+ parse_warn (cfile,
+ "malformed %s (bug!)",
+ "encapsulation format");
+ goto parse_exit;
+ }
+ case 'X':
+ len = parse_X (cfile, &hunkbuf [hunkix],
+ sizeof hunkbuf - hunkix);
+ hunkix += len;
+ break;
+
+ case 't': /* Text string... */
+ token = peek_token (&val,
+ &len, cfile);
+ if (token == SEMI && fmt[1] == 'o') {
+ fmt++;
+ break;
+ }
+ token = next_token (&val,
+ &len, cfile);
+ if (token != STRING) {
+ parse_warn (cfile,
+ "expecting string.");
+ goto parse_exit;
+ }
+ if (hunkix + len + 1 > sizeof hunkbuf) {
+ parse_warn (cfile,
+ "option data buffer %s",
+ "overflow");
+ goto parse_exit;
+ }
+ memcpy (&hunkbuf [hunkix], val, len + 1);
+ nul_term = 1;
+ hunkix += len;
+ break;
+
+ case 'D':
+ if (fmt[1] == 'c') {
+ compress = 1;
+ fmt++;
+ } else
+ compress = 0;
+
+ express = parse_domain_list(cfile, compress);
+
+ if (express == NULL)
+ goto exit;
+
+ if (express->op != expr_const_data) {
+ parse_warn(cfile, "unexpected "
+ "expression");
+ goto parse_exit;
+ }
+
+ len = express->data.const_data.len;
+ cdp = express->data.const_data.data;
+
+ if ((hunkix + len) > sizeof(hunkbuf)) {
+ parse_warn(cfile, "option data buffer "
+ "overflow");
+ goto parse_exit;
+ }
+ memcpy(&hunkbuf[hunkix], cdp, len);
+ hunkix += len;
+
+ expression_dereference(&express, MDL);
+ break;
+
+ case 'N':
+ f = fmt + 1;
+ fmt = strchr (fmt, '.');
+ if (!fmt) {
+ parse_warn (cfile,
+ "malformed %s (bug!)",
+ "enumeration format");
+ goto parse_exit;
+ }
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "identifier expected");
+ goto parse_exit;
+ }
+ e = find_enumeration_value (f, fmt - f,
+ &len, val);
+ if (!e) {
+ parse_warn (cfile,
+ "unknown value");
+ goto parse_exit;
+ }
+ dp = &e -> value;
+ goto alloc;
+
+ case '6':
+ if (!parse_ip6_addr(cfile, &ip_addr))
+ goto exit;
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+ goto alloc;
+
+ case 'I': /* IP address. */
+ if (!parse_ip_addr (cfile, &ip_addr))
+ goto exit;
+ len = ip_addr.len;
+ dp = ip_addr.iabuf;
+
+ alloc:
+ if (hunkix + len > sizeof hunkbuf) {
+ parse_warn (cfile,
+ "option data buffer %s",
+ "overflow");
+ goto parse_exit;
+ }
+ memcpy (&hunkbuf [hunkix], dp, len);
+ hunkix += len;
+ break;
+
+ case 'L': /* Unsigned 32-bit integer... */
+ case 'l': /* Signed 32-bit integer... */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if ((token != NUMBER) &&
+ (token != NUMBER_OR_NAME)) {
+ need_number:
+ parse_warn (cfile,
+ "expecting number.");
+ if (token != SEMI)
+ goto parse_exit;
+ else
+ goto exit;
+ }
+ convert_num (cfile, 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,
+ (unsigned *)0, cfile);
+ if ((token != NUMBER) &&
+ (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num (cfile, 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,
+ (unsigned *)0, cfile);
+ if ((token != NUMBER) &&
+ (token != NUMBER_OR_NAME))
+ goto need_number;
+ convert_num (cfile, buf, val, 0, 8);
+ len = 1;
+ dp = buf;
+ goto alloc;
+
+ case 'f': /* Boolean flag. */
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting identifier.");
+ bad_flag:
+ if (token != SEMI)
+ goto parse_exit;
+ else
+ goto exit;
+ }
+ if (!strcasecmp (val, "true")
+ || !strcasecmp (val, "on"))
+ buf [0] = 1;
+ else if (!strcasecmp (val, "false")
+ || !strcasecmp (val, "off"))
+ buf [0] = 0;
+ else {
+ parse_warn (cfile,
+ "expecting boolean.");
+ goto bad_flag;
+ }
+ len = 1;
+ dp = buf;
+ goto alloc;
+
+ case 'Z': /* Zero-length option */
+ token = next_token(&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile,
+ "semicolon expected.");
+ goto parse_exit;
+ }
+ len = 0;
+ buf[0] = '\0';
+ break;
+
+ default:
+ log_error ("parse_option_param: Bad format %c",
+ *fmt);
+ goto parse_exit;
+ }
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (*fmt == 'A' && token == COMMA);
+
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ goto parse_exit;
+ }
+
+ bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, hunkix + nul_term, MDL))
+ log_fatal ("no memory to store option declaration.");
+ if (!bp -> data)
+ log_fatal ("out of memory allocating option data.");
+ memcpy (bp -> data, hunkbuf, hunkix + nul_term);
+
+ if (!option_cache_allocate (oc, MDL))
+ log_fatal ("out of memory allocating option cache.");
+
+ (*oc) -> data.buffer = bp;
+ (*oc) -> data.data = &bp -> data [0];
+ (*oc) -> data.terminated = nul_term;
+ (*oc) -> data.len = hunkix;
+ option_reference(&(*oc)->option, option, MDL);
+ option_dereference(&option, MDL);
+ return 1;
+
+parse_exit:
+ if (express != NULL)
+ expression_dereference(&express, MDL);
+ skip_to_semi (cfile);
+exit:
+ option_dereference(&option, MDL);
+
+ return 0;
+}
+
+/* Consider merging parse_cshl into this. */
+
+int parse_X (cfile, buf, max)
+ struct parse *cfile;
+ u_int8_t *buf;
+ unsigned max;
+{
+ int token;
+ const char *val;
+ unsigned len;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER_OR_NAME || token == NUMBER) {
+ len = 0;
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "expecting hexadecimal constant.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ convert_num (cfile, &buf [len], val, 16, 8);
+ if (len++ > max) {
+ parse_warn (cfile,
+ "hexadecimal constant too long.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == COLON)
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token == COLON);
+ val = (char *)buf;
+ } else if (token == STRING) {
+ token = next_token (&val, &len, cfile);
+ if (len + 1 > max) {
+ parse_warn (cfile, "string constant too long.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ memcpy (buf, val, len + 1);
+ } else {
+ parse_warn (cfile, "expecting string or hexadecimal data");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return len;
+}
+
+int parse_warn (struct parse *cfile, const char *fmt, ...)
+{
+ va_list list;
+ char lexbuf [256];
+ char mbuf [1024];
+ char fbuf [1024];
+ unsigned i, lix;
+
+ do_percentm (mbuf, fmt);
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (fbuf, sizeof fbuf, "%s line %d: %s",
+ cfile -> tlname, cfile -> lexline, mbuf);
+
+ va_start (list, fmt);
+ vsnprintf (mbuf, sizeof mbuf, fbuf, list);
+ va_end (list);
+
+ lix = 0;
+ for (i = 0;
+ cfile -> token_line [i] && i < (cfile -> lexchar - 1); i++) {
+ if (lix < (sizeof lexbuf) - 1)
+ lexbuf [lix++] = ' ';
+ if (cfile -> token_line [i] == '\t') {
+ for (; lix < (sizeof lexbuf) - 1 && (lix & 7); lix++)
+ lexbuf [lix] = ' ';
+ }
+ }
+ lexbuf [lix] = 0;
+
+#ifndef DEBUG
+ syslog (log_priority | LOG_ERR, "%s", mbuf);
+ syslog (log_priority | LOG_ERR, "%s", cfile -> token_line);
+ if (cfile -> lexchar < 81)
+ syslog (log_priority | LOG_ERR, "%s^", lexbuf);
+#endif
+
+ if (log_perror) {
+ IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ IGNORE_RET (write (STDERR_FILENO, cfile -> token_line,
+ strlen (cfile -> token_line)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ if (cfile -> lexchar < 81)
+ IGNORE_RET (write (STDERR_FILENO, lexbuf, lix));
+ IGNORE_RET (write (STDERR_FILENO, "^\n", 2));
+ }
+
+ cfile -> warnings_occurred = 1;
+
+ return 0;
+}
+
+struct expression *
+parse_domain_list(struct parse *cfile, int compress)
+{
+ const char *val;
+ enum dhcp_token token = SEMI;
+ struct expression *t = NULL;
+ unsigned len, clen = 0;
+ int result;
+ unsigned char compbuf[256 * NS_MAXCDNAME];
+ const unsigned char *dnptrs[256], **lastdnptr;
+
+ memset(compbuf, 0, sizeof(compbuf));
+ memset(dnptrs, 0, sizeof(dnptrs));
+ dnptrs[0] = compbuf;
+ lastdnptr = &dnptrs[255];
+
+ do {
+ /* Consume the COMMA token if peeked. */
+ if (token == COMMA)
+ next_token(&val, NULL, cfile);
+
+ /* Get next (or first) value. */
+ token = next_token(&val, &len, cfile);
+
+ if (token != STRING) {
+ parse_warn(cfile, "Expecting a domain string.");
+ return NULL;
+ }
+
+ /* If compression pointers are enabled, compress. If not,
+ * just pack the names in series into the buffer.
+ */
+ if (compress) {
+ result = MRns_name_compress(val, compbuf + clen,
+ sizeof(compbuf) - clen,
+ dnptrs, lastdnptr);
+
+ if (result < 0) {
+ parse_warn(cfile, "Error compressing domain "
+ "list: %m");
+ return NULL;
+ }
+
+ clen += result;
+ } else {
+ result = MRns_name_pton(val, compbuf + clen,
+ sizeof(compbuf) - clen);
+
+ /* result == 1 means the input was fully qualified.
+ * result == 0 means the input wasn't.
+ * result == -1 means bad things.
+ */
+ if (result < 0) {
+ parse_warn(cfile, "Error assembling domain "
+ "list: %m");
+ return NULL;
+ }
+
+ /*
+ * We need to figure out how many bytes to increment
+ * our buffer pointer since pton doesn't tell us.
+ */
+ while (compbuf[clen] != 0)
+ clen += compbuf[clen] + 1;
+
+ /* Count the last label (0). */
+ clen++;
+ }
+
+ if (clen > sizeof(compbuf))
+ log_fatal("Impossible error at %s:%d", MDL);
+
+ token = peek_token(&val, NULL, cfile);
+ } while (token == COMMA);
+
+ if (!make_const_data(&t, compbuf, clen, 1, 1, MDL))
+ log_fatal("No memory for domain list object.");
+
+ return t;
+}
+
diff --git a/common/print.c b/common/print.c
new file mode 100644
index 0000000..e8eac79
--- /dev/null
+++ b/common/print.c
@@ -0,0 +1,1518 @@
+/* print.c
+
+ Turn data structures into printable text. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+int db_time_format = DEFAULT_TIME_FORMAT;
+
+char *quotify_string (const char *s, const char *file, int line)
+{
+ unsigned len = 0;
+ const char *sp;
+ char *buf, *nsp;
+
+ for (sp = s; sp && *sp; sp++) {
+ if (*sp == ' ')
+ len++;
+ else if (!isascii ((int)*sp) || !isprint ((int)*sp))
+ len += 4;
+ else if (*sp == '"' || *sp == '\\')
+ len += 2;
+ else
+ len++;
+ }
+
+ buf = dmalloc (len + 1, file, line);
+ if (buf) {
+ nsp = buf;
+ for (sp = s; sp && *sp; sp++) {
+ if (*sp == ' ')
+ *nsp++ = ' ';
+ else if (!isascii ((int)*sp) || !isprint ((int)*sp)) {
+ sprintf (nsp, "\\%03o",
+ *(const unsigned char *)sp);
+ nsp += 4;
+ } else if (*sp == '"' || *sp == '\\') {
+ *nsp++ = '\\';
+ *nsp++ = *sp;
+ } else
+ *nsp++ = *sp;
+ }
+ *nsp++ = 0;
+ }
+ return buf;
+}
+
+char *quotify_buf (const unsigned char *s, unsigned len,
+ const char *file, int line)
+{
+ unsigned nulen = 0;
+ char *buf, *nsp;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (s [i] == ' ')
+ nulen++;
+ else if (!isascii (s [i]) || !isprint (s [i]))
+ nulen += 4;
+ else if (s [i] == '"' || s [i] == '\\')
+ nulen += 2;
+ else
+ nulen++;
+ }
+
+ buf = dmalloc (nulen + 1, MDL);
+ if (buf) {
+ nsp = buf;
+ for (i = 0; i < len; i++) {
+ if (s [i] == ' ')
+ *nsp++ = ' ';
+ else if (!isascii (s [i]) || !isprint (s [i])) {
+ sprintf (nsp, "\\%03o", s [i]);
+ nsp += 4;
+ } else if (s [i] == '"' || s [i] == '\\') {
+ *nsp++ = '\\';
+ *nsp++ = s [i];
+ } else
+ *nsp++ = s [i];
+ }
+ *nsp++ = 0;
+ }
+ return buf;
+}
+
+char *print_base64 (const unsigned char *buf, unsigned len,
+ const char *file, int line)
+{
+ char *s, *b;
+ unsigned bl;
+ int i;
+ unsigned val, extra;
+ static char to64 [] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ bl = ((len * 4 + 2) / 3) + 1;
+ b = dmalloc (bl + 1, file, line);
+ if (!b)
+ return (char *)0;
+
+ i = 0;
+ s = b;
+ while (i != len) {
+ val = buf [i++];
+ extra = val & 3;
+ val = val >> 2;
+ *s++ = to64 [val];
+ if (i == len) {
+ *s++ = to64 [extra << 4];
+ *s++ = '=';
+ break;
+ }
+ val = (extra << 8) + buf [i++];
+ extra = val & 15;
+ val = val >> 4;
+ *s++ = to64 [val];
+ if (i == len) {
+ *s++ = to64 [extra << 2];
+ *s++ = '=';
+ break;
+ }
+ val = (extra << 8) + buf [i++];
+ extra = val & 0x3f;
+ val = val >> 6;
+ *s++ = to64 [val];
+ *s++ = to64 [extra];
+ }
+ if (!len)
+ *s++ = '=';
+ *s++ = 0;
+ if (s > b + bl + 1)
+ abort ();
+ return b;
+}
+
+char *print_hw_addr (htype, hlen, data)
+ const int htype;
+ const int hlen;
+ const unsigned char *data;
+{
+ static char habuf [49];
+ char *s;
+ int i;
+
+ if (hlen <= 0)
+ habuf [0] = 0;
+ else {
+ s = habuf;
+ for (i = 0; i < hlen; i++) {
+ sprintf (s, "%02x", data [i]);
+ s += strlen (s);
+ *s++ = ':';
+ }
+ *--s = 0;
+ }
+ return habuf;
+}
+
+void print_lease (lease)
+ struct lease *lease;
+{
+ struct tm *t;
+ char tbuf [32];
+
+ log_debug (" Lease %s",
+ piaddr (lease -> ip_addr));
+
+ t = gmtime (&lease -> starts);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ log_debug (" start %s", tbuf);
+
+ t = gmtime (&lease -> ends);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ log_debug (" end %s", tbuf);
+
+ if (lease -> hardware_addr.hlen)
+ log_debug (" hardware addr = %s",
+ print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1]));
+ log_debug (" host %s ",
+ lease -> host ? lease -> host -> name : "<none>");
+}
+
+#if defined (DEBUG_PACKET)
+void dump_packet_option (struct option_cache *oc,
+ struct packet *packet,
+ struct lease *lease,
+ struct client_state *client,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *foo)
+{
+ const char *name, *dot;
+ struct data_string ds;
+ memset (&ds, 0, sizeof ds);
+
+ if (u != &dhcp_universe) {
+ name = u -> name;
+ dot = ".";
+ } else {
+ name = "";
+ dot = "";
+ }
+ if (evaluate_option_cache (&ds, packet, lease, client,
+ in_options, cfg_options, scope, oc, MDL)) {
+ log_debug (" option %s%s%s %s;\n",
+ name, dot, oc -> option -> name,
+ pretty_print_option (oc -> option,
+ ds.data, ds.len, 1, 1));
+ data_string_forget (&ds, MDL);
+ }
+}
+
+void dump_packet (tp)
+ struct packet *tp;
+{
+ struct dhcp_packet *tdp = tp -> raw;
+
+ log_debug ("packet length %d", tp -> packet_length);
+ log_debug ("op = %d htype = %d hlen = %d hops = %d",
+ tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops);
+ log_debug ("xid = %x secs = %ld flags = %x",
+ tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags);
+ log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr));
+ log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr));
+ log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr));
+ log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr));
+ log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ ((unsigned char *)(tdp -> chaddr)) [0],
+ ((unsigned char *)(tdp -> chaddr)) [1],
+ ((unsigned char *)(tdp -> chaddr)) [2],
+ ((unsigned char *)(tdp -> chaddr)) [3],
+ ((unsigned char *)(tdp -> chaddr)) [4],
+ ((unsigned char *)(tdp -> chaddr)) [5]);
+ log_debug ("filename = %s", tdp -> file);
+ log_debug ("server_name = %s", tdp -> sname);
+ if (tp -> options_valid) {
+ int i;
+
+ for (i = 0; i < tp -> options -> universe_count; i++) {
+ if (tp -> options -> universes [i]) {
+ option_space_foreach (tp, (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ tp -> options,
+ &global_scope,
+ universes [i], 0,
+ dump_packet_option);
+ }
+ }
+ }
+ log_debug ("%s", "");
+}
+#endif
+
+void dump_raw (buf, len)
+ const unsigned char *buf;
+ unsigned len;
+{
+ int i;
+ char lbuf [80];
+ int lbix = 0;
+
+/*
+ 1 2 3 4 5 6 7
+01234567890123456789012345678901234567890123456789012345678901234567890123
+280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .................
+*/
+
+ memset(lbuf, ' ', 79);
+ lbuf [79] = 0;
+
+ for (i = 0; i < len; i++) {
+ if ((i & 15) == 0) {
+ if (lbix) {
+ lbuf[53]=' ';
+ lbuf[54]=' ';
+ lbuf[55]=' ';
+ lbuf[73]='\0';
+ log_info ("%s", lbuf);
+ }
+ memset(lbuf, ' ', 79);
+ lbuf [79] = 0;
+ sprintf (lbuf, "%03x:", i);
+ lbix = 4;
+ } else if ((i & 7) == 0)
+ lbuf [lbix++] = ' ';
+
+ if(isprint(buf[i])) {
+ lbuf[56+(i%16)]=buf[i];
+ } else {
+ lbuf[56+(i%16)]='.';
+ }
+
+ sprintf (&lbuf [lbix], " %02x", buf [i]);
+ lbix += 3;
+ lbuf[lbix]=' ';
+
+ }
+ lbuf[53]=' ';
+ lbuf[54]=' ';
+ lbuf[55]=' ';
+ lbuf[73]='\0';
+ log_info ("%s", lbuf);
+}
+
+void hash_dump (table)
+ struct hash_table *table;
+{
+ int i;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return;
+
+ for (i = 0; i < table -> hash_count; i++) {
+ if (!table -> buckets [i])
+ continue;
+ log_info ("hash bucket %d:", i);
+ for (bp = table -> buckets [i]; bp; bp = bp -> next) {
+ if (bp -> len)
+ dump_raw (bp -> name, bp -> len);
+ else
+ log_info ("%s", (const char *)bp -> name);
+ }
+ }
+}
+
+/*
+ * print a string as hex. This only outputs
+ * colon separated hex list no matter what
+ * the input looks like. See print_hex
+ * for a function that prints either cshl
+ * or a string if all bytes are printible
+ * It only uses limit characters from buf
+ * and doesn't do anything if buf == NULL
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf to use
+ * buf - output buffer
+ */
+void print_hex_only (len, data, limit, buf)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ char *buf;
+{
+ unsigned i;
+
+ if ((buf == NULL) || (limit < 3))
+ return;
+
+ for (i = 0; (i < limit / 3) && (i < len); i++) {
+ sprintf(&buf[i*3], "%02x:", data[i]);
+ }
+ buf[(i * 3) - 1] = 0;
+ return;
+}
+
+/*
+ * print a string as either text if all the characters
+ * are printable or colon separated hex if they aren't
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf to use
+ * buf - output buffer
+ */
+void print_hex_or_string (len, data, limit, buf)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ char *buf;
+{
+ unsigned i;
+ if ((buf == NULL) || (limit < 3))
+ return;
+
+ for (i = 0; (i < (limit - 3)) && (i < len); i++) {
+ if (!isascii(data[i]) || !isprint(data[i])) {
+ print_hex_only(len, data, limit, buf);
+ return;
+ }
+ }
+
+ buf[0] = '"';
+ i = len;
+ if (i > (limit - 3))
+ i = limit - 3;
+ memcpy(&buf[1], data, i);
+ buf[i + 1] = '"';
+ buf[i + 2] = 0;
+ return;
+}
+
+/*
+ * print a string as either hex or text
+ * using static buffers to hold the output
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf
+ * buf_num - the output buffer to use
+ */
+#define HBLEN 1024
+char *print_hex(len, data, limit, buf_num)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ unsigned buf_num;
+{
+ static char hex_buf_1[HBLEN + 1];
+ static char hex_buf_2[HBLEN + 1];
+ static char hex_buf_3[HBLEN + 1];
+ char *hex_buf;
+
+ switch(buf_num) {
+ case 0:
+ hex_buf = hex_buf_1;
+ if (limit >= sizeof(hex_buf_1))
+ limit = sizeof(hex_buf_1);
+ break;
+ case 1:
+ hex_buf = hex_buf_2;
+ if (limit >= sizeof(hex_buf_2))
+ limit = sizeof(hex_buf_2);
+ break;
+ case 2:
+ hex_buf = hex_buf_3;
+ if (limit >= sizeof(hex_buf_3))
+ limit = sizeof(hex_buf_3);
+ break;
+ default:
+ return(NULL);
+ }
+
+ print_hex_or_string(len, data, limit, hex_buf);
+ return(hex_buf);
+}
+
+#define DQLEN 80
+
+char *print_dotted_quads (len, data)
+ unsigned len;
+ const u_int8_t *data;
+{
+ static char dq_buf [DQLEN + 1];
+ int i;
+ char *s, *last;
+
+ s = &dq_buf [0];
+ last = s;
+
+ i = 0;
+
+ /* %Audit% Loop bounds checks to 21 bytes. %2004.06.17,Safe%
+ * The sprintf can't exceed 18 bytes, and since the loop enforces
+ * 21 bytes of space per iteration at no time can we exit the
+ * loop without at least 3 bytes spare.
+ */
+ do {
+ sprintf (s, "%u.%u.%u.%u, ",
+ data [i], data [i + 1], data [i + 2], data [i + 3]);
+ s += strlen (s);
+ i += 4;
+ } while ((s - &dq_buf [0] > DQLEN - 21) &&
+ i + 3 < len);
+ if (i == len)
+ s [-2] = 0;
+ else
+ strcpy (s, "...");
+ return dq_buf;
+}
+
+char *print_dec_1 (val)
+ unsigned long val;
+{
+ static char vbuf [32];
+ sprintf (vbuf, "%lu", val);
+ return vbuf;
+}
+
+char *print_dec_2 (val)
+ unsigned long val;
+{
+ static char vbuf [32];
+ sprintf (vbuf, "%lu", val);
+ return vbuf;
+}
+
+static unsigned print_subexpression (struct expression *, char *, unsigned);
+
+static unsigned print_subexpression (expr, buf, len)
+ struct expression *expr;
+ char *buf;
+ unsigned len;
+{
+ unsigned rv, left;
+ const char *s;
+
+ switch (expr -> op) {
+ case expr_none:
+ if (len > 3) {
+ strcpy (buf, "nil");
+ return 3;
+ }
+ break;
+
+ case expr_match:
+ if (len > 7) {
+ strcpy (buf, "(match)");
+ return 7;
+ }
+ break;
+
+ case expr_check:
+ rv = 10 + strlen (expr -> data.check -> name);
+ if (len > rv) {
+ sprintf (buf, "(check %s)",
+ expr -> data.check -> name);
+ return rv;
+ }
+ break;
+
+ case expr_equal:
+ if (len > 6) {
+ rv = 4;
+ strcpy (buf, "(eq ");
+ rv += print_subexpression (expr -> data.equal [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.equal [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_not_equal:
+ if (len > 7) {
+ rv = 5;
+ strcpy (buf, "(neq ");
+ rv += print_subexpression (expr -> data.equal [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.equal [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_regex_match:
+ if (len > 10) {
+ rv = 4;
+ strcpy(buf, "(regex ");
+ rv += print_subexpression(expr->data.equal[0],
+ buf + rv, len - rv - 2);
+ buf[rv++] = ' ';
+ rv += print_subexpression(expr->data.equal[1],
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_substring:
+ if (len > 11) {
+ rv = 8;
+ strcpy (buf, "(substr ");
+ rv += print_subexpression (expr -> data.substring.expr,
+ buf + rv, len - rv - 3);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.substring.offset,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.substring.len,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_suffix:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(suffix ");
+ rv += print_subexpression (expr -> data.suffix.expr,
+ buf + rv, len - rv - 2);
+ if (len > rv)
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.suffix.len,
+ buf + rv, len - rv - 1);
+ if (len > rv)
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_lcase:
+ if (len > 9) {
+ rv = 7;
+ strcpy(buf, "(lcase ");
+ rv += print_subexpression(expr->data.lcase,
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_ucase:
+ if (len > 9) {
+ rv = 7;
+ strcpy(buf, "(ucase ");
+ rv += print_subexpression(expr->data.ucase,
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_concat:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(concat ");
+ rv += print_subexpression (expr -> data.concat [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.concat [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_pick_first_value:
+ if (len > 8) {
+ rv = 6;
+ strcpy (buf, "(pick1st ");
+ rv += print_subexpression
+ (expr -> data.pick_first_value.car,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.pick_first_value.cdr,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_host_lookup:
+ rv = 15 + strlen (expr -> data.host_lookup -> hostname);
+ if (len > rv) {
+ sprintf (buf, "(dns-lookup %s)",
+ expr -> data.host_lookup -> hostname);
+ return rv;
+ }
+ break;
+
+ case expr_and:
+ s = "and";
+ binop:
+ rv = strlen (s);
+ if (len > rv + 4) {
+ buf [0] = '(';
+ strcpy (&buf [1], s);
+ rv += 1;
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.and [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.and [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_or:
+ s = "or";
+ goto binop;
+
+ case expr_add:
+ s = "+";
+ goto binop;
+
+ case expr_subtract:
+ s = "-";
+ goto binop;
+
+ case expr_multiply:
+ s = "*";
+ goto binop;
+
+ case expr_divide:
+ s = "/";
+ goto binop;
+
+ case expr_remainder:
+ s = "%";
+ goto binop;
+
+ case expr_binary_and:
+ s = "&";
+ goto binop;
+
+ case expr_binary_or:
+ s = "|";
+ goto binop;
+
+ case expr_binary_xor:
+ s = "^";
+ goto binop;
+
+ case expr_not:
+ if (len > 6) {
+ rv = 5;
+ strcpy (buf, "(not ");
+ rv += print_subexpression (expr -> data.not,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_config_option:
+ s = "cfg-option";
+ goto dooption;
+
+ case expr_option:
+ s = "option";
+ dooption:
+ rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) +
+ strlen (expr -> data.option -> universe -> name));
+ if (len > rv) {
+ sprintf (buf, "(option %s.%s)",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name);
+ return rv;
+ }
+ break;
+
+ case expr_hardware:
+ if (len > 10) {
+ strcpy (buf, "(hardware)");
+ return 10;
+ }
+ break;
+
+ case expr_packet:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(substr ");
+ rv += print_subexpression (expr -> data.packet.offset,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.packet.len,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_const_data:
+ s = print_hex_1 (expr -> data.const_data.len,
+ expr -> data.const_data.data, len);
+ rv = strlen (s);
+ if (rv >= len)
+ rv = len - 1;
+ strncpy (buf, s, rv);
+ buf [rv] = 0;
+ return rv;
+
+ case expr_encapsulate:
+ rv = 13;
+ strcpy (buf, "(encapsulate ");
+ rv += expr -> data.encapsulate.len;
+ if (rv + 2 > len)
+ rv = len - 2;
+ strncpy (buf,
+ (const char *)expr -> data.encapsulate.data, rv - 13);
+ buf [rv++] = ')';
+ buf [rv++] = 0;
+ break;
+
+ case expr_extract_int8:
+ if (len > 7) {
+ rv = 6;
+ strcpy (buf, "(int8 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_extract_int16:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(int16 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_extract_int32:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(int32 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int8:
+ if (len > 7) {
+ rv = 6;
+ strcpy (buf, "(to-int8 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int16:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(to-int16 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int32:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(to-int32 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_const_int:
+ s = print_dec_1 (expr -> data.const_int);
+ rv = strlen (s);
+ if (len > rv) {
+ strcpy (buf, s);
+ return rv;
+ }
+ break;
+
+ case expr_exists:
+ rv = 10 + (strlen (expr -> data.option -> name) +
+ strlen (expr -> data.option -> universe -> name));
+ if (len > rv) {
+ sprintf (buf, "(exists %s.%s)",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name);
+ return rv;
+ }
+ break;
+
+ case expr_variable_exists:
+ rv = 10 + strlen (expr -> data.variable);
+ if (len > rv) {
+ sprintf (buf, "(defined %s)", expr -> data.variable);
+ return rv;
+ }
+ break;
+
+ case expr_variable_reference:
+ rv = strlen (expr -> data.variable);
+ if (len > rv) {
+ sprintf (buf, "%s", expr -> data.variable);
+ return rv;
+ }
+ break;
+
+ case expr_known:
+ s = "known";
+ astring:
+ rv = strlen (s);
+ if (len > rv) {
+ strcpy (buf, s);
+ return rv;
+ }
+ break;
+
+ case expr_leased_address:
+ s = "leased-address";
+ goto astring;
+
+ case expr_client_state:
+ s = "client-state";
+ goto astring;
+
+ case expr_host_decl_name:
+ s = "host-decl-name";
+ goto astring;
+
+ case expr_lease_time:
+ s = "lease-time";
+ goto astring;
+
+ case expr_static:
+ s = "static";
+ goto astring;
+
+ case expr_filename:
+ s = "filename";
+ goto astring;
+
+ case expr_sname:
+ s = "server-name";
+ goto astring;
+
+ case expr_reverse:
+ if (len > 11) {
+ rv = 13;
+ strcpy (buf, "(reverse ");
+ rv += print_subexpression (expr -> data.reverse.width,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.reverse.buffer,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_binary_to_ascii:
+ if (len > 5) {
+ rv = 9;
+ strcpy (buf, "(b2a ");
+ rv += print_subexpression (expr -> data.b2a.base,
+ buf + rv, len - rv - 4);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.width,
+ buf + rv, len - rv - 3);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.separator,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.buffer,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_dns_transaction:
+ rv = 10;
+ if (len < rv + 2) {
+ buf [0] = '(';
+ strcpy (&buf [1], "ns-update ");
+ while (len < rv + 2) {
+ rv += print_subexpression
+ (expr -> data.dns_transaction.car,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ expr = expr -> data.dns_transaction.cdr;
+ }
+ buf [rv - 1] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ return 0;
+
+ case expr_ns_delete:
+ s = "delete";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_exists:
+ s = "exists";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_not_exists:
+ s = "not_exists";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_add:
+ s = "update";
+ left = 5;
+ dodnsupd:
+ rv = strlen (s);
+ if (len > strlen (s) + 1) {
+ buf [0] = '(';
+ strcpy (buf + 1, s);
+ rv++;
+ buf [rv++] = ' ';
+ s = print_dec_1 (expr -> data.ns_add.rrclass);
+ if (len > rv + strlen (s) + left) {
+ strcpy (&buf [rv], s);
+ rv += strlen (&buf [rv]);
+ }
+ buf [rv++] = ' ';
+ left--;
+ s = print_dec_1 (expr -> data.ns_add.rrtype);
+ if (len > rv + strlen (s) + left) {
+ strcpy (&buf [rv], s);
+ rv += strlen (&buf [rv]);
+ }
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.rrname,
+ buf + rv, len - rv - left);
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.rrdata,
+ buf + rv, len - rv - left);
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.ttl,
+ buf + rv, len - rv - left);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_null:
+ if (len > 6) {
+ strcpy (buf, "(null)");
+ return 6;
+ }
+ break;
+ case expr_funcall:
+ rv = 12 + strlen (expr -> data.funcall.name);
+ if (len > rv + 1) {
+ strcpy (buf, "(funcall ");
+ strcpy (buf + 9, expr -> data.funcall.name);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.funcall.arglist, buf + rv,
+ len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_arg:
+ rv = print_subexpression (expr -> data.arg.val, buf, len);
+ if (expr -> data.arg.next && rv + 2 < len) {
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.arg.next,
+ buf, len);
+ if (rv + 1 < len)
+ buf [rv++] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_function:
+ rv = 9;
+ if (len > rv + 1) {
+ struct string_list *foo;
+ strcpy (buf, "(function");
+ for (foo = expr -> data.func -> args;
+ foo; foo = foo -> next) {
+ if (len > rv + 2 + strlen (foo -> string)) {
+ buf [rv - 1] = ' ';
+ strcpy (&buf [rv], foo -> string);
+ rv += strlen (foo -> string);
+ }
+ }
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+
+ case expr_gethostname:
+ if (len > 13) {
+ strcpy(buf, "(gethostname)");
+ return 13;
+ }
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d (undefined expression "
+ "%d).", MDL, expr->op);
+ break;
+ }
+ return 0;
+}
+
+void print_expression (name, expr)
+ const char *name;
+ struct expression *expr;
+{
+ char buf [1024];
+
+ print_subexpression (expr, buf, sizeof buf);
+ log_info ("%s: %s", name, buf);
+}
+
+int token_print_indent_concat (FILE *file, int col, int indent,
+ const char *prefix,
+ const char *suffix, ...)
+{
+ va_list list;
+ unsigned len;
+ char *s, *t, *u;
+
+ va_start (list, suffix);
+ s = va_arg (list, char *);
+ len = 0;
+ while (s) {
+ len += strlen (s);
+ s = va_arg (list, char *);
+ }
+ va_end (list);
+
+ t = dmalloc (len + 1, MDL);
+ if (!t)
+ log_fatal ("token_print_indent: no memory for copy buffer");
+
+ va_start (list, suffix);
+ s = va_arg (list, char *);
+ u = t;
+ while (s) {
+ len = strlen (s);
+ strcpy (u, s);
+ u += len;
+ s = va_arg (list, char *);
+ }
+ va_end (list);
+
+ len = token_print_indent (file, col, indent,
+ prefix, suffix, t);
+ dfree (t, MDL);
+ return col;
+}
+
+int token_indent_data_string (FILE *file, int col, int indent,
+ const char *prefix, const char *suffix,
+ struct data_string *data)
+{
+ int i;
+ char *buf;
+ char obuf [3];
+
+ /* See if this is just ASCII. */
+ for (i = 0; i < data -> len; i++)
+ if (!isascii (data -> data [i]) ||
+ !isprint (data -> data [i]))
+ break;
+
+ /* If we have a purely ASCII string, output it as text. */
+ if (i == data -> len) {
+ buf = dmalloc (data -> len + 3, MDL);
+ if (buf) {
+ buf [0] = '"';
+ memcpy (buf + 1, data -> data, data -> len);
+ buf [data -> len + 1] = '"';
+ buf [data -> len + 2] = 0;
+ i = token_print_indent (file, col, indent,
+ prefix, suffix, buf);
+ dfree (buf, MDL);
+ return i;
+ }
+ }
+
+ for (i = 0; i < data -> len; i++) {
+ sprintf (obuf, "%2.2x", data -> data [i]);
+ col = token_print_indent (file, col, indent,
+ i == 0 ? prefix : "",
+ (i + 1 == data -> len
+ ? suffix
+ : ""), obuf);
+ if (i + 1 != data -> len)
+ col = token_print_indent (file, col, indent,
+ prefix, suffix, ":");
+ }
+ return col;
+}
+
+int token_print_indent (FILE *file, int col, int indent,
+ const char *prefix,
+ const char *suffix, const char *buf)
+{
+ int len = strlen (buf) + strlen (prefix);
+ if (col + len > 79) {
+ if (indent + len < 79) {
+ indent_spaces (file, indent);
+ col = indent;
+ } else {
+ indent_spaces (file, col);
+ col = len > 79 ? 0 : 79 - len - 1;
+ }
+ } else if (prefix && *prefix) {
+ fputs (prefix, file);
+ col += strlen (prefix);
+ }
+ fputs (buf, file);
+ col += len;
+ if (suffix && *suffix) {
+ if (col + strlen (suffix) > 79) {
+ indent_spaces (file, indent);
+ col = indent;
+ } else {
+ fputs (suffix, file);
+ col += strlen (suffix);
+ }
+ }
+ return col;
+}
+
+void indent_spaces (FILE *file, int indent)
+{
+ int i;
+ fputc ('\n', file);
+ for (i = 0; i < indent; i++)
+ fputc (' ', file);
+}
+
+#if defined (NSUPDATE)
+#if defined (DEBUG_DNS_UPDATES)
+/*
+ * direction outbound (messages to the dns server)
+ * inbound (messages from the dns server)
+ * ddns_cb is the control block associated with the message
+ * result is the result from the dns code. For outbound calls
+ * it is from the call to pass the message to the dns library.
+ * For inbound calls it is from the event returned by the library.
+ *
+ * For outbound messages we print whatever we think is interesting
+ * from the control block.
+ * For inbound messages we only print the transaction id pointer
+ * and the result and expect that the user will match them up as
+ * necessary. Note well: the transaction information is opaque to
+ * us so we simply print the pointer to it. This should be sufficient
+ * to match requests and replys in a short sequence but is awkward
+ * when trying to use it for longer sequences.
+ */
+void
+print_dns_status (int direction,
+ struct dhcp_ddns_cb *ddns_cb,
+ isc_result_t result)
+{
+ char obuf[1024];
+ char *s = obuf, *end = &obuf[sizeof(obuf)-2];
+ char *en;
+ const char *result_str;
+ char ddns_address[
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+
+ if (direction == DDNS_PRINT_INBOUND) {
+ log_info("DDNS reply: id ptr %p, result: %s",
+ ddns_cb->transaction, isc_result_totext(result));
+ return;
+ }
+
+ /*
+ * To avoid having to figure out if any of the strings
+ * aren't NULL terminated, just 0 the whole string
+ */
+ memset(obuf, 0, 1024);
+
+ en = "DDNS request: id ptr ";
+ if (s + strlen(en) + 16 < end) {
+ sprintf(s, "%s%p", en, ddns_cb->transaction);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ switch (ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ en = " add forward ";
+ break;
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ en = " modify forward ";
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ en = " add reverse ";
+ break;
+
+ case DDNS_STATE_REM_FW_YXDHCID:
+ en = " remove forward ";
+ break;
+
+ case DDNS_STATE_REM_FW_NXRR:
+ en = " remove rrset ";
+ break;
+
+ case DDNS_STATE_REM_PTR:
+ en = " remove reverse ";
+ break;
+
+ case DDNS_STATE_CLEANUP:
+ en = " cleanup ";
+ break;
+
+ default:
+ en = " unknown state ";
+ break;
+ }
+
+ switch (ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_NXRR:
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+ if (s + strlen(en) + strlen(ddns_address) +
+ ddns_cb->fwd_name.len + 5 < end) {
+ sprintf(s, "%s%s for %.*s", en, ddns_address,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ case DDNS_STATE_REM_PTR:
+ if (s + strlen(en) + ddns_cb->fwd_name.len +
+ ddns_cb->rev_name.len + 5 < end) {
+ sprintf(s, "%s%.*s for %.*s", en,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data,
+ ddns_cb->rev_name.len,
+ ddns_cb->rev_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_CLEANUP:
+ default:
+ if (s + strlen(en) < end) {
+ sprintf(s, "%s", en);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+ }
+
+ en = " zone: ";
+ if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
+ sprintf(s, "%s%s", en, ddns_cb->zone_name);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = " dhcid: ";
+ if (ddns_cb->dhcid.len > 0) {
+ if (s + strlen(en) + ddns_cb->dhcid.len-1 < end) {
+ strcpy(s, en);
+ s += strlen(s);
+ strncpy(s, (char *)ddns_cb->dhcid.data+1,
+ ddns_cb->dhcid.len-1);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ } else {
+ en = " dhcid: <empty>";
+ if (s + strlen(en) < end) {
+ strcpy(s, en);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ }
+
+ en = " ttl: ";
+ if (s + strlen(en) + 10 < end) {
+ sprintf(s, "%s%ld", en, ddns_cb->ttl);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = " result: ";
+ result_str = isc_result_totext(result);
+ if (s + strlen(en) + strlen(result_str) < end) {
+ sprintf(s, "%s%s", en, result_str);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ bailout:
+ /*
+ * We either finished building the string or ran out
+ * of space, print whatever we have in case it is useful
+ */
+ log_info("%s", obuf);
+
+ return;
+}
+#endif
+#endif /* NSUPDATE */
+
+/* Format the given time as "A; # B", where A is the format
+ * used by the parser, and B is the local time, for humans.
+ */
+const char *
+print_time(TIME t)
+{
+ static char buf[sizeof("epoch 9223372036854775807; "
+ "# Wed Jun 30 21:49:08 2147483647")];
+ static char buf1[sizeof("# Wed Jun 30 21:49:08 2147483647")];
+ time_t since_epoch;
+ /* The string: "6 2147483647/12/31 23:59:60;"
+ * is smaller than the other, used to declare the buffer size, so
+ * we can use one buffer for both.
+ */
+
+ if (t == MAX_TIME)
+ return "never;";
+
+ if (t < 0)
+ return NULL;
+
+ /* For those lucky enough to have a 128-bit time_t, ensure that
+ * whatever (corrupt) value we're given doesn't exceed the static
+ * buffer.
+ */
+#if (MAX_TIME > 0x7fffffffffffffff)
+ if (t > 0x7fffffffffffffff)
+ return NULL;
+#endif
+
+ if (db_time_format == LOCAL_TIME_FORMAT) {
+ since_epoch = mktime(localtime(&t));
+ if ((strftime(buf1, sizeof(buf1),
+ "# %a %b %d %H:%M:%S %Y",
+ localtime(&t)) == 0) ||
+ (snprintf(buf, sizeof(buf), "epoch %lu; %s",
+ (unsigned long)since_epoch, buf1) >= sizeof(buf)))
+ return NULL;
+
+ } else {
+ /* No bounds check for the year is necessary - in this case,
+ * strftime() will run out of space and assert an error.
+ */
+ if (strftime(buf, sizeof(buf), "%w %Y/%m/%d %H:%M:%S;",
+ gmtime(&t)) == 0)
+ return NULL;
+ }
+
+ return buf;
+}
diff --git a/common/raw.c b/common/raw.c
new file mode 100644
index 0000000..4730e8e
--- /dev/null
+++ b/common/raw.c
@@ -0,0 +1,139 @@
+/* raw.c
+
+ BSD raw socket interface code... */
+
+/* XXX
+
+ It's not clear how this should work, and that lack of clarity is
+ terribly detrimental to the NetBSD 1.1 kernel - it crashes and
+ burns.
+
+ Using raw sockets ought to be a big win over using BPF or something
+ like it, because you don't need to deal with the complexities of
+ the physical layer, but it appears not to be possible with existing
+ raw socket implementations. This may be worth revisiting in the
+ future. For now, this code can probably be considered a curiosity.
+ Sigh. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#if defined (USE_RAW_SEND)
+#include <sys/uio.h>
+
+/* Generic interface registration routine... */
+void if_register_send (info)
+ struct interface_info *info;
+{
+ struct sockaddr_in name;
+ int sock;
+ struct socklist *tmp;
+ int flag;
+
+ /* Set up the address we're going to connect to. */
+ name.sin_family = AF_INET;
+ name.sin_port = local_port;
+ name.sin_addr.s_addr = htonl (INADDR_BROADCAST);
+ memset (name.sin_zero, 0, sizeof (name.sin_zero));
+
+ /* List addresses on which we're listening. */
+ if (!quiet_interface_discovery)
+ log_info ("Sending on %s, port %d",
+ piaddr (info -> address), htons (local_port));
+ if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
+ log_fatal ("Can't create dhcp socket: %m");
+
+ /* Set the BROADCAST option so that we can broadcast DHCP responses. */
+ flag = 1;
+ if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST,
+ &flag, sizeof flag) < 0)
+ log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m");
+
+ /* Set the IP_HDRINCL flag so that we can supply our own IP
+ headers... */
+ if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &flag, sizeof flag) < 0)
+ log_fatal ("Can't set IP_HDRINCL flag: %m");
+
+ info -> wfdesc = sock;
+ if (!quiet_interface_discovery)
+ log_info ("Sending on Raw/%s%s%s",
+ info -> name,
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+ close (info -> wfdesc);
+ info -> wfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on Raw/%s%s%s",
+ info -> name,
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+size_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ unsigned char buf [256];
+ int bufp = 0;
+ struct iovec iov [2];
+ int result;
+
+ /* Assemble the headers... */
+ assemble_udp_ip_header (interface, 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)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_SOCKET_SEND */
diff --git a/common/resolv.c b/common/resolv.c
new file mode 100644
index 0000000..b29d4cf
--- /dev/null
+++ b/common/resolv.c
@@ -0,0 +1,195 @@
+/* resolv.c
+
+ Parser for /etc/resolv.conf file. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+struct name_server *name_servers;
+struct domain_search_list *domains;
+char path_resolv_conf [] = _PATH_RESOLV_CONF;
+
+void read_resolv_conf (parse_time)
+ TIME parse_time;
+{
+ int file;
+ struct parse *cfile;
+ const char *val;
+ int token;
+ struct name_server *sp, *sl, *ns;
+ struct domain_search_list *dp, *dl, *nd;
+ isc_result_t status;
+
+ if ((file = open (path_resolv_conf, O_RDONLY)) < 0) {
+ log_error ("Can't open %s: %m", path_resolv_conf);
+ return;
+ }
+
+ cfile = NULL;
+ status = new_parse(&cfile, file, NULL, 0, path_resolv_conf, 1);
+ if (status != ISC_R_SUCCESS || cfile == NULL)
+ return;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE)
+ break;
+ else if (token == EOL)
+ continue;
+ else if (token == DOMAIN || token == SEARCH) {
+ do {
+ struct domain_search_list *nd, **dp;
+ char *dn;
+
+ dn = parse_host_name (cfile);
+ if (!dn)
+ break;
+
+ dp = &domains;
+ for (nd = domains; nd; nd = nd -> next) {
+ dp = &nd -> next;
+ if (!strcmp (nd -> domain, dn))
+ break;
+ }
+ if (!nd) {
+ nd = new_domain_search_list (MDL);
+ if (!nd)
+ log_fatal ("No memory for %s",
+ dn);
+ nd -> next =
+ (struct domain_search_list *)0;
+ *dp = nd;
+ nd -> domain = dn;
+ dn = (char *)0;
+ }
+ nd -> rcdate = parse_time;
+ token = peek_token (&val,
+ (unsigned *)0, cfile);
+ } while (token != EOL);
+ if (token != EOL) {
+ parse_warn (cfile,
+ "junk after domain declaration");
+ skip_to_semi (cfile);
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ } else if (token == NAMESERVER) {
+ struct name_server *ns, **sp;
+ struct iaddr iaddr;
+
+ parse_ip_addr (cfile, &iaddr);
+
+ sp = &name_servers;
+ for (ns = name_servers; ns; ns = ns -> next) {
+ sp = &ns -> next;
+ if (!memcmp (&ns -> addr.sin_addr,
+ iaddr.iabuf, iaddr.len))
+ break;
+ }
+ if (!ns) {
+ ns = new_name_server (MDL);
+ if (!ns)
+ log_fatal ("No memory for nameserver %s",
+ piaddr (iaddr));
+ ns -> next = (struct name_server *)0;
+ *sp = ns;
+ memcpy (&ns -> addr.sin_addr,
+ iaddr.iabuf, iaddr.len);
+#ifdef HAVE_SA_LEN
+ ns -> addr.sin_len = sizeof ns -> addr;
+#endif
+ ns -> addr.sin_family = AF_INET;
+ ns -> addr.sin_port = htons (53);
+ memset (ns -> addr.sin_zero, 0,
+ sizeof ns -> addr.sin_zero);
+ }
+ ns -> rcdate = parse_time;
+ skip_to_semi (cfile);
+ } else
+ skip_to_semi (cfile); /* Ignore what we don't grok. */
+ } while (1);
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ /* Lose servers that are no longer in /etc/resolv.conf. */
+ sl = (struct name_server *)0;
+ for (sp = name_servers; sp; sp = ns) {
+ ns = sp -> next;
+ if (sp -> rcdate != parse_time) {
+ if (sl)
+ sl -> next = sp -> next;
+ else
+ name_servers = sp -> next;
+ /* We can't actually free the name server structure,
+ because somebody might be hanging on to it. If
+ your /etc/resolv.conf file changes a lot, this
+ could be a noticeable memory leak. */
+ } else
+ sl = sp;
+ }
+
+ /* Lose domains that are no longer in /etc/resolv.conf. */
+ dl = (struct domain_search_list *)0;
+ for (dp = domains; dp; dp = nd) {
+ nd = dp -> next;
+ if (dp -> rcdate != parse_time) {
+ if (dl)
+ dl -> next = dp -> next;
+ else
+ domains = dp -> next;
+ free_domain_search_list (dp, MDL);
+ } else
+ dl = dp;
+ }
+ end_parse (&cfile);
+}
+
+/* Pick a name server from the /etc/resolv.conf file. */
+
+struct name_server *first_name_server ()
+{
+ static TIME rcdate;
+ struct stat st;
+
+ /* Check /etc/resolv.conf and reload it if it's changed. */
+ if (cur_time > rcdate) {
+ if (stat (path_resolv_conf, &st) < 0) {
+ log_error ("Can't stat %s", path_resolv_conf);
+ return (struct name_server *)0;
+ }
+ if (st.st_mtime > rcdate) {
+ rcdate = cur_time + 1;
+
+ read_resolv_conf (rcdate);
+ }
+ }
+
+ return name_servers;
+}
diff --git a/common/socket.c b/common/socket.c
new file mode 100644
index 0000000..a48404b
--- /dev/null
+++ b/common/socket.c
@@ -0,0 +1,1128 @@
+/* socket.c
+
+ BSD socket interface code... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu).
+ * This sockopt allows a socket to be bound to a particular interface,
+ * thus enabling the use of DHCPD on a multihomed host.
+ * If SO_BINDTODEVICE is defined in your system header files, the use of
+ * this sockopt will be automatically enabled.
+ * I have implemented it under Linux; other systems should be doable also.
+ */
+
+#include "dhcpd.h"
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/uio.h>
+
+#if defined(sun) && defined(USE_V4_PKTINFO)
+#include <sys/sysmacros.h>
+#include <net/if.h>
+#include <sys/sockio.h>
+#include <net/if_dl.h>
+#endif
+
+#ifdef USE_SOCKET_FALLBACK
+# if !defined (USE_SOCKET_SEND)
+# define if_register_send if_register_fallback
+# define send_packet send_fallback
+# define if_reinitialize_send if_reinitialize_fallback
+# endif
+#endif
+
+#if defined(DHCPv6)
+/*
+ * XXX: this is gross. we need to go back and overhaul the API for socket
+ * handling.
+ */
+static unsigned int global_v6_socket_references = 0;
+static int global_v6_socket = -1;
+
+static void if_register_multicast(struct interface_info *info);
+#endif
+
+/*
+ * We can use a single socket for AF_INET (similar to AF_INET6) on all
+ * interfaces configured for DHCP if the system has support for IP_PKTINFO
+ * and IP_RECVPKTINFO (for example Solaris 11).
+ */
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+static unsigned int global_v4_socket_references = 0;
+static int global_v4_socket = -1;
+#endif
+
+/*
+ * If we can't bind() to a specific interface, then we can only have
+ * a single socket. This variable insures that we don't try to listen
+ * on two sockets.
+ */
+#if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK)
+static int once = 0;
+#endif /* !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK) */
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+#if 0
+#ifndef USE_SOCKET_RECEIVE
+ once = 0;
+ close (info -> wfdesc);
+#endif
+ if_register_send (info);
+#endif
+}
+#endif
+
+#ifdef USE_SOCKET_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+#if 0
+ once = 0;
+ close (info -> rfdesc);
+ if_register_receive (info);
+#endif
+}
+#endif
+
+#if defined (USE_SOCKET_SEND) || \
+ defined (USE_SOCKET_RECEIVE) || \
+ defined (USE_SOCKET_FALLBACK)
+/* Generic interface registration routine... */
+int
+if_register_socket(struct interface_info *info, int family,
+ int *do_multicast)
+{
+ struct sockaddr_storage name;
+ int name_len;
+ int sock;
+ int flag;
+ int domain;
+#ifdef DHCPv6
+ struct sockaddr_in6 *addr6;
+#endif
+ struct sockaddr_in *addr;
+
+ /* INSIST((family == AF_INET) || (family == AF_INET6)); */
+
+#if !defined(SO_BINDTODEVICE) && !defined(USE_FALLBACK)
+ /* Make sure only one interface is registered. */
+ if (once) {
+ log_fatal ("The standard socket API can only support %s",
+ "hosts with a single network interface.");
+ }
+ once = 1;
+#endif
+
+ /*
+ * Set up the address we're going to bind to, depending on the
+ * address family.
+ */
+ memset(&name, 0, sizeof(name));
+ switch (family) {
+#ifdef DHCPv6
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)&name;
+ addr6->sin6_family = AF_INET6;
+ addr6->sin6_port = local_port;
+ /* XXX: What will happen to multicasts if this is nonzero? */
+ memcpy(&addr6->sin6_addr,
+ &local_address6,
+ sizeof(addr6->sin6_addr));
+#ifdef HAVE_SA_LEN
+ addr6->sin6_len = sizeof(*addr6);
+#endif
+ name_len = sizeof(*addr6);
+ domain = PF_INET6;
+ if ((info->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM) {
+ *do_multicast = 0;
+ }
+ break;
+#endif /* DHCPv6 */
+
+ case AF_INET:
+ default:
+ addr = (struct sockaddr_in *)&name;
+ addr->sin_family = AF_INET;
+ addr->sin_port = local_port;
+ memcpy(&addr->sin_addr,
+ &local_address,
+ sizeof(addr->sin_addr));
+#ifdef HAVE_SA_LEN
+ addr->sin_len = sizeof(*addr);
+#endif
+ name_len = sizeof(*addr);
+ domain = PF_INET;
+ break;
+ }
+
+ /* Make a socket... */
+ sock = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0) {
+ log_fatal("Can't create dhcp socket: %m");
+ }
+
+ /* Set the REUSEADDR option so that we don't fail to start if
+ we're being restarted. */
+ flag = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&flag, sizeof(flag)) < 0) {
+ log_fatal("Can't set SO_REUSEADDR option on dhcp socket: %m");
+ }
+
+ /* Set the BROADCAST option so that we can broadcast DHCP responses.
+ We shouldn't do this for fallback devices, and we can detect that
+ a device is a fallback because it has no ifp structure. */
+ if (info->ifp &&
+ (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ (char *)&flag, sizeof(flag)) < 0)) {
+ log_fatal("Can't set SO_BROADCAST option on dhcp socket: %m");
+ }
+
+#if defined(DHCPv6) && defined(SO_REUSEPORT)
+ /*
+ * We only set SO_REUSEPORT on AF_INET6 sockets, so that multiple
+ * daemons can bind to their own sockets and get data for their
+ * respective interfaces. This does not (and should not) affect
+ * DHCPv4 sockets; we can't yet support BSD sockets well, much
+ * less multiple sockets.
+ */
+ if (local_family == AF_INET6) {
+ flag = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+ (char *)&flag, sizeof(flag)) < 0) {
+ log_fatal("Can't set SO_REUSEPORT option on dhcp "
+ "socket: %m");
+ }
+ }
+#endif
+
+ /* Bind the socket to this interface's IP address. */
+ if (bind(sock, (struct sockaddr *)&name, name_len) < 0) {
+ log_error("Can't bind to dhcp address: %m");
+ log_error("Please make sure there is no other dhcp server");
+ log_error("running and that there's no entry for dhcp or");
+ log_error("bootp in /etc/inetd.conf. Also make sure you");
+ log_error("are not running HP JetAdmin software, which");
+ log_fatal("includes a bootp server.");
+ }
+
+#if defined(SO_BINDTODEVICE)
+ /* Bind this socket to this interface. */
+ if ((local_family != AF_INET6) && (info->ifp != NULL) &&
+ setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
+ (char *)(info -> ifp), sizeof(*(info -> ifp))) < 0) {
+ log_fatal("setsockopt: SO_BINDTODEVICE: %m");
+ }
+#endif
+
+ /* IP_BROADCAST_IF instructs the kernel which interface to send
+ * IP packets whose destination address is 255.255.255.255. These
+ * will be treated as subnet broadcasts on the interface identified
+ * by ip address (info -> primary_address). This is only known to
+ * be defined in SCO system headers, and may not be defined in all
+ * releases.
+ */
+#if defined(SCO) && defined(IP_BROADCAST_IF)
+ if (info->address_count &&
+ setsockopt(sock, IPPROTO_IP, IP_BROADCAST_IF, &info->addresses[0],
+ sizeof(info->addresses[0])) < 0)
+ log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m");
+#endif
+
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ /*
+ * If we turn on IP_RECVPKTINFO we will be able to receive
+ * the interface index information of the received packet.
+ */
+ if (family == AF_INET) {
+ int on = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO,
+ &on, sizeof(on)) != 0) {
+ log_fatal("setsockopt: IPV_RECVPKTINFO: %m");
+ }
+ }
+#endif
+
+#ifdef DHCPv6
+ /*
+ * If we turn on IPV6_PKTINFO, we will be able to receive
+ * additional information, such as the destination IP address.
+ * We need this to spot unicast packets.
+ */
+ if (family == AF_INET6) {
+ int on = 1;
+#ifdef IPV6_RECVPKTINFO
+ /* RFC3542 */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &on, sizeof(on)) != 0) {
+ log_fatal("setsockopt: IPV6_RECVPKTINFO: %m");
+ }
+#else
+ /* RFC2292 */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
+ &on, sizeof(on)) != 0) {
+ log_fatal("setsockopt: IPV6_PKTINFO: %m");
+ }
+#endif
+ }
+
+ if ((family == AF_INET6) &&
+ ((info->flags & INTERFACE_UPSTREAM) != 0)) {
+ int hop_limit = 32;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &hop_limit, sizeof(int)) < 0) {
+ log_fatal("setsockopt: IPV6_MULTICAST_HOPS: %m");
+ }
+ }
+#endif /* DHCPv6 */
+
+ return sock;
+}
+#endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */
+
+#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
+void if_register_send (info)
+ struct interface_info *info;
+{
+#ifndef USE_SOCKET_RECEIVE
+ info->wfdesc = if_register_socket(info, AF_INET, 0);
+ /* If this is a normal IPv4 address, get the hardware address. */
+ if (strcmp(info->name, "fallback") != 0)
+ get_hw_addr(info->name, &info->hw_address);
+#if defined (USE_SOCKET_FALLBACK)
+ /* Fallback only registers for send, but may need to receive as
+ well. */
+ info->rfdesc = info->wfdesc;
+#endif
+#else
+ info->wfdesc = info->rfdesc;
+#endif
+ if (!quiet_interface_discovery)
+ log_info ("Sending on Socket/%s%s%s",
+ info->name,
+ (info->shared_network ? "/" : ""),
+ (info->shared_network ?
+ info->shared_network->name : ""));
+}
+
+#if defined (USE_SOCKET_SEND)
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+#ifndef USE_SOCKET_RECEIVE
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on Socket/%s%s%s",
+ info -> name,
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_SOCKET_SEND */
+#endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */
+
+#ifdef USE_SOCKET_RECEIVE
+void if_register_receive (info)
+ struct interface_info *info;
+{
+
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ if (global_v4_socket_references == 0) {
+ global_v4_socket = if_register_socket(info, AF_INET, 0);
+ if (global_v4_socket < 0) {
+ /*
+ * if_register_socket() fatally logs if it fails to
+ * create a socket, this is just a sanity check.
+ */
+ log_fatal("Failed to create AF_INET socket %s:%d",
+ MDL);
+ }
+ }
+
+ info->rfdesc = global_v4_socket;
+ global_v4_socket_references++;
+#else
+ /* If we're using the socket API for sending and receiving,
+ we don't need to register this interface twice. */
+ info->rfdesc = if_register_socket(info, AF_INET, 0);
+#endif /* IP_PKTINFO... */
+ /* If this is a normal IPv4 address, get the hardware address. */
+ if (strcmp(info->name, "fallback") != 0)
+ get_hw_addr(info->name, &info->hw_address);
+
+ if (!quiet_interface_discovery)
+ log_info ("Listening on Socket/%s%s%s",
+ info->name,
+ (info->shared_network ? "/" : ""),
+ (info->shared_network ?
+ info->shared_network->name : ""));
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ /* Dereference the global v4 socket. */
+ if ((info->rfdesc == global_v4_socket) &&
+ (info->wfdesc == global_v4_socket) &&
+ (global_v4_socket_references > 0)) {
+ global_v4_socket_references--;
+ info->rfdesc = -1;
+ } else {
+ log_fatal("Impossible condition at %s:%d", MDL);
+ }
+
+ if (global_v4_socket_references == 0) {
+ close(global_v4_socket);
+ global_v4_socket = -1;
+ }
+#else
+ close(info->rfdesc);
+ info->rfdesc = -1;
+#endif /* IP_PKTINFO... */
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on Socket/%s%s%s",
+ info -> name,
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_SOCKET_RECEIVE */
+
+
+#ifdef DHCPv6
+/*
+ * This function joins the interface to DHCPv6 multicast groups so we will
+ * receive multicast messages.
+ */
+static void
+if_register_multicast(struct interface_info *info) {
+ int sock = info->rfdesc;
+ struct ipv6_mreq mreq;
+
+ if (inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers,
+ &mreq.ipv6mr_multiaddr) <= 0) {
+ log_fatal("inet_pton: unable to convert '%s'",
+ All_DHCP_Relay_Agents_and_Servers);
+ }
+ mreq.ipv6mr_interface = if_nametoindex(info->name);
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
+ }
+
+ /*
+ * The relay agent code sets the streams so you know which way
+ * is up and down. But a relay agent shouldn't join to the
+ * Server address, or else you get fun loops. So up or down
+ * doesn't matter, we're just using that config to sense this is
+ * a relay agent.
+ */
+ if ((info->flags & INTERFACE_STREAMS) == 0) {
+ if (inet_pton(AF_INET6, All_DHCP_Servers,
+ &mreq.ipv6mr_multiaddr) <= 0) {
+ log_fatal("inet_pton: unable to convert '%s'",
+ All_DHCP_Servers);
+ }
+ mreq.ipv6mr_interface = if_nametoindex(info->name);
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ log_fatal("setsockopt: IPV6_JOIN_GROUP: %m");
+ }
+ }
+}
+
+void
+if_register6(struct interface_info *info, int do_multicast) {
+ /* Bounce do_multicast to a stack variable because we may change it. */
+ int req_multi = do_multicast;
+
+ if (global_v6_socket_references == 0) {
+ global_v6_socket = if_register_socket(info, AF_INET6,
+ &req_multi);
+ if (global_v6_socket < 0) {
+ /*
+ * if_register_socket() fatally logs if it fails to
+ * create a socket, this is just a sanity check.
+ */
+ log_fatal("Impossible condition at %s:%d", MDL);
+ } else {
+ log_info("Bound to *:%d", ntohs(local_port));
+ }
+ }
+
+ info->rfdesc = global_v6_socket;
+ info->wfdesc = global_v6_socket;
+ global_v6_socket_references++;
+
+ if (req_multi)
+ if_register_multicast(info);
+
+ get_hw_addr(info->name, &info->hw_address);
+
+ if (!quiet_interface_discovery) {
+ if (info->shared_network != NULL) {
+ log_info("Listening on Socket/%d/%s/%s",
+ global_v6_socket, info->name,
+ info->shared_network->name);
+ log_info("Sending on Socket/%d/%s/%s",
+ global_v6_socket, info->name,
+ info->shared_network->name);
+ } else {
+ log_info("Listening on Socket/%s", info->name);
+ log_info("Sending on Socket/%s", info->name);
+ }
+ }
+}
+
+void
+if_deregister6(struct interface_info *info) {
+ /* Dereference the global v6 socket. */
+ if ((info->rfdesc == global_v6_socket) &&
+ (info->wfdesc == global_v6_socket) &&
+ (global_v6_socket_references > 0)) {
+ global_v6_socket_references--;
+ info->rfdesc = -1;
+ info->wfdesc = -1;
+ } else {
+ log_fatal("Impossible condition at %s:%d", MDL);
+ }
+
+ if (!quiet_interface_discovery) {
+ if (info->shared_network != NULL) {
+ log_info("Disabling input on Socket/%s/%s", info->name,
+ info->shared_network->name);
+ log_info("Disabling output on Socket/%s/%s", info->name,
+ info->shared_network->name);
+ } else {
+ log_info("Disabling input on Socket/%s", info->name);
+ log_info("Disabling output on Socket/%s", info->name);
+ }
+ }
+
+ if (global_v6_socket_references == 0) {
+ close(global_v6_socket);
+ global_v6_socket = -1;
+
+ log_info("Unbound from *:%d", ntohs(local_port));
+ }
+}
+#endif /* DHCPv6 */
+
+#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_FALLBACK)
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ int result;
+#ifdef IGNORE_HOSTUNREACH
+ int retry = 0;
+ do {
+#endif
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ struct in_pktinfo pktinfo;
+
+ if (interface->ifp != NULL) {
+ memset(&pktinfo, 0, sizeof (pktinfo));
+ pktinfo.ipi_ifindex = interface->ifp->ifr_index;
+ if (setsockopt(interface->wfdesc, IPPROTO_IP,
+ IP_PKTINFO, (char *)&pktinfo,
+ sizeof(pktinfo)) < 0)
+ log_fatal("setsockopt: IP_PKTINFO: %m");
+ }
+#endif
+ result = sendto (interface -> wfdesc, (char *)raw, len, 0,
+ (struct sockaddr *)to, sizeof *to);
+#ifdef IGNORE_HOSTUNREACH
+ } while (to -> sin_addr.s_addr == htonl (INADDR_BROADCAST) &&
+ result < 0 &&
+ (errno == EHOSTUNREACH ||
+ errno == ECONNREFUSED) &&
+ retry++ < 10);
+#endif
+ if (result < 0) {
+ log_error ("send_packet: %m");
+ if (errno == ENETUNREACH)
+ log_error ("send_packet: please consult README file%s",
+ " regarding broadcast address.");
+ }
+ return result;
+}
+
+#endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */
+
+#ifdef DHCPv6
+/*
+ * Solaris 9 is missing the CMSG_LEN and CMSG_SPACE macros, so we will
+ * synthesize them (based on the BIND 9 technique).
+ */
+
+#ifndef CMSG_LEN
+static size_t CMSG_LEN(size_t len) {
+ size_t hdrlen;
+ /*
+ * Cast NULL so that any pointer arithmetic performed by CMSG_DATA
+ * is correct.
+ */
+ hdrlen = (size_t)CMSG_DATA(((struct cmsghdr *)NULL));
+ return hdrlen + len;
+}
+#endif /* !CMSG_LEN */
+
+#ifndef CMSG_SPACE
+static size_t CMSG_SPACE(size_t len) {
+ struct msghdr msg;
+ struct cmsghdr *cmsgp;
+
+ /*
+ * XXX: The buffer length is an ad-hoc value, but should be enough
+ * in a practical sense.
+ */
+ union {
+ struct cmsghdr cmsg_sizer;
+ u_int8_t pktinfo_sizer[sizeof(struct cmsghdr) + 1024];
+ } dummybuf;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = &dummybuf;
+ msg.msg_controllen = sizeof(dummybuf);
+
+ cmsgp = (struct cmsghdr *)&dummybuf;
+ cmsgp->cmsg_len = CMSG_LEN(len);
+
+ cmsgp = CMSG_NXTHDR(&msg, cmsgp);
+ if (cmsgp != NULL) {
+ return (char *)cmsgp - (char *)msg.msg_control;
+ } else {
+ return 0;
+ }
+}
+#endif /* !CMSG_SPACE */
+
+#endif /* DHCPv6 */
+
+#if defined(DHCPv6) || \
+ (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
+ defined(USE_V4_PKTINFO))
+/*
+ * For both send_packet6() and receive_packet6() we need to allocate
+ * space for the cmsg header information. We do this once and reuse
+ * the buffer. We also need the control buf for send_packet() and
+ * receive_packet() when we use a single socket and IP_PKTINFO to
+ * send the packet out the correct interface.
+ */
+static void *control_buf = NULL;
+static size_t control_buf_len = 0;
+
+static void
+allocate_cmsg_cbuf(void) {
+ control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ control_buf = dmalloc(control_buf_len, MDL);
+ return;
+}
+#endif /* DHCPv6, IP_PKTINFO ... */
+
+#ifdef DHCPv6
+/*
+ * For both send_packet6() and receive_packet6() we need to use the
+ * sendmsg()/recvmsg() functions rather than the simpler send()/recv()
+ * functions.
+ *
+ * In the case of send_packet6(), we need to do this in order to insure
+ * that the reply packet leaves on the same interface that it arrived
+ * on.
+ *
+ * In the case of receive_packet6(), we need to do this in order to
+ * get the IP address the packet was sent to. This is used to identify
+ * whether a packet is multicast or unicast.
+ *
+ * Helpful man pages: recvmsg, readv (talks about the iovec stuff), cmsg.
+ *
+ * Also see the sections in RFC 3542 about IPV6_PKTINFO.
+ */
+
+/* Send an IPv6 packet */
+ssize_t send_packet6(struct interface_info *interface,
+ const unsigned char *raw, size_t len,
+ struct sockaddr_in6 *to) {
+ struct msghdr m;
+ struct iovec v;
+ int result;
+ struct in6_pktinfo *pktinfo;
+ struct cmsghdr *cmsg;
+
+ /*
+ * If necessary allocate space for the control message header.
+ * The space is common between send and receive.
+ */
+
+ if (control_buf == NULL) {
+ allocate_cmsg_cbuf();
+ if (control_buf == NULL) {
+ log_error("send_packet6: unable to allocate cmsg header");
+ return(ENOMEM);
+ }
+ }
+ memset(control_buf, 0, control_buf_len);
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Set the target address we're sending to.
+ */
+ m.msg_name = to;
+ m.msg_namelen = sizeof(*to);
+
+ /*
+ * Set the data buffer we're sending. (Using this wacky
+ * "scatter-gather" stuff... we only have a single chunk
+ * of data to send, so we declare a single vector entry.)
+ */
+ v.iov_base = (char *)raw;
+ v.iov_len = len;
+ m.msg_iov = &v;
+ m.msg_iovlen = 1;
+
+ /*
+ * Setting the interface is a bit more involved.
+ *
+ * We have to create a "control message", and set that to
+ * define the IPv6 packet information. We could set the
+ * source address if we wanted, but we can safely let the
+ * kernel decide what that should be.
+ */
+ m.msg_control = control_buf;
+ m.msg_controllen = control_buf_len;
+ cmsg = CMSG_FIRSTHDR(&m);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ memset(pktinfo, 0, sizeof(*pktinfo));
+ pktinfo->ipi6_ifindex = if_nametoindex(interface->name);
+ m.msg_controllen = cmsg->cmsg_len;
+
+ result = sendmsg(interface->wfdesc, &m, 0);
+ if (result < 0) {
+ log_error("send_packet6: %m");
+ }
+ return result;
+}
+#endif /* DHCPv6 */
+
+#ifdef USE_SOCKET_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+#if !defined(USE_V4_PKTINFO)
+ SOCKLEN_T flen = sizeof *from;
+#endif
+ int result;
+
+ /*
+ * The normal Berkeley socket interface doesn't give us any way
+ * to know what hardware interface we received the message on,
+ * but we should at least make sure the structure is emptied.
+ */
+ memset(hfrom, 0, sizeof(*hfrom));
+
+#ifdef IGNORE_HOSTUNREACH
+ int retry = 0;
+ do {
+#endif
+
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
+ struct msghdr m;
+ struct iovec v;
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pktinfo;
+ unsigned int ifindex;
+ int found_pktinfo;
+
+ /*
+ * If necessary allocate space for the control message header.
+ * The space is common between send and receive.
+ */
+ if (control_buf == NULL) {
+ allocate_cmsg_cbuf();
+ if (control_buf == NULL) {
+ log_error("receive_packet: unable to allocate cmsg "
+ "header");
+ return(ENOMEM);
+ }
+ }
+ memset(control_buf, 0, control_buf_len);
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Point so we can get the from address.
+ */
+ m.msg_name = from;
+ m.msg_namelen = sizeof(*from);
+
+ /*
+ * Set the data buffer we're receiving. (Using this wacky
+ * "scatter-gather" stuff... but we that doesn't really make
+ * sense for us, so we use a single vector entry.)
+ */
+ v.iov_base = buf;
+ v.iov_len = len;
+ m.msg_iov = &v;
+ m.msg_iovlen = 1;
+
+ /*
+ * Getting the interface is a bit more involved.
+ *
+ * We set up some space for a "control message". We have
+ * previously asked the kernel to give us packet
+ * information (when we initialized the interface), so we
+ * should get the destination address from that.
+ */
+ m.msg_control = control_buf;
+ m.msg_controllen = control_buf_len;
+
+ result = recvmsg(interface->rfdesc, &m, 0);
+
+ if (result >= 0) {
+ /*
+ * If we did read successfully, then we need to loop
+ * through the control messages we received and
+ * find the one with our destination address.
+ *
+ * We also keep a flag to see if we found it. If we
+ * didn't, then we consider this to be an error.
+ */
+ found_pktinfo = 0;
+ cmsg = CMSG_FIRSTHDR(&m);
+ while (cmsg != NULL) {
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_PKTINFO)) {
+ pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ ifindex = pktinfo->ipi_ifindex;
+ /*
+ * We pass the ifindex back to the caller
+ * using the unused hfrom parameter avoiding
+ * interface changes between sockets and
+ * the discover code.
+ */
+ memcpy(hfrom->hbuf, &ifindex, sizeof(ifindex));
+ found_pktinfo = 1;
+ }
+ cmsg = CMSG_NXTHDR(&m, cmsg);
+ }
+ if (!found_pktinfo) {
+ result = -1;
+ errno = EIO;
+ }
+ }
+#else
+ result = recvfrom (interface -> rfdesc, (char *)buf, len, 0,
+ (struct sockaddr *)from, &flen);
+#endif /* IP_PKTINFO ... */
+#ifdef IGNORE_HOSTUNREACH
+ } while (result < 0 &&
+ (errno == EHOSTUNREACH ||
+ errno == ECONNREFUSED) &&
+ retry++ < 10);
+#endif
+ return result;
+}
+
+#endif /* USE_SOCKET_RECEIVE */
+
+#ifdef DHCPv6
+ssize_t
+receive_packet6(struct interface_info *interface,
+ unsigned char *buf, size_t len,
+ struct sockaddr_in6 *from, struct in6_addr *to_addr,
+ unsigned int *if_idx)
+{
+ struct msghdr m;
+ struct iovec v;
+ int result;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pktinfo;
+ int found_pktinfo;
+
+ /*
+ * If necessary allocate space for the control message header.
+ * The space is common between send and receive.
+ */
+ if (control_buf == NULL) {
+ allocate_cmsg_cbuf();
+ if (control_buf == NULL) {
+ log_error("receive_packet6: unable to allocate cmsg "
+ "header");
+ return(ENOMEM);
+ }
+ }
+ memset(control_buf, 0, control_buf_len);
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Point so we can get the from address.
+ */
+ m.msg_name = from;
+ m.msg_namelen = sizeof(*from);
+
+ /*
+ * Set the data buffer we're receiving. (Using this wacky
+ * "scatter-gather" stuff... but we that doesn't really make
+ * sense for us, so we use a single vector entry.)
+ */
+ v.iov_base = buf;
+ v.iov_len = len;
+ m.msg_iov = &v;
+ m.msg_iovlen = 1;
+
+ /*
+ * Getting the interface is a bit more involved.
+ *
+ * We set up some space for a "control message". We have
+ * previously asked the kernel to give us packet
+ * information (when we initialized the interface), so we
+ * should get the destination address from that.
+ */
+ m.msg_control = control_buf;
+ m.msg_controllen = control_buf_len;
+
+ result = recvmsg(interface->rfdesc, &m, 0);
+
+ if (result >= 0) {
+ /*
+ * If we did read successfully, then we need to loop
+ * through the control messages we received and
+ * find the one with our destination address.
+ *
+ * We also keep a flag to see if we found it. If we
+ * didn't, then we consider this to be an error.
+ */
+ found_pktinfo = 0;
+ cmsg = CMSG_FIRSTHDR(&m);
+ while (cmsg != NULL) {
+ if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
+ (cmsg->cmsg_type == IPV6_PKTINFO)) {
+ pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ *to_addr = pktinfo->ipi6_addr;
+ *if_idx = pktinfo->ipi6_ifindex;
+ found_pktinfo = 1;
+ }
+ cmsg = CMSG_NXTHDR(&m, cmsg);
+ }
+ if (!found_pktinfo) {
+ result = -1;
+ errno = EIO;
+ }
+ }
+
+ return result;
+}
+#endif /* DHCPv6 */
+
+#if defined (USE_SOCKET_FALLBACK)
+/* This just reads in a packet and silently discards it. */
+
+isc_result_t fallback_discard (object)
+ omapi_object_t *object;
+{
+ char buf [1540];
+ struct sockaddr_in from;
+ SOCKLEN_T flen = sizeof from;
+ int status;
+ struct interface_info *interface;
+
+ if (object -> type != dhcp_type_interface)
+ return DHCP_R_INVALIDARG;
+ interface = (struct interface_info *)object;
+
+ status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0,
+ (struct sockaddr *)&from, &flen);
+#if defined (DEBUG)
+ /* Only report fallback discard errors if we're debugging. */
+ if (status < 0) {
+ log_error ("fallback_discard: %m");
+ return ISC_R_UNEXPECTED;
+ }
+#endif
+ return ISC_R_SUCCESS;
+}
+#endif /* USE_SOCKET_FALLBACK */
+
+#if defined (USE_SOCKET_SEND)
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 0;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+#if defined (SOCKET_CAN_RECEIVE_UNICAST_UNCONFIGURED)
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+#if defined(SO_BINDTODEVICE) || \
+ (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
+ defined(USE_V4_PKTINFO))
+ return(1);
+#else
+ return(0);
+#endif
+}
+
+/* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise,
+ do not. */
+
+void maybe_setup_fallback ()
+{
+#if defined (USE_SOCKET_FALLBACK)
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ fbi -> wfdesc = if_register_socket (fbi, AF_INET, 0);
+ fbi -> rfdesc = fbi -> wfdesc;
+ log_info ("Sending on Socket/%s%s%s",
+ fbi -> name,
+ (fbi -> shared_network ? "/" : ""),
+ (fbi -> shared_network ?
+ fbi -> shared_network -> name : ""));
+
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+#endif
+}
+
+
+#if defined(sun) && defined(USE_V4_PKTINFO)
+/* This code assumes the existence of SIOCGLIFHWADDR */
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+ struct sockaddr_dl *dladdrp;
+ int rv, sock, i;
+ struct lifreq lifr;
+
+ memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ /*
+ * Check if the interface is a virtual or IPMP interface - in those
+ * cases it has no hw address, so generate a random one.
+ */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
+ ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) {
+ if (sock != -1)
+ (void) close(sock);
+
+#ifdef DHCPv6
+ /*
+ * If approrpriate try this with an IPv6 socket
+ */
+ if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 &&
+ ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) {
+ goto flag_check;
+ }
+ if (sock != -1)
+ (void) close(sock);
+#endif
+ log_fatal("Couldn't get interface flags for %s: %m", name);
+
+ }
+
+ flag_check:
+ if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) {
+ hw->hlen = sizeof (hw->hbuf);
+ srandom((long)gethrtime());
+
+ for (i = 0; i < hw->hlen; ++i) {
+ hw->hbuf[i] = random() % 256;
+ }
+
+ if (sock != -1)
+ (void) close(sock);
+ return;
+ }
+
+ if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0)
+ log_fatal("Couldn't get interface hardware address for %s: %m",
+ name);
+ dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr;
+ hw->hlen = dladdrp->sdl_alen;
+ memcpy(hw->hbuf, LLADDR(dladdrp), hw->hlen);
+
+ if (sock != -1)
+ (void) close(sock);
+}
+#endif /* defined(sun) */
+
+#endif /* USE_SOCKET_SEND */
diff --git a/common/tables.c b/common/tables.c
new file mode 100644
index 0000000..10e2ce0
--- /dev/null
+++ b/common/tables.c
@@ -0,0 +1,1414 @@
+/* tables.c
+
+ Tables of information... */
+
+/*
+ * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+/* XXXDPN: Moved here from hash.c, when it moved to libomapi. Not sure
+ where these really belong. */
+HASH_FUNCTIONS (group, const char *, struct group_object, group_hash_t,
+ group_reference, group_dereference, do_string_hash)
+HASH_FUNCTIONS (universe, const char *, struct universe, universe_hash_t, 0, 0,
+ do_case_hash)
+HASH_FUNCTIONS (option_name, const char *, struct option, option_name_hash_t,
+ option_reference, option_dereference, do_case_hash)
+HASH_FUNCTIONS (option_code, const unsigned *, struct option,
+ option_code_hash_t, option_reference, option_dereference,
+ do_number_hash)
+
+/* DHCP Option names, formats and codes, from RFC1533.
+
+ Format codes:
+
+ I - IPv4 address
+ 6 - IPv6 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
+ T - Lease Time, 32-bit unsigned integer implying a number of seconds from
+ some event. The special all-ones value means 'infinite'. May either
+ be printed as a decimal, eg, "3600", or as this name, eg, "infinite".
+ f - flag (true or false)
+ A - array of whatever precedes (e.g., IA means array of IP addresses)
+ a - array of the preceding character (e.g., IIa means two or more IP
+ addresses)
+ U - name of an option space (universe)
+ F - implicit flag - the presence of the option indicates that the
+ flag is true.
+ o - the preceding value is optional.
+ E - encapsulation, string or colon-separated hex list (the latter
+ two for parsing). E is followed by a text string containing
+ the name of the option space to encapsulate, followed by a '.'.
+ If the E is immediately followed by '.', the applicable vendor
+ option space is used if one is defined.
+ e - If an encapsulation directive is not the first thing in the string,
+ the option scanner requires an efficient way to find the encapsulation.
+ This is done by placing a 'e' at the beginning of the option. The
+ 'e' has no other purpose, and is not required if 'E' is the first
+ thing in the option.
+ X - either an ASCII string or binary data. On output, the string is
+ scanned to see if it's printable ASCII and, if so, output as a
+ quoted string. If not, it's output as colon-separated hex. On
+ input, the option can be specified either as a quoted string or as
+ a colon-separated hex list.
+ N - enumeration. N is followed by a text string containing
+ the name of the set of enumeration values to parse or emit,
+ followed by a '.'. The width of the data is specified in the
+ named enumeration. Named enumerations are tracked in parse.c.
+ d - Domain name (i.e., FOO or FOO.BAR).
+ D - Domain list (i.e., example.com eng.example.com)
+ c - When following a 'D' atom, enables compression pointers.
+ Z - Zero-length option
+*/
+
+struct universe dhcp_universe;
+static struct option dhcp_options[] = {
+ { "subnet-mask", "I", &dhcp_universe, 1, 1 },
+ { "time-offset", "l", &dhcp_universe, 2, 1 },
+ { "routers", "IA", &dhcp_universe, 3, 1 },
+ { "time-servers", "IA", &dhcp_universe, 4, 1 },
+ { "ien116-name-servers", "IA", &dhcp_universe, 5, 1 },
+ { "domain-name-servers", "IA", &dhcp_universe, 6, 1 },
+ { "log-servers", "IA", &dhcp_universe, 7, 1 },
+ { "cookie-servers", "IA", &dhcp_universe, 8, 1 },
+ { "lpr-servers", "IA", &dhcp_universe, 9, 1 },
+ { "impress-servers", "IA", &dhcp_universe, 10, 1 },
+ { "resource-location-servers", "IA", &dhcp_universe, 11, 1 },
+ { "host-name", "t", &dhcp_universe, 12, 1 },
+ { "boot-size", "S", &dhcp_universe, 13, 1 },
+ { "merit-dump", "t", &dhcp_universe, 14, 1 },
+ { "domain-name", "t", &dhcp_universe, 15, 1 },
+ { "swap-server", "I", &dhcp_universe, 16, 1 },
+ { "root-path", "t", &dhcp_universe, 17, 1 },
+ { "extensions-path", "t", &dhcp_universe, 18, 1 },
+ { "ip-forwarding", "f", &dhcp_universe, 19, 1 },
+ { "non-local-source-routing", "f", &dhcp_universe, 20, 1 },
+ { "policy-filter", "IIA", &dhcp_universe, 21, 1 },
+ { "max-dgram-reassembly", "S", &dhcp_universe, 22, 1 },
+ { "default-ip-ttl", "B", &dhcp_universe, 23, 1 },
+ { "path-mtu-aging-timeout", "L", &dhcp_universe, 24, 1 },
+ { "path-mtu-plateau-table", "SA", &dhcp_universe, 25, 1 },
+ { "interface-mtu", "S", &dhcp_universe, 26, 1 },
+ { "all-subnets-local", "f", &dhcp_universe, 27, 1 },
+ { "broadcast-address", "I", &dhcp_universe, 28, 1 },
+ { "perform-mask-discovery", "f", &dhcp_universe, 29, 1 },
+ { "mask-supplier", "f", &dhcp_universe, 30, 1 },
+ { "router-discovery", "f", &dhcp_universe, 31, 1 },
+ { "router-solicitation-address", "I", &dhcp_universe, 32, 1 },
+ { "static-routes", "IIA", &dhcp_universe, 33, 1 },
+ { "trailer-encapsulation", "f", &dhcp_universe, 34, 1 },
+ { "arp-cache-timeout", "L", &dhcp_universe, 35, 1 },
+ { "ieee802-3-encapsulation", "f", &dhcp_universe, 36, 1 },
+ { "default-tcp-ttl", "B", &dhcp_universe, 37, 1 },
+ { "tcp-keepalive-interval", "L", &dhcp_universe, 38, 1 },
+ { "tcp-keepalive-garbage", "f", &dhcp_universe, 39, 1 },
+ { "nis-domain", "t", &dhcp_universe, 40, 1 },
+ { "nis-servers", "IA", &dhcp_universe, 41, 1 },
+ { "ntp-servers", "IA", &dhcp_universe, 42, 1 },
+ { "vendor-encapsulated-options", "E.", &dhcp_universe, 43, 1 },
+ { "netbios-name-servers", "IA", &dhcp_universe, 44, 1 },
+ { "netbios-dd-server", "IA", &dhcp_universe, 45, 1 },
+ { "netbios-node-type", "B", &dhcp_universe, 46, 1 },
+ { "netbios-scope", "t", &dhcp_universe, 47, 1 },
+ { "font-servers", "IA", &dhcp_universe, 48, 1 },
+ { "x-display-manager", "IA", &dhcp_universe, 49, 1 },
+ { "dhcp-requested-address", "I", &dhcp_universe, 50, 1 },
+ { "dhcp-lease-time", "L", &dhcp_universe, 51, 1 },
+ { "dhcp-option-overload", "B", &dhcp_universe, 52, 1 },
+ { "dhcp-message-type", "B", &dhcp_universe, 53, 1 },
+ { "dhcp-server-identifier", "I", &dhcp_universe, 54, 1 },
+ { "dhcp-parameter-request-list", "BA", &dhcp_universe, 55, 1 },
+ { "dhcp-message", "t", &dhcp_universe, 56, 1 },
+ { "dhcp-max-message-size", "S", &dhcp_universe, 57, 1 },
+ { "dhcp-renewal-time", "L", &dhcp_universe, 58, 1 },
+ { "dhcp-rebinding-time", "L", &dhcp_universe, 59, 1 },
+ { "vendor-class-identifier", "X", &dhcp_universe, 60, 1 },
+ { "dhcp-client-identifier", "X", &dhcp_universe, 61, 1 },
+ { "nwip-domain", "t", &dhcp_universe, 62, 1 },
+ { "nwip-suboptions", "Enwip.", &dhcp_universe, 63, 1 },
+ { "nisplus-domain", "t", &dhcp_universe, 64, 1 },
+ { "nisplus-servers", "IA", &dhcp_universe, 65, 1 },
+ { "tftp-server-name", "t", &dhcp_universe, 66, 1 },
+ { "bootfile-name", "t", &dhcp_universe, 67, 1 },
+ { "mobile-ip-home-agent", "IA", &dhcp_universe, 68, 1 },
+ { "smtp-server", "IA", &dhcp_universe, 69, 1 },
+ { "pop-server", "IA", &dhcp_universe, 70, 1 },
+ { "nntp-server", "IA", &dhcp_universe, 71, 1 },
+ { "www-server", "IA", &dhcp_universe, 72, 1 },
+ { "finger-server", "IA", &dhcp_universe, 73, 1 },
+ { "irc-server", "IA", &dhcp_universe, 74, 1 },
+ { "streettalk-server", "IA", &dhcp_universe, 75, 1 },
+ { "streettalk-directory-assistance-server", "IA",
+ &dhcp_universe, 76, 1 },
+ { "user-class", "t", &dhcp_universe, 77, 1 },
+ { "slp-directory-agent", "fIa", &dhcp_universe, 78, 1 },
+ { "slp-service-scope", "fto", &dhcp_universe, 79, 1 },
+ /* 80 is the zero-length rapid-commit (RFC 4039) */
+ { "fqdn", "Efqdn.", &dhcp_universe, 81, 1 },
+ { "relay-agent-information", "Eagent.", &dhcp_universe, 82, 1 },
+ /* 83 is iSNS (RFC 4174) */
+ /* 84 is unassigned */
+ { "nds-servers", "IA", &dhcp_universe, 85, 1 },
+ { "nds-tree-name", "t", &dhcp_universe, 86, 1 },
+ { "nds-context", "t", &dhcp_universe, 87, 1 },
+
+ /* Note: RFC4280 fails to identify if the DHCPv4 option is to use
+ * compression pointers or not. Assume not.
+ */
+ { "bcms-controller-names", "D", &dhcp_universe, 88, 1 },
+ { "bcms-controller-address", "Ia", &dhcp_universe, 89, 1 },
+
+ /* 90 is the authentication option (RFC 3118) */
+
+ { "client-last-transaction-time", "L", &dhcp_universe, 91, 1 },
+ { "associated-ip", "Ia", &dhcp_universe, 92, 1 },
+#if 0
+ /* Defined by RFC 4578 */
+ { "pxe-system-type", "S", &dhcp_universe, 93, 1 },
+ { "pxe-interface-id", "BBB", &dhcp_universe, 94, 1 },
+ { "pxe-client-id", "BX", &dhcp_universe, 97, 1 },
+#endif
+ { "uap-servers", "t", &dhcp_universe, 98, 1 },
+ { "netinfo-server-address", "Ia", &dhcp_universe, 112, 1 },
+ { "netinfo-server-tag", "t", &dhcp_universe, 113, 1 },
+ { "default-url", "t", &dhcp_universe, 114, 1 },
+ { "subnet-selection", "I", &dhcp_universe, 118, 1 },
+ { "domain-search", "Dc", &dhcp_universe, 119, 1 },
+ { "vivco", "Evendor-class.", &dhcp_universe, 124, 1 },
+ { "vivso", "Evendor.", &dhcp_universe, 125, 1 },
+#if 0
+ /* Referenced by RFC 4578.
+ * DO NOT UNCOMMENT THESE DEFINITIONS: these names are placeholders
+ * and will not be used in future versions of the software.
+ */
+ { "pxe-undefined-1", "X", &dhcp_universe, 128, 1 },
+ { "pxe-undefined-2", "X", &dhcp_universe, 129, 1 },
+ { "pxe-undefined-3", "X", &dhcp_universe, 130, 1 },
+ { "pxe-undefined-4", "X", &dhcp_universe, 131, 1 },
+ { "pxe-undefined-5", "X", &dhcp_universe, 132, 1 },
+ { "pxe-undefined-6", "X", &dhcp_universe, 133, 1 },
+ { "pxe-undefined-7", "X", &dhcp_universe, 134, 1 },
+ { "pxe-undefined-8", "X", &dhcp_universe, 135, 1 },
+#endif
+#if 0
+ /* Not defined by RFC yet */
+ { "tftp-server-address", "Ia", &dhcp_universe, 150, 1 },
+#endif
+#if 0
+ /* PXELINUX options: defined by RFC 5071 */
+ { "pxelinux-magic", "BBBB", &dhcp_universe, 208, 1 },
+ { "loader-configfile", "t", &dhcp_universe, 209, 1 },
+ { "loader-pathprefix", "t", &dhcp_universe, 210, 1 },
+ { "loader-reboottime", "L", &dhcp_universe, 211, 1 },
+#endif
+#if 0
+ /* Not defined by RFC yet */
+ { "vss-info", "BX", &dhcp_universe, 221, 1 },
+#endif
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe nwip_universe;
+static struct option nwip_options[] = {
+ { "illegal-1", "", &nwip_universe, 1, 1 },
+ { "illegal-2", "", &nwip_universe, 2, 1 },
+ { "illegal-3", "", &nwip_universe, 3, 1 },
+ { "illegal-4", "", &nwip_universe, 4, 1 },
+ { "nsq-broadcast", "f", &nwip_universe, 5, 1 },
+ { "preferred-dss", "IA", &nwip_universe, 6, 1 },
+ { "nearest-nwip-server", "IA", &nwip_universe, 7, 1 },
+ { "autoretries", "B", &nwip_universe, 8, 1 },
+ { "autoretry-secs", "B", &nwip_universe, 9, 1 },
+ { "nwip-1-1", "f", &nwip_universe, 10, 1 },
+ { "primary-dss", "I", &nwip_universe, 11, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+/* Note that the "FQDN suboption space" does not reflect the FQDN option
+ * format - rather, this is a handy "virtualization" of a flat option
+ * which makes manual configuration and presentation of some of its
+ * contents easier (each of these suboptions is a fixed-space field within
+ * the fqdn contents - domain and host names are derived from a common field,
+ * and differ in the left and right hand side of the leftmost dot, fqdn is
+ * the combination of the two).
+ *
+ * Note further that the DHCPv6 and DHCPv4 'fqdn' options use the same
+ * virtualized option space to store their work.
+ */
+
+struct universe fqdn_universe;
+struct universe fqdn6_universe;
+static struct option fqdn_options[] = {
+ { "no-client-update", "f", &fqdn_universe, 1, 1 },
+ { "server-update", "f", &fqdn_universe, 2, 1 },
+ { "encoded", "f", &fqdn_universe, 3, 1 },
+ { "rcode1", "B", &fqdn_universe, 4, 1 },
+ { "rcode2", "B", &fqdn_universe, 5, 1 },
+ { "hostname", "t", &fqdn_universe, 6, 1 },
+ { "domainname", "t", &fqdn_universe, 7, 1 },
+ { "fqdn", "t", &fqdn_universe, 8, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe vendor_class_universe;
+static struct option vendor_class_options[] = {
+ { "isc", "X", &vendor_class_universe, 2495, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe vendor_universe;
+static struct option vendor_options[] = {
+ { "isc", "Eisc.", &vendor_universe, 2495, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe isc_universe;
+static struct option isc_options [] = {
+ { "media", "t", &isc_universe, 1, 1 },
+ { "update-assist", "X", &isc_universe, 2, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe dhcpv6_universe;
+static struct option dhcpv6_options[] = {
+
+ /* RFC3315 OPTIONS */
+
+ /* Client and server DUIDs are opaque fields, but marking them
+ * up somewhat makes configuration easier.
+ */
+ { "client-id", "X", &dhcpv6_universe, 1, 1 },
+ { "server-id", "X", &dhcpv6_universe, 2, 1 },
+
+ /* ia-* options actually have at their ends a space for options
+ * that are specific to this instance of the option. We can not
+ * handle this yet at this stage of development, so the encoding
+ * of these options is unspecified ("X").
+ */
+ { "ia-na", "X", &dhcpv6_universe, 3, 1 },
+ { "ia-ta", "X", &dhcpv6_universe, 4, 1 },
+ { "ia-addr", "X", &dhcpv6_universe, 5, 1 },
+
+ /* "oro" is DHCPv6 speak for "parameter-request-list" */
+ { "oro", "SA", &dhcpv6_universe, 6, 1 },
+
+ { "preference", "B", &dhcpv6_universe, 7, 1 },
+ { "elapsed-time", "S", &dhcpv6_universe, 8, 1 },
+ { "relay-msg", "X", &dhcpv6_universe, 9, 1 },
+
+ /* Option code 10 is curiously unassigned. */
+ /*
+ * In draft-ietf-dhc-dhcpv6-25 there were two OPTION_CLIENT_MSG and
+ * OPTION_SERVER_MSG options. They were eventually unified as
+ * OPTION_RELAY_MSG, hence no option with value of 10.
+ */
+#if 0
+ /* XXX: missing suitable atoms for the auth option. We may want
+ * to 'virtually encapsulate' this option a la the fqdn option
+ * seeing as it is processed explicitly by the server and unlikely
+ * to be configured by hand by users as such.
+ */
+ { "auth", "Nauth-protocol.Nauth-algorithm.Nrdm-type.LLX",
+ &dhcpv6_universe, 11, 1 },
+#endif
+ { "unicast", "6", &dhcpv6_universe, 12, 1 },
+ { "status-code", "Nstatus-codes.to", &dhcpv6_universe, 13, 1 },
+ { "rapid-commit", "Z", &dhcpv6_universe, 14, 1 },
+#if 0
+ /* XXX: user-class contents are of the form "StA" where the
+ * integer describes the length of the text field. We don't have
+ * an atom for pre-determined-length octet strings yet, so we
+ * can't quite do these two.
+ */
+ { "user-class", "X", &dhcpv6_universe, 15, 1 },
+ { "vendor-class", "X", &dhcpv6_universe, 16, 1 },
+#endif
+ { "vendor-opts", "Evsio.", &dhcpv6_universe, 17, 1 },
+ { "interface-id", "X", &dhcpv6_universe, 18, 1 },
+ { "reconf-msg", "Ndhcpv6-messages.", &dhcpv6_universe, 19, 1 },
+ { "reconf-accept", "Z", &dhcpv6_universe, 20, 1 },
+
+ /* RFC3319 OPTIONS */
+
+ /* Of course: we would HAVE to have a different atom for
+ * domain names without compression. Typical.
+ */
+ { "sip-servers-names", "D", &dhcpv6_universe, 21, 1 },
+ { "sip-servers-addresses", "6A", &dhcpv6_universe, 22, 1 },
+
+ /* RFC3646 OPTIONS */
+
+ { "name-servers", "6A", &dhcpv6_universe, 23, 1 },
+ { "domain-search", "D", &dhcpv6_universe, 24, 1 },
+
+ /* RFC3633 OPTIONS */
+
+ { "ia-pd", "X", &dhcpv6_universe, 25, 1 },
+ { "ia-prefix", "X", &dhcpv6_universe, 26, 1 },
+
+ /* RFC3898 OPTIONS */
+
+ { "nis-servers", "6A", &dhcpv6_universe, 27, 1 },
+ { "nisp-servers", "6A", &dhcpv6_universe, 28, 1 },
+ { "nis-domain-name", "D", &dhcpv6_universe, 29, 1 },
+ { "nisp-domain-name", "D", &dhcpv6_universe, 30, 1 },
+
+ /* RFC4075 OPTIONS */
+ { "sntp-servers", "6A", &dhcpv6_universe, 31, 1 },
+
+ /* RFC4242 OPTIONS */
+
+ { "info-refresh-time", "T", &dhcpv6_universe, 32, 1 },
+
+ /* RFC4280 OPTIONS */
+
+ { "bcms-server-d", "D", &dhcpv6_universe, 33, 1 },
+ { "bcms-server-a", "6A", &dhcpv6_universe, 34, 1 },
+
+ /* Note that 35 is not assigned. */
+
+ /* Not yet considering for inclusion. */
+#if 0
+ /* RFC4776 OPTIONS */
+
+ { "geoconf-civic", "X", &dhcpv6_universe, 36, 1 },
+#endif
+
+ /* RFC4649 OPTIONS */
+
+ /* The remote-id option looks like the VSIO option, but for all
+ * intents and purposes we only need to treat the entire field
+ * like a globally unique identifier (and if we create such an
+ * option, ensure the first 4 bytes are our enterprise-id followed
+ * by a globally unique ID so long as you're within that enterprise
+ * id). So we'll use "X" for now unless someone grumbles.
+ */
+ { "remote-id", "X", &dhcpv6_universe, 37, 1 },
+
+ /* RFC4580 OPTIONS */
+
+ { "subscriber-id", "X", &dhcpv6_universe, 38, 1 },
+
+ /* RFC4704 OPTIONS */
+
+ /* The DHCPv6 FQDN option is...weird.
+ *
+ * We use the same "virtual" encapsulated space as DHCPv4's FQDN
+ * option, so it can all be configured in one place. Since the
+ * options system does not support multiple inheritance, we use
+ * a 'shill' layer to perform the different protocol conversions,
+ * and to redirect any queries in the DHCPv4 FQDN's space.
+ */
+ { "fqdn", "Efqdn6-if-you-see-me-its-a-bug-bug-bug.",
+ &dhcpv6_universe, 39, 1 },
+
+ /* Not yet considering for inclusion. */
+#if 0
+ /* draft-ietf-dhc-paa-option-05 */
+ { "pana-agent", "6A", &dhcpv6_universe, 40, 1 },
+
+ /* RFC4833 OPTIONS */
+
+ { "new-posix-timezone", "t", &dhcpv6_universe, 41, 1 },
+ { "new-tzdb-timezone", "t", &dhcpv6_universe, 42, 1 },
+
+ /* RFC4994 OPTIONS */
+
+ { "ero", "SA", &dhcpv6_universe, 43, 1 },
+#endif
+
+ /* RFC5007 OPTIONS */
+
+ { "lq-query", "X", &dhcpv6_universe, 44, 1 },
+ { "client-data", "X", &dhcpv6_universe, 45, 1 },
+ { "clt-time", "L", &dhcpv6_universe, 46, 1 },
+ { "lq-relay-data", "6X", &dhcpv6_universe, 47, 1 },
+ { "lq-client-link", "6A", &dhcpv6_universe, 48, 1 },
+
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct enumeration_value dhcpv6_duid_type_values[] = {
+ { "duid-llt", DUID_LLT }, /* Link-Local Plus Time */
+ { "duid-en", DUID_EN }, /* DUID based upon enterprise-ID. */
+ { "duid-ll", DUID_LL }, /* DUID from Link Local address only. */
+ { NULL, 0 }
+};
+
+struct enumeration dhcpv6_duid_types = {
+ NULL,
+ "duid-types", 2,
+ dhcpv6_duid_type_values
+};
+
+struct enumeration_value dhcpv6_status_code_values[] = {
+ { "success", 0 }, /* Success */
+ { "UnspecFail", 1 }, /* Failure, for unspecified reasons. */
+ { "NoAddrsAvail", 2 }, /* Server has no addresses to assign. */
+ { "NoBinding", 3 }, /* Client record (binding) unavailable. */
+ { "NotOnLink", 4 }, /* Bad prefix for the link. */
+ { "UseMulticast", 5 }, /* Not just good advice. It's the law. */
+ { "NoPrefixAvail", 6 }, /* Server has no prefixes to assign. */
+ { "UnknownQueryType", 7 }, /* Query-type unknown/unsupported. */
+ { "MalformedQuery", 8 }, /* Leasequery not valid. */
+ { "NotConfigured", 9 }, /* The target address is not in config. */
+ { "NotAllowed", 10 }, /* Server doesn't allow the leasequery. */
+ { NULL, 0 }
+};
+
+struct enumeration dhcpv6_status_codes = {
+ NULL,
+ "status-codes", 2,
+ dhcpv6_status_code_values
+};
+
+struct enumeration_value lq6_query_type_values[] = {
+ { "query-by-address", 1 },
+ { "query-by-clientid", 2 },
+ { NULL, 0 }
+};
+
+struct enumeration lq6_query_types = {
+ NULL,
+ "query-types", 2,
+ lq6_query_type_values
+};
+
+struct enumeration_value dhcpv6_message_values[] = {
+ { "SOLICIT", 1 },
+ { "ADVERTISE", 2 },
+ { "REQUEST", 3 },
+ { "CONFIRM", 4 },
+ { "RENEW", 5 },
+ { "REBIND", 6 },
+ { "REPLY", 7 },
+ { "RELEASE", 8 },
+ { "DECLINE", 9 },
+ { "RECONFIGURE", 10 },
+ { "INFORMATION-REQUEST", 11 },
+ { "RELAY-FORW", 12 },
+ { "RELAY-REPL", 13 },
+ { "LEASEQUERY", 14 },
+ { "LEASEQUERY-REPLY", 15 },
+ { NULL, 0 }
+};
+
+/* Some code refers to a different table. */
+const char *dhcpv6_type_names[] = {
+ NULL,
+ "Solicit",
+ "Advertise",
+ "Request",
+ "Confirm",
+ "Renew",
+ "Rebind",
+ "Reply",
+ "Release",
+ "Decline",
+ "Reconfigure",
+ "Information-request",
+ "Relay-forward",
+ "Relay-reply",
+ "Leasequery",
+ "Leasequery-reply"
+};
+const int dhcpv6_type_name_max =
+ (sizeof(dhcpv6_type_names) / sizeof(dhcpv6_type_names[0]));
+
+struct enumeration dhcpv6_messages = {
+ NULL,
+ "dhcpv6-messages", 1,
+ dhcpv6_message_values
+};
+
+struct universe vsio_universe;
+static struct option vsio_options[] = {
+ { "isc", "Eisc6.", &vsio_universe, 2495, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe isc6_universe;
+static struct option isc6_options[] = {
+ { "media", "t", &isc6_universe, 1, 1 },
+ { "update-assist", "X", &isc6_universe, 2, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+const char *hardware_types [] = {
+ "unknown-0",
+ "ethernet",
+ "unknown-2",
+ "unknown-3",
+ "unknown-4",
+ "unknown-5",
+ "token-ring",
+ "unknown-7",
+ "fddi",
+ "unknown-9",
+ "unknown-10",
+ "unknown-11",
+ "unknown-12",
+ "unknown-13",
+ "unknown-14",
+ "unknown-15",
+ "unknown-16",
+ "unknown-17",
+ "unknown-18",
+ "unknown-19",
+ "unknown-20",
+ "unknown-21",
+ "unknown-22",
+ "unknown-23",
+ "unknown-24",
+ "unknown-25",
+ "unknown-26",
+ "unknown-27",
+ "unknown-28",
+ "unknown-29",
+ "unknown-30",
+ "unknown-31",
+ "unknown-32",
+ "unknown-33",
+ "unknown-34",
+ "unknown-35",
+ "unknown-36",
+ "unknown-37",
+ "unknown-38",
+ "unknown-39",
+ "unknown-40",
+ "unknown-41",
+ "unknown-42",
+ "unknown-43",
+ "unknown-44",
+ "unknown-45",
+ "unknown-46",
+ "unknown-47",
+ "unknown-48",
+ "unknown-49",
+ "unknown-50",
+ "unknown-51",
+ "unknown-52",
+ "unknown-53",
+ "unknown-54",
+ "unknown-55",
+ "unknown-56",
+ "unknown-57",
+ "unknown-58",
+ "unknown-59",
+ "unknown-60",
+ "unknown-61",
+ "unknown-62",
+ "unknown-63",
+ "unknown-64",
+ "unknown-65",
+ "unknown-66",
+ "unknown-67",
+ "unknown-68",
+ "unknown-69",
+ "unknown-70",
+ "unknown-71",
+ "unknown-72",
+ "unknown-73",
+ "unknown-74",
+ "unknown-75",
+ "unknown-76",
+ "unknown-77",
+ "unknown-78",
+ "unknown-79",
+ "unknown-80",
+ "unknown-81",
+ "unknown-82",
+ "unknown-83",
+ "unknown-84",
+ "unknown-85",
+ "unknown-86",
+ "unknown-87",
+ "unknown-88",
+ "unknown-89",
+ "unknown-90",
+ "unknown-91",
+ "unknown-92",
+ "unknown-93",
+ "unknown-94",
+ "unknown-95",
+ "unknown-96",
+ "unknown-97",
+ "unknown-98",
+ "unknown-99",
+ "unknown-100",
+ "unknown-101",
+ "unknown-102",
+ "unknown-103",
+ "unknown-104",
+ "unknown-105",
+ "unknown-106",
+ "unknown-107",
+ "unknown-108",
+ "unknown-109",
+ "unknown-110",
+ "unknown-111",
+ "unknown-112",
+ "unknown-113",
+ "unknown-114",
+ "unknown-115",
+ "unknown-116",
+ "unknown-117",
+ "unknown-118",
+ "unknown-119",
+ "unknown-120",
+ "unknown-121",
+ "unknown-122",
+ "unknown-123",
+ "unknown-124",
+ "unknown-125",
+ "unknown-126",
+ "unknown-127",
+ "unknown-128",
+ "unknown-129",
+ "unknown-130",
+ "unknown-131",
+ "unknown-132",
+ "unknown-133",
+ "unknown-134",
+ "unknown-135",
+ "unknown-136",
+ "unknown-137",
+ "unknown-138",
+ "unknown-139",
+ "unknown-140",
+ "unknown-141",
+ "unknown-142",
+ "unknown-143",
+ "unknown-144",
+ "unknown-145",
+ "unknown-146",
+ "unknown-147",
+ "unknown-148",
+ "unknown-149",
+ "unknown-150",
+ "unknown-151",
+ "unknown-152",
+ "unknown-153",
+ "unknown-154",
+ "unknown-155",
+ "unknown-156",
+ "unknown-157",
+ "unknown-158",
+ "unknown-159",
+ "unknown-160",
+ "unknown-161",
+ "unknown-162",
+ "unknown-163",
+ "unknown-164",
+ "unknown-165",
+ "unknown-166",
+ "unknown-167",
+ "unknown-168",
+ "unknown-169",
+ "unknown-170",
+ "unknown-171",
+ "unknown-172",
+ "unknown-173",
+ "unknown-174",
+ "unknown-175",
+ "unknown-176",
+ "unknown-177",
+ "unknown-178",
+ "unknown-179",
+ "unknown-180",
+ "unknown-181",
+ "unknown-182",
+ "unknown-183",
+ "unknown-184",
+ "unknown-185",
+ "unknown-186",
+ "unknown-187",
+ "unknown-188",
+ "unknown-189",
+ "unknown-190",
+ "unknown-191",
+ "unknown-192",
+ "unknown-193",
+ "unknown-194",
+ "unknown-195",
+ "unknown-196",
+ "unknown-197",
+ "unknown-198",
+ "unknown-199",
+ "unknown-200",
+ "unknown-201",
+ "unknown-202",
+ "unknown-203",
+ "unknown-204",
+ "unknown-205",
+ "unknown-206",
+ "unknown-207",
+ "unknown-208",
+ "unknown-209",
+ "unknown-210",
+ "unknown-211",
+ "unknown-212",
+ "unknown-213",
+ "unknown-214",
+ "unknown-215",
+ "unknown-216",
+ "unknown-217",
+ "unknown-218",
+ "unknown-219",
+ "unknown-220",
+ "unknown-221",
+ "unknown-222",
+ "unknown-223",
+ "unknown-224",
+ "unknown-225",
+ "unknown-226",
+ "unknown-227",
+ "unknown-228",
+ "unknown-229",
+ "unknown-230",
+ "unknown-231",
+ "unknown-232",
+ "unknown-233",
+ "unknown-234",
+ "unknown-235",
+ "unknown-236",
+ "unknown-237",
+ "unknown-238",
+ "unknown-239",
+ "unknown-240",
+ "unknown-241",
+ "unknown-242",
+ "unknown-243",
+ "unknown-244",
+ "unknown-245",
+ "unknown-246",
+ "unknown-247",
+ "unknown-248",
+ "unknown-249",
+ "unknown-250",
+ "unknown-251",
+ "unknown-252",
+ "unknown-253",
+ "unknown-254",
+ "unknown-255" };
+
+universe_hash_t *universe_hash;
+struct universe **universes;
+int universe_count, universe_max;
+
+/* Universe containing names of configuration options, which, rather than
+ writing "option universe-name.option-name ...;", can be set by writing
+ "option-name ...;". */
+
+struct universe *config_universe;
+
+/* XXX: omapi must die...all the below keeps us from having to make the
+ * option structures omapi typed objects, which is a bigger headache.
+ */
+
+char *default_option_format = (char *) "X";
+
+/* Must match hash_reference/dereference types in omapip/hash.h. */
+int
+option_reference(struct option **dest, struct option *src,
+ const char * file, int line)
+{
+ if (!dest || !src)
+ return DHCP_R_INVALIDARG;
+
+ if (*dest) {
+#if defined(POINTER_DEBUG)
+ log_fatal("%s(%d): reference store into non-null pointer!",
+ file, line);
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ *dest = src;
+ src->refcnt++;
+ rc_register(file, line, dest, src, src->refcnt, 0, RC_MISC);
+ return(ISC_R_SUCCESS);
+}
+
+int
+option_dereference(struct option **dest, const char *file, int line)
+{
+ if (!dest)
+ return DHCP_R_INVALIDARG;
+
+ if (!*dest) {
+#if defined (POINTER_DEBUG)
+ log_fatal("%s(%d): dereference of null pointer!", file, line);
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*dest)->refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_fatal("%s(%d): dereference of <= 0 refcnt!", file, line);
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ (*dest)->refcnt--;
+
+ rc_register(file, line, dest, (*dest), (*dest)->refcnt, 1, RC_MISC);
+
+ if ((*dest)->refcnt == 0) {
+ /* The option name may be packed in the same alloc as the
+ * option structure.
+ */
+ if ((char *) (*dest)->name != (char *) ((*dest) + 1))
+ dfree((char *) (*dest)->name, file, line);
+
+ /* It's either a user-configured format (allocated), or the
+ * default static format.
+ */
+ if (((*dest)->format != NULL) &&
+ ((*dest)->format != default_option_format)) {
+ dfree((char *) (*dest)->format, file, line);
+ }
+
+ dfree(*dest, file, line);
+ }
+
+ *dest = NULL;
+ return ISC_R_SUCCESS;
+}
+
+void initialize_common_option_spaces()
+{
+ unsigned code;
+ int i;
+
+ /* The 'universes' table is dynamically grown to contain
+ * universe as they're configured - except during startup.
+ * Since we know how many we put down in .c files, we can
+ * allocate a more-than-right-sized buffer now, leaving some
+ * space for user-configured option spaces.
+ *
+ * 1: dhcp_universe (dhcpv4 options)
+ * 2: nwip_universe (dhcpv4 NWIP option)
+ * 3: fqdn_universe (dhcpv4 fqdn option - reusable for v6)
+ * 4: vendor_class_universe (VIVCO)
+ * 5: vendor_universe (VIVSO)
+ * 6: isc_universe (dhcpv4 isc config space)
+ * 7: dhcpv6_universe (dhcpv6 options)
+ * 8: vsio_universe (DHCPv6 Vendor-Identified space)
+ * 9: isc6_universe (ISC's Vendor universe in DHCPv6 VSIO)
+ * 10: fqdn6_universe (dhcpv6 fqdn option shill to v4)
+ * 11: agent_universe (dhcpv4 relay agent - see server/stables.c)
+ * 12: server_universe (server's config, see server/stables.c)
+ * 13: user-config
+ * 14: more user-config
+ * 15: more user-config
+ * 16: more user-config
+ */
+ universe_max = 16;
+ i = universe_max * sizeof(struct universe *);
+ if (i <= 0)
+ log_fatal("Ludicrous initial size option space table.");
+ universes = dmalloc(i, MDL);
+ if (universes == NULL)
+ log_fatal("Can't allocate option space table.");
+ memset(universes, 0, i);
+
+ /* Set up the DHCP option universe... */
+ dhcp_universe.name = "dhcp";
+ dhcp_universe.concat_duplicates = 1;
+ dhcp_universe.lookup_func = lookup_hashed_option;
+ dhcp_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ dhcp_universe.save_func = save_hashed_option;
+ dhcp_universe.delete_func = delete_hashed_option;
+ dhcp_universe.encapsulate = hashed_option_space_encapsulate;
+ dhcp_universe.foreach = hashed_option_space_foreach;
+ dhcp_universe.decode = parse_option_buffer;
+ dhcp_universe.length_size = 1;
+ dhcp_universe.tag_size = 1;
+ dhcp_universe.get_tag = getUChar;
+ dhcp_universe.store_tag = putUChar;
+ dhcp_universe.get_length = getUChar;
+ dhcp_universe.store_length = putUChar;
+ dhcp_universe.site_code_min = 0;
+ dhcp_universe.end = DHO_END;
+ dhcp_universe.index = universe_count++;
+ universes [dhcp_universe.index] = &dhcp_universe;
+ if (!option_name_new_hash(&dhcp_universe.name_hash,
+ BYTE_NAME_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&dhcp_universe.code_hash,
+ BYTE_CODE_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate dhcp option hash table.");
+ for (i = 0 ; dhcp_options[i].name ; i++) {
+ option_code_hash_add(dhcp_universe.code_hash,
+ &dhcp_options[i].code, 0,
+ &dhcp_options[i], MDL);
+ option_name_hash_add(dhcp_universe.name_hash,
+ dhcp_options [i].name, 0,
+ &dhcp_options [i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("DHCP name hash: %s",
+ option_name_hash_report(dhcp_universe.name_hash));
+ log_info("DHCP code hash: %s",
+ option_code_hash_report(dhcp_universe.code_hash));
+#endif
+
+ /* Set up the Novell option universe (for option 63)... */
+ nwip_universe.name = "nwip";
+ nwip_universe.concat_duplicates = 0; /* XXX: reference? */
+ nwip_universe.lookup_func = lookup_linked_option;
+ nwip_universe.option_state_dereference =
+ linked_option_state_dereference;
+ nwip_universe.save_func = save_linked_option;
+ nwip_universe.delete_func = delete_linked_option;
+ nwip_universe.encapsulate = nwip_option_space_encapsulate;
+ nwip_universe.foreach = linked_option_space_foreach;
+ nwip_universe.decode = parse_option_buffer;
+ nwip_universe.length_size = 1;
+ nwip_universe.tag_size = 1;
+ nwip_universe.get_tag = getUChar;
+ nwip_universe.store_tag = putUChar;
+ nwip_universe.get_length = getUChar;
+ nwip_universe.store_length = putUChar;
+ nwip_universe.site_code_min = 0;
+ nwip_universe.end = 0;
+ code = DHO_NWIP_SUBOPTIONS;
+ nwip_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&nwip_universe.enc_opt,
+ dhcp_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find NWIP parent option (%s:%d).", MDL);
+ nwip_universe.index = universe_count++;
+ universes [nwip_universe.index] = &nwip_universe;
+ if (!option_name_new_hash(&nwip_universe.name_hash,
+ NWIP_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&nwip_universe.code_hash,
+ NWIP_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate nwip option hash table.");
+ for (i = 0 ; nwip_options[i].name ; i++) {
+ option_code_hash_add(nwip_universe.code_hash,
+ &nwip_options[i].code, 0,
+ &nwip_options[i], MDL);
+ option_name_hash_add(nwip_universe.name_hash,
+ nwip_options[i].name, 0,
+ &nwip_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("NWIP name hash: %s",
+ option_name_hash_report(nwip_universe.name_hash));
+ log_info("NWIP code hash: %s",
+ option_code_hash_report(nwip_universe.code_hash));
+#endif
+
+ /* Set up the FQDN option universe... */
+ fqdn_universe.name = "fqdn";
+ fqdn_universe.concat_duplicates = 0;
+ fqdn_universe.lookup_func = lookup_linked_option;
+ fqdn_universe.option_state_dereference =
+ linked_option_state_dereference;
+ fqdn_universe.save_func = save_linked_option;
+ fqdn_universe.delete_func = delete_linked_option;
+ fqdn_universe.encapsulate = fqdn_option_space_encapsulate;
+ fqdn_universe.foreach = linked_option_space_foreach;
+ fqdn_universe.decode = fqdn_universe_decode;
+ fqdn_universe.length_size = 1;
+ fqdn_universe.tag_size = 1;
+ fqdn_universe.get_tag = getUChar;
+ fqdn_universe.store_tag = putUChar;
+ fqdn_universe.get_length = getUChar;
+ fqdn_universe.store_length = putUChar;
+ fqdn_universe.site_code_min = 0;
+ fqdn_universe.end = 0;
+ fqdn_universe.index = universe_count++;
+ code = DHO_FQDN;
+ fqdn_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&fqdn_universe.enc_opt,
+ dhcp_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find FQDN parent option (%s:%d).", MDL);
+ universes [fqdn_universe.index] = &fqdn_universe;
+ if (!option_name_new_hash(&fqdn_universe.name_hash,
+ FQDN_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&fqdn_universe.code_hash,
+ FQDN_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate fqdn option hash table.");
+ for (i = 0 ; fqdn_options[i].name ; i++) {
+ option_code_hash_add(fqdn_universe.code_hash,
+ &fqdn_options[i].code, 0,
+ &fqdn_options[i], MDL);
+ option_name_hash_add(fqdn_universe.name_hash,
+ fqdn_options[i].name, 0,
+ &fqdn_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("FQDN name hash: %s",
+ option_name_hash_report(fqdn_universe.name_hash));
+ log_info("FQDN code hash: %s",
+ option_code_hash_report(fqdn_universe.code_hash));
+#endif
+
+ /* Set up the Vendor Identified Vendor Class options (for option
+ * 125)...
+ */
+ vendor_class_universe.name = "vendor-class";
+ vendor_class_universe.concat_duplicates = 0; /* XXX: reference? */
+ vendor_class_universe.lookup_func = lookup_hashed_option;
+ vendor_class_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ vendor_class_universe.save_func = save_hashed_option;
+ vendor_class_universe.delete_func = delete_hashed_option;
+ vendor_class_universe.encapsulate = hashed_option_space_encapsulate;
+ vendor_class_universe.foreach = hashed_option_space_foreach;
+ vendor_class_universe.decode = parse_option_buffer;
+ vendor_class_universe.length_size = 1;
+ vendor_class_universe.tag_size = 4;
+ vendor_class_universe.get_tag = getULong;
+ vendor_class_universe.store_tag = putULong;
+ vendor_class_universe.get_length = getUChar;
+ vendor_class_universe.store_length = putUChar;
+ vendor_class_universe.site_code_min = 0;
+ vendor_class_universe.end = 0;
+ code = DHO_VIVCO_SUBOPTIONS;
+ vendor_class_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&vendor_class_universe.enc_opt,
+ dhcp_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find VIVCO parent option (%s:%d).", MDL);
+ vendor_class_universe.index = universe_count++;
+ universes[vendor_class_universe.index] = &vendor_class_universe;
+ if (!option_name_new_hash(&vendor_class_universe.name_hash,
+ VIVCO_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&vendor_class_universe.code_hash,
+ VIVCO_HASH_SIZE, MDL))
+ log_fatal("Can't allocate Vendor Identified Vendor Class "
+ "option hash table.");
+ for (i = 0 ; vendor_class_options[i].name ; i++) {
+ option_code_hash_add(vendor_class_universe.code_hash,
+ &vendor_class_options[i].code, 0,
+ &vendor_class_options[i], MDL);
+ option_name_hash_add(vendor_class_universe.name_hash,
+ vendor_class_options[i].name, 0,
+ &vendor_class_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("VIVCO name hash: %s",
+ option_name_hash_report(vendor_class_universe.name_hash));
+ log_info("VIVCO code hash: %s",
+ option_code_hash_report(vendor_class_universe.code_hash));
+#endif
+
+ /* Set up the Vendor Identified Vendor Sub-options (option 126)... */
+ vendor_universe.name = "vendor";
+ vendor_universe.concat_duplicates = 0; /* XXX: reference? */
+ vendor_universe.lookup_func = lookup_hashed_option;
+ vendor_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ vendor_universe.save_func = save_hashed_option;
+ vendor_universe.delete_func = delete_hashed_option;
+ vendor_universe.encapsulate = hashed_option_space_encapsulate;
+ vendor_universe.foreach = hashed_option_space_foreach;
+ vendor_universe.decode = parse_option_buffer;
+ vendor_universe.length_size = 1;
+ vendor_universe.tag_size = 4;
+ vendor_universe.get_tag = getULong;
+ vendor_universe.store_tag = putULong;
+ vendor_universe.get_length = getUChar;
+ vendor_universe.store_length = putUChar;
+ vendor_universe.site_code_min = 0;
+ vendor_universe.end = 0;
+ code = DHO_VIVSO_SUBOPTIONS;
+ vendor_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&vendor_universe.enc_opt,
+ dhcp_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find VIVSO parent option (%s:%d).", MDL);
+ vendor_universe.index = universe_count++;
+ universes[vendor_universe.index] = &vendor_universe;
+ if (!option_name_new_hash(&vendor_universe.name_hash,
+ VIVSO_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&vendor_universe.code_hash,
+ VIVSO_HASH_SIZE, MDL))
+ log_fatal("Can't allocate Vendor Identified Vendor Sub-"
+ "options hash table.");
+ for (i = 0 ; vendor_options[i].name ; i++) {
+ option_code_hash_add(vendor_universe.code_hash,
+ &vendor_options[i].code, 0,
+ &vendor_options[i], MDL);
+ option_name_hash_add(vendor_universe.name_hash,
+ vendor_options[i].name, 0,
+ &vendor_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("VIVSO name hash: %s",
+ option_name_hash_report(vendor_universe.name_hash));
+ log_info("VIVSO code hash: %s",
+ option_code_hash_report(vendor_universe.code_hash));
+#endif
+
+ /* Set up the ISC Vendor-option universe (for option 125.2495)... */
+ isc_universe.name = "isc";
+ isc_universe.concat_duplicates = 0; /* XXX: check VIVSO ref */
+ isc_universe.lookup_func = lookup_linked_option;
+ isc_universe.option_state_dereference =
+ linked_option_state_dereference;
+ isc_universe.save_func = save_linked_option;
+ isc_universe.delete_func = delete_linked_option;
+ isc_universe.encapsulate = linked_option_space_encapsulate;
+ isc_universe.foreach = linked_option_space_foreach;
+ isc_universe.decode = parse_option_buffer;
+ isc_universe.length_size = 2;
+ isc_universe.tag_size = 2;
+ isc_universe.get_tag = getUShort;
+ isc_universe.store_tag = putUShort;
+ isc_universe.get_length = getUShort;
+ isc_universe.store_length = putUShort;
+ isc_universe.site_code_min = 0;
+ isc_universe.end = 0;
+ code = VENDOR_ISC_SUBOPTIONS;
+ isc_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&isc_universe.enc_opt,
+ vendor_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find ISC parent option (%s:%d).", MDL);
+ isc_universe.index = universe_count++;
+ universes[isc_universe.index] = &isc_universe;
+ if (!option_name_new_hash(&isc_universe.name_hash,
+ VIV_ISC_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&isc_universe.code_hash,
+ VIV_ISC_HASH_SIZE, MDL))
+ log_fatal("Can't allocate ISC Vendor options hash table.");
+ for (i = 0 ; isc_options[i].name ; i++) {
+ option_code_hash_add(isc_universe.code_hash,
+ &isc_options[i].code, 0,
+ &isc_options[i], MDL);
+ option_name_hash_add(isc_universe.name_hash,
+ isc_options[i].name, 0,
+ &isc_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("ISC name hash: %s",
+ option_name_hash_report(isc_universe.name_hash));
+ log_info("ISC code hash: %s",
+ option_code_hash_report(isc_universe.code_hash));
+#endif
+
+ /* Set up the DHCPv6 root universe. */
+ dhcpv6_universe.name = "dhcp6";
+ dhcpv6_universe.concat_duplicates = 0;
+ dhcpv6_universe.lookup_func = lookup_hashed_option;
+ dhcpv6_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ dhcpv6_universe.save_func = save_hashed_option;
+ dhcpv6_universe.delete_func = delete_hashed_option;
+ dhcpv6_universe.encapsulate = hashed_option_space_encapsulate;
+ dhcpv6_universe.foreach = hashed_option_space_foreach;
+ dhcpv6_universe.decode = parse_option_buffer;
+ dhcpv6_universe.length_size = 2;
+ dhcpv6_universe.tag_size = 2;
+ dhcpv6_universe.get_tag = getUShort;
+ dhcpv6_universe.store_tag = putUShort;
+ dhcpv6_universe.get_length = getUShort;
+ dhcpv6_universe.store_length = putUShort;
+ dhcpv6_universe.site_code_min = 0;
+ /* DHCPv6 has no END option. */
+ dhcpv6_universe.end = 0x00;
+ dhcpv6_universe.index = universe_count++;
+ universes[dhcpv6_universe.index] = &dhcpv6_universe;
+ if (!option_name_new_hash(&dhcpv6_universe.name_hash,
+ WORD_NAME_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&dhcpv6_universe.code_hash,
+ WORD_CODE_HASH_SIZE, MDL))
+ log_fatal("Can't allocate dhcpv6 option hash tables.");
+ for (i = 0 ; dhcpv6_options[i].name ; i++) {
+ option_code_hash_add(dhcpv6_universe.code_hash,
+ &dhcpv6_options[i].code, 0,
+ &dhcpv6_options[i], MDL);
+ option_name_hash_add(dhcpv6_universe.name_hash,
+ dhcpv6_options[i].name, 0,
+ &dhcpv6_options[i], MDL);
+ }
+
+ /* Add DHCPv6 protocol enumeration sets. */
+ add_enumeration(&dhcpv6_duid_types);
+ add_enumeration(&dhcpv6_status_codes);
+ add_enumeration(&dhcpv6_messages);
+
+ /* Set up DHCPv6 VSIO universe. */
+ vsio_universe.name = "vsio";
+ vsio_universe.concat_duplicates = 0;
+ vsio_universe.lookup_func = lookup_hashed_option;
+ vsio_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ vsio_universe.save_func = save_hashed_option;
+ vsio_universe.delete_func = delete_hashed_option;
+ vsio_universe.encapsulate = hashed_option_space_encapsulate;
+ vsio_universe.foreach = hashed_option_space_foreach;
+ vsio_universe.decode = parse_option_buffer;
+ vsio_universe.length_size = 0;
+ vsio_universe.tag_size = 4;
+ vsio_universe.get_tag = getULong;
+ vsio_universe.store_tag = putULong;
+ vsio_universe.get_length = NULL;
+ vsio_universe.store_length = NULL;
+ vsio_universe.site_code_min = 0;
+ /* No END option. */
+ vsio_universe.end = 0x00;
+ code = D6O_VENDOR_OPTS;
+ if (!option_code_hash_lookup(&vsio_universe.enc_opt,
+ dhcpv6_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find VSIO parent option (%s:%d).", MDL);
+ vsio_universe.index = universe_count++;
+ universes[vsio_universe.index] = &vsio_universe;
+ if (!option_name_new_hash(&vsio_universe.name_hash,
+ VSIO_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&vsio_universe.code_hash,
+ VSIO_HASH_SIZE, MDL))
+ log_fatal("Can't allocate Vendor Specific Information "
+ "Options space.");
+ for (i = 0 ; vsio_options[i].name != NULL ; i++) {
+ option_code_hash_add(vsio_universe.code_hash,
+ &vsio_options[i].code, 0,
+ &vsio_options[i], MDL);
+ option_name_hash_add(vsio_universe.name_hash,
+ vsio_options[i].name, 0,
+ &vsio_options[i], MDL);
+ }
+
+ /* Add ISC VSIO sub-sub-option space. */
+ isc6_universe.name = "isc6";
+ isc6_universe.concat_duplicates = 0;
+ isc6_universe.lookup_func = lookup_hashed_option;
+ isc6_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ isc6_universe.save_func = save_hashed_option;
+ isc6_universe.delete_func = delete_hashed_option;
+ isc6_universe.encapsulate = hashed_option_space_encapsulate;
+ isc6_universe.foreach = hashed_option_space_foreach;
+ isc6_universe.decode = parse_option_buffer;
+ isc6_universe.length_size = 0;
+ isc6_universe.tag_size = 4;
+ isc6_universe.get_tag = getULong;
+ isc6_universe.store_tag = putULong;
+ isc6_universe.get_length = NULL;
+ isc6_universe.store_length = NULL;
+ isc6_universe.site_code_min = 0;
+ /* No END option. */
+ isc6_universe.end = 0x00;
+ code = 2495;
+ if (!option_code_hash_lookup(&isc6_universe.enc_opt,
+ vsio_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find ISC parent option (%s:%d).", MDL);
+ isc6_universe.index = universe_count++;
+ universes[isc6_universe.index] = &isc6_universe;
+ if (!option_name_new_hash(&isc6_universe.name_hash,
+ VIV_ISC_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&isc6_universe.code_hash,
+ VIV_ISC_HASH_SIZE, MDL))
+ log_fatal("Can't allocate Vendor Specific Information "
+ "Options space.");
+ for (i = 0 ; isc6_options[i].name != NULL ; i++) {
+ option_code_hash_add(isc6_universe.code_hash,
+ &isc6_options[i].code, 0,
+ &isc6_options[i], MDL);
+ option_name_hash_add(isc6_universe.name_hash,
+ isc6_options[i].name, 0,
+ &isc6_options[i], MDL);
+ }
+
+ /* The fqdn6 option space is a protocol-wrapper shill for the
+ * old DHCPv4 space.
+ */
+ fqdn6_universe.name = "fqdn6-if-you-see-me-its-a-bug-bug-bug";
+ fqdn6_universe.lookup_func = lookup_fqdn6_option;
+ fqdn6_universe.option_state_dereference = NULL; /* Covered by v4. */
+ fqdn6_universe.save_func = save_fqdn6_option;
+ fqdn6_universe.delete_func = delete_fqdn6_option;
+ fqdn6_universe.encapsulate = fqdn6_option_space_encapsulate;
+ fqdn6_universe.foreach = fqdn6_option_space_foreach;
+ fqdn6_universe.decode = fqdn6_universe_decode;
+ /* This is not a 'normal' encapsulated space, so these values are
+ * meaningless.
+ */
+ fqdn6_universe.length_size = 0;
+ fqdn6_universe.tag_size = 0;
+ fqdn6_universe.get_tag = NULL;
+ fqdn6_universe.store_tag = NULL;
+ fqdn6_universe.get_length = NULL;
+ fqdn6_universe.store_length = NULL;
+ fqdn6_universe.site_code_min = 0;
+ fqdn6_universe.end = 0;
+ fqdn6_universe.index = universe_count++;
+ code = D6O_CLIENT_FQDN;
+ fqdn6_universe.enc_opt = NULL;
+ if (!option_code_hash_lookup(&fqdn6_universe.enc_opt,
+ dhcpv6_universe.code_hash, &code, 0, MDL))
+ log_fatal("Unable to find FQDN v6 parent option. (%s:%d).",
+ MDL);
+ universes[fqdn6_universe.index] = &fqdn6_universe;
+ /* The fqdn6 space shares the same option space as the v4 space.
+ * So there are no name or code hashes on the v6 side.
+ */
+ fqdn6_universe.name_hash = NULL;
+ fqdn6_universe.code_hash = NULL;
+
+
+ /* Set up the hash of DHCPv4 universes. */
+ universe_new_hash(&universe_hash, UNIVERSE_HASH_SIZE, MDL);
+ universe_hash_add(universe_hash, dhcp_universe.name, 0,
+ &dhcp_universe, MDL);
+ universe_hash_add(universe_hash, nwip_universe.name, 0,
+ &nwip_universe, MDL);
+ universe_hash_add(universe_hash, fqdn_universe.name, 0,
+ &fqdn_universe, MDL);
+ universe_hash_add(universe_hash, vendor_class_universe.name, 0,
+ &vendor_class_universe, MDL);
+ universe_hash_add(universe_hash, vendor_universe.name, 0,
+ &vendor_universe, MDL);
+ universe_hash_add(universe_hash, isc_universe.name, 0,
+ &isc_universe, MDL);
+
+ /* Set up hashes for DHCPv6 universes. */
+ universe_hash_add(universe_hash, dhcpv6_universe.name, 0,
+ &dhcpv6_universe, MDL);
+ universe_hash_add(universe_hash, vsio_universe.name, 0,
+ &vsio_universe, MDL);
+ universe_hash_add(universe_hash, isc6_universe.name, 0,
+ &isc6_universe, MDL);
+/* This should not be necessary. Listing here just for consistency.
+ * universe_hash_add(universe_hash, fqdn6_universe.name, 0,
+ * &fqdn6_universe, MDL);
+ */
+}
diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am
new file mode 100644
index 0000000..70eb50a
--- /dev/null
+++ b/common/tests/Makefile.am
@@ -0,0 +1,11 @@
+AM_CPPFLAGS = -I../..
+
+check_PROGRAMS = test_alloc
+
+TESTS = test_alloc
+
+test_alloc_SOURCES = test_alloc.c
+test_alloc_LDADD = ../libdhcp.a ../../tests/libt_api.a \
+ ../../omapip/libomapi.a ../../bind/lib/libdns.a \
+ ../../bind/lib/libisc.a
+
diff --git a/common/tests/Makefile.in b/common/tests/Makefile.in
new file mode 100644
index 0000000..80a0024
--- /dev/null
+++ b/common/tests/Makefile.in
@@ -0,0 +1,468 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+check_PROGRAMS = test_alloc$(EXEEXT)
+TESTS = test_alloc$(EXEEXT)
+subdir = common/tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am_test_alloc_OBJECTS = test_alloc.$(OBJEXT)
+test_alloc_OBJECTS = $(am_test_alloc_OBJECTS)
+test_alloc_DEPENDENCIES = ../libdhcp.a ../../tests/libt_api.a \
+ ../../omapip/libomapi.a ../../bind/lib/libdns.a \
+ ../../bind/lib/libisc.a
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(test_alloc_SOURCES)
+DIST_SOURCES = $(test_alloc_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I../..
+test_alloc_SOURCES = test_alloc.c
+test_alloc_LDADD = ../libdhcp.a ../../tests/libt_api.a \
+ ../../omapip/libomapi.a ../../bind/lib/libdns.a \
+ ../../bind/lib/libisc.a
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign common/tests/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign common/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-checkPROGRAMS:
+ -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS)
+test_alloc$(EXEEXT): $(test_alloc_OBJECTS) $(test_alloc_DEPENDENCIES)
+ @rm -f test_alloc$(EXEEXT)
+ $(LINK) $(test_alloc_OBJECTS) $(test_alloc_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alloc.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; ws='[ ]'; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ echo "XPASS: $$tst"; \
+ ;; \
+ *) \
+ echo "PASS: $$tst"; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *$$ws$$tst$$ws*) \
+ xfail=`expr $$xfail + 1`; \
+ echo "XFAIL: $$tst"; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ echo "FAIL: $$tst"; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ echo "SKIP: $$tst"; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="All $$all tests behaved as expected ($$xfail expected failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all tests failed"; \
+ else \
+ banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ skipped="($$skip tests were not run)"; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-checkPROGRAMS clean-generic ctags distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/common/tests/test_alloc.c b/common/tests/test_alloc.c
new file mode 100644
index 0000000..c0e1b9a
--- /dev/null
+++ b/common/tests/test_alloc.c
@@ -0,0 +1,497 @@
+/*
+ * We test the functions provided in alloc.c here. These are very
+ * basic functions, and it is very important that they work correctly.
+ *
+ * You can see two different styles of testing:
+ *
+ * - In the first, we have a single test for each function that tests
+ * all of the possible ways it can operate. (This is the case for
+ * the buffer tests.)
+ *
+ * - In the second, we have a separate test for each of the ways a
+ * function can operate. (This is the case for the data_string
+ * tests.)
+ *
+ * The advantage of a single test per function is that you have fewer
+ * tests, and less duplicated and extra code. The advantage of having
+ * a separate test is that each test is simpler. Plus if you need to
+ * allow certain tests to fail for some reason (known bugs that are
+ * hard to fix for example), then
+ */
+
+/* TODO: dmalloc() test */
+
+#include "config.h"
+#include "t_api.h"
+
+#include "dhcpd.h"
+
+static void test_buffer_allocate(void);
+static void test_buffer_reference(void);
+static void test_buffer_dereference(void);
+static void test_data_string_forget(void);
+static void test_data_string_forget_nobuf(void);
+static void test_data_string_copy(void);
+static void test_data_string_copy_nobuf(void);
+
+/*
+ * T_testlist is a list of tests that are invoked.
+ */
+testspec_t T_testlist[] = {
+ { test_buffer_allocate,
+ "buffer_allocate()" },
+ { test_buffer_reference,
+ "buffer_reference()" },
+ { test_buffer_dereference,
+ "buffer_dereference()" },
+ { test_data_string_forget,
+ "data_string_forget()" },
+ { test_data_string_forget_nobuf,
+ "data_string_forget(), no buffer" },
+ { test_data_string_copy,
+ "data_string_copy()" },
+ { test_data_string_copy_nobuf,
+ "data_string_copy(), no buffer" },
+ { NULL, NULL }
+};
+
+static void
+test_buffer_allocate(void) {
+ static const char *test_desc =
+ "buffer_allocate basic test";
+
+ struct buffer *buf;
+
+ t_assert("buffer_allocate", 1, T_REQUIRED, "%s", test_desc);
+
+ /*
+ * Check a 0-length buffer.
+ */
+ buf = NULL;
+ if (!buffer_allocate(&buf, 0, MDL)) {
+ t_info("failed on 0-len buffer\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_dereference(&buf, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (buf != NULL) {
+ t_info("buffer_dereference() did not NULL-out buffer\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Check an actual buffer.
+ */
+ buf = NULL;
+ if (!buffer_allocate(&buf, 100, MDL)) {
+ t_info("failed on allocate\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_dereference(&buf, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (buf != NULL) {
+ t_info("buffer_dereference() did not NULL-out buffer\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Okay, we're happy.
+ */
+ t_result(T_PASS);
+}
+
+static void
+test_buffer_reference(void) {
+ static const char *test_desc =
+ "buffer_reference basic test";
+ int result = T_PASS;
+
+ struct buffer *a, *b;
+
+ t_assert("buffer_reference", 1, T_REQUIRED, "%s", test_desc);
+
+ /*
+ * Create a buffer.
+ */
+ a = NULL;
+ if (!buffer_allocate(&a, 100, MDL)) {
+ t_info("failed on allocate\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Confirm buffer_reference() doesn't work if we pass in NULL.
+ *
+ * TODO: we should confirm we get an error message here.
+ */
+ if (buffer_reference(NULL, a, MDL)) {
+ t_info("succeeded on an error input\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * TODO: we should confirm we get an error message if we pass
+ * a non-NULL target.
+ */
+
+ /*
+ * Confirm we work under normal circumstances.
+ */
+ b = NULL;
+ if (!buffer_reference(&b, a, MDL)) {
+ t_info("buffer_reference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ if (b != a) {
+ t_info("incorrect pointer\n");
+ result = T_FAIL;
+ }
+ if (b->refcnt != 2) {
+ t_info("incorrect refcnt\n");
+ result = T_FAIL;
+ }
+
+ /*
+ * Clean up.
+ */
+ if (!buffer_dereference(&b, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_dereference(&a, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ t_result(result);
+}
+
+static void
+test_buffer_dereference(void) {
+ static const char *test_desc =
+ "buffer_dereference basic test";
+
+ struct buffer *a, *b;
+
+ t_assert("buffer_dereference", 1, T_REQUIRED, "%s", test_desc);
+
+ /*
+ * Confirm buffer_dereference() doesn't work if we pass in NULL.
+ *
+ * TODO: we should confirm we get an error message here.
+ */
+ if (buffer_dereference(NULL, MDL)) {
+ t_info("succeeded on an error input\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Confirm buffer_dereference() doesn't work if we pass in
+ * a pointer to NULL.
+ *
+ * TODO: we should confirm we get an error message here.
+ */
+ a = NULL;
+ if (buffer_dereference(&a, MDL)) {
+ t_info("succeeded on an error input\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Confirm we work under normal circumstances.
+ */
+ a = NULL;
+ if (!buffer_allocate(&a, 100, MDL)) {
+ t_info("failed on allocate\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_dereference(&a, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (a != NULL) {
+ t_info("non-null buffer after buffer_dereference()\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ /*
+ * Confirm we get an error from negative refcnt.
+ *
+ * TODO: we should confirm we get an error message here.
+ */
+ a = NULL;
+ if (!buffer_allocate(&a, 100, MDL)) {
+ t_info("failed on allocate\n");
+ t_result(T_FAIL);
+ return;
+ }
+ b = NULL;
+ if (!buffer_reference(&b, a, MDL)) {
+ t_info("buffer_reference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ a->refcnt = 0; /* purposely set to invalid value */
+ if (buffer_dereference(&a, MDL)) {
+ t_info("buffer_dereference() succeeded on error input\n");
+ t_result(T_FAIL);
+ return;
+ }
+ a->refcnt = 2;
+ if (!buffer_dereference(&b, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_dereference(&a, MDL)) {
+ t_info("buffer_dereference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ t_result(T_PASS);
+}
+
+static void
+test_data_string_forget(void) {
+ static const char *test_desc =
+ "data_string_forget basic test";
+ int result = T_PASS;
+
+ struct buffer *buf;
+ struct data_string a;
+ const char *str = "Lorem ipsum dolor sit amet turpis duis.";
+
+ t_assert("data_string_forget", 1, T_REQUIRED, "%s", test_desc);
+
+ /*
+ * Create the string we want to forget.
+ */
+ memset(&a, 0, sizeof(a));
+ a.len = strlen(str);
+ buf = NULL;
+ if (!buffer_allocate(&buf, a.len, MDL)) {
+ t_info("out of memory\n");
+ t_result(T_FAIL);
+ return;
+ }
+ if (!buffer_reference(&a.buffer, buf, MDL)) {
+ t_info("buffer_reference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+ a.data = a.buffer->data;
+ memcpy(a.buffer->data, str, a.len);
+
+ /*
+ * Forget and confirm we've forgotten.
+ */
+ data_string_forget(&a, MDL);
+
+ if (a.len != 0) {
+ t_info("incorrect length\n");
+ result = T_FAIL;
+ }
+ if (a.data != NULL) {
+ t_info("incorrect data\n");
+ result = T_FAIL;
+ }
+ if (a.terminated) {
+ t_info("incorrect terminated\n");
+ result = T_FAIL;
+ }
+ if (a.buffer != NULL) {
+ t_info("incorrect buffer\n");
+ result = T_FAIL;
+ }
+ if (buf->refcnt != 1) {
+ t_info("too many references to buf\n");
+ result = T_FAIL;
+ }
+
+ /*
+ * Clean up buffer.
+ */
+ if (!buffer_dereference(&buf, MDL)) {
+ t_info("buffer_reference() failed\n");
+ t_result(T_FAIL);
+ return;
+ }
+
+ t_result(result);
+}
+
+static void
+test_data_string_forget_nobuf(void) {
+ static const char *test_desc =
+ "data_string_forget test, data_string without buffer";
+ int result = T_PASS;
+
+ struct data_string a;
+ const char *str = "Lorem ipsum dolor sit amet massa nunc.";
+
+ t_assert("data_string_forget, no buffer", 1, T_REQUIRED, "%s", test_desc);
+
+ /*
+ * Create the string we want to forget.
+ */
+ memset(&a, 0, sizeof(a));
+ a.len = strlen(str);
+ a.data = (const unsigned char *)str;
+ a.terminated = 1;
+
+ /*
+ * Forget and confirm we've forgotten.
+ */
+ data_string_forget(&a, MDL);
+
+ if (a.len != 0) {
+ t_info("incorrect length\n");
+ result = T_FAIL;
+ }
+ if (a.data != NULL) {
+ t_info("incorrect data\n");
+ result = T_FAIL;
+ }
+ if (a.terminated) {
+ t_info("incorrect terminated\n");
+ result = T_FAIL;
+ }
+ if (a.buffer != NULL) {
+ t_info("incorrect buffer\n");
+ result = T_FAIL;
+ }
+
+ t_result(result);
+}
+
+static void
+test_data_string_copy(void) {
+ static const char *test_desc =
+ "data_string_copy basic test";
+ int result = T_PASS;
+
+ struct data_string a, b;
+ const char *str = "Lorem ipsum dolor sit amet orci aliquam.";
+
+ t_assert("data_string_copy", 1, T_REQUIRED, "%s", test_desc);
+
+
+ /*
+ * Create the string we want to copy.
+ */
+ memset(&a, 0, sizeof(a));
+ a.len = strlen(str);
+ if (!buffer_allocate(&a.buffer, a.len, MDL)) {
+ t_info("out of memory\n");
+ t_result(T_FAIL);
+ return;
+ }
+ a.data = a.buffer->data;
+ memcpy(a.buffer->data, str, a.len);
+
+ /*
+ * Copy the string, and confirm it works.
+ */
+ memset(&b, 0, sizeof(b));
+ data_string_copy(&b, &a, MDL);
+
+ if (b.len != a.len) {
+ t_info("incorrect length\n");
+ result = T_FAIL;
+ }
+ if (b.data != a.data) {
+ t_info("incorrect data\n");
+ result = T_FAIL;
+ }
+ if (b.terminated != a.terminated) {
+ t_info("incorrect terminated\n");
+ result = T_FAIL;
+ }
+ if (b.buffer != a.buffer) {
+ t_info("incorrect buffer\n");
+ result = T_FAIL;
+ }
+
+ /*
+ * Clean up.
+ */
+ data_string_forget(&b, MDL);
+ data_string_forget(&a, MDL);
+
+ t_result(result);
+}
+
+static void
+test_data_string_copy_nobuf(void) {
+ static const char *test_desc =
+ "data_string_copy test, data_string without buffer";
+ int result = T_PASS;
+
+ struct data_string a, b;
+ const char *str = "Lorem ipsum dolor sit amet cras amet.";
+
+ t_assert("data_string_copy, no buffer", 1, T_REQUIRED, "%s",
+ test_desc);
+
+
+ /*
+ * Create the string we want to copy.
+ */
+ memset(&a, 0, sizeof(a));
+ a.len = strlen(str);
+ a.data = (const unsigned char *)str;
+ a.terminated = 1;
+
+ /*
+ * Copy the string, and confirm it works.
+ */
+ memset(&b, 0, sizeof(b));
+ data_string_copy(&b, &a, MDL);
+
+ if (b.len != a.len) {
+ t_info("incorrect length\n");
+ result = T_FAIL;
+ }
+ if (b.data != a.data) {
+ t_info("incorrect data\n");
+ result = T_FAIL;
+ }
+ if (b.terminated != a.terminated) {
+ t_info("incorrect terminated\n");
+ result = T_FAIL;
+ }
+ if (b.buffer != a.buffer) {
+ t_info("incorrect buffer\n");
+ result = T_FAIL;
+ }
+
+ /*
+ * Clean up.
+ */
+ data_string_forget(&b, MDL);
+ data_string_forget(&a, MDL);
+
+ t_result(result);
+}
diff --git a/common/tr.c b/common/tr.c
new file mode 100644
index 0000000..6e05faf
--- /dev/null
+++ b/common/tr.c
@@ -0,0 +1,331 @@
+/* tr.c
+
+ token ring interface support
+ Contributed in May of 1999 by Andrew Chittenden */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#include "dhcpd.h"
+
+#if defined (HAVE_TR_SUPPORT) && \
+ (defined (PACKET_ASSEMBLY) || defined (PACKET_DECODING))
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+#include "netinet/if_tr.h"
+#include <sys/time.h>
+
+/*
+ * token ring device handling subroutines. These are required as token-ring
+ * does not have a simple on-the-wire header but requires the use of
+ * source routing
+ */
+
+static int insert_source_routing (struct trh_hdr *trh, struct interface_info* interface);
+static void save_source_routing (struct trh_hdr *trh, struct interface_info* interface);
+static void expire_routes (void);
+
+/*
+ * As we keep a list of interesting routing information only, a singly
+ * linked list is all we need
+ */
+struct routing_entry {
+ struct routing_entry *next;
+ unsigned char addr[TR_ALEN];
+ unsigned char iface[5];
+ u_int16_t rcf; /* route control field */
+ u_int16_t rseg[8]; /* routing registers */
+ unsigned long access_time; /* time we last used this entry */
+};
+
+static struct routing_entry *routing_info = NULL;
+
+static int routing_timeout = 10;
+static struct timeval routing_timer;
+
+void assemble_tr_header (interface, buf, bufix, to)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned *bufix;
+ struct hardware *to;
+{
+ struct trh_hdr *trh;
+ int hdr_len;
+ struct trllc *llc;
+
+
+ /* set up the token header */
+ trh = (struct trh_hdr *) &buf[*bufix];
+ if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr))
+ memcpy (trh->saddr, &interface -> hw_address.hbuf [1],
+ sizeof (trh->saddr));
+ else
+ memset (trh->saddr, 0x00, sizeof (trh->saddr));
+
+ if (to && to -> hlen == 7) /* XXX */
+ memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr);
+ else
+ memset (trh->daddr, 0xff, sizeof (trh->daddr));
+
+ hdr_len = insert_source_routing (trh, interface);
+
+ trh->ac = AC;
+ trh->fc = LLC_FRAME;
+
+ /* set up the llc header for snap encoding after the tr header */
+ llc = (struct trllc *)(buf + *bufix + hdr_len);
+ llc->dsap = EXTENDED_SAP;
+ llc->ssap = EXTENDED_SAP;
+ llc->llc = UI_CMD;
+ llc->protid[0] = 0;
+ llc->protid[1] = 0;
+ llc->protid[2] = 0;
+ llc->ethertype = htons(ETHERTYPE_IP);
+
+ hdr_len += sizeof(struct trllc);
+
+ *bufix += hdr_len;
+}
+
+
+static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * decoding the token header is a bit complex as you can see here. It is
+ * further complicated by the linux kernel stripping off some valuable
+ * information (see comment below) even though we've asked for the raw
+ * packets.
+ */
+ssize_t decode_tr_header (interface, buf, bufix, from)
+ struct interface_info *interface;
+ unsigned char *buf;
+ unsigned bufix;
+ struct hardware *from;
+{
+ struct trh_hdr *trh = (struct trh_hdr *) buf + bufix;
+ struct trllc *llc;
+ struct ip *ip;
+ struct udphdr *udp;
+ unsigned int route_len = 0;
+ ssize_t hdr_len;
+ struct timeval now;
+
+ /* see whether any source routing information has expired */
+ gettimeofday(&now, NULL);
+
+ if (routing_timer.tv_sec == 0)
+ routing_timer.tv_sec = now.tv_sec + routing_timeout;
+ else if ((now.tv_sec - routing_timer.tv_sec) > 0)
+ expire_routes();
+
+ /* the kernel might have stripped off the source
+ * routing bit. We try a heuristic to determine whether
+ * this is the case and put it back on if so
+ */
+ route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
+ llc = (struct trllc *)(buf + bufix + sizeof(struct trh_hdr)-TR_MAXRIFLEN+route_len);
+ if (llc->dsap == EXTENDED_SAP
+ && llc->ssap == EXTENDED_SAP
+ && llc->llc == UI_CMD
+ && llc->protid[0] == 0
+ && llc->protid[1] == 0
+ && llc->protid[2] == 0) {
+ /* say there is source routing information present */
+ trh->saddr[0] |= TR_RII;
+ }
+
+ if (trh->saddr[0] & TR_RII)
+ route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
+ else
+ route_len = 0;
+
+ hdr_len = sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
+
+ /* now filter out unwanted packets: this is based on the packet
+ * filter code in bpf.c */
+ llc = (struct trllc *)(buf + bufix + hdr_len);
+ ip = (struct ip *) (llc + 1);
+ udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip));
+
+ /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent
+ * to our port */
+ if (llc->dsap != EXTENDED_SAP
+ || ntohs(llc->ethertype) != ETHERTYPE_IP
+ || ip->ip_p != IPPROTO_UDP
+ || (ntohs (ip->ip_off) & IP_OFFMASK) != 0
+ || udp->uh_dport != local_port)
+ return -1;
+
+ /* only save source routing information for packets from valued hosts */
+ save_source_routing(trh, interface);
+
+ return hdr_len + sizeof (struct trllc);
+}
+
+/* insert_source_routing inserts source route information into the token ring
+ * header
+ */
+static int insert_source_routing (trh, interface)
+ struct trh_hdr *trh;
+ struct interface_info* interface;
+{
+ struct routing_entry *rover;
+ struct timeval now;
+ unsigned int route_len = 0;
+
+ gettimeofday(&now, NULL);
+
+ /* single route broadcasts as per rfc 1042 */
+ if (memcmp(trh->daddr, tr_broadcast,TR_ALEN) == 0) {
+ trh->saddr[0] |= TR_RII;
+ trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
+ trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
+ trh->rcf = htons(trh->rcf);
+ } else {
+ /* look for a routing entry */
+ for (rover = routing_info; rover != NULL; rover = rover->next) {
+ if (memcmp(rover->addr, trh->daddr, TR_ALEN) == 0)
+ break;
+ }
+
+ if (rover != NULL) {
+ /* success: route that frame */
+ if ((rover->rcf & TR_RCF_LEN_MASK) >> 8) {
+ u_int16_t rcf = rover->rcf;
+ memcpy(trh->rseg,rover->rseg,sizeof(trh->rseg));
+ rcf ^= TR_RCF_DIR_BIT;
+ rcf &= ~TR_RCF_BROADCAST_MASK;
+ trh->rcf = htons(rcf);
+ trh->saddr[0] |= TR_RII;
+ }
+ rover->access_time = now.tv_sec;
+ } else {
+ /* we don't have any routing information so send a
+ * limited broadcast */
+ trh->saddr[0] |= TR_RII;
+ trh->rcf = ((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK;
+ trh->rcf |= (TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
+ trh->rcf = htons(trh->rcf);
+ }
+ }
+
+ /* return how much of the header we've actually used */
+ if (trh->saddr[0] & TR_RII)
+ route_len = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8;
+ else
+ route_len = 0;
+
+ return sizeof (struct trh_hdr) - TR_MAXRIFLEN + route_len;
+}
+
+/*
+ * save any source routing information
+ */
+static void save_source_routing(trh, interface)
+ struct trh_hdr *trh;
+ struct interface_info *interface;
+{
+ struct routing_entry *rover;
+ struct timeval now;
+ unsigned char saddr[TR_ALEN];
+ u_int16_t rcf = 0;
+
+ gettimeofday(&now, NULL);
+
+ memcpy(saddr, trh->saddr, sizeof(saddr));
+ saddr[0] &= 0x7f; /* strip off source routing present flag */
+
+ /* scan our table to see if we've got it */
+ for (rover = routing_info; rover != NULL; rover = rover->next) {
+ if (memcmp(&rover->addr[0], &saddr[0], TR_ALEN) == 0)
+ break;
+ }
+
+ /* found an entry so update it with fresh information */
+ if (rover != NULL) {
+ if ((trh->saddr[0] & TR_RII) &&
+ ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
+ rcf = ntohs(trh->rcf);
+ rcf &= ~TR_RCF_BROADCAST_MASK;
+ memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
+ }
+ rover->rcf = rcf;
+ rover->access_time = now.tv_sec;
+ return; /* that's all folks */
+ }
+
+ /* no entry found, so create one */
+ rover = dmalloc (sizeof (struct routing_entry), MDL);
+ if (rover == NULL) {
+ fprintf(stderr,
+ "%s: unable to save source routing information\n",
+ __FILE__);
+ return;
+ }
+
+ memcpy(rover->addr, saddr, sizeof(rover->addr));
+ memcpy(rover->iface, interface->name, 5);
+ rover->access_time = now.tv_sec;
+ if (trh->saddr[0] & TR_RII) {
+ if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) {
+ rcf = ntohs(trh->rcf);
+ rcf &= ~TR_RCF_BROADCAST_MASK;
+ memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg));
+ }
+ rover->rcf = rcf;
+ }
+
+ /* insert into list */
+ rover->next = routing_info;
+ routing_info = rover;
+
+ return;
+}
+
+/*
+ * get rid of old routes
+ */
+static void expire_routes()
+{
+ struct routing_entry *rover;
+ struct routing_entry **prover = &routing_info;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ while((rover = *prover) != NULL) {
+ if ((now.tv_sec - rover->access_time) > routing_timeout) {
+ *prover = rover->next;
+ dfree (rover, MDL);
+ } else
+ prover = &rover->next;
+ }
+
+ /* Reset the timer */
+ routing_timer.tv_sec = now.tv_sec + routing_timeout;
+ routing_timer.tv_usec = now.tv_usec;
+}
+
+#endif
diff --git a/common/tree.c b/common/tree.c
new file mode 100644
index 0000000..d09107b
--- /dev/null
+++ b/common/tree.c
@@ -0,0 +1,4461 @@
+/* tree.c
+
+ Routines for manipulating parse trees... */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <ctype.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+struct binding_scope *global_scope;
+
+static int do_host_lookup (struct data_string *, struct dns_host_entry *);
+
+#define DS_SPRINTF_SIZE 128
+
+/*
+ * If we are using a data_string structure to hold a NUL-terminated
+ * ASCII string, this function can be used to append a printf-formatted
+ * string to the end of it. The data_string structure will be resized to
+ * be big enough to hold the new string.
+ *
+ * If the append works, then 1 is returned.
+ *
+ * If it is not possible to allocate a buffer big enough to hold the
+ * new value, then the old data_string is unchanged, and 0 is returned.
+ */
+int
+data_string_sprintfa(struct data_string *ds, const char *fmt, ...) {
+ va_list args;
+ int cur_strlen;
+ int max;
+ int vsnprintf_ret;
+ int new_len;
+ struct buffer *tmp_buffer;
+
+ /*
+ * If the data_string is empty, then initialize it.
+ */
+ if (ds->data == NULL) {
+ /* INSIST(ds.buffer == NULL); */
+ if (!buffer_allocate(&ds->buffer, DS_SPRINTF_SIZE, MDL)) {
+ return 0;
+ }
+ ds->data = ds->buffer->data;
+ ds->len = DS_SPRINTF_SIZE;
+ *((char *)ds->data) = '\0';
+ }
+
+ /*
+ * Get the length of the string, and figure out how much space
+ * is left.
+ */
+ cur_strlen = strlen((char *)ds->data);
+ max = ds->len - cur_strlen;
+
+ /*
+ * Use vsnprintf(), which won't write past our space, but will
+ * tell us how much space it wants.
+ */
+ va_start(args, fmt);
+ vsnprintf_ret = vsnprintf((char *)ds->data+cur_strlen, max, fmt, args);
+ va_end(args);
+ /* INSIST(vsnprintf_ret >= 0); */
+
+ /*
+ * If our buffer is not big enough, we need a new buffer.
+ */
+ if (vsnprintf_ret >= max) {
+ /*
+ * Figure out a size big enough.
+ */
+ new_len = ds->len * 2;
+ while (new_len <= cur_strlen + vsnprintf_ret) {
+ new_len *= 2;
+ }
+
+ /*
+ * Create a new buffer and fill it.
+ */
+ tmp_buffer = NULL;
+ if (!buffer_allocate(&tmp_buffer, new_len, MDL)) {
+ /*
+ * If we can't create a big enough buffer,
+ * we should remove any truncated output that we had.
+ */
+ *((char *)ds->data+cur_strlen) = '\0';
+ va_end(args);
+ return 0;
+ }
+ memcpy(tmp_buffer->data, ds->data, cur_strlen);
+
+ /* Rerun the vsprintf. */
+ va_start(args, fmt);
+ vsprintf((char *)tmp_buffer->data + cur_strlen, fmt, args);
+ va_end(args);
+
+ /*
+ * Replace our old buffer with the new buffer.
+ */
+ buffer_dereference(&ds->buffer, MDL);
+ buffer_reference(&ds->buffer, tmp_buffer, MDL);
+ buffer_dereference(&tmp_buffer, MDL);
+ ds->data = ds->buffer->data;
+ ds->len = new_len;
+ }
+ return 1;
+}
+
+pair cons (car, cdr)
+ caddr_t car;
+ pair cdr;
+{
+ pair foo = (pair)dmalloc (sizeof *foo, MDL);
+ if (!foo)
+ log_fatal ("no memory for cons.");
+ foo -> car = car;
+ foo -> cdr = cdr;
+ return foo;
+}
+
+int make_const_option_cache (oc, buffer, data, len, option, file, line)
+ struct option_cache **oc;
+ struct buffer **buffer;
+ u_int8_t *data;
+ unsigned len;
+ struct option *option;
+ const char *file;
+ int line;
+{
+ struct buffer *bp;
+
+ if (buffer) {
+ bp = *buffer;
+ *buffer = 0;
+ } else {
+ bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, len, file, line)) {
+ log_error ("%s(%d): can't allocate buffer.",
+ file, line);
+ return 0;
+ }
+ }
+
+ if (!option_cache_allocate (oc, file, line)) {
+ log_error ("%s(%d): can't allocate option cache.", file, line);
+ buffer_dereference (&bp, file, line);
+ return 0;
+ }
+
+ (*oc) -> data.len = len;
+ (*oc) -> data.buffer = bp;
+ (*oc) -> data.data = &bp -> data [0];
+ (*oc) -> data.terminated = 0;
+ if (data)
+ memcpy (&bp -> data [0], data, len);
+ option_reference(&((*oc)->option), option, MDL);
+ return 1;
+}
+
+int make_host_lookup (expr, name)
+ struct expression **expr;
+ const char *name;
+{
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for host lookup tree node.");
+ return 0;
+ }
+ (*expr) -> op = expr_host_lookup;
+ if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) {
+ expression_dereference (expr, MDL);
+ return 0;
+ }
+ return 1;
+}
+
+int enter_dns_host (dh, name)
+ struct dns_host_entry **dh;
+ const char *name;
+{
+ /* XXX This should really keep a hash table of hostnames
+ XXX and just add a new reference to a hostname that
+ XXX already exists, if possible, rather than creating
+ XXX a new structure. */
+ if (!dns_host_entry_allocate (dh, name, MDL)) {
+ log_error ("Can't allocate space for new host.");
+ return 0;
+ }
+ return 1;
+}
+
+int make_const_data (struct expression **expr, const unsigned char *data,
+ unsigned len, int terminated, int allocate,
+ const char *file, int line)
+{
+ struct expression *nt;
+
+ if (!expression_allocate (expr, file, line)) {
+ log_error ("No memory for make_const_data tree node.");
+ return 0;
+ }
+ nt = *expr;
+
+ if (len) {
+ if (allocate) {
+ if (!buffer_allocate (&nt -> data.const_data.buffer,
+ len + terminated, file, line)) {
+ log_error ("Can't allocate const_data buffer");
+ expression_dereference (expr, file, line);
+ return 0;
+ }
+ nt -> data.const_data.data =
+ &nt -> data.const_data.buffer -> data [0];
+ memcpy (nt -> data.const_data.buffer -> data,
+ data, len + terminated);
+ } else
+ nt -> data.const_data.data = data;
+ nt -> data.const_data.terminated = terminated;
+ } else
+ nt -> data.const_data.data = 0;
+
+ nt -> op = expr_const_data;
+ nt -> data.const_data.len = len;
+ return 1;
+}
+
+int make_const_int (expr, val)
+ struct expression **expr;
+ unsigned long val;
+{
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for make_const_int tree node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = val;
+ return 1;
+}
+
+int make_concat (expr, left, right)
+ struct expression **expr;
+ struct expression *left, *right;
+{
+ /* If we're concatenating a null tree to a non-null tree, just
+ return the non-null tree; if both trees are null, return
+ a null tree. */
+ if (!left) {
+ if (!right)
+ return 0;
+ expression_reference (expr, right, MDL);
+ return 1;
+ }
+ if (!right) {
+ expression_reference (expr, left, MDL);
+ return 1;
+ }
+
+ /* Otherwise, allocate a new node to concatenate the two. */
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for concatenation expression node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_concat;
+ expression_reference (&(*expr) -> data.concat [0], left, MDL);
+ expression_reference (&(*expr) -> data.concat [1], right, MDL);
+ return 1;
+}
+
+int make_encapsulation (expr, name)
+ struct expression **expr;
+ struct data_string *name;
+{
+ /* Allocate a new node to store the encapsulation. */
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for encapsulation expression node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_encapsulate;
+ data_string_copy (&(*expr) -> data.encapsulate, name, MDL);
+ return 1;
+}
+
+int make_substring (new, expr, offset, length)
+ struct expression **new;
+ struct expression *expr;
+ struct expression *offset;
+ struct expression *length;
+{
+ /* Allocate an expression node to compute the substring. */
+ if (!expression_allocate (new, MDL)) {
+ log_error ("no memory for substring expression.");
+ return 0;
+ }
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr, expr, MDL);
+ expression_reference (&(*new) -> data.substring.offset, offset, MDL);
+ expression_reference (&(*new) -> data.substring.len, length, MDL);
+ return 1;
+}
+
+int make_limit (new, expr, limit)
+ struct expression **new;
+ struct expression *expr;
+ int limit;
+{
+ /* Allocate a node to enforce a limit on evaluation. */
+ if (!expression_allocate (new, MDL))
+ log_error ("no memory for limit expression");
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr, expr, MDL);
+
+ /* Offset is a constant 0. */
+ if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) {
+ log_error ("no memory for limit offset expression");
+ expression_dereference (new, MDL);
+ return 0;
+ }
+ (*new) -> data.substring.offset -> op = expr_const_int;
+ (*new) -> data.substring.offset -> data.const_int = 0;
+
+ /* Length is a constant: the specified limit. */
+ if (!expression_allocate (&(*new) -> data.substring.len, MDL)) {
+ log_error ("no memory for limit length expression");
+ expression_dereference (new, MDL);
+ return 0;
+ }
+ (*new) -> data.substring.len -> op = expr_const_int;
+ (*new) -> data.substring.len -> data.const_int = limit;
+
+ return 1;
+}
+
+int option_cache (struct option_cache **oc, struct data_string *dp,
+ struct expression *expr, struct option *option,
+ const char *file, int line)
+{
+ if (!option_cache_allocate (oc, file, line))
+ return 0;
+ if (dp)
+ data_string_copy (&(*oc) -> data, dp, file, line);
+ if (expr)
+ expression_reference (&(*oc) -> expression, expr, file, line);
+ option_reference(&(*oc)->option, option, MDL);
+ return 1;
+}
+
+int make_let (result, name)
+ struct executable_statement **result;
+ const char *name;
+{
+ if (!(executable_statement_allocate (result, MDL)))
+ return 0;
+
+ (*result) -> op = let_statement;
+ (*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL);
+ if (!(*result) -> data.let.name) {
+ executable_statement_dereference (result, MDL);
+ return 0;
+ }
+ strcpy ((*result) -> data.let.name, name);
+ return 1;
+}
+
+static int do_host_lookup (result, dns)
+ struct data_string *result;
+ struct dns_host_entry *dns;
+{
+ struct hostent *h;
+ unsigned i, count;
+ unsigned new_len;
+
+#ifdef DEBUG_EVAL
+ log_debug ("time: now = %d dns = %d diff = %d",
+ cur_time, dns -> timeout, cur_time - dns -> timeout);
+#endif
+
+ /* If the record hasn't timed out, just copy the data and return. */
+ if (cur_time <= dns -> timeout) {
+#ifdef DEBUG_EVAL
+ log_debug ("easy copy: %d %s",
+ dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data))
+ : 0));
+#endif
+ data_string_copy (result, &dns -> data, MDL);
+ return 1;
+ }
+#ifdef DEBUG_EVAL
+ log_debug ("Looking up %s", dns -> hostname);
+#endif
+
+ /* Otherwise, look it up... */
+ h = gethostbyname (dns -> hostname);
+ if (!h) {
+#ifndef NO_H_ERRNO
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+#endif
+ log_error ("%s: host unknown.", dns -> hostname);
+#ifndef NO_H_ERRNO
+ break;
+ case TRY_AGAIN:
+ log_error ("%s: temporary name server failure",
+ dns -> hostname);
+ break;
+ case NO_RECOVERY:
+ log_error ("%s: name server failed", dns -> hostname);
+ break;
+ case NO_DATA:
+ log_error ("%s: no A record associated with address",
+ dns -> hostname);
+ }
+#endif /* !NO_H_ERRNO */
+
+ /* Okay to try again after a minute. */
+ dns -> timeout = cur_time + 60;
+ data_string_forget (&dns -> data, MDL);
+ return 0;
+ }
+
+#ifdef DEBUG_EVAL
+ log_debug ("Lookup succeeded; first address is %s",
+ inet_ntoa (h -> h_addr_list [0]));
+#endif
+
+ /* Count the number of addresses we got... */
+ for (count = 0; h -> h_addr_list [count]; count++)
+ ;
+
+ /* Dereference the old data, if any. */
+ data_string_forget (&dns -> data, MDL);
+
+ /* Do we need to allocate more memory? */
+ new_len = count * h -> h_length;
+ if (!buffer_allocate (&dns -> data.buffer, new_len, MDL))
+ {
+ log_error ("No memory for %s.", dns -> hostname);
+ return 0;
+ }
+
+ dns -> data.data = &dns -> data.buffer -> data [0];
+ dns -> data.len = new_len;
+ dns -> data.terminated = 0;
+
+ /* Addresses are conveniently stored one to the buffer, so we
+ have to copy them out one at a time... :'( */
+ for (i = 0; i < count; i++) {
+ memcpy (&dns -> data.buffer -> data [h -> h_length * i],
+ h -> h_addr_list [i], (unsigned)(h -> h_length));
+ }
+#ifdef DEBUG_EVAL
+ log_debug ("dns -> data: %x h -> h_addr_list [0]: %x",
+ *(int *)(dns -> buffer), h -> h_addr_list [0]);
+#endif
+
+ /* XXX Set the timeout for an hour from now.
+ XXX This should really use the time on the DNS reply. */
+ dns -> timeout = cur_time + 3600;
+
+#ifdef DEBUG_EVAL
+ log_debug ("hard copy: %d %s", dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0));
+#endif
+ data_string_copy (result, &dns -> data, MDL);
+ return 1;
+}
+
+int evaluate_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, file, line)
+ struct binding_value **result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+ const char *file;
+ int line;
+{
+ struct binding_value *bv;
+ int status;
+ struct binding *binding;
+
+ bv = (struct binding_value *)0;
+
+ if (expr -> op == expr_variable_reference) {
+ if (!scope || !*scope)
+ return 0;
+
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (result)
+ binding_value_reference (result,
+ binding -> value,
+ file, line);
+ return 1;
+ } else
+ return 0;
+ } else if (expr -> op == expr_funcall) {
+ struct string_list *s;
+ struct expression *arg;
+ struct binding_scope *ns;
+ struct binding *nb;
+
+ if (!scope || !*scope) {
+ log_error ("%s: no such function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ binding = find_binding (*scope, expr -> data.funcall.name);
+
+ if (!binding || !binding -> value) {
+ log_error ("%s: no such function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+ if (binding -> value -> type != binding_function) {
+ log_error ("%s: not a function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ /* Create a new binding scope in which to define
+ the arguments to the function. */
+ ns = (struct binding_scope *)0;
+ if (!binding_scope_allocate (&ns, MDL)) {
+ log_error ("%s: can't allocate argument scope.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ arg = expr -> data.funcall.arglist;
+ s = binding -> value -> value.fundef -> args;
+ while (arg && s) {
+ nb = dmalloc (sizeof *nb, MDL);
+ if (!nb) {
+ blb:
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ } else {
+ memset (nb, 0, sizeof *nb);
+ nb -> name = dmalloc (strlen (s -> string) + 1,
+ MDL);
+ if (nb -> name)
+ strcpy (nb -> name, s -> string);
+ else {
+ dfree (nb, MDL);
+ nb = (struct binding *)0;
+ goto blb;
+ }
+ }
+ evaluate_expression (&nb -> value, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ arg -> data.arg.val, file, line);
+ nb -> next = ns -> bindings;
+ ns -> bindings = nb;
+ arg = arg -> data.arg.next;
+ s = s -> next;
+ }
+ if (arg) {
+ log_error ("%s: too many arguments.",
+ expr -> data.funcall.name);
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ }
+ if (s) {
+ log_error ("%s: too few arguments.",
+ expr -> data.funcall.name);
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ }
+
+ if (scope && *scope)
+ binding_scope_reference (&ns -> outer, *scope, MDL);
+
+ status = (execute_statements
+ (&bv, packet,
+ lease, client_state, in_options, cfg_options, &ns,
+ binding -> value -> value.fundef -> statements));
+ binding_scope_dereference (&ns, MDL);
+
+ if (!bv)
+ return 1;
+ } else if (is_boolean_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_boolean;
+ status = (evaluate_boolean_expression
+ (&bv -> value.boolean, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+ } else if (is_numeric_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_numeric;
+ status = (evaluate_numeric_expression
+ (&bv -> value.intval, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+ } else if (is_data_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_data;
+ status = (evaluate_data_expression
+ (&bv -> value.data, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, MDL));
+#if defined (NSUPDATE_OLD)
+ } else if (is_dns_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_dns;
+ status = (evaluate_dns_expression
+ (&bv -> value.dns, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+#endif
+ } else {
+ log_error ("%s: invalid expression type: %d",
+ "evaluate_expression", expr -> op);
+ return 0;
+ }
+ if (result && status)
+ binding_value_reference (result, bv, file, line);
+ binding_value_dereference (&bv, MDL);
+
+ return status;
+}
+
+int binding_value_dereference (struct binding_value **v,
+ const char *file, int line)
+{
+ struct binding_value *bv = *v;
+
+ *v = (struct binding_value *)0;
+
+ /* Decrement the reference count. If it's nonzero, we're
+ done. */
+ --(bv -> refcnt);
+ rc_register (file, line, v, bv, bv -> refcnt, 1, RC_MISC);
+ if (bv -> refcnt > 0)
+ return 1;
+ if (bv -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (bv);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ switch (bv -> type) {
+ case binding_boolean:
+ case binding_numeric:
+ break;
+ case binding_data:
+ if (bv -> value.data.buffer)
+ data_string_forget (&bv -> value.data, file, line);
+ break;
+ case binding_dns:
+#if defined (NSUPDATE_OLD)
+ if (bv -> value.dns) {
+ if (bv -> value.dns -> r_data) {
+ dfree (bv -> value.dns -> r_data_ephem, MDL);
+ bv -> value.dns -> r_data = (unsigned char *)0;
+ bv -> value.dns -> r_data_ephem =
+ (unsigned char *)0;
+ }
+ minires_freeupdrec (bv -> value.dns);
+ }
+ break;
+#endif
+ default:
+ log_error ("%s(%d): invalid binding type: %d",
+ file, line, bv -> type);
+ return 0;
+ }
+ free_binding_value(bv, file, line);
+ return 1;
+}
+
+#if defined (NSUPDATE_OLD)
+int evaluate_dns_expression (result, packet, lease, client_state, in_options,
+ cfg_options, scope, expr)
+ ns_updrec **result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+{
+ unsigned long ttl = 0;
+ char *tname;
+ struct data_string name, data;
+ int r0, r1, r2;
+
+ if (!result || *result) {
+ log_error ("evaluate_dns_expression called with non-null %s",
+ "result pointer");
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ switch (expr -> op) {
+#if defined (NSUPDATE)
+ case expr_ns_add:
+ r0 = evaluate_numeric_expression (&ttl, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.ns_add.ttl);
+ goto nsfinish;
+
+ case expr_ns_exists:
+ ttl = 1;
+
+ case expr_ns_delete:
+ case expr_ns_not_exists:
+ r0 = 1;
+ nsfinish:
+ memset (&name, 0, sizeof name);
+ r1 = evaluate_data_expression (&name, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.ns_add.rrname,
+ MDL);
+ if (r1) {
+ /* The result of the evaluation may or may not
+ be NUL-terminated, but we need it
+ terminated for sure, so we have to allocate
+ a buffer and terminate it. */
+ tname = dmalloc (name.len + 1, MDL);
+ if (!tname) {
+ r2 = 0;
+ r1 = 0;
+ data_string_forget (&name, MDL);
+ } else {
+ memcpy (tname, name.data, name.len);
+ tname [name.len] = 0;
+ memset (&data, 0, sizeof data);
+ r2 = evaluate_data_expression
+ (&data, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ expr -> data.ns_add.rrdata, MDL);
+ }
+ } else {
+ r2 = 0;
+ tname = NULL;
+ }
+ if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) {
+ *result = minires_mkupdrec (((expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete)
+ ? S_UPDATE : S_PREREQ),
+ tname,
+ expr -> data.ns_add.rrclass,
+ expr -> data.ns_add.rrtype,
+ ttl);
+ if (!*result) {
+ ngood:
+ if (r2) {
+ data_string_forget (&data, MDL);
+ r2 = 0;
+ }
+ } else {
+ if (data.len) {
+ /* As a special case, if we get exactly
+ four bytes of data, it's an IP address
+ represented as a 32-bit quantity, which
+ is actually what we *should* be getting
+ here. Because res_mkupdrec is currently
+ broken and expects a dotted quad, convert
+ it. This should be fixed when the new
+ resolver is merged. */
+ if (data.len == 4) {
+ (*result) -> r_data_ephem =
+ dmalloc (16, MDL);
+ if (!(*result) -> r_data_ephem)
+ goto dpngood;
+ (*result) -> r_data =
+ (*result) -> r_data_ephem;
+ /*%Audit% 16 bytes max. %2004.06.17,Safe%*/
+ sprintf ((char *)(*result) -> r_data_ephem,
+ "%u.%u.%u.%u",
+ data.data [0] & 0xff,
+ data.data [1] & 0xff,
+ data.data [2] & 0xff,
+ data.data [3] & 0xff);
+ (*result) -> r_size =
+ strlen ((const char *)
+ (*result) -> r_data);
+ } else {
+ (*result) -> r_size = data.len;
+ (*result) -> r_data_ephem =
+ dmalloc (data.len, MDL);
+ if (!(*result) -> r_data_ephem) {
+ dpngood: /* double plus ungood. */
+ minires_freeupdrec (*result);
+ *result = 0;
+ goto ngood;
+ }
+ (*result) -> r_data =
+ (*result) -> r_data_ephem;
+ memcpy ((*result) -> r_data_ephem,
+ data.data, data.len);
+ }
+ } else {
+ (*result) -> r_data = 0;
+ (*result) -> r_size = 0;
+ }
+ switch (expr -> op) {
+ case expr_ns_add:
+ (*result) -> r_opcode = ADD;
+ break;
+ case expr_ns_delete:
+ (*result) -> r_opcode = DELETE;
+ break;
+ case expr_ns_exists:
+ (*result) -> r_opcode = YXRRSET;
+ break;
+ case expr_ns_not_exists:
+ (*result) -> r_opcode = NXRRSET;
+ break;
+
+ /* Can't happen, but satisfy gcc. */
+ default:
+ break;
+ }
+ }
+ }
+ if (r1) {
+ data_string_forget (&name, MDL);
+ dfree (tname, MDL);
+ }
+ if (r2)
+ data_string_forget (&data, MDL);
+ /* One flaw in the thinking here: an IP address and an
+ ASCII string both look like data expressions, but
+ for A records, we want an ASCII string, not a
+ binary IP address. Do I need to turn binary IP
+ addresses into a separate type? */
+ return (r0 && r1 &&
+ (r2 || expr -> op != expr_ns_add) && *result);
+
+#else
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ return 0;
+#endif
+ case expr_funcall:
+ log_error ("%s: dns values for functions not supported.",
+ expr -> data.funcall.name);
+ break;
+
+ case expr_variable_reference:
+ log_error ("%s: dns values for variables not supported.",
+ expr -> data.variable);
+ break;
+
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_none:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ case expr_gethostname:
+ log_error ("Data opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("Function opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+}
+#endif /* defined (NSUPDATE_OLD) */
+
+int evaluate_boolean_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ int *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+{
+ struct data_string left, right;
+ int bleft, bright;
+ int sleft, sright;
+ struct binding *binding;
+ struct binding_value *bv, *obv;
+#ifdef HAVE_REGEX_H
+ int regflags = REG_EXTENDED | REG_NOSUB;
+ regex_t re;
+#endif
+
+ switch (expr -> op) {
+ case expr_check:
+ *result = check_collection (packet, lease,
+ expr -> data.check);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: check (%s) returns %s",
+ expr -> data.check -> name,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_equal:
+ case expr_not_equal:
+ bv = obv = (struct binding_value *)0;
+ sleft = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ expr -> data.equal [0], MDL);
+ sright = evaluate_expression (&obv, packet, lease,
+ client_state, in_options,
+ cfg_options, scope,
+ expr -> data.equal [1], MDL);
+ if (sleft && sright) {
+ if (bv -> type != obv -> type)
+ *result = expr -> op == expr_not_equal;
+ else {
+ switch (obv -> type) {
+ case binding_boolean:
+ if (bv -> value.boolean == obv -> value.boolean)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+
+ case binding_data:
+ if ((bv -> value.data.len ==
+ obv -> value.data.len) &&
+ !memcmp (bv -> value.data.data,
+ obv -> value.data.data,
+ obv -> value.data.len))
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+
+ case binding_numeric:
+ if (bv -> value.intval == obv -> value.intval)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+#if defined (NSUPDATE_OLD)
+ case binding_dns:
+#if defined (NSUPDATE)
+ /* XXX This should be a comparison for equal
+ XXX values, not for identity. */
+ if (bv -> value.dns == obv -> value.dns)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+#else
+ *result = expr -> op == expr_not_equal;
+#endif
+ break;
+#endif /* NSUPDATE_OLD */
+ case binding_function:
+ if (bv -> value.fundef == obv -> value.fundef)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+ default:
+ *result = expr -> op == expr_not_equal;
+ break;
+ }
+ }
+ } else if (!sleft && !sright)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: %sequal = %s",
+ expr -> op == expr_not_equal ? "not" : "",
+ (*result ? "true" : "false"));
+#endif
+ if (sleft)
+ binding_value_dereference (&bv, MDL);
+ if (sright)
+ binding_value_dereference (&obv, MDL);
+ return 1;
+
+ case expr_iregex_match:
+#ifdef HAVE_REGEX_H
+ regflags |= REG_ICASE;
+#endif
+ /* FALL THROUGH */
+ case expr_regex_match:
+#ifdef HAVE_REGEX_H
+ memset(&left, 0, sizeof left);
+ bleft = evaluate_data_expression(&left, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr->data.equal[0], MDL);
+ memset(&right, 0, sizeof right);
+ bright = evaluate_data_expression(&right, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr->data.equal[1], MDL);
+
+ *result = 0;
+ memset(&re, 0, sizeof(re));
+ if (bleft && bright &&
+ (regcomp(&re, (char *)right.data, regflags) == 0) &&
+ (regexec(&re, (char *)left.data, (size_t)0, NULL, 0) == 0))
+ *result = 1;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("bool: %s ~= %s yields %s",
+ bleft ? print_hex_1(left.len, left.data, 20)
+ : "NULL",
+ bright ? print_hex_2 (right.len, right.data, 20)
+ : "NULL",
+ *result ? "true" : "false");
+#endif
+
+ if (bleft)
+ data_string_forget(&left, MDL);
+ if (bright)
+ data_string_forget(&right, MDL);
+
+ regfree(&re);
+
+ /*
+ * If we have bleft and bright then we have a good
+ * syntax, otherwise not.
+ *
+ * XXX: we don't warn on invalid regular expression
+ * syntax, should we?
+ */
+ return bleft && bright;
+#else
+ /* It shouldn't be possible to configure a regex operator
+ * when there's no support.
+ */
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ break;
+#endif
+
+ case expr_and:
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ if (sleft && bleft)
+ sright = evaluate_boolean_expression
+ (&bright, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr -> data.and [1]);
+ else
+ sright = bright = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: and (%s, %s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ sright ? (bright ? "true" : "false") : "NULL",
+ ((sleft && sright)
+ ? (bleft && bright ? "true" : "false") : "NULL"));
+#endif
+ if (sleft && sright) {
+ *result = bleft && bright;
+ return 1;
+ }
+ return 0;
+
+ case expr_or:
+ bleft = bright = 0;
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.or [0]);
+ if (!sleft || !bleft)
+ sright = evaluate_boolean_expression
+ (&bright, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr -> data.or [1]);
+ else
+ sright = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: or (%s, %s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ sright ? (bright ? "true" : "false") : "NULL",
+ ((sleft || sright)
+ ? (bleft || bright ? "true" : "false") : "NULL"));
+#endif
+ if (sleft || sright) {
+ *result = bleft || bright;
+ return 1;
+ }
+ return 0;
+
+ case expr_not:
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.not);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: not (%s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ ((sleft && sright)
+ ? (!bleft ? "true" : "false") : "NULL"));
+
+#endif
+ if (sleft) {
+ *result = !bleft;
+ return 1;
+ }
+ return 0;
+
+ case expr_exists:
+ memset (&left, 0, sizeof left);
+ if (!in_options ||
+ !get_option (&left, expr -> data.exists -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, in_options,
+ scope, expr -> data.exists -> code, MDL))
+ *result = 0;
+ else {
+ *result = 1;
+ data_string_forget (&left, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: exists %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_known:
+ if (!packet) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: known = NULL");
+#endif
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: known = %s",
+ packet -> known ? "true" : "false");
+#endif
+ *result = packet -> known;
+ return 1;
+
+ case expr_static:
+ if (!lease || !(lease -> flags & STATIC_LEASE)) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: static = false (%s %s %s %d)",
+ lease ? "y" : "n",
+ (lease && (lease -> flags & STATIC_LEASE)
+ ? "y" : "n"),
+ piaddr (lease -> ip_addr),
+ lease ? lease -> flags : 0);
+#endif
+ *result = 0;
+ return 1;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: static = true");
+#endif
+ *result = 1;
+ return 1;
+
+ case expr_variable_exists:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding) {
+ if (binding -> value)
+ *result = 1;
+ else
+ *result = 0;
+ } else
+ *result = 0;
+ } else
+ *result = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s? = %s", expr -> data.variable,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type ==
+ binding_boolean) {
+ *result = binding -> value -> value.boolean;
+ sleft = 1;
+ } else {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_boolean_expression");
+ sleft = 0;
+ }
+ } else
+ sleft = 0;
+ } else
+ sleft = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s = %s", expr -> data.variable,
+ sleft ? (*result ? "true" : "false") : "NULL");
+#endif
+ return sleft;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ sleft = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (sleft) {
+ if (bv -> type != binding_boolean)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_boolean_expression");
+ else
+ *result = bv -> value.boolean;
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s() = %s", expr -> data.funcall.name,
+ sleft ? (*result ? "true" : "false") : "NULL");
+#endif
+ break;
+
+ case expr_none:
+ case expr_match:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ case expr_filename:
+ case expr_sname:
+ case expr_gethostname:
+ log_error ("Data opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_boolean_expr");
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+}
+
+int evaluate_data_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, file, line)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+ const char *file;
+ int line;
+{
+ struct data_string data, other;
+ unsigned long offset, len, i;
+ int s0, s1, s2, s3;
+ int status;
+ struct binding *binding;
+ unsigned char *s;
+ struct binding_value *bv;
+
+ switch (expr -> op) {
+ /* Extract N bytes starting at byte M of a data string. */
+ case expr_substring:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.substring.expr,
+ MDL);
+
+ /* Evaluate the offset and length. */
+ s1 = evaluate_numeric_expression
+ (&offset, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.substring.offset);
+ s2 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.substring.len);
+
+ if (s0 && s1 && s2) {
+ /* If the offset is after end of the string,
+ return an empty string. Otherwise, do the
+ adjustments and return what's left. */
+ if (data.len > offset) {
+ data_string_copy (result, &data, file, line);
+ result -> len -= offset;
+ if (result -> len > len) {
+ result -> len = len;
+ result -> terminated = 0;
+ }
+ result -> data += offset;
+ }
+ s3 = 1;
+ } else
+ s3 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: substring (%s, %s, %s) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s1 ? print_dec_1 (offset) : "NULL",
+ s2 ? print_dec_2 (len) : "NULL",
+ (s3 ? print_hex_2 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s0)
+ data_string_forget (&data, MDL);
+ if (s3)
+ return 1;
+ return 0;
+
+ /* Extract the last N bytes of a data string. */
+ case expr_suffix:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.suffix.expr, MDL);
+ /* Evaluate the length. */
+ s1 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.suffix.len);
+ if (s0 && s1) {
+ data_string_copy (result, &data, file, line);
+
+ /* If we are returning the last N bytes of a
+ string whose length is <= N, just return
+ the string - otherwise, compute a new
+ starting address and decrease the
+ length. */
+ if (data.len > len) {
+ result -> data += data.len - len;
+ result -> len = len;
+ }
+ data_string_forget (&data, MDL);
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: suffix (%s, %s) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s1 ? print_dec_1 (len) : "NULL",
+ ((s0 && s1)
+ ? print_hex_2 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ return s0 && s1;
+
+ /* Convert string to lowercase. */
+ case expr_lcase:
+ memset(&data, 0, sizeof data);
+ s0 = evaluate_data_expression(&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.lcase, MDL);
+ s1 = 0;
+ if (s0) {
+ result->len = data.len;
+ if (buffer_allocate(&result->buffer,
+ result->len + data.terminated,
+ MDL)) {
+ result->data = &result->buffer->data[0];
+ memcpy(result->buffer->data, data.data,
+ data.len + data.terminated);
+ result->terminated = data.terminated;
+ s = (unsigned char *)result->data;
+ for (i = 0; i < result->len; i++, s++)
+ *s = tolower(*s);
+ s1 = 1;
+ } else {
+ log_error("data: lcase: no buffer memory.");
+ }
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("data: lcase (%s) = %s",
+ s0 ? print_hex_1(data.len, data.data, 30) : "NULL",
+ s1 ? print_hex_2(result->len, result->data, 30)
+ : "NULL");
+#endif
+ if (s0)
+ data_string_forget(&data, MDL);
+ return s1;
+
+ /* Convert string to uppercase. */
+ case expr_ucase:
+ memset(&data, 0, sizeof data);
+ s0 = evaluate_data_expression(&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.lcase, MDL);
+ s1 = 0;
+ if (s0) {
+ result->len = data.len;
+ if (buffer_allocate(&result->buffer,
+ result->len + data.terminated,
+ file, line)) {
+ result->data = &result->buffer->data[0];
+ memcpy(result->buffer->data, data.data,
+ data.len + data.terminated);
+ result->terminated = data.terminated;
+ s = (unsigned char *)result->data;
+ for (i = 0; i < result->len; i++, s++)
+ *s = toupper(*s);
+ s1 = 1;
+ } else {
+ log_error("data: lcase: no buffer memory.");
+ }
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("data: ucase (%s) = %s",
+ s0 ? print_hex_1(data.len, data.data, 30) : "NULL",
+ s1 ? print_hex_2(result->len, result->data, 30)
+ : "NULL");
+#endif
+ if (s0)
+ data_string_forget(&data, MDL);
+ return s1;
+
+ /* Extract an option. */
+ case expr_option:
+ if (in_options)
+ s0 = get_option (result,
+ expr -> data.option -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, in_options,
+ scope, expr -> data.option -> code,
+ file, line);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: option %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name,
+ s0 ? print_hex_1 (result -> len, result -> data, 60)
+ : "NULL");
+#endif
+ return s0;
+
+ case expr_config_option:
+ if (cfg_options)
+ s0 = get_option (result,
+ expr -> data.option -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, cfg_options,
+ scope, expr -> data.option -> code,
+ file, line);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: config-option %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name,
+ s0 ? print_hex_1 (result -> len, result -> data, 60)
+ : "NULL");
+#endif
+ return s0;
+
+ /* Combine the hardware type and address. */
+ case expr_hardware:
+ /* On the client, hardware is our hardware. */
+ if (client_state) {
+ memset (result, 0, sizeof *result);
+ result -> data =
+ client_state -> interface -> hw_address.hbuf;
+ result -> len =
+ client_state -> interface -> hw_address.hlen;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: hardware = %s",
+ print_hex_1 (result -> len,
+ result -> data, 60));
+#endif
+ return 1;
+ }
+
+ /* The server cares about the client's hardware address,
+ so only in the case where we are examining a packet can
+ we return anything. */
+ if (!packet || !packet -> raw) {
+ log_error ("data: hardware: raw packet not available");
+ return 0;
+ }
+ if (packet -> raw -> hlen > sizeof packet -> raw -> chaddr) {
+ log_error ("data: hardware: invalid hlen (%d)\n",
+ packet -> raw -> hlen);
+ return 0;
+ }
+ result -> len = packet -> raw -> hlen + 1;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ result -> buffer -> data [0] = packet -> raw -> htype;
+ memcpy (&result -> buffer -> data [1],
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: hardware: no memory for buffer.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: hardware = %s",
+ print_hex_1 (result -> len, result -> data, 60));
+#endif
+ return 1;
+
+ /* Extract part of the raw packet. */
+ case expr_packet:
+ if (!packet || !packet -> raw) {
+ log_error ("data: packet: raw packet not available");
+ return 0;
+ }
+
+ s0 = evaluate_numeric_expression (&offset, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.packet.offset);
+ s1 = evaluate_numeric_expression (&len,
+ packet, lease, client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.packet.len);
+ if (s0 && s1 && offset < packet -> packet_length) {
+ if (offset + len > packet -> packet_length)
+ result -> len =
+ packet -> packet_length - offset;
+ else
+ result -> len = len;
+ if (buffer_allocate (&result -> buffer,
+ result -> len, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> buffer -> data,
+ (((unsigned char *)(packet -> raw))
+ + offset), result -> len);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: packet: no buffer memory.");
+ return 0;
+ }
+ s2 = 1;
+ } else
+ s2 = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: packet (%ld, %ld) = %s",
+ offset, len,
+ s2 ? print_hex_1 (result -> len,
+ result -> data, 60) : NULL);
+#endif
+ return s2;
+
+ /* The encapsulation of all defined options in an
+ option space... */
+ case expr_encapsulate:
+ if (cfg_options)
+ s0 = option_space_encapsulate
+ (result, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ &expr -> data.encapsulate);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: encapsulate (%s) = %s",
+ expr -> data.encapsulate.data,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 60) : "NULL");
+#endif
+ return s0;
+
+ /* Some constant data... */
+ case expr_const_data:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: const = %s",
+ print_hex_1 (expr -> data.const_data.len,
+ expr -> data.const_data.data, 60));
+#endif
+ data_string_copy (result,
+ &expr -> data.const_data, file, line);
+ return 1;
+
+ /* Hostname lookup... */
+ case expr_host_lookup:
+ s0 = do_host_lookup (result, expr -> data.host_lookup);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: DNS lookup (%s) = %s",
+ expr -> data.host_lookup -> hostname,
+ (s0
+ ? print_dotted_quads (result -> len, result -> data)
+ : "NULL"));
+#endif
+ return s0;
+
+ /* Concatenation... */
+ case expr_concat:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.concat [0], MDL);
+ memset (&other, 0, sizeof other);
+ s1 = evaluate_data_expression (&other, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.concat [1], MDL);
+
+ if (s0 && s1) {
+ result -> len = data.len + other.len;
+ if (!buffer_allocate (&result -> buffer,
+ (result -> len + other.terminated),
+ file, line)) {
+ log_error ("data: concat: no memory");
+ result -> len = 0;
+ data_string_forget (&data, MDL);
+ data_string_forget (&other, MDL);
+ return 0;
+ }
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> buffer -> data, data.data, data.len);
+ memcpy (&result -> buffer -> data [data.len],
+ other.data, other.len + other.terminated);
+ }
+
+ if (s0)
+ data_string_forget (&data, MDL);
+ if (s1)
+ data_string_forget (&other, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: concat (%s, %s) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 20) : "NULL",
+ s1 ? print_hex_2 (other.len, other.data, 20) : "NULL",
+ ((s0 && s1)
+ ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ return s0 && s1;
+
+ case expr_encode_int8:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 1;
+ if (!buffer_allocate (&result -> buffer,
+ 1, file, line)) {
+ log_error ("data: encode_int8: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ result -> buffer -> data [0] = len;
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int8 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int8 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+
+ case expr_encode_int16:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 2;
+ if (!buffer_allocate (&result -> buffer, 2,
+ file, line)) {
+ log_error ("data: encode_int16: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ putUShort (result -> buffer -> data, len);
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int16 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int16 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+ case expr_encode_int32:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 4;
+ if (!buffer_allocate (&result -> buffer, 4,
+ file, line)) {
+ log_error ("data: encode_int32: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ putULong (result -> buffer -> data, len);
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int32 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int32 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+ case expr_binary_to_ascii:
+ /* Evaluate the base (offset) and width (len): */
+ s0 = evaluate_numeric_expression
+ (&offset, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.b2a.base);
+ s1 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.b2a.width);
+
+ /* Evaluate the separator string. */
+ memset (&data, 0, sizeof data);
+ s2 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.b2a.separator,
+ MDL);
+
+ /* Evaluate the data to be converted. */
+ memset (&other, 0, sizeof other);
+ s3 = evaluate_data_expression (&other, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.b2a.buffer, MDL);
+
+ if (s0 && s1 && s2 && s3) {
+ unsigned buflen, i;
+
+ if (len != 8 && len != 16 && len != 32) {
+ log_info ("binary_to_ascii: %s %ld!",
+ "invalid width", len);
+ status = 0;
+ goto b2a_out;
+ }
+ len /= 8;
+
+ /* The buffer must be a multiple of the number's
+ width. */
+ if (other.len % len) {
+ log_info ("binary-to-ascii: %s %d %s %ld!",
+ "length of buffer", other.len,
+ "not a multiple of width", len);
+ status = 0;
+ goto b2a_out;
+ }
+
+ /* Count the width of the output. */
+ buflen = 0;
+ for (i = 0; i < other.len; i += len) {
+ if (len == 1) {
+ if (offset == 8) {
+ if (other.data [i] < 8)
+ buflen++;
+ else if (other.data [i] < 64)
+ buflen += 2;
+ else
+ buflen += 3;
+ } else if (offset == 10) {
+ if (other.data [i] < 10)
+ buflen++;
+ else if (other.data [i] < 100)
+ buflen += 2;
+ else
+ buflen += 3;
+ } else if (offset == 16) {
+ if (other.data [i] < 16)
+ buflen++;
+ else
+ buflen += 2;
+ } else
+ buflen += (converted_length
+ (&other.data [i],
+ offset, 1));
+ } else
+ buflen += (converted_length
+ (&other.data [i],
+ offset, len));
+ if (i + len != other.len)
+ buflen += data.len;
+ }
+
+ if (!buffer_allocate (&result -> buffer,
+ buflen + 1, file, line)) {
+ log_error ("data: binary-to-ascii: no memory");
+ status = 0;
+ goto b2a_out;
+ }
+ result -> data = &result -> buffer -> data [0];
+ result -> len = buflen;
+ result -> terminated = 1;
+
+ buflen = 0;
+ for (i = 0; i < other.len; i += len) {
+ buflen += (binary_to_ascii
+ (&result -> buffer -> data [buflen],
+ &other.data [i], offset, len));
+ if (i + len != other.len) {
+ memcpy (&result ->
+ buffer -> data [buflen],
+ data.data, data.len);
+ buflen += data.len;
+ }
+ }
+ /* NUL terminate. */
+ result -> buffer -> data [buflen] = 0;
+ status = 1;
+ } else
+ status = 0;
+
+ b2a_out:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s",
+ s0 ? print_dec_1 (offset) : "NULL",
+ s1 ? print_dec_2 (len) : "NULL",
+ s2 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s3 ? print_hex_2 (other.len, other.data, 30) : "NULL",
+ (status ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s2)
+ data_string_forget (&data, MDL);
+ if (s3)
+ data_string_forget (&other, MDL);
+ if (status)
+ return 1;
+ return 0;
+
+ case expr_reverse:
+ /* Evaluate the width (len): */
+ s0 = evaluate_numeric_expression
+ (&len, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.reverse.width);
+
+ /* Evaluate the data. */
+ memset (&data, 0, sizeof data);
+ s1 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.reverse.buffer,
+ MDL);
+
+ if (s0 && s1) {
+ int i;
+
+ /* The buffer must be a multiple of the number's
+ width. */
+ if (data.len % len) {
+ log_info ("reverse: %s %d %s %ld!",
+ "length of buffer", data.len,
+ "not a multiple of width", len);
+ status = 0;
+ goto reverse_out;
+ }
+
+ /* XXX reverse in place? I don't think we can. */
+ if (!buffer_allocate (&result -> buffer,
+ data.len, file, line)) {
+ log_error ("data: reverse: no memory");
+ status = 0;
+ goto reverse_out;
+ }
+ result -> data = &result -> buffer -> data [0];
+ result -> len = data.len;
+ result -> terminated = 0;
+
+ for (i = 0; i < data.len; i += len) {
+ memcpy (&result -> buffer -> data [i],
+ &data.data [data.len - i - len], len);
+ }
+ status = 1;
+ } else
+ status = 0;
+
+ reverse_out:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: reverse (%s, %s) = %s",
+ s0 ? print_dec_1 (len) : "NULL",
+ s1 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ (status ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s0)
+ data_string_forget (&data, MDL);
+ if (status)
+ return 1;
+ return 0;
+
+ case expr_leased_address:
+ if (!lease) {
+ log_debug("data: \"leased-address\" configuration "
+ "directive: there is no lease associated "
+ "with this client.");
+ return 0;
+ }
+ result -> len = lease -> ip_addr.len;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ lease -> ip_addr.iabuf, lease -> ip_addr.len);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: leased-address: no memory.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: leased-address = %s",
+ print_hex_1 (result -> len, result -> data, 60));
+#endif
+ return 1;
+
+ case expr_pick_first_value:
+ memset (&data, 0, sizeof data);
+ if ((evaluate_data_expression
+ (result, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, expr -> data.pick_first_value.car, MDL))) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (%s, xxx)",
+ print_hex_1 (result -> len,
+ result -> data, 40));
+#endif
+ return 1;
+ }
+
+ if (expr -> data.pick_first_value.cdr &&
+ (evaluate_data_expression
+ (result, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, expr -> data.pick_first_value.cdr, MDL))) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (NULL, %s)",
+ print_hex_1 (result -> len,
+ result -> data, 40));
+#endif
+ return 1;
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (NULL, NULL) = NULL");
+#endif
+ return 0;
+
+ case expr_host_decl_name:
+ if (!lease || !lease -> host) {
+ log_error ("data: host_decl_name: not available");
+ return 0;
+ }
+ result -> len = strlen (lease -> host -> name);
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ strcpy ((char *)&result -> buffer -> data [0],
+ lease -> host -> name);
+ result -> terminated = 1;
+ } else {
+ log_error ("data: host-decl-name: no memory.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: host-decl-name = %s", lease -> host -> name);
+#endif
+ return 1;
+
+ case expr_null:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: null = NULL");
+#endif
+ return 0;
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type == binding_data) {
+ data_string_copy (result,
+ &binding -> value -> value.data,
+ file, line);
+ s0 = 1;
+ } else if (binding -> value -> type != binding_data) {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_data_expression");
+ s0 = 0;
+ } else
+ s0 = 0;
+ } else
+ s0 = 0;
+ } else
+ s0 = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %s", expr -> data.variable,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 50) : "NULL");
+#endif
+ return s0;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ s0 = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (s0) {
+ if (bv -> type != binding_data)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_data_expression");
+ else
+ data_string_copy (result, &bv -> value.data,
+ file, line);
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %s", expr -> data.funcall.name,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 50) : "NULL");
+#endif
+ break;
+
+ /* Extract the filename. */
+ case expr_filename:
+ if (packet && packet -> raw -> file [0]) {
+ char *fn =
+ memchr (packet -> raw -> file, 0,
+ sizeof packet -> raw -> file);
+ if (!fn)
+ fn = ((char *)packet -> raw -> file +
+ sizeof packet -> raw -> file);
+ result -> len = fn - &(packet -> raw -> file [0]);
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ packet -> raw -> file,
+ result -> len);
+ result -> buffer -> data [result -> len] = 0;
+ result -> terminated = 1;
+ s0 = 1;
+ } else {
+ log_error ("data: filename: no memory.");
+ s0 = 0;
+ }
+ } else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_info ("data: filename = \"%s\"",
+ s0 ? (const char *)(result -> data) : "NULL");
+#endif
+ return s0;
+
+ /* Extract the server name. */
+ case expr_sname:
+ if (packet && packet -> raw -> sname [0]) {
+ char *fn =
+ memchr (packet -> raw -> sname, 0,
+ sizeof packet -> raw -> sname);
+ if (!fn)
+ fn = ((char *)packet -> raw -> sname +
+ sizeof packet -> raw -> sname);
+ result -> len = fn - &packet -> raw -> sname [0];
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ packet -> raw -> sname,
+ result -> len);
+ result -> buffer -> data [result -> len] = 0;
+ result -> terminated = 1;
+ s0 = 1;
+ } else {
+ log_error ("data: sname: no memory.");
+ s0 = 0;
+ }
+ } else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_info ("data: sname = \"%s\"",
+ s0 ? (const char *)(result -> data) : "NULL");
+#endif
+ return s0;
+
+ /* Provide the system's local hostname as a return value. */
+ case expr_gethostname:
+ /*
+ * Allocate a buffer to return.
+ *
+ * The largest valid hostname is maybe 64 octets at a single
+ * label, or 255 octets if you think a hostname is allowed
+ * to contain labels (plus termination).
+ */
+ memset(result, 0, sizeof(*result));
+ if (!buffer_allocate(&result->buffer, 255, file, line)) {
+ log_error("data: gethostname(): no memory for buffer");
+ return 0;
+ }
+ result->data = result->buffer->data;
+
+ /*
+ * On successful completion, gethostname() resturns 0. It may
+ * not null-terminate the string if there was insufficient
+ * space.
+ */
+ if (!gethostname((char *)result->buffer->data, 255)) {
+ if (result->buffer->data[255] == '\0')
+ result->len =
+ strlen((char *)result->buffer->data);
+ else
+ result->len = 255;
+ return 1;
+ }
+
+ data_string_forget(result, MDL);
+ return 0;
+
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_none:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns update opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_data_expression");
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
+ return 0;
+}
+
+int evaluate_numeric_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ unsigned long *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+{
+ struct data_string data;
+ int status, sleft, sright;
+#if defined (NSUPDATE_OLD)
+ ns_updrec *nut;
+ ns_updque uq;
+ struct expression *cur, *next;
+#endif
+
+ struct binding *binding;
+ struct binding_value *bv;
+ unsigned long ileft, iright;
+
+ switch (expr -> op) {
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_none:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ case expr_gethostname:
+ log_error ("Data opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ memset (&data, 0, sizeof data);
+ status = evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL);
+ if (status)
+ *result = data.data [0];
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: extract_int8 (%s) = %s",
+ status ? print_hex_1 (data.len, data.data, 60) : "NULL",
+ status ? print_dec_1 (*result) : "NULL" );
+#endif
+ if (status) data_string_forget (&data, MDL);
+ return status;
+
+ case expr_extract_int16:
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL));
+ if (status && data.len >= 2)
+ *result = getUShort (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: extract_int16 (%s) = %ld",
+ ((status && data.len >= 2) ?
+ print_hex_1 (data.len, data.data, 60) : "NULL"),
+ *result);
+#endif
+ if (status) data_string_forget (&data, MDL);
+ return (status && data.len >= 2);
+
+ case expr_extract_int32:
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL));
+ if (status && data.len >= 4)
+ *result = getULong (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: extract_int32 (%s) = %ld",
+ ((status && data.len >= 4) ?
+ print_hex_1 (data.len, data.data, 60) : "NULL"),
+ *result);
+#endif
+ if (status) data_string_forget (&data, MDL);
+ return (status && data.len >= 4);
+
+ case expr_const_int:
+ *result = expr -> data.const_int;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("number: CONSTANT = %ld", *result);
+#endif
+ return 1;
+
+ case expr_lease_time:
+ if (!lease) {
+ log_error ("data: leased_lease: not available");
+ return 0;
+ }
+ if (lease -> ends < cur_time) {
+ log_error ("%s %lu when it is now %lu",
+ "data: lease_time: lease ends at",
+ (long)(lease -> ends), (long)cur_time);
+ return 0;
+ }
+ *result = lease -> ends - cur_time;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("number: lease-time = (%lu - %lu) = %ld",
+ lease -> ends,
+ cur_time, *result);
+#endif
+ return 1;
+
+ case expr_dns_transaction:
+#if !defined (NSUPDATE_OLD)
+ return 0;
+#else
+ if (!resolver_inited) {
+ minires_ninit (&resolver_state);
+ resolver_inited = 1;
+ resolver_state.retrans = 1;
+ resolver_state.retry = 1;
+ }
+ ISC_LIST_INIT (uq);
+ cur = expr;
+ do {
+ next = cur -> data.dns_transaction.cdr;
+ nut = 0;
+ status = (evaluate_dns_expression
+ (&nut, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, cur -> data.dns_transaction.car));
+ if (!status)
+ goto dns_bad;
+ ISC_LIST_APPEND (uq, nut, r_link);
+ cur = next;
+ } while (next);
+
+ /* Do the update and record the error code, if there was
+ an error; otherwise set it to NOERROR. */
+ *result = minires_nupdate (&resolver_state,
+ ISC_LIST_HEAD (uq));
+ status = 1;
+
+ print_dns_status ((int)*result, &uq);
+
+ dns_bad:
+ while (!ISC_LIST_EMPTY (uq)) {
+ ns_updrec *tmp = ISC_LIST_HEAD (uq);
+ ISC_LIST_UNLINK (uq, tmp, r_link);
+ if (tmp -> r_data_ephem) {
+ dfree (tmp -> r_data_ephem, MDL);
+ tmp -> r_data = (unsigned char *)0;
+ tmp -> r_data_ephem = (unsigned char *)0;
+ }
+ minires_freeupdrec (tmp);
+ }
+ return status;
+#endif /* NSUPDATE_OLD */
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type == binding_numeric) {
+ *result = binding -> value -> value.intval;
+ status = 1;
+ } else {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_numeric_expression");
+ status = 0;
+ }
+ } else
+ status = 0;
+ } else
+ status = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ if (status)
+ log_debug ("numeric: %s = %ld",
+ expr -> data.variable, *result);
+ else
+ log_debug ("numeric: %s = NULL",
+ expr -> data.variable);
+#endif
+ return status;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ status = evaluate_expression (&bv, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (status) {
+ if (bv -> type != binding_numeric)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_numeric_expression");
+ else
+ *result = bv -> value.intval;
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %ld", expr -> data.funcall.name,
+ status ? *result : 0);
+#endif
+ break;
+
+ case expr_add:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld + %ld = %ld",
+ ileft, iright, ileft + iright);
+ else if (sleft)
+ log_debug ("num: %ld + NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL + %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft + iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_subtract:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld - %ld = %ld",
+ ileft, iright, ileft - iright);
+ else if (sleft)
+ log_debug ("num: %ld - NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL - %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft - iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_multiply:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld * %ld = %ld",
+ ileft, iright, ileft * iright);
+ else if (sleft)
+ log_debug ("num: %ld * NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL * %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft * iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_divide:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright) {
+ if (iright != 0)
+ log_debug ("num: %ld / %ld = %ld",
+ ileft, iright, ileft / iright);
+ else
+ log_debug ("num: %ld / %ld = NULL",
+ ileft, iright);
+ } else if (sleft)
+ log_debug ("num: %ld / NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL / %ld = NULL", iright);
+#endif
+ if (sleft && sright && iright) {
+ *result = ileft / iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_remainder:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright) {
+ if (iright != 0)
+ log_debug ("num: %ld %% %ld = %ld",
+ ileft, iright, ileft % iright);
+ else
+ log_debug ("num: %ld %% %ld = NULL",
+ ileft, iright);
+ } else if (sleft)
+ log_debug ("num: %ld %% NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL %% %ld = NULL", iright);
+#endif
+ if (sleft && sright && iright) {
+ *result = ileft % iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_and:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld | %ld = %ld",
+ ileft, iright, ileft & iright);
+ else if (sleft)
+ log_debug ("num: %ld & NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL & %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft & iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_or:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld | %ld = %ld",
+ ileft, iright, ileft | iright);
+ else if (sleft)
+ log_debug ("num: %ld | NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL | %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft | iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_xor:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld ^ %ld = %ld",
+ ileft, iright, ileft ^ iright);
+ else if (sleft)
+ log_debug ("num: %ld ^ NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL ^ %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft ^ iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_client_state:
+ if (client_state) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: client-state = %d",
+ client_state -> state);
+#endif
+ *result = client_state -> state;
+ return 1;
+ } else {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: client-state = NULL");
+#endif
+ return 0;
+ }
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_numeric_expr");
+ return 0;
+
+ case expr_arg:
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d. Undefined operator "
+ "%d.", MDL, expr->op);
+ break;
+ }
+
+ log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op);
+ return 0;
+}
+
+/* Return data hanging off of an option cache structure, or if there
+ isn't any, evaluate the expression hanging off of it and return the
+ result of that evaluation. There should never be both an expression
+ and a valid data_string. */
+
+int evaluate_option_cache (result, packet, lease, client_state,
+ in_options, cfg_options, scope, oc, file, line)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct option_cache *oc;
+ const char *file;
+ int line;
+{
+ if (oc->data.data != NULL) {
+ data_string_copy (result, &oc -> data, file, line);
+ return 1;
+ }
+ if (!oc -> expression)
+ return 0;
+ return evaluate_data_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ oc -> expression, file, line);
+}
+
+/* Evaluate an option cache and extract a boolean from the result,
+ returning the boolean. Return false if there is no data. */
+
+int evaluate_boolean_option_cache (ignorep, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, file, line)
+ int *ignorep;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct option_cache *oc;
+ const char *file;
+ int line;
+{
+ struct data_string ds;
+ int result;
+
+ /* So that we can be called with option_lookup as an argument. */
+ if (!oc || !in_options)
+ return 0;
+
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_option_cache (&ds, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, file, line))
+ return 0;
+
+ /* The boolean option cache is actually a trinary value. Zero is
+ * off, one is on, and 2 is 'ignore'.
+ */
+ if (ds.len) {
+ result = ds.data [0];
+ if (result == 2) {
+ result = 0;
+ if (ignorep != NULL)
+ *ignorep = 1;
+ } else if (ignorep != NULL)
+ *ignorep = 0;
+ } else
+ result = 0;
+ data_string_forget (&ds, MDL);
+ return result;
+}
+
+
+/* Evaluate a boolean expression and return the result of the evaluation,
+ or FALSE if it failed. */
+
+int evaluate_boolean_expression_result (ignorep, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ int *ignorep;
+ struct packet *packet;
+ struct lease *lease;
+ struct client_state *client_state;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct expression *expr;
+{
+ int result;
+
+ /* So that we can be called with option_lookup as an argument. */
+ if (!expr)
+ return 0;
+
+ if (!evaluate_boolean_expression (&result, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr))
+ return 0;
+
+ if (result == 2) {
+ *ignorep = 1;
+ result = 0;
+ } else
+ *ignorep = 0;
+ return result;
+}
+
+
+/* Dereference an expression node, and if the reference count goes to zero,
+ dereference any data it refers to, and then free it. */
+void expression_dereference (eptr, file, line)
+ struct expression **eptr;
+ const char *file;
+ int line;
+{
+ struct expression *expr = *eptr;
+
+ /* Zero the pointer. */
+ *eptr = (struct expression *)0;
+
+ /* Decrement the reference count. If it's nonzero, we're
+ done. */
+ --(expr -> refcnt);
+ rc_register (file, line, eptr, expr, expr -> refcnt, 1, RC_MISC);
+ if (expr -> refcnt > 0)
+ return;
+ if (expr -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (expr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return;
+#endif
+ }
+
+ /* Dereference subexpressions. */
+ switch (expr -> op) {
+ /* All the binary operators can be handled the same way. */
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ case expr_concat:
+ case expr_and:
+ case expr_or:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ if (expr -> data.equal [0])
+ expression_dereference (&expr -> data.equal [0],
+ file, line);
+ if (expr -> data.equal [1])
+ expression_dereference (&expr -> data.equal [1],
+ file, line);
+ break;
+
+ case expr_substring:
+ if (expr -> data.substring.expr)
+ expression_dereference (&expr -> data.substring.expr,
+ file, line);
+ if (expr -> data.substring.offset)
+ expression_dereference (&expr -> data.substring.offset,
+ file, line);
+ if (expr -> data.substring.len)
+ expression_dereference (&expr -> data.substring.len,
+ file, line);
+ break;
+
+ case expr_suffix:
+ if (expr -> data.suffix.expr)
+ expression_dereference (&expr -> data.suffix.expr,
+ file, line);
+ if (expr -> data.suffix.len)
+ expression_dereference (&expr -> data.suffix.len,
+ file, line);
+ break;
+
+ case expr_lcase:
+ if (expr->data.lcase)
+ expression_dereference(&expr->data.lcase, MDL);
+ break;
+
+ case expr_ucase:
+ if (expr->data.ucase)
+ expression_dereference(&expr->data.ucase, MDL);
+ break;
+
+ case expr_not:
+ if (expr -> data.not)
+ expression_dereference (&expr -> data.not, file, line);
+ break;
+
+ case expr_packet:
+ if (expr -> data.packet.offset)
+ expression_dereference (&expr -> data.packet.offset,
+ file, line);
+ if (expr -> data.packet.len)
+ expression_dereference (&expr -> data.packet.len,
+ file, line);
+ break;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ if (expr -> data.extract_int)
+ expression_dereference (&expr -> data.extract_int,
+ file, line);
+ break;
+
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ if (expr -> data.encode_int)
+ expression_dereference (&expr -> data.encode_int,
+ file, line);
+ break;
+
+ case expr_encapsulate:
+ case expr_const_data:
+ data_string_forget (&expr -> data.const_data, file, line);
+ break;
+
+ case expr_host_lookup:
+ if (expr -> data.host_lookup)
+ dns_host_entry_dereference (&expr -> data.host_lookup,
+ file, line);
+ break;
+
+ case expr_binary_to_ascii:
+ if (expr -> data.b2a.base)
+ expression_dereference (&expr -> data.b2a.base,
+ file, line);
+ if (expr -> data.b2a.width)
+ expression_dereference (&expr -> data.b2a.width,
+ file, line);
+ if (expr -> data.b2a.separator)
+ expression_dereference (&expr -> data.b2a.separator,
+ file, line);
+ if (expr -> data.b2a.buffer)
+ expression_dereference (&expr -> data.b2a.buffer,
+ file, line);
+ break;
+
+ case expr_pick_first_value:
+ if (expr -> data.pick_first_value.car)
+ expression_dereference (&expr -> data.pick_first_value.car,
+ file, line);
+ if (expr -> data.pick_first_value.cdr)
+ expression_dereference (&expr -> data.pick_first_value.cdr,
+ file, line);
+ break;
+
+ case expr_reverse:
+ if (expr -> data.reverse.width)
+ expression_dereference (&expr -> data.reverse.width,
+ file, line);
+ if (expr -> data.reverse.buffer)
+ expression_dereference
+ (&expr -> data.reverse.buffer, file, line);
+ break;
+
+ case expr_dns_transaction:
+ if (expr -> data.dns_transaction.car)
+ expression_dereference (&expr -> data.dns_transaction.car,
+ file, line);
+ if (expr -> data.dns_transaction.cdr)
+ expression_dereference (&expr -> data.dns_transaction.cdr,
+ file, line);
+ break;
+
+ case expr_ns_add:
+ if (expr -> data.ns_add.rrname)
+ expression_dereference (&expr -> data.ns_add.rrname,
+ file, line);
+ if (expr -> data.ns_add.rrdata)
+ expression_dereference (&expr -> data.ns_add.rrdata,
+ file, line);
+ if (expr -> data.ns_add.ttl)
+ expression_dereference (&expr -> data.ns_add.ttl,
+ file, line);
+ break;
+
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ if (expr -> data.ns_delete.rrname)
+ expression_dereference (&expr -> data.ns_delete.rrname,
+ file, line);
+ if (expr -> data.ns_delete.rrdata)
+ expression_dereference (&expr -> data.ns_delete.rrdata,
+ file, line);
+ break;
+
+ case expr_variable_reference:
+ case expr_variable_exists:
+ if (expr -> data.variable)
+ dfree (expr -> data.variable, file, line);
+ break;
+
+ case expr_funcall:
+ if (expr -> data.funcall.name)
+ dfree (expr -> data.funcall.name, file, line);
+ if (expr -> data.funcall.arglist)
+ expression_dereference (&expr -> data.funcall.arglist,
+ file, line);
+ break;
+
+ case expr_arg:
+ if (expr -> data.arg.val)
+ expression_dereference (&expr -> data.arg.val,
+ file, line);
+ if (expr -> data.arg.next)
+ expression_dereference (&expr -> data.arg.next,
+ file, line);
+ break;
+
+ case expr_function:
+ fundef_dereference (&expr -> data.func, file, line);
+ break;
+
+ /* No subexpressions. */
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_filename:
+ case expr_sname:
+ case expr_const_int:
+ case expr_check:
+ case expr_option:
+ case expr_hardware:
+ case expr_exists:
+ case expr_known:
+ case expr_null:
+ case expr_gethostname:
+ break;
+
+ default:
+ break;
+ }
+ free_expression (expr, MDL);
+}
+
+int is_dns_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete ||
+ expr -> op == expr_ns_exists ||
+ expr -> op == expr_ns_not_exists);
+}
+
+int is_boolean_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_check ||
+ expr -> op == expr_exists ||
+ expr -> op == expr_variable_exists ||
+ expr -> op == expr_equal ||
+ expr -> op == expr_not_equal ||
+ expr->op == expr_regex_match ||
+ expr->op == expr_iregex_match ||
+ expr -> op == expr_and ||
+ expr -> op == expr_or ||
+ expr -> op == expr_not ||
+ expr -> op == expr_known ||
+ expr -> op == expr_static);
+}
+
+int is_data_expression (expr)
+ struct expression *expr;
+{
+ return (expr->op == expr_substring ||
+ expr->op == expr_suffix ||
+ expr->op == expr_lcase ||
+ expr->op == expr_ucase ||
+ expr->op == expr_option ||
+ expr->op == expr_hardware ||
+ expr->op == expr_const_data ||
+ expr->op == expr_packet ||
+ expr->op == expr_concat ||
+ expr->op == expr_encapsulate ||
+ expr->op == expr_encode_int8 ||
+ expr->op == expr_encode_int16 ||
+ expr->op == expr_encode_int32 ||
+ expr->op == expr_host_lookup ||
+ expr->op == expr_binary_to_ascii ||
+ expr->op == expr_filename ||
+ expr->op == expr_sname ||
+ expr->op == expr_reverse ||
+ expr->op == expr_pick_first_value ||
+ expr->op == expr_host_decl_name ||
+ expr->op == expr_leased_address ||
+ expr->op == expr_config_option ||
+ expr->op == expr_null ||
+ expr->op == expr_gethostname);
+}
+
+int is_numeric_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_extract_int8 ||
+ expr -> op == expr_extract_int16 ||
+ expr -> op == expr_extract_int32 ||
+ expr -> op == expr_const_int ||
+ expr -> op == expr_lease_time ||
+ expr -> op == expr_dns_transaction ||
+ expr -> op == expr_add ||
+ expr -> op == expr_subtract ||
+ expr -> op == expr_multiply ||
+ expr -> op == expr_divide ||
+ expr -> op == expr_remainder ||
+ expr -> op == expr_binary_and ||
+ expr -> op == expr_binary_or ||
+ expr -> op == expr_binary_xor ||
+ expr -> op == expr_client_state);
+}
+
+int is_compound_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete ||
+ expr -> op == expr_ns_exists ||
+ expr -> op == expr_ns_not_exists ||
+ expr -> op == expr_substring ||
+ expr -> op == expr_suffix ||
+ expr -> op == expr_option ||
+ expr -> op == expr_concat ||
+ expr -> op == expr_encode_int8 ||
+ expr -> op == expr_encode_int16 ||
+ expr -> op == expr_encode_int32 ||
+ expr -> op == expr_binary_to_ascii ||
+ expr -> op == expr_reverse ||
+ expr -> op == expr_pick_first_value ||
+ expr -> op == expr_config_option ||
+ expr -> op == expr_extract_int8 ||
+ expr -> op == expr_extract_int16 ||
+ expr -> op == expr_extract_int32 ||
+ expr -> op == expr_dns_transaction);
+}
+
+static int op_val (enum expr_op);
+
+static int op_val (op)
+ enum expr_op op;
+{
+ switch (op) {
+ case expr_none:
+ case expr_match:
+ case expr_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_packet:
+ case expr_const_data:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ /* XXXDPN: Need to assign sane precedences to these. */
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ case expr_gethostname:
+ return 100;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ return 4;
+
+ case expr_or:
+ case expr_and:
+ return 3;
+
+ case expr_add:
+ case expr_subtract:
+ return 2;
+
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ return 1;
+ }
+ return 100;
+}
+
+int op_precedence (op1, op2)
+ enum expr_op op1, op2;
+{
+ return op_val (op1) - op_val (op2);
+}
+
+enum expression_context expression_context (struct expression *expr)
+{
+ if (is_data_expression (expr))
+ return context_data;
+ if (is_numeric_expression (expr))
+ return context_numeric;
+ if (is_boolean_expression (expr))
+ return context_boolean;
+ if (is_dns_expression (expr))
+ return context_dns;
+ return context_any;
+}
+
+enum expression_context op_context (op)
+ enum expr_op op;
+{
+ switch (op) {
+/* XXX Why aren't these specific? */
+ case expr_none:
+ case expr_match:
+ case expr_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_lcase:
+ case expr_ucase:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_packet:
+ case expr_const_data:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_dns_transaction:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ case expr_gethostname:
+ return context_any;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ return context_data;
+
+ case expr_and:
+ return context_boolean;
+
+ case expr_or:
+ return context_boolean;
+
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ return context_numeric;
+ }
+ return context_any;
+}
+
+int write_expression (file, expr, col, indent, firstp)
+ FILE *file;
+ struct expression *expr;
+ int col;
+ int indent;
+ int firstp;
+{
+ struct expression *e;
+ const char *s;
+ char obuf [65];
+ int scol;
+ int width;
+
+ /* If this promises to be a fat expression, start a new line. */
+ if (!firstp && is_compound_expression (expr)) {
+ indent_spaces (file, indent);
+ col = indent;
+ }
+
+ switch (expr -> op) {
+ case expr_none:
+ col = token_print_indent (file, col, indent, "", "", "null");
+ break;
+
+ case expr_check:
+ col = token_print_indent (file, col, indent, "", "", "check");
+ col = token_print_indent_concat (file, col, indent,
+ " ", "", "\"",
+ expr -> data.check -> name,
+ "\"", (char *)0);
+ break;
+
+ case expr_regex_match:
+ s = "~=";
+ goto binary;
+
+ case expr_iregex_match:
+ s = "~~";
+ goto binary;
+
+ case expr_not_equal:
+ s = "!=";
+ goto binary;
+
+ case expr_equal:
+ s = "=";
+ binary:
+ col = write_expression (file, expr -> data.equal [0],
+ col, indent, 1);
+ col = token_print_indent (file, col, indent, " ", " ", s);
+ col = write_expression (file, expr -> data.equal [1],
+ col, indent + 2, 0);
+ break;
+
+ case expr_substring:
+ col = token_print_indent (file, col, indent, "", "",
+ "substring");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.substring.expr,
+ col, scol, 1);
+ col = token_print_indent (file, col, indent, "", " ", ",");
+ col = write_expression (file, expr -> data.substring.offset,
+ col, indent, 0);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.substring.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_suffix:
+ col = token_print_indent (file, col, indent, "", "", "suffix");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.suffix.expr,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.suffix.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+
+ case expr_lcase:
+ col = token_print_indent(file, col, indent, "", "", "lcase");
+ col = token_print_indent(file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression(file, expr->data.lcase, col, scol, 1);
+ col = token_print_indent(file, col, indent, "", "", ")");
+ break;
+
+ case expr_ucase:
+ col = token_print_indent(file, col, indent, "", "", "ucase");
+ col = token_print_indent(file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression(file, expr->data.ucase, col, scol, 1);
+ col = token_print_indent(file, col, indent, "", "", ")");
+ break;
+
+ case expr_concat:
+ e = expr;
+ col = token_print_indent (file, col, indent, "", "",
+ "concat");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ firstp = 1;
+ concat_again:
+ col = write_expression (file, e -> data.concat [0],
+ col, scol, firstp);
+ firstp = 0;
+ if (!e -> data.concat [1])
+ goto no_concat_cdr;
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ if (e -> data.concat [1] -> op == expr_concat) {
+ e = e -> data.concat [1];
+ goto concat_again;
+ }
+ col = write_expression (file, e -> data.concat [1],
+ col, scol, 0);
+ no_concat_cdr:
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_host_lookup:
+ col = token_print_indent (file, col, indent, "", "",
+ "gethostbyname");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ col = token_print_indent_concat
+ (file, col, indent, "", "",
+ "\"", expr -> data.host_lookup -> hostname, "\"",
+ (char *)0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_add:
+ s = "+";
+ goto binary;
+
+ case expr_subtract:
+ s = "-";
+ goto binary;
+
+ case expr_multiply:
+ s = "*";
+ goto binary;
+
+ case expr_divide:
+ s = "/";
+ goto binary;
+
+ case expr_remainder:
+ s = "%";
+ goto binary;
+
+ case expr_binary_and:
+ s = "&";
+ goto binary;
+
+ case expr_binary_or:
+ s = "|";
+ goto binary;
+
+ case expr_binary_xor:
+ s = "^";
+ goto binary;
+
+ case expr_and:
+ s = "and";
+ goto binary;
+
+ case expr_or:
+ s = "or";
+ goto binary;
+
+ case expr_not:
+ col = token_print_indent (file, col, indent, "", " ", "not");
+ col = write_expression (file,
+ expr -> data.not, col, indent + 2, 1);
+ break;
+
+ case expr_option:
+ s = "option";
+
+ print_option_name:
+ col = token_print_indent (file, col, indent, "", "", s);
+
+ if (expr -> data.option -> universe != &dhcp_universe) {
+ col = token_print_indent (file, col, indent,
+ " ", "",
+ (expr -> data.option ->
+ universe -> name));
+ col = token_print_indent (file, col, indent, "", "",
+ ".");
+ col = token_print_indent (file, col, indent, "", "",
+ expr -> data.option -> name);
+ } else {
+ col = token_print_indent (file, col, indent, " ", "",
+ expr -> data.option -> name);
+ }
+ break;
+
+ case expr_hardware:
+ col = token_print_indent (file, col, indent, "", "",
+ "hardware");
+ break;
+
+ case expr_packet:
+ col = token_print_indent (file, col, indent, "", "",
+ "packet");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.packet.offset,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.packet.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_const_data:
+ col = token_indent_data_string (file, col, indent, "", "",
+ &expr -> data.const_data);
+ break;
+
+ case expr_extract_int8:
+ width = 8;
+ extract_int:
+ col = token_print_indent (file, col, indent, "", "",
+ "extract-int");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.extract_int,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ sprintf (obuf, "%d", width);
+ col = token_print_indent (file, col, scol, " ", "", obuf);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_extract_int16:
+ width = 16;
+ goto extract_int;
+
+ case expr_extract_int32:
+ width = 32;
+ goto extract_int;
+
+ case expr_encode_int8:
+ width = 8;
+ encode_int:
+ col = token_print_indent (file, col, indent, "", "",
+ "encode-int");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.extract_int,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ sprintf (obuf, "%d", width);
+ col = token_print_indent (file, col, scol, " ", "", obuf);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_encode_int16:
+ width = 16;
+ goto encode_int;
+
+ case expr_encode_int32:
+ width = 32;
+ goto encode_int;
+
+ case expr_const_int:
+ sprintf (obuf, "%lu", expr -> data.const_int);
+ col = token_print_indent (file, col, indent, "", "", obuf);
+ break;
+
+ case expr_exists:
+ s = "exists";
+ goto print_option_name;
+
+ case expr_encapsulate:
+ col = token_print_indent (file, col, indent, "", "",
+ "encapsulate");
+ col = token_indent_data_string (file, col, indent, " ", "",
+ &expr -> data.encapsulate);
+ break;
+
+ case expr_known:
+ col = token_print_indent (file, col, indent, "", "", "known");
+ break;
+
+ case expr_reverse:
+ col = token_print_indent (file, col, indent, "", "",
+ "reverse");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.reverse.width,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.reverse.buffer,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_leased_address:
+ col = token_print_indent (file, col, indent, "", "",
+ "leased-address");
+ break;
+
+ case expr_client_state:
+ col = token_print_indent (file, col, indent, "", "",
+ "client-state");
+ break;
+
+ case expr_binary_to_ascii:
+ col = token_print_indent (file, col, indent, "", "",
+ "binary-to-ascii");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ col = write_expression (file, expr -> data.b2a.base,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.width,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.separator,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.buffer,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_config_option:
+ s = "config-option";
+ goto print_option_name;
+
+ case expr_host_decl_name:
+ col = token_print_indent (file, col, indent, "", "",
+ "host-decl-name");
+ break;
+
+ case expr_pick_first_value:
+ e = expr;
+ col = token_print_indent (file, col, indent, "", "",
+ "concat");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ firstp = 1;
+ pick_again:
+ col = write_expression (file,
+ e -> data.pick_first_value.car,
+ col, scol, firstp);
+ firstp = 0;
+ /* We're being very lisp-like right now - instead of
+ representing this expression as (first middle . last) we're
+ representing it as (first middle last), which means that the
+ tail cdr is always nil. Apologies to non-wisp-lizards - may
+ this obscure way of describing the problem motivate you to
+ learn more about the one true computing language. */
+ if (!e -> data.pick_first_value.cdr)
+ goto no_pick_cdr;
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ if (e -> data.pick_first_value.cdr -> op ==
+ expr_pick_first_value) {
+ e = e -> data.pick_first_value.cdr;
+ goto pick_again;
+ }
+ col = write_expression (file,
+ e -> data.pick_first_value.cdr,
+ col, scol, 0);
+ no_pick_cdr:
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_lease_time:
+ col = token_print_indent (file, col, indent, "", "",
+ "lease-time");
+ break;
+
+ case expr_dns_transaction:
+ col = token_print_indent (file, col, indent, "", "",
+ "ns-update");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = 0;
+ for (e = expr;
+ e && e -> op == expr_dns_transaction;
+ e = e -> data.dns_transaction.cdr) {
+ if (!scol) {
+ scol = col;
+ firstp = 1;
+ } else
+ firstp = 0;
+ col = write_expression (file,
+ e -> data.dns_transaction.car,
+ col, scol, firstp);
+ if (e -> data.dns_transaction.cdr)
+ col = token_print_indent (file, col, scol,
+ "", " ", ",");
+ }
+ if (e)
+ col = write_expression (file, e, col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_ns_add:
+ col = token_print_indent (file, col, indent, "", "",
+ "update");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrname,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrdata,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.ttl,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_ns_delete:
+ col = token_print_indent (file, col, indent, "", "",
+ "delete");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ finish_ns_small:
+ scol = col;
+ sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrname,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrdata,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_ns_exists:
+ col = token_print_indent (file, col, indent, "", "",
+ "exists");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ goto finish_ns_small;
+
+ case expr_ns_not_exists:
+ col = token_print_indent (file, col, indent, "", "",
+ "not exists");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ goto finish_ns_small;
+
+ case expr_static:
+ col = token_print_indent (file, col, indent, "", "",
+ "static");
+ break;
+
+ case expr_null:
+ col = token_print_indent (file, col, indent, "", "", "null");
+ break;
+
+ case expr_variable_reference:
+ col = token_print_indent (file, indent, indent, "", "",
+ expr -> data.variable);
+ break;
+
+ case expr_variable_exists:
+ col = token_print_indent (file, indent, indent, "", "",
+ "defined");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ col = token_print_indent (file, col, indent, "", "",
+ expr -> data.variable);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_gethostname:
+ col = token_print_indent(file, col, indent, "", "",
+ "gethostname()");
+ break;
+
+ case expr_funcall:
+ col = token_print_indent(file, indent, indent, "", "",
+ expr->data.funcall.name);
+ col = token_print_indent(file, col, indent, " ", "", "(");
+
+ firstp = 1;
+ e = expr->data.funcall.arglist;
+ while (e != NULL) {
+ if (!firstp)
+ col = token_print_indent(file, col, indent,
+ "", " ", ",");
+
+ col = write_expression(file, e->data.arg.val, col,
+ indent, firstp);
+ firstp = 0;
+ e = e->data.arg.next;
+ }
+
+ col = token_print_indent(file, col, indent, "", "", ")");
+ break;
+
+ default:
+ log_fatal ("invalid expression type in print_expression: %d",
+ expr -> op);
+ }
+ return col;
+}
+
+struct binding *find_binding (struct binding_scope *scope, const char *name)
+{
+ struct binding *bp;
+ struct binding_scope *s;
+
+ for (s = scope; s; s = s -> outer) {
+ for (bp = s -> bindings; bp; bp = bp -> next) {
+ if (!strcasecmp (name, bp -> name)) {
+ return bp;
+ }
+ }
+ }
+ return (struct binding *)0;
+}
+
+int free_bindings (struct binding_scope *scope, const char *file, int line)
+{
+ struct binding *bp, *next;
+
+ for (bp = scope -> bindings; bp; bp = next) {
+ next = bp -> next;
+ if (bp -> name)
+ dfree (bp -> name, file, line);
+ if (bp -> value)
+ binding_value_dereference (&bp -> value, file, line);
+ dfree (bp, file, line);
+ }
+ scope -> bindings = (struct binding *)0;
+ return 1;
+}
+
+int binding_scope_dereference (ptr, file, line)
+ struct binding_scope **ptr;
+ const char *file;
+ int line;
+{
+ struct binding_scope *binding_scope;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ binding_scope = *ptr;
+ *ptr = (struct binding_scope *)0;
+ --binding_scope -> refcnt;
+ rc_register (file, line, ptr,
+ binding_scope, binding_scope -> refcnt, 1, RC_MISC);
+ if (binding_scope -> refcnt > 0)
+ return 1;
+
+ if (binding_scope -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (binding_scope);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ free_bindings (binding_scope, file, line);
+ if (binding_scope -> outer)
+ binding_scope_dereference (&binding_scope -> outer, MDL);
+ dfree (binding_scope, file, line);
+ return 1;
+}
+
+int fundef_dereference (ptr, file, line)
+ struct fundef **ptr;
+ const char *file;
+ int line;
+{
+ struct fundef *bp = *ptr;
+ struct string_list *sp, *next;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (!bp) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ bp -> refcnt--;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 1, RC_MISC);
+ if (bp -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (bp);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (!bp -> refcnt) {
+ for (sp = bp -> args; sp; sp = next) {
+ next = sp -> next;
+ dfree (sp, file, line);
+ }
+ if (bp -> statements)
+ executable_statement_dereference (&bp -> statements,
+ file, line);
+ dfree (bp, file, line);
+ }
+ *ptr = (struct fundef *)0;
+ return 1;
+}
+
+#if defined (NOTYET) /* Post 3.0 final. */
+int data_subexpression_length (int *rv,
+ struct expression *expr)
+{
+ int crhs, clhs, llhs, lrhs;
+ switch (expr -> op) {
+ case expr_substring:
+ if (expr -> data.substring.len &&
+ expr -> data.substring.len -> op == expr_const_int) {
+ (*rv =
+ (int)expr -> data.substring.len -> data.const_int);
+ return 1;
+ }
+ return 0;
+
+ case expr_packet:
+ case expr_suffix:
+ if (expr -> data.suffix.len &&
+ expr -> data.suffix.len -> op == expr_const_int) {
+ (*rv =
+ (int)expr -> data.suffix.len -> data.const_int);
+ return 1;
+ }
+ return 0;
+
+ case expr_lcase:
+ return data_subexpression_length(rv, expr->data.lcase);
+
+ case expr_ucase:
+ return data_subexpression_length(rv, expr->data.ucase);
+
+ case expr_concat:
+ clhs = data_subexpression_length (&llhs,
+ expr -> data.concat [0]);
+ crhs = data_subexpression_length (&lrhs,
+ expr -> data.concat [1]);
+ if (crhs == 0 || clhs == 0)
+ return 0;
+ *rv = llhs + lrhs;
+ return 1;
+ break;
+
+ case expr_hardware:
+ return 0;
+
+ case expr_const_data:
+ *rv = expr -> data.const_data.len;
+ return 2;
+
+ case expr_reverse:
+ return data_subexpression_length (rv,
+ expr -> data.reverse.buffer);
+
+ case expr_leased_address:
+ case expr_lease_time:
+ *rv = 4;
+ return 2;
+
+ case expr_pick_first_value:
+ clhs = data_subexpression_length (&llhs,
+ expr -> data.concat [0]);
+ crhs = data_subexpression_length (&lrhs,
+ expr -> data.concat [1]);
+ if (crhs == 0 || clhs == 0)
+ return 0;
+ if (llhs > lrhs)
+ *rv = llhs;
+ else
+ *rv = lrhs;
+ return 1;
+
+ case expr_binary_to_ascii:
+ case expr_config_option:
+ case expr_host_decl_name:
+ case expr_encapsulate:
+ case expr_filename:
+ case expr_sname:
+ case expr_host_lookup:
+ case expr_option:
+ case expr_none:
+ case expr_match:
+ case expr_check:
+ case expr_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_known:
+ case expr_dns_transaction:
+ case expr_static:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_not_equal:
+ case expr_null:
+ case expr_variable_exists:
+ case expr_variable_reference:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ case expr_gethostname:
+ return 0;
+ }
+ return 0;
+}
+
+int expr_valid_for_context (struct expression *expr,
+ enum expression_context context)
+{
+ /* We don't know at parse time what type of value a function may
+ return, so we can't flag an error on it. */
+ if (expr -> op == expr_funcall ||
+ expr -> op == expr_variable_reference)
+ return 1;
+
+ switch (context) {
+ case context_any:
+ return 1;
+
+ case context_boolean:
+ if (is_boolean_expression (expr))
+ return 1;
+ return 0;
+
+ case context_data:
+ if (is_data_expression (expr))
+ return 1;
+ return 0;
+
+ case context_numeric:
+ if (is_numeric_expression (expr))
+ return 1;
+ return 0;
+
+ case context_dns:
+ if (is_dns_expression (expr)) {
+ return 1;
+ }
+ return 0;
+
+ case context_data_or_numeric:
+ if (is_numeric_expression (expr) ||
+ is_data_expression (expr)) {
+ return 1;
+ }
+ return 0;
+
+ case context_function:
+ if (expr -> op == expr_function)
+ return 1;
+ return 0;
+ }
+ return 0;
+}
+#endif /* NOTYET */
+
+struct binding *create_binding (struct binding_scope **scope, const char *name)
+{
+ struct binding *binding;
+
+ if (!*scope) {
+ if (!binding_scope_allocate (scope, MDL))
+ return (struct binding *)0;
+ }
+
+ binding = find_binding (*scope, name);
+ if (!binding) {
+ binding = dmalloc (sizeof *binding, MDL);
+ if (!binding)
+ return (struct binding *)0;
+
+ memset (binding, 0, sizeof *binding);
+ binding -> name = dmalloc (strlen (name) + 1, MDL);
+ if (!binding -> name) {
+ dfree (binding, MDL);
+ return (struct binding *)0;
+ }
+ strcpy (binding -> name, name);
+
+ binding -> next = (*scope) -> bindings;
+ (*scope) -> bindings = binding;
+ }
+
+ return binding;
+}
+
+
+int bind_ds_value (struct binding_scope **scope,
+ const char *name,
+ struct data_string *value)
+{
+ struct binding *binding;
+
+ binding = create_binding (scope, name);
+ if (!binding)
+ return 0;
+
+ if (binding -> value)
+ binding_value_dereference (&binding -> value, MDL);
+
+ if (!binding_value_allocate (&binding -> value, MDL))
+ return 0;
+
+ data_string_copy (&binding -> value -> value.data, value, MDL);
+ binding -> value -> type = binding_data;
+
+ return 1;
+}
+
+
+int find_bound_string (struct data_string *value,
+ struct binding_scope *scope,
+ const char *name)
+{
+ struct binding *binding;
+
+ binding = find_binding (scope, name);
+ if (!binding ||
+ !binding -> value ||
+ binding -> value -> type != binding_data)
+ return 0;
+
+ if (binding -> value -> value.data.terminated) {
+ data_string_copy (value, &binding -> value -> value.data, MDL);
+ } else {
+ buffer_allocate (&value -> buffer,
+ binding -> value -> value.data.len,
+ MDL);
+ if (!value -> buffer)
+ return 0;
+
+ memcpy (value -> buffer -> data,
+ binding -> value -> value.data.data,
+ binding -> value -> value.data.len);
+ value -> data = value -> buffer -> data;
+ value -> len = binding -> value -> value.data.len;
+ }
+
+ return 1;
+}
+
+int unset (struct binding_scope *scope, const char *name)
+{
+ struct binding *binding;
+
+ binding = find_binding (scope, name);
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ return 1;
+ }
+ return 0;
+}
+
+/* vim: set tabstop=8: */
diff --git a/common/upf.c b/common/upf.c
new file mode 100644
index 0000000..feb82a2
--- /dev/null
+++ b/common/upf.c
@@ -0,0 +1,373 @@
+/* upf.c
+
+ Ultrix PacketFilter interface code. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#if defined (USE_UPF_SEND) || defined (USE_UPF_RECEIVE)
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/pfilt.h>
+#include <netinet/in_systm.h>
+#include "includes/netinet/ip.h"
+#include "includes/netinet/udp.h"
+#include "includes/netinet/if_ether.h"
+
+/* Reinitializes the specified interface after an address change. This
+ is not required for packet-filter APIs. */
+
+#ifdef USE_UPF_SEND
+void if_reinitialize_send (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+#ifdef USE_UPF_RECEIVE
+void if_reinitialize_receive (info)
+ struct interface_info *info;
+{
+}
+#endif
+
+/* 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_upf (info)
+ struct interface_info *info;
+{
+ int sock;
+ char filename[50];
+ int b;
+ struct endevp param;
+
+ /* Open a UPF device */
+ for (b = 0; 1; b++) {
+ /* %Audit% Cannot exceed 36 bytes. %2004.06.17,Safe% */
+ sprintf(filename, "/dev/pf/pfilt%d", b);
+
+ sock = open (filename, O_RDWR, 0);
+ if (sock < 0) {
+ if (errno == EBUSY) {
+ continue;
+ } else {
+ log_fatal ("Can't find free upf: %m");
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* Set the UPF device to point at this interface. */
+ if (ioctl (sock, EIOCSETIF, info -> ifp) < 0)
+ log_fatal ("Can't attach interface %s to upf device %s: %m",
+ info -> name, filename);
+
+ /* Get the hardware address. */
+ if (ioctl (sock, EIOCDEVP, &param) < 0)
+ log_fatal ("Can't get interface %s hardware address: %m",
+ info -> name);
+
+ /* We only know how to do ethernet. */
+ if (param.end_dev_type != ENDT_10MB)
+ log_fatal ("Invalid device type on network interface %s: %d",
+ info -> name, param.end_dev_type);
+
+ if (param.end_addr_len != 6)
+ log_fatal ("Invalid hardware address length on %s: %d",
+ info -> name, param.end_addr_len);
+
+ info -> hw_address.hlen = 7;
+ info -> hw_address.hbuf [0] = ARPHRD_ETHER;
+ memcpy (&info -> hw_address.hbuf [1], param.end_addr, 6);
+
+ return sock;
+}
+#endif /* USE_UPF_SEND || USE_UPF_RECEIVE */
+
+#ifdef USE_UPF_SEND
+void if_register_send (info)
+ struct interface_info *info;
+{
+ /* If we're using the upf API for sending and receiving,
+ we don't need to register this interface twice. */
+#ifndef USE_UPF_RECEIVE
+ info -> wfdesc = if_register_upf (info, interface);
+#else
+ info -> wfdesc = info -> rfdesc;
+#endif
+ if (!quiet_interface_discovery)
+ log_info ("Sending on UPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_send (info)
+ struct interface_info *info;
+{
+#ifndef USE_UPF_RECEIVE
+ close (info -> wfdesc);
+#endif
+ info -> wfdesc = -1;
+ if (!quiet_interface_discovery)
+ log_info ("Disabling output on UPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_UPF_SEND */
+
+#ifdef USE_UPF_RECEIVE
+/* Packet filter program...
+ XXX Changes to the filter program may require changes to the constant
+ offsets used in if_register_send to patch the UPF program! XXX */
+
+
+void if_register_receive (info)
+ struct interface_info *info;
+{
+ int flag = 1;
+ u_int32_t addr;
+ struct enfilter pf;
+ u_int32_t bits;
+
+ /* Open a UPF device and hang it on this interface... */
+ info -> rfdesc = if_register_upf (info);
+
+ /* Allow the copyall flag to be set... */
+ if (ioctl(info -> rfdesc, EIOCALLOWCOPYALL, &flag) < 0)
+ log_fatal ("Can't set ALLOWCOPYALL: %m");
+
+ /* Clear all the packet filter mode bits first... */
+ flag = (ENHOLDSIG | ENBATCH | ENTSTAMP | ENPROMISC |
+ ENNONEXCL | ENCOPYALL);
+ if (ioctl (info -> rfdesc, EIOCMBIC, &flag) < 0)
+ log_fatal ("Can't clear pfilt bits: %m");
+
+ /* Set the ENBATCH and ENCOPYALL bits... */
+ bits = ENBATCH | ENCOPYALL;
+ if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
+ log_fatal ("Can't set ENBATCH|ENCOPYALL: %m");
+
+ /* Set up the UPF filter program. */
+ /* XXX Unlike the BPF filter program, this one won't work if the
+ XXX IP packet is fragmented or if there are options on the IP
+ XXX header. */
+ pf.enf_Priority = 0;
+ pf.enf_FilterLen = 0;
+
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 6;
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+ pf.enf_Filter [pf.enf_FilterLen++] = htons (ETHERTYPE_IP);
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT;
+ pf.enf_Filter [pf.enf_FilterLen++] = htons (IPPROTO_UDP);
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 11;
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_AND;
+ pf.enf_Filter [pf.enf_FilterLen++] = htons (0xFF);
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_CAND;
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHWORD + 18;
+ pf.enf_Filter [pf.enf_FilterLen++] = ENF_PUSHLIT + ENF_CAND;
+ pf.enf_Filter [pf.enf_FilterLen++] = local_port;
+
+ if (ioctl (info -> rfdesc, EIOCSETF, &pf) < 0)
+ log_fatal ("Can't install packet filter program: %m");
+ if (!quiet_interface_discovery)
+ log_info ("Listening on UPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+
+void if_deregister_receive (info)
+ struct interface_info *info;
+{
+ close (info -> rfdesc);
+ info -> rfdesc = -1;
+ if (!quiet_interface_discovery)
+ log_info ("Disabling input on UPF/%s/%s%s%s",
+ info -> name,
+ print_hw_addr (info -> hw_address.hbuf [0],
+ info -> hw_address.hlen - 1,
+ &info -> hw_address.hbuf [1]),
+ (info -> shared_network ? "/" : ""),
+ (info -> shared_network ?
+ info -> shared_network -> name : ""));
+}
+#endif /* USE_UPF_RECEIVE */
+
+#ifdef USE_UPF_SEND
+ssize_t send_packet (interface, packet, raw, len, from, to, hto)
+ struct interface_info *interface;
+ struct packet *packet;
+ struct dhcp_packet *raw;
+ size_t len;
+ struct in_addr from;
+ struct sockaddr_in *to;
+ struct hardware *hto;
+{
+ unsigned hbufp = 0, ibufp = 0;
+ double hw [4];
+ double ip [32];
+ struct iovec iov [3];
+ int result;
+ int fudge;
+
+ if (!strcmp (interface -> name, "fallback"))
+ return send_fallback (interface, packet, raw,
+ len, from, to, hto);
+
+ if (hto == NULL && interface->anycast_mac_addr.hlen)
+ hto = &interface->anycast_mac_addr;
+
+ /* Assemble the headers... */
+ assemble_hw_header (interface, (unsigned char *)hw, &hbufp, hto);
+ assemble_udp_ip_header (interface,
+ (unsigned char *)ip, &ibufp, from.s_addr,
+ to -> sin_addr.s_addr, to -> sin_port,
+ (unsigned char *)raw, len);
+
+ /* Fire it off */
+ iov [0].iov_base = ((char *)hw);
+ iov [0].iov_len = hbufp;
+ iov [1].iov_base = ((char *)ip);
+ iov [1].iov_len = ibufp;
+ iov [2].iov_base = (char *)raw;
+ iov [2].iov_len = len;
+
+ result = writev(interface -> wfdesc, iov, 3);
+ if (result < 0)
+ log_error ("send_packet: %m");
+ return result;
+}
+#endif /* USE_UPF_SEND */
+
+#ifdef USE_UPF_RECEIVE
+ssize_t receive_packet (interface, buf, len, from, hfrom)
+ struct interface_info *interface;
+ unsigned char *buf;
+ size_t len;
+ struct sockaddr_in *from;
+ struct hardware *hfrom;
+{
+ int nread;
+ int length = 0;
+ int offset = 0;
+ unsigned char ibuf [1500 + sizeof (struct enstamp)];
+ int bufix = 0;
+ unsigned paylen;
+
+ length = read (interface -> rfdesc, ibuf, sizeof ibuf);
+ if (length <= 0)
+ return length;
+
+ bufix = sizeof (struct enstamp);
+ /* Decode the physical header... */
+ offset = decode_hw_header (interface, ibuf, bufix, hfrom);
+
+ /* If a physical layer checksum failed (dunno of any
+ physical layer that supports this, but WTH), skip this
+ packet. */
+ if (offset < 0) {
+ return 0;
+ }
+
+ bufix += offset;
+ length -= offset;
+
+ /* Decode the IP and UDP headers... */
+ offset = decode_udp_ip_header (interface, ibuf, bufix,
+ from, length, &paylen);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0)
+ return 0;
+
+ bufix += offset;
+ length -= offset;
+
+ if (length < paylen)
+ log_fatal("Internal inconsistency at %s:%d.", MDL);
+
+ /* Copy out the data in the packet... */
+ memcpy (buf, &ibuf[bufix], paylen);
+ return paylen;
+}
+
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int can_receive_unicast_unconfigured (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
+void maybe_setup_fallback ()
+{
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
+ if_register_fallback (fbi);
+ status = omapi_register_io_object ((omapi_object_t *)fbi,
+ if_readsocket, 0,
+ fallback_discard, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ fbi -> name, isc_result_totext (status));
+ interface_dereference (&fbi, MDL);
+ }
+}
+#endif
diff --git a/configure b/configure
new file mode 100755
index 0000000..7825997
--- /dev/null
+++ b/configure
@@ -0,0 +1,9935 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.61 for DHCP 4.2.2.
+#
+# Report bugs to <dhcp-users@isc.org>.
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+if test "x$CONFIG_SHELL" = x; then
+ if (eval ":") 2>/dev/null; then
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+
+ if test $as_have_required = yes && (eval ":
+(as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=\$LINENO
+ as_lineno_2=\$LINENO
+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
+") 2> /dev/null; then
+ :
+else
+ as_candidate_shells=
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ case $as_dir in
+ /*)
+ for as_base in sh bash ksh sh5; do
+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
+ done;;
+ esac
+done
+IFS=$as_save_IFS
+
+
+ for as_shell in $as_candidate_shells $SHELL; do
+ # Try only shells that exist, to save several forks.
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { ("$as_shell") 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+_ASEOF
+}; then
+ CONFIG_SHELL=$as_shell
+ as_have_required=yes
+ if { "$as_shell" 2> /dev/null <<\_ASEOF
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+:
+(as_func_return () {
+ (exit $1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = "$1" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test $exitcode = 0) || { (exit 1); exit 1; }
+
+(
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
+
+_ASEOF
+}; then
+ break
+fi
+
+fi
+
+ done
+
+ if test "x$CONFIG_SHELL" != x; then
+ for as_var in BASH_ENV ENV
+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ done
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+
+ if test $as_have_required = no; then
+ echo This script requires a shell more modern than all the
+ echo shells that I found on your system. Please install a
+ echo modern shell, or manually run the script under such a
+ echo shell if you do have one.
+ { (exit 1); exit 1; }
+fi
+
+
+fi
+
+fi
+
+
+
+(eval "as_func_return () {
+ (exit \$1)
+}
+as_func_success () {
+ as_func_return 0
+}
+as_func_failure () {
+ as_func_return 1
+}
+as_func_ret_success () {
+ return 0
+}
+as_func_ret_failure () {
+ return 1
+}
+
+exitcode=0
+if as_func_success; then
+ :
+else
+ exitcode=1
+ echo as_func_success failed.
+fi
+
+if as_func_failure; then
+ exitcode=1
+ echo as_func_failure succeeded.
+fi
+
+if as_func_ret_success; then
+ :
+else
+ exitcode=1
+ echo as_func_ret_success failed.
+fi
+
+if as_func_ret_failure; then
+ exitcode=1
+ echo as_func_ret_failure succeeded.
+fi
+
+if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
+ :
+else
+ exitcode=1
+ echo positional parameters were not saved.
+fi
+
+test \$exitcode = 0") || {
+ echo No shell found that supports shell functions.
+ echo Please tell autoconf@gnu.org about your system,
+ echo including any error possibly output before this
+ echo message
+}
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+exec 7<&0 </dev/null 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Identity of this package.
+PACKAGE_NAME='DHCP'
+PACKAGE_TARNAME='dhcp'
+PACKAGE_VERSION='4.2.2'
+PACKAGE_STRING='DHCP 4.2.2'
+PACKAGE_BUGREPORT='dhcp-users@isc.org'
+
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL
+PATH_SEPARATOR
+PACKAGE_NAME
+PACKAGE_TARNAME
+PACKAGE_VERSION
+PACKAGE_STRING
+PACKAGE_BUGREPORT
+exec_prefix
+prefix
+program_transform_name
+bindir
+sbindir
+libexecdir
+datarootdir
+datadir
+sysconfdir
+sharedstatedir
+localstatedir
+includedir
+oldincludedir
+docdir
+infodir
+htmldir
+dvidir
+pdfdir
+psdir
+libdir
+localedir
+mandir
+DEFS
+ECHO_C
+ECHO_N
+ECHO_T
+LIBS
+build_alias
+host_alias
+target_alias
+INSTALL_PROGRAM
+INSTALL_SCRIPT
+INSTALL_DATA
+am__isrc
+CYGPATH_W
+PACKAGE
+VERSION
+ACLOCAL
+AUTOCONF
+AUTOMAKE
+AUTOHEADER
+MAKEINFO
+install_sh
+STRIP
+INSTALL_STRIP_PROGRAM
+mkdir_p
+AWK
+SET_MAKE
+am__leading_dot
+AMTAR
+am__tar
+am__untar
+CC
+CFLAGS
+LDFLAGS
+CPPFLAGS
+ac_ct_CC
+EXEEXT
+OBJEXT
+DEPDIR
+am__include
+am__quote
+AMDEP_TRUE
+AMDEP_FALSE
+AMDEPBACKSLASH
+CCDEPMODE
+am__fastdepCC_TRUE
+am__fastdepCC_FALSE
+CPP
+GREP
+EGREP
+RANLIB
+byte_order
+ac_prefix_program
+LDAP_CFLAGS
+LIBOBJS
+LTLIBOBJS'
+ac_subst_files=''
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`
+ eval enable_$ac_feature=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`
+ eval with_$ac_package=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute directory names.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; }
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ { echo "$as_me: error: Working directory cannot be determined" >&2
+ { (exit 1); exit 1; }; }
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ { echo "$as_me: error: pwd does not report name of working directory" >&2
+ { (exit 1); exit 1; }; }
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$0" ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
+ { (exit 1); exit 1; }; }
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures DHCP 4.2.2 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/dhcp]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of DHCP 4.2.2:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-debug create a debug-only version of the software (default
+ is no).
+ --enable-failover enable support for failover (default is yes)
+ --enable-execute enable support for execute() in config (default is
+ yes)
+ --enable-tracing enable support for server activity tracing (default
+ is yes)
+ --enable-delayed-ack queues multiple DHCPACK replies (default is no)
+ --enable-dhcpv6 enable support for DHCPv6 (default is yes)
+ --enable-paranoia enable support for chroot/setuid (default is no)
+ --enable-early-chroot enable chrooting prior to configuration (default is
+ no)
+ --enable-ipv4-pktinfo enable use of pktinfo on IPv4 sockets (default is
+ no)
+ --enable-use-sockets use the standard BSD socket API (default is no)
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-srv-lease-file=PATH
+ File for dhcpd leases (default is
+ LOCALSTATEDIR/db/dhcpd.leases)
+ --with-srv6-lease-file=PATH
+ File for dhcpd6 leases (default is
+ LOCALSTATEDIR/db/dhcpd6.leases)
+ --with-cli-lease-file=PATH
+ File for dhclient leases (default is
+ LOCALSTATEDIR/db/dhclient.leases)
+ --with-cli6-lease-file=PATH
+ File for dhclient6 leases (default is
+ LOCALSTATEDIR/db/dhclient6.leases)
+ --with-srv-pid-file=PATH
+ File for dhcpd process information (default is
+ LOCALSTATEDIR/run/dhcpd.pid)
+ --with-srv6-pid-file=PATH
+ File for dhcpd6 process information (default is
+ LOCALSTATEDIR/run/dhcpd6.pid)
+ --with-cli-pid-file=PATH
+ File for dhclient process information (default is
+ LOCALSTATEDIR/run/dhclient.pid)
+ --with-cli6-pid-file=PATH
+ File for dhclient6 process information (default is
+ LOCALSTATEDIR/run/dhclient6.pid)
+ --with-relay-pid-file=PATH
+ File for dhcrelay process information (default is
+ LOCALSTATEDIR/run/dhcrelay.pid)
+ --with-relay6-pid-file=PATH
+ File for dhcrelay6 process information (default is
+ LOCALSTATEDIR/run/dhcrelay6.pid)
+ --with-libbind=PATH bind includes and libraries are in PATH (default is
+ ./bind)
+ --with-ldap enable OpenLDAP support in dhcpd (default is no)
+ --with-ldapcrypto enable OpenLDAP crypto support in dhcpd (default is
+ no)
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <dhcp-users@isc.org>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" || continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+DHCP configure 4.2.2
+generated by GNU Autoconf 2.61
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by DHCP $as_me 4.2.2, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args '$ac_arg'"
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------------- ##
+## File substitutions. ##
+## ------------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+ set x "$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+ set x "$prefix/share/config.site" "$prefix/etc/config.site"
+else
+ set x "$ac_default_prefix/share/config.site" \
+ "$ac_default_prefix/etc/config.site"
+fi
+shift
+for ac_site_file
+do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+# we specify "foreign" to avoid having to have the GNU mandated files,
+# like AUTHORS, COPYING, and such
+am__api_version='1.10'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5
+echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+IFS=$as_save_IFS
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ echo "$as_me:$LINENO: checking whether build environment is sane" >&5
+echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t $srcdir/configure conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken
+alias in your environment" >&5
+echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken
+alias in your environment" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
+Check your system clock" >&5
+echo "$as_me: error: newly created file is older than distributed files!
+Check your system clock" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $. echo might interpret backslashes.
+# By default was `s,x,x', remove it if useless.
+cat <<\_ACEOF >conftest.sed
+s/[\\$]/&&/g;s/;s,x,x,$//
+_ACEOF
+program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
+rm -f conftest.sed
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
+echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+{ echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
+echo $ECHO_N "checking for a thread-safe mkdir -p... $ECHO_C" >&6; }
+if test -z "$MKDIR_P"; then
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+done
+IFS=$as_save_IFS
+
+fi
+
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ test -d ./--version && rmdir ./--version
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $MKDIR_P" >&5
+echo "${ECHO_T}$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; }
+set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+ SET_MAKE=
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
+echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='dhcp'
+ VERSION='4.2.2'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+# We want to turn on warnings if we are using gcc and the user did
+# not specify CFLAGS. The autoconf check for the C compiler sets the
+# CFLAGS if gcc is used, so we will save it before we run that check.
+SAVE_CFLAGS="$CFLAGS"
+
+# Now find our C compiler.
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO: checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (ac_try="$ac_compiler --version >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler --version >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -v >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -v >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (ac_try="$ac_compiler -V >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compiler -V >&5") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+#
+# List of possible output files, starting from the most likely.
+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
+# only as a last resort. b.out is created by i960 compilers.
+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
+#
+# The IRIX 6 linker writes into existing files which may not be
+# executable, retaining their permissions. Remove them first so a
+# subsequent execution test works.
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { (ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+
+{ echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6; }
+if test -z "$ac_file"; then
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+{ echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }
+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6; }
+
+{ echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_compiler_gnu=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ CFLAGS=""
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5
+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_prog_cc_c89=$ac_arg
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6; } ;;
+ xno)
+ { echo "$as_me:$LINENO: result: unsupported" >&5
+echo "${ECHO_T}unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
+echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+ am__include=include
+ am__quote=
+ _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ fi
+fi
+
+
+{ echo "$as_me:$LINENO: result: $_am_result" >&5
+echo "${ECHO_T}$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; }
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ case $depmode in
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ none) break ;;
+ esac
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this.
+ if depmode=$depmode \
+ source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+
+# Suppress warnings about --datarootdir
+
+
+# If we have gcc, and AC_PROG_CC changed the flags, then we know the
+# user did not specify any flags. Add warnings in this case.
+if test "$GCC" = "yes"; then
+ if test "$CFLAGS" != "$SAVE_CFLAGS"; then
+ STD_CWARNINGS="$STD_CWARNINGS -Wall -Werror -fno-strict-aliasing"
+ fi
+fi
+
+# POSIX doesn't include the IPv6 Advanced Socket API and glibc hides
+# parts of the IPv6 Advanced Socket API as a result. This is stupid
+# as it breaks how the two halves (Basic and Advanced) of the IPv6
+# Socket API were designed to be used but we have to live with it.
+# Use this to define _GNU_SOURCE to pull in the IPv6 Advanced Socket API.
+
+cat >>confdefs.h <<\_ACEOF
+#define _GNU_SOURCE 1
+_ACEOF
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Extract the first word of "grep ggrep" to use in msg output
+if test -z "$GREP"; then
+set dummy grep ggrep; ac_prog_name=$2
+if test "${ac_cv_path_GREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_path_GREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ # Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+ $ac_path_GREP_found && break 3
+ done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+GREP="$ac_cv_path_GREP"
+if test -z "$GREP"; then
+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ # Extract the first word of "egrep" to use in msg output
+if test -z "$EGREP"; then
+set dummy egrep; ac_prog_name=$2
+if test "${ac_cv_path_EGREP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_path_EGREP_found=false
+# Loop through the user's path and test for each of PROGNAME-LIST
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ # Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ ac_count=`expr $ac_count + 1`
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+
+ $ac_path_EGREP_found && break 3
+ done
+done
+
+done
+IFS=$as_save_IFS
+
+
+fi
+
+EGREP="$ac_cv_path_EGREP"
+if test -z "$EGREP"; then
+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+
+ fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+
+{ echo "$as_me:$LINENO: checking for AIX" >&5
+echo $ECHO_N "checking for AIX... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef _AIX
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+cat >>confdefs.h <<\_ACEOF
+#define _ALL_SOURCE 1
+_ACEOF
+
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+rm -f conftest*
+
+
+{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_stdc=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ eval "$as_ac_Header=no"
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking minix/config.h usability" >&5
+echo $ECHO_N "checking minix/config.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <minix/config.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking minix/config.h presence" >&5
+echo $ECHO_N "checking minix/config.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <minix/config.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: minix/config.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: minix/config.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: minix/config.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: minix/config.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: minix/config.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: minix/config.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: minix/config.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: minix/config.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for minix/config.h" >&5
+echo $ECHO_N "checking for minix/config.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_minix_config_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_minix_config_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_minix_config_h" >&5
+echo "${ECHO_T}$ac_cv_header_minix_config_h" >&6; }
+
+fi
+if test $ac_cv_header_minix_config_h = yes; then
+ MINIX=yes
+else
+ MINIX=
+fi
+
+
+if test "$MINIX" = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_SOURCE 1
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _POSIX_1_SOURCE 2
+_ACEOF
+
+
+cat >>confdefs.h <<\_ACEOF
+#define _MINIX 1
+_ACEOF
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+ { echo "$as_me:$LINENO: checking whether it is safe to define __EXTENSIONS__" >&5
+echo $ECHO_N "checking whether it is safe to define __EXTENSIONS__... $ECHO_C" >&6; }
+if test "${ac_cv_safe_to_define___extensions__+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+# define __EXTENSIONS__ 1
+ $ac_includes_default
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_safe_to_define___extensions__=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_safe_to_define___extensions__=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_safe_to_define___extensions__" >&5
+echo "${ECHO_T}$ac_cv_safe_to_define___extensions__" >&6; }
+ test $ac_cv_safe_to_define___extensions__ = yes &&
+ cat >>confdefs.h <<\_ACEOF
+#define __EXTENSIONS__ 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define _POSIX_PTHREAD_SEMANTICS 1
+_ACEOF
+
+ cat >>confdefs.h <<\_ACEOF
+#define _TANDEM_SOURCE 1
+_ACEOF
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&5
+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
+whose name does not start with the host triplet. If you think this
+configuration is useful to you, please write to autoconf@gnu.org." >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+ac_config_headers="$ac_config_headers includes/config.h"
+
+
+# we sometimes need to know byte order for building packets
+{ echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6; }
+if test "${ac_cv_c_bigendian+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN && defined LITTLE_ENDIAN \
+ && BYTE_ORDER && BIG_ENDIAN && LITTLE_ENDIAN)
+ bogus endian macros
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_bigendian=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_bigendian=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+ # try to guess the endianness by grepping values into an object file
+ ac_cv_c_bigendian=unknown
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+ ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+fi
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+ u.l = 1;
+ return u.c[sizeof (long int) - 1] == 1;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_bigendian=no
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6; }
+case $ac_cv_c_bigendian in
+ yes)
+ byte_order=BIG_ENDIAN
+ ;;
+ no)
+ byte_order=LITTLE_ENDIAN
+ ;;
+ *)
+ { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+ { (exit 1); exit 1; }; } ;;
+esac
+
+
+cat >>confdefs.h <<_ACEOF
+#define DHCP_BYTE_ORDER $byte_order
+_ACEOF
+
+
+# Optional compile-time DEBUGging.
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval=$enable_debug;
+fi
+
+# This is very much off by default.
+if test "$enable_debug" = "yes" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DEBUG 1
+_ACEOF
+
+ # Just override CFLAGS to totally to remove optimization.
+ CFLAGS="-g"
+fi
+# XXX: there are actually quite a lot more DEBUG_ features we could enable,
+# but I don't want to pollute the --help space.
+#
+#/* #define DEBUG_TOKENS */
+#/* #define DEBUG_PACKET */
+#/* #define DEBUG_EXPRESSIONS */
+#/* #define DEBUG_FIND_LEASE */
+#/* #define DEBUG_EXPRESSION_PARSE */
+#/* #define DEBUG_CLASS_MATCHING */
+#/* #define DEBUG_MEMORY_LEAKAGE */
+#/* #define DEBUG_MALLOC_POOL */
+#/* #define DEBUG_LEASE_STATE_TRANSITIONS */
+#/* #define DEBUG_RC_HISTORY */
+#/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */
+#/* #define RC_HISTORY_MAX 10240 */
+#/* #define POINTER_DEBUG */
+#/* #define DEBUG_FAILOVER_MESSAGES */
+#/* #define DEBUG_FAILOVER_TIMING */
+#/* #define DEBUG_DUMP_ALL_LEASES */
+
+# Failover optional compile-time feature.
+# Check whether --enable-failover was given.
+if test "${enable_failover+set}" = set; then
+ enableval=$enable_failover;
+fi
+
+# Failover is on by default, so define if it is not explicitly disabled.
+if test "$enable_failover" != "no"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define FAILOVER_PROTOCOL 1
+_ACEOF
+
+fi
+
+# execute() support.
+# Check whether --enable-execute was given.
+if test "${enable_execute+set}" = set; then
+ enableval=$enable_execute;
+fi
+
+# execute() is on by default, so define if it is not explicitly disabled.
+if test "$enable_execute" != "no" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define ENABLE_EXECUTE 1
+_ACEOF
+
+fi
+
+# Server tracing support.
+# Check whether --enable-tracing was given.
+if test "${enable_tracing+set}" = set; then
+ enableval=$enable_tracing;
+fi
+
+# tracing is on by default, so define if it is not explicitly disabled.
+if test "$enable_tracing" != "no" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define TRACING 1
+_ACEOF
+
+fi
+
+# Delayed-ack feature support (experimental).
+# Check whether --enable-delayed_ack was given.
+if test "${enable_delayed_ack+set}" = set; then
+ enableval=$enable_delayed_ack;
+fi
+
+if test "$enable_delayed_ack" = "yes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DELAYED_ACK 1
+_ACEOF
+
+fi
+
+# DHCPv6 optional compile-time feature.
+# Check whether --enable-dhcpv6 was given.
+if test "${enable_dhcpv6+set}" = set; then
+ enableval=$enable_dhcpv6;
+fi
+
+# DHCPv6 is on by default, so define if it is not explicitly disabled.
+if test "$enable_dhcpv6" != "no"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define DHCPv6 1
+_ACEOF
+
+fi
+
+# PARANOIA is off by default (until we can test it with all features)
+# Check whether --enable-paranoia was given.
+if test "${enable_paranoia+set}" = set; then
+ enableval=$enable_paranoia;
+fi
+
+# Check whether --enable-early_chroot was given.
+if test "${enable_early_chroot+set}" = set; then
+ enableval=$enable_early_chroot;
+fi
+
+# If someone enables early chroot, but does not enable paranoia, do so for
+# them.
+if test "$enable_paranoia" != "yes" && \
+ test "$enable_early_chroot" = "yes" ; then
+ enable_paranoia="yes"
+fi
+
+if test "$enable_paranoia" = "yes" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define PARANOIA 1
+_ACEOF
+
+fi
+if test "$enable_early_chroot" = "yes" ; then
+
+cat >>confdefs.h <<\_ACEOF
+#define EARLY_CHROOT 1
+_ACEOF
+
+fi
+
+# Check whether --enable-IPv4_PKTINFO was given.
+if test "${enable_IPv4_PKTINFO+set}" = set; then
+ enableval=$enable_IPv4_PKTINFO;
+fi
+
+
+if test "$enable_ipv4_pktinfo" = "yes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_V4_PKTINFO 1
+_ACEOF
+
+fi
+
+# Check whether --enable-USE_SOCKETS was given.
+if test "${enable_USE_SOCKETS+set}" = set; then
+ enableval=$enable_USE_SOCKETS;
+fi
+
+
+if test "$enable_use_sockets" = "yes"; then
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_SOCKETS 1
+_ACEOF
+
+fi
+
+###
+### Path fun. Older versions of DHCP were installed in /usr/sbin, so we
+### need to look there and potentially overwrite by default (but not if
+### the user configures an alternate value). LOCALSTATEDIR is totally
+### braindead. No one uses /usr/local/var/db/ nor /usr/local/var/run, and
+### they would be insane for suggesting it. We need to look in /var/for
+### 'db' and 'state/dhcp' for db files, and /var/run for pid files by
+### default.
+###
+if test "x$prefix" = xNONE; then
+ echo $ECHO_N "checking for prefix by $ECHO_C" >&6
+ # Extract the first word of "dhcpd", so it can be a program name with args.
+set dummy dhcpd; ac_word=$2
+{ echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }
+if test "${ac_cv_path_ac_prefix_program+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $ac_prefix_program in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_prefix_program="$ac_prefix_program" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ac_prefix_program="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_prefix_program=$ac_cv_path_ac_prefix_program
+if test -n "$ac_prefix_program"; then
+ { echo "$as_me:$LINENO: result: $ac_prefix_program" >&5
+echo "${ECHO_T}$ac_prefix_program" >&6; }
+else
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+
+ if test -n "$ac_prefix_program"; then
+ prefix=`$as_dirname -- "$ac_prefix_program" ||
+$as_expr X"$ac_prefix_program" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_prefix_program" : 'X\(//\)[^/]' \| \
+ X"$ac_prefix_program" : 'X\(//\)$' \| \
+ X"$ac_prefix_program" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_prefix_program" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ prefix=`$as_dirname -- "$prefix" ||
+$as_expr X"$prefix" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$prefix" : 'X\(//\)[^/]' \| \
+ X"$prefix" : 'X\(//\)$' \| \
+ X"$prefix" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$prefix" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ fi
+fi
+
+
+# XXX - isn't there SOME WAY to default autoconf to /var instead of
+# /usr/local/var/no/one/has/this/please/stop/trying?
+case "$localstatedir" in
+ '${prefix}/var')
+ localstatedir=/var
+ ;;
+esac
+
+# Allow specification of alternate state files
+
+# Check whether --with-srv-lease-file was given.
+if test "${with_srv_lease_file+set}" = set; then
+ withval=$with_srv_lease_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCPD_DB "$withval"
+_ACEOF
+
+fi
+
+
+echo -n "checking for dhcpd.leases location..."
+if [ "x$with_srv_lease_file" = "x" ] ; then
+ if [ -d "${localstatedir}/db" ] ; then
+ with_srv_lease_file="${localstatedir}/db/dhcpd.leases"
+ elif [ -d "${localstatedir}/state" ] ; then
+ if [ -d "${localstatedir}/state/dhcp" ] ; then
+ with_srv_lease_file="${localstatedir}/state/dhcp/dhcpd.leases"
+ else
+ with_srv_lease_file="${localstatedir}/state/dhcpd.leases"
+ fi
+ elif [ -d "${localstatedir}/lib" ] ; then
+ if [ -d "${localstatedir}/lib/dhcp" ] ; then
+ with_srv_lease_file="${localstatedir}/lib/dhcp/dhcpd.leases"
+ else
+ with_srv_lease_file="${localstatedir}/lib/dhcpd.leases"
+ fi
+ elif [ -d "${localstatedir}/etc" ] ; then
+ with_srv_lease_file="${localstatedir}/etc/dhcpd.leases"
+ else
+ with_srv_lease_file="/etc/dhcpd.leases"
+ fi
+fi
+echo "$with_srv_lease_file"
+
+
+# Check whether --with-srv6-lease-file was given.
+if test "${with_srv6_lease_file+set}" = set; then
+ withval=$with_srv6_lease_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCPD6_DB "$withval"
+_ACEOF
+
+fi
+
+
+echo -n "checking for dhcpd6.leases location..."
+if [ "x$with_srv6_lease_file" = "x" ] ; then
+ if [ -d "${localstatedir}/db" ] ; then
+ with_srv6_lease_file="${localstatedir}/db/dhcpd6.leases"
+ elif [ -d "${localstatedir}/state" ] ; then
+ if [ -d "${localstatedir}/state/dhcp" ] ; then
+ with_srv6_lease_file="${localstatedir}/state/dhcp/dhcpd6.leases"
+ else
+ with_srv6_lease_file="${localstatedir}/state/dhcpd6.leases"
+ fi
+ elif [ -d "${localstatedir}/lib" ] ; then
+ if [ -d "${localstatedir}/lib/dhcp" ] ; then
+ with_srv6_lease_file="${localstatedir}/lib/dhcp/dhcpd6.leases"
+ else
+ with_srv6_lease_file="${localstatedir}/lib/dhcpd6.leases"
+ fi
+ elif [ -d "${localstatedir}/etc" ] ; then
+ with_srv6_lease_file="${localstatedir}/etc/dhcpd6.leases"
+ else
+ with_srv6_lease_file="/etc/dhcpd6.leases"
+ fi
+fi
+echo "$with_srv6_lease_file"
+
+
+# Check whether --with-cli-lease-file was given.
+if test "${with_cli_lease_file+set}" = set; then
+ withval=$with_cli_lease_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCLIENT_DB "$withval"
+_ACEOF
+
+fi
+
+
+echo -n "checking for dhclient.leases location..."
+if [ "x$with_cli_lease_file" = "x" ] ; then
+ if [ -d "${localstatedir}/db" ] ; then
+ with_cli_lease_file="${localstatedir}/db/dhclient.leases"
+ elif [ -d "${localstatedir}/state" ] ; then
+ if [ -d "${localstatedir}/state/dhcp" ] ; then
+ with_cli_lease_file="${localstatedir}/state/dhcp/dhclient.leases"
+ else
+ with_cli_lease_file="${localstatedir}/state/dhclient.leases"
+ fi
+ elif [ -d "${localstatedir}/lib" ] ; then
+ if [ -d "${localstatedir}/lib/dhcp" ] ; then
+ with_cli_lease_file="${localstatedir}/lib/dhcp/dhclient.leases"
+ else
+ with_cli_lease_file="${localstatedir}/lib/dhclient.leases"
+ fi
+ elif [ -d "${localstatedir}/etc" ] ; then
+ with_cli_lease_file="${localstatedir}/etc/dhclient.leases"
+ else
+ with_cli_lease_file="/etc/dhclient.leases"
+ fi
+fi
+echo "$with_cli_lease_file"
+
+
+# Check whether --with-cli6-lease-file was given.
+if test "${with_cli6_lease_file+set}" = set; then
+ withval=$with_cli6_lease_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCLIENT6_DB "$withval"
+_ACEOF
+
+fi
+
+
+echo -n "checking for dhclient6.leases location..."
+if [ "x$with_cli6_lease_file" = "x" ] ; then
+ if [ -d "${localstatedir}/db" ] ; then
+ with_cli6_lease_file="${localstatedir}/db/dhclient6.leases"
+ elif [ -d "${localstatedir}/state" ] ; then
+ if [ -d "${localstatedir}/state/dhcp" ] ; then
+ with_cli6_lease_file="${localstatedir}/state/dhcp/dhclient6.leases"
+ else
+ with_cli6_lease_file="${localstatedir}/state/dhclient6.leases"
+ fi
+ elif [ -d "${localstatedir}/lib" ] ; then
+ if [ -d "${localstatedir}/lib/dhcp" ] ; then
+ with_cli6_lease_file="${localstatedir}/lib/dhcp/dhclient6.leases"
+ else
+ with_cli6_lease_file="${localstatedir}/lib/dhclient6.leases"
+ fi
+ elif [ -d "${localstatedir}/etc" ] ; then
+ with_cli6_lease_file="${localstatedir}/etc/dhclient6.leases"
+ else
+ with_cli6_lease_file="/etc/dhclient6.leases"
+ fi
+fi
+echo "$with_cli6_lease_file"
+
+
+# Check whether --with-srv-pid-file was given.
+if test "${with_srv_pid_file+set}" = set; then
+ withval=$with_srv_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCPD_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check whether --with-srv6-pid-file was given.
+if test "${with_srv6_pid_file+set}" = set; then
+ withval=$with_srv6_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCPD6_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check whether --with-cli-pid-file was given.
+if test "${with_cli_pid_file+set}" = set; then
+ withval=$with_cli_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCLIENT_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check whether --with-cli6-pid-file was given.
+if test "${with_cli6_pid_file+set}" = set; then
+ withval=$with_cli6_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCLIENT6_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check whether --with-relay-pid-file was given.
+if test "${with_relay_pid_file+set}" = set; then
+ withval=$with_relay_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCRELAY_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check whether --with-relay6-pid-file was given.
+if test "${with_relay6_pid_file+set}" = set; then
+ withval=$with_relay6_pid_file;
+cat >>confdefs.h <<_ACEOF
+#define _PATH_DHCRELAY6_PID "$withval"
+_ACEOF
+
+fi
+
+
+# Check basic types.
+
+ { echo "$as_me:$LINENO: checking for int8_t" >&5
+echo $ECHO_N "checking for int8_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_int8_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_int8_t=no
+ for ac_type in 'int8_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (8 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int8_t) ac_cv_c_int8_t=yes ;;
+ *) ac_cv_c_int8_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int8_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_int8_t" >&5
+echo "${ECHO_T}$ac_cv_c_int8_t" >&6; }
+ case $ac_cv_c_int8_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int8_t $ac_cv_c_int8_t
+_ACEOF
+;;
+ esac
+
+
+ { echo "$as_me:$LINENO: checking for int16_t" >&5
+echo $ECHO_N "checking for int16_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_int16_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_int16_t=no
+ for ac_type in 'int16_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (16 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int16_t) ac_cv_c_int16_t=yes ;;
+ *) ac_cv_c_int16_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int16_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_int16_t" >&5
+echo "${ECHO_T}$ac_cv_c_int16_t" >&6; }
+ case $ac_cv_c_int16_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int16_t $ac_cv_c_int16_t
+_ACEOF
+;;
+ esac
+
+
+ { echo "$as_me:$LINENO: checking for int32_t" >&5
+echo $ECHO_N "checking for int32_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_int32_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_int32_t=no
+ for ac_type in 'int32_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (32 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int32_t) ac_cv_c_int32_t=yes ;;
+ *) ac_cv_c_int32_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int32_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_int32_t" >&5
+echo "${ECHO_T}$ac_cv_c_int32_t" >&6; }
+ case $ac_cv_c_int32_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int32_t $ac_cv_c_int32_t
+_ACEOF
+;;
+ esac
+
+
+ { echo "$as_me:$LINENO: checking for int64_t" >&5
+echo $ECHO_N "checking for int64_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_int64_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_int64_t=no
+ for ac_type in 'int64_t' 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 1)
+ < ($ac_type) (((($ac_type) 1 << (64 - 2)) - 1) * 2 + 2))];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ case $ac_type in
+ int64_t) ac_cv_c_int64_t=yes ;;
+ *) ac_cv_c_int64_t=$ac_type ;;
+esac
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_int64_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_int64_t" >&5
+echo "${ECHO_T}$ac_cv_c_int64_t" >&6; }
+ case $ac_cv_c_int64_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int64_t $ac_cv_c_int64_t
+_ACEOF
+;;
+ esac
+
+
+# Some systems need the u_intX_t types defined across.
+{ echo "$as_me:$LINENO: checking for u_int8_t" >&5
+echo $ECHO_N "checking for u_int8_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_u_int8_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef u_int8_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_u_int8_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_u_int8_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_u_int8_t" >&5
+echo "${ECHO_T}$ac_cv_type_u_int8_t" >&6; }
+if test $ac_cv_type_u_int8_t = yes; then
+ :
+else
+
+
+ { echo "$as_me:$LINENO: checking for uint8_t" >&5
+echo $ECHO_N "checking for uint8_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_uint8_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_uint8_t=no
+ for ac_type in 'uint8_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (8 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint8_t) ac_cv_c_uint8_t=yes ;;
+ *) ac_cv_c_uint8_t=$ac_type ;;
+esac
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint8_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_uint8_t" >&5
+echo "${ECHO_T}$ac_cv_c_uint8_t" >&6; }
+ case $ac_cv_c_uint8_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT8_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint8_t $ac_cv_c_uint8_t
+_ACEOF
+;;
+ esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define u_int8_t uint8_t
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for u_int16_t" >&5
+echo $ECHO_N "checking for u_int16_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_u_int16_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef u_int16_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_u_int16_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_u_int16_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_u_int16_t" >&5
+echo "${ECHO_T}$ac_cv_type_u_int16_t" >&6; }
+if test $ac_cv_type_u_int16_t = yes; then
+ :
+else
+
+
+ { echo "$as_me:$LINENO: checking for uint16_t" >&5
+echo $ECHO_N "checking for uint16_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_uint16_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_uint16_t=no
+ for ac_type in 'uint16_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (16 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint16_t) ac_cv_c_uint16_t=yes ;;
+ *) ac_cv_c_uint16_t=$ac_type ;;
+esac
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint16_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_uint16_t" >&5
+echo "${ECHO_T}$ac_cv_c_uint16_t" >&6; }
+ case $ac_cv_c_uint16_t in #(
+ no|yes) ;; #(
+ *)
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint16_t $ac_cv_c_uint16_t
+_ACEOF
+;;
+ esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define u_int16_t uint16_t
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for u_int32_t" >&5
+echo $ECHO_N "checking for u_int32_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_u_int32_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef u_int32_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_u_int32_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_u_int32_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_u_int32_t" >&5
+echo "${ECHO_T}$ac_cv_type_u_int32_t" >&6; }
+if test $ac_cv_type_u_int32_t = yes; then
+ :
+else
+
+
+ { echo "$as_me:$LINENO: checking for uint32_t" >&5
+echo $ECHO_N "checking for uint32_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_uint32_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_uint32_t=no
+ for ac_type in 'uint32_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (32 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint32_t) ac_cv_c_uint32_t=yes ;;
+ *) ac_cv_c_uint32_t=$ac_type ;;
+esac
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint32_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_uint32_t" >&5
+echo "${ECHO_T}$ac_cv_c_uint32_t" >&6; }
+ case $ac_cv_c_uint32_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT32_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint32_t $ac_cv_c_uint32_t
+_ACEOF
+;;
+ esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define u_int32_t uint32_t
+_ACEOF
+
+
+fi
+
+{ echo "$as_me:$LINENO: checking for u_int64_t" >&5
+echo $ECHO_N "checking for u_int64_t... $ECHO_C" >&6; }
+if test "${ac_cv_type_u_int64_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+typedef u_int64_t ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_u_int64_t=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_u_int64_t=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_u_int64_t" >&5
+echo "${ECHO_T}$ac_cv_type_u_int64_t" >&6; }
+if test $ac_cv_type_u_int64_t = yes; then
+ :
+else
+
+
+ { echo "$as_me:$LINENO: checking for uint64_t" >&5
+echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6; }
+if test "${ac_cv_c_uint64_t+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_c_uint64_t=no
+ for ac_type in 'uint64_t' 'unsigned int' 'unsigned long int' \
+ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) -1 >> (64 - 1) == 1)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ case $ac_type in
+ uint64_t) ac_cv_c_uint64_t=yes ;;
+ *) ac_cv_c_uint64_t=$ac_type ;;
+esac
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_uint64_t" != no && break
+ done
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_uint64_t" >&5
+echo "${ECHO_T}$ac_cv_c_uint64_t" >&6; }
+ case $ac_cv_c_uint64_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<\_ACEOF
+#define _UINT64_T 1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define uint64_t $ac_cv_c_uint64_t
+_ACEOF
+;;
+ esac
+
+
+cat >>confdefs.h <<\_ACEOF
+#define u_int64_t uint64_t
+_ACEOF
+
+
+fi
+
+
+# see if ifaddrs.h is available
+
+for ac_header in ifaddrs.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# figure out what IPv4 interface code to use
+
+for ac_header in linux/types.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+ # needed for linux/filter.h on old systems
+
+{ echo "$as_me:$LINENO: checking for linux/filter.h" >&5
+echo $ECHO_N "checking for linux/filter.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_linux_filter_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+
+#include <linux/filter.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_header_linux_filter_h=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_header_linux_filter_h=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_linux_filter_h" >&5
+echo "${ECHO_T}$ac_cv_header_linux_filter_h" >&6; }
+if test $ac_cv_header_linux_filter_h = yes; then
+ DO_LPF=1
+fi
+
+
+if test -n "$DO_LPF"
+then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LPF 1
+_ACEOF
+
+else
+ if test "${ac_cv_header_sys_dlpi_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for sys/dlpi.h" >&5
+echo $ECHO_N "checking for sys/dlpi.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_dlpi_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_dlpi_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_dlpi_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking sys/dlpi.h usability" >&5
+echo $ECHO_N "checking sys/dlpi.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/dlpi.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking sys/dlpi.h presence" >&5
+echo $ECHO_N "checking sys/dlpi.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/dlpi.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/dlpi.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sys/dlpi.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/dlpi.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/dlpi.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sys/dlpi.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sys/dlpi.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/dlpi.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/dlpi.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sys/dlpi.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for sys/dlpi.h" >&5
+echo $ECHO_N "checking for sys/dlpi.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_sys_dlpi_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sys_dlpi_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_sys_dlpi_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_dlpi_h" >&6; }
+
+fi
+if test $ac_cv_header_sys_dlpi_h = yes; then
+ DO_DLPI=1
+fi
+
+
+ if test -n "$DO_DLPI"
+ then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DLPI 1
+_ACEOF
+
+ else
+ if test "${ac_cv_header_net_bpf_h+set}" = set; then
+ { echo "$as_me:$LINENO: checking for net/bpf.h" >&5
+echo $ECHO_N "checking for net/bpf.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_net_bpf_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_net_bpf_h" >&5
+echo "${ECHO_T}$ac_cv_header_net_bpf_h" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking net/bpf.h usability" >&5
+echo $ECHO_N "checking net/bpf.h usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <net/bpf.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking net/bpf.h presence" >&5
+echo $ECHO_N "checking net/bpf.h presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <net/bpf.h>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: net/bpf.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: net/bpf.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: net/bpf.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: net/bpf.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: net/bpf.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: net/bpf.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: net/bpf.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: net/bpf.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: net/bpf.h: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for net/bpf.h" >&5
+echo $ECHO_N "checking for net/bpf.h... $ECHO_C" >&6; }
+if test "${ac_cv_header_net_bpf_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_net_bpf_h=$ac_header_preproc
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_header_net_bpf_h" >&5
+echo "${ECHO_T}$ac_cv_header_net_bpf_h" >&6; }
+
+fi
+if test $ac_cv_header_net_bpf_h = yes; then
+ DO_BPF=1
+fi
+
+
+ if test -n "$DO_BPF"
+ then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_BPF ""
+_ACEOF
+
+ fi
+ fi
+fi
+
+# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
+# use the same structures. We like to use 'struct lifconf' and 'struct
+# lifreq', but we'll use these other structures if they're present. HPUX
+# does not define 'struct lifnum', but does use SIOCGLIFNUM - they use an
+# int value.
+#
+{ echo "$as_me:$LINENO: checking for struct lifnum" >&5
+echo $ECHO_N "checking for struct lifnum... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <net/if.h>
+
+int
+main ()
+{
+ struct lifnum a;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define ISC_PLATFORM_HAVELIFNUM 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for struct if_laddrconf" >&5
+echo $ECHO_N "checking for struct if_laddrconf... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+ #include <sys/types.h>
+ #include <net/if6.h>
+
+int
+main ()
+{
+ struct if_laddrconf a;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define ISC_PLATFORM_HAVEIF_LADDRCONF 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+{ echo "$as_me:$LINENO: checking for struct if_laddrreq" >&5
+echo $ECHO_N "checking for struct if_laddrreq... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <net/if6.h>
+
+int
+main ()
+{
+ struct if_laddrreq a;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define ISC_PLATFORM_HAVEIF_LADDRREQ 1
+_ACEOF
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+
+# Look for optional headers.
+
+
+
+
+for ac_header in sys/socket.h net/if_dl.h net/if6.h regex.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ { echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_compiler=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+
+rm -f conftest.err conftest.$ac_ext
+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ ( cat <<\_ASBOX
+## --------------------------------- ##
+## Report this to dhcp-users@isc.org ##
+## --------------------------------- ##
+_ASBOX
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+{ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+ac_res=`eval echo '${'$as_ac_Header'}'`
+ { echo "$as_me:$LINENO: result: $ac_res" >&5
+echo "${ECHO_T}$ac_res" >&6; }
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+# Solaris needs some libraries for functions
+{ echo "$as_me:$LINENO: checking for library containing socket" >&5
+echo $ECHO_N "checking for library containing socket... $ECHO_C" >&6; }
+if test "${ac_cv_search_socket+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_socket=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_socket+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_socket+set}" = set; then
+ :
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_socket" >&5
+echo "${ECHO_T}$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+{ echo "$as_me:$LINENO: checking for library containing inet_ntoa" >&5
+echo $ECHO_N "checking for library containing inet_ntoa... $ECHO_C" >&6; }
+if test "${ac_cv_search_inet_ntoa+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_ntoa ();
+int
+main ()
+{
+return inet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_inet_ntoa=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_inet_ntoa+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_inet_ntoa+set}" = set; then
+ :
+else
+ ac_cv_search_inet_ntoa=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_inet_ntoa" >&5
+echo "${ECHO_T}$ac_cv_search_inet_ntoa" >&6; }
+ac_res=$ac_cv_search_inet_ntoa
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ echo "$as_me:$LINENO: checking for library containing inet_aton" >&5
+echo $ECHO_N "checking for library containing inet_aton... $ECHO_C" >&6; }
+if test "${ac_cv_search_inet_aton+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_aton ();
+int
+main ()
+{
+return inet_aton ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_inet_aton=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_inet_aton+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_inet_aton+set}" = set; then
+ :
+else
+ ac_cv_search_inet_aton=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_inet_aton" >&5
+echo "${ECHO_T}$ac_cv_search_inet_aton" >&6; }
+ac_res=$ac_cv_search_inet_aton
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+cat >>confdefs.h <<\_ACEOF
+#define NEED_INET_ATON 1
+_ACEOF
+
+fi
+
+
+# Check for a standalone regex library.
+{ echo "$as_me:$LINENO: checking for library containing regcomp" >&5
+echo $ECHO_N "checking for library containing regcomp... $ECHO_C" >&6; }
+if test "${ac_cv_search_regcomp+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regcomp ();
+int
+main ()
+{
+return regcomp ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' regex; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_regcomp=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_regcomp+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_regcomp+set}" = set; then
+ :
+else
+ ac_cv_search_regcomp=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_regcomp" >&5
+echo "${ECHO_T}$ac_cv_search_regcomp" >&6; }
+ac_res=$ac_cv_search_regcomp
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+# For HP/UX we need -lipv6 for if_nametoindex, perhaps others.
+{ echo "$as_me:$LINENO: checking for library containing if_nametoindex" >&5
+echo $ECHO_N "checking for library containing if_nametoindex... $ECHO_C" >&6; }
+if test "${ac_cv_search_if_nametoindex+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char if_nametoindex ();
+int
+main ()
+{
+return if_nametoindex ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ipv6; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_if_nametoindex=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_if_nametoindex+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_if_nametoindex+set}" = set; then
+ :
+else
+ ac_cv_search_if_nametoindex=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_if_nametoindex" >&5
+echo "${ECHO_T}$ac_cv_search_if_nametoindex" >&6; }
+ac_res=$ac_cv_search_if_nametoindex
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+# check for /dev/random (declares HAVE_DEV_RANDOM)
+{ echo "$as_me:$LINENO: checking for /dev/random" >&5
+echo $ECHO_N "checking for /dev/random... $ECHO_C" >&6; }
+if test "${ac_cv_file__dev_random+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ test "$cross_compiling" = yes &&
+ { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
+echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
+ { (exit 1); exit 1; }; }
+if test -r "/dev/random"; then
+ ac_cv_file__dev_random=yes
+else
+ ac_cv_file__dev_random=no
+fi
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_file__dev_random" >&5
+echo "${ECHO_T}$ac_cv_file__dev_random" >&6; }
+if test $ac_cv_file__dev_random = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_DEV_RANDOM 1
+_ACEOF
+
+fi
+
+
+# see if there is a "sa_len" field in our interface information structure
+{ echo "$as_me:$LINENO: checking for struct sockaddr.sa_len" >&5
+echo $ECHO_N "checking for struct sockaddr.sa_len... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct_sockaddr_sa_len+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/socket.h>
+
+int
+main ()
+{
+static struct sockaddr ac_aggr;
+if (ac_aggr.sa_len)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_sockaddr_sa_len=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/socket.h>
+
+int
+main ()
+{
+static struct sockaddr ac_aggr;
+if (sizeof ac_aggr.sa_len)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_sockaddr_sa_len=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_sockaddr_sa_len=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_sockaddr_sa_len" >&5
+echo "${ECHO_T}$ac_cv_member_struct_sockaddr_sa_len" >&6; }
+if test $ac_cv_member_struct_sockaddr_sa_len = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_SA_LEN
+_ACEOF
+
+fi
+
+
+# figure out pointer size
+{ echo "$as_me:$LINENO: checking for struct iaddr *" >&5
+echo $ECHO_N "checking for struct iaddr *... $ECHO_C" >&6; }
+if test "${ac_cv_type_struct_iaddr_p+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+typedef struct iaddr * ac__type_new_;
+int
+main ()
+{
+if ((ac__type_new_ *) 0)
+ return 0;
+if (sizeof (ac__type_new_))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_type_struct_iaddr_p=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_type_struct_iaddr_p=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_type_struct_iaddr_p" >&5
+echo "${ECHO_T}$ac_cv_type_struct_iaddr_p" >&6; }
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ echo "$as_me:$LINENO: checking size of struct iaddr *" >&5
+echo $ECHO_N "checking size of struct iaddr *... $ECHO_C" >&6; }
+if test "${ac_cv_sizeof_struct_iaddr_p+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=$ac_mid; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo=`expr $ac_mid + 1`
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ ac_mid=`expr 2 '*' $ac_mid + 1`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) < 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) >= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_lo=$ac_mid; break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_hi=`expr '(' $ac_mid ')' - 1`
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ ac_mid=`expr 2 '*' $ac_mid`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo= ac_hi=
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo`
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+int
+main ()
+{
+static int test_array [1 - 2 * !(((long int) (sizeof (ac__type_sizeof_))) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_hi=$ac_mid
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_lo=`expr '(' $ac_mid ')' + 1`
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in
+?*) ac_cv_sizeof_struct_iaddr_p=$ac_lo;;
+'') if test "$ac_cv_type_struct_iaddr_p" = yes; then
+ { { echo "$as_me:$LINENO: error: cannot compute sizeof (struct iaddr *)
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (struct iaddr *)
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+ else
+ ac_cv_sizeof_struct_iaddr_p=0
+ fi ;;
+esac
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include "includes/inet.h"
+#include <stdio.h>
+
+
+ typedef struct iaddr * ac__type_sizeof_;
+static long int longval () { return (long int) (sizeof (ac__type_sizeof_)); }
+static unsigned long int ulongval () { return (long int) (sizeof (ac__type_sizeof_)); }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (((long int) (sizeof (ac__type_sizeof_))) < 0)
+ {
+ long int i = longval ();
+ if (i != ((long int) (sizeof (ac__type_sizeof_))))
+ return 1;
+ fprintf (f, "%ld\n", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ((long int) (sizeof (ac__type_sizeof_))))
+ return 1;
+ fprintf (f, "%lu\n", i);
+ }
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_sizeof_struct_iaddr_p=`cat conftest.val`
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+if test "$ac_cv_type_struct_iaddr_p" = yes; then
+ { { echo "$as_me:$LINENO: error: cannot compute sizeof (struct iaddr *)
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute sizeof (struct iaddr *)
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+ else
+ ac_cv_sizeof_struct_iaddr_p=0
+ fi
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f conftest.val
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_sizeof_struct_iaddr_p" >&5
+echo "${ECHO_T}$ac_cv_sizeof_struct_iaddr_p" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_STRUCT_IADDR_P $ac_cv_sizeof_struct_iaddr_p
+_ACEOF
+
+
+
+# Solaris does not have the msg_control or msg_controlen members in
+# in the msghdr structure unless you define:
+#
+# _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED, and __EXTENSIONS__
+#
+# See the "standards" man page for details.
+#
+# We check for the msg_control member, and if it is not found, we check
+# again with the appropriate defines added to the CFLAGS. (In order to
+# do this we have to remove the check from the cache, which is what the
+# "unset" is for.)
+{ echo "$as_me:$LINENO: checking for struct msghdr.msg_control" >&5
+echo $ECHO_N "checking for struct msghdr.msg_control... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct_msghdr_msg_control+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+int
+main ()
+{
+static struct msghdr ac_aggr;
+if (ac_aggr.msg_control)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_msghdr_msg_control=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+int
+main ()
+{
+static struct msghdr ac_aggr;
+if (sizeof ac_aggr.msg_control)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_msghdr_msg_control=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_msghdr_msg_control=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_msghdr_msg_control" >&5
+echo "${ECHO_T}$ac_cv_member_struct_msghdr_msg_control" >&6; }
+if test $ac_cv_member_struct_msghdr_msg_control = yes; then
+ :
+else
+ CFLAGS="$CFLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1"
+ CFLAGS="$CFLAGS -D__EXTENSIONS__"
+ unset ac_cv_member_struct_msghdr_msg_control
+ { echo "$as_me:$LINENO: checking for struct msghdr.msg_control" >&5
+echo $ECHO_N "checking for struct msghdr.msg_control... $ECHO_C" >&6; }
+if test "${ac_cv_member_struct_msghdr_msg_control+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+int
+main ()
+{
+static struct msghdr ac_aggr;
+if (ac_aggr.msg_control)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_msghdr_msg_control=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+
+int
+main ()
+{
+static struct msghdr ac_aggr;
+if (sizeof ac_aggr.msg_control)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_member_struct_msghdr_msg_control=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_member_struct_msghdr_msg_control=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_msghdr_msg_control" >&5
+echo "${ECHO_T}$ac_cv_member_struct_msghdr_msg_control" >&6; }
+if test $ac_cv_member_struct_msghdr_msg_control = yes; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: Missing msg_control member in
+ msg_control structure." >&5
+echo "$as_me: error: Missing msg_control member in
+ msg_control structure." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+fi
+
+
+libbind=
+
+# Check whether --with-libbind was given.
+if test "${with_libbind+set}" = set; then
+ withval=$with_libbind; use_libbind="$withval"
+else
+ use_libbind="no"
+fi
+
+case "$use_libbind" in
+yes)
+ libbind="\${top_srcdir}/bind"
+ ;;
+no)
+ libbind="\${top_srcdir}/bind"
+ ;;
+*)
+ libbind="$use_libbind"
+ ;;
+esac
+
+# OpenLDAP support.
+
+# Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then
+ withval=$with_ldap; ldap=$withval
+else
+ ldap=no
+fi
+
+
+# OpenLDAP with SSL support.
+
+# Check whether --with-ldapcrypto was given.
+if test "${with_ldapcrypto+set}" = set; then
+ withval=$with_ldapcrypto; ldapcrypto=$withval
+else
+ ldapcrypto=no
+fi
+
+
+# OpenLDAP support is disabled by default, if enabled then SSL support is an
+# extra optional that is also disabled by default. Enabling LDAP SSL support
+# implies enabling LDAP support.
+if test x$ldap = xyes || test x$ldapcrypto = xyes ; then
+ { echo "$as_me:$LINENO: checking for library containing ldap_initialize" >&5
+echo $ECHO_N "checking for library containing ldap_initialize... $ECHO_C" >&6; }
+if test "${ac_cv_search_ldap_initialize+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_initialize ();
+int
+main ()
+{
+return ldap_initialize ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_ldap_initialize=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_ldap_initialize+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_ldap_initialize+set}" = set; then
+ :
+else
+ ac_cv_search_ldap_initialize=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_ldap_initialize" >&5
+echo "${ECHO_T}$ac_cv_search_ldap_initialize" >&6; }
+ac_res=$ac_cv_search_ldap_initialize
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+ { { echo "$as_me:$LINENO: error: *** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?
+See \`config.log' for more details." >&5
+echo "$as_me: error: *** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ { echo "$as_me:$LINENO: checking for library containing ber_pvt_opt_on" >&5
+echo $ECHO_N "checking for library containing ber_pvt_opt_on... $ECHO_C" >&6; }
+if test "${ac_cv_search_ber_pvt_opt_on+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_pvt_opt_on ();
+int
+main ()
+{
+return ber_pvt_opt_on ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' lber; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext &&
+ $as_test_x conftest$ac_exeext; then
+ ac_cv_search_ber_pvt_opt_on=$ac_res
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext
+ if test "${ac_cv_search_ber_pvt_opt_on+set}" = set; then
+ break
+fi
+done
+if test "${ac_cv_search_ber_pvt_opt_on+set}" = set; then
+ :
+else
+ ac_cv_search_ber_pvt_opt_on=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_search_ber_pvt_opt_on" >&5
+echo "${ECHO_T}$ac_cv_search_ber_pvt_opt_on" >&6; }
+ac_res=$ac_cv_search_ber_pvt_opt_on
+if test "$ac_res" != no; then
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+ { { echo "$as_me:$LINENO: error: *** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?
+See \`config.log' for more details." >&5
+echo "$as_me: error: *** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+ if test x$ldapcrypto = xyes ; then
+ LDAP_CFLAGS="-DLDAP_CONFIGURATION -DLDAP_USE_SSL"
+
+ else
+ LDAP_CFLAGS="-DLDAP_CONFIGURATION"
+
+ fi
+fi
+
+# Append selected warning levels to CFLAGS before substitution (but after
+# AC_TRY_COMPILE & etc).
+CFLAGS="$CFLAGS $STD_CWARNINGS"
+
+# Try to add the bind include directory
+CFLAGS="$CFLAGS -I$libbind/include"
+
+
+ { echo "$as_me:$LINENO: checking for flexible array members" >&5
+echo $ECHO_N "checking for flexible array members... $ECHO_C" >&6; }
+if test "${ac_cv_c_flexmember+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+ #include <stdio.h>
+ #include <stddef.h>
+ struct s { int n; double d[]; };
+int
+main ()
+{
+int m = getchar ();
+ struct s *p = malloc (offsetof (struct s, d)
+ + m * sizeof (double));
+ p->d[0] = 0.0;
+ return p->d != (double *) NULL;
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+ (eval "$ac_compile") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then
+ ac_cv_c_flexmember=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_c_flexmember=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_c_flexmember" >&5
+echo "${ECHO_T}$ac_cv_c_flexmember" >&6; }
+ if test $ac_cv_c_flexmember = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define FLEXIBLE_ARRAY_MEMBER
+_ACEOF
+
+ else
+ cat >>confdefs.h <<\_ACEOF
+#define FLEXIBLE_ARRAY_MEMBER 1
+_ACEOF
+
+ fi
+
+
+ac_config_files="$ac_config_files Makefile client/Makefile common/Makefile common/tests/Makefile dhcpctl/Makefile dst/Makefile includes/Makefile omapip/Makefile relay/Makefile server/Makefile tests/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ *) $as_unset $ac_var ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { echo "$as_me:$LINENO: updating cache $cache_file" >&5
+echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
+echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in
+ *posix*) set -o posix ;;
+esac
+
+fi
+
+
+
+
+# PATH needs CR
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+as_nl='
+'
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ { (exit 1); exit 1; }
+fi
+
+# Work around bugs in pre-3.0 UWIN ksh.
+for as_var in ENV MAIL MAILPATH
+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# CDPATH.
+$as_unset CDPATH
+
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line after each line using $LINENO; the second 'sed'
+ # does the real work. The second script uses 'N' to pair each
+ # line-number line with the line containing $LINENO, and appends
+ # trailing '-' during substitution so that $LINENO is not a special
+ # case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # scripts with optimization help from Paolo Bonzini. Blame Lee
+ # E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in
+-n*)
+ case `echo 'x\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ *) ECHO_C='\c';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir
+fi
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+
+# Save the log message, to keep $[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by DHCP $as_me 4.2.2, which was
+generated by GNU Autoconf 2.61. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+DHCP config.status 4.2.2
+configured by $0, generated by GNU Autoconf 2.61,
+ with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2006 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ echo "$ac_cs_version"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ { echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ CONFIG_SHELL=$SHELL
+ export CONFIG_SHELL
+ exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "includes/config.h") CONFIG_HEADERS="$CONFIG_HEADERS includes/config.h" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "client/Makefile") CONFIG_FILES="$CONFIG_FILES client/Makefile" ;;
+ "common/Makefile") CONFIG_FILES="$CONFIG_FILES common/Makefile" ;;
+ "common/tests/Makefile") CONFIG_FILES="$CONFIG_FILES common/tests/Makefile" ;;
+ "dhcpctl/Makefile") CONFIG_FILES="$CONFIG_FILES dhcpctl/Makefile" ;;
+ "dst/Makefile") CONFIG_FILES="$CONFIG_FILES dst/Makefile" ;;
+ "includes/Makefile") CONFIG_FILES="$CONFIG_FILES includes/Makefile" ;;
+ "omapip/Makefile") CONFIG_FILES="$CONFIG_FILES omapip/Makefile" ;;
+ "relay/Makefile") CONFIG_FILES="$CONFIG_FILES relay/Makefile" ;;
+ "server/Makefile") CONFIG_FILES="$CONFIG_FILES server/Makefile" ;;
+ "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+#
+# Set up the sed scripts for CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+
+_ACEOF
+
+
+
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ cat >conf$$subs.sed <<_ACEOF
+SHELL!$SHELL$ac_delim
+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
+PACKAGE_NAME!$PACKAGE_NAME$ac_delim
+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
+PACKAGE_STRING!$PACKAGE_STRING$ac_delim
+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
+exec_prefix!$exec_prefix$ac_delim
+prefix!$prefix$ac_delim
+program_transform_name!$program_transform_name$ac_delim
+bindir!$bindir$ac_delim
+sbindir!$sbindir$ac_delim
+libexecdir!$libexecdir$ac_delim
+datarootdir!$datarootdir$ac_delim
+datadir!$datadir$ac_delim
+sysconfdir!$sysconfdir$ac_delim
+sharedstatedir!$sharedstatedir$ac_delim
+localstatedir!$localstatedir$ac_delim
+includedir!$includedir$ac_delim
+oldincludedir!$oldincludedir$ac_delim
+docdir!$docdir$ac_delim
+infodir!$infodir$ac_delim
+htmldir!$htmldir$ac_delim
+dvidir!$dvidir$ac_delim
+pdfdir!$pdfdir$ac_delim
+psdir!$psdir$ac_delim
+libdir!$libdir$ac_delim
+localedir!$localedir$ac_delim
+mandir!$mandir$ac_delim
+DEFS!$DEFS$ac_delim
+ECHO_C!$ECHO_C$ac_delim
+ECHO_N!$ECHO_N$ac_delim
+ECHO_T!$ECHO_T$ac_delim
+LIBS!$LIBS$ac_delim
+build_alias!$build_alias$ac_delim
+host_alias!$host_alias$ac_delim
+target_alias!$target_alias$ac_delim
+INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
+INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
+INSTALL_DATA!$INSTALL_DATA$ac_delim
+am__isrc!$am__isrc$ac_delim
+CYGPATH_W!$CYGPATH_W$ac_delim
+PACKAGE!$PACKAGE$ac_delim
+VERSION!$VERSION$ac_delim
+ACLOCAL!$ACLOCAL$ac_delim
+AUTOCONF!$AUTOCONF$ac_delim
+AUTOMAKE!$AUTOMAKE$ac_delim
+AUTOHEADER!$AUTOHEADER$ac_delim
+MAKEINFO!$MAKEINFO$ac_delim
+install_sh!$install_sh$ac_delim
+STRIP!$STRIP$ac_delim
+INSTALL_STRIP_PROGRAM!$INSTALL_STRIP_PROGRAM$ac_delim
+mkdir_p!$mkdir_p$ac_delim
+AWK!$AWK$ac_delim
+SET_MAKE!$SET_MAKE$ac_delim
+am__leading_dot!$am__leading_dot$ac_delim
+AMTAR!$AMTAR$ac_delim
+am__tar!$am__tar$ac_delim
+am__untar!$am__untar$ac_delim
+CC!$CC$ac_delim
+CFLAGS!$CFLAGS$ac_delim
+LDFLAGS!$LDFLAGS$ac_delim
+CPPFLAGS!$CPPFLAGS$ac_delim
+ac_ct_CC!$ac_ct_CC$ac_delim
+EXEEXT!$EXEEXT$ac_delim
+OBJEXT!$OBJEXT$ac_delim
+DEPDIR!$DEPDIR$ac_delim
+am__include!$am__include$ac_delim
+am__quote!$am__quote$ac_delim
+AMDEP_TRUE!$AMDEP_TRUE$ac_delim
+AMDEP_FALSE!$AMDEP_FALSE$ac_delim
+AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim
+CCDEPMODE!$CCDEPMODE$ac_delim
+am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim
+am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim
+CPP!$CPP$ac_delim
+GREP!$GREP$ac_delim
+EGREP!$EGREP$ac_delim
+RANLIB!$RANLIB$ac_delim
+byte_order!$byte_order$ac_delim
+ac_prefix_program!$ac_prefix_program$ac_delim
+LDAP_CFLAGS!$LDAP_CFLAGS$ac_delim
+LIBOBJS!$LIBOBJS$ac_delim
+LTLIBOBJS!$LTLIBOBJS$ac_delim
+_ACEOF
+
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 84; then
+ break
+ elif $ac_last_try; then
+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
+ { (exit 1); exit 1; }; }
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
+if test -n "$ac_eof"; then
+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
+ ac_eof=`expr $ac_eof + 1`
+fi
+
+cat >>$CONFIG_STATUS <<_ACEOF
+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
+_ACEOF
+sed '
+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
+s/^/s,@/; s/!/@,|#_!!_#|/
+:n
+t n
+s/'"$ac_delim"'$/,g/; t
+s/$/\\/; p
+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
+' >>$CONFIG_STATUS <conf$$subs.sed
+rm -f conf$$subs.sed
+cat >>$CONFIG_STATUS <<_ACEOF
+:end
+s/|#_!!_#|//g
+CEOF$ac_eof
+_ACEOF
+
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/
+s/:*\${srcdir}:*/:/
+s/:*@srcdir@:*/:/
+s/^\([^=]*=[ ]*\):*/\1/
+s/:*$//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+fi # test -n "$CONFIG_FILES"
+
+
+for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
+echo "$as_me: error: Invalid tag $ac_tag." >&2;}
+ { (exit 1); exit 1; }; };;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
+echo "$as_me: error: cannot find input file: $ac_f" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+ ac_file_inputs="$ac_file_inputs $ac_f"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input="Generated from "`IFS=:
+ echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ fi
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin";;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir="$ac_dir"
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s&@configure_input@&$configure_input&;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out
+
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out"; rm -f "$tmp/out";;
+ *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
+ esac
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+_ACEOF
+
+# Transform confdefs.h into a sed script `conftest.defines', that
+# substitutes the proper values into config.h.in to produce config.h.
+rm -f conftest.defines conftest.tail
+# First, append a space to every undef/define line, to ease matching.
+echo 's/$/ /' >conftest.defines
+# Then, protect against being on the right side of a sed subst, or in
+# an unquoted here document, in config.status. If some macros were
+# called several times there might be several #defines for the same
+# symbol, which is useless. But do not sort them, since the last
+# AC_DEFINE must be honored.
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where
+# NAME is the cpp macro being defined, VALUE is the value it is being given.
+# PARAMS is the parameter list in the macro definition--in most cases, it's
+# just an empty string.
+ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*'
+ac_dB='\\)[ (].*,\\1define\\2'
+ac_dC=' '
+ac_dD=' ,'
+
+uniq confdefs.h |
+ sed -n '
+ t rset
+ :rset
+ s/^[ ]*#[ ]*define[ ][ ]*//
+ t ok
+ d
+ :ok
+ s/[\\&,]/\\&/g
+ s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p
+ s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p
+ ' >>conftest.defines
+
+# Remove the space that was appended to ease matching.
+# Then replace #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+# (The regexp can be short, since the line contains either #define or #undef.)
+echo 's/ $//
+s,^[ #]*u.*,/* & */,' >>conftest.defines
+
+# Break up conftest.defines:
+ac_max_sed_lines=50
+
+# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1"
+# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2"
+# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1"
+# et cetera.
+ac_in='$ac_file_inputs'
+ac_out='"$tmp/out1"'
+ac_nxt='"$tmp/out2"'
+
+while :
+do
+ # Write a here document:
+ cat >>$CONFIG_STATUS <<_ACEOF
+ # First, check the format of the line:
+ cat >"\$tmp/defines.sed" <<\\CEOF
+/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def
+/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def
+b
+:def
+_ACEOF
+ sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS
+ ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in
+ sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail
+ grep . conftest.tail >/dev/null || break
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines conftest.tail
+
+echo "ac_result=$ac_in" >>$CONFIG_STATUS
+cat >>$CONFIG_STATUS <<\_ACEOF
+ if test x"$ac_file" != x-; then
+ echo "/* $configure_input */" >"$tmp/config.h"
+ cat "$ac_result" >>"$tmp/config.h"
+ if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f $ac_file
+ mv "$tmp/config.h" $ac_file
+ fi
+ else
+ echo "/* $configure_input */"
+ cat "$ac_result"
+ fi
+ rm -f "$tmp/out12"
+# Compute $ac_file's index in $config_headers.
+_am_arg=$ac_file
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { echo "$as_me:$LINENO: executing $ac_file commands" >&5
+echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { as_dir=$dirpart/$fdir
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
+echo "$as_me: error: cannot create directory $as_dir" >&2;}
+ { (exit 1); exit 1; }; }; }
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+done
+ ;;
+
+ esac
+done # for ac_tag
+
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+sh util/bindvar.sh
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..df4bae3
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,583 @@
+AC_INIT([DHCP], [4.2.2], [dhcp-users@isc.org])
+
+# we specify "foreign" to avoid having to have the GNU mandated files,
+# like AUTHORS, COPYING, and such
+AM_INIT_AUTOMAKE([foreign])
+
+# We want to turn on warnings if we are using gcc and the user did
+# not specify CFLAGS. The autoconf check for the C compiler sets the
+# CFLAGS if gcc is used, so we will save it before we run that check.
+SAVE_CFLAGS="$CFLAGS"
+
+# Now find our C compiler.
+AC_PROG_CC
+
+# Suppress warnings about --datarootdir
+AC_DEFUN([AC_DATAROOTDIR_CHECKED])
+
+# If we have gcc, and AC_PROG_CC changed the flags, then we know the
+# user did not specify any flags. Add warnings in this case.
+if test "$GCC" = "yes"; then
+ if test "$CFLAGS" != "$SAVE_CFLAGS"; then
+ STD_CWARNINGS="$STD_CWARNINGS -Wall -Werror -fno-strict-aliasing"
+ fi
+fi
+
+# POSIX doesn't include the IPv6 Advanced Socket API and glibc hides
+# parts of the IPv6 Advanced Socket API as a result. This is stupid
+# as it breaks how the two halves (Basic and Advanced) of the IPv6
+# Socket API were designed to be used but we have to live with it.
+# Use this to define _GNU_SOURCE to pull in the IPv6 Advanced Socket API.
+AC_USE_SYSTEM_EXTENSIONS
+
+AC_PROG_RANLIB
+AC_CONFIG_HEADERS([includes/config.h])
+
+# we sometimes need to know byte order for building packets
+AC_C_BIGENDIAN(AC_SUBST(byte_order, BIG_ENDIAN),
+ AC_SUBST(byte_order, LITTLE_ENDIAN))
+AC_DEFINE_UNQUOTED([DHCP_BYTE_ORDER], [$byte_order],
+ [Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs)
+ or LITTLE_ENDIAN for LSB (Intel CPUs).])
+
+# Optional compile-time DEBUGging.
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [create a debug-only version of the software (default is no).]))
+# This is very much off by default.
+if test "$enable_debug" = "yes" ; then
+ AC_DEFINE([DEBUG], [1],
+ [Define to compile debug-only DHCP software.])
+ # Just override CFLAGS to totally to remove optimization.
+ CFLAGS="-g"
+fi
+# XXX: there are actually quite a lot more DEBUG_ features we could enable,
+# but I don't want to pollute the --help space.
+#
+#/* #define DEBUG_TOKENS */
+#/* #define DEBUG_PACKET */
+#/* #define DEBUG_EXPRESSIONS */
+#/* #define DEBUG_FIND_LEASE */
+#/* #define DEBUG_EXPRESSION_PARSE */
+#/* #define DEBUG_CLASS_MATCHING */
+#/* #define DEBUG_MEMORY_LEAKAGE */
+#/* #define DEBUG_MALLOC_POOL */
+#/* #define DEBUG_LEASE_STATE_TRANSITIONS */
+#/* #define DEBUG_RC_HISTORY */
+#/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */
+#/* #define RC_HISTORY_MAX 10240 */
+#/* #define POINTER_DEBUG */
+#/* #define DEBUG_FAILOVER_MESSAGES */
+#/* #define DEBUG_FAILOVER_TIMING */
+#/* #define DEBUG_DUMP_ALL_LEASES */
+
+# Failover optional compile-time feature.
+AC_ARG_ENABLE(failover,
+ AC_HELP_STRING([--enable-failover],
+ [enable support for failover (default is yes)]))
+# Failover is on by default, so define if it is not explicitly disabled.
+if test "$enable_failover" != "no"; then
+ AC_DEFINE([FAILOVER_PROTOCOL], [1],
+ [Define to include Failover Protocol support.])
+fi
+
+# execute() support.
+AC_ARG_ENABLE(execute,
+ AC_HELP_STRING([--enable-execute],
+ [enable support for execute() in config (default is yes)]))
+# execute() is on by default, so define if it is not explicitly disabled.
+if test "$enable_execute" != "no" ; then
+ AC_DEFINE([ENABLE_EXECUTE], [1],
+ [Define to include execute() config language support.])
+fi
+
+# Server tracing support.
+AC_ARG_ENABLE(tracing,
+ AC_HELP_STRING([--enable-tracing],
+ [enable support for server activity tracing (default is yes)]))
+# tracing is on by default, so define if it is not explicitly disabled.
+if test "$enable_tracing" != "no" ; then
+ AC_DEFINE([TRACING], [1],
+ [Define to include server activity tracing support.])
+fi
+
+# Delayed-ack feature support (experimental).
+AC_ARG_ENABLE(delayed_ack,
+ AC_HELP_STRING([--enable-delayed-ack],
+ [queues multiple DHCPACK replies (default is no)]))
+if test "$enable_delayed_ack" = "yes"; then
+ AC_DEFINE([DELAYED_ACK], [1],
+ [Define to queue multiple DHCPACK replies per fsync.])
+fi
+
+# DHCPv6 optional compile-time feature.
+AC_ARG_ENABLE(dhcpv6,
+ AC_HELP_STRING([--enable-dhcpv6],
+ [enable support for DHCPv6 (default is yes)]))
+# DHCPv6 is on by default, so define if it is not explicitly disabled.
+if test "$enable_dhcpv6" != "no"; then
+ AC_DEFINE([DHCPv6], [1],
+ [Define to 1 to include DHCPv6 support.])
+fi
+
+# PARANOIA is off by default (until we can test it with all features)
+AC_ARG_ENABLE(paranoia,
+ AC_HELP_STRING([--enable-paranoia],
+ [enable support for chroot/setuid (default is no)]))
+AC_ARG_ENABLE(early_chroot,
+ AC_HELP_STRING([--enable-early-chroot],
+ [enable chrooting prior to configuration (default is no)]))
+# If someone enables early chroot, but does not enable paranoia, do so for
+# them.
+if test "$enable_paranoia" != "yes" && \
+ test "$enable_early_chroot" = "yes" ; then
+ enable_paranoia="yes"
+fi
+
+if test "$enable_paranoia" = "yes" ; then
+ AC_DEFINE([PARANOIA], [1],
+ [Define to any value to include Ari's PARANOIA patch.])
+fi
+if test "$enable_early_chroot" = "yes" ; then
+ AC_DEFINE([EARLY_CHROOT], [1],
+ [Define to any value to chroot() prior to loading config.])
+fi
+
+AC_ARG_ENABLE(IPv4_PKTINFO,
+ AC_HELP_STRING([--enable-ipv4-pktinfo],
+ [enable use of pktinfo on IPv4 sockets (default is no)]))
+
+if test "$enable_ipv4_pktinfo" = "yes"; then
+ AC_DEFINE([USE_V4_PKTINFO], [1],
+ [Define to 1 to enable IPv4 packet info support.])
+fi
+
+AC_ARG_ENABLE(USE_SOCKETS,
+ AC_HELP_STRING([--enable-use-sockets],
+ [use the standard BSD socket API (default is no)]))
+
+if test "$enable_use_sockets" = "yes"; then
+ AC_DEFINE([USE_SOCKETS], [1],
+ [Define to 1 to use the standard BSD socket API.])
+fi
+
+###
+### Path fun. Older versions of DHCP were installed in /usr/sbin, so we
+### need to look there and potentially overwrite by default (but not if
+### the user configures an alternate value). LOCALSTATEDIR is totally
+### braindead. No one uses /usr/local/var/db/ nor /usr/local/var/run, and
+### they would be insane for suggesting it. We need to look in /var/for
+### 'db' and 'state/dhcp' for db files, and /var/run for pid files by
+### default.
+###
+AC_PREFIX_PROGRAM(dhcpd)
+
+# XXX - isn't there SOME WAY to default autoconf to /var instead of
+# /usr/local/var/no/one/has/this/please/stop/trying?
+case "$localstatedir" in
+ '${prefix}/var')
+ localstatedir=/var
+ ;;
+esac
+
+# Allow specification of alternate state files
+AC_ARG_WITH(srv-lease-file,
+ AC_HELP_STRING([--with-srv-lease-file=PATH],
+ [File for dhcpd leases
+ (default is LOCALSTATEDIR/db/dhcpd.leases)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCPD_DB], ["$withval"],
+ [File for dhcpd leases.]))
+
+echo -n "checking for dhcpd.leases location..."
+if [[ "x$with_srv_lease_file" = "x" ]] ; then
+ if [[ -d "${localstatedir}/db" ]] ; then
+ with_srv_lease_file="${localstatedir}/db/dhcpd.leases"
+ elif [[ -d "${localstatedir}/state" ]] ; then
+ if [[ -d "${localstatedir}/state/dhcp" ]] ; then
+ with_srv_lease_file="${localstatedir}/state/dhcp/dhcpd.leases"
+ else
+ with_srv_lease_file="${localstatedir}/state/dhcpd.leases"
+ fi
+ elif [[ -d "${localstatedir}/lib" ]] ; then
+ if [[ -d "${localstatedir}/lib/dhcp" ]] ; then
+ with_srv_lease_file="${localstatedir}/lib/dhcp/dhcpd.leases"
+ else
+ with_srv_lease_file="${localstatedir}/lib/dhcpd.leases"
+ fi
+ elif [[ -d "${localstatedir}/etc" ]] ; then
+ with_srv_lease_file="${localstatedir}/etc/dhcpd.leases"
+ else
+ with_srv_lease_file="/etc/dhcpd.leases"
+ fi
+fi
+echo "$with_srv_lease_file"
+
+AC_ARG_WITH(srv6-lease-file,
+ AC_HELP_STRING([--with-srv6-lease-file=PATH],
+ [File for dhcpd6 leases
+ (default is LOCALSTATEDIR/db/dhcpd6.leases)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCPD6_DB], ["$withval"],
+ [File for dhcpd6 leases.]))
+
+echo -n "checking for dhcpd6.leases location..."
+if [[ "x$with_srv6_lease_file" = "x" ]] ; then
+ if [[ -d "${localstatedir}/db" ]] ; then
+ with_srv6_lease_file="${localstatedir}/db/dhcpd6.leases"
+ elif [[ -d "${localstatedir}/state" ]] ; then
+ if [[ -d "${localstatedir}/state/dhcp" ]] ; then
+ with_srv6_lease_file="${localstatedir}/state/dhcp/dhcpd6.leases"
+ else
+ with_srv6_lease_file="${localstatedir}/state/dhcpd6.leases"
+ fi
+ elif [[ -d "${localstatedir}/lib" ]] ; then
+ if [[ -d "${localstatedir}/lib/dhcp" ]] ; then
+ with_srv6_lease_file="${localstatedir}/lib/dhcp/dhcpd6.leases"
+ else
+ with_srv6_lease_file="${localstatedir}/lib/dhcpd6.leases"
+ fi
+ elif [[ -d "${localstatedir}/etc" ]] ; then
+ with_srv6_lease_file="${localstatedir}/etc/dhcpd6.leases"
+ else
+ with_srv6_lease_file="/etc/dhcpd6.leases"
+ fi
+fi
+echo "$with_srv6_lease_file"
+
+AC_ARG_WITH(cli-lease-file,
+ AC_HELP_STRING([--with-cli-lease-file=PATH],
+ [File for dhclient leases
+ (default is LOCALSTATEDIR/db/dhclient.leases)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_DB], ["$withval"],
+ [File for dhclient leases.]))
+
+echo -n "checking for dhclient.leases location..."
+if [[ "x$with_cli_lease_file" = "x" ]] ; then
+ if [[ -d "${localstatedir}/db" ]] ; then
+ with_cli_lease_file="${localstatedir}/db/dhclient.leases"
+ elif [[ -d "${localstatedir}/state" ]] ; then
+ if [[ -d "${localstatedir}/state/dhcp" ]] ; then
+ with_cli_lease_file="${localstatedir}/state/dhcp/dhclient.leases"
+ else
+ with_cli_lease_file="${localstatedir}/state/dhclient.leases"
+ fi
+ elif [[ -d "${localstatedir}/lib" ]] ; then
+ if [[ -d "${localstatedir}/lib/dhcp" ]] ; then
+ with_cli_lease_file="${localstatedir}/lib/dhcp/dhclient.leases"
+ else
+ with_cli_lease_file="${localstatedir}/lib/dhclient.leases"
+ fi
+ elif [[ -d "${localstatedir}/etc" ]] ; then
+ with_cli_lease_file="${localstatedir}/etc/dhclient.leases"
+ else
+ with_cli_lease_file="/etc/dhclient.leases"
+ fi
+fi
+echo "$with_cli_lease_file"
+
+AC_ARG_WITH(cli6-lease-file,
+ AC_HELP_STRING([--with-cli6-lease-file=PATH],
+ [File for dhclient6 leases
+ (default is LOCALSTATEDIR/db/dhclient6.leases)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_DB], ["$withval"],
+ [File for dhclient6 leases.]))
+
+echo -n "checking for dhclient6.leases location..."
+if [[ "x$with_cli6_lease_file" = "x" ]] ; then
+ if [[ -d "${localstatedir}/db" ]] ; then
+ with_cli6_lease_file="${localstatedir}/db/dhclient6.leases"
+ elif [[ -d "${localstatedir}/state" ]] ; then
+ if [[ -d "${localstatedir}/state/dhcp" ]] ; then
+ with_cli6_lease_file="${localstatedir}/state/dhcp/dhclient6.leases"
+ else
+ with_cli6_lease_file="${localstatedir}/state/dhclient6.leases"
+ fi
+ elif [[ -d "${localstatedir}/lib" ]] ; then
+ if [[ -d "${localstatedir}/lib/dhcp" ]] ; then
+ with_cli6_lease_file="${localstatedir}/lib/dhcp/dhclient6.leases"
+ else
+ with_cli6_lease_file="${localstatedir}/lib/dhclient6.leases"
+ fi
+ elif [[ -d "${localstatedir}/etc" ]] ; then
+ with_cli6_lease_file="${localstatedir}/etc/dhclient6.leases"
+ else
+ with_cli6_lease_file="/etc/dhclient6.leases"
+ fi
+fi
+echo "$with_cli6_lease_file"
+
+AC_ARG_WITH(srv-pid-file,
+ AC_HELP_STRING([--with-srv-pid-file=PATH],
+ [File for dhcpd process information
+ (default is LOCALSTATEDIR/run/dhcpd.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCPD_PID], ["$withval"],
+ [File for dhcpd process information.]))
+AC_ARG_WITH(srv6-pid-file,
+ AC_HELP_STRING([--with-srv6-pid-file=PATH],
+ [File for dhcpd6 process information
+ (default is LOCALSTATEDIR/run/dhcpd6.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCPD6_PID], ["$withval"],
+ [File for dhcpd6 process information.]))
+AC_ARG_WITH(cli-pid-file,
+ AC_HELP_STRING([--with-cli-pid-file=PATH],
+ [File for dhclient process information
+ (default is LOCALSTATEDIR/run/dhclient.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCLIENT_PID], ["$withval"],
+ [File for dhclient process information.]))
+AC_ARG_WITH(cli6-pid-file,
+ AC_HELP_STRING([--with-cli6-pid-file=PATH],
+ [File for dhclient6 process information
+ (default is LOCALSTATEDIR/run/dhclient6.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCLIENT6_PID], ["$withval"],
+ [File for dhclient6 process information.]))
+AC_ARG_WITH(relay-pid-file,
+ AC_HELP_STRING([--with-relay-pid-file=PATH],
+ [File for dhcrelay process information
+ (default is LOCALSTATEDIR/run/dhcrelay.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCRELAY_PID], ["$withval"],
+ [File for dhcrelay process information.]))
+AC_ARG_WITH(relay6-pid-file,
+ AC_HELP_STRING([--with-relay6-pid-file=PATH],
+ [File for dhcrelay6 process information
+ (default is LOCALSTATEDIR/run/dhcrelay6.pid)]),
+ AC_DEFINE_UNQUOTED([_PATH_DHCRELAY6_PID], ["$withval"],
+ [File for dhcrelay6 process information.]))
+
+# Check basic types.
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+
+# Some systems need the u_intX_t types defined across.
+AC_CHECK_TYPE([u_int8_t], [], [
+ AC_TYPE_UINT8_T
+ AC_DEFINE(u_int8_t, [uint8_t], [Define a type for 8-bit unsigned
+ integers.])
+])
+AC_CHECK_TYPE([u_int16_t], [], [
+ AC_TYPE_UINT16_T
+ AC_DEFINE(u_int16_t, [uint16_t], [Define a type for 16-bit unsigned
+ integers.])
+])
+AC_CHECK_TYPE([u_int32_t], [], [
+ AC_TYPE_UINT32_T
+ AC_DEFINE(u_int32_t, [uint32_t], [Define a type for 32-bit unsigned
+ integers.])
+])
+AC_CHECK_TYPE([u_int64_t], [], [
+ AC_TYPE_UINT64_T
+ AC_DEFINE(u_int64_t, [uint64_t], [Define a type for 64-bit unsigned
+ integers.])
+])
+
+# see if ifaddrs.h is available
+AC_CHECK_HEADERS(ifaddrs.h)
+
+# figure out what IPv4 interface code to use
+AC_CHECK_HEADERS(linux/types.h) # needed for linux/filter.h on old systems
+
+AC_CHECK_HEADER(linux/filter.h, DO_LPF=1, ,
+[
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+])
+if test -n "$DO_LPF"
+then
+ AC_DEFINE([HAVE_LPF], [1],
+ [Define to 1 to use the Linux Packet Filter interface code.])
+else
+ AC_CHECK_HEADER(sys/dlpi.h, DO_DLPI=1)
+ if test -n "$DO_DLPI"
+ then
+ AC_DEFINE([HAVE_DLPI], [1],
+ [Define to 1 to use DLPI interface code.])
+ else
+ AC_CHECK_HEADER(net/bpf.h, DO_BPF=1)
+ if test -n "$DO_BPF"
+ then
+ AC_DEFINE([HAVE_BPF], [""],
+ [Define to 1 to use the
+ Berkeley Packet Filter interface code.])
+ fi
+ fi
+fi
+
+# SIOCGLIFCONF uses some transport structures. Trick is not all platforms
+# use the same structures. We like to use 'struct lifconf' and 'struct
+# lifreq', but we'll use these other structures if they're present. HPUX
+# does not define 'struct lifnum', but does use SIOCGLIFNUM - they use an
+# int value.
+#
+AC_MSG_CHECKING([for struct lifnum])
+AC_TRY_COMPILE(
+[ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <net/if.h>
+],
+[ struct lifnum a;
+],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE([ISC_PLATFORM_HAVELIFNUM], [1],
+ [Define to 1 if the system has 'struct lifnum'.])],
+ [AC_MSG_RESULT(no)])
+
+AC_MSG_CHECKING([for struct if_laddrconf])
+AC_TRY_COMPILE(
+[ #include <sys/types.h>
+ #include <net/if6.h>
+],
+[ struct if_laddrconf a;
+],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRCONF], [1],
+ [Define to 1 if the system has 'struct if_laddrconf'.])],
+ [AC_MSG_RESULT(no)])
+
+AC_MSG_CHECKING([for struct if_laddrreq])
+AC_TRY_LINK(
+[#include <sys/types.h>
+ #include <net/if6.h>
+],
+[ struct if_laddrreq a;
+],
+ [AC_MSG_RESULT(yes)
+ AC_DEFINE([ISC_PLATFORM_HAVEIF_LADDRREQ], [1],
+ [Define to 1 if the system has 'struct if_laddrreq'.])],
+ [AC_MSG_RESULT(no)])
+
+# Look for optional headers.
+AC_CHECK_HEADERS(sys/socket.h net/if_dl.h net/if6.h regex.h)
+
+# Solaris needs some libraries for functions
+AC_SEARCH_LIBS(socket, [socket])
+AC_SEARCH_LIBS(inet_ntoa, [nsl])
+
+AC_SEARCH_LIBS(inet_aton, [socket nsl], ,
+ AC_DEFINE([NEED_INET_ATON], [1],
+ [Define to 1 if the inet_aton() function is missing.]))
+
+# Check for a standalone regex library.
+AC_SEARCH_LIBS(regcomp, [regex])
+
+# For HP/UX we need -lipv6 for if_nametoindex, perhaps others.
+AC_SEARCH_LIBS(if_nametoindex, [ipv6])
+
+# check for /dev/random (declares HAVE_DEV_RANDOM)
+AC_CHECK_FILE(/dev/random,
+ AC_DEFINE([HAVE_DEV_RANDOM], [1],
+ [Define to 1 if you have the /dev/random file.]))
+
+# see if there is a "sa_len" field in our interface information structure
+AC_CHECK_MEMBER(struct sockaddr.sa_len,
+ AC_DEFINE([HAVE_SA_LEN], [],
+ [Define to 1 if the sockaddr structure has a length field.]),
+ ,
+ [#include <sys/socket.h>])
+
+# figure out pointer size
+AC_CHECK_SIZEOF(struct iaddr *, , [
+#include "includes/inet.h"
+#include <stdio.h>
+])
+
+# Solaris does not have the msg_control or msg_controlen members in
+# in the msghdr structure unless you define:
+#
+# _XOPEN_SOURCE, _XOPEN_SOURCE_EXTENDED, and __EXTENSIONS__
+#
+# See the "standards" man page for details.
+#
+# We check for the msg_control member, and if it is not found, we check
+# again with the appropriate defines added to the CFLAGS. (In order to
+# do this we have to remove the check from the cache, which is what the
+# "unset" is for.)
+AC_CHECK_MEMBER(struct msghdr.msg_control,,
+ [CFLAGS="$CFLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1"
+ CFLAGS="$CFLAGS -D__EXTENSIONS__"
+ unset ac_cv_member_struct_msghdr_msg_control
+ AC_CHECK_MEMBER(struct msghdr.msg_control,,
+ [AC_MSG_ERROR([Missing msg_control member in
+ msg_control structure.])],
+ [
+#include <sys/types.h>
+#include <sys/socket.h>
+ ])
+ ],
+ [
+#include <sys/types.h>
+#include <sys/socket.h>
+ ])
+
+libbind=
+AC_ARG_WITH(libbind,
+ AC_HELP_STRING([--with-libbind=PATH],
+ [bind includes and libraries are in PATH
+ (default is ./bind)]),
+ use_libbind="$withval", use_libbind="no")
+case "$use_libbind" in
+yes)
+ libbind="\${top_srcdir}/bind"
+ ;;
+no)
+ libbind="\${top_srcdir}/bind"
+ ;;
+*)
+ libbind="$use_libbind"
+ ;;
+esac
+
+# OpenLDAP support.
+AC_ARG_WITH(ldap,
+ AC_HELP_STRING([--with-ldap],
+ [enable OpenLDAP support in dhcpd (default is no)]),
+ [ldap=$withval],
+ [ldap=no])
+
+# OpenLDAP with SSL support.
+AC_ARG_WITH(ldapcrypto,
+ AC_HELP_STRING([--with-ldapcrypto],
+ [enable OpenLDAP crypto support in dhcpd (default is no)]),
+ [ldapcrypto=$withval],
+ [ldapcrypto=no])
+
+# OpenLDAP support is disabled by default, if enabled then SSL support is an
+# extra optional that is also disabled by default. Enabling LDAP SSL support
+# implies enabling LDAP support.
+if test x$ldap = xyes || test x$ldapcrypto = xyes ; then
+ AC_SEARCH_LIBS(ldap_initialize, [ldap], ,
+ AC_MSG_FAILURE([*** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?]))
+ AC_SEARCH_LIBS(ber_pvt_opt_on, [lber], ,
+ AC_MSG_FAILURE([*** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?]))
+
+ if test x$ldapcrypto = xyes ; then
+ AC_SUBST(LDAP_CFLAGS, ["-DLDAP_CONFIGURATION -DLDAP_USE_SSL"])
+ else
+ AC_SUBST(LDAP_CFLAGS, ["-DLDAP_CONFIGURATION"])
+ fi
+fi
+
+# Append selected warning levels to CFLAGS before substitution (but after
+# AC_TRY_COMPILE & etc).
+CFLAGS="$CFLAGS $STD_CWARNINGS"
+
+# Try to add the bind include directory
+CFLAGS="$CFLAGS -I$libbind/include"
+
+AC_C_FLEXIBLE_ARRAY_MEMBER
+
+AC_OUTPUT([
+ Makefile
+ client/Makefile
+ common/Makefile
+ common/tests/Makefile
+ dhcpctl/Makefile
+ dst/Makefile
+ includes/Makefile
+ omapip/Makefile
+ relay/Makefile
+ server/Makefile
+ tests/Makefile
+])
+
+sh util/bindvar.sh
diff --git a/contrib/3.0b1-lease-convert b/contrib/3.0b1-lease-convert
new file mode 100755
index 0000000..6e8157f
--- /dev/null
+++ b/contrib/3.0b1-lease-convert
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+#
+# Start Date: Mon, 26 Mar 2001 14:24:09 +0200
+# Time-stamp: <Monday, 26 March 2001 16:09:44 by brister>
+# File: leaseconvertor.pl
+# RCSId: $Id: 3.0b1-lease-convert,v 1.1 2001-04-18 19:17:34 mellon Exp $
+#
+# Description: Convert 3.0b1 to 3.0b2/final lease file format
+#
+
+require 5.004;
+
+my $rcsID =<<'EOM';
+$Id: 3.0b1-lease-convert,v 1.1 2001-04-18 19:17:34 mellon Exp $
+EOM
+
+use strict;
+
+my $revstatement =<<'EOS';
+ switch (ns-update (delete (1, 12, ddns-rev-name, null))) {
+ case 0:
+ unset ddns-rev-name;
+ break;
+ }
+EOS
+
+my $fwdstatement =<<'EOS';
+ switch (ns-update (delete (1, 1, ddns-fwd-name, leased-address))) {
+ case 0:
+ unset ddns-fwd-name;
+ break;
+ }
+EOS
+
+
+if (@ARGV && $ARGV[0] =~ m!^-!) {
+ usage();
+}
+
+
+
+# read stdin and write stdout.
+while (<>) {
+ if (! /^lease\s/) {
+ print;
+ } else {
+ my $lease = $_;
+ while (<>) {
+ $lease .= $_;
+ # in a b1 file we should only see a left curly brace on a lease
+ # lines. Seening it anywhere else means the user is probably
+ # running a b2 or later file through this.
+ # Ditto for a 'set' statement.
+ if (m!\{! || m!^\s*set\s!) {
+ warn "this doesn't look like a 3.0b1 file. Ignoring rest.\n";
+ print $lease;
+ dumpRestAndExit();
+ }
+
+ last if m!^\}\s*$!;
+ }
+
+ # $lease contains all the lines for the lease entry.
+ $lease = makeNewLease($lease);
+ print $lease;
+ }
+}
+
+
+
+sub usage {
+ my $prog = $0;
+ $prog =~ s!.*/!!;
+
+ print STDERR <<EOM;
+usage: $prog [ file ]
+
+Reads from the lease file listed on the command line (or stdin if not filename
+given) and writes to stdout. Converts a 3.0b1-style leases file to a 3.0b2
+style (for ad-hoc ddns updates).
+EOM
+
+ exit (0);
+}
+
+
+
+# takes a string that's the lines of a lease entry and converts it, if
+# necessary to a b2 style lease entry. Returns the new lease in printable form.
+sub makeNewLease {
+ my ($lease) = @_;
+
+ my $convertedfwd;
+ my $convertedrev;
+ my $newlease = "";
+ foreach (split "\n", $lease) {
+ if (m!^(\s+)(ddns-fwd-name|ddns-rev-name)\s+(\"[^\"]+\"\s*;)!) {
+ $newlease .= $1 . "set " . $2 . " = " . $3 . "\n";
+
+ # If there's one of them, then it will always be the -fwd-. There
+ # may not always be a -rev-.
+ $convertedfwd++;
+ $convertedrev++ if ($2 eq "ddns-rev-name");
+ } elsif (m!^\s*\}!) {
+ if ($convertedfwd) {
+ $newlease .= "\ton expiry or release {\n";
+ $newlease .= $revstatement if $convertedrev;
+ $newlease .= $fwdstatement;
+ $newlease .= "\t on expiry or release;\n\t}\n";
+ }
+ $newlease .= "}\n";
+ } else {
+ $newlease .= $_ . "\n";
+ }
+ }
+
+ return $newlease;
+}
+
+
+sub dumpRestAndExit {
+ while (<>) {
+ print;
+ }
+ exit (0);
+}
diff --git a/contrib/dhclient-tz-exithook.sh b/contrib/dhclient-tz-exithook.sh
new file mode 100644
index 0000000..9aa63c0
--- /dev/null
+++ b/contrib/dhclient-tz-exithook.sh
@@ -0,0 +1,179 @@
+#!/bin/bash
+#
+# dhclient-tz-exithook.sh
+# Version 1.01 elear
+#
+# Copyright (c) 2007, Cisco Systems, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - 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.
+#
+# - Neither the name of Cisco Systems, Inc. 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 COPYRIGHT HOLDERS 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
+# COPYRIGHT OWNER 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.
+#
+# the following script is used to set the timezone based on the new
+# dhcp timezone option defined currently in the IETF document
+# draft-ietf-dhc-timezone-option-04.txt.
+
+# this code is intended for use with ISC's dhclient. it is to be called
+# either as, or by, dhclient-exit-hooks
+#
+# As this is test code, in order for it to be called two changes
+# must be made to /etc/dhclient.conf. First, dhclient.conf must be
+# aware of the tzName option. The IANA has assigned tzName option
+# code 101. You may need to add this to your configuration file.
+#
+# option tzName code 101 = text;
+#
+# Next, add tzName to the list of options in the "request" statement.
+# For example:
+#
+# request subnet-mask, broadcast-address, time-offset, routers,
+# domain-name, domain-name-servers, host-name, tzName;
+#
+#
+# And of course make sure that your dhcp server is transmitting timezone
+# information for option 101. For IOS this can be done as follows:
+#
+# option 101 ascii "Europe/Berlin"
+#
+
+timefile=/etc/localtime
+oldfile=$timefile.old
+tmpfile=$timefile.$$
+
+# function to clean up just in case we are interrupted or something
+# bad happens.
+restore_file () {
+
+ if [ ! -f $timefile ]; then
+ $DEBUG mv $tmpfile $timefile
+ fi
+ $DEBUG rm $tmpfile
+ exit
+}
+
+
+#set DEBUG to "echo" to see what would happen.
+if [ x$DEBUG = x ]; then
+ DEBUG=
+fi
+
+# if something has already gone wrong we're not doing a thing.
+if [ x$exit_status != x0 ]; then
+ exit $exit_status
+fi
+
+
+# if we don't have a new timezone, then we have nothing to change, so
+# goodbye.
+if [ x$new_tzName = x ]; then
+ exit 0
+fi
+
+# if the timezone doesn't exist, goodbye.
+if [ ! -e $timefile ]; then
+ exit 0
+fi
+
+# find zoneinfo. use the first one.
+ftz=0
+for a in /usr/share/zoneinfo /usr/lib/zoneinfo /var/share/zoneinfo /var/zoneinfo; do
+ if [ -d $a -a $ftz = 0 ]; then
+ zoneinfo=$a
+ ftz=1
+ fi
+done
+
+# no zoneinfo found. goodbye.
+if [ x$zoneinfo = x ]; then
+ exit 0
+fi
+
+# timezone not found. goodbye.
+if [ ! -f $zoneinfo/$new_tzName ]; then
+ exit 0
+fi
+
+# if we're here we can actually do something useful.
+# first, link a copy of the existing timefile.
+
+$DEBUG ln $timefile $tmpfile
+
+if [ $? != 0 ]; then
+ echo "unable to create temporary file"
+ exit -1
+fi
+
+# in case of interrupt, cleanup.
+trap restore_file SIGINT SIGSEGV SIGQUIT SIGTERM
+
+# we destroy old backup files in this process. if we cannot and the
+# file exists then something went wrong.
+if [ -e $oldfile ]; then
+ $DEBUG rm $oldfile
+ if [ $? != 0 ]; then
+ echo "$0: failed to remove $oldfile"
+ rm -f $tmpfile
+ exit -1
+ fi
+fi
+
+# sensitive part happens here:
+#
+$DEBUG mv $timefile $oldfile
+
+ if [ $? != 0 ]; then
+ echo "$0: failed to move old $timefile file out of the way"
+ rm $tmpfile
+ exit -1
+ fi
+
+$DEBUG ln $zoneinfo/$new_tzName $timefile
+
+# we don't complain just yet- a hard link could fail because
+# we're on two different file systems. Go for a soft link.
+#
+
+if [ $? != 0 ]; then
+ $DEBUG ln -s $zoneinfo/$new_tzName $timefile
+fi
+
+if [ $? != 0 ]; then # failed to softlink. now we're getting nervous.
+ echo "$0: unable to establish new timezone. Attempting to revert."
+ $DEBUG ln $tmpfile $timefile
+fi
+
+
+if [ $? != 0 ]; then # we're absolutely hosed
+ echo "$0: unable to link or softlink timezone file, and unable to restore old file - giving up!"
+ exit -1
+fi
+
+$DEBUG rm $tmpfile
+
+exit $?
diff --git a/contrib/dhcp.spec b/contrib/dhcp.spec
new file mode 100644
index 0000000..0eb7201
--- /dev/null
+++ b/contrib/dhcp.spec
@@ -0,0 +1,157 @@
+Summary: The Internet Systems Consortium (ISC) DHCP server
+Name: dhcp
+%define version 3.0.2
+Version: %{version}
+Release: 2tac
+Group: System Environment/Daemons
+Source: /usr/local/src/RPM/SOURCES/dhcp-%{version}.tar.gz
+Copyright: ISC
+BuildRoot: /var/tmp/dhcp-%{version}-root
+
+%description
+Dhcp includes the DHCP server which is used for dynamically configuring
+hosts on a network. Host configuration items such as IP address, name
+servers, domain name, etc. can all be retrieved from the DHCP server by
+a DHCP client. This eases the burden of network wide configuration by
+putting all of the configuration into one place.
+
+%package client
+Summary: A DHCP client
+Group: System Environment/Configuration
+
+%description client
+Dhcp client is a DHCP client for various UNIX operating systems. It allows
+a UNIX machine to obtain it's networking parameters from a DHCP server.
+
+%package relay
+Summary: A DHCP relay
+Group: System Environment/Daemons
+
+%description relay
+Dhcp relay is a relay agent for DHCP packets. It is used on a subnet with
+DHCP clients to "relay" their requests to a subnet that has a DHCP server
+on it. Because DHCP packets can be broadcast, they will not be routed off
+of the local subnet. The DHCP relay takes care of this for the client.
+
+%package devel
+Summary: Development headers and libraries for the dhcpctl API
+Group: Development/Libraries
+
+%description devel
+Dhcp devel contains all of the libraries and headers for developing with
+the dhcpctl API.
+
+%prep
+%setup -q -n dhcp-%{version}
+# do some file editing
+egrep "VARRUN
+ETC
+VARDB" site.conf | sed -e 's/ *=/=/g' -e 's/= */=/g' > vars
+. ./vars
+cat << EOF >> includes/site.h
+#define _PATH_DHCPD_PID "$VARRUN/dhcpd.pid"
+#define _PATH_DHCPD_DB "$ETC/dhcpd.leases"
+#define _PATH_DHCPD_CONF "$ETC/dhcpd.conf"
+EOF
+./configure --with-nsupdate
+
+%build
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/local/sbin
+
+make DESTDIR="$RPM_BUILD_ROOT" install
+
+%ifos linux
+mkdir -p ${RPM_BUILD_ROOT}/etc/rc.d/{init,rc0,rc1,rc2,rc3,rc4,rc5,rc6}.d
+install -m 755 linux.init ${RPM_BUILD_ROOT}/etc/rc.d/init.d/dhcpd
+%else
+%ifos solaris
+mkdir -p ${RPM_BUILD_ROOT}/etc/init.d
+sed -e s'|@PREFIX@|%{_prefix}|g' < contrib/solaris.init > ${RPM_BUILD_ROOT}/etc/init.d/dhcpd
+chmod 755 ${RPM_BUILD_ROOT}/etc/init.d/dhcpd
+%endif
+%endif
+
+# strip binaries and libraries
+strip $RPM_BUILD_ROOT%{_prefix}/sbin/* || :
+for i in `find $RPM_BUILD_ROOT/ -type 'f' -perm '+a=x' ! -name 'lib*so*'`; do
+ file $i |grep -q "not stripped" && strip $i
+done
+
+%post
+%ifos linux
+ /sbin/chkconfig --add dhcpd
+ /etc/rc.d/init.d/dhcpd start
+%else
+ %ifos solaris
+ ln /etc/init.d/dhcpd /etc/rc2.d/S90dhcpd
+ ln /etc/init.d/dhcpd /etc/rc0.d/K30dhcpd
+ /etc/init.d/dhcpd start
+ %else
+ echo "Unknown O/S. You will need to manually configure your\nsystem"
+ echo "to start the DHCP server on system startup."
+ %endif
+%endif
+
+%preun
+if [ $1 = 0 ]; then
+ %ifos linux
+ /etc/rc.d/init.d/dhcpd stop
+ /sbin/chkconfig --del dhcpd
+ %else
+ %ifos solaris
+ /etc/init.d/dhcpd stop
+ rm /etc/rc2.d/S90dhcpd
+ rm /etc/rc0.d/K30dhcpd
+ %else
+ echo "Unknown O/S. You will need to manually clean up the DHCP"
+ echo "server startup\n in your system startup environment."
+ %endif
+ %endif
+fi
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc COPYRIGHT DOCUMENTATION ISC-LICENSE CHANGES README RELNOTES doc/*
+
+%{_prefix}/sbin/dhcpd
+%{_prefix}/man/cat1m/dhcpd.1m
+%{_prefix}/man/cat4/dhcpd.conf.4
+%{_prefix}/man/cat4/dhcpd.leases.4
+%{_prefix}/man/cat4/dhcp-options.4
+%{_prefix}/man/cat4/dhcp-eval.4
+%{_prefix}/man/cat4/dhcp-contrib.4
+%ifos linux
+%config /etc/rc.d/init.d/dhcpd
+%else
+%ifos solaris
+%config /etc/init.d/dhcpd
+%endif
+%endif
+
+%files devel
+%{_prefix}/man/cat3
+%{_prefix}/lib
+%{_prefix}/include
+
+%files client
+%{_prefix}/etc/dhclient-script
+%{_prefix}/sbin/dhclient
+%{_prefix}/man/cat1m/dhclient.1m
+%{_prefix}/man/cat1m/dhclient-script.1m
+%{_prefix}/man/cat4/dhclient.conf.4
+%{_prefix}/man/cat4/dhclient.leases.4
+
+%files relay
+%{_prefix}/sbin/dhcrelay
+%{_prefix}/man/cat1m/dhcrelay.1m
+
+%changelog
+* Fri Oct 1 1999 Brian J. Murrell <brian@interlinx.bc.ca>
+- write a spec file for dhcpd
diff --git a/contrib/ldap/README.ldap b/contrib/ldap/README.ldap
new file mode 100644
index 0000000..c413790
--- /dev/null
+++ b/contrib/ldap/README.ldap
@@ -0,0 +1,191 @@
+LDAP Support in DHCP
+Original Author: Brian Masney <masneyb@gftp.org>
+Current Maintainer: David Cantrell <dcantrell@redhat.com>
+Last updated 07-Jul-2009
+
+This document describes setting up the DHCP server to read it's configuration
+from LDAP. This work is based on the IETF document
+draft-ietf-dhc-ldap-schema-01.txt included in the doc directory. For the
+latest version of this document, please see
+http://dcantrel.fedorapeople.org/dhcp/ldap-patch/
+
+First question on most people's mind is "Why do I want to store my
+configuration in LDAP?" If you run a small DHCP server, and the configuration
+on it rarely changes, then you won't need to store your configuration in LDAP.
+But, if you have several DHCP servers, and you want an easy way to manage your
+configuration, this can be a solution.
+
+The first step will be to setup your LDAP server. I am using OpenLDAP from
+www.openldap.org. Building and installing OpenLDAP is beyond the scope of
+this document. There is plenty of documentation out there about this. Once
+you have OpenLDAP installed, you will have to edit your slapd.conf file. I
+added the following 2 lines to my configuration file:
+
+include /etc/ldap/schema/dhcp.schema
+index dhcpHWAddress eq
+index dhcpClassData eq
+
+The first line tells it to include the dhcp schema file. You will find this
+file under the contrib directory in this distribution. You will need to copy
+this file to where your other schema files are (maybe /etc/openldap/schema/).
+The second line sets up an index for the dhcpHWAddress parameter. The third
+parameter is for reading subclasses from LDAP every time a DHCP request comes
+in. Make sure you run the slapindex command and restart slapd to have these
+changes to into effect.
+
+Now that you have LDAP setup, you should be able to use gq
+(http://biot.com/gq/) to verify that the dhcp schema file is loaded into LDAP.
+Pull up gq, and click on the Schema tab. Go under objectClasses, and you
+should see at least the following object classes listed: dhcpClass, dhcpGroup,
+dhcpHost, dhcpOptions, dhcpPool, dhcpServer, dhcpService, dhcpSharedNetwork,
+dhcpSubClass, and dhcpSubnet. If you do not see these, you need to check over
+your LDAP configuration before you go any further.
+
+You should now be ready to build DHCP. If you would like to enable LDAP in
+dhcpd, you will need to perform the following steps:
+
+ * Apply the patch here to the unpacked ISC dhcp source tree.
+ * Regenerate the configure script (requires GNU autoconf and automake):
+ aclocal
+ libtoolize --copy --force
+ autoconf
+ autoheader
+ automake --foreign --add-missing --copy
+ * Run ./configure with the '--with-ldap' argument to enable OpenLDAP.
+ If you want LDAP over SSL, also use the '--with-ldapcrypto' argument.
+ * Run 'make' to build ISC dhcp.
+
+Once you have DHCP installed, you will need to setup your initial plaintext
+config file. In my /etc/dhcpd.conf file, I have:
+
+ldap-server "localhost";
+ldap-port 389;
+ldap-username "cn=DHCP User, dc=ntelos, dc=net";
+ldap-password "blah";
+ldap-base-dn "dc=ntelos, dc=net";
+ldap-method dynamic;
+ldap-debug-file "/var/log/dhcp-ldap-startup.log";
+
+If SSL has been enabled at compile time, the dhcp server trys to use TLS if
+possible, but continues without TLS if not.
+
+You can modify this behaviour using following option in /etc/dhcp/dhcpd.conf:
+
+ldap-ssl <off | ldaps | start_tls | on>
+ off: disables TLS/LDAPS.
+ ldaps: enables LDAPS -- don't forget to set ldap-port to 636.
+ start_tls: enables TLS using START_TLS command
+ on: enables LDAPS if ldap-port is set to 636 or TLS in
+ other cases.
+
+See also "man 5 ldap.conf" for description the following TLS related
+options:
+ ldap-tls-reqcert, ldap-tls-ca-file, ldap-tls-ca-dir, ldap-tls-cert
+ ldap-tls-key, ldap-tls-crlcheck, ldap-tls-ciphers, ldap-tls-randfile
+
+All of these parameters should be self explanatory except for the ldap-method.
+You can set this to static or dynamic. If you set it to static, the
+configuration is read once on startup, and LDAP isn't used anymore. But, if
+you set this to dynamic, the configuration is read once on startup, and the
+hosts that are stored in LDAP are looked up every time a DHCP request comes
+in.
+
+When the optional statement ldap-debug-file is specified, on startup the DHCP
+server will write out the configuration that it generated from LDAP. If you
+are getting errors about your LDAP configuration, this is a good place to
+start looking.
+
+The next step is to set up your LDAP tree. Here is an example config that will
+give a 10.100.0.x address to machines that have a host entry in LDAP.
+Otherwise, it will give a 10.200.0.x address to them. (NOTE: replace
+dc=ntelos, dc=net with your base dn). If you would like to convert your
+existing dhcpd.conf file to LDIF format, there is a script
+dhcpd-conf-to-ldap that will convert it for you. Type
+dhcpd-conf-to-ldap --help to see the usage information for this script.
+
+# You must specify the server's host name in LDAP that you are going to run
+# DHCP on and point it to which config tree you want to use. Whenever DHCP
+# first starts up, it will do a search for this entry to find out which
+# config to use
+dn: cn=brian.ntelos.net, dc=ntelos, dc=net
+objectClass: top
+objectClass: dhcpServer
+cn: brian.ntelos.net
+dhcpServiceDN: cn=DHCP Service Config, dc=ntelos, dc=net
+
+# Here is the config tree that brian.ntelos.net points to.
+dn: cn=DHCP Service Config, dc=ntelos, dc=net
+cn: DHCP Service Config
+objectClass: top
+objectClass: dhcpService
+dhcpPrimaryDN: dc=ntelos, dc=net
+dhcpStatements: ddns-update-style none
+dhcpStatements: default-lease-time 600
+dhcpStatements: max-lease-time 7200
+
+# Set up a shared network segment
+dn: cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net
+cn: WV
+objectClass: top
+objectClass: dhcpSharedNetwork
+
+# Set up a subnet declaration with a pool statement. Also note that we have
+# a dhcpOptions object with this entry
+dn: cn=10.100.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net
+cn: 10.100.0.0
+objectClass: top
+objectClass: dhcpSubnet
+objectClass: dhcpOptions
+dhcpOption: domain-name-servers 10.100.0.2
+dhcpOption: routers 10.100.0.1
+dhcpOption: subnet-mask 255.255.255.0
+dhcpOption: broadcast-address 10.100.0.255
+dhcpNetMask: 24
+
+# Set up a pool for this subnet. Only known hosts will get these IPs
+dn: cn=Known Pool, cn=10.100.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net
+cn: Known Pool
+objectClass: top
+objectClass: dhcpPool
+dhcpRange: 10.100.0.3 10.100.0.254
+dhcpPermitList: deny unknown-clients
+
+# Set up another subnet declaration with a pool statement
+dn: cn=10.200.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net
+cn: 10.200.0.0
+objectClass: top
+objectClass: dhcpSubnet
+objectClass: dhcpOptions
+dhcpOption: domain-name-servers 10.200.0.2
+dhcpOption: routers 10.200.0.1
+dhcpOption: subnet-mask 255.255.255.0
+dhcpOption: broadcast-address 10.200.0.255
+dhcpNetMask: 24
+
+# Set up a pool for this subnet. Only unknown hosts will get these IPs
+dn: cn=Known Pool, cn=10.200.0.0, cn=WV Test, cn=DHCP Service Config, dc=ntelos, dc=net
+cn: Known Pool
+objectClass: top
+objectClass: dhcpPool
+dhcpRange: 10.200.0.3 10.200.0.254
+dhcpPermitList: deny known clients
+
+# Set aside a group for all of our known MAC addresses
+dn: cn=Customers, cn=DHCP Service Config, dc=ntelos, dc=net
+objectClass: top
+objectClass: dhcpGroup
+cn: Customers
+
+# Host entry for my laptop
+dn: cn=brianlaptop, cn=Customers, cn=DHCP Service Config, dc=ntelos, dc=net
+objectClass: top
+objectClass: dhcpHost
+cn: brianlaptop
+dhcpHWAddress: ethernet 00:00:00:00:00:00
+
+You can use the command ldapadd to load all of these entries into your LDAP
+server. After you load this, you should be able to start up DHCP. If you run
+into problems reading the configuration, try running dhcpd with the -d flag.
+If you still have problems, edit the site.conf file in the DHCP source and
+add the line: COPTS= -DDEBUG_LDAP and recompile DHCP. (make sure you run make
+clean and rerun configure before you rebuild).
diff --git a/contrib/ldap/dhcp.schema b/contrib/ldap/dhcp.schema
new file mode 100644
index 0000000..c5ed6c7
--- /dev/null
+++ b/contrib/ldap/dhcp.schema
@@ -0,0 +1,462 @@
+attributetype ( 2.16.840.1.113719.1.203.4.1
+ NAME 'dhcpPrimaryDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DN of the dhcpServer which is the primary server for the configuration.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.2
+ NAME 'dhcpSecondaryDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DN of dhcpServer(s) which provide backup service for the configuration.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.3
+ NAME 'dhcpStatements'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Flexible storage for specific data depending on what object this exists in. Like conditional statements, server parameters, etc. This allows the standard to evolve without needing to adjust the schema.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.4
+ NAME 'dhcpRange'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'The starting & ending IP Addresses in the range (inclusive), separated by a hyphen; if the range only contains one address, then just the address can be specified with no hyphen. Each range is defined as a separate value.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.5
+ NAME 'dhcpPermitList'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'This attribute contains the permit lists associated with a pool. Each permit list is defined as a separate value.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.6
+ NAME 'dhcpNetMask'
+ EQUALITY integerMatch
+ DESC 'The subnet mask length for the subnet. The mask can be easily computed from this length.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.7
+ NAME 'dhcpOption'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Encoded option values to be sent to clients. Each value represents a single option and contains (OptionTag, Length, OptionValue) encoded in the format used by DHCP.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.8
+ NAME 'dhcpClassData'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Encoded text string or list of bytes expressed in hexadecimal, separated by colons. Clients match subclasses based on matching the class data with the results of match or spawn with statements in the class name declarations.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.9
+ NAME 'dhcpOptionsDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of the dhcpOption objects containing the configuration options provided by the server.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.10
+ NAME 'dhcpHostDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'the distinguished name(s) of the dhcpHost objects.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.11
+ NAME 'dhcpPoolDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of pools.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.12
+ NAME 'dhcpGroupDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of the groups.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.13
+ NAME 'dhcpSubnetDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of the subnets.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.14
+ NAME 'dhcpLeaseDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name of a client address.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE)
+
+attributetype ( 2.16.840.1.113719.1.203.4.15
+ NAME 'dhcpLeasesDN'
+ DESC 'The distinguished name(s) client addresses.'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.16
+ NAME 'dhcpClassesDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of a class(es) in a subclass.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.17
+ NAME 'dhcpSubclassesDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of subclass(es).'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.18
+ NAME 'dhcpSharedNetworkDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name(s) of sharedNetworks.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.19
+ NAME 'dhcpServiceDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DN of dhcpService object(s)which contain the configuration information. Each dhcpServer object has this attribute identifying the DHCP configuration(s) that the server is associated with.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.20
+ NAME 'dhcpVersion'
+ DESC 'The version attribute of this object.'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.21
+ NAME 'dhcpImplementation'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Description of the DHCP Server implementation e.g. DHCP Servers vendor.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.22
+ NAME 'dhcpAddressState'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'This stores information about the current binding-status of an address. For dynamic addresses managed by DHCP, the values should be restricted to the following: "FREE", "ACTIVE", "EXPIRED", "RELEASED", "RESET", "ABANDONED", "BACKUP". For other addresses, it SHOULD be one of the following: "UNKNOWN", "RESERVED" (an address that is managed by DHCP that is reserved for a specific client), "RESERVED-ACTIVE" (same as reserved, but address is currently in use), "ASSIGNED" (assigned manually or by some other mechanism), "UNASSIGNED", "NOTASSIGNABLE".'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.23
+ NAME 'dhcpExpirationTime'
+ EQUALITY generalizedTimeMatch
+ DESC 'This is the time the current lease for an address expires.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.24
+ NAME 'dhcpStartTimeOfState'
+ EQUALITY generalizedTimeMatch
+ DESC 'This is the time of the last state change for a leased address.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.25
+ NAME 'dhcpLastTransactionTime'
+ EQUALITY generalizedTimeMatch
+ DESC 'This is the last time a valid DHCP packet was received from the client.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.26
+ NAME 'dhcpBootpFlag'
+ EQUALITY booleanMatch
+ DESC 'This indicates whether the address was assigned via BOOTP.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.27
+ NAME 'dhcpDomainName'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'This is the name of the domain sent to the client by the server. It is essentially the same as the value for DHCP option 15 sent to the client, and represents only the domain - not the full FQDN. To obtain the full FQDN assigned to the client you must prepend the "dhcpAssignedHostName" to this value with a ".".'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.28
+ NAME 'dhcpDnsStatus'
+ EQUALITY integerMatch
+ DESC 'This indicates the status of updating DNS resource records on behalf of the client by the DHCP server for this address. The value is a 16-bit bitmask.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.29
+ NAME 'dhcpRequestedHostName'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'This is the hostname that was requested by the client.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.30
+ NAME 'dhcpAssignedHostName'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'This is the actual hostname that was assigned to a client. It may not be the name that was requested by the client. The fully qualified domain name can be determined by appending the value of "dhcpDomainName" (with a dot separator) to this name.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.31
+ NAME 'dhcpReservedForClient'
+ EQUALITY distinguishedNameMatch
+ DESC 'The distinguished name of a "dhcpClient" that an address is reserved for. This may not be the same as the "dhcpAssignedToClient" attribute if the address is being reassigned but the current lease has not yet expired.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.32
+ NAME 'dhcpAssignedToClient'
+ EQUALITY distinguishedNameMatch
+ DESC 'This is the distinguished name of a "dhcpClient" that an address is currently assigned to. This attribute is only present in the class when the address is leased.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.33
+ NAME 'dhcpRelayAgentInfo'
+ EQUALITY octetStringMatch
+ DESC 'If the client request was received via a relay agent, this contains information about the relay agent that was available from the DHCP request. This is a hex-encoded option value.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.34
+ NAME 'dhcpHWAddress'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'The clients hardware address that requested this IP address.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.35
+ NAME 'dhcpHashBucketAssignment'
+ EQUALITY octetStringMatch
+ DESC 'HashBucketAssignment bit map for the DHCP Server, as defined in DHC Load Balancing Algorithm [RFC 3074].'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.36
+ NAME 'dhcpDelayedServiceParameter'
+ EQUALITY integerMatch
+ DESC 'Delay in seconds corresponding to Delayed Service Parameter configuration, as defined in DHC Load Balancing Algorithm [RFC 3074]. '
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.37
+ NAME 'dhcpMaxClientLeadTime'
+ EQUALITY integerMatch
+ DESC 'Maximum Client Lead Time configuration in seconds, as defined in DHCP Failover Protocol [FAILOVR]'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.38
+ NAME 'dhcpFailOverEndpointState'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Server (Failover Endpoint) state, as defined in DHCP Failover Protocol [FAILOVR]'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.39
+ NAME 'dhcpErrorLog'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Generic error log attribute that allows logging error conditions within a dhcpService or a dhcpSubnet, like no IP addresses available for lease.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.40
+ NAME 'dhcpLocatorDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DN of dhcpLocator object which contain the DNs of all DHCP configuration objects. There will be a single dhcpLocator object in the tree with links to all the DHCP objects in the tree'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.41
+ NAME 'dhcpKeyAlgorithm'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Algorithm to generate TSIG Key'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.42
+ NAME 'dhcpKeySecret'
+ EQUALITY octetStringMatch
+ DESC 'Secret to generate TSIG Key' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.43
+ NAME 'dhcpDnsZoneServer'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Master server of the DNS Zone'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113719.1.203.4.44
+ NAME 'dhcpKeyDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DNs of TSIG Key to use in secure dynamic updates. In case of locator object, this will be list of TSIG keys. In case of DHCP Service, Shared Network, Subnet and DNS Zone, it will be a single key.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12)
+
+attributetype ( 2.16.840.1.113719.1.203.4.45
+ NAME 'dhcpZoneDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DNs of DNS Zone. In case of locator object, this will be list of DNS Zones in the tree. In case of DHCP Service, Shared Network and Subnet, it will be a single DNS Zone.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12)
+
+attributetype ( 2.16.840.1.113719.1.203.4.46
+ NAME 'dhcpFailOverPrimaryServer'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'IP address or DNS name of the server playing primary role in DHC Load Balancing and Fail over.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.47
+ NAME 'dhcpFailOverSecondaryServer'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'IP address or DNS name of the server playing secondary role in DHC Load Balancing and Fail over.'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.48
+ NAME 'dhcpFailOverPrimaryPort'
+ EQUALITY integerMatch
+ DESC 'Port on which primary server listens for connections from its fail over peer (secondary server)'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.49
+ NAME 'dhcpFailOverSecondaryPort'
+ EQUALITY integerMatch
+ DESC 'Port on which secondary server listens for connections from its fail over peer (primary server)'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.50
+ NAME 'dhcpFailOverResponseDelay'
+ EQUALITY integerMatch
+ DESC 'Maximum response time in seconds, before Server assumes that connection to fail over peer has failed'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.51
+ NAME 'dhcpFailOverUnackedUpdates'
+ EQUALITY integerMatch
+ DESC 'Number of BNDUPD messages that server can send before it receives BNDACK from its fail over peer'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.52
+ NAME 'dhcpFailOverSplit'
+ EQUALITY integerMatch
+ DESC 'Split between the primary and secondary servers for fail over purpose'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.53
+ NAME 'dhcpFailOverLoadBalanceTime'
+ EQUALITY integerMatch
+ DESC 'Cutoff time in seconds, after which load balance is disabled'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.54
+ NAME 'dhcpFailOverPeerDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'The DNs of Fail over peers. In case of locator object, this will be list of fail over peers in the tree. In case of Subnet and pool, it will be a single Fail Over Peer'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+#List of all servers in the tree
+attributetype ( 2.16.840.1.113719.1.203.4.55
+ NAME 'dhcpServerDN'
+ EQUALITY distinguishedNameMatch
+ DESC 'List of all DHCP Servers in the tree. Used by dhcpLocatorObject'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 2.16.840.1.113719.1.203.4.56
+ NAME 'dhcpComments'
+ EQUALITY caseIgnoreIA5Match
+ DESC 'Generic attribute that allows coments within any DHCP object'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+# Classes
+
+objectclass ( 2.16.840.1.113719.1.203.6.1
+ NAME 'dhcpService'
+ DESC 'Service object that represents the actual DHCP Service configuration. This is a container object.'
+ SUP top
+ MUST (cn)
+ MAY ( dhcpPrimaryDN $ dhcpSecondaryDN $ dhcpServerDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $dhcpComments $ dhcpOption) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.2
+ NAME 'dhcpSharedNetwork'
+ DESC 'This stores configuration information for a shared network.'
+ SUP top
+ MUST cn
+ MAY ( dhcpSubnetDN $ dhcpPoolDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpStatements $dhcpComments $ dhcpOption) X-NDS_CONTAINMENT ('dhcpService' ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.3
+ NAME 'dhcpSubnet'
+ DESC 'This class defines a subnet. This is a container object.'
+ SUP top
+ MUST ( cn $ dhcpNetMask )
+ MAY ( dhcpRange $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $ dhcpComments $ dhcpOption ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.4
+ NAME 'dhcpPool'
+ DESC 'This stores configuration information about a pool.'
+ SUP top
+ MUST ( cn $ dhcpRange )
+ MAY ( dhcpClassesDN $ dhcpPermitList $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $dhcpKeyDN $ dhcpStatements $ dhcpComments $ dhcpOption )
+ X-NDS_CONTAINMENT ('dhcpSubnet' 'dhcpSharedNetwork') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.5
+ NAME 'dhcpGroup'
+ DESC 'Group object that lists host DNs and parameters. This is a container object.'
+ SUP top
+ MUST cn
+ MAY ( dhcpHostDN $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption )
+ X-NDS_CONTAINMENT ('dhcpSubnet' 'dhcpService' ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.6
+ NAME 'dhcpHost'
+ DESC 'This represents information about a particular client'
+ SUP top
+ MUST cn
+ MAY (dhcpLeaseDN $ dhcpHWAddress $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption)
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSubnet' 'dhcpGroup') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.7
+ NAME 'dhcpClass'
+ DESC 'Represents information about a collection of related clients.'
+ SUP top
+ MUST cn
+ MAY (dhcpSubClassesDN $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption)
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSubnet' ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.8
+ NAME 'dhcpSubClass'
+ DESC 'Represents information about a collection of related classes.'
+ SUP top
+ MUST cn
+ MAY (dhcpClassData $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption) X-NDS_CONTAINMENT 'dhcpClass' )
+
+objectclass ( 2.16.840.1.113719.1.203.6.9
+ NAME 'dhcpOptions'
+ DESC 'Represents information about a collection of options defined.'
+ SUP top AUXILIARY
+ MUST cn
+ MAY ( dhcpOption $ dhcpComments )
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet' 'dhcpPool' 'dhcpGroup' 'dhcpHost' 'dhcpClass' ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.10
+ NAME 'dhcpLeases'
+ DESC 'This class represents an IP Address, which may or may not have been leased.'
+ SUP top
+ MUST ( cn $ dhcpAddressState )
+ MAY ( dhcpExpirationTime $ dhcpStartTimeOfState $ dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ dhcpReservedForClient $ dhcpAssignedToClient $ dhcpRelayAgentInfo $ dhcpHWAddress )
+ X-NDS_CONTAINMENT ( 'dhcpService' 'dhcpSubnet' 'dhcpPool') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.11
+ NAME 'dhcpLog'
+ DESC 'This is the object that holds past information about the IP address. The cn is the time/date stamp when the address was assigned or released, the address state at the time, if the address was assigned or released.'
+ SUP top
+ MUST ( cn )
+ MAY ( dhcpAddressState $ dhcpExpirationTime $ dhcpStartTimeOfState $ dhcpLastTransactionTime $ dhcpBootpFlag $ dhcpDomainName $ dhcpDnsStatus $ dhcpRequestedHostName $ dhcpAssignedHostName $ dhcpReservedForClient $ dhcpAssignedToClient $ dhcpRelayAgentInfo $ dhcpHWAddress $ dhcpErrorLog)
+ X-NDS_CONTAINMENT ('dhcpLeases' 'dhcpPool' 'dhcpSubnet' 'dhcpSharedNetwork' 'dhcpService' ) )
+
+objectclass ( 2.16.840.1.113719.1.203.6.12
+ NAME 'dhcpServer'
+ DESC 'DHCP Server Object'
+ SUP top
+ MUST ( cn )
+ MAY (dhcpServiceDN $ dhcpLocatorDN $ dhcpVersion $ dhcpImplementation $ dhcpHashBucketAssignment $ dhcpDelayedServiceParameter $ dhcpMaxClientLeadTime $ dhcpFailOverEndpointState $ dhcpStatements $ dhcpComments $ dhcpOption)
+ X-NDS_CONTAINMENT ('organization' 'organizationalunit' 'domain') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.13
+ NAME 'dhcpTSigKey'
+ DESC 'TSIG key for secure dynamic updates'
+ SUP top
+ MUST (cn $ dhcpKeyAlgorithm $ dhcpKeySecret )
+ MAY ( dhcpComments )
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.14
+ NAME 'dhcpDnsZone'
+ DESC 'DNS Zone for updating leases'
+ SUP top
+ MUST (cn $ dhcpDnsZoneServer )
+ MAY (dhcpKeyDN $ dhcpComments)
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.15
+ NAME 'dhcpFailOverPeer'
+ DESC 'This class defines the Fail over peer'
+ SUP top
+ MUST ( cn $ dhcpFailOverPrimaryServer $ dhcpFailOverSecondaryServer $ dhcpFailoverPrimaryPort $ dhcpFailOverSecondaryPort) MAY (dhcpFailOverResponseDelay $ dhcpFailOverUnackedUpdates $ dhcpMaxClientLeadTime $ dhcpFailOverSplit $ dhcpHashBucketAssignment $ dhcpFailOverLoadBalanceTime $ dhcpComments )
+ X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork' 'dhcpSubnet') )
+
+objectclass ( 2.16.840.1.113719.1.203.6.16
+ NAME 'dhcpLocator'
+ DESC 'Locator object for DHCP configuration in the tree. There will be a single dhcpLocator object in the tree with links to all the DHCP objects in the tree'
+ SUP top
+ MUST ( cn )
+ MAY ( dhcpServiceDN $dhcpServerDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpKeyDN $ dhcpZoneDN $ dhcpFailOverPeerDN $ dhcpOption $ dhcpComments)
+ X-NDS_CONTAINMENT ('organization' 'organizationalunit' 'domain') )
+
+
diff --git a/contrib/ldap/dhcpd-conf-to-ldap b/contrib/ldap/dhcpd-conf-to-ldap
new file mode 100644
index 0000000..aee6c97
--- /dev/null
+++ b/contrib/ldap/dhcpd-conf-to-ldap
@@ -0,0 +1,760 @@
+#!/usr/bin/perl -w
+
+# Brian Masney <masneyb@gftp.org>
+# To use this script, set your base DN below. Then run
+# ./dhcpd-conf-to-ldap.pl < /path-to-dhcpd-conf/dhcpd.conf > output-file
+# The output of this script will generate entries in LDIF format. You can use
+# the slapadd command to add these entries into your LDAP server. You will
+# definately want to double check that your LDAP entries are correct before
+# you load them into LDAP.
+
+# This script does not do much error checking. Make sure before you run this
+# that the DHCP server doesn't give any errors about your config file
+
+# FailOver notes:
+# Failover is disabled by default, since it may need manually intervention.
+# You can try the '--use=failover' option to see what happens :-)
+#
+# If enabled, the failover pool references will be written to LDIF output.
+# The failover configs itself will be added to the dhcpServer statements
+# and not to the dhcpService object (since this script uses only one and
+# it may be usefull to have multiple service containers in failover mode).
+# Further, this script does not check if primary or secondary makes sense,
+# it simply converts what it gets...
+
+use Net::Domain qw(hostname hostfqdn hostdomain);
+use Getopt::Long;
+
+my $domain = hostdomain(); # your.domain
+my $basedn = "dc=".$domain;
+ $basedn =~ s/\./,dc=/g; # dc=your,dc=domain
+my $server = hostname(); # hostname (nodename)
+my $dhcpcn = 'DHCP Config'; # CN of DHCP config tree
+my $dhcpdn = "cn=$dhcpcn, $basedn"; # DHCP config tree DN
+my $second = ''; # secondary server DN / hostname
+my $i_conf = ''; # dhcp.conf file to read or stdin
+my $o_ldif = ''; # output ldif file name or stdout
+my @use = (); # extended flags (failover)
+
+sub usage($;$)
+{
+ my $rc = shift;
+ my $err= shift;
+
+ print STDERR "Error: $err\n\n" if(defined $err);
+ print STDERR <<__EOF_USAGE__;
+usage:
+ $0 [options] < dhcpd.conf > dhcpd.ldif
+
+options:
+
+ --basedn "dc=your,dc=domain" ("$basedn")
+
+ --dhcpdn "dhcp config DN" ("$dhcpdn")
+
+ --server "dhcp server name" ("$server")
+
+ --second "secondary server or DN" ("$second")
+
+ --conf "/path/to/dhcpd.conf" (default is stdin)
+ --ldif "/path/to/output.ldif" (default is stdout)
+
+ --use "extended features" (see source comments)
+__EOF_USAGE__
+ exit($rc);
+}
+
+
+sub next_token
+{
+ local ($lowercase) = @_;
+ local ($token, $newline);
+
+ do
+ {
+ if (!defined ($line) || length ($line) == 0)
+ {
+ $line = <>;
+ return undef if !defined ($line);
+ chop $line;
+ $line_number++;
+ $token_number = 0;
+ }
+
+ $line =~ s/#.*//;
+ $line =~ s/^\s+//;
+ $line =~ s/\s+$//;
+ }
+ while (length ($line) == 0);
+
+ if (($token, $newline) = $line =~ /^(.*?)\s+(.*)/)
+ {
+ if ($token =~ /^"/) {
+ #handle quoted token
+ if ($token !~ /"\s*$/)
+ {
+ ($tok, $newline) = $newline =~ /([^"]+")(.*)/;
+ $token .= " $tok";
+ }
+ }
+ $line = $newline;
+ }
+ else
+ {
+ $token = $line;
+ $line = '';
+ }
+ $token_number++;
+
+ $token =~ y/[A-Z]/[a-z]/ if $lowercase;
+
+ return ($token);
+}
+
+
+sub remaining_line
+{
+ local ($block) = shift || 0;
+ local ($tmp, $str);
+
+ $str = "";
+ while (defined($tmp = next_token (0)))
+ {
+ $str .= ' ' if !($str eq "");
+ $str .= $tmp;
+ last if $tmp =~ /;\s*$/;
+ last if($block and $tmp =~ /\s*[}{]\s*$/);
+ }
+
+ $str =~ s/;$//;
+ return ($str);
+}
+
+
+sub
+add_dn_to_stack
+{
+ local ($dn) = @_;
+
+ $current_dn = "$dn, $current_dn";
+}
+
+
+sub
+remove_dn_from_stack
+{
+ $current_dn =~ s/^.*?,\s*//;
+}
+
+
+sub
+parse_error
+{
+ print "Parse error on line number $line_number at token number $token_number\n";
+ exit (1);
+}
+
+
+sub
+print_entry
+{
+ return if (scalar keys %curentry == 0);
+
+ if (!defined ($curentry{'type'}))
+ {
+ $hostdn = "cn=$server, $basedn";
+ print "dn: $hostdn\n";
+ print "cn: $server\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpServer\n";
+ print "dhcpServiceDN: $current_dn\n";
+ if(grep(/FaIlOvEr/i, @use))
+ {
+ foreach my $fo_peer (keys %failover)
+ {
+ next if(scalar(@{$failover{$fo_peer}}) <= 1);
+ print "dhcpStatements: failover peer $fo_peer { ",
+ join('; ', @{$failover{$fo_peer}}), "; }\n";
+ }
+ }
+ print "\n";
+
+ print "dn: $current_dn\n";
+ print "cn: $dhcpcn\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpService\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+ print "dhcpPrimaryDN: $hostdn\n";
+ if(grep(/FaIlOvEr/i, @use) and ($second ne ''))
+ {
+ print "dhcpSecondaryDN: $second\n";
+ }
+ }
+ elsif ($curentry{'type'} eq 'subnet')
+ {
+ print "dn: $current_dn\n";
+ print "cn: " . $curentry{'ip'} . "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpSubnet\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+
+ print "dhcpNetMask: " . $curentry{'netmask'} . "\n";
+ if (defined ($curentry{'ranges'}))
+ {
+ foreach $statement (@{$curentry{'ranges'}})
+ {
+ print "dhcpRange: $statement\n";
+ }
+ }
+ }
+ elsif ($curentry{'type'} eq 'shared-network')
+ {
+ print "dn: $current_dn\n";
+ print "cn: " . $curentry{'descr'} . "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpSharedNetwork\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+ }
+ elsif ($curentry{'type'} eq 'group')
+ {
+ print "dn: $current_dn\n";
+ print "cn: group", $curentry{'idx'}, "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpGroup\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+ }
+ elsif ($curentry{'type'} eq 'host')
+ {
+ print "dn: $current_dn\n";
+ print "cn: " . $curentry{'host'} . "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpHost\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+
+ if (defined ($curentry{'hwaddress'}))
+ {
+ $curentry{'hwaddress'} =~ y/[A-Z]/[a-z]/;
+ print "dhcpHWAddress: " . $curentry{'hwaddress'} . "\n";
+ }
+ }
+ elsif ($curentry{'type'} eq 'pool')
+ {
+ print "dn: $current_dn\n";
+ print "cn: pool", $curentry{'idx'}, "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpPool\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+
+ if (defined ($curentry{'ranges'}))
+ {
+ foreach $statement (@{$curentry{'ranges'}})
+ {
+ print "dhcpRange: $statement\n";
+ }
+ }
+ }
+ elsif ($curentry{'type'} eq 'class')
+ {
+ print "dn: $current_dn\n";
+ print "cn: " . $curentry{'class'} . "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpClass\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+ }
+ elsif ($curentry{'type'} eq 'subclass')
+ {
+ print "dn: $current_dn\n";
+ print "cn: " . $curentry{'subclass'} . "\n";
+ print "objectClass: top\n";
+ print "objectClass: dhcpSubClass\n";
+ if (defined ($curentry{'options'}))
+ {
+ print "objectClass: dhcpOptions\n";
+ }
+ print "dhcpClassData: " . $curentry{'class'} . "\n";
+ }
+
+ if (defined ($curentry{'statements'}))
+ {
+ foreach $statement (@{$curentry{'statements'}})
+ {
+ print "dhcpStatements: $statement\n";
+ }
+ }
+
+ if (defined ($curentry{'options'}))
+ {
+ foreach $statement (@{$curentry{'options'}})
+ {
+ print "dhcpOption: $statement\n";
+ }
+ }
+
+ print "\n";
+ undef (%curentry);
+}
+
+
+sub parse_netmask
+{
+ local ($netmask) = @_;
+ local ($i);
+
+ if ((($a, $b, $c, $d) = $netmask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) != 4)
+ {
+ parse_error ();
+ }
+
+ $num = (($a & 0xff) << 24) |
+ (($b & 0xff) << 16) |
+ (($c & 0xff) << 8) |
+ ($d & 0xff);
+
+ for ($i=1; $i<=32 && $num & (1 << (32 - $i)); $i++)
+ {
+ }
+ $i--;
+
+ return ($i);
+}
+
+
+sub parse_subnet
+{
+ local ($ip, $tmp, $netmask);
+
+ print_entry () if %curentry;
+
+ $ip = next_token (0);
+ parse_error () if !defined ($ip);
+
+ $tmp = next_token (1);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq 'netmask');
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ $netmask = parse_netmask ($tmp);
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ add_dn_to_stack ("cn=$ip");
+ $curentry{'type'} = 'subnet';
+ $curentry{'ip'} = $ip;
+ $curentry{'netmask'} = $netmask;
+ $cursubnet = $ip;
+ $curcounter{$ip} = { pool => 0, group => 0 };
+}
+
+
+sub parse_shared_network
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $descr = next_token (0);
+ parse_error () if !defined ($descr);
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ add_dn_to_stack ("cn=$descr");
+ $curentry{'type'} = 'shared-network';
+ $curentry{'descr'} = $descr;
+}
+
+
+sub parse_host
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $host = next_token (0);
+ parse_error () if !defined ($host);
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ add_dn_to_stack ("cn=$host");
+ $curentry{'type'} = 'host';
+ $curentry{'host'} = $host;
+}
+
+
+sub parse_group
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ my $idx;
+ if(exists($curcounter{$cursubnet})) {
+ $idx = ++$curcounter{$cursubnet}->{'group'};
+ } else {
+ $idx = ++$curcounter{''}->{'group'};
+ }
+
+ add_dn_to_stack ("cn=group".$idx);
+ $curentry{'type'} = 'group';
+ $curentry{'idx'} = $idx;
+}
+
+
+sub parse_pool
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ my $idx;
+ if(exists($curcounter{$cursubnet})) {
+ $idx = ++$curcounter{$cursubnet}->{'pool'};
+ } else {
+ $idx = ++$curcounter{''}->{'pool'};
+ }
+
+ add_dn_to_stack ("cn=pool".$idx);
+ $curentry{'type'} = 'pool';
+ $curentry{'idx'} = $idx;
+}
+
+
+sub parse_class
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $class = next_token (0);
+ parse_error () if !defined ($class);
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ $class =~ s/\"//g;
+ add_dn_to_stack ("cn=$class");
+ $curentry{'type'} = 'class';
+ $curentry{'class'} = $class;
+}
+
+
+sub parse_subclass
+{
+ local ($descr, $tmp);
+
+ print_entry () if %curentry;
+
+ $class = next_token (0);
+ parse_error () if !defined ($class);
+
+ $subclass = next_token (0);
+ parse_error () if !defined ($subclass);
+
+ $tmp = next_token (0);
+ parse_error () if !defined ($tmp);
+ parse_error () if !($tmp eq '{');
+
+ add_dn_to_stack ("cn=$subclass");
+ $curentry{'type'} = 'subclass';
+ $curentry{'class'} = $class;
+ $curentry{'subclass'} = $subclass;
+}
+
+
+sub parse_hwaddress
+{
+ local ($type, $hw, $tmp);
+
+ $type = next_token (1);
+ parse_error () if !defined ($type);
+
+ $hw = next_token (1);
+ parse_error () if !defined ($hw);
+ $hw =~ s/;$//;
+
+ $curentry{'hwaddress'} = "$type $hw";
+}
+
+
+sub parse_range
+{
+ local ($tmp, $str);
+
+ $str = remaining_line ();
+
+ if (!($str eq ''))
+ {
+ $str =~ s/;$//;
+ push (@{$curentry{'ranges'}}, $str);
+ }
+}
+
+
+sub parse_statement
+{
+ local ($token) = shift;
+ local ($str);
+
+ if ($token eq 'option')
+ {
+ $str = remaining_line ();
+ push (@{$curentry{'options'}}, $str);
+ }
+ elsif($token eq 'failover')
+ {
+ $str = remaining_line (1); # take care on block
+ if($str =~ /[{]/)
+ {
+ my ($peername, @statements);
+
+ parse_error() if($str !~ /^\s*peer\s+(.+?)\s+[{]\s*$/);
+ parse_error() if(($peername = $1) !~ /^\"?[^\"]+\"?$/);
+
+ #
+ # failover config block found:
+ # e.g. 'failover peer "some-name" {'
+ #
+ if(not grep(/FaIlOvEr/i, @use))
+ {
+ print STDERR "Warning: Failover config 'peer $peername' found!\n";
+ print STDERR " Skipping it, since failover disabled!\n";
+ print STDERR " You may try out --use=failover option.\n";
+ }
+
+ until($str =~ /[}]/ or $str eq "")
+ {
+ $str = remaining_line (1);
+ # collect all statements, except ending '}'
+ push(@statements, $str) if($str !~ /[}]/);
+ }
+ $failover{$peername} = [@statements];
+ }
+ else
+ {
+ #
+ # pool reference to failover config is fine
+ # e.g. 'failover peer "some-name";'
+ #
+ if(not grep(/FaIlOvEr/i, @use))
+ {
+ print STDERR "Warning: Failover reference '$str' found!\n";
+ print STDERR " Skipping it, since failover disabled!\n";
+ print STDERR " You may try out --use=failover option.\n";
+ }
+ else
+ {
+ push (@{$curentry{'statements'}}, $token. " " . $str);
+ }
+ }
+ }
+ elsif($token eq 'zone')
+ {
+ $str = $token;
+ while($str !~ /}$/) {
+ $str .= ' ' . next_token (0);
+ }
+ push (@{$curentry{'statements'}}, $str);
+ }
+ elsif($token =~ /^(authoritative)[;]*$/)
+ {
+ push (@{$curentry{'statements'}}, $1);
+ }
+ else
+ {
+ $str = $token . " " . remaining_line ();
+ push (@{$curentry{'statements'}}, $str);
+ }
+}
+
+
+my $ok = GetOptions(
+ 'basedn=s' => \$basedn,
+ 'dhcpdn=s' => \$dhcpdn,
+ 'server=s' => \$server,
+ 'second=s' => \$second,
+ 'conf=s' => \$i_conf,
+ 'ldif=s' => \$o_ldif,
+ 'use=s' => \@use,
+ 'h|help|usage' => sub { usage(0); },
+);
+
+unless($server =~ /^\w+/)
+ {
+ usage(1, "invalid server name '$server'");
+ }
+unless($basedn =~ /^\w+=[^,]+/)
+ {
+ usage(1, "invalid base dn '$basedn'");
+ }
+
+if($dhcpdn =~ /^cn=([^,]+)/i)
+ {
+ $dhcpcn = "$1";
+ }
+$second = '' if not defined $second;
+unless($second eq '' or $second =~ /^cn=[^,]+\s*,\s*\w+=[^,]+/i)
+ {
+ if($second =~ /^cn=[^,]+$/i)
+ {
+ # relative DN 'cn=name'
+ $second = "$second, $basedn";
+ }
+ elsif($second =~ /^\w+/)
+ {
+ # assume hostname only
+ $second = "cn=$second, $basedn";
+ }
+ else
+ {
+ usage(1, "invalid secondary '$second'")
+ }
+ }
+
+usage(1) unless($ok);
+
+if($i_conf ne "" and -f $i_conf)
+ {
+ if(not open(STDIN, '<', $i_conf))
+ {
+ print STDERR "Error: can't open conf file '$i_conf': $!\n";
+ exit(1);
+ }
+ }
+if($o_ldif ne "")
+ {
+ if(-e $o_ldif)
+ {
+ print STDERR "Error: output ldif name '$o_ldif' already exists!\n";
+ exit(1);
+ }
+ if(not open(STDOUT, '>', $o_ldif))
+ {
+ print STDERR "Error: can't open ldif file '$o_ldif': $!\n";
+ exit(1);
+ }
+ }
+
+
+print STDERR "Creating LDAP Configuration with the following options:\n";
+print STDERR "\tBase DN: $basedn\n";
+print STDERR "\tDHCP DN: $dhcpdn\n";
+print STDERR "\tServer DN: cn=$server, $basedn\n";
+print STDERR "\tSecondary DN: $second\n"
+ if(grep(/FaIlOvEr/i, @use) and $second ne '');
+print STDERR "\n";
+
+my $token;
+my $token_number = 0;
+my $line_number = 0;
+my %curentry;
+my $cursubnet = '';
+my %curcounter = ( '' => { pool => 0, group => 0 } );
+
+$current_dn = "$dhcpdn";
+$curentry{'descr'} = $dhcpcn;
+$line = '';
+%failover = ();
+
+while (($token = next_token (1)))
+ {
+ if ($token eq '}')
+ {
+ print_entry () if %curentry;
+ if($current_dn =~ /.+?,\s*${dhcpdn}$/) {
+ # don't go below dhcpdn ...
+ remove_dn_from_stack ();
+ }
+ }
+ elsif ($token eq 'subnet')
+ {
+ parse_subnet ();
+ next;
+ }
+ elsif ($token eq 'shared-network')
+ {
+ parse_shared_network ();
+ next;
+ }
+ elsif ($token eq 'class')
+ {
+ parse_class ();
+ next;
+ }
+ elsif ($token eq 'subclass')
+ {
+ parse_subclass ();
+ next;
+ }
+ elsif ($token eq 'pool')
+ {
+ parse_pool ();
+ next;
+ }
+ elsif ($token eq 'group')
+ {
+ parse_group ();
+ next;
+ }
+ elsif ($token eq 'host')
+ {
+ parse_host ();
+ next;
+ }
+ elsif ($token eq 'hardware')
+ {
+ parse_hwaddress ();
+ next;
+ }
+ elsif ($token eq 'range')
+ {
+ parse_range ();
+ next;
+ }
+ else
+ {
+ parse_statement ($token);
+ next;
+ }
+ }
+
+close(STDIN) if($i_conf);
+close(STDOUT) if($o_ldif);
+
+print STDERR "Done.\n";
+
diff --git a/contrib/ms2isc/Registry.pm b/contrib/ms2isc/Registry.pm
new file mode 100644
index 0000000..69e2413
--- /dev/null
+++ b/contrib/ms2isc/Registry.pm
@@ -0,0 +1,361 @@
+# Registry.pm
+# A perl module provided easy Windows Registry access
+#
+# Author: Shu-Min Chang
+#
+# Copyright(c) 2002 Intel 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 Intel Corporation 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 INTEL CORPORATION 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 INTEL CORPORATION OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE
+# 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 ADVICED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+
+package Registry;
+use strict;
+use Win32API::Registry 0.21 qw( :ALL );
+
+
+###############################################################################
+
+#-----------------------------------------
+sub GetRegKeyVal($*) {
+ my ($FullRegPath, $value) = @_;
+#-----------------------------------------
+# Purpose: uses Win32API to get registry information from a given server
+#
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual
+# to figure out why something is done.
+# input: $FullRegPath: a MS specific way of fully qualifying a registry path
+# \\Server\RootKey\Path\ValueName
+# output: *value: the value of the registry key of $FullRegPath
+#
+
+ my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);
+
+#print "in sub:GetRegKeyVal:Parameters:", @_, "\n";
+
+ # Check the for valid fully qualified registry path
+ return -1 if (! ($FullRegPath =~ /\\.+\\.+/)) && (!($FullRegPath =~ /\\\\.+\\.+\\.+/));
+
+
+ $RemoteMachine = (index($FullRegPath, "\\\\") == $[ ? substr($FullRegPath, $[+2, index($FullRegPath, "\\", $[+2)-2):0);
+
+#print "RemoteMachine = $RemoteMachine\n";
+
+ $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;
+ $RootKey = substr ($FullRegPath, $i, index($FullRegPath, "\\", $i)-$i);
+
+ $KeyName = $FullRegPath;
+ $KeyName =~ s/.*\\(.+)/$1/;
+#print "KeyName = $KeyName\n";
+
+ $i = index($FullRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;
+ $RegPath = substr ($FullRegPath, $i, length($FullRegPath) - length($KeyName) -$i - 1);
+#print "RegPath = $RegPath\n";
+
+ my ($RootKeyHandle, $handle, $key, $type);
+
+ if ($RemoteMachine) {
+ $RootKeyHandle = regConstant($RootKey);
+
+ if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {
+ $$value = regLastError();
+ return -2;
+ }
+ } else { # not valid actually because I can't find the mapping table of default
+ # local handle mapping. Should always pass in the Machine name to use for now
+ $handle = $RootKey;
+ }
+
+ if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {
+ $$value = regLastError();
+#print "regLastError = $$value\n";
+ return -3;
+ }
+ if (!RegQueryValueEx( $key, $KeyName, [], $type, $$value, [] )) {
+ $$value = regLastError();
+#print "regLastError = $$value\n";
+ return -4;
+ }
+
+#print "RegType=$type\n"; # Perl doesn't fetch type, at this in this
+ # ActiveState 5.6.0 that I'm using
+#print "RegValue=$$value\n";
+ RegCloseKey ($key);
+ RegCloseKey ($handle);
+
+ return 0;
+}
+
+###############################################################################
+
+#-----------------------------------------
+sub GetRegSubkeyList($*) {
+ my ($FullKeyRegPath, $Subkeys) = @_;
+#-----------------------------------------
+# Purpose: uses Win32API to get registry subkey list from a given server
+#
+# WARNING: this procedure is VERY Win32 specific, you'll need a Win32 manual
+# to figure out why something is done.
+# input: $FullKeyRegPath: a MS specific way of fully qualifying a registry path
+# \\Server\RootKey\Path\KeyName
+# output: *Subkeys: the list of subkeys in array of the registry key of
+# $FullKeyRegPath
+#
+
+ my ($RemoteMachine, $RootKey, $RegPath, $KeyName, $i);
+
+#print "in sub:GetRegSubkeyList:Parameters:", @_, "\n";
+
+ # Check the for valid registry key path
+ return -1 if (! ($FullKeyRegPath =~ /\\.+\\.+/)) && (!($FullKeyRegPath =~ /\\\\.+\\.+\\.+/));
+
+
+ $RemoteMachine = (index($FullKeyRegPath, "\\\\") == $[ ? substr($FullKeyRegPath, $[+2, index($FullKeyRegPath, "\\", $[+2)-2):0);
+
+#print "RemoteMachine = $RemoteMachine\n";
+
+ $i = $RemoteMachine ? $[+3+length($RemoteMachine) : $[+1;
+ $RootKey = substr ($FullKeyRegPath, $i, index($FullKeyRegPath, "\\", $i)-$i);
+
+ $i = index($FullKeyRegPath, $RootKey, $[+length($RemoteMachine)) + $[ + length($RootKey)+1;
+ $RegPath = substr ($FullKeyRegPath, $i);
+
+#print "RegPath = $RegPath\n";
+
+ my ($RootKeyHandle, $handle, $key, $type);
+
+ if ($RemoteMachine) {
+ $RootKeyHandle = regConstant($RootKey);
+
+ if (!RegConnectRegistry ($RemoteMachine, $RootKeyHandle, $handle)) {
+ @$Subkeys[0]= regLastError();
+ return -2;
+ }
+ } else { # not valid actually because I can't find the mapping table of default
+ # local handle mapping. Should always pass in the Machine name to use for now
+ $handle = $RootKey;
+ }
+
+ if (!RegOpenKeyEx ($handle, $RegPath, 0, KEY_READ, $key)) {
+ @$Subkeys[0] = regLastError();
+#print "regLastError = @$Subkeys[0]\n";
+ return -3;
+ }
+
+ my $tmp;
+ # For some reason, the regLastError() stays at ERROR_NO_MORE_ITEMS
+ # in occasional call sequence, so I'm resetting the error code
+ # before entering the loop
+ regLastError(0);
+ for ($i=0; regLastError()==regConstant("ERROR_NO_MORE_ITEMS"); $i++) {
+#print "\nERROR: error enumumerating reg\n";
+ if (RegEnumKeyEx ($key, $i, $tmp, [], [], [], [], [])) {
+ @$Subkeys[$i] = $tmp;
+ }
+ }
+
+#print "RegType=$type\n";
+#print "RegValue=@$Subkeys\n";
+ RegCloseKey ($key);
+ RegCloseKey ($handle);
+
+ return 0;
+}
+
+#####################################################
+
+sub ExtractOptionIps ($) {
+ my ($MSDHCPOption6Value) = @_;
+ my @ip;
+# purpose: DHCP registry specific; to return the extracted IP addresses from
+# the input variable
+# input:
+# $MSDHCPOption6Value: Option 6 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @ip: an arry of IP addresses in human readable format.
+
+
+ # First extract the size of the option
+ my ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVV", $MSDHCPOption6Value);
+# print "byte = $byte\nsize=$size\nind1=$ind1\nind2=$ind2\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $size * $ind1;
+ ($byte, $size, $ind1, $ind2, @octet) = unpack("VVVVC$number", $MSDHCPOption6Value);
+
+ for (my $i=0; $i<$#octet; $i=$i+4) {
+ $ip[$i/4] = "$octet[$i+3]\.$octet[$i+2]\.$octet[$i+1]\.$octet[$i]";
+ }
+
+ return @ip;
+}
+
+#####################################################
+
+sub ExtractOptionStrings ($) {
+ my ($MSDHCPOption15Value) = @_;
+ my @string;
+# purpose: DHCP registry specific; to return the extracted string from
+# the input variable
+# input:
+# $MSDHCPOption15Value: Option 15 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @string: an arry of strings in human readable format.
+
+
+ # First extract the size of the option
+ my ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVV", $MSDHCPOption15Value);
+# print "byte = $byte\nstart=$start\nind1=$ind1\nind2=$ind2\nsize=$size\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $size * $ind1;
+ ($byte, $start, $ind1, $ind2, $size, @data) = unpack("VVVVVC$number", $MSDHCPOption15Value);
+
+ for (my $i=0; $i<$ind1; $i++) {
+ # actually this is only programmed to do one string, until I see
+ # example of how the multiple strings are represented, I don't have a
+ # guess to how to program them properly.
+ for (my $j=0; $j<$#data & $data[$j]!=0; $j+=2) {
+ $string[$i] = $string[$i].chr($data[$j]);
+ }
+ }
+
+ return @string;
+}
+
+#####################################################
+
+sub ExtractOptionHex ($) {
+ my ($MSDHCPOption46Value) = @_;
+ my @Hex;
+# purpose: DHCP registry specific; to return the extracted hex from the input
+# variable
+# input:
+# $MSDHCPOption46Value: Option 46 was used to develop, but it works for any
+# other options of the same datatype.
+# output: none
+# return:
+# @Hex: an arry of hex strings in human readable format.
+ my $Temp;
+
+
+ # First extract the size of the option
+ my ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVV", $MSDHCPOption46Value);
+# print "byte=$byte\nunknown=$unknown\nind1=$ind1\nind2=$ind2\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+ my $number = $byte - 15;
+ ($byte, $unknown, $ind1, $ind2, @data) = unpack("VVVVC$number", $MSDHCPOption46Value);
+
+# printf "data=%4x\n", $data[0];
+
+ for (my $i=0; $i<$ind1; $i++) {
+ # actually this is only programmed to do one Hex, until I see
+ # example of how the multiple Hexes are represented, I don't have a
+ # guess to how to program them properly.
+ for (my $j=3; $j>=0; $j--) {
+ $Hex[$i] = $Hex[$i].sprintf ("%x", $data[$j+$i*4]);
+ }
+ }
+
+ return @Hex;
+}
+
+#####################################################
+
+sub ExtractExclusionRanges ($) {
+ my ($MSDHCPExclusionRanges) = @_;
+ my @RangeList;
+# purpose: DHCP registry specific; to return the extracted exclusion ranges
+# from the input variable
+# input:
+# $MSDHCPExclusionRanges: Exclusion range as DHCP server returns them
+# output: none
+# return:
+# @RangeList: an arry of paird IP addresses strings in human readable format.
+
+
+ # First extract the size of the option
+ my ($paircount, @data) = unpack("V", $MSDHCPExclusionRanges);
+# print "paircount = $paircount\n";
+
+ # Calculate total number of bytes that IP addresses occupy
+# my $number = $paircount * 4*2;
+# ($paircount, @data) = unpack("VC$number", $MSDHCPExclusionRanges);
+#
+# for (my $i=0; $i<$#data; $i=$i+4) {
+# $ip[$i/4] = "$data[$i+3]\.$data[$i+2]\.$data[$i+1]\.$data[$i]";
+# }
+#
+ my $number = $paircount * 2;
+ ($paircount, @data) = unpack("VL$number", $MSDHCPExclusionRanges);
+
+ for (my $i=0; $i<=$#data; $i++) {
+ $RangeList[$i] = pack ("L", $data[$i]);
+# print "extracted", ExtractIp ($RangeList[$i]), "\n";
+ }
+
+ return @RangeList;
+}
+#####################################################
+
+sub ExtractIp ($) {
+ my ($octet) = @_;
+# purpose: to return the registry saved IP address in a readable form
+# input:
+# $octet: a 4 byte data storing the IP address as the registry save it as
+# output: none
+# return: anonymous variable of a string of IP address
+
+ my (@data) = unpack ("C4", $octet);
+
+ return "$data[3]\.$data[2]\.$data[1]\.$data[0]";
+
+}
+#####################################################
+
+sub ExtractHex ($) {
+ my ($HexVal) = @_;
+ my @Hex;
+# purpose: to return the registry saved hex number in a readable form
+# input:
+# $octet: a 4 byte data storing the hex number as the registry save it as
+# output: none
+# return:
+# $Hex: string of hex digit
+
+
+ # First extract the size of the option
+ my (@data) = unpack("C4", $HexVal);
+
+ for (my $i=3; $i>=0; $i--) {
+ $Hex[0] = $Hex[0] . sprintf ("%x", $data[$i]);
+ }
+
+ return @Hex;
+}
+1;
diff --git a/contrib/ms2isc/ms2isc.pl b/contrib/ms2isc/ms2isc.pl
new file mode 100644
index 0000000..da3e10f
--- /dev/null
+++ b/contrib/ms2isc/ms2isc.pl
@@ -0,0 +1,634 @@
+#set ts=3
+#
+# ms2isc.pl
+# MS NT4 DHCP to ISC DHCP Configuration Migration Tool
+#
+# Author: Shu-Min Chang
+#
+# Copyright(c) 2003 Intel 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 Intel Corporation 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 INTEL CORPORATION 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 INTEL CORPORATION OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO PROCUREMENT OF SUBSTITUE
+# 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 ADVICED OF THE POSSIBILITY OF SUCH
+# DAMAGE.
+
+use strict;
+use Socket;
+use Getopt::Std;
+use Filehandle;
+use Registry; # Custom Perl Module to make Registry access easier.
+
+my $usage = << 'ENDOFHELP';
+
+Purpose: A Perl Script converting MS NT4 DHCP configuration to ISC DHCP3
+configuration file by reading NT4's registry.
+
+Requires: Registry.pm and ActiveState 5.6.0
+
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
+
+ <Srv> Server IP or name for NT4 DHCP server to fetch the configuration from.
+ <Out> Output filename for the configuration file.
+ <Pri> Primary DNS server name for sending the dynamic DNS update to.
+ <Key> Key name for use in updating the dynamic DNS zone.
+ <Fo> Failover peer name shared with the DHCP partner.
+
+Essentially the <Srv> needs to be an NT4 (3.x should work but not tested) which
+you should have registry read access to. You must run this script from a
+Windows machine because of the requirement to access the registry.
+
+The <Pri> is optional parameter for desginating the dynamic DNS update if
+missing then the "zone" section of the declaration will be skipped. The <Key>
+is needed if you've configured your DNS zone with a key, in addition, you'll
+need to define that key in this DHCP configuration file elsewhere manually,
+read the DHCP Handbook to figure out what you need to define.
+
+The <Fo> specifies the fail-over peer name in the pool section, you'll need to
+define additional detail elsewhere manually, again read the DHCP handbook.
+
+NOTE: the program only knows of the following global and subnet options:
+ 3, 6, 15, 28, 44, and 46
+
+ If it runs into options other than the known ones, it will quit. You
+ may fix this by modifying the following procedures:
+ GetGlobalOptions
+ GetScopes
+ PrintSubnetConfig
+
+ In addition, the resulting subnets configuration will have the "deny
+ dynamic bootp clients" you should take them out if that's not what you
+ want :).
+
+ Finally, as the parameter structures implied, it is assumed that you
+ want the same zone primary and update key for all zones and that the
+ same failover is to be applied to all the pools. Furthermore the
+ subnet zones are all assumed to be class C delineated, but if you
+ happend to be delegated at the class B level, this will work fine too.
+
+Author: Shu-Min Chang <smchang@yahoo.com>
+
+Copyright: Please read the top of the source code
+
+Acknowledgement:
+ Brian L. King for coding help, Douglas A. Darrah for testing, and James E.
+Pressley for being the DHCP reference book :).
+
+Usage: $ARGV -s <Srv> -o <Out> [-p <Pri> [-k <key>]] [-f <Fo>]
+
+Version: 1.0.1
+
+ENDOFHELP
+
+###################### Begin Main Program ####################################
+
+ my (%opts, %GlobalOptions, %SuperScopes, %Scopes);
+
+ ### Get parameters and make sure that they meet the require/optoinal criteria
+ getopts('s:o:p:k:f:', \%opts) or die $usage;
+ ($opts{s} and $opts{o}) or die $usage;
+ if ($opts{k}) { $opts{p} or die $usage; }
+
+ ### Read all the registry stuff into the memory
+ %GlobalOptions = GetGlobalOptions($opts{s});
+ %SuperScopes = GetSuperScope($opts{s});
+ %Scopes = GetScopes ($opts{s});
+
+ ### Process and print out to the output file
+ my ($outfile, $i, $j, @Domains);
+
+ $outfile = new FileHandle "> $opts{o}";
+ if (!defined $outfile) {
+ die "Can't open file: $opts{o}: $!";
+ }
+
+ for $i (keys %SuperScopes) {
+ print $outfile "\n##############################################################\n";
+ my ($Scopename) = $i;
+ $Scopename =~ s/ //g;
+ print $outfile "shared-network $Scopename {\n";
+ foreach $j (@{$SuperScopes{$i}}) {
+ PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$j}}, $j, "\t", $opts{f});
+ InsertIfUnique (\@Domains, $Scopes{$j}{domain}) if exists $Scopes{$j}{domain};
+ delete $Scopes{$j};
+ }
+ print $outfile "}\n";
+ if ($opts{p} or $opts{k}) {
+ foreach $j (@{$SuperScopes{$i}}) {
+ PrintSubnetUpdate($outfile, $j, $opts{p}, $opts{k});
+ }
+ }
+ }
+
+ for $i (keys %Scopes) {
+ print $outfile "\n##############################################################\n";
+ PrintSubnetConfig($outfile, \%GlobalOptions, \%{$Scopes{$i}}, $i, "", $opts{f});
+ if ($opts{p} or $opts{k}) { PrintSubnetUpdate($outfile, $i, $opts{p}, $opts{k}); }
+ InsertIfUnique (\@Domains, $Scopes{$i}{domain}) if exists $Scopes{$i}{domain};
+ }
+
+ if ($opts{p} or $opts{k}) {
+ InsertIfUnique (\@Domains, $GlobalOptions{domain}) if exists $GlobalOptions{domain};
+ for $i (@Domains) {
+ PrintDomainUpdate($outfile, $i, $opts{p}, $opts{k});
+ }
+ }
+
+ undef ($outfile);
+ print "Done.\n";
+ exit();
+
+################################## End Main Program ###########################
+
+
+
+
+
+######################################################################
+sub InsertIfUnique ($$) {
+ my ($Array, $data) = @_;
+# purpose: insert $data into array @{$Array} iff the data is not in there yet
+# input:
+# $data: scalar data to be added to the @{$Array} if unique
+# $Array: reference of the Array to compare the uniqueness of the $data
+# output:
+# $Array: reference of the array with the resulting array.
+# return: none
+
+ my ($i);
+
+ for ($i=0; $i<=$#{$Array} && ${$Array}[$i] ne $data; $i++) { }
+
+ if ($i > $#{$Array}) {
+ ${$Array}[$i] = $data;
+ }
+}
+######################################################################
+sub PrintDomainUpdate ($$$$) {
+ my ($outfile, $Domain, $DDNSServer, $key) = @_;
+# purpose: print out the foward domain zone update declaration
+# input:
+# $outfile: filehandle of the file to write the output to
+# $Domain: a string representing the forward domain
+# $DDNSServer: a string of the DNS server accepting the DDNS update
+# $key: a string representing the key used to update the zone
+# output: none
+# return: none
+#
+
+ print $outfile "zone $Domain {\n";
+ print $outfile "\tprimary $DDNSServer;\n";
+ !$key or print $outfile "\tkey $key;\n";
+ print $outfile "}\n";
+
+}
+######################################################################
+sub PrintSubnetUpdate ($$$$) {
+ my ($outfile, $Subnet, $DDNSServer, $key) = @_;
+# purpose: print out the reverse domain zone update declaration
+# input:
+# $outfile: filehandle of the file to write the output to
+# $Subnet: a string representing the subnet in the form 1.2.3.4
+# $DDNSServer: a string of the DNS server accepting the DDNS update
+# $key: a string representing the key used to update the zone
+# output: none
+# return: none
+#
+
+ my ($Reverse);
+
+ $_ = join (".", reverse(split(/\./, $Subnet)));
+ m/\d*\.(.*)/;
+ $Reverse = $1;
+ print $outfile "zone $Reverse.in-addr.arpa. {\n";
+ print $outfile "\tprimary $DDNSServer;\n";
+ !$key or print $outfile "\tkey $key;\n";
+ print $outfile "}\n";
+
+}
+######################################################################
+sub PrintSubnetConfig ($$$$$$) {
+ my ($outfile, $GlobalOptions, $Scope, $Subnet, $prefix, $failover) = @_;
+# purpose: print out the effective scope configuration for one subnet as
+# derived from the global and scope options.
+# input:
+# $outfile: filehandle of the file to write the output to
+# $GlobalOptions: refernce to the hashed variable from GetGlobalOptions
+# $Scopes: reference to the hashed variable of the subnet in interest
+# $Subnet: string variable of the subnet being processed
+# $prefix: string to be printed before each line (designed for tab)
+# $failover: string to be used for the "failover peer" line
+# output: none
+# return: none
+#
+ my ($pound) = ( ${$Scope}{disable}? "#".$prefix : $prefix);
+ print $outfile $pound, "subnet $Subnet netmask ${$Scope}{mask} {\n";
+ print $outfile "$prefix# Name: ${$Scope}{name}\n";
+ print $outfile "$prefix# Comment: ${$Scope}{comment}\n";
+ if (exists ${$Scope}{routers}) {
+ print $outfile $pound, "\toption routers @{${$Scope}{routers}};\n";
+ } elsif (exists ${$GlobalOptions}{routers}) {
+ print $outfile $pound, "\toption routers @{${$GlobalOptions}{routers}};\t# NOTE: obtained from global option, bad practice detected\n";
+ } else {
+ print $outfile "### WARNING: No router was found for this subnet!!! ##########\n";
+ }
+
+ if (exists ${$Scope}{dnses}) {
+ print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$Scope}{dnses}}), ";\n";
+ } elsif (exists ${$GlobalOptions}{dnses}) {
+ print $outfile $pound, "\toption domain-name-servers ", join(",", @{${$GlobalOptions}{dnses}}), ";\n";
+ }
+
+ if (exists ${$Scope}{domain}) {
+ print $outfile $pound, "\toption domain-name \"${$Scope}{domain}\";\n";
+ } elsif (exists ${$GlobalOptions}{domain}) {
+ print $outfile $pound, "\toption domain-name \"${$GlobalOptions}{domain}\";\n";
+ }
+
+ if (exists ${$Scope}{broadcast}) {
+ print $outfile $pound, "\toption broadcast-address ${$Scope}{broadcast};\n";
+ } elsif (exists ${$GlobalOptions}{broadcast}) {
+ print $outfile $pound, "\toption broadcast-address ${$GlobalOptions}{broadcast};\n";
+ }
+
+ if (exists ${$Scope}{winses}) {
+ print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$Scope}{winses}}), ";\n";
+ } elsif (exists ${$GlobalOptions}{winses}) {
+ print $outfile $pound, "\toption netbios-name-servers ", join(",", @{${$GlobalOptions}{winses}}), ";\n";
+ }
+
+ if (exists ${$Scope}{winstype}) {
+ print $outfile $pound, "\toption netbios-node-type ${$Scope}{winstype};\n";
+ } elsif (exists ${$GlobalOptions}{winstype}) {
+ print $outfile $pound, "\toption netbios-node-type ${$GlobalOptions}{winstype};\n"
+ }
+
+ print $outfile $pound, "\tdefault-lease-time ${$Scope}{leaseduration};\n";
+ print $outfile $pound, "\tpool {\n";
+ for (my $r=0; $r<=$#{${$Scope}{ranges}}; $r+=2) {
+ print $outfile $pound, "\t\trange ${$Scope}{ranges}[$r] ${$Scope}{ranges}[$r+1];\n";
+ }
+ !$failover or print $outfile $pound, "\t\tfailover peer \"$failover\";\n";
+ print $outfile $pound, "\t\tdeny dynamic bootp clients;\n";
+ print $outfile $pound, "\t}\n";
+ print $outfile $pound, "}\n";
+}
+
+######################################################################
+sub GetScopes ($) {
+ my ($Server) = @_;
+ my (%Scopes);
+# purpose: to return NT4 server's scope configuration
+# input:
+# $Server: string of the valid IP or name of the NT4 server
+# output: none
+# return:
+# %Scope: hash of hash of hash of various data types to be returned of the
+# following data structure
+# $Scope{<subnet>}{disable} => boolean
+# $Scope{<subnet>}{mask} => string (e.g. "1.2.3.255")
+# $Scope{<subnet>}{name} => string (e.g "Office Subnet #1")
+# $Scope{<subnet>}{comment} => string (e.g. "This is a funny subnet")
+# $Scope{<subnet>}{ranges} => array of paired inclusion IP addresses
+# (e.g. "1.2.3.1 1.2.3.10 1.2.3.100 10.2.3.200
+# says that we have 2 inclusion ranges of
+# 1-10 and 100-200)
+# $Scopes{<subnet>}{routers} => array of IP address strings
+# $Scopes{<subnet>}{dnses} => array of IP address/name string
+# $Scopes{<subnet>}{domain} > string
+# $Scopes{<subnet>}{broadcast} => string
+# $Scopes{<subnet>}{winses} => array of IP addresses/name string
+# $Scopes{<subnet>}{winstype} => integer
+# $Scopes{<subnet>}{leaseduration} => integer
+
+ my ($RegVal, @Subnets, @Router, $SubnetName, $SubnetComment, @SubnetOptions, @SRouter, @SDNSServers, @SDomainname, @SWINSservers, @SNetBIOS, @SLeaseDuration, @SSubnetState, @SExclusionRanges, @SSubnetAddress, @SSubnetMask, @SFirstAddress, $SStartAddress, $SEndAddress, @InclusionRanges, @SBroadcastAddress);
+
+ print "Getting list of subnets\n";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets", \@Subnets)) {
+ die "Unable to obtain a list of subnets from the server!\n";
+ }
+
+ for (my $i=0; $i<=$#Subnets; $i++) {
+ print "\t Fetching Subnet $Subnets[$i] (",$i+1, "/", $#Subnets+1, "): ";
+
+ print ".";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges", \@SFirstAddress)) {
+ # Don't know why MS has a tree for this, but as far
+ # as I can tell, only one subtree will ever come out of
+ # this, so I'm skipping the 'for' loop
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\StartAddress", \$RegVal)) {
+ $SStartAddress = $RegVal;
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\IpRanges\\$SFirstAddress[0]\\EndAddress", \$RegVal)) {
+ $SEndAddress = $RegVal;
+ }
+# print "\n\tInclusion Range: ", Registry::ExtractIp($SStartAddress), " - ", Registry::ExtractIp($SEndAddress),"\n";
+
+ } else {
+ die "\n\n# Error Getting Inclusion Range FirstAddress!!!\n\n";
+ }
+
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\ExcludedIpRanges", \$RegVal)) {
+ @SExclusionRanges = Registry::ExtractExclusionRanges($RegVal);
+
+# for (my $j=2; $j<=$#SExclusionRanges; $j+=2) {
+# if (unpack("L",$SExclusionRanges[$j]) < unpack("L",$SExclusionRanges[$j-2])) {
+# print ("\n******** Subnet exclusion ranges out of order ********\n");
+# }
+# }
+
+ @SExclusionRanges = sort(@SExclusionRanges);
+
+# print "\n\tExclusion Ranges: ";
+# for (my $j=0; $j<=$#SExclusionRanges; $j+=2) {
+# print "\n\t\t",Registry::ExtractIp($SExclusionRanges[$j])," - ",Registry::ExtractIp($SExclusionRanges[$j+1]);
+# }
+
+ }
+ @InclusionRanges = FindInclusionRanges ($SStartAddress, $SEndAddress, @SExclusionRanges);
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetName", \$RegVal)) {
+ $SubnetName = $RegVal;
+# print "\n\tSubnetName: $SubnetName";
+ }
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetComment", \$RegVal)) {
+ $SubnetComment = $RegVal;
+# print "\n\tSubnetComment: $SubnetComment";
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetAddress", \$RegVal)) {
+ @SSubnetAddress = Registry::ExtractIp($RegVal);
+# print "\n\tSubnetAddress: $SSubnetAddress[0]";
+ }
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetMask", \$RegVal)) {
+ @SSubnetMask = Registry::ExtractIp($RegVal);
+# print "\n\tSubnetMask: $SSubnetMask[0]";
+ }
+
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetState", \$RegVal)) {
+ @SSubnetState = Registry::ExtractHex ($RegVal);
+# print "\n\tSubnetState = $SSubnetState[0]";
+ }
+
+ $Scopes{$Subnets[$i]}{disable} = hex($SSubnetState[0]) ? 1 : 0;
+ $Scopes{$Subnets[$i]}{mask} = $SSubnetMask[0];
+ $Scopes{$Subnets[$i]}{name} = $SubnetName;
+ $Scopes{$Subnets[$i]}{comment} = $SubnetComment;
+ for (my $r=0; $r<=$#InclusionRanges; $r++) {
+ $Scopes{$Subnets[$i]}{ranges}[$r] = Registry::ExtractIp($InclusionRanges[$r]);
+ }
+
+################## Get scope options
+
+ my (@SubnetOptionsList);
+
+ print "\n\t\tOptions:";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions", \@SubnetOptionsList)) {
+ die "Unable to get subnet options list for $Subnets[$i]!\n";
+ }
+
+ for (my $j=0; $j<=$#SubnetOptionsList; $j++) {
+ print ".";
+ if (!Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\Subnets\\$Subnets[$i]\\SubnetOptions\\$SubnetOptionsList[$j]\\OptionValue", \$RegVal)) {
+ for ($SubnetOptionsList[$j]) {
+ /003/ and do {
+# @SRouter = Registry::ExtractOptionIps($RegVal);
+ $Scopes{$Subnets[$i]}{routers} = [Registry::ExtractOptionIps($RegVal)];
+ last;
+ };
+ /006/ and do {
+ @SDNSServers = Registry::ExtractOptionIps($RegVal);
+ for (my $d=0; $d<=$#SDNSServers; $d++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SDNSServers[$d])), &AF_INET);
+ $Scopes{$Subnets[$i]}{dnses}[$d] = $ipname ? $ipname : $SDNSServers[$d];
+ }
+ last;
+ };
+ /015/ and do {
+ @SDomainname = Registry::ExtractOptionStrings($RegVal);
+ $Scopes{$Subnets[$i]}{domain} = $SDomainname[0];
+ last;
+ };
+ /028/ and do {
+ @SBroadcastAddress = Registry::ExtractOptionIps($RegVal);
+ $Scopes{$Subnets[$i]}{broadcast} = $SBroadcastAddress[0];
+ last;
+ };
+ /044/ and do {
+ @SWINSservers = Registry::ExtractOptionIps($RegVal);
+ for (my $w=0; $w<=$#SWINSservers; $w++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $SWINSservers[$w])), &AF_INET);
+ $Scopes{$Subnets[$i]}{winses}[$w] = $ipname ? $ipname : $SWINSservers[$w];
+ }
+ last;
+ };
+ /046/ and do {
+ @SNetBIOS = Registry::ExtractOptionHex($RegVal);
+ $Scopes{$Subnets[$i]}{winstype} = hex($SNetBIOS[0]);
+ last;
+ };
+ /051/ and do {
+ @SLeaseDuration = Registry::ExtractOptionHex($RegVal);
+ $Scopes{$Subnets[$i]}{leaseduration} = hex($SLeaseDuration[0]);
+ last;
+ };
+ die "This program does not recognize subnet option \#$SubnetOptionsList[$j] yet!\n"
+ }
+ } else {
+ die "Unable to obtain option SubnetOptionsList[$j] from $Subnets[$i], most likely a registry problem!\n"
+ }
+ }
+ print "\n";
+ }
+
+ return %Scopes;
+}
+
+######################################################################
+sub FindInclusionRanges ($$@) {
+ my ($StartAddress, $EndAddress, @ExclusionRanges) = @_;
+# Purpose: to calculate and return the DHCP inclusion ranges out of
+# data provided by the NT4 DHCP server
+# input: $StartAddress:
+# $EndAddress:
+# @ExclusionRanges
+# output: none
+# return: An arry of IP address pair representing the inclusion ranges
+# in the native registry format.
+#
+
+ my ($SA, $EA, @ER);
+ $SA = unpack("L", $StartAddress);
+ $EA = unpack("L", $EndAddress);
+ @ER = @ExclusionRanges;
+ for (my $i=0; $i<=$#ER; $i++) {
+ $ER[$i] = unpack ("L", $ER[$i]);
+ }
+
+ my @InclusionRanges;
+
+
+ $InclusionRanges[0] = $SA;
+ $InclusionRanges[1] = $EA;
+
+ for (my $i=0; $i<=$#ER; $i+=2) {
+ if ($ER[$i] == $InclusionRanges[$#InclusionRanges-1]) {
+ $InclusionRanges[$#InclusionRanges-1] = $ER[$i+1] + 1;
+ }
+ if ($ER[$i] > $InclusionRanges[$#InclusionRanges-1]) {
+ $InclusionRanges[$#InclusionRanges] = $ER[$i]-1;
+ }
+ if (($ER[$i+1] > $InclusionRanges[$#InclusionRanges]) &&
+ ($ER[$i+1] != $EA)) {
+ $InclusionRanges[$#InclusionRanges+1] = $ER[$i+1] + 1;
+ $InclusionRanges[$#InclusionRanges+1] = $EA;
+ }
+ if ($InclusionRanges[$#InclusionRanges] < $InclusionRanges[$#InclusionRanges-1]) {
+ $#InclusionRanges -= 2;
+ }
+ }
+
+ for (my $i=0; $i<=$#InclusionRanges; $i++) {
+ $InclusionRanges[$i] = pack("L", $InclusionRanges[$i]);
+ # print "Inclusion: ", Registry::ExtractIp($InclusionRanges[$i]), "\n";
+ }
+ return @InclusionRanges;
+}
+
+####################################################################
+sub GetSuperScope ($) {
+ my ($Server) = @_;
+ my (%SuperScopes);
+#
+# purpose: gets the Superscope list from the given server
+# input:
+# $Server: string of the valid IP address or name of the NT4 server
+# ouput: none
+# return:
+# %SuperScopes: hash of array subnets with the following data structure
+# $SuperScopes{<SuperscopeName>} => array of sunbets
+#
+ my (@SuperScopeNames, @SCSubnetList);
+
+ print "Getting Superscope list: ";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope", \@SuperScopeNames)) {
+ for (my $i=0; $i<=$#SuperScopeNames; $i++) {
+ print ".";
+ if (!Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\SuperScope\\$SuperScopeNames[$i]", \@SCSubnetList)) {
+ $SuperScopes{$SuperScopeNames[$i]} = [@SCSubnetList];
+ }
+ }
+ print "\n";
+ }
+
+ return %SuperScopes;
+}
+
+####################################################################
+sub GetGlobalOptions($) {
+ my ($Server) = @_;
+ my (%GlobalOptions);
+# purpose: to return NT4 server's global scope configuration
+# input:
+# $Server: string of the valid IP or name of the NT4 server
+# output: none
+# return:
+# %GlobalOptions: hash of hash of various data types to be returned of the
+# following data structure
+# $GlobalOptions{routers} => array of IP address strings
+# $GlobalOptions{dnses} => array of IP address/name string
+# $GlobalOptions{domain} > string
+# $GlobalOptions{broadcast} => string
+# $GlobalOptions{winses} => array of IP addresses/name string
+# $GlobalOptions{winstype} => integer
+
+ my ($RegVal, @temp, @GlobalOptionValues);
+
+ print "Getting Global Options: ";
+ if (Registry::GetRegSubkeyList ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\Configuration\\GlobalOptionValues", \@GlobalOptionValues)) {
+ die "Unable to obtain GlobalOptionValues";
+ }
+
+ for (my $i=0; $i<=$#GlobalOptionValues; $i++) {
+ print ".";
+ if (Registry::GetRegKeyVal ("\\\\$Server\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\DHCPServer\\configuration\\globaloptionvalues\\$GlobalOptionValues[$i]\\optionvalue", \$RegVal)) {
+ die "Unable to retrive global option $GlobalOptionValues[$i]\n";
+ }
+
+
+ for ($GlobalOptionValues[$i]) {
+ /003/ and do {
+ @temp=Registry::ExtractOptionIps($RegVal);
+ $GlobalOptions{routers} = [@temp];
+ last;
+ };
+ /006/ and do {
+ # DNS Servers
+ @temp = Registry::ExtractOptionIps($RegVal);
+ for (my $d=0; $d<=$#temp; $d++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$d])), &AF_INET);
+ $GlobalOptions{dnses}[$d] = $ipname ? $ipname : $temp[$d];
+ }
+ last;
+ };
+ /015/ and do {
+ # Domain Name
+ @temp = Registry::ExtractOptionStrings($RegVal);
+ $GlobalOptions{domain} = $temp[0];
+ last;
+ };
+ /028/ and do {
+ # broadcast address
+ @temp = Registry::ExtractOptionIps($RegVal);
+ $GlobalOptions{broadcast} = $temp[0];
+ last;
+ };
+ /044/ and do {
+ # WINS Servers
+ @temp = Registry::ExtractOptionIps ($RegVal);
+ $GlobalOptions{winses} = [@temp];
+ for (my $w=0; $w<=$#temp; $w++) {
+ my ($ipname, $rest) = gethostbyaddr(pack("C4", split(/\./, $temp[$w])), &AF_INET);
+ $GlobalOptions{winses}[$w] = $ipname ? $ipname : $temp[$w];
+ }
+ last;
+ };
+ /046/ and do {
+ # NETBIOS node type
+ @temp = Registry::ExtractOptionHex($RegVal);
+ $GlobalOptions{winstype} = hex($temp[0]);
+ last;
+ };
+ die "This program does not recgonize global option \#$GlobalOptionValues[$i] yet!\n"
+ }
+ }
+ print "\n";
+
+ return %GlobalOptions;
+}
diff --git a/contrib/ms2isc/readme.txt b/contrib/ms2isc/readme.txt
new file mode 100644
index 0000000..862d010
--- /dev/null
+++ b/contrib/ms2isc/readme.txt
@@ -0,0 +1,15 @@
+Copyright: please read the top of the source code.
+
+Usage:
+Objective: please read the help screen by executing the program without any
+ parameter.
+
+Revision:
+SMC: Shu-Min Chang
+
+Who When What
+--- ------ --------------------------------------------------------------------
+SMC 021107 Initial release Version 1.0 to ISC DHCP repository
+SMC 030129 Fixed inclusion range calculation by sorting exclusion before
+ passing to FindInclusionRanges
+SMC 030228 release 1.0.1 to ISC DHCP repository
diff --git a/contrib/sethostname.sh b/contrib/sethostname.sh
new file mode 100644
index 0000000..7088c00
--- /dev/null
+++ b/contrib/sethostname.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# This script can be installed in /etc/dhclient-enter-hooks to set the client's
+# hostname based either on the hostname that the DHCP server supplied or the
+# hostname in whatever ptr record exists for the assigned IP address.
+
+if [ x$new_host_name = x ]; then
+ ptrname=`echo $new_ip_address \
+ |sed -e \
+ 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\4.\3.\2.\1.in-addr.arpa/'`
+ (echo "set type=ptr"; echo "$ptrname") |nslookup >/tmp/nslookup.$$
+ set `sed -n -e "s/$ptrname[ ]*\(canonical \)*name *= *\(.*\)/\2 \1/p" \
+ < /tmp/nslookup.$$` _
+ if [ x$1 = x_ ]; then
+ new_host_name=""
+ else
+ if [ $# -gt 1 ] && [ x$2 = xcanonical ]; then
+ new_host_name=`sed -n -e "s/$1[ ]*name *= *\(.*\)/\1/p" \
+ </tmp/nslookup.$$`
+ else
+ new_host_name=$1
+ fi
+ fi
+ rm /tmp/nslookup.$$
+fi
+if [ x$new_host_name != x ]; then
+ hostname $new_host_name
+fi
+
diff --git a/contrib/solaris.init b/contrib/solaris.init
new file mode 100644
index 0000000..2c79898
--- /dev/null
+++ b/contrib/solaris.init
@@ -0,0 +1,28 @@
+#!/bin/sh
+# Contributed by Brian Murrell
+
+state=$1
+
+set `who -r`
+case $state in
+
+'start')
+ if [ $9 = "2" -o $9 = "3" ]
+ then
+ exit
+ fi
+ if [ -f @PREFIX@/sbin/dhcpd ]; then
+ echo "Starting the ISC DHCP server"
+ @PREFIX@/sbin/dhcpd
+ fi
+ ;;
+'stop')
+ if [ -f @PREFIX@/etc/dhcpd.pid ]; then
+ PID=`cat @PREFIX@/etc/dhcpd.pid`
+ if [ -d /proc/$PID ]; then
+ echo "Stopping the ISC DHCP server"
+ kill $PID
+ fi
+ fi
+ ;;
+esac
diff --git a/depcomp b/depcomp
new file mode 100755
index 0000000..04701da
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,530 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2005-07-09.11
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+ "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> $depfile
+ echo >> $depfile
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> $depfile
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
+ tmpdepfile="$stripped.u"
+ if test "$libtool" = yes; then
+ "$@" -Wc,-M
+ else
+ "$@" -M
+ fi
+ stat=$?
+
+ if test -f "$tmpdepfile"; then :
+ else
+ stripped=`echo "$stripped" | sed 's,^.*/,,'`
+ tmpdepfile="$stripped.u"
+ fi
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+
+ if test -f "$tmpdepfile"; then
+ outname="$stripped.o"
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+ sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mecanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no
+ for arg in "$@"; do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix="`echo $object | sed 's/^.*\././'`"
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o,
+ # because we must use -o when running libtool.
+ "$@" || exit $?
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/dhcpctl/Makefile.am b/dhcpctl/Makefile.am
new file mode 100644
index 0000000..61049be
--- /dev/null
+++ b/dhcpctl/Makefile.am
@@ -0,0 +1,15 @@
+bin_PROGRAMS = omshell
+lib_LIBRARIES = libdhcpctl.a
+noinst_PROGRAMS = cltest
+man_MANS = omshell.1 dhcpctl.3
+EXTRA_DIST = $(man_MANS)
+
+omshell_SOURCES = omshell.c
+omshell_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+
+libdhcpctl_a_SOURCES = dhcpctl.c callback.c remote.c
+
+cltest_SOURCES = cltest.c
+cltest_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a \ No newline at end of file
diff --git a/dhcpctl/Makefile.in b/dhcpctl/Makefile.in
new file mode 100644
index 0000000..6546419
--- /dev/null
+++ b/dhcpctl/Makefile.in
@@ -0,0 +1,591 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+bin_PROGRAMS = omshell$(EXEEXT)
+noinst_PROGRAMS = cltest$(EXEEXT)
+subdir = dhcpctl
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
+ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"
+libLIBRARIES_INSTALL = $(INSTALL_DATA)
+LIBRARIES = $(lib_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libdhcpctl_a_AR = $(AR) $(ARFLAGS)
+libdhcpctl_a_LIBADD =
+am_libdhcpctl_a_OBJECTS = dhcpctl.$(OBJEXT) callback.$(OBJEXT) \
+ remote.$(OBJEXT)
+libdhcpctl_a_OBJECTS = $(am_libdhcpctl_a_OBJECTS)
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am_cltest_OBJECTS = cltest.$(OBJEXT)
+cltest_OBJECTS = $(am_cltest_OBJECTS)
+cltest_DEPENDENCIES = libdhcpctl.a ../common/libdhcp.a \
+ ../omapip/libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a
+am_omshell_OBJECTS = omshell.$(OBJEXT)
+omshell_OBJECTS = $(am_omshell_OBJECTS)
+omshell_DEPENDENCIES = libdhcpctl.a ../common/libdhcp.a \
+ ../omapip/libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libdhcpctl_a_SOURCES) $(cltest_SOURCES) $(omshell_SOURCES)
+DIST_SOURCES = $(libdhcpctl_a_SOURCES) $(cltest_SOURCES) \
+ $(omshell_SOURCES)
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+NROFF = nroff
+MANS = $(man_MANS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LIBRARIES = libdhcpctl.a
+man_MANS = omshell.1 dhcpctl.3
+EXTRA_DIST = $(man_MANS)
+omshell_SOURCES = omshell.c
+omshell_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+
+libdhcpctl_a_SOURCES = dhcpctl.c callback.c remote.c
+cltest_SOURCES = cltest.c
+cltest_LDADD = libdhcpctl.a ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign dhcpctl/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign dhcpctl/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLIBRARIES: $(lib_LIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+ @$(POST_INSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ p=$(am__strip_dir) \
+ echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \
+ $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \
+ else :; fi; \
+ done
+
+uninstall-libLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLIBRARIES:
+ -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+libdhcpctl.a: $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_DEPENDENCIES)
+ -rm -f libdhcpctl.a
+ $(libdhcpctl_a_AR) libdhcpctl.a $(libdhcpctl_a_OBJECTS) $(libdhcpctl_a_LIBADD)
+ $(RANLIB) libdhcpctl.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+cltest$(EXEEXT): $(cltest_OBJECTS) $(cltest_DEPENDENCIES)
+ @rm -f cltest$(EXEEXT)
+ $(LINK) $(cltest_OBJECTS) $(cltest_LDADD) $(LIBS)
+omshell$(EXEEXT): $(omshell_OBJECTS) $(omshell_DEPENDENCIES)
+ @rm -f omshell$(EXEEXT)
+ $(LINK) $(omshell_OBJECTS) $(omshell_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cltest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpctl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/omshell.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man1: $(man1_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+install-man3: $(man3_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 3*) ;; \
+ *) ext='3' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst"; \
+ done
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 3*) ;; \
+ *) ext='3' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man3dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man3dir)/$$inst"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-binPROGRAMS install-libLIBRARIES
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man1 install-man3
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-libLIBRARIES \
+ uninstall-man
+
+uninstall-man: uninstall-man1 uninstall-man3
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libLIBRARIES clean-noinstPROGRAMS ctags \
+ distclean distclean-compile distclean-generic distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-binPROGRAMS install-data install-data-am \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-libLIBRARIES install-man install-man1 install-man3 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-libLIBRARIES uninstall-man uninstall-man1 \
+ uninstall-man3
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/dhcpctl/callback.c b/dhcpctl/callback.c
new file mode 100644
index 0000000..b9033f4
--- /dev/null
+++ b/dhcpctl/callback.c
@@ -0,0 +1,168 @@
+/* callback.c
+
+ The dhcpctl callback object. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include "dhcpctl.h"
+
+/* dhcpctl_set_callback
+
+ synchronous, with asynchronous aftereffect
+ handle is some object upon which some kind of process has been
+ started - e.g., an open, an update or a refresh.
+ data is an anonymous pointer containing some information that
+ the callback will use to figure out what event completed.
+ return value of 0 means callback was successfully set, a nonzero
+ status code is returned otherwise.
+ Upon completion of whatever task is in process, the callback
+ will be passed the handle to the object, a status code
+ indicating what happened, and the anonymous pointer passed to */
+
+dhcpctl_status dhcpctl_set_callback (dhcpctl_handle h, void *data,
+ void (*func) (dhcpctl_handle,
+ dhcpctl_status, void *))
+{
+ dhcpctl_callback_object_t *callback;
+ omapi_object_t *inner;
+
+ callback = dmalloc (sizeof *callback, MDL);
+ if (!callback)
+ return ISC_R_NOMEMORY;
+
+ /* Tie the callback object to the innermost object in the chain. */
+ for (inner = h; inner -> inner; inner = inner -> inner)
+ ;
+ omapi_object_reference (&inner -> inner,
+ (omapi_object_t *)callback, MDL);
+ omapi_object_reference ((omapi_object_t **)&callback -> outer,
+ inner, MDL);
+
+ /* Save the actual handle pointer we were passed for the callback. */
+ omapi_object_reference (&callback -> object, h, MDL);
+ callback -> data = data;
+ callback -> callback = func;
+
+ return ISC_R_SUCCESS;
+}
+
+/* Callback methods (not meant to be called directly) */
+
+isc_result_t dhcpctl_callback_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != dhcpctl_callback_type)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcpctl_callback_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != dhcpctl_callback_type)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *o,
+ const char *name, va_list ap)
+{
+ dhcpctl_callback_object_t *p;
+ isc_result_t waitstatus;
+
+ if (o -> type != dhcpctl_callback_type)
+ return DHCP_R_INVALIDARG;
+ p = (dhcpctl_callback_object_t *)o;
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "ready")) {
+ if (p -> inner && p -> inner -> type -> signal_handler)
+ return (*(p -> inner -> type -> signal_handler))
+ (p -> inner, name, ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ if (p -> object -> type == dhcpctl_remote_type) {
+ waitstatus = (((dhcpctl_remote_object_t *)
+ (p -> object)) -> waitstatus);
+ } else
+ waitstatus = ISC_R_SUCCESS;
+
+ /* Do the callback. */
+ if (p -> callback)
+ (*(p -> callback)) (p -> object, waitstatus, p -> data);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcpctl_callback_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ dhcpctl_callback_object_t *p;
+ if (h -> type != dhcpctl_callback_type)
+ return DHCP_R_INVALIDARG;
+ p = (dhcpctl_callback_object_t *)h;
+ if (p -> handle)
+ omapi_object_dereference ((omapi_object_t **)&p -> handle,
+ file, line);
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *p)
+{
+ if (p -> type != dhcpctl_callback_type)
+ return DHCP_R_INVALIDARG;
+
+ if (p -> inner && p -> inner -> type -> stuff_values)
+ return (*(p -> inner -> type -> stuff_values)) (c, id,
+ p -> inner);
+ return ISC_R_SUCCESS;
+}
+
diff --git a/dhcpctl/cltest.c b/dhcpctl/cltest.c
new file mode 100644
index 0000000..dfc2c87
--- /dev/null
+++ b/dhcpctl/cltest.c
@@ -0,0 +1,179 @@
+/* cltest.c
+
+ Example program that uses the dhcpctl library. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2000-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software was contributed to Internet Systems Consortium
+ * by Brian Murrell.
+ */
+
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "omapip/result.h"
+#include "dhcpctl.h"
+
+int main (int, char **);
+
+enum modes { up, down, undefined };
+
+static void usage (char *s) {
+ fprintf (stderr,
+ "Usage: %s [-n <username>] [-p <password>] [-a <algorithm>]"
+ "(-u | -d) <if>\n", s);
+ exit (1);
+}
+
+int main (argc, argv)
+ int argc;
+ char **argv;
+{
+ isc_result_t status, waitstatus;
+ dhcpctl_handle authenticator;
+ dhcpctl_handle connection;
+ dhcpctl_handle interface_handle;
+ dhcpctl_data_string result;
+ int i;
+ int mode = undefined;
+ const char *interface = 0;
+ const char *action;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp (argv[i], "-u")) {
+ mode = up;
+ } else if (!strcmp (argv [i], "-d")) {
+ mode = down;
+ } else if (argv[i][0] == '-') {
+ usage(argv[0]);
+ } else {
+ interface = argv[i];
+ }
+ }
+
+ if (!interface)
+ usage(argv[0]);
+ if (mode == undefined)
+ usage(argv[0]);
+
+ status = dhcpctl_initialize ();
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_initialize: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ authenticator = dhcpctl_null_handle;
+ connection = dhcpctl_null_handle;
+
+ status = dhcpctl_connect (&connection, "127.0.0.1", 7911,
+ authenticator);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_connect: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ interface_handle = dhcpctl_null_handle;
+ status = dhcpctl_new_object (&interface_handle,
+ connection, "interface");
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_new_object: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ status = dhcpctl_set_string_value (interface_handle,
+ interface, "name");
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_set_value: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ if (mode == up) {
+ /* "up" the interface */
+ printf ("upping interface %s\n", interface);
+ action = "create";
+ status = dhcpctl_open_object (interface_handle, connection,
+ DHCPCTL_CREATE | DHCPCTL_EXCL);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_open_object: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ } else {
+ /* down the interface */
+ printf ("downing interface %s\n", interface);
+ action = "remove";
+ status = dhcpctl_open_object (interface_handle, connection, 0);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_open_object: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ status = dhcpctl_wait_for_completion (interface_handle,
+ &waitstatus);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_wait_for_completion: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ if (waitstatus != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_wait_for_completion: %s\n",
+ isc_result_totext (waitstatus));
+ exit (1);
+ }
+ status = dhcpctl_object_remove (connection, interface_handle);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_open_object: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ }
+
+ status = dhcpctl_wait_for_completion (interface_handle, &waitstatus);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_wait_for_completion: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ if (waitstatus != ISC_R_SUCCESS) {
+ fprintf (stderr, "interface object %s: %s\n", action,
+ isc_result_totext (waitstatus));
+ exit (1);
+ }
+
+ memset (&result, 0, sizeof result);
+ status = dhcpctl_get_value (&result, interface_handle, "state");
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_get_value: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ exit (0);
+}
diff --git a/dhcpctl/dhcpctl.3 b/dhcpctl/dhcpctl.3
new file mode 100644
index 0000000..9aa1851
--- /dev/null
+++ b/dhcpctl/dhcpctl.3
@@ -0,0 +1,493 @@
+.\" -*- nroff -*-
+.\"
+.\" Project: DHCP
+.\" File: dhcpctl.3
+.\" RCSId: $Id: dhcpctl.3,v 1.7.24.2 2011-04-25 23:49:52 sar Exp $
+.\"
+.\" Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2000-2003 by Internet Software Consortium
+.\" Copyright (c) 2000 Nominum, Inc.
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" Description: dhcpctl man page.
+.\"
+.\"
+.Dd Nov 15, 2000
+.Dt DHCPCTL 3
+.Os DHCP 3
+.ds vT DHCP Programmer's Manual
+.\"
+.\"
+.\"
+.Sh NAME
+.Nm dhcpctl_initialize
+.Nd dhcpctl library initialization.
+.\"
+.\"
+.\"
+.Sh SYNOPSIS
+.Fd #include <dhcpctl/dhcpctl.h>
+.Ft dhcpctl_status
+.Fo dhcpctl_initialize
+.Fa void
+.Fc
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_connect
+.Fa "dhcpctl_handle *cxn"
+.Fa "const char *host"
+.Fa "int port"
+.Fa "dhcpctl_handle auth"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_wait_for_completion
+.Fa "dhcpctl_handle object"
+.Fa "dhcpctl_status *status"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_get_value
+.Fa "dhcpctl_data_string *value"
+.Fa "dhcpctl_handle object"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_get_boolean
+.Fa "int *value"
+.Fa "dhcpctl_handle object"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_set_value
+.Fa "dhcpctl_handle object"
+.Fa "dhcpctl_data_string value"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_set_string_value
+.Fa "dhcpctl_handle object"
+.Fa "const char *value"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_set_boolean_value
+.Fa "dhcpctl_handle object"
+.Fa "int value"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_set_int_value
+.Fa "dhcpctl_handle object"
+.Fa "int value"
+.Fa "const char *name"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_object_update
+.Fa "dhcpctl_handle connection"
+.Fa "dhcpctl_handle object"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_object_refresh
+.Fa "dhcpctl_handle connection"
+.Fa "dhcpctl_handle object"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_object_remove
+.Fa "dhcpctl_handle connection"
+.Fa "dhcpctl_handle object"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_set_callback
+.Fa "dhcpctl_handle object"
+.Fa "void *data"
+.Fa "void (*function) (dhcpctl_handle, dhcpctl_status, void *)"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_new_authenticator
+.Fa "dhcpctl_handle *object"
+.Fa "const char *name"
+.Fa "const char *algorithm"
+.Fa "const char *secret"
+.Fa "unsigned secret_len"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_new_object
+.Fa "dhcpctl_handle *object"
+.Fa "dhcpctl_handle connection"
+.Fa "const char *object_type"
+.Fc
+.\"
+.\"
+.\"
+.Ft dhcpctl_status
+.Fo dhcpctl_open_object
+.Fa "dhcpctl_handle object"
+.Fa "dhcpctl_handle connection"
+.Fa "int flags"
+.Fc
+.\"
+.\"
+.\"
+.Ft isc_result_t
+.Fo omapi_data_string_new
+.Fa dhcpctl_data_string *data
+.Fa unsigned int length
+.Fa const char *filename,
+.Fa int lineno
+.Fc
+.\"
+.\"
+.\"
+.Ft isc_result_t
+.Fo dhcpctl_data_string_dereference
+.Fa "dhcpctl_data_string *"
+.Fa "const char *"
+.Fa "int"
+.Fc
+.Sh DESCRIPTION
+The dhcpctl set of functions provide an API that can be used to communicate
+with and manipulate a running ISC DHCP server. All functions return a value of
+.Dv isc_result_t .
+The return values reflects the result of operations to local data
+structures. If an operation fails on the server for any reason, then the error
+result will be returned through the
+second parameter of the
+.Fn dhcpctl_wait_for_completion
+call.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_initialize
+sets up the data structures the library needs to do its work. This function
+must be called once before any other.
+.Pp
+.Fn dhcpctl_connect
+opens a connection to the DHCP server at the given host and port. If an
+authenticator has been created for the connection, then it is given as the 4th
+argument. On a successful return the address pointed at by the first
+argument will have a new connection object assigned to it.
+.Pp
+For example:
+.Bd -literal -offset indent
+s = dhcpctl_connect(&cxn, "127.0.0.1", 7911, NULL);
+.Ed
+.Pp
+connects to the DHCP server on the localhost via port 7911 (the standard
+OMAPI port). No authentication is used for the connection.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_wait_for_completion
+flushes a pending message to the server and waits for the response. The result
+of the request as processed on the server is returned via the second
+parameter.
+.Bd -literal -offset indent
+s = dhcpctl_wait_for_completion(cxn, &wv);
+if (s != ISC_R_SUCCESS)
+ local_failure(s);
+else if (wv != ISC_R_SUCCESS)
+ server_failure(wc);
+.Ed
+.Pp
+The call to
+.Fn dhcpctl_wait_for_completion
+won't return until the remote message processing completes or the connection
+to the server is lost.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_get_value
+extracts a value of an attribute from the handle. The value can be of any
+length and is treated as a sequence of bytes. The handle must have been
+created first with
+.Fn dhcpctl_new_object
+and opened with
+.Fn dhcpctl_open_object .
+The value is returned via the parameter named
+.Dq value .
+The last parameter is the name of attribute to retrieve.
+.Bd -literal -offset indent
+dhcpctl_data_string value = NULL;
+dhcpctl_handle lease;
+time_t thetime;
+
+s = dhcpctl_get_value (&value, lease, "ends");
+assert(s == ISC_R_SUCCESS && value->len == sizeof(thetime));
+memcpy(&thetime, value->value, value->len);
+.Ed
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_get_boolean
+extracts a boolean valued attribute from the object handle.
+.\"
+.\"
+.\"
+.Pp
+The
+.Fn dhcpctl_set_value ,
+.Fn dhcpctl_set_string_value ,
+.Fn dhcpctl_set_boolean_value ,
+and
+.Fn dhcpctl_set_int_value
+functions all set a value on the object handle.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_object_update
+function queues a request for
+all the changes made to the object handle be be sent to the remote
+for processing. The changes made to the atributes on the handle will be
+applied to remote object if permitted.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_object_refresh
+queues up a request for a fresh copy of all the attribute values to be sent
+from the remote to
+refresh the values in the local object handle.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_object_remove
+queues a request for the removal on the server of the object referenced by the
+handle.
+.\"
+.\"
+.\"
+.Pp
+The
+.Fn dhcpctl_set_callback
+function sets up a user-defined function to be called when an event completes
+on the given object handle. This is needed for asynchronous handling of
+events, versus the synchronous handling given by
+.Fn dhcpctl_wait_for_completion .
+When the function is called the first parameter is the object the event
+arrived for, the second is the status of the message that was processed, the
+third is the same value as the second parameter given to
+.Fn dhcpctl_set_callback .
+.\"
+.\"
+.\"
+.Pp
+The
+.Fn dhcpctl_new_authenticator
+creates a new authenticator object to be used for signing the messages
+that cross over the network. The
+.Dq name ,
+.Dq algorithm ,
+and
+.Dq secret
+values must all match what the server uses and are defined in its
+configuration file. The created object is returned through the first parameter
+and must be used as the 4th parameter to
+.Fn dhcpctl_connect .
+Note that the 'secret' value must not be base64 encoded, which is different
+from how the value appears in the dhcpd.conf file.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_new_object
+creates a local handle for an object on the the server. The
+.Dq object_type
+parameter is the ascii name of the type of object being accessed. e.g.
+.Qq lease .
+This function only sets up local data structures, it does not queue any
+messages
+to be sent to the remote side,
+.Fn dhcpctl_open_object
+does that.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_open_object
+builds and queues the request to the remote side. This function is used with
+handle created via
+.Fn dhcpctl_new_object .
+The flags argument is a bit mask with the following values available for
+setting:
+.Bl -tag -offset indent -width 20
+.It DHCPCTL_CREATE
+if the object does not exist then the remote will create it
+.It DHCPCTL_UPDATE
+update the object on the remote side using the
+attributes already set in the handle.
+.It DHCPCTL_EXCL
+return and error if the object exists and DHCPCTL_CREATE
+was also specified
+.El
+.\"
+.\"
+.\"
+.Pp
+The
+.Fn omapi_data_string_new
+function allocates a new
+.Ft dhcpctl_data_string
+object. The data string will be large enough to hold
+.Dq length
+bytes of data. The
+.Dq file
+and
+.Dq lineno
+arguments are the source file location the call is made from, typically by
+using the
+.Dv __FILE__
+and
+.Dv __LINE__
+macros or the
+.Dv MDL
+macro defined in
+.
+.\"
+.\"
+.\"
+.Pp
+.Fn dhcpctl_data_string_dereference
+deallocates a data string created by
+.Fn omapi_data_string_new .
+The memory for the object won't be freed until the last reference is
+released.
+.Sh EXAMPLES
+.Pp
+The following program will connect to the DHCP server running on the local
+host and will get the details of the existing lease for IP address
+10.0.0.101. It will then print out the time the lease is due to expire. Note
+that most error checking has been ommitted for brevity.
+.Bd -literal -offset indent
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "omapip/result.h"
+#include "dhcpctl.h"
+
+int main (int argc, char **argv) {
+ dhcpctl_data_string ipaddrstring = NULL;
+ dhcpctl_data_string value = NULL;
+ dhcpctl_handle connection = NULL;
+ dhcpctl_handle lease = NULL;
+ isc_result_t waitstatus;
+ struct in_addr convaddr;
+ time_t thetime;
+
+ dhcpctl_initialize ();
+
+ dhcpctl_connect (&connection, "127.0.0.1",
+ 7911, 0);
+
+ dhcpctl_new_object (&lease, connection,
+ "lease");
+
+ memset (&ipaddrstring, 0, sizeof
+ ipaddrstring);
+
+ inet_pton(AF_INET, "10.0.0.101",
+ &convaddr);
+
+ omapi_data_string_new (&ipaddrstring,
+ 4, MDL);
+ memcpy(ipaddrstring->value, &convaddr.s_addr, 4);
+
+ dhcpctl_set_value (lease, ipaddrstring,
+ "ip-address");
+
+ dhcpctl_open_object (lease, connection, 0);
+
+ dhcpctl_wait_for_completion (lease,
+ &waitstatus);
+ if (waitstatus != ISC_R_SUCCESS) {
+ /* server not authoritative */
+ exit (0);
+ }
+
+ dhcpctl_data_string_dereference(&ipaddrstring,
+ MDL);
+
+ dhcpctl_get_value (&value, lease, "ends");
+
+ memcpy(&thetime, value->value, value->len);
+
+ dhcpctl_data_string_dereference(&value, MDL);
+
+ fprintf (stdout, "ending time is %s",
+ ctime(&thetime));
+}
+.Ed
+.Sh SEE ALSO
+omapi(3), omshell(1), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5).
+.Sh AUTHOR
+.Em dhcpctl
+was written by Ted Lemon of Nominum, Inc.
+This preliminary documentation was written by James Brister of Nominum, Inc.
diff --git a/dhcpctl/dhcpctl.c b/dhcpctl/dhcpctl.c
new file mode 100644
index 0000000..5ec617a
--- /dev/null
+++ b/dhcpctl/dhcpctl.c
@@ -0,0 +1,590 @@
+/* dhcpctl.c
+
+ Subroutines providing general support for objects. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include "dhcpctl.h"
+
+omapi_object_type_t *dhcpctl_callback_type;
+omapi_object_type_t *dhcpctl_remote_type;
+
+/* dhcpctl_initialize ()
+
+ Must be called before any other dhcpctl function. */
+
+dhcpctl_status dhcpctl_initialize ()
+{
+ isc_result_t status;
+
+ /* Set up the isc and dns library managers */
+ status = dhcp_context_create();
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_init();
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&dhcpctl_callback_type,
+ "dhcpctl-callback",
+ dhcpctl_callback_set_value,
+ dhcpctl_callback_get_value,
+ dhcpctl_callback_destroy,
+ dhcpctl_callback_signal_handler,
+ dhcpctl_callback_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof
+ (dhcpctl_callback_object_t), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&dhcpctl_remote_type,
+ "dhcpctl-remote",
+ dhcpctl_remote_set_value,
+ dhcpctl_remote_get_value,
+ dhcpctl_remote_destroy,
+ dhcpctl_remote_signal_handler,
+ dhcpctl_remote_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (dhcpctl_remote_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ return ISC_R_SUCCESS;
+}
+
+/* dhcpctl_connect
+
+ synchronous
+ returns nonzero status code if it didn't connect, zero otherwise
+ stores connection handle through connection, which can be used
+ for subsequent access to the specified server.
+ server_name is the name of the server, and port is the TCP
+ port on which it is listening.
+ authinfo is the handle to an object containing authentication
+ information. */
+
+dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection,
+ const char *server_name, int port,
+ dhcpctl_handle authinfo)
+{
+ isc_result_t status;
+
+ status = omapi_generic_new (connection, MDL);
+ if (status != ISC_R_SUCCESS) {
+ return status;
+ }
+
+ status = omapi_protocol_connect (*connection, server_name,
+ (unsigned)port, authinfo);
+ if (status == ISC_R_SUCCESS)
+ return status;
+ if (status != DHCP_R_INCOMPLETE) {
+ omapi_object_dereference (connection, MDL);
+ return status;
+ }
+
+ status = omapi_wait_for_completion (*connection, 0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (connection, MDL);
+ return status;
+ }
+
+ return status;
+}
+
+/* dhcpctl_wait_for_completion
+
+ synchronous
+ returns zero if the callback completes, a nonzero status if
+ there was some problem relating to the wait operation. The
+ status of the queued request will be stored through s, and
+ will also be either zero for success or nonzero for some kind
+ of failure. Never returns until completion or until the
+ connection to the server is lost. This performs the same
+ function as dhcpctl_set_callback and the subsequent callback,
+ for programs that want to do inline execution instead of using
+ callbacks. */
+
+dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h,
+ dhcpctl_status *s)
+{
+ isc_result_t status;
+ status = omapi_wait_for_completion (h, 0);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (h -> type == dhcpctl_remote_type)
+ *s = ((dhcpctl_remote_object_t *)h) -> waitstatus;
+ return ISC_R_SUCCESS;
+}
+
+/* dhcpctl_get_value
+
+ synchronous
+ returns zero if the call succeeded, a nonzero status code if
+ it didn't.
+ result is the address of an empty data string (initialized
+ with bzero or cleared with data_string_forget). On
+ successful completion, the addressed data string will contain
+ the value that was fetched.
+ dhcpctl_handle refers to some dhcpctl item
+ value_name refers to some value related to that item - e.g.,
+ for a handle associated with a completed host lookup, value
+ could be one of "hardware-address", "dhcp-client-identifier",
+ "known" or "client-hostname". */
+
+dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *result,
+ dhcpctl_handle h, const char *value_name)
+{
+ isc_result_t status;
+ omapi_value_t *tv = (omapi_value_t *)0;
+ unsigned len;
+ int ip;
+
+ status = omapi_get_value_str (h, (omapi_object_t *)0, value_name, &tv);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ switch (tv -> value -> type) {
+ case omapi_datatype_int:
+ len = sizeof (int);
+ break;
+
+ case omapi_datatype_string:
+ case omapi_datatype_data:
+ len = tv -> value -> u.buffer.len;
+ break;
+
+ case omapi_datatype_object:
+ len = sizeof (omapi_handle_t);
+ break;
+
+ default:
+ omapi_typed_data_dereference (&tv -> value, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+ status = omapi_data_string_new (result, len, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_typed_data_dereference (&tv -> value, MDL);
+ return status;
+ }
+
+ switch (tv -> value -> type) {
+ case omapi_datatype_int:
+ ip = htonl (tv -> value -> u.integer);
+ memcpy ((*result) -> value, &ip, sizeof ip);
+ break;
+
+ case omapi_datatype_string:
+ case omapi_datatype_data:
+ memcpy ((*result) -> value,
+ tv -> value -> u.buffer.value,
+ tv -> value -> u.buffer.len);
+ break;
+
+ case omapi_datatype_object:
+ ip = htonl (tv -> value -> u.object -> handle);
+ memcpy ((*result) -> value, &ip, sizeof ip);
+ break;
+ }
+
+ omapi_value_dereference (&tv, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/* dhcpctl_get_boolean
+
+ like dhcpctl_get_value, but more convenient for boolean
+ values, since no data_string needs to be dealt with. */
+
+dhcpctl_status dhcpctl_get_boolean (int *result,
+ dhcpctl_handle h, const char *value_name)
+{
+ isc_result_t status;
+ dhcpctl_data_string data = (dhcpctl_data_string)0;
+ int rv;
+
+ status = dhcpctl_get_value (&data, h, value_name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (data -> len != sizeof rv) {
+ omapi_data_string_dereference (&data, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+ memcpy (&rv, data -> value, sizeof rv);
+ *result = ntohl (rv);
+ return ISC_R_SUCCESS;
+}
+
+/* dhcpctl_set_value
+
+ Sets a value on an object referred to by a dhcpctl_handle.
+ The opposite of dhcpctl_get_value. Does not update the
+ server - just sets the value on the handle. */
+
+dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, dhcpctl_data_string value,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&name, strlen (value_name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, strlen (value_name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_data,
+ value -> len);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+ }
+ memcpy (tv -> u.buffer.value, value -> value, value -> len);
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
+ omapi_data_string_dereference (&name, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+/* dhcpctl_set_string_value
+
+ Sets a NUL-terminated ASCII value on an object referred to by
+ a dhcpctl_handle. like dhcpctl_set_value, but saves the
+ trouble of creating a data_string for a NUL-terminated string.
+ Does not update the server - just sets the value on the handle. */
+
+dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, const char *value,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&name, strlen (value_name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, strlen (value_name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
+ omapi_data_string_dereference (&name, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+/* dhcpctl_set_buffer_value
+
+ Sets a value on an object referred to by a dhcpctl_handle. like
+ dhcpctl_set_value, but saves the trouble of creating a data_string
+ for string for which we have a buffer and length. Does not update
+ the server - just sets the value on the handle. */
+
+dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle h,
+ const char *value, unsigned len,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+ unsigned ll;
+
+ ll = strlen (value_name);
+ status = omapi_data_string_new (&name, ll, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, ll);
+
+ status = omapi_typed_data_new (MDL, &tv,
+ omapi_datatype_data, len, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+ }
+ memcpy (tv -> u.buffer.value, value, len);
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
+ omapi_data_string_dereference (&name, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+/* dhcpctl_set_null_value
+
+ Sets a null value on an object referred to by a dhcpctl_handle. */
+
+dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle h,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+ unsigned ll;
+
+ ll = strlen (value_name);
+ status = omapi_data_string_new (&name, ll, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, ll);
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name,
+ (omapi_typed_data_t *)0);
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+}
+
+/* dhcpctl_set_boolean_value
+
+ Sets a boolean value on an object - like dhcpctl_set_value,
+ only more convenient for booleans. */
+
+dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle h, int value,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&name, strlen (value_name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, strlen (value_name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
+ omapi_data_string_dereference (&name, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+/* dhcpctl_set_int_value
+
+ Sets a boolean value on an object - like dhcpctl_set_value,
+ only more convenient for booleans. */
+
+dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle h, int value,
+ const char *value_name)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *name = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&name, strlen (value_name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (name -> value, value_name, strlen (value_name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&name, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, (omapi_object_t *)0, name, tv);
+ omapi_data_string_dereference (&name, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+/* dhcpctl_object_update
+
+ Queues an update on the object referenced by the handle (there
+ can't be any other work in progress on the handle). An
+ update means local parameters will be sent to the server. */
+
+dhcpctl_status dhcpctl_object_update (dhcpctl_handle connection,
+ dhcpctl_handle h)
+{
+ isc_result_t status;
+ omapi_object_t *message = (omapi_object_t *)0;
+ dhcpctl_remote_object_t *ro;
+
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ ro = (dhcpctl_remote_object_t *)h;
+
+ status = omapi_message_new (&message, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_int_value (message, (omapi_object_t *)0,
+ "op", OMAPI_OP_UPDATE);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_object_value (message, (omapi_object_t *)0,
+ "object", h);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_int_value (message, (omapi_object_t *)0, "handle",
+ (int)(ro -> remote_handle));
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ omapi_message_register (message);
+ status = omapi_protocol_send_message (connection -> outer,
+ (omapi_object_t *)0,
+ message, (omapi_object_t *)0);
+ omapi_object_dereference (&message, MDL);
+ return status;
+}
+
+/* Requests a refresh on the object referenced by the handle (there
+ can't be any other work in progress on the handle). A
+ refresh means local parameters are updated from the server. */
+
+dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle connection,
+ dhcpctl_handle h)
+{
+ isc_result_t status;
+ omapi_object_t *message = (omapi_object_t *)0;
+ dhcpctl_remote_object_t *ro;
+
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ ro = (dhcpctl_remote_object_t *)h;
+
+ status = omapi_message_new (&message, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_int_value (message, (omapi_object_t *)0,
+ "op", OMAPI_OP_REFRESH);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_int_value (message, (omapi_object_t *)0,
+ "handle", (int)(ro -> remote_handle));
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ omapi_message_register (message);
+ status = omapi_protocol_send_message (connection -> outer,
+ (omapi_object_t *)0,
+ message, (omapi_object_t *)0);
+
+ /* We don't want to send the contents of the object down the
+ wire, but we do need to reference it so that we know what
+ to do with the update. */
+ status = omapi_set_object_value (message, (omapi_object_t *)0,
+ "object", h);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ omapi_object_dereference (&message, MDL);
+ return status;
+}
+
+/* Requests the removal of the object referenced by the handle (there
+ can't be any other work in progress on the handle). A
+ removal means that all searchable references to the object on the
+ server are deleted. */
+
+dhcpctl_status dhcpctl_object_remove (dhcpctl_handle connection,
+ dhcpctl_handle h)
+{
+ isc_result_t status;
+ omapi_object_t *message = (omapi_object_t *)0;
+ dhcpctl_remote_object_t *ro;
+
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ ro = (dhcpctl_remote_object_t *)h;
+
+ status = omapi_message_new (&message, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_int_value (message, (omapi_object_t *)0,
+ "op", OMAPI_OP_DELETE);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_int_value (message, (omapi_object_t *)0, "handle",
+ (int)(ro -> remote_handle));
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_object_value (message, (omapi_object_t *)0,
+ "notify-object", h);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ omapi_message_register (message);
+ status = omapi_protocol_send_message (connection -> outer,
+ (omapi_object_t *)0,
+ message, (omapi_object_t *)0);
+ omapi_object_dereference (&message, MDL);
+ return status;
+}
+
+isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *vp,
+ const char *file, int line)
+{
+ return omapi_data_string_dereference (vp, file, line);
+}
diff --git a/dhcpctl/dhcpctl.h b/dhcpctl/dhcpctl.h
new file mode 100644
index 0000000..855985b
--- /dev/null
+++ b/dhcpctl/dhcpctl.h
@@ -0,0 +1,125 @@
+/* $Id: dhcpctl.h,v 1.17.24.1 2009-11-20 01:49:01 sar Exp $
+
+ Subroutines providing general support for objects. */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef _DHCPCTL_H_
+#define _DHCPCTL_H_
+
+#include <omapip/omapip.h>
+
+typedef isc_result_t dhcpctl_status;
+typedef omapi_object_t *dhcpctl_handle;
+typedef omapi_data_string_t *dhcpctl_data_string;
+
+#define dhcpctl_null_handle ((dhcpctl_handle) 0)
+
+#define DHCPCTL_CREATE OMAPI_CREATE
+#define DHCPCTL_UPDATE OMAPI_UPDATE
+#define DHCPCTL_EXCL OMAPI_EXCL
+
+typedef struct {
+ OMAPI_OBJECT_PREAMBLE;
+ omapi_object_t *object;
+ void *data;
+ void (*callback) (dhcpctl_handle, dhcpctl_status, void *);
+} dhcpctl_callback_object_t;
+
+typedef struct {
+ OMAPI_OBJECT_PREAMBLE;
+ omapi_typed_data_t *rtype;
+ isc_result_t waitstatus;
+ omapi_typed_data_t *message;
+ omapi_handle_t remote_handle;
+} dhcpctl_remote_object_t;
+
+extern omapi_object_type_t *dhcpctl_callback_type;
+extern omapi_object_type_t *dhcpctl_remote_type;
+
+dhcpctl_status dhcpctl_initialize (void);
+dhcpctl_status dhcpctl_connect (dhcpctl_handle *,
+ const char *, int, dhcpctl_handle);
+dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle, dhcpctl_status *);
+dhcpctl_status dhcpctl_get_value (dhcpctl_data_string *,
+ dhcpctl_handle, const char *);
+dhcpctl_status dhcpctl_get_boolean (int *, dhcpctl_handle, const char *);
+dhcpctl_status dhcpctl_set_value (dhcpctl_handle,
+ dhcpctl_data_string, const char *);
+dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle, const char *,
+ const char *);
+dhcpctl_status dhcpctl_set_data_value (dhcpctl_handle,
+ const char *, unsigned, const char *);
+dhcpctl_status dhcpctl_set_null_value (dhcpctl_handle, const char *);
+dhcpctl_status dhcpctl_set_boolean_value (dhcpctl_handle, int, const char *);
+dhcpctl_status dhcpctl_set_int_value (dhcpctl_handle, int, const char *);
+dhcpctl_status dhcpctl_object_update (dhcpctl_handle, dhcpctl_handle);
+dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle, dhcpctl_handle);
+dhcpctl_status dhcpctl_object_remove (dhcpctl_handle, dhcpctl_handle);
+
+dhcpctl_status dhcpctl_set_callback (dhcpctl_handle, void *,
+ void (*) (dhcpctl_handle,
+ dhcpctl_status, void *));
+isc_result_t dhcpctl_callback_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcpctl_callback_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcpctl_callback_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcpctl_callback_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcpctl_callback_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+
+dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *,
+ const char *, const char *,
+ const unsigned char *, unsigned);
+
+dhcpctl_status dhcpctl_open_object (dhcpctl_handle, dhcpctl_handle, int);
+dhcpctl_status dhcpctl_new_object (dhcpctl_handle *,
+ dhcpctl_handle, const char *);
+isc_result_t dhcpctl_remote_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcpctl_remote_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcpctl_remote_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcpctl_data_string_dereference (dhcpctl_data_string *,
+ const char *, int);
+#endif /* _DHCPCTL_H_ */
diff --git a/dhcpctl/omshell.1 b/dhcpctl/omshell.1
new file mode 100644
index 0000000..91a8108
--- /dev/null
+++ b/dhcpctl/omshell.1
@@ -0,0 +1,334 @@
+.\" $Id: omshell.1,v 1.5.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2001-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.TH omshell 1
+.SH NAME
+omshell - OMAPI Command Shell
+.SH SYNOPSIS
+.B omshell
+.SH DESCRIPTION
+The OMAPI Command Shell, omshell, provides an interactive way to connect to,
+query, and possibly change, the ISC DHCP Server's state via OMAPI, the Object
+Management API. By using OMAPI and omshell, you do not have to stop, make
+changes, and then restart the DHCP server, but can make the changes
+while the server is running. Omshell provides a way of accessing
+OMAPI.
+.PP
+OMAPI is simply a communications mechanism that allows you to
+manipulate objects. In order to actually \fIuse\fR omshell, you
+.I must
+understand what objects are available and how to use them.
+Documentation for OMAPI objects can be found in the documentation for
+the server that provides them - for example, in the \fBdhcpd(1)\fR
+manual page and the \fBdhclient(1)\fR manual page.
+.SH CONTRIBUTIONS
+.PP
+This software is free software. At various times its development has
+been underwritten by various organizations, including the ISC and
+Vixie Enterprises. The development of 3.0 has been funded almost
+entirely by Nominum, Inc.
+.PP
+At this point development is being shepherded by Ted Lemon, and hosted
+by the ISC, but the future of this project depends on you. If you
+have features you want, please consider implementing them.
+.SH LOCAL AND REMOTE OBJECTS
+.PP
+Throughout this document, there are references to local and remote objects.
+Local objects are ones created in omshell with the \fBnew\fR command. Remote
+objects are ones on the server: leases, hosts, and groups that the DHCP
+server knows about. Local and remote objects are associated together to
+enable viewing and modification of object attributes. Also, new remote
+objects can be created to match local objects.
+.SH OPENING A CONNECTION
+.PP
+omshell is started from the command line. Once omshell is started, there are
+several commands that can be issued:
+.PP
+.B server \fIaddress\fR
+.RS 0.5i
+where address is the IP address of the DHCP server to connect to. If this is
+not specified, the default server is 127.0.0.1 (localhost).
+.RE
+.PP
+.B port \fInumber\fR
+.RS 0.5i
+where number is the port that OMAPI listens on. By default, this is 7911.
+.RE
+.PP
+.B key \fIname secret\fR
+.RS 0.5i
+This specifies the TSIG key to use to authenticate the OMAPI transactions.
+\fIname\fR is the name of a key defined in \fIdhcpd.conf\fR with the
+\fBomapi-key\fR statement. The \fIsecret\fR is the secret key generated from
+\fBdnssec-keygen\fR or another key generation program.
+.RE
+.PP
+.B connect
+.RS 0.5i
+This starts the OMAPI connection to the server as specified by the \fIserver\fR
+statement.
+.SH CREATING LOCAL OBJECTS
+.PP
+Any object defined in OMAPI can be created, queried, and/or modified. The
+object types available to OMAPI are defined in \fBdhcpd(8)\fR and
+\fBdhclient(8)\fR. When using omshell, objects are first defined locally,
+manipulated as desired, and then associated with an object on the server.
+Only one object can be manipulated at a time. To create a local object, use
+.PP
+.B new \fIobject-type\fR
+.RS 0.5i
+\fIobject-type\fR is one of group, host, or lease.
+.RE
+.PP
+At this point, you now have an object that you can set properties on. For
+example, if a new lease object was created with \fInew lease\fR, any of a
+lease's attributes can be set as follows:
+.PP
+.B set \fIattribute-name = value\fR
+.RS 0.5i
+\fBAttribute\fR names are defined in \fBdhcpd(8)\fR and \fBdhclient(8)\fR.
+Values should be quoted if they are strings. So, to set a lease's IP address,
+you would do the following:
+\fB set ip-address = 192.168.4.50\fR
+.SH ASSOCIATING LOCAL AND REMOTE OBJECTS
+.PP
+At this point, you can query the server for information about this lease, by
+.PP
+.B open
+.PP
+Now, the local lease object you created and set the IP address for is associated
+with the corresponding lease object on the DHCP server. All of the lease
+attributes from the DHCP server are now also the attributes on the local
+object, and will be shown in omshell.
+.SH VIEWING A REMOTE OBJECT
+.PP
+To query a lease of address 192.168.4.50, and find out its attributes, after
+connecting to the server, take the following steps:
+.PP
+.B new "lease"
+.PP
+This creates a new local lease object.
+.PP
+.B set ip-address = 192.168.4.50
+.PP
+This sets the \fIlocal\fR object's IP address to be 192.168.4.50
+.PP
+.B open
+.PP
+Now, if a lease with that IP address exists, you will see all the information
+the DHCP server has about that particular lease. Any data that isn't readily
+printable text will show up in colon-separated hexadecimal values. In this
+example, output back from the server for the entire transaction might look
+like this:
+.nf
+.sp 1
+> new "lease"
+obj: lease
+> set ip-address = 192.168.4.50
+obj: lease
+ip-address = c0:a8:04:32
+> open
+obj: lease
+ip-address = c0:a8:04:32
+state = 00:00:00:02
+dhcp-client-identifier = 01:00:10:a4:b2:36:2c
+client-hostname = "wendelina"
+subnet = 00:00:00:06
+pool = 00:00:00:07
+hardware-address = 00:10:a4:b2:36:2c
+hardware-type = 00:00:00:01
+ends = dc:d9:0d:3b
+starts = 5c:9f:04:3b
+tstp = 00:00:00:00
+tsfp = 00:00:00:00
+cltt = 00:00:00:00
+.fi
+.PP
+As you can see here, the IP address is represented in hexadecimal, as are the
+starting and ending times of the lease.
+.SH MODIFYING A REMOTE OBJECT
+.PP
+Attributes of remote objects are updated by using the \fBset\fR command as
+before, and then issuing an \fBupdate\fR command. The \fBset\fR command sets
+the attributes on the current local object, and the \fBupdate\fR command
+pushes those changes out to the server.
+.PP
+Continuing with the previous example, if a \fBset client-hostname =
+"something-else"\fR was issued, followed by an \fBupdate\fR command, the
+output would look about like this:
+.nf
+.sp 1
+> set client-hostname = "something-else"
+obj: lease
+ip-address = c0:a8:04:32
+state = 00:00:00:02
+dhcp-client-identifier = 01:00:10:a4:b2:36:2c
+client-hostname = "something-else"
+subnet = 00:00:00:06
+pool = 00:00:00:07
+hardware-address = 00:10:a4:b2:36:2c
+hardware-type = 00:00:00:01
+ends = dc:d9:0d:3b
+starts = 5c:9f:04:3b
+tstp = 00:00:00:00
+tsfp = 00:00:00:00
+cltt = 00:00:00:00
+> update
+obj: lease
+ip-address = c0:a8:04:32
+state = 00:00:00:02
+dhcp-client-identifier = 01:00:10:a4:b2:36:2c
+client-hostname = "something-else"
+subnet = 00:00:00:06
+pool = 00:00:00:07
+hardware-address = 00:10:a4:b2:36:2c
+hardware-type = 00:00:00:01
+ends = dc:d9:0d:3b
+starts = 5c:9f:04:3b
+tstp = 00:00:00:00
+tsfp = 00:00:00:00
+cltt = 00:00:00:00
+.fi
+.SH NEW REMOTE OBJECTS
+.PP
+New remote objects are created much in the same way that existing server
+objects are modified. Create a local object using \fBnew\fR, set the
+attributes as you'd wish them to be, and then create the remote object with
+the same properties by using
+.PP
+.B create
+.PP
+Now a new object exists on the DHCP server which matches the properties that
+you gave your local object. Objects created via OMAPI are saved into the
+dhcpd.leases file.
+.PP
+For example, if a new host with the IP address of 192.168.4.40 needs to be
+created it would be done as follows:
+.nf
+.sp 1
+> new host
+obj: host
+> set name = "some-host"
+obj: host
+name = "some-host"
+> set hardware-address = 00:80:c7:84:b1:94
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+> set hardware-type = 1
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 1
+> set ip-address = 192.168.4.40
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 1
+ip-address = c0:a8:04:28
+> create
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 00:00:00:01
+ip-address = c0:a8:04:28
+>
+.fi
+.PP
+Your dhcpd.leases file would then have an entry like this in it:
+.nf
+.sp 1
+host some-host {
+ dynamic;
+ hardware ethernet 00:80:c7:84:b1:94;
+ fixed-address 192.168.4.40;
+}
+.fi
+.PP
+The \fIdynamic;\fR line is to denote that this host entry did not come from
+dhcpd.conf, but was created dynamically via OMAPI.
+.SH RESETTING ATTRIBUTES
+.PP
+If you want to remove an attribute from an object, you can do this with the
+\fBunset\fR command. Once you have unset an attribute, you must use the
+\fBupdate\fR command to update the remote object. So, if the host "some-host"
+from the previous example will not have a static IP address anymore, the
+commands in omshell would look like this:
+.nf
+.sp 1
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 00:00:00:01
+ip-address = c0:a8:04:28
+> unset ip-address
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 00:00:00:01
+ip-address = <null>
+>
+.fi
+.SH REFRESHING OBJECTS
+.PP
+A local object may be refreshed with the current remote object properties
+using the \fBrefresh\fR command. This is useful for object that change
+periodically, like leases, to see if they have been updated. This isn't
+particularly useful for hosts.
+.SH DELETING OBJECTS
+.PP
+Any remote object that can be created can also be destroyed. This is done by
+creating a new local object, setting attributes, associating the local and
+remote object using \fBopen\fR, and then using the \fBremove\fR command.
+If the host "some-host" from before was created in error, this could be
+corrected as follows:
+.nf
+.sp 1
+obj: host
+name = "some-host"
+hardware-address = 00:80:c7:84:b1:94
+hardware-type = 00:00:00:01
+ip-address = c0:a8:04:28
+> remove
+obj: <null>
+>
+.fi
+.SH HELP
+.PP
+The \fBhelp\fR command will print out all of the commands available in
+omshell, with some syntax pointers.
+.SH SEE ALSO
+dhcpctl(3), omapi(3), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5).
+.SH AUTHOR
+.B omshell
+was written by Ted Lemon of Nominum, Inc. Information about Nominum
+can be found at
+.B http://www.nominum.com.
+This preliminary documentation was written by Wendy Verschoor of Nominum,
+Inc., while she was testing omshell.
diff --git a/dhcpctl/omshell.c b/dhcpctl/omshell.c
new file mode 100644
index 0000000..bb489d8
--- /dev/null
+++ b/dhcpctl/omshell.c
@@ -0,0 +1,734 @@
+/* omshell.c
+
+ Examine and modify omapi objects. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+//#include "result.h"
+#include <syslog.h>
+#include "dhcpctl.h"
+#include "dhcpd.h"
+
+/* Fixups */
+isc_result_t find_class (struct class **c, const char *n, const char *f, int l)
+{
+ return 0;
+}
+int parse_allow_deny (struct option_cache **oc, struct parse *cfile, int flag)
+{
+ return 0;
+}
+void dhcp (struct packet *packet) { }
+void bootp (struct packet *packet) { }
+
+#ifdef DHCPv6
+/* XXX: should we warn or something here? */
+void dhcpv6(struct packet *packet) { }
+#endif /* DHCPv6 */
+
+int check_collection (struct packet *p, struct lease *l, struct collection *c)
+{
+ return 0;
+}
+void classify (struct packet *packet, struct class *class) { }
+
+static void usage (char *s) {
+ fprintf (stderr, "Usage: %s\n", s);
+ exit (1);
+}
+
+static void check (isc_result_t status, const char *func) {
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "%s: %s\n", func, isc_result_totext (status));
+ exit (1);
+ }
+}
+
+int
+main(int argc, char **argv) {
+ isc_result_t status, waitstatus;
+ dhcpctl_handle connection;
+ dhcpctl_handle authenticator;
+ dhcpctl_handle oh;
+ struct data_string secret;
+ const char *name = 0, *algorithm = "hmac-md5";
+ int i;
+ int port = 7911;
+ const char *server = "127.0.0.1";
+ struct parse *cfile;
+ enum dhcp_token token;
+ const char *val;
+ char *s;
+ char buf[1024];
+ char s1[1024];
+ int connected = 0;
+ char hex_buf[1025];
+
+ for (i = 1; i < argc; i++) {
+ usage(argv[0]);
+ }
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog ("omshell", LOG_NDELAY, DHCPD_LOG_FACILITY);
+ status = dhcpctl_initialize ();
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_initialize: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+
+ memset (&oh, 0, sizeof oh);
+
+ do {
+ if (!connected) {
+ } else if (oh == NULL) {
+ printf ("obj: <null>\n");
+ } else {
+ dhcpctl_remote_object_t *r = (dhcpctl_remote_object_t *)oh;
+ omapi_generic_object_t *g =
+ (omapi_generic_object_t *)(r -> inner);
+
+ printf ("obj: ");
+
+ if (r -> rtype -> type != omapi_datatype_string) {
+ printf ("?\n");
+ } else {
+ printf ("%.*s\n",
+ (int)(r -> rtype -> u . buffer . len),
+ r -> rtype -> u . buffer . value);
+ }
+
+ for (i = 0; i < g -> nvalues; i++) {
+ omapi_value_t *v = g -> values [i];
+
+ if (!g -> values [i])
+ continue;
+
+ printf ("%.*s = ", (int)v -> name -> len,
+ v -> name -> value);
+
+ if (!v -> value) {
+ printf ("<null>\n");
+ continue;
+ }
+ switch (v -> value -> type) {
+ case omapi_datatype_int:
+ printf ("%d\n",
+ v -> value -> u . integer);
+ break;
+
+ case omapi_datatype_string:
+ printf ("\"%.*s\"\n",
+ (int) v -> value -> u.buffer.len,
+ v -> value -> u.buffer.value);
+ break;
+
+ case omapi_datatype_data:
+ print_hex_or_string(v->value->u.buffer.len,
+ v->value->u.buffer.value,
+ sizeof(hex_buf), hex_buf);
+ printf("%s\n", hex_buf);
+ break;
+
+ case omapi_datatype_object:
+ printf ("<obj>\n");
+ break;
+ }
+ }
+ }
+
+ fputs ("> ", stdout);
+ fflush (stdout);
+ if (fgets (buf, sizeof(buf), stdin) == NULL)
+ break;
+
+ status = new_parse (&cfile, -1, buf, strlen(buf), "<STDIN>", 1);
+ check(status, "new_parse()");
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ default:
+ parse_warn (cfile, "unknown token: %s", val);
+ skip_to_semi (cfile);
+ break;
+
+ case END_OF_FILE:
+ case ENDOFLINE: /* EOL: */
+ break;
+
+ case TOKEN_HELP:
+ case QUESTIONMARK: /* '?': */
+ printf ("Commands:\n");
+ printf (" port <server omapi port>\n");
+ printf (" server <server address>\n");
+ printf (" key <key name> <key value>\n");
+ printf (" connect\n");
+ printf (" new <object-type>\n");
+ printf (" set <name> = <value>\n");
+ printf (" create\n");
+ printf (" open\n");
+ printf (" update\n");
+ printf (" unset <name>\n");
+ printf (" refresh\n");
+ printf (" remove\n");
+ skip_to_semi (cfile);
+ break;
+
+ case PORT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token)) {
+ struct servent *se;
+ se = getservbyname (val, "tcp");
+ if (se)
+ port = ntohs (se -> s_port);
+ else {
+ printf ("unknown service name: %s\n", val);
+ break;
+ }
+ } else if (token == NUMBER) {
+ port = atoi (val);
+ } else {
+ skip_to_semi (cfile);
+ printf ("usage: port <port>\n");
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: port <server>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case TOKEN_SERVER:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER) {
+ int alen = (sizeof buf) - 1;
+ int len;
+
+ s = &buf [0];
+ len = strlen (val);
+ if (len + 1 > alen) {
+ baddq:
+ printf ("usage: server <server>\n");
+ skip_to_semi (cfile);
+ break;
+ } strcpy (buf, val);
+ s += len;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != DOT)
+ goto baddq;
+ *s++ = '.';
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto baddq;
+ len = strlen (val);
+ if (len + 1 > alen)
+ goto baddq;
+ strcpy (s, val);
+ s += len;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != DOT)
+ goto baddq;
+ *s++ = '.';
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto baddq;
+ len = strlen (val);
+ if (len + 1 > alen)
+ goto baddq;
+ strcpy (s, val);
+ s += len;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != DOT)
+ goto baddq;
+ *s++ = '.';
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER)
+ goto baddq;
+ len = strlen (val);
+ if (len + 1 > alen)
+ goto baddq;
+ strcpy (s, val);
+ val = &buf [0];
+ } else if (is_identifier (token)) {
+ /* Use val directly. */
+ } else {
+ printf ("usage: server <server>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!server) {
+ printf ("no memory to store server name.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ strcpy (s, val);
+ server = s;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: server <server>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case KEY:
+ token = peek_token(&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ printf ("usage: key <name> <value>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!s) {
+ printf ("no memory for key name.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ strcpy (s, val);
+ } else {
+ s = parse_host_name(cfile);
+ if (s == NULL) {
+ printf ("usage: key <name> <value>\n");
+ skip_to_semi(cfile);
+ break;
+ }
+ }
+ name = s;
+
+ memset (&secret, 0, sizeof secret);
+ if (!parse_base64 (&secret, cfile)) {
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: key <name> <secret>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case CONNECT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: connect\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ authenticator = dhcpctl_null_handle;
+
+ if (name) {
+ status = dhcpctl_new_authenticator (&authenticator,
+ name, algorithm,
+ secret.data,
+ secret.len);
+
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr,
+ "Cannot create authenticator: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+ }
+
+ memset (&connection, 0, sizeof connection);
+ status = dhcpctl_connect (&connection,
+ server, port, authenticator);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "dhcpctl_connect: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+ connected = 1;
+ break;
+
+ case TOKEN_NEW:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if ((!is_identifier (token) && token != STRING)) {
+ printf ("usage: new <object-type>\n");
+ break;
+ }
+
+ if (oh) {
+ printf ("an object is already open.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ status = dhcpctl_new_object (&oh, connection, val);
+ if (status != ISC_R_SUCCESS) {
+ printf ("can't create object: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: new <object-type>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+
+ case TOKEN_CLOSE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: close\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!oh) {
+ printf ("not open.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+ omapi_object_dereference (&oh, MDL);
+
+ break;
+
+ case TOKEN_SET:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if ((!is_identifier (token) && token != STRING)) {
+ set_usage:
+ printf ("usage: set <name> = <value>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (oh == NULL) {
+ printf ("no open object.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ s1[0] = '\0';
+ strncat (s1, val, sizeof(s1)-1);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL)
+ goto set_usage;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case STRING:
+ dhcpctl_set_string_value (oh, val, s1);
+ token = next_token (&val, (unsigned *)0, cfile);
+ break;
+
+ case NUMBER:
+ strcpy (buf, val);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ /* Colon-separated hex list? */
+ if (token == COLON)
+ goto cshl;
+ else if (token == DOT) {
+ s = buf;
+ val = buf;
+ do {
+ int intval = atoi (val);
+ if (intval > 255) {
+ parse_warn (cfile,
+ "dotted octet > 255: %s",
+ val);
+ skip_to_semi (cfile);
+ goto badnum;
+ }
+ *s++ = intval;
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != DOT)
+ break;
+ /* DOT is zero. */
+ while ((token = next_token (&val,
+ (unsigned *)0, cfile)) == DOT)
+ *s++ = 0;
+ } while (token == NUMBER);
+ dhcpctl_set_data_value (oh, buf,
+ (unsigned)(s - buf),
+ s1);
+ break;
+ }
+ dhcpctl_set_int_value (oh, atoi (buf), s1);
+ token = next_token (&val, (unsigned *)0, cfile);
+ badnum:
+ break;
+
+ case NUMBER_OR_NAME:
+ strcpy (buf, val);
+ cshl:
+ s = buf;
+ val = buf;
+ do {
+ convert_num (cfile, (unsigned char *)s,
+ val, 16, 8);
+ ++s;
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != COLON)
+ break;
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ } while (token == NUMBER ||
+ token == NUMBER_OR_NAME);
+ dhcpctl_set_data_value (oh, buf,
+ (unsigned)(s - buf), s1);
+ break;
+
+ default:
+ printf ("invalid value.\n");
+ skip_to_semi (cfile);
+ }
+
+ if (token != END_OF_FILE && token != EOL)
+ goto set_usage;
+ break;
+
+ case UNSET:
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if ((!is_identifier (token) && token != STRING)) {
+ unset_usage:
+ printf ("usage: unset <name>\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!oh) {
+ printf ("no open object.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ s1[0] = '\0';
+ strncat (s1, val, sizeof(s1)-1);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL)
+ goto unset_usage;
+
+ dhcpctl_set_null_value (oh, s1);
+ break;
+
+
+ case TOKEN_CREATE:
+ case TOKEN_OPEN:
+ i = token;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: %s\n", val);
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!oh) {
+ printf ("you must make a new object first!\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (i == TOKEN_CREATE)
+ i = DHCPCTL_CREATE | DHCPCTL_EXCL;
+ else
+ i = 0;
+
+ status = dhcpctl_open_object (oh, connection, i);
+ if (status == ISC_R_SUCCESS)
+ status = dhcpctl_wait_for_completion
+ (oh, &waitstatus);
+ if (status == ISC_R_SUCCESS)
+ status = waitstatus;
+ if (status != ISC_R_SUCCESS) {
+ printf ("can't open object: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+
+ break;
+
+ case UPDATE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: %s\n", val);
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!oh) {
+ printf ("you haven't opened an object yet!\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ status = dhcpctl_object_update(connection, oh);
+ if (status == ISC_R_SUCCESS)
+ status = dhcpctl_wait_for_completion
+ (oh, &waitstatus);
+ if (status == ISC_R_SUCCESS)
+ status = waitstatus;
+ if (status != ISC_R_SUCCESS) {
+ printf ("can't update object: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+
+ break;
+
+ case REMOVE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: remove\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ break;
+ }
+
+ if (!oh) {
+ printf ("no object.\n");
+ break;
+ }
+
+ status = dhcpctl_object_remove(connection, oh);
+ if (status == ISC_R_SUCCESS)
+ status = dhcpctl_wait_for_completion
+ (oh, &waitstatus);
+ if (status == ISC_R_SUCCESS)
+ status = waitstatus;
+ if (status != ISC_R_SUCCESS) {
+ printf ("can't destroy object: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+ omapi_object_dereference (&oh, MDL);
+ break;
+
+ case REFRESH:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != END_OF_FILE && token != EOL) {
+ printf ("usage: refresh\n");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ if (!connected) {
+ printf ("not connected.\n");
+ break;
+ }
+
+ if (!oh) {
+ printf ("no object.\n");
+ break;
+ }
+
+ status = dhcpctl_object_refresh(connection, oh);
+ if (status == ISC_R_SUCCESS)
+ status = dhcpctl_wait_for_completion
+ (oh, &waitstatus);
+ if (status == ISC_R_SUCCESS)
+ status = waitstatus;
+ if (status != ISC_R_SUCCESS) {
+ printf ("can't refresh object: %s\n",
+ isc_result_totext (status));
+ break;
+ }
+
+ break;
+ }
+ end_parse (&cfile);
+ } while (1);
+
+ exit (0);
+}
+
+/* Sigh */
+isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
+ control_object_state_t newstate)
+{
+ return ISC_R_SUCCESS;
+}
diff --git a/dhcpctl/remote.c b/dhcpctl/remote.c
new file mode 100644
index 0000000..e4e870b
--- /dev/null
+++ b/dhcpctl/remote.c
@@ -0,0 +1,361 @@
+/* remote.c
+
+ The dhcpctl remote object. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include "dhcpctl.h"
+
+/* dhcpctl_new_authenticator
+
+ synchronous - creates an authenticator object.
+ returns nonzero status code if the object couldn't be created
+ stores handle to authenticator through h if successful, and returns zero.
+ name is the authenticator name (NUL-terminated string).
+ algorithm is the NUL-terminated string name of the algorithm to use
+ (currently, only "hmac-md5" is supported).
+ secret and secret_len is the key secret. */
+
+dhcpctl_status dhcpctl_new_authenticator (dhcpctl_handle *h,
+ const char *name,
+ const char *algorithm,
+ const unsigned char *secret,
+ unsigned secret_len)
+{
+ struct auth_key *key = (struct auth_key *)0;
+ isc_result_t status;
+
+ status = omapi_auth_key_new (&key, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ key -> name = dmalloc (strlen (name) + 1, MDL);
+ if (!key -> name) {
+ omapi_auth_key_dereference (&key, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ strcpy (key -> name, name);
+
+ /* If the algorithm name isn't an FQDN, tack on the
+ .SIG-ALG.REG.NET. domain. */
+ if (strchr (algorithm, '.') == 0) {
+ static char add[] = ".SIG-ALG.REG.INT.";
+ key -> algorithm = dmalloc (strlen (algorithm) +
+ sizeof (add), MDL);
+ if (!key -> algorithm) {
+ omapi_auth_key_dereference (&key, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ strcpy (key -> algorithm, algorithm);
+ strcat (key -> algorithm, add);
+ } else {
+ key -> algorithm = dmalloc (strlen (algorithm) + 1, MDL);
+ if (!key -> algorithm) {
+ omapi_auth_key_dereference (&key, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ strcpy (key -> algorithm, algorithm);
+ }
+
+ status = omapi_data_string_new (&key -> key, secret_len, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_auth_key_dereference (&key, MDL);
+ return status;
+ }
+ memcpy (key -> key -> value, secret, secret_len);
+ key -> key -> len = secret_len;
+
+ *h = (dhcpctl_handle) key;
+ return ISC_R_SUCCESS;
+}
+
+
+/* dhcpctl_new_object
+
+ synchronous - creates a local handle for a host entry.
+ returns nonzero status code if the local host entry couldn't
+ be created
+ stores handle to host through h if successful, and returns zero.
+ object_type is a pointer to a NUL-terminated string containing
+ the ascii name of the type of object being accessed - e.g., "host" */
+
+dhcpctl_status dhcpctl_new_object (dhcpctl_handle *h,
+ dhcpctl_handle connection,
+ const char *object_type)
+{
+ dhcpctl_remote_object_t *m;
+ omapi_object_t *g;
+ isc_result_t status;
+
+ m = (dhcpctl_remote_object_t *)0;
+ status = omapi_object_allocate((omapi_object_t **)&m,
+ dhcpctl_remote_type, 0, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ g = (omapi_object_t *)0;
+ status = omapi_generic_new (&g, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dfree (m, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&m -> inner, g, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference ((omapi_object_t **)&m, MDL);
+ omapi_object_dereference (&g, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&g -> outer,
+ (omapi_object_t *)m, MDL);
+
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference ((omapi_object_t **)&m, MDL);
+ omapi_object_dereference (&g, MDL);
+ return status;
+ }
+
+ status = omapi_typed_data_new (MDL, &m -> rtype,
+ omapi_datatype_string,
+ object_type);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference ((omapi_object_t **)&m, MDL);
+ omapi_object_dereference (&g, MDL);
+ return status;
+ }
+
+ status = omapi_object_reference (h, (omapi_object_t *)m, MDL);
+ omapi_object_dereference ((omapi_object_t **)&m, MDL);
+ omapi_object_dereference (&g, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ return status;
+}
+
+/* asynchronous - just queues the request
+ returns nonzero status code if open couldn't be queued
+ returns zero if open was queued
+ h is a handle to an object created by dhcpctl_new_object
+ connection is a connection to a DHCP server
+ flags include:
+ DHCPCTL_CREATE - if the object doesn't exist, create it
+ DHCPCTL_UPDATE - update the object on the server using the
+ attached parameters
+ DHCPCTL_EXCL - error if the object exists and DHCPCTL_CREATE
+ was also specified */
+
+dhcpctl_status dhcpctl_open_object (dhcpctl_handle h,
+ dhcpctl_handle connection,
+ int flags)
+{
+ isc_result_t status;
+ omapi_object_t *message = (omapi_object_t *)0;
+ dhcpctl_remote_object_t *remote;
+
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ remote = (dhcpctl_remote_object_t *)h;
+
+ status = omapi_message_new (&message, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_set_int_value (message, (omapi_object_t *)0,
+ "op", OMAPI_OP_OPEN);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_object_value (message, (omapi_object_t *)0,
+ "object", h);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ if (flags & DHCPCTL_CREATE) {
+ status = omapi_set_boolean_value (message, (omapi_object_t *)0,
+ "create", 1);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ }
+ if (flags & DHCPCTL_UPDATE) {
+ status = omapi_set_boolean_value (message, (omapi_object_t *)0,
+ "update", 1);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ }
+ if (flags & DHCPCTL_EXCL) {
+ status = omapi_set_boolean_value (message, (omapi_object_t *)0,
+ "exclusive", 1);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ }
+
+ if (remote -> rtype) {
+ status = omapi_set_value_str (message, (omapi_object_t *)0,
+ "type", remote -> rtype);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+ }
+
+ status = omapi_message_register (message);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_protocol_send_message (connection -> outer,
+ (omapi_object_t *)0,
+ message, (omapi_object_t *)0);
+
+ if (status != ISC_R_SUCCESS)
+ omapi_message_unregister (message);
+
+ omapi_object_dereference (&message, MDL);
+ return status;
+}
+
+/* Callback methods (not meant to be called directly) */
+
+isc_result_t dhcpctl_remote_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ dhcpctl_remote_object_t *ro;
+ unsigned long rh;
+ isc_result_t status;
+
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ ro = (dhcpctl_remote_object_t *)h;
+
+ if (!omapi_ds_strcmp (name, "remote-handle")) {
+ status = omapi_get_int_value (&rh, value);
+ if (status == ISC_R_SUCCESS)
+ ro -> remote_handle = rh;
+ return status;
+ }
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcpctl_remote_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcpctl_remote_signal_handler (omapi_object_t *o,
+ const char *name, va_list ap)
+{
+ dhcpctl_remote_object_t *p;
+ omapi_typed_data_t *tv;
+
+ if (o -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ p = (dhcpctl_remote_object_t *)o;
+
+ if (!strcmp (name, "updated")) {
+ p -> waitstatus = ISC_R_SUCCESS;
+ if (o -> inner -> type == omapi_type_generic)
+ omapi_generic_clear_flags (o -> inner);
+ return omapi_signal_in (o -> inner, "ready");
+ }
+ if (!strcmp (name, "status")) {
+ p -> waitstatus = va_arg (ap, isc_result_t);
+ if (p -> message)
+ omapi_typed_data_dereference (&p -> message, MDL);
+ tv = va_arg (ap, omapi_typed_data_t *);
+ if (tv)
+ omapi_typed_data_reference (&p -> message, tv, MDL);
+ return omapi_signal_in (o -> inner, "ready");
+ }
+
+ if (p -> inner && p -> inner -> type -> signal_handler)
+ return (*(p -> inner -> type -> signal_handler))
+ (p -> inner, name, ap);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcpctl_remote_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ dhcpctl_remote_object_t *p;
+ if (h -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+ p = (dhcpctl_remote_object_t *)h;
+ if (p -> handle)
+ omapi_object_dereference ((omapi_object_t **)&p -> handle,
+ file, line);
+ if (p -> rtype)
+ omapi_typed_data_dereference ((omapi_typed_data_t **)&p->rtype,
+ file, line);
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t dhcpctl_remote_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *p)
+{
+ if (p -> type != dhcpctl_remote_type)
+ return DHCP_R_INVALIDARG;
+
+ if (p -> inner && p -> inner -> type -> stuff_values)
+ return (*(p -> inner -> type -> stuff_values)) (c, id,
+ p -> inner);
+ return ISC_R_SUCCESS;
+}
+
diff --git a/doc/IANA-arp-parameters b/doc/IANA-arp-parameters
new file mode 100644
index 0000000..9fa8f11
--- /dev/null
+++ b/doc/IANA-arp-parameters
@@ -0,0 +1,145 @@
+
+
+ADDRESS RESOLUTION PROTOCOL PARAMETERS
+
+The Address Resolution Protocol (ARP) specified in [RFC826] has
+several parameters. The assigned values for these parameters are
+listed here.
+
+REVERSE ADDRESS RESOLUTION PROTOCOL OPERATION CODES
+
+The Reverse Address Resolution Protocol (RARP) specified in [RFC903]
+uses the "Reverse" codes below.
+
+DYNAMIC REVERSE ARP
+
+The Dynamic Reverse Address Resolution Protocol (DRARP) uses the
+"DRARP" codes below. For further information, contact: David Brownell
+(suneast!helium!db@Sun.COM).
+
+INVERSE ADDRESS RESOULUTION PROTOCOL
+
+The Inverse Address Resolution Protocol (IARP) specified in [RFC1293]
+uses the "InARP" codes below.
+
+Assignments:
+
+Number Operation Code (op) References
+------ -------------------------- ----------
+ 1 REQUEST [RFC826]
+ 2 REPLY [RFC826]
+ 3 request Reverse [RFC903]
+ 4 reply Reverse [RFC903]
+ 5 DRARP-Request [David Brownell]
+ 6 DRARP-Reply [David Brownell]
+ 7 DRARP-Error [David Brownell]
+ 8 InARP-Request [RFC1293]
+ 9 InARP-Reply [RFC1293]
+ 10 ARP-NAK [RFC1577]
+ 11 MARS-Request [Armitage]
+ 12 MARS-Multi [Armitage]
+ 13 MARS-MServ [Armitage]
+ 14 MARS-Join [Armitage]
+ 15 MARS-Leave [Armitage]
+ 16 MARS-NAK [Armitage]
+ 17 MARS-Unserv [Armitage]
+ 18 MARS-SJoin [Armitage]
+ 19 MARS-SLeave [Armitage]
+ 20 MARS-Grouplist-Request [Armitage]
+ 21 MARS-Grouplist-Reply [Armitage]
+ 22 MARS-Redirect-Map [Armitage]
+ 23 MAPOS-UNARP [Maruyama]
+
+
+Number Hardware Type (hrd) References
+------ ----------------------------------- ----------
+ 1 Ethernet (10Mb) [JBP]
+ 2 Experimental Ethernet (3Mb) [JBP]
+ 3 Amateur Radio AX.25 [PXK]
+ 4 Proteon ProNET Token Ring [Doria]
+ 5 Chaos [GXP]
+ 6 IEEE 802 Networks [JBP]
+ 7 ARCNET [JBP]
+ 8 Hyperchannel [JBP]
+ 9 Lanstar [TU]
+ 10 Autonet Short Address [MXB1]
+ 11 LocalTalk [JKR1]
+ 12 LocalNet (IBM PCNet or SYTEK LocalNET) [JXM]
+ 13 Ultra link [RXD2]
+ 14 SMDS [GXC1]
+ 15 Frame Relay [AGM]
+ 16 Asynchronous Transmission Mode (ATM) [JXB2]
+ 17 HDLC [JBP]
+ 18 Fibre Channel [Yakov Rekhter]
+ 19 Asynchronous Transmission Mode (ATM) [RFC1577]
+ 20 Serial Line [JBP]
+ 21 Asynchronous Transmission Mode (ATM) [MXB1]
+ 22 MIL-STD-188-220 [Jensen]
+ 23 Metricom [Stone]
+ 24 IEEE 1394.1995 [Hattig]
+ 25 MAPOS [Maruyama]
+
+Protocol Type (pro)
+
+Use the same codes as listed in the section called "Ethernet Numbers
+of Interest" (all hardware types use this code set for the protocol
+type).
+
+
+REFERENCES
+
+[RFC826] Plummer, D., "An Ethernet Address Resolution Protocol or
+ Converting Network Protocol Addresses to 48-bit Ethernet
+ Addresses for Transmission on Ethernet Hardware", STD 37, RFC
+ 826, MIT-LCS, November 1982.
+
+[RFC903] Finlayson, R., Mann, T., Mogul, J., and M. Theimer, "A
+ Reverse Address Resolution Protocol", STD 38, RFC 903,
+ Stanford University, June 1984.
+
+[RFC1293] Bradley, T., and C. Brown, "Inverse Address Resolution
+ Protocol", RFC 1293, Wellfleet Communications, Inc.,
+ January 1992.
+
+
+PEOPLE
+
+[Armitage] Grenville Armitage, <gja@thumper.belcore.com>, April 1995.
+
+[AGM] Andy Malis <malis_a@timeplex.com>
+
+[GXC1] George Clapp <meritec!clapp@bellcore.bellcore.com>
+
+[Doria] Avri Doria <avri@peoteon.com> December 1994.
+
+[GXP] Gill Pratt <gill%mit-ccc@MC.LCS.MIT.EDU>
+
+[Jensen] Herb Jensen, <hwjensen@itt.com>, February 1995.
+
+[JBP] Jon Postel <postel@isi.edu>
+
+[JKR1] Joyce K. Reynolds <jkrey@isi.edu>
+
+[JXM] Joseph Murdock <---none--->
+
+[Hattig] Myron Hattig, <Myron_Hattig@ccm.jf.intel.com>, February 1997.
+
+[Maruyama] Mitsuru Maruyama, <mitsuru@ntt-20.ecl.net>, March 1997.
+
+[MXB1] Mike Burrows <burrows@SRC.DEC.COM>
+
+[PXK] Philip Koch <Philip.Koch@DARTMOUTH.EDU>
+
+[RXD2] Rajiv Dhingra <rajiv@ULTRA.COM>
+
+[Stone] Jonathan Stone, <jonathan@DSG.Stanford.edu>, May 1996.
+
+[TU] Tom Unger <tom@CITI.UMICH>
+
+[David Brownell]
+
+[Mark Laubach]
+
+[Yakov Rekhter] <Yakov@IBM.COM>
+
+[]
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..229a0c5
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,29 @@
+# Copyright (c) 2004-2006,2009 by Internet Systems Consortium, Inc. ("ISC")
+# Copyright (c) 1995-2003 by Internet Software Consortium
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+all: References.txt References.html
+
+References.txt: References.xml
+ xml2txt References.xml
+
+References.html: References.xml
+ xml2html References.xml
+
diff --git a/doc/References.html b/doc/References.html
new file mode 100644
index 0000000..9bf4dc4
--- /dev/null
+++ b/doc/References.html
@@ -0,0 +1,1031 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en"><head><title>ISC-DHCP-REFERENCES: ISC DHCP References Collection</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="description" content="ISC DHCP References Collection">
+<meta name="keywords" content="ISC, DHCP, Reference Implementation">
+<meta name="generator" content="xml2rfc v1.36 (http://xml.resource.org/)">
+<style type='text/css'><!--
+ body {
+ font-family: verdana, charcoal, helvetica, arial, sans-serif;
+ font-size: small; color: #000; background-color: #FFF;
+ margin: 2em;
+ }
+ h1, h2, h3, h4, h5, h6 {
+ font-family: helvetica, monaco, "MS Sans Serif", arial, sans-serif;
+ font-weight: bold; font-style: normal;
+ }
+ h1 { color: #900; background-color: transparent; text-align: right; }
+ h3 { color: #333; background-color: transparent; }
+
+ td.RFCbug {
+ font-size: x-small; text-decoration: none;
+ width: 30px; height: 30px; padding-top: 2px;
+ text-align: justify; vertical-align: middle;
+ background-color: #000;
+ }
+ td.RFCbug span.RFC {
+ font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, verdana, sans-serif;
+ font-weight: bold; color: #666;
+ }
+ td.RFCbug span.hotText {
+ font-family: charcoal, monaco, geneva, "MS Sans Serif", helvetica, verdana, sans-serif;
+ font-weight: normal; text-align: center; color: #FFF;
+ }
+
+ table.TOCbug { width: 30px; height: 15px; }
+ td.TOCbug {
+ text-align: center; width: 30px; height: 15px;
+ color: #FFF; background-color: #900;
+ }
+ td.TOCbug a {
+ font-family: monaco, charcoal, geneva, "MS Sans Serif", helvetica, sans-serif;
+ font-weight: bold; font-size: x-small; text-decoration: none;
+ color: #FFF; background-color: transparent;
+ }
+
+ td.header {
+ font-family: arial, helvetica, sans-serif; font-size: x-small;
+ vertical-align: top; width: 33%;
+ color: #FFF; background-color: #666;
+ }
+ td.author { font-weight: bold; font-size: x-small; margin-left: 4em; }
+ td.author-text { font-size: x-small; }
+
+ /* info code from SantaKlauss at http://www.madaboutstyle.com/tooltip2.html */
+ a.info {
+ /* This is the key. */
+ position: relative;
+ z-index: 24;
+ text-decoration: none;
+ }
+ a.info:hover {
+ z-index: 25;
+ color: #FFF; background-color: #900;
+ }
+ a.info span { display: none; }
+ a.info:hover span.info {
+ /* The span will display just on :hover state. */
+ display: block;
+ position: absolute;
+ font-size: smaller;
+ top: 2em; left: -5em; width: 15em;
+ padding: 2px; border: 1px solid #333;
+ color: #900; background-color: #EEE;
+ text-align: left;
+ }
+
+ a { font-weight: bold; }
+ a:link { color: #900; background-color: transparent; }
+ a:visited { color: #633; background-color: transparent; }
+ a:active { color: #633; background-color: transparent; }
+
+ p { margin-left: 2em; margin-right: 2em; }
+ p.copyright { font-size: x-small; }
+ p.toc { font-size: small; font-weight: bold; margin-left: 3em; }
+ table.toc { margin: 0 0 0 3em; padding: 0; border: 0; vertical-align: text-top; }
+ td.toc { font-size: small; font-weight: bold; vertical-align: text-top; }
+
+ ol.text { margin-left: 2em; margin-right: 2em; }
+ ul.text { margin-left: 2em; margin-right: 2em; }
+ li { margin-left: 3em; }
+
+ /* RFC-2629 <spanx>s and <artwork>s. */
+ em { font-style: italic; }
+ strong { font-weight: bold; }
+ dfn { font-weight: bold; font-style: normal; }
+ cite { font-weight: normal; font-style: normal; }
+ tt { color: #036; }
+ tt, pre, pre dfn, pre em, pre cite, pre span {
+ font-family: "Courier New", Courier, monospace; font-size: small;
+ }
+ pre {
+ text-align: left; padding: 4px;
+ color: #000; background-color: #CCC;
+ }
+ pre dfn { color: #900; }
+ pre em { color: #66F; background-color: #FFC; font-weight: normal; }
+ pre .key { color: #33C; font-weight: bold; }
+ pre .id { color: #900; }
+ pre .str { color: #000; background-color: #CFF; }
+ pre .val { color: #066; }
+ pre .rep { color: #909; }
+ pre .oth { color: #000; background-color: #FCF; }
+ pre .err { background-color: #FCC; }
+
+ /* RFC-2629 <texttable>s. */
+ table.all, table.full, table.headers, table.none {
+ font-size: small; text-align: center; border-width: 2px;
+ vertical-align: top; border-collapse: collapse;
+ }
+ table.all, table.full { border-style: solid; border-color: black; }
+ table.headers, table.none { border-style: none; }
+ th {
+ font-weight: bold; border-color: black;
+ border-width: 2px 2px 3px 2px;
+ }
+ table.all th, table.full th { border-style: solid; }
+ table.headers th { border-style: none none solid none; }
+ table.none th { border-style: none; }
+ table.all td {
+ border-style: solid; border-color: #333;
+ border-width: 1px 2px;
+ }
+ table.full td, table.headers td, table.none td { border-style: none; }
+
+ hr { height: 1px; }
+ hr.insert {
+ width: 80%; border-style: none; border-width: 0;
+ color: #CCC; background-color: #CCC;
+ }
+--></style>
+</head>
+<body>
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<table summary="layout" width="66%" border="0" cellpadding="0" cellspacing="0"><tr><td><table summary="layout" width="100%" border="0" cellpadding="2" cellspacing="1">
+<tr><td class="header">ISC-DHCP-REFERENCES</td><td class="header">D. Hankins</td></tr>
+<tr><td class="header">&nbsp;</td><td class="header">T. Mrugalski</td></tr>
+<tr><td class="header">&nbsp;</td><td class="header">ISC</td></tr>
+<tr><td class="header">&nbsp;</td><td class="header">May 20, 2011</td></tr>
+</table></td></tr></table>
+<h1><br />ISC DHCP References Collection</h1>
+
+<h3>Abstract</h3>
+
+<p>This document describes a collection of reference material
+ to which ISC DHCP has been implemented as well as a more
+ complete listing of references for DHCP and DHCPv6 protocols.
+</p>
+<h3>Copyright Notice</h3>
+
+<p>Copyright (c) 2006-2007,2009,2011 by Internet Systems
+ Consortium, Inc. ("ISC")
+</p>
+<p>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.
+</p>
+<p>THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+</p><a name="toc"></a><br /><hr />
+<h3>Table of Contents</h3>
+<p class="toc">
+<a href="#anchor1">1.</a>&nbsp;
+Introduction<br />
+<br />
+<a href="#anchor2">2.</a>&nbsp;
+Definition: Reference Implementation<br />
+<br />
+<a href="#anchor3">3.</a>&nbsp;
+Low Layer References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor4">3.1.</a>&nbsp;
+Ethernet Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor5">3.2.</a>&nbsp;
+Token Ring Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor6">3.3.</a>&nbsp;
+FDDI Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor7">3.4.</a>&nbsp;
+Internet Protocol Version 4 References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor8">3.5.</a>&nbsp;
+Unicast Datagram Protocol References<br />
+<br />
+<a href="#anchor9">4.</a>&nbsp;
+BOOTP Protocol References<br />
+<br />
+<a href="#anchor10">5.</a>&nbsp;
+DHCPv4 Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor11">5.1.</a>&nbsp;
+DHCPv4 Protocol<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor12">5.1.1.</a>&nbsp;
+Core Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor13">5.2.</a>&nbsp;
+DHCPv4 Option References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor14">5.2.1.</a>&nbsp;
+Relay Agent Information Option Options<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor15">5.2.2.</a>&nbsp;
+Dynamic DNS Updates References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor16">5.2.3.</a>&nbsp;
+Experimental: Failover References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor17">5.3.</a>&nbsp;
+DHCP Procedures<br />
+<br />
+<a href="#anchor18">6.</a>&nbsp;
+DHCPv6 Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor19">6.1.</a>&nbsp;
+DHCPv6 Protocol References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#anchor20">6.2.</a>&nbsp;
+DHCPv6 Options References<br />
+<br />
+<a href="#rfc.references1">7.</a>&nbsp;
+References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#rfc.references1">7.1.</a>&nbsp;
+Published DHCPv4 References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#rfc.references2">7.2.</a>&nbsp;
+Published Common (DHCPv4/DHCPv6) References<br />
+&nbsp;&nbsp;&nbsp;&nbsp;<a href="#rfc.references3">7.3.</a>&nbsp;
+Published DHCPv6 References<br />
+<br />
+<a href="#rfc.authors">&#167;</a>&nbsp;
+Authors' Addresses<br />
+</p>
+<br clear="all" />
+
+<a name="anchor1"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.1"></a><h3>1.&nbsp;
+Introduction</h3>
+
+<p>As a little historical anecdote, ISC DHCP once packaged all the
+ relevant RFCs and standards documents along with the software
+ package. Until one day when a voice was heard from one of the
+ many fine institutions that build and distribute this software...
+ they took issue with the IETF's copyright on the RFC's. It
+ seems the IETF's copyrights don't allow modification of RFC's
+ (except for translation purposes).
+</p>
+<p>Our main purpose in providing the RFCs is to aid in
+ documentation, but since RFCs are now available widely from many
+ points of distribution on the Internet, there is no real need to
+ provide the documents themselves. So, this document has been
+ created in their stead, to list the various IETF RFCs one might
+ want to read, and to comment on how well (or poorly) we have
+ managed to implement them.
+</p>
+<a name="anchor2"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.2"></a><h3>2.&nbsp;
+Definition: Reference Implementation</h3>
+
+<p>ISC DHCP, much like its other cousins in ISC software, is
+ self-described as a 'Reference Implementation.' There has been
+ a great deal of confusion about this term. Some people seem to
+ think that this term applies to any software that once passed
+ a piece of reference material on its way to market (but may do
+ quite a lot of things that aren't described in any reference, or
+ may choose to ignore the reference it saw entirely). Other folks
+ get confused by the word 'reference' and understand that to mean
+ that there is some special status applied to the software - that
+ the software itself is the reference by which all other software
+ is measured. Something along the lines of being "The DHCP
+ Protocol's Reference Clock," it is supposed.
+</p>
+<p>The truth is actually quite a lot simpler. Reference
+ implementations are software packages which were written
+ to behave precisely as appears in reference material. They
+ are written "to match reference."
+</p>
+<p>If the software has a behaviour that manifests itself
+ externally (whether it be something as simple as the 'wire
+ format' or something higher level, such as a complicated
+ behaviour that arises from multiple message exchanges), that
+ behaviour must be found in a reference document.
+</p>
+<p>Anything else is a bug, the only question is whether the
+ bug is in reference or software (failing to implement the
+ reference).
+</p>
+<p>This means:
+</p>
+<p>
+ </p>
+<ul class="text">
+<li>To produce new externally-visible behaviour, one must first
+ provide a reference.
+</li>
+<li>Before changing externally visible behaviour to work around
+ simple incompatibilities in any other implementation, one must
+ first provide a reference.
+</li>
+</ul><p>
+
+</p>
+<p>That is the lofty goal, at any rate. It's well understood that,
+ especially because the ISC DHCP Software package has not always been
+ held to this standard (but not entirely due to it), there are many
+ non-referenced behaviours within ISC DHCP.
+</p>
+<p>The primary goal of reference implementation is to prove the
+ reference material. If the reference material is good, then you
+ should be able to sit down and write a program that implements the
+ reference, to the word, and come to an implementation that
+ is distinguishable from others in the details, but not in the
+ facts of operating the protocol. This means that there is no
+ need for 'special knowledge' to work around arcane problems that
+ were left undocumented. No secret handshakes need to be learned
+ to be imparted with the necessary "real documentation".
+</p>
+<p>Also, by accepting only reference as the guidebook for ISC
+ DHCP's software implementation, anyone who can make an impact on
+ the color texture or form of that reference has a (somewhat
+ indirect) voice in ISC DHCP's software design. As the IETF RFC's
+ have been selected as the source of reference, that means everyone
+ on the Internet with the will to participate has a say.
+</p>
+<a name="anchor3"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3"></a><h3>3.&nbsp;
+Low Layer References</h3>
+
+<p>It may surprise you to realize that ISC DHCP implements 802.1
+ 'Ethernet' framing, Token Ring, and FDDI. In order to bridge the
+ gap there between these physical and DHCP layers, it must also
+ implement IP and UDP framing.
+</p>
+<p>The reason for this stems from Unix systems' handling of BSD
+ sockets (the general way one might engage in transmission of UDP
+ packets) on unconfigured interfaces, or even the handling of
+ broadcast addressing on configured interfaces.
+</p>
+<p>There are a few things that DHCP servers, relays, and clients all
+ need to do in order to speak the DHCP protocol in strict compliance
+ with <a class='info' href='#RFC2131'>[RFC2131]<span> (</span><span class='info'>Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a>.
+
+ </p>
+<ol class="text">
+<li>Transmit a UDP packet from IP:0.0.0.0 Ethernet:Self, destined to
+ IP:255.255.255.255 LinkLayer:Broadcast on an unconfigured (no IP
+ address yet) interface.
+</li>
+<li>Receive a UDP packet from IP:remote-system LinkLayer:remote-system,
+ destined to IP:255.255.255.255 LinkLayer:Broadcast, again on an
+ unconfigured interface.
+</li>
+<li>Transmit a UDP packet from IP:Self, Ethernet:Self, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.
+</li>
+<li>And of course the simple case, a regular IP unicast that is
+ routed via the usual means (so it may be direct to a local system,
+ with ARP providing the glue, or it may be to a remote system via
+ one or more routers as normal). In this case, the interfaces are
+ always configured.
+</li>
+</ol>
+
+<p>The above isn't as simple as it sounds on a regular BSD socket.
+ Many unix implementations will transmit broadcasts not to
+ 255.255.255.255, but to x.y.z.255 (where x.y.z is the system's local
+ subnet). Such packets are not received by several known DHCP client
+ implementations - and it's not their fault, <a class='info' href='#RFC2131'>[RFC2131]<span> (</span><span class='info'>Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a>
+ very explicitly demands that these packets' IP destination
+ addresses be set to 255.255.255.255.
+</p>
+<p>Receiving packets sent to 255.255.255.255 isn't a problem on most
+ modern unixes...so long as the interface is configured. When there
+ is no IPv4 address on the interface, things become much more murky.
+</p>
+<p>So, for this convoluted and unfortunate state of affairs in the
+ unix systems of the day ISC DHCP was manufactured, in order to do
+ what it needs not only to implement the reference but to interoperate
+ with other implementations, the software must create some form of
+ raw socket to operate on.
+</p>
+<p>What it actually does is create, for each interface detected on
+ the system, a Berkeley Packet Filter socket (or equivalent), and
+ program it with a filter that brings in only DHCP packets. A
+ "fallback" UDP Berkeley socket is generally also created, a single
+ one no matter how many interfaces. Should the software need to
+ transmit a contrived packet to the local network the packet is
+ formed piece by piece and transmitted via the BPF socket. Hence
+ the need to implement many forms of Link Layer framing and above.
+ The software gets away with not having to implement IP routing
+ tables as well by simply utilizing the aforementioned 'fallback'
+ UDP socket when unicasting between two configured systems is
+ needed.
+</p>
+<p>Modern unixes have opened up some facilities that diminish how
+ much of this sort of nefarious kludgery is necessary, but have not
+ found the state of affairs absolutely resolved. In particular,
+ one might now unicast without ARP by inserting an entry into the
+ ARP cache prior to transmitting. Unconfigured interfaces remain
+ the sticking point, however...on virtually no modern unixes is
+ it possible to receive broadcast packets unless a local IPv4
+ address has been configured, unless it is done with raw sockets.
+</p>
+<a name="anchor4"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.1"></a><h3>3.1.&nbsp;
+Ethernet Protocol References</h3>
+
+<p>ISC DHCP Implements Ethernet Version 2 ("DIX"), which is a variant
+ of IEEE 802.2. No good reference of this framing is known to exist
+ at this time, but it is vaguely described in <a class='info' href='#RFC0894'>[RFC0894]<span> (</span><span class='info'>Hornig, C., &ldquo;Standard for the transmission of IP datagrams over Ethernet networks,&rdquo; April&nbsp;1984.</span><span>)</span></a>
+ see the section titled "Packet format"), and
+ the following URL is also thought to be useful.
+</p>
+<p><a href='http://en.wikipedia.org/wiki/DIX_Ethernet'>http://en.wikipedia.org/wiki/DIX_Ethernet</a>
+</p>
+<a name="anchor5"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.2"></a><h3>3.2.&nbsp;
+Token Ring Protocol References</h3>
+
+<p>IEEE 802.5 defines the Token Ring framing format used by ISC
+ DHCP.
+</p>
+<a name="anchor6"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.3"></a><h3>3.3.&nbsp;
+FDDI Protocol References</h3>
+
+<p><a class='info' href='#RFC1188'>[RFC1188]<span> (</span><span class='info'>Katz, D., &ldquo;Proposed Standard for the Transmission of IP Datagrams over FDDI Networks,&rdquo; October&nbsp;1990.</span><span>)</span></a> is the most helpful
+ reference ISC DHCP has used to form FDDI packets.
+</p>
+<a name="anchor7"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.4"></a><h3>3.4.&nbsp;
+Internet Protocol Version 4 References</h3>
+
+<p><a class='info' href='#RFC0760'>RFC760<span> (</span><span class='info'>Postel, J., &ldquo;DoD standard Internet Protocol,&rdquo; January&nbsp;1980.</span><span>)</span></a> [RFC0760] fundamentally defines the
+ bare IPv4 protocol which ISC DHCP implements.
+</p>
+<a name="anchor8"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.3.5"></a><h3>3.5.&nbsp;
+Unicast Datagram Protocol References</h3>
+
+<p><a class='info' href='#RFC0768'>RFC768<span> (</span><span class='info'>Postel, J., &ldquo;User Datagram Protocol,&rdquo; August&nbsp;1980.</span><span>)</span></a> [RFC0768] defines the User Datagram
+ Protocol that ultimately carries the DHCP or BOOTP protocol. The
+ destination DHCP server port is 67, the client port is 68. Source
+ ports are irrelevant.
+</p>
+<a name="anchor9"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.4"></a><h3>4.&nbsp;
+BOOTP Protocol References</h3>
+
+<p>The DHCP Protocol is strange among protocols in that it is
+ grafted over the top of another protocol - BOOTP (but we don't
+ call it "DHCP over BOOTP" like we do, say "TCP over IP"). BOOTP
+ and DHCP share UDP packet formats - DHCP is merely a conventional
+ use of both BOOTP header fields and the trailing 'options' space.
+</p>
+<p>The ISC DHCP server supports BOOTP clients conforming to
+ <a class='info' href='#RFC0951'>RFC951<span> (</span><span class='info'>Croft, B. and J. Gilmore, &ldquo;Bootstrap Protocol,&rdquo; September&nbsp;1985.</span><span>)</span></a> [RFC0951] and <a class='info' href='#RFC1542'>RFC1542<span> (</span><span class='info'>Wimer, W., &ldquo;Clarifications and Extensions for the Bootstrap Protocol,&rdquo; October&nbsp;1993.</span><span>)</span></a> [RFC1542].
+</p>
+<a name="anchor10"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5"></a><h3>5.&nbsp;
+DHCPv4 Protocol References</h3>
+
+<a name="anchor11"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.1"></a><h3>5.1.&nbsp;
+DHCPv4 Protocol</h3>
+
+<p>"The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The
+ DHCPv4 Protocol".
+</p>
+<a name="anchor12"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.1.1"></a><h3>5.1.1.&nbsp;
+Core Protocol References</h3>
+
+<p><a class='info' href='#RFC2131'>RFC2131<span> (</span><span class='info'>Droms, R., &ldquo;Dynamic Host Configuration Protocol,&rdquo; March&nbsp;1997.</span><span>)</span></a> [RFC2131] defines the protocol format
+ and procedures. ISC DHCP is not known to diverge from this document
+ in any way. There are, however, a few points on which different
+ implementations have arisen out of vagueries in the document.
+ DHCP Clients exist which, at one time, present themselves as using
+ a Client Identifier Option which is equal to the client's hardware
+ address. Later, the client transmits DHCP packets with no Client
+ Identifier Option present - essentially identifying themselves using
+ the hardware address. Some DHCP Servers have been developed which
+ identify this client as a single client. ISC has interpreted
+ RFC2131 to indicate that these clients must be treated as two
+ separate entities (and hence two, separate addresses). Client
+ behaviour (Embedded Windows products) has developed that relies on
+ the former implementation, and hence is incompatible with the
+ latter. Also, RFC2131 demands explicitly that some header fields
+ be zeroed upon certain message types. The ISC DHCP Server instead
+ copies many of these fields from the packet received from the client
+ or relay, which may not be zero. It is not known if there is a good
+ reason for this that has not been documented.
+</p>
+<p><a class='info' href='#RFC2132'>RFC2132<span> (</span><span class='info'>Alexander, S. and R. Droms, &ldquo;DHCP Options and BOOTP Vendor Extensions,&rdquo; March&nbsp;1997.</span><span>)</span></a> [RFC2132] defines the initial set of
+ DHCP Options and provides a great deal of guidance on how to go about
+ formatting and processing options. The document unfortunately
+ waffles to a great extent about the NULL termination of DHCP Options,
+ and some DHCP Clients (Windows 95) have been implemented that rely
+ upon DHCP Options containing text strings to be NULL-terminated (or
+ else they crash). So, ISC DHCP detects if clients null-terminate the
+ host-name option and, if so, null terminates any text options it
+ transmits to the client. It also removes NULL termination from any
+ known text option it receives prior to any other processing.
+</p>
+<a name="anchor13"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.2"></a><h3>5.2.&nbsp;
+DHCPv4 Option References</h3>
+
+<p><a class='info' href='#RFC2241'>RFC2241<span> (</span><span class='info'>Provan, D., &ldquo;DHCP Options for Novell Directory Services,&rdquo; November&nbsp;1997.</span><span>)</span></a> [RFC2241] defines options for
+ Novell Directory Services.
+</p>
+<p><a class='info' href='#RFC2242'>RFC2242<span> (</span><span class='info'>Droms, R. and K. Fong, &ldquo;NetWare/IP Domain Name and Information,&rdquo; November&nbsp;1997.</span><span>)</span></a> [RFC2242] defines an encapsulated
+ option space for NWIP configuration.
+</p>
+<p><a class='info' href='#RFC2485'>RFC2485<span> (</span><span class='info'>Drach, S., &ldquo;DHCP Option for The Open Group&apos;s User Authentication Protocol,&rdquo; January&nbsp;1999.</span><span>)</span></a> [RFC2485] defines the Open Group's
+ UAP option.
+</p>
+<p><a class='info' href='#RFC2610'>RFC2610<span> (</span><span class='info'>Perkins, C. and E. Guttman, &ldquo;DHCP Options for Service Location Protocol,&rdquo; June&nbsp;1999.</span><span>)</span></a> [RFC2610] defines options for
+ the Service Location Protocol (SLP).
+</p>
+<p><a class='info' href='#RFC2937'>RFC2937<span> (</span><span class='info'>Smith, C., &ldquo;The Name Service Search Option for DHCP,&rdquo; September&nbsp;2000.</span><span>)</span></a> [RFC2937] defines the Name Service
+ Search Option (not to be confused with the domain-search option).
+ The Name Service Search Option allows eg nsswitch.conf to be
+ reconfigured via dhcp. The ISC DHCP server implements this option,
+ and the ISC DHCP client is compatible...but does not by default
+ install this option's value. One would need to make their relevant
+ dhclient-script process this option in a way that is suitable for
+ the system.
+</p>
+<p><a class='info' href='#RFC3004'>RFC3004<span> (</span><span class='info'>Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis, A., Beser, B., and J. Privat, &ldquo;The User Class Option for DHCP,&rdquo; November&nbsp;2000.</span><span>)</span></a> [RFC3004] defines the User-Class
+ option. Note carefully that ISC DHCP currently does not implement
+ to this reference, but has (inexplicably) selected an incompatible
+ format: a plain text string.
+</p>
+<p><a class='info' href='#RFC3011'>RFC3011<span> (</span><span class='info'>Waters, G., &ldquo;The IPv4 Subnet Selection Option for DHCP,&rdquo; November&nbsp;2000.</span><span>)</span></a> [RFC3011] defines the Subnet-Selection
+ plain DHCPv4 option. Do not confuse this option with the relay agent
+ "link selection" sub-option, although their behaviour is
+ similar.
+</p>
+<p><a class='info' href='#RFC3396'>RFC3396<span> (</span><span class='info'>Lemon, T. and S. Cheshire, &ldquo;Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4),&rdquo; November&nbsp;2002.</span><span>)</span></a> [RFC3396] documents both how long
+ options may be encoded in DHCPv4 packets, and also how multiple
+ instances of the same option code within a DHCPv4 packet will be
+ decoded by receivers.
+</p>
+<p><a class='info' href='#RFC3397'>RFC3397<span> (</span><span class='info'>Aboba, B. and S. Cheshire, &ldquo;Dynamic Host Configuration Protocol (DHCP) Domain Search Option,&rdquo; November&nbsp;2002.</span><span>)</span></a> [RFC3397] documents the Domain-Search
+ Option, which allows the configuration of the /etc/resolv.conf
+ 'search' parameter in a way that is <a class='info' href='#RFC1035'>RFC1035<span> (</span><span class='info'>Mockapetris, P., &ldquo;Domain names - implementation and specification,&rdquo; November&nbsp;1987.</span><span>)</span></a> [RFC1035] wire format compatible (in fact, it uses the RFC1035 wire
+ format). ISC DHCP has both client and server support, and supports
+ RFC1035 name compression.
+</p>
+<p><a class='info' href='#RFC3679'>RFC3679<span> (</span><span class='info'>Droms, R., &ldquo;Unused Dynamic Host Configuration Protocol (DHCP) Option Codes,&rdquo; January&nbsp;2004.</span><span>)</span></a> [RFC3679] documents a number of
+ options that were documented earlier in history, but were not
+ made use of.
+</p>
+<p><a class='info' href='#RFC3925'>RFC3925<span> (</span><span class='info'>Littlefield, J., &ldquo;Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4),&rdquo; October&nbsp;2004.</span><span>)</span></a> [RFC3925] documents a pair of
+ Enterprise-ID delimited option spaces for vendors to use in order
+ to inform servers of their "vendor class" (sort of like 'uname'
+ or 'who and what am I'), and a means to deliver vendor-specific
+ and vendor-documented option codes and values.
+</p>
+<p><a class='info' href='#RFC3942'>RFC3942<span> (</span><span class='info'>Volz, B., &ldquo;Reclassifying Dynamic Host Configuration Protocol version 4 (DHCPv4) Options,&rdquo; November&nbsp;2004.</span><span>)</span></a> [RFC3942] redefined the 'site local'
+ option space.
+</p>
+<p><a class='info' href='#RFC4280'>[RFC4280]<span> (</span><span class='info'>Chowdhury, K., Yegani, P., and L. Madour, &ldquo;Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers,&rdquo; November&nbsp;2005.</span><span>)</span></a> defines two BCMS server options
+ for each protocol family.
+</p>
+<p><a class='info' href='#RFC4388'>RFC4388<span> (</span><span class='info'>Woundy, R. and K. Kinnear, &ldquo;Dynamic Host Configuration Protocol (DHCP) Leasequery,&rdquo; February&nbsp;2006.</span><span>)</span></a> [RFC4388] defined the DHCPv4
+ LEASEQUERY message type and a number of suitable response messages,
+ for the purpose of sharing information about DHCP served addresses
+ and clients.
+</p>
+<a name="anchor14"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.2.1"></a><h3>5.2.1.&nbsp;
+Relay Agent Information Option Options</h3>
+
+<p><a class='info' href='#RFC3046'>RFC3046<span> (</span><span class='info'>Patrick, M., &ldquo;DHCP Relay Agent Information Option,&rdquo; January&nbsp;2001.</span><span>)</span></a> [RFC3046] defines the Relay Agent
+ Information Option and provides a number of sub-option
+ definitions.
+</p>
+<p><a class='info' href='#RFC3256'>RFC3256<span> (</span><span class='info'>Jones, D. and R. Woundy, &ldquo;The DOCSIS (Data-Over-Cable Service Interface Specifications) Device Class DHCP (Dynamic Host Configuration Protocol) Relay Agent Information Sub-option,&rdquo; April&nbsp;2002.</span><span>)</span></a> [RFC3256] defines the DOCSIS Device
+ Class sub-option.
+</p>
+<p><a class='info' href='#RFC3527'>RFC3527<span> (</span><span class='info'>Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy, &ldquo;Link Selection sub-option for the Relay Agent Information Option for DHCPv4,&rdquo; April&nbsp;2003.</span><span>)</span></a> [RFC3527] defines the Link Selection
+ sub-option.
+</p>
+<a name="anchor15"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.2.2"></a><h3>5.2.2.&nbsp;
+Dynamic DNS Updates References</h3>
+
+<p>The collection of documents that describe the standards-based
+ method to update dns names of DHCP clients starts most easily
+ with <a class='info' href='#RFC4703'>RFC4703<span> (</span><span class='info'>Stapp, M. and B. Volz, &ldquo;Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients,&rdquo; October&nbsp;2006.</span><span>)</span></a> [RFC4703] to define the overall
+ architecture, travels through RFCs <a class='info' href='#RFC4702'>4702<span> (</span><span class='info'>Stapp, M., Volz, B., and Y. Rekhter, &ldquo;The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option,&rdquo; October&nbsp;2006.</span><span>)</span></a> [RFC4702]
+ and <a class='info' href='#RFC4704'>4704<span> (</span><span class='info'>Volz, B., &ldquo;The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option,&rdquo; October&nbsp;2006.</span><span>)</span></a> [RFC4704] to describe the DHCPv4 and
+ DHCPv6 FQDN options (to carry the client name), and ends up at
+ <a class='info' href='#RFC4701'>RFC4701<span> (</span><span class='info'>Stapp, M., Lemon, T., and A. Gustafsson, &ldquo;A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR),&rdquo; October&nbsp;2006.</span><span>)</span></a> [RFC4701] which describes the DHCID
+ RR used in DNS to perform a kind of atomic locking.
+</p>
+<p>ISC DHCP adopted early versions of these documents, and has not
+ yet synchronized with the final standards versions.
+</p>
+<p>For RFCs 4702 and 4704, the 'N' bit is not yet supported. The
+ result is that it is always set zero, and is ignored if set.
+</p>
+<p>For RFC4701, which is used to match client identities with names
+ in the DNS as part of name conflict resolution. Note that ISC DHCP's
+ implementation of DHCIDs vary wildly from this specification.
+ First, ISC DHCP uses a TXT record in which the contents are stored
+ in hexadecimal. Second, there is a flaw in the selection of the
+ 'Identifier Type', which results in a completely different value
+ being selected than was defined in an older revision of this
+ document...also this field is one byte prior to hexadecimal
+ encoding rather than two. Third, ISC DHCP does not use a digest
+ type code. Rather, all values for such TXT records are reached
+ via an MD5 sum. In short, nothing is compatible, but the
+ principle of the TXT record is the same as the standard DHCID
+ record. However, for DHCPv6 FQDN, we do use DHCID type code '2',
+ as no other value really makes sense in our context.
+</p>
+<a name="anchor16"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.2.3"></a><h3>5.2.3.&nbsp;
+Experimental: Failover References</h3>
+
+<p>The Failover Protocol defines means by which two DHCP Servers
+ can share all the relevant information about leases granted to
+ DHCP clients on given networks, so that one of the two servers may
+ fail and be survived by a server that can act responsibly.
+</p>
+<p>Unfortunately it has been quite some years (2003) since the last
+ time this document was edited, and the authors no longer show any
+ interest in fielding comments or improving the document.
+</p>
+<p>The status of this protocol is very unsure, but ISC's
+ implementation of it has proven stable and suitable for use in
+ sizable production environments.
+</p>
+<p><a class='info' href='#draft-failover'>draft-ietf-dhc-failover-12.txt<span> (</span><span class='info'>Droms, R., &ldquo;DHCP Failover Protocol,&rdquo; March&nbsp;2003.</span><span>)</span></a> [draft&#8209;failover]
+ describes the Failover Protocol. In addition to what is described
+ in this document, ISC DHCP has elected to make some experimental
+ changes that may be revoked in a future version of ISC DHCP (if the
+ draft authors do not adopt the new behaviour). Specifically, ISC
+ DHCP's POOLREQ behaviour differs substantially from what is
+ documented in the draft, and the server also implements a form of
+ 'MAC Address Affinity' which is not described in the failover
+ document. The full nature of these changes have been described on
+ the IETF DHC WG mailing list (which has archives), and also in ISC
+ DHCP's manual pages. Also note that although this document
+ references a RECOVER-WAIT state, it does not document a protocol
+ number assignment for this state. As a consequence, ISC DHCP has
+ elected to use the value 254.
+</p>
+<p> An optimization described in the failover protocol draft
+ is included since 4.2.0a1. It permits a DHCP server
+ operating in communications-interrupted state to 'rewind' a
+ lease to the state most recently transmitted to its peer,
+ greatly increasing a server's endurance in
+ communications-interrupted. This is supported using a new
+ 'rewind state' record on the dhcpd.leases entry for each
+ lease.
+
+</p>
+<p><a class='info' href='#RFC3074'>[RFC3074]<span> (</span><span class='info'>Volz, B., Gonczi, S., Lemon, T., and R. Stevens, &ldquo;DHC Load Balancing Algorithm,&rdquo; February&nbsp;2001.</span><span>)</span></a> describes the Load Balancing
+ Algorithm (LBA) that ISC DHCP uses in concert with the Failover
+ protocol. Note that versions 3.0.* are known to misimplement the
+ hash algorithm (it will only use the low 4 bits of every byte of
+ the hash bucket array).
+</p>
+<a name="anchor17"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.5.3"></a><h3>5.3.&nbsp;
+DHCP Procedures</h3>
+
+<p><a class='info' href='#RFC2939'>[RFC2939]<span> (</span><span class='info'>Droms, R., &ldquo;Procedures and IANA Guidelines for Definition of New DHCP Options and Message Types,&rdquo; September&nbsp;2000.</span><span>)</span></a> explains how to go about
+ obtaining a new DHCP Option code assignment.
+</p>
+<a name="anchor18"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.6"></a><h3>6.&nbsp;
+DHCPv6 Protocol References</h3>
+
+<a name="anchor19"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.6.1"></a><h3>6.1.&nbsp;
+DHCPv6 Protocol References</h3>
+
+<p>For now there is only one document that specifies the base
+ of the DHCPv6 protocol (there have been no updates yet),
+ <a class='info' href='#RFC3315'>[RFC3315]<span> (</span><span class='info'>Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C., and M. Carney, &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; July&nbsp;2003.</span><span>)</span></a>.
+</p>
+<p>Support for DHCPv6 was first added in version 4.0.0. The server
+ and client support only IA_NA. While the server does support multiple
+ IA_NAs within one packet from the client, our client only supports
+ sending one. There is no relay support.
+</p>
+<p>DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.
+</p>
+<p>
+ </p>
+<ol class="text">
+<li>Options sometimes may appear multiple times. The common
+ library used to treat all appearance of multiple options as
+ specified in RFC2131 - to be concatenated. DHCPv6 options
+ may sometimes appear multiple times (such as with IA_NA or
+ IAADDR), but often must not. As of 4.2.1-P1, multiple IA_NA, IA_PD
+ or IA_TA are not supported.
+</li>
+<li>The same option space appears in DHCPv6 packets multiple times.
+ If the packet was got via a relay, then the client's packet is
+ stored to an option within the relay's packet...if there were two
+ relays, this recurses. At each of these steps, the root "DHCPv6
+ option space" is used. Further, a client packet may contain an
+ IA_NA, which may contain an IAADDR - but really, in an abstract
+ sense, this is again re-encapsulation of the DHCPv6 option space
+ beneath options it also contains.
+</li>
+</ol><p>
+
+</p>
+<p>Precisely how to correctly support the above conundrums has not
+ quite yet been settled, so support is incomplete.
+</p>
+<a name="anchor20"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.6.2"></a><h3>6.2.&nbsp;
+DHCPv6 Options References</h3>
+
+<p><a class='info' href='#RFC3319'>[RFC3319]<span> (</span><span class='info'>Schulzrinne, H. and B. Volz, &ldquo;Dynamic Host Configuration Protocol (DHCPv6) Options for Session Initiation Protocol (SIP) Servers,&rdquo; July&nbsp;2003.</span><span>)</span></a> defines the SIP server
+ options for DHCPv6.
+</p>
+<p><a class='info' href='#RFC3646'>[RFC3646]<span> (</span><span class='info'>Droms, R., &ldquo;DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; December&nbsp;2003.</span><span>)</span></a> documents the DHCPv6
+ name-servers and domain-search options.
+</p>
+<p><a class='info' href='#RFC3633'>[RFC3633]<span> (</span><span class='info'>Troan, O. and R. Droms, &ldquo;IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP) version 6,&rdquo; December&nbsp;2003.</span><span>)</span></a> documents the Identity
+ Association Prefix Delegation for DHCPv6, which is included
+ here for protocol wire reference, but which is not supported
+ by ISC DHCP.
+</p>
+<p><a class='info' href='#RFC3898'>[RFC3898]<span> (</span><span class='info'>Kalusivalingam, V., &ldquo;Network Information Service (NIS) Configuration Options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; October&nbsp;2004.</span><span>)</span></a> documents four NIS options
+ for delivering NIS servers and domain information in DHCPv6.
+</p>
+<p><a class='info' href='#RFC4075'>[RFC4075]<span> (</span><span class='info'>Kalusivalingam, V., &ldquo;Simple Network Time Protocol (SNTP) Configuration Option for DHCPv6,&rdquo; May&nbsp;2005.</span><span>)</span></a> defines the DHCPv6 SNTP
+ Servers option.
+</p>
+<p><a class='info' href='#RFC4242'>[RFC4242]<span> (</span><span class='info'>Venaas, S., Chown, T., and B. Volz, &ldquo;Information Refresh Time Option for Dynamic Host Configuration Protocol for IPv6 (DHCPv6),&rdquo; November&nbsp;2005.</span><span>)</span></a> defines the Information
+ Refresh Time option, which advises DHCPv6 Information-Request
+ clients to return for updated information.
+</p>
+<p><a class='info' href='#RFC4280'>[RFC4280]<span> (</span><span class='info'>Chowdhury, K., Yegani, P., and L. Madour, &ldquo;Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers,&rdquo; November&nbsp;2005.</span><span>)</span></a> defines two BCMS server options
+ for each protocol family.
+</p>
+<p><a class='info' href='#RFC4580'>[RFC4580]<span> (</span><span class='info'>Volz, B., &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Subscriber-ID Option,&rdquo; June&nbsp;2006.</span><span>)</span></a> defines a DHCPv6
+ subscriber-id option, which is similar in principle to the DHCPv4
+ relay agent option of the same name.
+</p>
+<p><a class='info' href='#RFC4649'>[RFC4649]<span> (</span><span class='info'>Volz, B., &ldquo;Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Remote-ID Option,&rdquo; August&nbsp;2006.</span><span>)</span></a> defines a DHCPv6 remote-id
+ option, which is similar in principle to the DHCPv4 relay agent
+ remote-id.
+</p>
+<a name="rfc.references"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<a name="rfc.section.7"></a><h3>7.&nbsp;
+References</h3>
+
+<a name="rfc.references1"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>7.1.&nbsp;Published DHCPv4 References</h3>
+<table width="99%" border="0">
+<tr><td class="author-text" valign="top"><a name="RFC0760">[RFC0760]</a></td>
+<td class="author-text">Postel, J., &ldquo;<a href="http://tools.ietf.org/html/rfc760">DoD standard Internet Protocol</a>,&rdquo; RFC&nbsp;760, January&nbsp;1980 (<a href="http://www.rfc-editor.org/rfc/rfc760.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0768">[RFC0768]</a></td>
+<td class="author-text">Postel, J., &ldquo;<a href="http://tools.ietf.org/html/rfc768">User Datagram Protocol</a>,&rdquo; STD&nbsp;6, RFC&nbsp;768, August&nbsp;1980 (<a href="http://www.rfc-editor.org/rfc/rfc768.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0894">[RFC0894]</a></td>
+<td class="author-text">Hornig, C., &ldquo;<a href="http://tools.ietf.org/html/rfc894">Standard for the transmission of IP datagrams over Ethernet networks</a>,&rdquo; STD&nbsp;41, RFC&nbsp;894, April&nbsp;1984 (<a href="http://www.rfc-editor.org/rfc/rfc894.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC0951">[RFC0951]</a></td>
+<td class="author-text">Croft, B. and J. Gilmore, &ldquo;<a href="http://tools.ietf.org/html/rfc951">Bootstrap Protocol</a>,&rdquo; RFC&nbsp;951, September&nbsp;1985 (<a href="http://www.rfc-editor.org/rfc/rfc951.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1035">[RFC1035]</a></td>
+<td class="author-text">Mockapetris, P., &ldquo;<a href="http://tools.ietf.org/html/rfc1035">Domain names - implementation and specification</a>,&rdquo; STD&nbsp;13, RFC&nbsp;1035, November&nbsp;1987 (<a href="http://www.rfc-editor.org/rfc/rfc1035.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1188">[RFC1188]</a></td>
+<td class="author-text"><a href="mailto:dkatz@merit.edu">Katz, D.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc1188">Proposed Standard for the Transmission of IP Datagrams over FDDI Networks</a>,&rdquo; RFC&nbsp;1188, October&nbsp;1990 (<a href="http://www.rfc-editor.org/rfc/rfc1188.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC1542">[RFC1542]</a></td>
+<td class="author-text"><a href="mailto:Walter.Wimer@CMU.EDU">Wimer, W.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc1542">Clarifications and Extensions for the Bootstrap Protocol</a>,&rdquo; RFC&nbsp;1542, October&nbsp;1993 (<a href="http://www.rfc-editor.org/rfc/rfc1542.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2131">[RFC2131]</a></td>
+<td class="author-text"><a href="mailto:droms@bucknell.edu">Droms, R.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2131">Dynamic Host Configuration Protocol</a>,&rdquo; RFC&nbsp;2131, March&nbsp;1997 (<a href="http://www.rfc-editor.org/rfc/rfc2131.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2131.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2131.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2132">[RFC2132]</a></td>
+<td class="author-text"><a href="mailto:sca@engr.sgi.com">Alexander, S.</a> and <a href="mailto:droms@bucknell.edu">R. Droms</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2132">DHCP Options and BOOTP Vendor Extensions</a>,&rdquo; RFC&nbsp;2132, March&nbsp;1997 (<a href="http://www.rfc-editor.org/rfc/rfc2132.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2132.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2132.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2241">[RFC2241]</a></td>
+<td class="author-text"><a href="mailto:donp@Novell.Com">Provan, D.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2241">DHCP Options for Novell Directory Services</a>,&rdquo; RFC&nbsp;2241, November&nbsp;1997 (<a href="http://www.rfc-editor.org/rfc/rfc2241.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2241.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2241.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2242">[RFC2242]</a></td>
+<td class="author-text"><a href="mailto:droms@bucknell.edu">Droms, R.</a> and <a href="mailto:kfong@novell.com">K. Fong</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2242">NetWare/IP Domain Name and Information</a>,&rdquo; RFC&nbsp;2242, November&nbsp;1997 (<a href="http://www.rfc-editor.org/rfc/rfc2242.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2242.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2242.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2485">[RFC2485]</a></td>
+<td class="author-text"><a href="mailto:drach@sun.com">Drach, S.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2485">DHCP Option for The Open Group&#039;s User Authentication Protocol</a>,&rdquo; RFC&nbsp;2485, January&nbsp;1999 (<a href="http://www.rfc-editor.org/rfc/rfc2485.txt">TXT</a>, <a href="http://xml.resource.org/public/rfc/html/rfc2485.html">HTML</a>, <a href="http://xml.resource.org/public/rfc/xml/rfc2485.xml">XML</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2563">[RFC2563]</a></td>
+<td class="author-text"><a href="mailto:rtroll@corp.home.net">Troll, R.</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2563">DHCP Option to Disable Stateless Auto-Configuration in IPv4 Clients</a>,&rdquo; RFC&nbsp;2563, May&nbsp;1999 (<a href="http://www.rfc-editor.org/rfc/rfc2563.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2610">[RFC2610]</a></td>
+<td class="author-text"><a href="mailto:Charles.Perkins@Sun.Com">Perkins, C.</a> and <a href="mailto:Erik.Guttman@Sun.Com">E. Guttman</a>, &ldquo;<a href="http://tools.ietf.org/html/rfc2610">DHCP Options for Service Location Protocol</a>,&rdquo; RFC&nbsp;2610, June&nbsp;1999 (<a href="http://www.rfc-editor.org/rfc/rfc2610.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2855">[RFC2855]</a></td>
+<td class="author-text">Fujisawa, K., &ldquo;<a href="http://tools.ietf.org/html/rfc2855">DHCP for IEEE 1394</a>,&rdquo; RFC&nbsp;2855, June&nbsp;2000 (<a href="http://www.rfc-editor.org/rfc/rfc2855.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2937">[RFC2937]</a></td>
+<td class="author-text">Smith, C., &ldquo;<a href="http://tools.ietf.org/html/rfc2937">The Name Service Search Option for DHCP</a>,&rdquo; RFC&nbsp;2937, September&nbsp;2000 (<a href="http://www.rfc-editor.org/rfc/rfc2937.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC2939">[RFC2939]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="http://tools.ietf.org/html/rfc2939">Procedures and IANA Guidelines for Definition of New DHCP Options and Message Types</a>,&rdquo; BCP&nbsp;43, RFC&nbsp;2939, September&nbsp;2000 (<a href="http://www.rfc-editor.org/rfc/rfc2939.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3004">[RFC3004]</a></td>
+<td class="author-text">Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis, A., Beser, B., and J. Privat, &ldquo;<a href="http://tools.ietf.org/html/rfc3004">The User Class Option for DHCP</a>,&rdquo; RFC&nbsp;3004, November&nbsp;2000 (<a href="http://www.rfc-editor.org/rfc/rfc3004.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3011">[RFC3011]</a></td>
+<td class="author-text">Waters, G., &ldquo;<a href="http://tools.ietf.org/html/rfc3011">The IPv4 Subnet Selection Option for DHCP</a>,&rdquo; RFC&nbsp;3011, November&nbsp;2000 (<a href="http://www.rfc-editor.org/rfc/rfc3011.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3046">[RFC3046]</a></td>
+<td class="author-text">Patrick, M., &ldquo;<a href="http://tools.ietf.org/html/rfc3046">DHCP Relay Agent Information Option</a>,&rdquo; RFC&nbsp;3046, January&nbsp;2001 (<a href="http://www.rfc-editor.org/rfc/rfc3046.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3074">[RFC3074]</a></td>
+<td class="author-text">Volz, B., Gonczi, S., Lemon, T., and R. Stevens, &ldquo;<a href="http://tools.ietf.org/html/rfc3074">DHC Load Balancing Algorithm</a>,&rdquo; RFC&nbsp;3074, February&nbsp;2001 (<a href="http://www.rfc-editor.org/rfc/rfc3074.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3118">[RFC3118]</a></td>
+<td class="author-text">Droms, R. and W. Arbaugh, &ldquo;<a href="http://tools.ietf.org/html/rfc3118">Authentication for DHCP Messages</a>,&rdquo; RFC&nbsp;3118, June&nbsp;2001 (<a href="http://www.rfc-editor.org/rfc/rfc3118.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3203">[RFC3203]</a></td>
+<td class="author-text">T&#039;Joens, Y., Hublet, C., and P. De Schrijver, &ldquo;<a href="http://tools.ietf.org/html/rfc3203">DHCP reconfigure extension</a>,&rdquo; RFC&nbsp;3203, December&nbsp;2001 (<a href="http://www.rfc-editor.org/rfc/rfc3203.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3256">[RFC3256]</a></td>
+<td class="author-text">Jones, D. and R. Woundy, &ldquo;<a href="http://tools.ietf.org/html/rfc3256">The DOCSIS (Data-Over-Cable Service Interface Specifications) Device Class DHCP (Dynamic Host Configuration Protocol) Relay Agent Information Sub-option</a>,&rdquo; RFC&nbsp;3256, April&nbsp;2002 (<a href="http://www.rfc-editor.org/rfc/rfc3256.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3361">[RFC3361]</a></td>
+<td class="author-text">Schulzrinne, H., &ldquo;<a href="http://tools.ietf.org/html/rfc3361">Dynamic Host Configuration Protocol (DHCP-for-IPv4) Option for Session Initiation Protocol (SIP) Servers</a>,&rdquo; RFC&nbsp;3361, August&nbsp;2002 (<a href="http://www.rfc-editor.org/rfc/rfc3361.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3396">[RFC3396]</a></td>
+<td class="author-text">Lemon, T. and S. Cheshire, &ldquo;<a href="http://tools.ietf.org/html/rfc3396">Encoding Long Options in the Dynamic Host Configuration Protocol (DHCPv4)</a>,&rdquo; RFC&nbsp;3396, November&nbsp;2002 (<a href="http://www.rfc-editor.org/rfc/rfc3396.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3397">[RFC3397]</a></td>
+<td class="author-text">Aboba, B. and S. Cheshire, &ldquo;<a href="http://tools.ietf.org/html/rfc3397">Dynamic Host Configuration Protocol (DHCP) Domain Search Option</a>,&rdquo; RFC&nbsp;3397, November&nbsp;2002 (<a href="http://www.rfc-editor.org/rfc/rfc3397.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3442">[RFC3442]</a></td>
+<td class="author-text">Lemon, T., Cheshire, S., and B. Volz, &ldquo;<a href="http://tools.ietf.org/html/rfc3442">The Classless Static Route Option for Dynamic Host Configuration Protocol (DHCP) version 4</a>,&rdquo; RFC&nbsp;3442, December&nbsp;2002 (<a href="http://www.rfc-editor.org/rfc/rfc3442.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3456">[RFC3456]</a></td>
+<td class="author-text">Patel, B., Aboba, B., Kelly, S., and V. Gupta, &ldquo;<a href="http://tools.ietf.org/html/rfc3456">Dynamic Host Configuration Protocol (DHCPv4) Configuration of IPsec Tunnel Mode</a>,&rdquo; RFC&nbsp;3456, January&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3456.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3495">[RFC3495]</a></td>
+<td class="author-text">Beser, B. and P. Duffy, &ldquo;<a href="http://tools.ietf.org/html/rfc3495">Dynamic Host Configuration Protocol (DHCP) Option for CableLabs Client Configuration</a>,&rdquo; RFC&nbsp;3495, March&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3495.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3527">[RFC3527]</a></td>
+<td class="author-text">Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy, &ldquo;<a href="http://tools.ietf.org/html/rfc3527">Link Selection sub-option for the Relay Agent Information Option for DHCPv4</a>,&rdquo; RFC&nbsp;3527, April&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3527.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3594">[RFC3594]</a></td>
+<td class="author-text">Duffy, P., &ldquo;<a href="http://tools.ietf.org/html/rfc3594">PacketCable Security Ticket Control Sub-Option for the DHCP CableLabs Client Configuration (CCC) Option</a>,&rdquo; RFC&nbsp;3594, September&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3594.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3634">[RFC3634]</a></td>
+<td class="author-text">Luehrs, K., Woundy, R., Bevilacqua, J., and N. Davoust, &ldquo;<a href="http://tools.ietf.org/html/rfc3634">Key Distribution Center (KDC) Server Address Sub-option for the Dynamic Host Configuration Protocol (DHCP) CableLabs Client Configuration (CCC) Option</a>,&rdquo; RFC&nbsp;3634, December&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3634.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3679">[RFC3679]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="http://tools.ietf.org/html/rfc3679">Unused Dynamic Host Configuration Protocol (DHCP) Option Codes</a>,&rdquo; RFC&nbsp;3679, January&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3679.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3825">[RFC3825]</a></td>
+<td class="author-text">Polk, J., Schnizlein, J., and M. Linsner, &ldquo;<a href="http://tools.ietf.org/html/rfc3825">Dynamic Host Configuration Protocol Option for Coordinate-based Location Configuration Information</a>,&rdquo; RFC&nbsp;3825, July&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3825.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3925">[RFC3925]</a></td>
+<td class="author-text">Littlefield, J., &ldquo;<a href="http://tools.ietf.org/html/rfc3925">Vendor-Identifying Vendor Options for Dynamic Host Configuration Protocol version 4 (DHCPv4)</a>,&rdquo; RFC&nbsp;3925, October&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3925.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3942">[RFC3942]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="http://tools.ietf.org/html/rfc3942">Reclassifying Dynamic Host Configuration Protocol version 4 (DHCPv4) Options</a>,&rdquo; RFC&nbsp;3942, November&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3942.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3993">[RFC3993]</a></td>
+<td class="author-text">Johnson, R., Palaniappan, T., and M. Stapp, &ldquo;<a href="http://tools.ietf.org/html/rfc3993">Subscriber-ID Suboption for the Dynamic Host Configuration Protocol (DHCP) Relay Agent Option</a>,&rdquo; RFC&nbsp;3993, March&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc3993.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4014">[RFC4014]</a></td>
+<td class="author-text">Droms, R. and J. Schnizlein, &ldquo;<a href="http://tools.ietf.org/html/rfc4014">Remote Authentication Dial-In User Service (RADIUS) Attributes Suboption for the Dynamic Host Configuration Protocol (DHCP) Relay Agent Information Option</a>,&rdquo; RFC&nbsp;4014, February&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4014.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4030">[RFC4030]</a></td>
+<td class="author-text">Stapp, M. and T. Lemon, &ldquo;<a href="http://tools.ietf.org/html/rfc4030">The Authentication Suboption for the Dynamic Host Configuration Protocol (DHCP) Relay Agent Option</a>,&rdquo; RFC&nbsp;4030, March&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4030.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4039">[RFC4039]</a></td>
+<td class="author-text">Park, S., Kim, P., and B. Volz, &ldquo;<a href="http://tools.ietf.org/html/rfc4039">Rapid Commit Option for the Dynamic Host Configuration Protocol version 4 (DHCPv4)</a>,&rdquo; RFC&nbsp;4039, March&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4039.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4174">[RFC4174]</a></td>
+<td class="author-text">Monia, C., Tseng, J., and K. Gibbons, &ldquo;<a href="http://tools.ietf.org/html/rfc4174">The IPv4 Dynamic Host Configuration Protocol (DHCP) Option for the Internet Storage Name Service</a>,&rdquo; RFC&nbsp;4174, September&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4174.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4243">[RFC4243]</a></td>
+<td class="author-text">Stapp, M., Johnson, R., and T. Palaniappan, &ldquo;<a href="http://tools.ietf.org/html/rfc4243">Vendor-Specific Information Suboption for the Dynamic Host Configuration Protocol (DHCP) Relay Agent Option</a>,&rdquo; RFC&nbsp;4243, December&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4243.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4361">[RFC4361]</a></td>
+<td class="author-text">Lemon, T. and B. Sommerfeld, &ldquo;<a href="http://tools.ietf.org/html/rfc4361">Node-specific Client Identifiers for Dynamic Host Configuration Protocol Version Four (DHCPv4)</a>,&rdquo; RFC&nbsp;4361, February&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4361.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4388">[RFC4388]</a></td>
+<td class="author-text">Woundy, R. and K. Kinnear, &ldquo;<a href="http://tools.ietf.org/html/rfc4388">Dynamic Host Configuration Protocol (DHCP) Leasequery</a>,&rdquo; RFC&nbsp;4388, February&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4388.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4390">[RFC4390]</a></td>
+<td class="author-text">Kashyap, V., &ldquo;<a href="http://tools.ietf.org/html/rfc4390">Dynamic Host Configuration Protocol (DHCP) over InfiniBand</a>,&rdquo; RFC&nbsp;4390, April&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4390.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4436">[RFC4436]</a></td>
+<td class="author-text">Aboba, B., Carlson, J., and S. Cheshire, &ldquo;<a href="http://tools.ietf.org/html/rfc4436">Detecting Network Attachment in IPv4 (DNAv4)</a>,&rdquo; RFC&nbsp;4436, March&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4436.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4701">[RFC4701]</a></td>
+<td class="author-text">Stapp, M., Lemon, T., and A. Gustafsson, &ldquo;<a href="http://tools.ietf.org/html/rfc4701">A DNS Resource Record (RR) for Encoding Dynamic Host Configuration Protocol (DHCP) Information (DHCID RR)</a>,&rdquo; RFC&nbsp;4701, October&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4701.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4702">[RFC4702]</a></td>
+<td class="author-text">Stapp, M., Volz, B., and Y. Rekhter, &ldquo;<a href="http://tools.ietf.org/html/rfc4702">The Dynamic Host Configuration Protocol (DHCP) Client Fully Qualified Domain Name (FQDN) Option</a>,&rdquo; RFC&nbsp;4702, October&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4702.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4703">[RFC4703]</a></td>
+<td class="author-text">Stapp, M. and B. Volz, &ldquo;<a href="http://tools.ietf.org/html/rfc4703">Resolution of Fully Qualified Domain Name (FQDN) Conflicts among Dynamic Host Configuration Protocol (DHCP) Clients</a>,&rdquo; RFC&nbsp;4703, October&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4703.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5010">[RFC5010]</a></td>
+<td class="author-text">Kinnear, K., Normoyle, M., and M. Stapp, &ldquo;<a href="http://tools.ietf.org/html/rfc5010">The Dynamic Host Configuration Protocol Version 4 (DHCPv4) Relay Agent Flags Suboption</a>,&rdquo; RFC&nbsp;5010, September&nbsp;2007 (<a href="http://www.rfc-editor.org/rfc/rfc5010.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5071">[RFC5071]</a></td>
+<td class="author-text">Hankins, D., &ldquo;<a href="http://tools.ietf.org/html/rfc5071">Dynamic Host Configuration Protocol Options Used by PXELINUX</a>,&rdquo; RFC&nbsp;5071, December&nbsp;2007 (<a href="http://www.rfc-editor.org/rfc/rfc5071.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5107">[RFC5107]</a></td>
+<td class="author-text">Johnson, R., Kumarasamy, J., Kinnear, K., and M. Stapp, &ldquo;<a href="http://tools.ietf.org/html/rfc5107">DHCP Server Identifier Override Suboption</a>,&rdquo; RFC&nbsp;5107, February&nbsp;2008 (<a href="http://www.rfc-editor.org/rfc/rfc5107.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5192">[RFC5192]</a></td>
+<td class="author-text">Morand, L., Yegin, A., Kumar, S., and S. Madanapalli, &ldquo;<a href="http://tools.ietf.org/html/rfc5192">DHCP Options for Protocol for Carrying Authentication for Network Access (PANA) Authentication Agents</a>,&rdquo; RFC&nbsp;5192, May&nbsp;2008 (<a href="http://www.rfc-editor.org/rfc/rfc5192.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5223">[RFC5223]</a></td>
+<td class="author-text">Schulzrinne, H., Polk, J., and H. Tschofenig, &ldquo;<a href="http://tools.ietf.org/html/rfc5223">Discovering Location-to-Service Translation (LoST) Servers Using the Dynamic Host Configuration Protocol (DHCP)</a>,&rdquo; RFC&nbsp;5223, August&nbsp;2008 (<a href="http://www.rfc-editor.org/rfc/rfc5223.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5859">[RFC5859]</a></td>
+<td class="author-text">Johnson, R., &ldquo;<a href="http://tools.ietf.org/html/rfc5859">TFTP Server Address Option for DHCPv4</a>,&rdquo; RFC&nbsp;5859, June&nbsp;2010 (<a href="http://www.rfc-editor.org/rfc/rfc5859.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5969">[RFC5969]</a></td>
+<td class="author-text">Townsley, W. and O. Troan, &ldquo;<a href="http://tools.ietf.org/html/rfc5969">IPv6 Rapid Deployment on IPv4 Infrastructures (6rd) -- Protocol Specification</a>,&rdquo; RFC&nbsp;5969, August&nbsp;2010 (<a href="http://www.rfc-editor.org/rfc/rfc5969.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="draft-failover">[draft-failover]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="https://www.isc.org/sw/dhcp/drafts/draft-ietf-dhc-failover-12.txt">DHCP Failover Protocol</a>,&rdquo; March&nbsp;2003.</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-dhcpv4-relay-encapsulation">[I-D.ietf-dhc-dhcpv4-relay-encapsulation]</a></td>
+<td class="author-text">Lemon, T. and H. Deng, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-dhcpv4-relay-encapsulation-00">Relay Agent Encapsulation for DHCPv4</a>,&rdquo; draft-ietf-dhc-dhcpv4-relay-encapsulation-00 (work in progress), October&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-dhcpv4-relay-encapsulation-00.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-dhcpv4-bulk-leasequery">[I-D.ietf-dhc-dhcpv4-bulk-leasequery]</a></td>
+<td class="author-text">Kinnear, K., Volz, B., Russell, N., Stapp, M., Rao, D., Joshi, B., and P. Kurapati, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-dhcpv4-bulk-leasequery-03">Bulk DHCPv4 Lease Query</a>,&rdquo; draft-ietf-dhc-dhcpv4-bulk-leasequery-03 (work in progress), October&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-dhcpv4-bulk-leasequery-03.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-leasequery-by-remote-id">[I-D.ietf-dhc-leasequery-by-remote-id]</a></td>
+<td class="author-text">Kurapati, P. and B. Joshi, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-leasequery-by-remote-id-09">DHCPv4 lease query by Relay Agent Remote ID</a>,&rdquo; draft-ietf-dhc-leasequery-by-remote-id-09 (work in progress), December&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-leasequery-by-remote-id-09.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-relay-id-suboption">[I-D.ietf-dhc-relay-id-suboption]</a></td>
+<td class="author-text">Stapp, M., &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-relay-id-suboption-07">The DHCPv4 Relay Agent Identifier Suboption</a>,&rdquo; draft-ietf-dhc-relay-id-suboption-07 (work in progress), July&nbsp;2009 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-relay-id-suboption-07.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-mip6-hiopt">[I-D.ietf-mip6-hiopt]</a></td>
+<td class="author-text">Jang, H., Yegin, A., Chowdhury, K., and J. Choi, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-mip6-hiopt-17">DHCP Options for Home Information Discovery in MIPv6</a>,&rdquo; draft-ietf-mip6-hiopt-17 (work in progress), May&nbsp;2008 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-mip6-hiopt-17.txt">TXT</a>).</td></tr>
+</table>
+
+<a name="rfc.references2"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>7.2.&nbsp;Published Common (DHCPv4/DHCPv6) References</h3>
+<table width="99%" border="0">
+<tr><td class="author-text" valign="top"><a name="RFC4280">[RFC4280]</a></td>
+<td class="author-text">Chowdhury, K., Yegani, P., and L. Madour, &ldquo;<a href="http://tools.ietf.org/html/rfc4280">Dynamic Host Configuration Protocol (DHCP) Options for Broadcast and Multicast Control Servers</a>,&rdquo; RFC&nbsp;4280, November&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4280.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4477">[RFC4477]</a></td>
+<td class="author-text">Chown, T., Venaas, S., and C. Strauf, &ldquo;<a href="http://tools.ietf.org/html/rfc4477">Dynamic Host Configuration Protocol (DHCP): IPv4 and IPv6 Dual-Stack Issues</a>,&rdquo; RFC&nbsp;4477, May&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4477.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4578">[RFC4578]</a></td>
+<td class="author-text">Johnston, M. and S. Venaas, &ldquo;<a href="http://tools.ietf.org/html/rfc4578">Dynamic Host Configuration Protocol (DHCP) Options for the Intel Preboot eXecution Environment (PXE)</a>,&rdquo; RFC&nbsp;4578, November&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4578.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4776">[RFC4776]</a></td>
+<td class="author-text">Schulzrinne, H., &ldquo;<a href="http://tools.ietf.org/html/rfc4776">Dynamic Host Configuration Protocol (DHCPv4 and DHCPv6) Option for Civic Addresses Configuration Information</a>,&rdquo; RFC&nbsp;4776, November&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4776.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4833">[RFC4833]</a></td>
+<td class="author-text">Lear, E. and P. Eggert, &ldquo;<a href="http://tools.ietf.org/html/rfc4833">Timezone Options for DHCP</a>,&rdquo; RFC&nbsp;4833, April&nbsp;2007 (<a href="http://www.rfc-editor.org/rfc/rfc4833.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5417">[RFC5417]</a></td>
+<td class="author-text">Calhoun, P., &ldquo;<a href="http://tools.ietf.org/html/rfc5417">Control And Provisioning of Wireless Access Points (CAPWAP) Access Controller DHCP Option</a>,&rdquo; RFC&nbsp;5417, March&nbsp;2009 (<a href="http://www.rfc-editor.org/rfc/rfc5417.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5678">[RFC5678]</a></td>
+<td class="author-text">Bajko, G. and S. Das, &ldquo;<a href="http://tools.ietf.org/html/rfc5678">Dynamic Host Configuration Protocol (DHCPv4 and DHCPv6) Options for IEEE 802.21 Mobility Services (MoS) Discovery</a>,&rdquo; RFC&nbsp;5678, December&nbsp;2009 (<a href="http://www.rfc-editor.org/rfc/rfc5678.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5908">[RFC5908]</a></td>
+<td class="author-text">Gayraud, R. and B. Lourdelet, &ldquo;<a href="http://tools.ietf.org/html/rfc5908">Network Time Protocol (NTP) Server Option for DHCPv6</a>,&rdquo; RFC&nbsp;5908, June&nbsp;2010 (<a href="http://www.rfc-editor.org/rfc/rfc5908.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5970">[RFC5970]</a></td>
+<td class="author-text">Huth, T., Freimann, J., Zimmer, V., and D. Thaler, &ldquo;<a href="http://tools.ietf.org/html/rfc5970">DHCPv6 Options for Network Boot</a>,&rdquo; RFC&nbsp;5970, September&nbsp;2010 (<a href="http://www.rfc-editor.org/rfc/rfc5970.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5986">[RFC5986]</a></td>
+<td class="author-text">Thomson, M. and J. Winterbottom, &ldquo;<a href="http://tools.ietf.org/html/rfc5986">Discovering the Local Location Information Server (LIS)</a>,&rdquo; RFC&nbsp;5986, September&nbsp;2010 (<a href="http://www.rfc-editor.org/rfc/rfc5986.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-vpn-option">[I-D.ietf-dhc-vpn-option]</a></td>
+<td class="author-text">Kinnear, K., Johnson, R., and M. Stapp, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-vpn-option-12">Virtual Subnet Selection Options for DHCPv4 and DHCPv6</a>,&rdquo; draft-ietf-dhc-vpn-option-12 (work in progress), October&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-vpn-option-12.txt">TXT</a>).</td></tr>
+</table>
+
+<a name="rfc.references3"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>7.3.&nbsp;Published DHCPv6 References</h3>
+<table width="99%" border="0">
+<tr><td class="author-text" valign="top"><a name="RFC3315">[RFC3315]</a></td>
+<td class="author-text">Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C., and M. Carney, &ldquo;<a href="http://tools.ietf.org/html/rfc3315">Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3315, July&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3315.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3319">[RFC3319]</a></td>
+<td class="author-text">Schulzrinne, H. and B. Volz, &ldquo;<a href="http://tools.ietf.org/html/rfc3319">Dynamic Host Configuration Protocol (DHCPv6) Options for Session Initiation Protocol (SIP) Servers</a>,&rdquo; RFC&nbsp;3319, July&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3319.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3633">[RFC3633]</a></td>
+<td class="author-text">Troan, O. and R. Droms, &ldquo;<a href="http://tools.ietf.org/html/rfc3633">IPv6 Prefix Options for Dynamic Host Configuration Protocol (DHCP) version 6</a>,&rdquo; RFC&nbsp;3633, December&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3633.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3646">[RFC3646]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="http://tools.ietf.org/html/rfc3646">DNS Configuration options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3646, December&nbsp;2003 (<a href="http://www.rfc-editor.org/rfc/rfc3646.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3736">[RFC3736]</a></td>
+<td class="author-text">Droms, R., &ldquo;<a href="http://tools.ietf.org/html/rfc3736">Stateless Dynamic Host Configuration Protocol (DHCP) Service for IPv6</a>,&rdquo; RFC&nbsp;3736, April&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3736.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC3898">[RFC3898]</a></td>
+<td class="author-text">Kalusivalingam, V., &ldquo;<a href="http://tools.ietf.org/html/rfc3898">Network Information Service (NIS) Configuration Options for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;3898, October&nbsp;2004 (<a href="http://www.rfc-editor.org/rfc/rfc3898.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4075">[RFC4075]</a></td>
+<td class="author-text">Kalusivalingam, V., &ldquo;<a href="http://tools.ietf.org/html/rfc4075">Simple Network Time Protocol (SNTP) Configuration Option for DHCPv6</a>,&rdquo; RFC&nbsp;4075, May&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4075.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4076">[RFC4076]</a></td>
+<td class="author-text">Chown, T., Venaas, S., and A. Vijayabhaskar, &ldquo;<a href="http://tools.ietf.org/html/rfc4076">Renumbering Requirements for Stateless Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;4076, May&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4076.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4242">[RFC4242]</a></td>
+<td class="author-text">Venaas, S., Chown, T., and B. Volz, &ldquo;<a href="http://tools.ietf.org/html/rfc4242">Information Refresh Time Option for Dynamic Host Configuration Protocol for IPv6 (DHCPv6)</a>,&rdquo; RFC&nbsp;4242, November&nbsp;2005 (<a href="http://www.rfc-editor.org/rfc/rfc4242.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4580">[RFC4580]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="http://tools.ietf.org/html/rfc4580">Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Subscriber-ID Option</a>,&rdquo; RFC&nbsp;4580, June&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4580.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4649">[RFC4649]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="http://tools.ietf.org/html/rfc4649">Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Relay Agent Remote-ID Option</a>,&rdquo; RFC&nbsp;4649, August&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4649.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4704">[RFC4704]</a></td>
+<td class="author-text">Volz, B., &ldquo;<a href="http://tools.ietf.org/html/rfc4704">The Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN) Option</a>,&rdquo; RFC&nbsp;4704, October&nbsp;2006 (<a href="http://www.rfc-editor.org/rfc/rfc4704.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC4994">[RFC4994]</a></td>
+<td class="author-text">Zeng, S., Volz, B., Kinnear, K., and J. Brzozowski, &ldquo;<a href="http://tools.ietf.org/html/rfc4994">DHCPv6 Relay Agent Echo Request Option</a>,&rdquo; RFC&nbsp;4994, September&nbsp;2007 (<a href="http://www.rfc-editor.org/rfc/rfc4994.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5007">[RFC5007]</a></td>
+<td class="author-text">Brzozowski, J., Kinnear, K., Volz, B., and S. Zeng, &ldquo;<a href="http://tools.ietf.org/html/rfc5007">DHCPv6 Leasequery</a>,&rdquo; RFC&nbsp;5007, September&nbsp;2007 (<a href="http://www.rfc-editor.org/rfc/rfc5007.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="RFC5460">[RFC5460]</a></td>
+<td class="author-text">Stapp, M., &ldquo;<a href="http://tools.ietf.org/html/rfc5460">DHCPv6 Bulk Leasequery</a>,&rdquo; RFC&nbsp;5460, February&nbsp;2009 (<a href="http://www.rfc-editor.org/rfc/rfc5460.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-mif-dhcpv6-route-option">[I-D.ietf-mif-dhcpv6-route-option]</a></td>
+<td class="author-text">Dec, W., Mrugalski, T., Sun, T., and B. Sarikaya, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-mif-dhcpv6-route-option-01">DHCPv6 Route Option</a>,&rdquo; draft-ietf-mif-dhcpv6-route-option-01 (work in progress), March&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-mif-dhcpv6-route-option-01.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-dhcpv6-ldra">[I-D.ietf-dhc-dhcpv6-ldra]</a></td>
+<td class="author-text">Miles, D., Ooghe, S., Dec, W., Krishnan, S., and A. Kavanagh, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-dhcpv6-ldra-03">Lightweight DHCPv6 Relay Agent</a>,&rdquo; draft-ietf-dhc-dhcpv6-ldra-03 (work in progress), October&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-dhcpv6-ldra-03.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-dhcpv6-relay-supplied-options">[I-D.ietf-dhc-dhcpv6-relay-supplied-options]</a></td>
+<td class="author-text">Lemon, T. and W. Wu, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-dhcpv6-relay-supplied-options-06">Relay-Supplied DHCP Options</a>,&rdquo; draft-ietf-dhc-dhcpv6-relay-supplied-options-06 (work in progress), May&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-dhcpv6-relay-supplied-options-06.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-pd-exclude">[I-D.ietf-dhc-pd-exclude]</a></td>
+<td class="author-text">Korhonen, J., Savolainen, T., Krishnan, S., and O. Troan, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-pd-exclude-01">Prefix Exclude Option for DHCPv6-based Prefix Delegation</a>,&rdquo; draft-ietf-dhc-pd-exclude-01 (work in progress), January&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-pd-exclude-01.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-secure-dhcpv6">[I-D.ietf-dhc-secure-dhcpv6]</a></td>
+<td class="author-text">Jiang, S., &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-secure-dhcpv6-02">Secure DHCPv6 Using CGAs</a>,&rdquo; draft-ietf-dhc-secure-dhcpv6-02 (work in progress), December&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-secure-dhcpv6-02.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-mext-nemo-pd">[I-D.ietf-mext-nemo-pd]</a></td>
+<td class="author-text">Droms, R., Thubert, P., Dupont, F., Haddad, W., and C. Bernardos, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-mext-nemo-pd-07">DHCPv6 Prefix Delegation for NEMO</a>,&rdquo; draft-ietf-mext-nemo-pd-07 (work in progress), December&nbsp;2010 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-mext-nemo-pd-07.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-dhc-duid-uuid">[I-D.ietf-dhc-duid-uuid]</a></td>
+<td class="author-text">Narten, T. and J. Johnson, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-dhc-duid-uuid-03">Definition of the UUID-based DHCPv6 Unique Identifier (DUID-UUID)</a>,&rdquo; draft-ietf-dhc-duid-uuid-03 (work in progress), February&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-dhc-duid-uuid-03.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-softwire-ds-lite-tunnel-option">[I-D.ietf-softwire-ds-lite-tunnel-option]</a></td>
+<td class="author-text">Hankins, D. and T. Mrugalski, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-softwire-ds-lite-tunnel-option-10">Dynamic Host Configuration Protocol for IPv6 (DHCPv6) Option for Dual- Stack Lite</a>,&rdquo; draft-ietf-softwire-ds-lite-tunnel-option-10 (work in progress), March&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-softwire-ds-lite-tunnel-option-10.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-mif-dns-server-selection">[I-D.ietf-mif-dns-server-selection]</a></td>
+<td class="author-text">Savolainen, T. and J. Kato, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-mif-dns-server-selection-01">Improved DNS Server Selection for Multi-Homed Nodes</a>,&rdquo; draft-ietf-mif-dns-server-selection-01 (work in progress), March&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-mif-dns-server-selection-01.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="I-D.ietf-geopriv-rfc3825bis">[I-D.ietf-geopriv-rfc3825bis]</a></td>
+<td class="author-text">Polk, J., Linsner, M., Thomson, M., and B. Aboba, &ldquo;<a href="http://tools.ietf.org/html/draft-ietf-geopriv-rfc3825bis-17">Dynamic Host Configuration Protocol Options for Coordinate-based Location Configuration Information</a>,&rdquo; draft-ietf-geopriv-rfc3825bis-17 (work in progress), February&nbsp;2011 (<a href="http://www.ietf.org/internet-drafts/draft-ietf-geopriv-rfc3825bis-17.txt">TXT</a>).</td></tr>
+<tr><td class="author-text" valign="top"><a name="draft-addr-params">[draft-addr-params]</a></td>
+<td class="author-text">Mrugalski, T., &ldquo;<a href="http://klub.com.pl/dhcpv6/doc/draft-mrugalski-addropts-XX-2007-04-17.txt">Address Parameters Option for DHCPv6</a>,&rdquo; April&nbsp;2007.</td></tr>
+</table>
+
+<a name="rfc.authors"></a><br /><hr />
+<table summary="layout" cellpadding="0" cellspacing="2" class="TOCbug" align="right"><tr><td class="TOCbug"><a href="#toc">&nbsp;TOC&nbsp;</a></td></tr></table>
+<h3>Authors' Addresses</h3>
+<table width="99%" border="0" cellpadding="0" cellspacing="0">
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">David W. Hankins</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Internet Systems Consortium,
+ Inc.</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">950 Charter Street</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Redwood City, CA 94063</td></tr>
+<tr cellpadding="3"><td>&nbsp;</td><td>&nbsp;</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Tomasz Mrugalski</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Internet Systems Consortium,
+ Inc.</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">950 Charter Street</td></tr>
+<tr><td class="author-text">&nbsp;</td>
+<td class="author-text">Redwood City, CA 94063</td></tr>
+<tr><td class="author" align="right">Phone:&nbsp;</td>
+<td class="author-text">+1 650 423 1345</td></tr>
+<tr><td class="author" align="right">Email:&nbsp;</td>
+<td class="author-text"><a href="mailto:Tomasz_Mrugalski@isc.org">Tomasz_Mrugalski@isc.org</a></td></tr>
+</table>
+</body></html>
diff --git a/doc/References.txt b/doc/References.txt
new file mode 100644
index 0000000..9d28f23
--- /dev/null
+++ b/doc/References.txt
@@ -0,0 +1,1120 @@
+
+
+
+ISC-DHCP-REFERENCES D. Hankins
+ T. Mrugalski
+ ISC
+ May 20, 2011
+
+
+ ISC DHCP References Collection
+
+Abstract
+
+ This document describes a collection of reference material to which
+ ISC DHCP has been implemented as well as a more complete listing of
+ references for DHCP and DHCPv6 protocols.
+
+Copyright Notice
+
+ Copyright (c) 2006-2007,2009,2011 by Internet Systems Consortium,
+ Inc. ("ISC")
+
+ 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 ISC DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
+ SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins & Mrugalski [Page 1]
+
+ ISC DHCP References Collection May 2011
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
+
+ 2. Definition: Reference Implementation . . . . . . . . . . . . . 3
+
+ 3. Low Layer References . . . . . . . . . . . . . . . . . . . . . 4
+ 3.1. Ethernet Protocol References . . . . . . . . . . . . . . . 6
+ 3.2. Token Ring Protocol References . . . . . . . . . . . . . . 6
+ 3.3. FDDI Protocol References . . . . . . . . . . . . . . . . . 6
+ 3.4. Internet Protocol Version 4 References . . . . . . . . . . 6
+ 3.5. Unicast Datagram Protocol References . . . . . . . . . . . 6
+
+ 4. BOOTP Protocol References . . . . . . . . . . . . . . . . . . 6
+
+ 5. DHCPv4 Protocol References . . . . . . . . . . . . . . . . . . 7
+ 5.1. DHCPv4 Protocol . . . . . . . . . . . . . . . . . . . . . 7
+ 5.1.1. Core Protocol References . . . . . . . . . . . . . . . 7
+ 5.2. DHCPv4 Option References . . . . . . . . . . . . . . . . . 7
+ 5.2.1. Relay Agent Information Option Options . . . . . . . . 9
+ 5.2.2. Dynamic DNS Updates References . . . . . . . . . . . . 9
+ 5.2.3. Experimental: Failover References . . . . . . . . . . 9
+ 5.3. DHCP Procedures . . . . . . . . . . . . . . . . . . . . . 10
+
+ 6. DHCPv6 Protocol References . . . . . . . . . . . . . . . . . . 10
+ 6.1. DHCPv6 Protocol References . . . . . . . . . . . . . . . . 10
+ 6.2. DHCPv6 Options References . . . . . . . . . . . . . . . . 11
+
+ 7. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12
+ 7.1. Published DHCPv4 References . . . . . . . . . . . . . . . 12
+ 7.2. Published Common (DHCPv4/DHCPv6) References . . . . . . . 17
+ 7.3. Published DHCPv6 References . . . . . . . . . . . . . . . 18
+
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Hankins & Mrugalski [Page 2]
+
+ ISC DHCP References Collection May 2011
+
+
+1. Introduction
+
+ As a little historical anecdote, ISC DHCP once packaged all the
+ relevant RFCs and standards documents along with the software
+ package. Until one day when a voice was heard from one of the many
+ fine institutions that build and distribute this software... they
+ took issue with the IETF's copyright on the RFC's. It seems the
+ IETF's copyrights don't allow modification of RFC's (except for
+ translation purposes).
+
+ Our main purpose in providing the RFCs is to aid in documentation,
+ but since RFCs are now available widely from many points of
+ distribution on the Internet, there is no real need to provide the
+ documents themselves. So, this document has been created in their
+ stead, to list the various IETF RFCs one might want to read, and to
+ comment on how well (or poorly) we have managed to implement them.
+
+
+2. Definition: Reference Implementation
+
+ ISC DHCP, much like its other cousins in ISC software, is self-
+ described as a 'Reference Implementation.' There has been a great
+ deal of confusion about this term. Some people seem to think that
+ this term applies to any software that once passed a piece of
+ reference material on its way to market (but may do quite a lot of
+ things that aren't described in any reference, or may choose to
+ ignore the reference it saw entirely). Other folks get confused by
+ the word 'reference' and understand that to mean that there is some
+ special status applied to the software - that the software itself is
+ the reference by which all other software is measured. Something
+ along the lines of being "The DHCP Protocol's Reference Clock," it is
+ supposed.
+
+ The truth is actually quite a lot simpler. Reference implementations
+ are software packages which were written to behave precisely as
+ appears in reference material. They are written "to match
+ reference."
+
+ If the software has a behaviour that manifests itself externally
+ (whether it be something as simple as the 'wire format' or something
+ higher level, such as a complicated behaviour that arises from
+ multiple message exchanges), that behaviour must be found in a
+ reference document.
+
+ Anything else is a bug, the only question is whether the bug is in
+ reference or software (failing to implement the reference).
+
+ This means:
+
+
+
+Hankins & Mrugalski [Page 3]
+
+ ISC DHCP References Collection May 2011
+
+
+ o To produce new externally-visible behaviour, one must first
+ provide a reference.
+
+ o Before changing externally visible behaviour to work around simple
+ incompatibilities in any other implementation, one must first
+ provide a reference.
+
+ That is the lofty goal, at any rate. It's well understood that,
+ especially because the ISC DHCP Software package has not always been
+ held to this standard (but not entirely due to it), there are many
+ non-referenced behaviours within ISC DHCP.
+
+ The primary goal of reference implementation is to prove the
+ reference material. If the reference material is good, then you
+ should be able to sit down and write a program that implements the
+ reference, to the word, and come to an implementation that is
+ distinguishable from others in the details, but not in the facts of
+ operating the protocol. This means that there is no need for
+ 'special knowledge' to work around arcane problems that were left
+ undocumented. No secret handshakes need to be learned to be imparted
+ with the necessary "real documentation".
+
+ Also, by accepting only reference as the guidebook for ISC DHCP's
+ software implementation, anyone who can make an impact on the color
+ texture or form of that reference has a (somewhat indirect) voice in
+ ISC DHCP's software design. As the IETF RFC's have been selected as
+ the source of reference, that means everyone on the Internet with the
+ will to participate has a say.
+
+
+3. Low Layer References
+
+ It may surprise you to realize that ISC DHCP implements 802.1
+ 'Ethernet' framing, Token Ring, and FDDI. In order to bridge the gap
+ there between these physical and DHCP layers, it must also implement
+ IP and UDP framing.
+
+ The reason for this stems from Unix systems' handling of BSD sockets
+ (the general way one might engage in transmission of UDP packets) on
+ unconfigured interfaces, or even the handling of broadcast addressing
+ on configured interfaces.
+
+ There are a few things that DHCP servers, relays, and clients all
+ need to do in order to speak the DHCP protocol in strict compliance
+ with [RFC2131].
+
+ 1. Transmit a UDP packet from IP:0.0.0.0 Ethernet:Self, destined to
+ IP:255.255.255.255 LinkLayer:Broadcast on an unconfigured (no IP
+
+
+
+Hankins & Mrugalski [Page 4]
+
+ ISC DHCP References Collection May 2011
+
+
+ address yet) interface.
+
+ 2. Receive a UDP packet from IP:remote-system LinkLayer:remote-
+ system, destined to IP:255.255.255.255 LinkLayer:Broadcast, again
+ on an unconfigured interface.
+
+ 3. Transmit a UDP packet from IP:Self, Ethernet:Self, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.
+
+ 4. And of course the simple case, a regular IP unicast that is
+ routed via the usual means (so it may be direct to a local
+ system, with ARP providing the glue, or it may be to a remote
+ system via one or more routers as normal). In this case, the
+ interfaces are always configured.
+
+ The above isn't as simple as it sounds on a regular BSD socket. Many
+ unix implementations will transmit broadcasts not to 255.255.255.255,
+ but to x.y.z.255 (where x.y.z is the system's local subnet). Such
+ packets are not received by several known DHCP client implementations
+ - and it's not their fault, [RFC2131] very explicitly demands that
+ these packets' IP destination addresses be set to 255.255.255.255.
+
+ Receiving packets sent to 255.255.255.255 isn't a problem on most
+ modern unixes...so long as the interface is configured. When there
+ is no IPv4 address on the interface, things become much more murky.
+
+ So, for this convoluted and unfortunate state of affairs in the unix
+ systems of the day ISC DHCP was manufactured, in order to do what it
+ needs not only to implement the reference but to interoperate with
+ other implementations, the software must create some form of raw
+ socket to operate on.
+
+ What it actually does is create, for each interface detected on the
+ system, a Berkeley Packet Filter socket (or equivalent), and program
+ it with a filter that brings in only DHCP packets. A "fallback" UDP
+ Berkeley socket is generally also created, a single one no matter how
+ many interfaces. Should the software need to transmit a contrived
+ packet to the local network the packet is formed piece by piece and
+ transmitted via the BPF socket. Hence the need to implement many
+ forms of Link Layer framing and above. The software gets away with
+ not having to implement IP routing tables as well by simply utilizing
+ the aforementioned 'fallback' UDP socket when unicasting between two
+ configured systems is needed.
+
+ Modern unixes have opened up some facilities that diminish how much
+ of this sort of nefarious kludgery is necessary, but have not found
+ the state of affairs absolutely resolved. In particular, one might
+
+
+
+Hankins & Mrugalski [Page 5]
+
+ ISC DHCP References Collection May 2011
+
+
+ now unicast without ARP by inserting an entry into the ARP cache
+ prior to transmitting. Unconfigured interfaces remain the sticking
+ point, however...on virtually no modern unixes is it possible to
+ receive broadcast packets unless a local IPv4 address has been
+ configured, unless it is done with raw sockets.
+
+3.1. Ethernet Protocol References
+
+ ISC DHCP Implements Ethernet Version 2 ("DIX"), which is a variant of
+ IEEE 802.2. No good reference of this framing is known to exist at
+ this time, but it is vaguely described in [RFC0894] see the section
+ titled "Packet format"), and the following URL is also thought to be
+ useful.
+
+ http://en.wikipedia.org/wiki/DIX_Ethernet
+
+3.2. Token Ring Protocol References
+
+ IEEE 802.5 defines the Token Ring framing format used by ISC DHCP.
+
+3.3. FDDI Protocol References
+
+ [RFC1188] is the most helpful reference ISC DHCP has used to form
+ FDDI packets.
+
+3.4. Internet Protocol Version 4 References
+
+ RFC760 [RFC0760] fundamentally defines the bare IPv4 protocol which
+ ISC DHCP implements.
+
+3.5. Unicast Datagram Protocol References
+
+ RFC768 [RFC0768] defines the User Datagram Protocol that ultimately
+ carries the DHCP or BOOTP protocol. The destination DHCP server port
+ is 67, the client port is 68. Source ports are irrelevant.
+
+
+4. BOOTP Protocol References
+
+ The DHCP Protocol is strange among protocols in that it is grafted
+ over the top of another protocol - BOOTP (but we don't call it "DHCP
+ over BOOTP" like we do, say "TCP over IP"). BOOTP and DHCP share UDP
+ packet formats - DHCP is merely a conventional use of both BOOTP
+ header fields and the trailing 'options' space.
+
+ The ISC DHCP server supports BOOTP clients conforming to RFC951
+ [RFC0951] and RFC1542 [RFC1542].
+
+
+
+
+Hankins & Mrugalski [Page 6]
+
+ ISC DHCP References Collection May 2011
+
+
+5. DHCPv4 Protocol References
+
+5.1. DHCPv4 Protocol
+
+ "The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The DHCPv4
+ Protocol".
+
+5.1.1. Core Protocol References
+
+ RFC2131 [RFC2131] defines the protocol format and procedures. ISC
+ DHCP is not known to diverge from this document in any way. There
+ are, however, a few points on which different implementations have
+ arisen out of vagueries in the document. DHCP Clients exist which,
+ at one time, present themselves as using a Client Identifier Option
+ which is equal to the client's hardware address. Later, the client
+ transmits DHCP packets with no Client Identifier Option present -
+ essentially identifying themselves using the hardware address. Some
+ DHCP Servers have been developed which identify this client as a
+ single client. ISC has interpreted RFC2131 to indicate that these
+ clients must be treated as two separate entities (and hence two,
+ separate addresses). Client behaviour (Embedded Windows products)
+ has developed that relies on the former implementation, and hence is
+ incompatible with the latter. Also, RFC2131 demands explicitly that
+ some header fields be zeroed upon certain message types. The ISC
+ DHCP Server instead copies many of these fields from the packet
+ received from the client or relay, which may not be zero. It is not
+ known if there is a good reason for this that has not been
+ documented.
+
+ RFC2132 [RFC2132] defines the initial set of DHCP Options and
+ provides a great deal of guidance on how to go about formatting and
+ processing options. The document unfortunately waffles to a great
+ extent about the NULL termination of DHCP Options, and some DHCP
+ Clients (Windows 95) have been implemented that rely upon DHCP
+ Options containing text strings to be NULL-terminated (or else they
+ crash). So, ISC DHCP detects if clients null-terminate the host-name
+ option and, if so, null terminates any text options it transmits to
+ the client. It also removes NULL termination from any known text
+ option it receives prior to any other processing.
+
+5.2. DHCPv4 Option References
+
+ RFC2241 [RFC2241] defines options for Novell Directory Services.
+
+ RFC2242 [RFC2242] defines an encapsulated option space for NWIP
+ configuration.
+
+
+
+
+Hankins & Mrugalski [Page 7]
+
+ ISC DHCP References Collection May 2011
+
+
+ RFC2485 [RFC2485] defines the Open Group's UAP option.
+
+ RFC2610 [RFC2610] defines options for the Service Location Protocol
+ (SLP).
+
+ RFC2937 [RFC2937] defines the Name Service Search Option (not to be
+ confused with the domain-search option). The Name Service Search
+ Option allows eg nsswitch.conf to be reconfigured via dhcp. The ISC
+ DHCP server implements this option, and the ISC DHCP client is
+ compatible...but does not by default install this option's value.
+ One would need to make their relevant dhclient-script process this
+ option in a way that is suitable for the system.
+
+ RFC3004 [RFC3004] defines the User-Class option. Note carefully that
+ ISC DHCP currently does not implement to this reference, but has
+ (inexplicably) selected an incompatible format: a plain text string.
+
+ RFC3011 [RFC3011] defines the Subnet-Selection plain DHCPv4 option.
+ Do not confuse this option with the relay agent "link selection" sub-
+ option, although their behaviour is similar.
+
+ RFC3396 [RFC3396] documents both how long options may be encoded in
+ DHCPv4 packets, and also how multiple instances of the same option
+ code within a DHCPv4 packet will be decoded by receivers.
+
+ RFC3397 [RFC3397] documents the Domain-Search Option, which allows
+ the configuration of the /etc/resolv.conf 'search' parameter in a way
+ that is RFC1035 [RFC1035] wire format compatible (in fact, it uses
+ the RFC1035 wire format). ISC DHCP has both client and server
+ support, and supports RFC1035 name compression.
+
+ RFC3679 [RFC3679] documents a number of options that were documented
+ earlier in history, but were not made use of.
+
+ RFC3925 [RFC3925] documents a pair of Enterprise-ID delimited option
+ spaces for vendors to use in order to inform servers of their "vendor
+ class" (sort of like 'uname' or 'who and what am I'), and a means to
+ deliver vendor-specific and vendor-documented option codes and
+ values.
+
+ RFC3942 [RFC3942] redefined the 'site local' option space.
+
+ [RFC4280] defines two BCMS server options for each protocol family.
+
+ RFC4388 [RFC4388] defined the DHCPv4 LEASEQUERY message type and a
+ number of suitable response messages, for the purpose of sharing
+ information about DHCP served addresses and clients.
+
+
+
+
+Hankins & Mrugalski [Page 8]
+
+ ISC DHCP References Collection May 2011
+
+
+5.2.1. Relay Agent Information Option Options
+
+ RFC3046 [RFC3046] defines the Relay Agent Information Option and
+ provides a number of sub-option definitions.
+
+ RFC3256 [RFC3256] defines the DOCSIS Device Class sub-option.
+
+ RFC3527 [RFC3527] defines the Link Selection sub-option.
+
+5.2.2. Dynamic DNS Updates References
+
+ The collection of documents that describe the standards-based method
+ to update dns names of DHCP clients starts most easily with RFC4703
+ [RFC4703] to define the overall architecture, travels through RFCs
+ 4702 [RFC4702] and 4704 [RFC4704] to describe the DHCPv4 and DHCPv6
+ FQDN options (to carry the client name), and ends up at RFC4701
+ [RFC4701] which describes the DHCID RR used in DNS to perform a kind
+ of atomic locking.
+
+ ISC DHCP adopted early versions of these documents, and has not yet
+ synchronized with the final standards versions.
+
+ For RFCs 4702 and 4704, the 'N' bit is not yet supported. The result
+ is that it is always set zero, and is ignored if set.
+
+ For RFC4701, which is used to match client identities with names in
+ the DNS as part of name conflict resolution. Note that ISC DHCP's
+ implementation of DHCIDs vary wildly from this specification. First,
+ ISC DHCP uses a TXT record in which the contents are stored in
+ hexadecimal. Second, there is a flaw in the selection of the
+ 'Identifier Type', which results in a completely different value
+ being selected than was defined in an older revision of this
+ document...also this field is one byte prior to hexadecimal encoding
+ rather than two. Third, ISC DHCP does not use a digest type code.
+ Rather, all values for such TXT records are reached via an MD5 sum.
+ In short, nothing is compatible, but the principle of the TXT record
+ is the same as the standard DHCID record. However, for DHCPv6 FQDN,
+ we do use DHCID type code '2', as no other value really makes sense
+ in our context.
+
+5.2.3. Experimental: Failover References
+
+ The Failover Protocol defines means by which two DHCP Servers can
+ share all the relevant information about leases granted to DHCP
+ clients on given networks, so that one of the two servers may fail
+ and be survived by a server that can act responsibly.
+
+ Unfortunately it has been quite some years (2003) since the last time
+
+
+
+Hankins & Mrugalski [Page 9]
+
+ ISC DHCP References Collection May 2011
+
+
+ this document was edited, and the authors no longer show any interest
+ in fielding comments or improving the document.
+
+ The status of this protocol is very unsure, but ISC's implementation
+ of it has proven stable and suitable for use in sizable production
+ environments.
+
+ draft-ietf-dhc-failover-12.txt [draft-failover] describes the
+ Failover Protocol. In addition to what is described in this
+ document, ISC DHCP has elected to make some experimental changes that
+ may be revoked in a future version of ISC DHCP (if the draft authors
+ do not adopt the new behaviour). Specifically, ISC DHCP's POOLREQ
+ behaviour differs substantially from what is documented in the draft,
+ and the server also implements a form of 'MAC Address Affinity' which
+ is not described in the failover document. The full nature of these
+ changes have been described on the IETF DHC WG mailing list (which
+ has archives), and also in ISC DHCP's manual pages. Also note that
+ although this document references a RECOVER-WAIT state, it does not
+ document a protocol number assignment for this state. As a
+ consequence, ISC DHCP has elected to use the value 254.
+
+ An optimization described in the failover protocol draft is included
+ since 4.2.0a1. It permits a DHCP server operating in communications-
+ interrupted state to 'rewind' a lease to the state most recently
+ transmitted to its peer, greatly increasing a server's endurance in
+ communications-interrupted. This is supported using a new 'rewind
+ state' record on the dhcpd.leases entry for each lease.
+
+ [RFC3074] describes the Load Balancing Algorithm (LBA) that ISC DHCP
+ uses in concert with the Failover protocol. Note that versions 3.0.*
+ are known to misimplement the hash algorithm (it will only use the
+ low 4 bits of every byte of the hash bucket array).
+
+5.3. DHCP Procedures
+
+ [RFC2939] explains how to go about obtaining a new DHCP Option code
+ assignment.
+
+
+6. DHCPv6 Protocol References
+
+6.1. DHCPv6 Protocol References
+
+ For now there is only one document that specifies the base of the
+ DHCPv6 protocol (there have been no updates yet), [RFC3315].
+
+ Support for DHCPv6 was first added in version 4.0.0. The server and
+ client support only IA_NA. While the server does support multiple
+
+
+
+Hankins & Mrugalski [Page 10]
+
+ ISC DHCP References Collection May 2011
+
+
+ IA_NAs within one packet from the client, our client only supports
+ sending one. There is no relay support.
+
+ DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.
+
+ 1. Options sometimes may appear multiple times. The common library
+ used to treat all appearance of multiple options as specified in
+ RFC2131 - to be concatenated. DHCPv6 options may sometimes
+ appear multiple times (such as with IA_NA or IAADDR), but often
+ must not. As of 4.2.1-P1, multiple IA_NA, IA_PD or IA_TA are not
+ supported.
+
+ 2. The same option space appears in DHCPv6 packets multiple times.
+ If the packet was got via a relay, then the client's packet is
+ stored to an option within the relay's packet...if there were two
+ relays, this recurses. At each of these steps, the root "DHCPv6
+ option space" is used. Further, a client packet may contain an
+ IA_NA, which may contain an IAADDR - but really, in an abstract
+ sense, this is again re-encapsulation of the DHCPv6 option space
+ beneath options it also contains.
+
+ Precisely how to correctly support the above conundrums has not quite
+ yet been settled, so support is incomplete.
+
+6.2. DHCPv6 Options References
+
+ [RFC3319] defines the SIP server options for DHCPv6.
+
+ [RFC3646] documents the DHCPv6 name-servers and domain-search
+ options.
+
+ [RFC3633] documents the Identity Association Prefix Delegation for
+ DHCPv6, which is included here for protocol wire reference, but which
+ is not supported by ISC DHCP.
+
+ [RFC3898] documents four NIS options for delivering NIS servers and
+ domain information in DHCPv6.
+
+ [RFC4075] defines the DHCPv6 SNTP Servers option.
+
+ [RFC4242] defines the Information Refresh Time option, which advises
+ DHCPv6 Information-Request clients to return for updated information.
+
+ [RFC4280] defines two BCMS server options for each protocol family.
+
+ [RFC4580] defines a DHCPv6 subscriber-id option, which is similar in
+ principle to the DHCPv4 relay agent option of the same name.
+
+
+
+Hankins & Mrugalski [Page 11]
+
+ ISC DHCP References Collection May 2011
+
+
+ [RFC4649] defines a DHCPv6 remote-id option, which is similar in
+ principle to the DHCPv4 relay agent remote-id.
+
+
+7. References
+
+7.1. Published DHCPv4 References
+
+ [RFC0760] Postel, J., "DoD standard Internet Protocol", RFC 760,
+ January 1980.
+
+ [RFC0768] Postel, J., "User Datagram Protocol", STD 6, RFC 768,
+ August 1980.
+
+ [RFC0894] Hornig, C., "Standard for the transmission of IP datagrams
+ over Ethernet networks", STD 41, RFC 894, April 1984.
+
+ [RFC0951] Croft, B. and J. Gilmore, "Bootstrap Protocol", RFC 951,
+ September 1985.
+
+ [RFC1035] Mockapetris, P., "Domain names - implementation and
+ specification", STD 13, RFC 1035, November 1987.
+
+ [RFC1188] Katz, D., "Proposed Standard for the Transmission of IP
+ Datagrams over FDDI Networks", RFC 1188, October 1990.
+
+ [RFC1542] Wimer, W., "Clarifications and Extensions for the
+ Bootstrap Protocol", RFC 1542, October 1993.
+
+ [RFC2131] Droms, R., "Dynamic Host Configuration Protocol",
+ RFC 2131, March 1997.
+
+ [RFC2132] Alexander, S. and R. Droms, "DHCP Options and BOOTP Vendor
+ Extensions", RFC 2132, March 1997.
+
+ [RFC2241] Provan, D., "DHCP Options for Novell Directory Services",
+ RFC 2241, November 1997.
+
+ [RFC2242] Droms, R. and K. Fong, "NetWare/IP Domain Name and
+ Information", RFC 2242, November 1997.
+
+ [RFC2485] Drach, S., "DHCP Option for The Open Group's User
+ Authentication Protocol", RFC 2485, January 1999.
+
+ [RFC2563] Troll, R., "DHCP Option to Disable Stateless Auto-
+ Configuration in IPv4 Clients", RFC 2563, May 1999.
+
+ [RFC2610] Perkins, C. and E. Guttman, "DHCP Options for Service
+
+
+
+Hankins & Mrugalski [Page 12]
+
+ ISC DHCP References Collection May 2011
+
+
+ Location Protocol", RFC 2610, June 1999.
+
+ [RFC2855] Fujisawa, K., "DHCP for IEEE 1394", RFC 2855, June 2000.
+
+ [RFC2937] Smith, C., "The Name Service Search Option for DHCP",
+ RFC 2937, September 2000.
+
+ [RFC2939] Droms, R., "Procedures and IANA Guidelines for Definition
+ of New DHCP Options and Message Types", BCP 43, RFC 2939,
+ September 2000.
+
+ [RFC3004] Stump, G., Droms, R., Gu, Y., Vyaghrapuri, R., Demirtjis,
+ A., Beser, B., and J. Privat, "The User Class Option for
+ DHCP", RFC 3004, November 2000.
+
+ [RFC3011] Waters, G., "The IPv4 Subnet Selection Option for DHCP",
+ RFC 3011, November 2000.
+
+ [RFC3046] Patrick, M., "DHCP Relay Agent Information Option",
+ RFC 3046, January 2001.
+
+ [RFC3074] Volz, B., Gonczi, S., Lemon, T., and R. Stevens, "DHC Load
+ Balancing Algorithm", RFC 3074, February 2001.
+
+ [RFC3118] Droms, R. and W. Arbaugh, "Authentication for DHCP
+ Messages", RFC 3118, June 2001.
+
+ [RFC3203] T'Joens, Y., Hublet, C., and P. De Schrijver, "DHCP
+ reconfigure extension", RFC 3203, December 2001.
+
+ [RFC3256] Jones, D. and R. Woundy, "The DOCSIS (Data-Over-Cable
+ Service Interface Specifications) Device Class DHCP
+ (Dynamic Host Configuration Protocol) Relay Agent
+ Information Sub-option", RFC 3256, April 2002.
+
+ [RFC3361] Schulzrinne, H., "Dynamic Host Configuration Protocol
+ (DHCP-for-IPv4) Option for Session Initiation Protocol
+ (SIP) Servers", RFC 3361, August 2002.
+
+ [RFC3396] Lemon, T. and S. Cheshire, "Encoding Long Options in the
+ Dynamic Host Configuration Protocol (DHCPv4)", RFC 3396,
+ November 2002.
+
+ [RFC3397] Aboba, B. and S. Cheshire, "Dynamic Host Configuration
+ Protocol (DHCP) Domain Search Option", RFC 3397,
+ November 2002.
+
+ [RFC3442] Lemon, T., Cheshire, S., and B. Volz, "The Classless
+
+
+
+Hankins & Mrugalski [Page 13]
+
+ ISC DHCP References Collection May 2011
+
+
+ Static Route Option for Dynamic Host Configuration
+ Protocol (DHCP) version 4", RFC 3442, December 2002.
+
+ [RFC3456] Patel, B., Aboba, B., Kelly, S., and V. Gupta, "Dynamic
+ Host Configuration Protocol (DHCPv4) Configuration of
+ IPsec Tunnel Mode", RFC 3456, January 2003.
+
+ [RFC3495] Beser, B. and P. Duffy, "Dynamic Host Configuration
+ Protocol (DHCP) Option for CableLabs Client
+ Configuration", RFC 3495, March 2003.
+
+ [RFC3527] Kinnear, K., Stapp, M., Johnson, R., and J. Kumarasamy,
+ "Link Selection sub-option for the Relay Agent Information
+ Option for DHCPv4", RFC 3527, April 2003.
+
+ [RFC3594] Duffy, P., "PacketCable Security Ticket Control Sub-Option
+ for the DHCP CableLabs Client Configuration (CCC) Option",
+ RFC 3594, September 2003.
+
+ [RFC3634] Luehrs, K., Woundy, R., Bevilacqua, J., and N. Davoust,
+ "Key Distribution Center (KDC) Server Address Sub-option
+ for the Dynamic Host Configuration Protocol (DHCP)
+ CableLabs Client Configuration (CCC) Option", RFC 3634,
+ December 2003.
+
+ [RFC3679] Droms, R., "Unused Dynamic Host Configuration Protocol
+ (DHCP) Option Codes", RFC 3679, January 2004.
+
+ [RFC3825] Polk, J., Schnizlein, J., and M. Linsner, "Dynamic Host
+ Configuration Protocol Option for Coordinate-based
+ Location Configuration Information", RFC 3825, July 2004.
+
+ [RFC3925] Littlefield, J., "Vendor-Identifying Vendor Options for
+ Dynamic Host Configuration Protocol version 4 (DHCPv4)",
+ RFC 3925, October 2004.
+
+ [RFC3942] Volz, B., "Reclassifying Dynamic Host Configuration
+ Protocol version 4 (DHCPv4) Options", RFC 3942,
+ November 2004.
+
+ [RFC3993] Johnson, R., Palaniappan, T., and M. Stapp, "Subscriber-ID
+ Suboption for the Dynamic Host Configuration Protocol
+ (DHCP) Relay Agent Option", RFC 3993, March 2005.
+
+ [RFC4014] Droms, R. and J. Schnizlein, "Remote Authentication
+ Dial-In User Service (RADIUS) Attributes Suboption for the
+ Dynamic Host Configuration Protocol (DHCP) Relay Agent
+ Information Option", RFC 4014, February 2005.
+
+
+
+Hankins & Mrugalski [Page 14]
+
+ ISC DHCP References Collection May 2011
+
+
+ [RFC4030] Stapp, M. and T. Lemon, "The Authentication Suboption for
+ the Dynamic Host Configuration Protocol (DHCP) Relay Agent
+ Option", RFC 4030, March 2005.
+
+ [RFC4039] Park, S., Kim, P., and B. Volz, "Rapid Commit Option for
+ the Dynamic Host Configuration Protocol version 4
+ (DHCPv4)", RFC 4039, March 2005.
+
+ [RFC4174] Monia, C., Tseng, J., and K. Gibbons, "The IPv4 Dynamic
+ Host Configuration Protocol (DHCP) Option for the Internet
+ Storage Name Service", RFC 4174, September 2005.
+
+ [RFC4243] Stapp, M., Johnson, R., and T. Palaniappan, "Vendor-
+ Specific Information Suboption for the Dynamic Host
+ Configuration Protocol (DHCP) Relay Agent Option",
+ RFC 4243, December 2005.
+
+ [RFC4361] Lemon, T. and B. Sommerfeld, "Node-specific Client
+ Identifiers for Dynamic Host Configuration Protocol
+ Version Four (DHCPv4)", RFC 4361, February 2006.
+
+ [RFC4388] Woundy, R. and K. Kinnear, "Dynamic Host Configuration
+ Protocol (DHCP) Leasequery", RFC 4388, February 2006.
+
+ [RFC4390] Kashyap, V., "Dynamic Host Configuration Protocol (DHCP)
+ over InfiniBand", RFC 4390, April 2006.
+
+ [RFC4436] Aboba, B., Carlson, J., and S. Cheshire, "Detecting
+ Network Attachment in IPv4 (DNAv4)", RFC 4436, March 2006.
+
+ [RFC4701] Stapp, M., Lemon, T., and A. Gustafsson, "A DNS Resource
+ Record (RR) for Encoding Dynamic Host Configuration
+ Protocol (DHCP) Information (DHCID RR)", RFC 4701,
+ October 2006.
+
+ [RFC4702] Stapp, M., Volz, B., and Y. Rekhter, "The Dynamic Host
+ Configuration Protocol (DHCP) Client Fully Qualified
+ Domain Name (FQDN) Option", RFC 4702, October 2006.
+
+ [RFC4703] Stapp, M. and B. Volz, "Resolution of Fully Qualified
+ Domain Name (FQDN) Conflicts among Dynamic Host
+ Configuration Protocol (DHCP) Clients", RFC 4703,
+ October 2006.
+
+ [RFC5010] Kinnear, K., Normoyle, M., and M. Stapp, "The Dynamic Host
+ Configuration Protocol Version 4 (DHCPv4) Relay Agent
+ Flags Suboption", RFC 5010, September 2007.
+
+
+
+
+Hankins & Mrugalski [Page 15]
+
+ ISC DHCP References Collection May 2011
+
+
+ [RFC5071] Hankins, D., "Dynamic Host Configuration Protocol Options
+ Used by PXELINUX", RFC 5071, December 2007.
+
+ [RFC5107] Johnson, R., Kumarasamy, J., Kinnear, K., and M. Stapp,
+ "DHCP Server Identifier Override Suboption", RFC 5107,
+ February 2008.
+
+ [RFC5192] Morand, L., Yegin, A., Kumar, S., and S. Madanapalli,
+ "DHCP Options for Protocol for Carrying Authentication for
+ Network Access (PANA) Authentication Agents", RFC 5192,
+ May 2008.
+
+ [RFC5223] Schulzrinne, H., Polk, J., and H. Tschofenig, "Discovering
+ Location-to-Service Translation (LoST) Servers Using the
+ Dynamic Host Configuration Protocol (DHCP)", RFC 5223,
+ August 2008.
+
+ [RFC5859] Johnson, R., "TFTP Server Address Option for DHCPv4",
+ RFC 5859, June 2010.
+
+ [RFC5969] Townsley, W. and O. Troan, "IPv6 Rapid Deployment on IPv4
+ Infrastructures (6rd) -- Protocol Specification",
+ RFC 5969, August 2010.
+
+ [draft-failover]
+ Droms, R., "DHCP Failover Protocol", March 2003.
+
+ [I-D.ietf-dhc-dhcpv4-relay-encapsulation]
+ Lemon, T. and H. Deng, "Relay Agent Encapsulation for
+ DHCPv4", draft-ietf-dhc-dhcpv4-relay-encapsulation-00
+ (work in progress), October 2010.
+
+ [I-D.ietf-dhc-dhcpv4-bulk-leasequery]
+ Kinnear, K., Volz, B., Russell, N., Stapp, M., Rao, D.,
+ Joshi, B., and P. Kurapati, "Bulk DHCPv4 Lease Query",
+ draft-ietf-dhc-dhcpv4-bulk-leasequery-03 (work in
+ progress), October 2010.
+
+ [I-D.ietf-dhc-leasequery-by-remote-id]
+ Kurapati, P. and B. Joshi, "DHCPv4 lease query by Relay
+ Agent Remote ID",
+ draft-ietf-dhc-leasequery-by-remote-id-09 (work in
+ progress), December 2010.
+
+ [I-D.ietf-dhc-relay-id-suboption]
+ Stapp, M., "The DHCPv4 Relay Agent Identifier Suboption",
+ draft-ietf-dhc-relay-id-suboption-07 (work in progress),
+ July 2009.
+
+
+
+Hankins & Mrugalski [Page 16]
+
+ ISC DHCP References Collection May 2011
+
+
+ [I-D.ietf-mip6-hiopt]
+ Jang, H., Yegin, A., Chowdhury, K., and J. Choi, "DHCP
+ Options for Home Information Discovery in MIPv6",
+ draft-ietf-mip6-hiopt-17 (work in progress), May 2008.
+
+7.2. Published Common (DHCPv4/DHCPv6) References
+
+ [RFC4280] Chowdhury, K., Yegani, P., and L. Madour, "Dynamic Host
+ Configuration Protocol (DHCP) Options for Broadcast and
+ Multicast Control Servers", RFC 4280, November 2005.
+
+ [RFC4477] Chown, T., Venaas, S., and C. Strauf, "Dynamic Host
+ Configuration Protocol (DHCP): IPv4 and IPv6 Dual-Stack
+ Issues", RFC 4477, May 2006.
+
+ [RFC4578] Johnston, M. and S. Venaas, "Dynamic Host Configuration
+ Protocol (DHCP) Options for the Intel Preboot eXecution
+ Environment (PXE)", RFC 4578, November 2006.
+
+ [RFC4776] Schulzrinne, H., "Dynamic Host Configuration Protocol
+ (DHCPv4 and DHCPv6) Option for Civic Addresses
+ Configuration Information", RFC 4776, November 2006.
+
+ [RFC4833] Lear, E. and P. Eggert, "Timezone Options for DHCP",
+ RFC 4833, April 2007.
+
+ [RFC5417] Calhoun, P., "Control And Provisioning of Wireless Access
+ Points (CAPWAP) Access Controller DHCP Option", RFC 5417,
+ March 2009.
+
+ [RFC5678] Bajko, G. and S. Das, "Dynamic Host Configuration Protocol
+ (DHCPv4 and DHCPv6) Options for IEEE 802.21 Mobility
+ Services (MoS) Discovery", RFC 5678, December 2009.
+
+ [RFC5908] Gayraud, R. and B. Lourdelet, "Network Time Protocol (NTP)
+ Server Option for DHCPv6", RFC 5908, June 2010.
+
+ [RFC5970] Huth, T., Freimann, J., Zimmer, V., and D. Thaler, "DHCPv6
+ Options for Network Boot", RFC 5970, September 2010.
+
+ [RFC5986] Thomson, M. and J. Winterbottom, "Discovering the Local
+ Location Information Server (LIS)", RFC 5986,
+ September 2010.
+
+ [I-D.ietf-dhc-vpn-option]
+ Kinnear, K., Johnson, R., and M. Stapp, "Virtual Subnet
+ Selection Options for DHCPv4 and DHCPv6",
+ draft-ietf-dhc-vpn-option-12 (work in progress),
+
+
+
+Hankins & Mrugalski [Page 17]
+
+ ISC DHCP References Collection May 2011
+
+
+ October 2010.
+
+7.3. Published DHCPv6 References
+
+ [RFC3315] Droms, R., Bound, J., Volz, B., Lemon, T., Perkins, C.,
+ and M. Carney, "Dynamic Host Configuration Protocol for
+ IPv6 (DHCPv6)", RFC 3315, July 2003.
+
+ [RFC3319] Schulzrinne, H. and B. Volz, "Dynamic Host Configuration
+ Protocol (DHCPv6) Options for Session Initiation Protocol
+ (SIP) Servers", RFC 3319, July 2003.
+
+ [RFC3633] Troan, O. and R. Droms, "IPv6 Prefix Options for Dynamic
+ Host Configuration Protocol (DHCP) version 6", RFC 3633,
+ December 2003.
+
+ [RFC3646] Droms, R., "DNS Configuration options for Dynamic Host
+ Configuration Protocol for IPv6 (DHCPv6)", RFC 3646,
+ December 2003.
+
+ [RFC3736] Droms, R., "Stateless Dynamic Host Configuration Protocol
+ (DHCP) Service for IPv6", RFC 3736, April 2004.
+
+ [RFC3898] Kalusivalingam, V., "Network Information Service (NIS)
+ Configuration Options for Dynamic Host Configuration
+ Protocol for IPv6 (DHCPv6)", RFC 3898, October 2004.
+
+ [RFC4075] Kalusivalingam, V., "Simple Network Time Protocol (SNTP)
+ Configuration Option for DHCPv6", RFC 4075, May 2005.
+
+ [RFC4076] Chown, T., Venaas, S., and A. Vijayabhaskar, "Renumbering
+ Requirements for Stateless Dynamic Host Configuration
+ Protocol for IPv6 (DHCPv6)", RFC 4076, May 2005.
+
+ [RFC4242] Venaas, S., Chown, T., and B. Volz, "Information Refresh
+ Time Option for Dynamic Host Configuration Protocol for
+ IPv6 (DHCPv6)", RFC 4242, November 2005.
+
+ [RFC4580] Volz, B., "Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6) Relay Agent Subscriber-ID Option", RFC 4580,
+ June 2006.
+
+ [RFC4649] Volz, B., "Dynamic Host Configuration Protocol for IPv6
+ (DHCPv6) Relay Agent Remote-ID Option", RFC 4649,
+ August 2006.
+
+ [RFC4704] Volz, B., "The Dynamic Host Configuration Protocol for
+ IPv6 (DHCPv6) Client Fully Qualified Domain Name (FQDN)
+
+
+
+Hankins & Mrugalski [Page 18]
+
+ ISC DHCP References Collection May 2011
+
+
+ Option", RFC 4704, October 2006.
+
+ [RFC4994] Zeng, S., Volz, B., Kinnear, K., and J. Brzozowski,
+ "DHCPv6 Relay Agent Echo Request Option", RFC 4994,
+ September 2007.
+
+ [RFC5007] Brzozowski, J., Kinnear, K., Volz, B., and S. Zeng,
+ "DHCPv6 Leasequery", RFC 5007, September 2007.
+
+ [RFC5460] Stapp, M., "DHCPv6 Bulk Leasequery", RFC 5460,
+ February 2009.
+
+ [I-D.ietf-mif-dhcpv6-route-option]
+ Dec, W., Mrugalski, T., Sun, T., and B. Sarikaya, "DHCPv6
+ Route Option", draft-ietf-mif-dhcpv6-route-option-01 (work
+ in progress), March 2011.
+
+ [I-D.ietf-dhc-dhcpv6-ldra]
+ Miles, D., Ooghe, S., Dec, W., Krishnan, S., and A.
+ Kavanagh, "Lightweight DHCPv6 Relay Agent",
+ draft-ietf-dhc-dhcpv6-ldra-03 (work in progress),
+ October 2010.
+
+ [I-D.ietf-dhc-dhcpv6-relay-supplied-options]
+ Lemon, T. and W. Wu, "Relay-Supplied DHCP Options",
+ draft-ietf-dhc-dhcpv6-relay-supplied-options-06 (work in
+ progress), May 2011.
+
+ [I-D.ietf-dhc-pd-exclude]
+ Korhonen, J., Savolainen, T., Krishnan, S., and O. Troan,
+ "Prefix Exclude Option for DHCPv6-based Prefix
+ Delegation", draft-ietf-dhc-pd-exclude-01 (work in
+ progress), January 2011.
+
+ [I-D.ietf-dhc-secure-dhcpv6]
+ Jiang, S., "Secure DHCPv6 Using CGAs",
+ draft-ietf-dhc-secure-dhcpv6-02 (work in progress),
+ December 2010.
+
+ [I-D.ietf-mext-nemo-pd]
+ Droms, R., Thubert, P., Dupont, F., Haddad, W., and C.
+ Bernardos, "DHCPv6 Prefix Delegation for NEMO",
+ draft-ietf-mext-nemo-pd-07 (work in progress),
+ December 2010.
+
+ [I-D.ietf-dhc-duid-uuid]
+ Narten, T. and J. Johnson, "Definition of the UUID-based
+ DHCPv6 Unique Identifier (DUID-UUID)",
+
+
+
+Hankins & Mrugalski [Page 19]
+
+ ISC DHCP References Collection May 2011
+
+
+ draft-ietf-dhc-duid-uuid-03 (work in progress),
+ February 2011.
+
+ [I-D.ietf-softwire-ds-lite-tunnel-option]
+ Hankins, D. and T. Mrugalski, "Dynamic Host Configuration
+ Protocol for IPv6 (DHCPv6) Option for Dual- Stack Lite",
+ draft-ietf-softwire-ds-lite-tunnel-option-10 (work in
+ progress), March 2011.
+
+ [I-D.ietf-mif-dns-server-selection]
+ Savolainen, T. and J. Kato, "Improved DNS Server Selection
+ for Multi-Homed Nodes",
+ draft-ietf-mif-dns-server-selection-01 (work in progress),
+ March 2011.
+
+ [I-D.ietf-geopriv-rfc3825bis]
+ Polk, J., Linsner, M., Thomson, M., and B. Aboba, "Dynamic
+ Host Configuration Protocol Options for Coordinate-based
+ Location Configuration Information",
+ draft-ietf-geopriv-rfc3825bis-17 (work in progress),
+ February 2011.
+
+ [draft-addr-params]
+ Mrugalski, T., "Address Parameters Option for DHCPv6",
+ April 2007.
+
+
+Authors' Addresses
+
+ David W. Hankins
+ Internet Systems Consortium, Inc.
+ 950 Charter Street
+ Redwood City, CA 94063
+
+
+ Tomasz Mrugalski
+ Internet Systems Consortium, Inc.
+ 950 Charter Street
+ Redwood City, CA 94063
+
+ Phone: +1 650 423 1345
+ Email: Tomasz_Mrugalski@isc.org
+
+
+
+
+
+
+
+
+
+Hankins & Mrugalski [Page 20]
+
diff --git a/doc/References.xml b/doc/References.xml
new file mode 100644
index 0000000..de19fd0
--- /dev/null
+++ b/doc/References.xml
@@ -0,0 +1,788 @@
+<?xml version='1.0' ?>
+
+<!-- $Id: References.xml,v 1.4.24.3 2011-07-05 16:57:20 sar Exp $ -->
+
+<?rfc private="ISC-DHCP-REFERENCES" ?>
+
+<?rfc toc="yes"?>
+
+<?rfc compact="yes"?>
+<?rfc subcompact="no"?>
+<?rfc tocompact="no"?>
+<?rfc symrefs="yes"?>
+
+<!DOCTYPE rfc SYSTEM 'rfc2629bis.dtd' [
+ <!ENTITY rfc760 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0760.xml'>
+ <!ENTITY rfc768 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0768.xml'>
+ <!ENTITY rfc894 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0894.xml'>
+ <!ENTITY rfc951 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.0951.xml'>
+ <!ENTITY rfc1035 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1035.xml'>
+ <!ENTITY rfc1188 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1188.xml'>
+ <!ENTITY rfc1542 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.1542.xml'>
+ <!ENTITY rfc2131 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2131.xml'>
+ <!ENTITY rfc2132 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2132.xml'>
+ <!ENTITY rfc2241 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2241.xml'>
+ <!ENTITY rfc2242 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2242.xml'>
+ <!ENTITY rfc2485 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2485.xml'>
+ <!ENTITY rfc2610 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2610.xml'>
+ <!ENTITY rfc2937 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2937.xml'>
+ <!ENTITY rfc2939 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.2939.xml'>
+ <!ENTITY rfc3004 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3004.xml'>
+ <!ENTITY rfc3011 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3011.xml'>
+ <!ENTITY rfc3046 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3046.xml'>
+ <!ENTITY rfc3074 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3074.xml'>
+ <!ENTITY rfc3256 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3256.xml'>
+ <!ENTITY rfc3315 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3315.xml'>
+ <!ENTITY rfc3319 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3319.xml'>
+ <!ENTITY rfc3396 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3396.xml'>
+ <!ENTITY rfc3397 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3397.xml'>
+ <!ENTITY rfc3527 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3527.xml'>
+ <!ENTITY rfc3633 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3633.xml'>
+ <!ENTITY rfc3646 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3646.xml'>
+ <!ENTITY rfc3679 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3679.xml'>
+ <!ENTITY rfc3898 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3898.xml'>
+ <!ENTITY rfc3925 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3925.xml'>
+ <!ENTITY rfc3942 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.3942.xml'>
+ <!ENTITY rfc4075 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4075.xml'>
+ <!ENTITY rfc4242 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4242.xml'>
+ <!ENTITY rfc4361 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4361.xml'>
+ <!ENTITY rfc4388 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4388.xml'>
+ <!ENTITY rfc4580 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4580.xml'>
+ <!ENTITY rfc4649 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4649.xml'>
+ <!ENTITY rfc4701 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4701.xml'>
+ <!ENTITY rfc4702 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4702.xml'>
+ <!ENTITY rfc4703 PUBLIC ''
+ 'http://xml.resource.org/public/rfc/bibxml/reference.RFC.4703.xml'>
+ ]>
+
+
+
+<rfc ipr="none">
+ <front>
+ <title>ISC DHCP References Collection</title>
+
+ <author initials="D.H." surname="Hankins" fullname="David W. Hankins">
+ <organization abbrev="ISC">Internet Systems Consortium,
+ Inc.
+ </organization>
+
+ <address>
+ <postal>
+ <street>950 Charter Street</street>
+ <city>Redwood City</city>
+ <region>CA</region>
+ <code>94063</code>
+ </postal>
+ </address>
+ </author>
+
+ <author initials="T." surname="Mrugalski" fullname="Tomasz Mrugalski">
+ <organization abbrev="ISC">Internet Systems Consortium,
+ Inc.
+ </organization>
+
+ <address>
+ <postal>
+ <street>950 Charter Street</street>
+ <city>Redwood City</city>
+ <region>CA</region>
+ <code>94063</code>
+ </postal>
+
+ <phone>+1 650 423 1345</phone>
+ <email>Tomasz_Mrugalski@isc.org</email>
+ </address>
+ </author>
+
+ <date day="20" month="May" year="2011"/>
+
+ <keyword>ISC</keyword>
+ <keyword>DHCP</keyword>
+ <keyword>Reference Implementation</keyword>
+
+ <abstract>
+ <t>This document describes a collection of reference material
+ to which ISC DHCP has been implemented as well as a more
+ complete listing of references for DHCP and DHCPv6 protocols.</t>
+ </abstract>
+
+ <note title="Copyright Notice">
+ <t>Copyright (c) 2006-2007,2009,2011 by Internet Systems
+ Consortium, Inc. ("ISC")</t>
+
+ <t>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.</t>
+
+ <t>THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</t>
+ </note>
+
+ </front>
+
+ <middle>
+ <section title="Introduction">
+ <t>As a little historical anecdote, ISC DHCP once packaged all the
+ relevant RFCs and standards documents along with the software
+ package. Until one day when a voice was heard from one of the
+ many fine institutions that build and distribute this software...
+ they took issue with the IETF's copyright on the RFC's. It
+ seems the IETF's copyrights don't allow modification of RFC's
+ (except for translation purposes).</t>
+
+ <t>Our main purpose in providing the RFCs is to aid in
+ documentation, but since RFCs are now available widely from many
+ points of distribution on the Internet, there is no real need to
+ provide the documents themselves. So, this document has been
+ created in their stead, to list the various IETF RFCs one might
+ want to read, and to comment on how well (or poorly) we have
+ managed to implement them.</t>
+ </section>
+
+ <section title="Definition: Reference Implementation">
+ <t>ISC DHCP, much like its other cousins in ISC software, is
+ self-described as a 'Reference Implementation.' There has been
+ a great deal of confusion about this term. Some people seem to
+ think that this term applies to any software that once passed
+ a piece of reference material on its way to market (but may do
+ quite a lot of things that aren't described in any reference, or
+ may choose to ignore the reference it saw entirely). Other folks
+ get confused by the word 'reference' and understand that to mean
+ that there is some special status applied to the software - that
+ the software itself is the reference by which all other software
+ is measured. Something along the lines of being "The DHCP
+ Protocol's Reference Clock," it is supposed.</t>
+
+ <t>The truth is actually quite a lot simpler. Reference
+ implementations are software packages which were written
+ to behave precisely as appears in reference material. They
+ are written "to match reference."</t>
+
+ <t>If the software has a behaviour that manifests itself
+ externally (whether it be something as simple as the 'wire
+ format' or something higher level, such as a complicated
+ behaviour that arises from multiple message exchanges), that
+ behaviour must be found in a reference document.</t>
+
+ <t>Anything else is a bug, the only question is whether the
+ bug is in reference or software (failing to implement the
+ reference).</t>
+
+ <t>This means:</t>
+
+ <t>
+ <list style="symbols">
+ <t>To produce new externally-visible behaviour, one must first
+ provide a reference.</t>
+
+ <t>Before changing externally visible behaviour to work around
+ simple incompatibilities in any other implementation, one must
+ first provide a reference.</t>
+ </list>
+ </t>
+
+ <t>That is the lofty goal, at any rate. It's well understood that,
+ especially because the ISC DHCP Software package has not always been
+ held to this standard (but not entirely due to it), there are many
+ non-referenced behaviours within ISC DHCP.</t>
+
+ <t>The primary goal of reference implementation is to prove the
+ reference material. If the reference material is good, then you
+ should be able to sit down and write a program that implements the
+ reference, to the word, and come to an implementation that
+ is distinguishable from others in the details, but not in the
+ facts of operating the protocol. This means that there is no
+ need for 'special knowledge' to work around arcane problems that
+ were left undocumented. No secret handshakes need to be learned
+ to be imparted with the necessary "real documentation".</t>
+
+ <t>Also, by accepting only reference as the guidebook for ISC
+ DHCP's software implementation, anyone who can make an impact on
+ the color texture or form of that reference has a (somewhat
+ indirect) voice in ISC DHCP's software design. As the IETF RFC's
+ have been selected as the source of reference, that means everyone
+ on the Internet with the will to participate has a say.</t>
+ </section>
+
+ <section title="Low Layer References">
+ <t>It may surprise you to realize that ISC DHCP implements 802.1
+ 'Ethernet' framing, Token Ring, and FDDI. In order to bridge the
+ gap there between these physical and DHCP layers, it must also
+ implement IP and UDP framing.</t>
+
+ <t>The reason for this stems from Unix systems' handling of BSD
+ sockets (the general way one might engage in transmission of UDP
+ packets) on unconfigured interfaces, or even the handling of
+ broadcast addressing on configured interfaces.</t>
+
+ <t>There are a few things that DHCP servers, relays, and clients all
+ need to do in order to speak the DHCP protocol in strict compliance
+ with <xref target="RFC2131"/>.
+
+ <list style="numbers">
+ <t>Transmit a UDP packet from IP:0.0.0.0 Ethernet:Self, destined to
+ IP:255.255.255.255 LinkLayer:Broadcast on an unconfigured (no IP
+ address yet) interface.</t>
+
+ <t>Receive a UDP packet from IP:remote-system LinkLayer:remote-system,
+ destined to IP:255.255.255.255 LinkLayer:Broadcast, again on an
+ unconfigured interface.</t>
+
+ <t>Transmit a UDP packet from IP:Self, Ethernet:Self, destined to
+ IP:remote-system LinkLayer:remote-system, without transmitting a
+ single ARP.</t>
+
+ <t>And of course the simple case, a regular IP unicast that is
+ routed via the usual means (so it may be direct to a local system,
+ with ARP providing the glue, or it may be to a remote system via
+ one or more routers as normal). In this case, the interfaces are
+ always configured.</t>
+ </list></t>
+
+ <t>The above isn't as simple as it sounds on a regular BSD socket.
+ Many unix implementations will transmit broadcasts not to
+ 255.255.255.255, but to x.y.z.255 (where x.y.z is the system's local
+ subnet). Such packets are not received by several known DHCP client
+ implementations - and it's not their fault, <xref target="RFC2131"/>
+ very explicitly demands that these packets' IP destination
+ addresses be set to 255.255.255.255.</t>
+
+ <t>Receiving packets sent to 255.255.255.255 isn't a problem on most
+ modern unixes...so long as the interface is configured. When there
+ is no IPv4 address on the interface, things become much more murky.</t>
+
+ <t>So, for this convoluted and unfortunate state of affairs in the
+ unix systems of the day ISC DHCP was manufactured, in order to do
+ what it needs not only to implement the reference but to interoperate
+ with other implementations, the software must create some form of
+ raw socket to operate on.</t>
+
+ <t>What it actually does is create, for each interface detected on
+ the system, a Berkeley Packet Filter socket (or equivalent), and
+ program it with a filter that brings in only DHCP packets. A
+ "fallback" UDP Berkeley socket is generally also created, a single
+ one no matter how many interfaces. Should the software need to
+ transmit a contrived packet to the local network the packet is
+ formed piece by piece and transmitted via the BPF socket. Hence
+ the need to implement many forms of Link Layer framing and above.
+ The software gets away with not having to implement IP routing
+ tables as well by simply utilizing the aforementioned 'fallback'
+ UDP socket when unicasting between two configured systems is
+ needed.</t>
+
+ <t>Modern unixes have opened up some facilities that diminish how
+ much of this sort of nefarious kludgery is necessary, but have not
+ found the state of affairs absolutely resolved. In particular,
+ one might now unicast without ARP by inserting an entry into the
+ ARP cache prior to transmitting. Unconfigured interfaces remain
+ the sticking point, however...on virtually no modern unixes is
+ it possible to receive broadcast packets unless a local IPv4
+ address has been configured, unless it is done with raw sockets.</t>
+
+ <section title="Ethernet Protocol References">
+ <t>ISC DHCP Implements Ethernet Version 2 ("DIX"), which is a variant
+ of IEEE 802.2. No good reference of this framing is known to exist
+ at this time, but it is vaguely described in <xref target="RFC0894"/>
+ see the section titled "Packet format"), and
+ the following URL is also thought to be useful.</t>
+
+ <t><eref target="http://en.wikipedia.org/wiki/DIX_Ethernet">http://en.wikipedia.org/wiki/DIX_Ethernet</eref></t>
+ </section>
+
+ <section title="Token Ring Protocol References">
+ <t>IEEE 802.5 defines the Token Ring framing format used by ISC
+ DHCP.</t>
+ </section>
+
+ <section title="FDDI Protocol References">
+ <t><xref target="RFC1188"/> is the most helpful
+ reference ISC DHCP has used to form FDDI packets.</t>
+ </section>
+
+ <section title="Internet Protocol Version 4 References">
+ <t><xref target="RFC0760">RFC760</xref> fundamentally defines the
+ bare IPv4 protocol which ISC DHCP implements.</t>
+ </section>
+
+ <section title="Unicast Datagram Protocol References">
+ <t><xref target="RFC0768">RFC768</xref> defines the User Datagram
+ Protocol that ultimately carries the DHCP or BOOTP protocol. The
+ destination DHCP server port is 67, the client port is 68. Source
+ ports are irrelevant.</t>
+ </section>
+ </section>
+
+ <section title="BOOTP Protocol References">
+ <t>The DHCP Protocol is strange among protocols in that it is
+ grafted over the top of another protocol - BOOTP (but we don't
+ call it "DHCP over BOOTP" like we do, say "TCP over IP"). BOOTP
+ and DHCP share UDP packet formats - DHCP is merely a conventional
+ use of both BOOTP header fields and the trailing 'options' space.</t>
+
+ <t>The ISC DHCP server supports BOOTP clients conforming to
+ <xref target="RFC0951">RFC951</xref> and <xref target="RFC1542">
+ RFC1542</xref>.</t>
+ </section>
+
+ <section title="DHCPv4 Protocol References">
+ <section title="DHCPv4 Protocol">
+ <t>"The DHCP[v4] Protocol" is not defined in a single document. The
+ following collection of references of what ISC DHCP terms "The
+ DHCPv4 Protocol".</t>
+
+ <section title="Core Protocol References">
+ <t><xref target="RFC2131">RFC2131</xref> defines the protocol format
+ and procedures. ISC DHCP is not known to diverge from this document
+ in any way. There are, however, a few points on which different
+ implementations have arisen out of vagueries in the document.
+ DHCP Clients exist which, at one time, present themselves as using
+ a Client Identifier Option which is equal to the client's hardware
+ address. Later, the client transmits DHCP packets with no Client
+ Identifier Option present - essentially identifying themselves using
+ the hardware address. Some DHCP Servers have been developed which
+ identify this client as a single client. ISC has interpreted
+ RFC2131 to indicate that these clients must be treated as two
+ separate entities (and hence two, separate addresses). Client
+ behaviour (Embedded Windows products) has developed that relies on
+ the former implementation, and hence is incompatible with the
+ latter. Also, RFC2131 demands explicitly that some header fields
+ be zeroed upon certain message types. The ISC DHCP Server instead
+ copies many of these fields from the packet received from the client
+ or relay, which may not be zero. It is not known if there is a good
+ reason for this that has not been documented.</t>
+
+ <t><xref target="RFC2132">RFC2132</xref> defines the initial set of
+ DHCP Options and provides a great deal of guidance on how to go about
+ formatting and processing options. The document unfortunately
+ waffles to a great extent about the NULL termination of DHCP Options,
+ and some DHCP Clients (Windows 95) have been implemented that rely
+ upon DHCP Options containing text strings to be NULL-terminated (or
+ else they crash). So, ISC DHCP detects if clients null-terminate the
+ host-name option and, if so, null terminates any text options it
+ transmits to the client. It also removes NULL termination from any
+ known text option it receives prior to any other processing.</t>
+ </section>
+ </section>
+
+ <section title="DHCPv4 Option References">
+ <t><xref target="RFC2241">RFC2241</xref> defines options for
+ Novell Directory Services.</t>
+
+ <t><xref target="RFC2242">RFC2242</xref> defines an encapsulated
+ option space for NWIP configuration.</t>
+
+ <t><xref target="RFC2485">RFC2485</xref> defines the Open Group's
+ UAP option.</t>
+
+ <t><xref target="RFC2610">RFC2610</xref> defines options for
+ the Service Location Protocol (SLP).</t>
+
+ <t><xref target="RFC2937">RFC2937</xref> defines the Name Service
+ Search Option (not to be confused with the domain-search option).
+ The Name Service Search Option allows eg nsswitch.conf to be
+ reconfigured via dhcp. The ISC DHCP server implements this option,
+ and the ISC DHCP client is compatible...but does not by default
+ install this option's value. One would need to make their relevant
+ dhclient-script process this option in a way that is suitable for
+ the system.</t>
+
+ <t><xref target="RFC3004">RFC3004</xref> defines the User-Class
+ option. Note carefully that ISC DHCP currently does not implement
+ to this reference, but has (inexplicably) selected an incompatible
+ format: a plain text string.</t>
+
+ <t><xref target="RFC3011">RFC3011</xref> defines the Subnet-Selection
+ plain DHCPv4 option. Do not confuse this option with the relay agent
+ "link selection" sub-option, although their behaviour is
+ similar.</t>
+
+ <t><xref target="RFC3396">RFC3396</xref> documents both how long
+ options may be encoded in DHCPv4 packets, and also how multiple
+ instances of the same option code within a DHCPv4 packet will be
+ decoded by receivers.</t>
+
+ <t><xref target="RFC3397">RFC3397</xref> documents the Domain-Search
+ Option, which allows the configuration of the /etc/resolv.conf
+ 'search' parameter in a way that is <xref target="RFC1035">RFC1035
+ </xref> wire format compatible (in fact, it uses the RFC1035 wire
+ format). ISC DHCP has both client and server support, and supports
+ RFC1035 name compression.</t>
+
+ <t><xref target="RFC3679">RFC3679</xref> documents a number of
+ options that were documented earlier in history, but were not
+ made use of.</t>
+
+ <t><xref target="RFC3925">RFC3925</xref> documents a pair of
+ Enterprise-ID delimited option spaces for vendors to use in order
+ to inform servers of their "vendor class" (sort of like 'uname'
+ or 'who and what am I'), and a means to deliver vendor-specific
+ and vendor-documented option codes and values.</t>
+
+ <t><xref target="RFC3942">RFC3942</xref> redefined the 'site local'
+ option space.</t>
+
+ <t><xref target="RFC4280" /> defines two BCMS server options
+ for each protocol family.</t>
+
+ <t><xref target="RFC4388">RFC4388</xref> defined the DHCPv4
+ LEASEQUERY message type and a number of suitable response messages,
+ for the purpose of sharing information about DHCP served addresses
+ and clients.</t>
+
+ <section title="Relay Agent Information Option Options">
+ <t><xref target="RFC3046">RFC3046</xref> defines the Relay Agent
+ Information Option and provides a number of sub-option
+ definitions.</t>
+
+ <t><xref target="RFC3256">RFC3256</xref> defines the DOCSIS Device
+ Class sub-option.</t>
+
+ <t><xref target="RFC3527">RFC3527</xref> defines the Link Selection
+ sub-option.</t>
+ </section>
+
+
+ <section title="Dynamic DNS Updates References">
+ <t>The collection of documents that describe the standards-based
+ method to update dns names of DHCP clients starts most easily
+ with <xref target="RFC4703">RFC4703</xref> to define the overall
+ architecture, travels through RFCs <xref target="RFC4702">4702</xref>
+ and <xref target="RFC4704">4704</xref> to describe the DHCPv4 and
+ DHCPv6 FQDN options (to carry the client name), and ends up at
+ <xref target="RFC4701">RFC4701</xref> which describes the DHCID
+ RR used in DNS to perform a kind of atomic locking.</t>
+
+ <t>ISC DHCP adopted early versions of these documents, and has not
+ yet synchronized with the final standards versions.</t>
+
+ <t>For RFCs 4702 and 4704, the 'N' bit is not yet supported. The
+ result is that it is always set zero, and is ignored if set.</t>
+
+ <t>For RFC4701, which is used to match client identities with names
+ in the DNS as part of name conflict resolution. Note that ISC DHCP's
+ implementation of DHCIDs vary wildly from this specification.
+ First, ISC DHCP uses a TXT record in which the contents are stored
+ in hexadecimal. Second, there is a flaw in the selection of the
+ 'Identifier Type', which results in a completely different value
+ being selected than was defined in an older revision of this
+ document...also this field is one byte prior to hexadecimal
+ encoding rather than two. Third, ISC DHCP does not use a digest
+ type code. Rather, all values for such TXT records are reached
+ via an MD5 sum. In short, nothing is compatible, but the
+ principle of the TXT record is the same as the standard DHCID
+ record. However, for DHCPv6 FQDN, we do use DHCID type code '2',
+ as no other value really makes sense in our context.</t>
+ </section>
+
+ <section title="Experimental: Failover References">
+ <t>The Failover Protocol defines means by which two DHCP Servers
+ can share all the relevant information about leases granted to
+ DHCP clients on given networks, so that one of the two servers may
+ fail and be survived by a server that can act responsibly.</t>
+
+ <t>Unfortunately it has been quite some years (2003) since the last
+ time this document was edited, and the authors no longer show any
+ interest in fielding comments or improving the document.</t>
+
+ <t>The status of this protocol is very unsure, but ISC's
+ implementation of it has proven stable and suitable for use in
+ sizable production environments.</t>
+
+ <t><xref target="draft-failover">draft-ietf-dhc-failover-12.txt</xref>
+ describes the Failover Protocol. In addition to what is described
+ in this document, ISC DHCP has elected to make some experimental
+ changes that may be revoked in a future version of ISC DHCP (if the
+ draft authors do not adopt the new behaviour). Specifically, ISC
+ DHCP's POOLREQ behaviour differs substantially from what is
+ documented in the draft, and the server also implements a form of
+ 'MAC Address Affinity' which is not described in the failover
+ document. The full nature of these changes have been described on
+ the IETF DHC WG mailing list (which has archives), and also in ISC
+ DHCP's manual pages. Also note that although this document
+ references a RECOVER-WAIT state, it does not document a protocol
+ number assignment for this state. As a consequence, ISC DHCP has
+ elected to use the value 254.</t>
+
+ <t> An optimization described in the failover protocol draft
+ is included since 4.2.0a1. It permits a DHCP server
+ operating in communications-interrupted state to 'rewind' a
+ lease to the state most recently transmitted to its peer,
+ greatly increasing a server's endurance in
+ communications-interrupted. This is supported using a new
+ 'rewind state' record on the dhcpd.leases entry for each
+ lease.
+ </t>
+
+ <t><xref target="RFC3074" /> describes the Load Balancing
+ Algorithm (LBA) that ISC DHCP uses in concert with the Failover
+ protocol. Note that versions 3.0.* are known to misimplement the
+ hash algorithm (it will only use the low 4 bits of every byte of
+ the hash bucket array).</t>
+ </section>
+ </section>
+
+ <section title="DHCP Procedures">
+ <t><xref target="RFC2939" /> explains how to go about
+ obtaining a new DHCP Option code assignment.</t>
+ </section>
+ </section>
+
+
+ <section title="DHCPv6 Protocol References">
+
+ <section title="DHCPv6 Protocol References">
+ <t>For now there is only one document that specifies the base
+ of the DHCPv6 protocol (there have been no updates yet),
+ <xref target="RFC3315"/>.</t>
+
+ <t>Support for DHCPv6 was first added in version 4.0.0. The server
+ and client support only IA_NA. While the server does support multiple
+ IA_NAs within one packet from the client, our client only supports
+ sending one. There is no relay support.</t>
+
+ <t>DHCPv6 introduces some new and uncomfortable ideas to the common
+ software library.</t>
+
+ <t>
+ <list style="numbers">
+ <t>Options sometimes may appear multiple times. The common
+ library used to treat all appearance of multiple options as
+ specified in RFC2131 - to be concatenated. DHCPv6 options
+ may sometimes appear multiple times (such as with IA_NA or
+ IAADDR), but often must not. As of 4.2.1-P1, multiple IA_NA, IA_PD
+ or IA_TA are not supported.</t>
+
+ <t>The same option space appears in DHCPv6 packets multiple times.
+ If the packet was got via a relay, then the client's packet is
+ stored to an option within the relay's packet...if there were two
+ relays, this recurses. At each of these steps, the root "DHCPv6
+ option space" is used. Further, a client packet may contain an
+ IA_NA, which may contain an IAADDR - but really, in an abstract
+ sense, this is again re-encapsulation of the DHCPv6 option space
+ beneath options it also contains.</t>
+ </list>
+ </t>
+
+ <t>Precisely how to correctly support the above conundrums has not
+ quite yet been settled, so support is incomplete.</t>
+ </section>
+
+ <section title="DHCPv6 Options References">
+ <t><xref target="RFC3319"/> defines the SIP server
+ options for DHCPv6.</t>
+
+ <t><xref target="RFC3646"/> documents the DHCPv6
+ name-servers and domain-search options.</t>
+
+ <t><xref target="RFC3633"/> documents the Identity
+ Association Prefix Delegation for DHCPv6, which is included
+ here for protocol wire reference, but which is not supported
+ by ISC DHCP.</t>
+
+ <t><xref target="RFC3898"/> documents four NIS options
+ for delivering NIS servers and domain information in DHCPv6.</t>
+
+ <t><xref target="RFC4075"/> defines the DHCPv6 SNTP
+ Servers option.</t>
+
+ <t><xref target="RFC4242"/> defines the Information
+ Refresh Time option, which advises DHCPv6 Information-Request
+ clients to return for updated information.</t>
+
+ <t><xref target="RFC4280"/> defines two BCMS server options
+ for each protocol family.</t>
+
+ <t><xref target="RFC4580"/> defines a DHCPv6
+ subscriber-id option, which is similar in principle to the DHCPv4
+ relay agent option of the same name.</t>
+
+ <t><xref target="RFC4649"/> defines a DHCPv6 remote-id
+ option, which is similar in principle to the DHCPv4 relay agent
+ remote-id.</t>
+
+ </section>
+ </section>
+
+ </middle>
+
+ <back>
+ <references title="Published DHCPv4 References">
+ &rfc760;
+ &rfc768;
+ &rfc894;
+ &rfc951;
+ &rfc1035;
+ &rfc1188;
+ &rfc1542;
+ &rfc2131;
+ &rfc2132;
+ &rfc2241;
+ &rfc2242;
+ &rfc2485;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.2563'?>
+ &rfc2610;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.2855'?>
+ &rfc2937;
+ &rfc2939;
+ &rfc3004;
+ &rfc3011;
+ &rfc3046;
+ &rfc3074;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3118'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3203'?>
+ &rfc3256;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3361'?>
+ &rfc3396;
+ &rfc3397;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3442'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3456'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3495'?>
+ &rfc3527;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3594'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3634'?>
+ &rfc3679;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3825'?>
+ &rfc3925;
+ &rfc3942;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3993'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4014'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4030'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4039'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4174'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4243'?>
+ &rfc4361;
+ &rfc4388;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4390'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4436'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4701'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4702'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4703'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5010'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5071'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5107'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5192'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5223'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5859'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5969'?>
+
+ <reference anchor='draft-failover'>
+ <front>
+ <title>DHCP Failover Protocol</title>
+ <author initials='R.' surname='Droms' fullname='Ralph Droms'>
+ <organization abbrev='Cisco'>Cisco Systems</organization>
+ </author>
+ <date month='March' year='2003'/>
+ </front>
+ <format type="TXT" octets="312151" target="https://www.isc.org/sw/dhcp/drafts/draft-ietf-dhc-failover-12.txt"/>
+ </reference>
+
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-dhcpv4-relay-encapsulation-00.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-dhcpv4-bulk-leasequery-03.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-leasequery-by-remote-id-09.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-relay-id-suboption-07.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-mip6-hiopt-17.xml'?>
+
+ </references>
+
+ <references title="Published Common (DHCPv4/DHCPv6) References">
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4280'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4477'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4578'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4776'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4833'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5417'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5678'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5908'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5970'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5986'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-vpn-option-12.xml'?>
+
+ </references>
+
+ <references title="Published DHCPv6 References">
+
+ &rfc3315;
+ &rfc3319;
+ &rfc3633;
+ &rfc3646;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.3736'?>
+ &rfc3898;
+ &rfc4075;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4076'?>
+ &rfc4242;
+ &rfc4580;
+ &rfc4649;
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4704'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.4994'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5007'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml/reference.RFC.5460'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.ietf-mif-dhcpv6-route-option'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.ietf-dhc-dhcpv6-ldra'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.ietf-dhc-dhcpv6-relay-supplied-options'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-pd-exclude-01.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-secure-dhcpv6-02.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.ietf-mext-nemo-pd'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-dhc-duid-uuid-03.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-softwire-ds-lite-tunnel-option-10.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-mif-dns-server-selection-01.xml'?>
+ <?rfc include='http://xml.resource.org/public/rfc/bibxml3/reference.I-D.draft-ietf-geopriv-rfc3825bis-17.xml'?>
+
+ <reference anchor='draft-addr-params'>
+ <front>
+ <title>Address Parameters Option for DHCPv6</title>
+ <author initials='T.' surname='Mrugalski' fullname='Mrugalski'>
+ <organization abbrev='Cisco'>Gdansk University of Technology</organization>
+ </author>
+ <date month='April' year='2007'/>
+ </front>
+ <format type="TXT" target="http://klub.com.pl/dhcpv6/doc/draft-mrugalski-addropts-XX-2007-04-17.txt"/>
+ </reference>
+
+ </references>
+ </back>
+</rfc>
diff --git a/doc/api+protocol b/doc/api+protocol
new file mode 100644
index 0000000..48569f2
--- /dev/null
+++ b/doc/api+protocol
@@ -0,0 +1,436 @@
+This file documents the protocol that the ISC DHCP server and ISC
+Object Management clients (clients that use the ISC Object Management
+API) speak between one another.
+
+Protocol:
+
+All multi-byte numbers are represented in network byte order.
+
+On startup, each side sends a status message indicating what version
+of the protocol they are speaking. The status message looks like
+this:
+
++---------+---------+
+| version | hlength |
++---------+---------+
+
+version - a 32-bit fixed-point number with the decimal point between
+ the third and second decimal digits from the left,
+ representing the version of the protocol. The current
+ protocol version is 1.00. If the field were considered as
+ a 32-bit integer, this would correspond to a value of 100
+ decimal, or 0x64.
+
+hlength - a 32-bit integer representing the length of the fixed-length
+ header in subsequent messages. This is normally 56, but
+ can be changed to a value larger than 56 by either side
+ without upgrading the revision number.
+
+
+The startup message is not authenticated. Either side may reject the
+other side's startup message as invalid by simply closing the
+connection. The only fixed part of the startup message is the
+version number - future versions may delete hlength, or add further
+startup information.
+
+Following the startup message, all messages have the same format.
+Currently, the format includes a fixed-length header (the length in
+hlength, above)
+
++--------+----+--------+----+-----+---------+------------+------------+-----+
+| authid | op | handle | id | rid | authlen | msg values | obj values | sig |
++--------+----+--------+----+-----+---------+------------+------------+-----+
+
+The fixed-length header consists of:
+
+authid = a 32-bit authenticator handle.
+ For an original message (one not in response to some other
+ message), this will be chosen by the originator. For a
+ message in response to another message, the authenticator for
+ that message is used, except if the response is an error
+ message indicating that the authenticator used was unknown,
+ in which case the null authenticator is used. Messages that
+ are generated as the result of a notify registration use the
+ authenticator used in the original notify registration.
+ The authenticator itself is generated by having one side of
+ the connection send an object of type "authenticator" to the
+ other side with values that indicate what kind of
+ authentication mechanism to use and what key to use. The two
+ most likely things here are a Kerberos V principal name or the
+ name of a shared secret that can be used to calculate an MD5
+ hash. The mechanism for doing this has yet to be finalized.
+ If authid is zero, the message is not authenticated.
+
+op = 32-bit opcode, one of:
+ open = 1
+ refresh = 2
+ update = 3
+ notify = 4
+ error = 5
+ delete = 6
+handle = 32-bit object handle
+ A handle on the object being opened, created, refreshed or
+ updated. If no handle is yet available (e.g., with open and
+ new), then the value zero is sent.
+id = 32-bit transaction id of the message - a monotonically increasing
+ number that starts with some randomly chosen number at the
+ beginning of the life of the connection. The value should never
+ be zero.
+rid = 32-bit transaction ID of the message to which this message is a
+ response, or zero if this message is not in response to a
+ message from the other side.
+
+authlen = a 32-bit number representing the length of the authenticator
+
+msg values = a series of name+value pairs, specific to this message.
+ Each name+value pair starts with a 16-bit name length,
+ followed by that many bytes of name, followed by a 32-bit
+ value length, followed by that many bytes of value. If the
+ length is zero, this is a value of the blank string. If the
+ length is all ones (2^32-1), then there is no value - for an
+ update, this means the value for this name and the name
+ itself should be deleted from the object, which may or may
+ not be possible. The list of name/value pairs ends with a
+ zero-length name, which is not followed by a value
+ length/value pair.
+
+obj values = a series of name+value pairs, as above, specific to the
+ object being created, updated or refreshed.
+
+signature = authlen bytes of data signing the message. The signature
+ algorithm is a property of the authenticator handle.
+
+Message types:
+
+1: open
+ relevant input values:
+ object-type = the name of the type of object
+ open:create = boolean - create the object if it doesn't yet exist
+ open:exclusive = boolean - don't open the object if it does exist
+ open:update = boolean - update the object with included values
+ if it matches.
+ the handle should always be the null handle
+
+ The input value must also contain key information for the type of
+ object being searched that uniquely identifies an object, or search
+ information that matches only one object. Each object has a key
+ specification (a key is something that uniquely identifies an
+ object), so see the key specification for that object to see
+ what to send here. An open message with the create flag set must
+ specify a key, and not merely matching criteria. Some objects may
+ allow more than one key, and it may be that the union of those keys
+ is required to uniquely identify the object, or it may be that any
+ one such key will uniquely identify the object. The documentation
+ for the type of object will specify this.
+
+ An open message will result in an immediate response message whose
+ opcode will either be "error" or "update". The error message may
+ include an error:reason value containing a text string explaining
+ the error, and will always include an error:code value which will
+ be the numeric error code for what went wrong. Possible error
+ codes are:
+
+ not found - no such object exists
+ already exists - object already exists, and exclusive flag was
+ set.
+ not unique - more than one object matching the specification
+ exists.
+ permission denied - the authenticator ID specified does not
+ have authorization to access this object,
+ or if the update flag was specified, to
+ update the object.
+
+ If the response is an update message, the update message will
+ include the object handle and all of the name/value pairs
+ associated with that object.
+
+2: refresh
+
+ no input values except the handle need be specified. The null
+ handle may not be specified. If the handle is valid, and the
+ authenticator ID specified has permission to examine the object,
+ then an update message will be sent for that object. Otherwise,
+ one of the following errors will be sent:
+
+ invalid handle - the handle does not refer to a known object
+ permisson denied - the handle refers to an object that the
+ requestor does not have permission to
+ examine.
+
+3: update
+
+ Requests that the contents of the specified object be updated with
+ the values included. Values that are not specified are not
+ updated. The response will be either an error message or an
+ update-ok message. If rid is nonzero, no response will be
+ generated, even if there was an error. Possible errors include:
+
+ invalid handle - no such object was found
+ permission denied - the handle refers to an object that the
+ requestor does not have permission to
+ modify.
+ not confirmed - the update could not be committed due to some
+ kind of resource problem, for example
+ insufficient memory or a disk failure.
+
+4: notify
+
+ Requests that whenever the object with the specified handle is
+ modified, an update be sent. If there is something wrong with the
+ request, an error message will be returned immediately.
+ Otherwise, whenever a change is made to the object, an update
+ message will be sent containing whatever changes were made (or
+ possibly all the values associated with the object, depending on
+ the implementation). Possible errors:
+
+ invalid handle
+ permission denied - the handle refers to an object that the
+ requestor does not have permission to
+ examine.
+ not supported - the object implementation does not support
+ notifications
+
+5: status
+
+ Sends a status code in response to a message. Always sent in
+ response to a message sent by the other side. There should never
+ be a response to this message.
+
+6: delete
+
+ Deletes the specified object. Response will be either request-ok,
+ or error. Possible errors include:
+
+ invalid handle - no such object was found
+ permission denied - the handle refers to an object that the
+ requestor does not have permission to
+ modify.
+ not confirmed - the deletion could not be committed due to
+ some kind of resource problem, for example
+ insufficient memory or a disk failure.
+
+7: notify-cancel
+
+ Like notify, but requests that an existing notification be cancelled.
+
+8: notify-cancelled
+
+ Indicates that because of a local change, a notification that had
+ been registered can no longer be performed. This could be as a
+ result of the permissions on a object changing, or an object being
+ deleted. There should never be a response to this message.
+
+internals:
+
+Both client and server use same protocol and infrastructure. There
+are many object types, each of which is stored in a registry.
+Objects whose type is not recognized can either be handled by the
+generic object type, which is registered with the type "*". If no
+generic object type is registered, then objects with unknown types are
+simply not supported. On the client, there are probably no special
+object handlers (although this is by no means forbidden). On the
+server, probably everything is a special object.
+
+Each object type has the following methods:
+
+
+
+
+dhcpctl_status dhcpctl_connect (dhcpctl_handle *connection,
+ char *server_name, int port,
+ dhcpctl_handle *authinfo)
+ synchronous
+ returns nonzero status code if it didn't connect, zero otherwise
+ stores connection handle through connection, which can be used
+ for subsequent access to the specified server.
+ server_name is the name of the server, and port is the TCP
+ port on which it is listening.
+ authinfo is the handle to an object containing authentication
+ information.
+
+dhcpctl_status dhcpctl_open_object (dhcpctl_handle h,
+ dhcpctl_handle connection,
+ int flags)
+ asynchronous - just queues the request
+ returns nonzero status code if open couldn't be queued
+ returns zero if open was queued
+ h is a handle to an object created by dhcpctl_new_object
+ connection is a connection to a DHCP server
+ flags include:
+ DHCPCTL_CREATE - if the object doesn't exist, create it
+ DHCPCTL_UPDATE - update the object on the server using the
+ attached parameters
+ DHCPCTL_EXCL - error if the object exists and DHCPCTL_CREATE
+ was also specified
+
+dhcpctl_status dhcpctl_new_object (dhcpctl_handle *h,
+ dhcpctl_handle connection,
+ char *object_type)
+ synchronous - creates a local handle for a host entry.
+ returns nonzero status code if the local host entry couldn't
+ be created
+ stores handle to host through h if successful, and returns zero.
+ object_type is a pointer to a NUL-terminated string containing
+ the ascii name of the type of object being accessed - e.g., "host"
+
+dhcpctl_status dhcpctl_set_callback (dhcpctl_handle h, void *data,
+ void (*callback) (dhcpctl_handle,
+ dhcpctl_status, void *))
+ synchronous, with asynchronous aftereffect
+ handle is some object upon which some kind of process has been
+ started - e.g., an open, an update or a refresh.
+ data is an anonymous pointer containing some information that
+ the callback will use to figure out what event completed.
+ return value of 0 means callback was successfully set, a nonzero
+ status code is returned otherwise.
+ Upon completion of whatever task is in process, the callback
+ will be passed the handle to the object, a status code
+ indicating what happened, and the anonymous pointer passed to
+
+dhcpctl_status dhcpctl_wait_for_completion (dhcpctl_handle h,
+ dhcpctl_status *s)
+ synchronous
+ returns zero if the callback completes, a nonzero status if
+ there was some problem relating to the wait operation. The
+ status of the queued request will be stored through s, and
+ will also be either zero for success or nonzero for some kind
+ of failure. Never returns until completion or until the
+ connection to the server is lost. This performs the same
+ function as dhcpctl_set_callback and the subsequent callback,
+ for programs that want to do inline execution instead of using
+ callbacks.
+
+dhcpctl_status dhcpctl_get_value (data_string *result,
+ dhcpctl_handle h, char *value_name)
+ synchronous
+ returns zero if the call succeeded, a nonzero status code if
+ it didn't.
+ result is the address of an empty data string (initialized
+ with bzero or cleared with data_string_forget). On
+ successful completion, the addressed data string will contain
+ the value that was fetched.
+ dhcpctl_handle refers to some dhcpctl item
+ value_name refers to some value related to that item - e.g.,
+ for a handle associated with a completed host lookup, value
+ could be one of "hardware-address", "dhcp-client-identifier",
+ "known" or "client-hostname".
+
+dhcpctl_status dhcpctl_get_boolean (int *result,
+ dhcpctl_handle h, char *value_name)
+ like dhcpctl_get_value, but more convenient for boolean
+ values, since no data_string needs to be dealt with.
+
+dhcpctl_status dhcpctl_set_value (dhcpctl_handle h, data_string value,
+ char *value_name)
+ Sets a value on an object referred to by a dhcpctl_handle.
+ The opposite of dhcpctl_get_value. Does not update the
+ server - just sets the value on the handle.
+
+dhcpctl_status dhcpctl_set_string_value (dhcpctl_handle h, char *value,
+ char *value_name)
+ Sets a NUL-terminated ASCII value on an object referred to by
+ a dhcpctl_handle. like dhcpctl_set_value, but saves the
+ trouble of creating a data_string for a NUL-terminated string.
+ Does not update the server - just sets the value on the handle.
+
+dhcpctl_status dhcpctl_set_boolean (dhcpctl_handle h, int value,
+ char *value_name)
+ Sets a boolean value on an object - like dhcpctl_set_value,
+ only more convenient for booleans.
+
+dhcpctl_status dhcpctl_object_update (dhcpctl_handle h)
+ Queues an update on the object referenced by the handle (there
+ can't be any other work in progress on the handle). An
+ update means local parameters will be sent to the server.
+
+dhcpctl_status dhcpctl_object_refresh (dhcpctl_handle h)
+ Queues an update on the object referenced by the handle (there
+ can't be any other work in progress on the handle). An
+ update means local parameters will be sent to the server.
+
+dhcpctl_status dhcpctl_object_delete (dhcpctl_handle h)
+ Queues a delete of the object referenced by the handle (there
+ can't be any other work in progress on the handle). A
+ delete means that the object will be permanently deleted on
+ the remote end, assuming the remote end supports object
+ persistence.
+
+So a sample program that would update a host declaration would look
+something like this:
+
+ /* Create a local object into which to store authentication
+ information. */
+ if ((status = dhcpctl_new_object (&auth, dhcpctl_null_handle,
+ "authentication-information")))
+ dhcpctl_error ("Can't create authentication information: %m");
+
+ /* Set up the authenticator with an algorithm type, user name and
+ password. */
+ if ((status = dhcpctl_set_string_value (&auth, "mellon", "username")))
+ dhcpctl_error ("Can't set username: %m", status);
+ if ((status = dhcpctl_set_string_value (&auth, "three blind mice",
+ "password")))
+ dhcpctl_error ("Can't set password: %m", status);
+ if ((status = dhcpctl_set_string_value (&auth, "md5-hash",
+ "algorithm")))
+ dhcpctl_error ("Can't set authentication algorithm: %m.",
+ status);
+
+ /* Connect to the server. */
+ if ((status = dhcpctl_connect (&c, "dhcp.server.com", 612, &auth)))
+
+ dhcpctl_error ("Can't connect to dhcp.server.com: %m",
+ status);
+
+ /* Create a host object. */
+ if ((status = dhcpctl_new_object (&hp, c, "host")))
+ dhcpctl_error ("Host create failed: %m", status);
+
+ /* Create a data_string to contain the host's client
+ identifier, and set it. */
+ if ((status =
+ data_string_create_from_hex (&client_id,
+ "1:08:00:2b:34:1a:c3")))
+ dhcpctl_error ("Can't create client identifier: %m");
+ if ((status = dhcpctl_set_value (hp, client_id,
+ "dhcp-client-identifier")))
+ dhcpctl_error ("Host client identifier set failed.");
+ /* Set the known flag to 1. */
+ if ((status = dhcpctl_set_boolean (hp, 1, "known")))
+ dhcpctl_error ("Host known set failed.");
+
+ /* Open an existing host object that matches the client identifier,
+ and update it from the local context, or if no host entry
+ yet exists matching the identifier, create one and
+ initialize it. */
+ if ((status = dhcpctl_open_object (&hp, c,
+ DHCPCTL_CREATE | DHCPCTL_UPDATE)))
+ dhcpctl_error ("Can't open host: %m", status);
+
+ /* Wait for the process to complete, check status. */
+ if ((status = dhcpctl_wait_for_completion (hp, &wait_status)))
+ dhcpctl_error ("Host create/lookup wait failed: %m", status);
+ if (waitstatus)
+ dhcpctl_error ("Host create/lookup failed: %m", status);
+
+The API is a bit complicated, for a couple of reasons. I want to
+make it general, so that there aren't a bazillion functions to call,
+one for each data type. I want it to be thread-safe, which is why
+each function returns a status and the error printer requires a status
+code for input. I want it to be possible to make it asynchronous, so
+that it can work in tandem with, for example, an X toolkit. If
+you're just writing a simple update cgi program, you probably won't
+want to bother to use the asynchronous callbacks, and indeed the above
+example doesn't.
+
+I glossed over data strings above - basically, they're objects with a
+pointer to a reference-counted buffer structure, an offset into that
+buffer, and a length. These are used within the DHCP server, so you
+can get an idea of how they work - basically, they're a convenient and
+efficient way to store a string with a length such that substrings can
+easily be taken and such that more than one user at a time can have a
+pointer to the string.
+
+I will also probably add locking primitives, so that you can get the
+value of something and be sure that some other updator process won't
+modify it while you have the lock.
diff --git a/doc/examples/dhclient-dhcpv6.conf b/doc/examples/dhclient-dhcpv6.conf
new file mode 100644
index 0000000..6b63a1f
--- /dev/null
+++ b/doc/examples/dhclient-dhcpv6.conf
@@ -0,0 +1,11 @@
+# Client configuration file example for DHCPv6
+
+# The client side command to enable rapid-commit (2 packet exchange)
+##send dhcp6.rapid-commit;
+
+# name-servers and domain-search are requested by default.
+# here is the way to request sip-servers-addresses too
+also request dhcp6.sip-servers-addresses;
+
+# Likely to be useful: the script path
+script "/usr/local/etc/dhclient-script";
diff --git a/doc/examples/dhcpd-dhcpv6.conf b/doc/examples/dhcpd-dhcpv6.conf
new file mode 100644
index 0000000..611dbe7
--- /dev/null
+++ b/doc/examples/dhcpd-dhcpv6.conf
@@ -0,0 +1,105 @@
+# Server configuration file example for DHCPv6
+# From the file used for TAHI tests.
+
+# IPv6 address valid lifetime
+# (at the end the address is no longer usable by the client)
+# (set to 30 days, the usual IPv6 default)
+default-lease-time 2592000;
+
+# IPv6 address preferred lifetime
+# (at the end the address is deprecated, i.e., the client should use
+# other addresses for new connections)
+# (set to 7 days, the usual IPv6 default)
+preferred-lifetime 604800;
+
+# T1, the delay before Renew
+# (default is 1/2 preferred lifetime)
+# (set to 1 hour)
+option dhcp-renewal-time 3600;
+
+# T2, the delay before Rebind (if Renews failed)
+# (default is 3/4 preferred lifetime)
+# (set to 2 hours)
+option dhcp-rebinding-time 7200;
+
+# Enable RFC 5007 support (same than for DHCPv4)
+allow leasequery;
+
+# Global definitions for name server address(es) and domain search list
+option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:3f3e;
+option dhcp6.domain-search "test.example.com","example.com";
+
+# Set preference to 255 (maximum) in order to avoid waiting for
+# additional servers when there is only one
+##option dhcp6.preference 255;
+
+# Server side command to enable rapid-commit (2 packet exchange)
+##option dhcp6.rapid-commit;
+
+# The delay before information-request refresh
+# (minimum is 10 minutes, maximum one day, default is to not refresh)
+# (set to 6 hours)
+option dhcp6.info-refresh-time 21600;
+
+# The path of the lease file
+dhcpv6-lease-file-name "/usr/local/var/db/dhcpd6.leases";
+
+# Static definition (must be global)
+host myclient {
+ # The entry is looked up by this
+ host-identifier option
+ dhcp6.client-id 00:01:00:01:00:04:93:e0:00:00:00:00:a2:a2;
+
+ # A fixed address
+ fixed-address6 3ffe:501:ffff:100::1234;
+
+ # A fixed prefix
+ fixed-prefix6 3ffe:501:ffff:101::/64;
+
+ # Override of the global definitions,
+ # works only when a resource (address or prefix) is assigned
+ option dhcp6.name-servers 3ffe:501:ffff:100:200:ff:fe00:4f4e;
+
+ # For debug (to see when the entry statements are executed)
+ # (log "sol" when a matching Solicitation is received)
+ ##if packet(0,1) = 1 { log(debug,"sol"); }
+}
+
+host otherclient {
+ # This host entry is hopefully matched if the client supplies a DUID-LL
+ # or DUID-LLT containing this MAC address.
+ hardware ethernet 01:00:80:a2:55:67;
+
+ fixed-address6 3ffe:501:ffff:100::4321;
+}
+
+# The subnet where the server is attached
+# (i.e., the server has an address in this subnet)
+subnet6 3ffe:501:ffff:100::/64 {
+ # Two addresses available to clients
+ # (the third client should get NoAddrsAvail)
+ range6 3ffe:501:ffff:100::10 3ffe:501:ffff:100::11;
+
+ # Use the whole /64 prefix for temporary addresses
+ # (i.e., direct application of RFC 4941)
+ range6 3ffe:501:ffff:100:: temporary;
+
+ # Some /64 prefixes available for Prefix Delegation (RFC 3633)
+ prefix6 3ffe:501:ffff:100:: 3ffe:501:ffff:111:: /64;
+}
+
+# A second subnet behind a relay agent
+subnet6 3ffe:501:ffff:101::/64 {
+ range6 3ffe:501:ffff:101::10 3ffe:501:ffff:101::11;
+
+ # Override of the global definitions,
+ # works only when a resource (address or prefix) is assigned
+ option dhcp6.name-servers 3ffe:501:ffff:101:200:ff:fe00:3f3e;
+
+}
+
+# A third subnet behind a relay agent chain
+subnet6 3ffe:501:ffff:102::/64 {
+ range6 3ffe:501:ffff:102::10 3ffe:501:ffff:102::11;
+}
+
diff --git a/doc/ja_JP.eucJP/dhclient-script.8 b/doc/ja_JP.eucJP/dhclient-script.8
new file mode 100644
index 0000000..c53aad2
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhclient-script.8
@@ -0,0 +1,242 @@
+.\" $Id: dhclient-script.8,v 1.3.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.\"
+.\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient-script.8,v 1.5.2.4 2002/04/11 10:16:45 murray Exp %
+.\"
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man8/dhclient-script.8,v 1.13 2002/05/08 03:27:27 horikawa Exp $
+.TH dhclient-script 8
+.SH ̾¾Î
+dhclient-script - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄꥹ¥¯¥ê¥×¥È
+.SH ²òÀâ
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄꥹ¥¯¥ê¥×¥È¤Ï¡¢
+»þ¤¢¤ë¤´¤È¤Ë \fBdhclient(8)\fR ¤¬¸Æ¤Ó½Ð¤·¤Þ¤¹¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ËÜ¥¹¥¯¥ê¥×¥È¤ò»ÈÍѤ¹¤ë¤³¤È¤Ë¤è¤ê¡¢
+¥¢¥É¥ì¥¹Í×µá¤ËÀèΩ¤Ä³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î½é´üÀßÄê¤È¡¢
+ÉÕÍ¿¤µ¤ì¤¿¥¢¥É¥ì¥¹¤Î¸¡ºº¤È¡¢
+¥ê¡¼¥¹³ÍÆÀ»þ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎºÇ½ªÀßÄê¤ò¹Ô¤¤¤Þ¤¹¡£
+¥ê¡¼¥¹¤¬³ÍÆÀ¤µ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¡¢
+ÄêµÁºÑ¤ß¤Î¥ê¡¼¥¹¤¬Â¸ºß¤¹¤ë¤Ê¤é¤Ð¤³¤ì¤ò¸¡ºº¤¹¤ë¤¿¤á¤ËËÜ¥¹¥¯¥ê¥×¥È¤Ï»ÈÍѤµ¤ì¡¢
+Í­¸ú¤Ê¥ê¡¼¥¹¤¬È½ÌÀ¤·¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤â¤â¤¦ 1 ²ó¤³¤Î¥¹¥¯¥ê¥×¥È¤¬¸Æ¤Ð¤ì¤Þ¤¹¡£
+.PP
+ËÜ¥¹¥¯¥ê¥×¥È¤Ï¡¢¥¨¥ó¥É¥æ¡¼¥¶¤Ë¥«¥¹¥¿¥Þ¥¤¥º¤µ¤ì¤ë¤³¤È¤ò°Õ¿Þ¤·¤Æ¤¤¤Þ¤»¤ó¡£
+¥í¡¼¥«¥ë¤Ê¥«¥¹¥¿¥Þ¥¤¥º¤¬É¬Íפʾì¹ç¡¢
+¤³¤ì¤ÏÆþ (enter) ¤È½Ð (exit) ¤È¤¤¤¦¥Õ¥Ã¥¯¤ò»ÈÍѤ¹¤ë¤³¤È¤Ç²Äǽ¤È¤Ê¤ê¤Þ¤¹
+(¾ÜºÙ¤Ï¥Õ¥Ã¥¯»²¾È)¡£
+¤³¤ì¤é¤Î¥Õ¥Ã¥¯¤Ï¡¢
+.B /etc/resolv.conf
+ºîÀ®»þ¤Ë¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Î¥Ç¥Õ¥©¥ë¥ÈÆ°ºî¤ò¥æ¡¼¥¶¤¬¥ª¡¼¥Ð¥é¥¤¥É¤Ç¤­¤ë¤è¤¦¤Ë¤·¤Þ¤¹¡£
+.PP
+ÆÃÄê¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Ç¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Î¼ÂÂΤÏÆ°ºî¤¹¤ë¤È¤·¤Æ¤â¡¢
+ɸ½à¤Î¥¹¥¯¥ê¥×¥È¤¬Æ°ºî¤·¤Ê¤¤¤«¤â¤·¤ì¤Þ¤»¤ó¡£
+Àè¶îŪ¤Ê¥æ¡¼¥¶¤¬¿·µ¬¥¹¥¯¥ê¥×¥È¤òºîÀ®¤·¤¿¤ê´û¸¤Î¤â¤Î¤ò½¤Àµ¤·¤¿¤ê¤¹¤ëɬÍפ¬¤¢¤ë
+¤³¤È¤Ï¤â¤Ã¤È¤â¤Ê¤³¤È¤Ç¤¹¡£
+°ìÈÌŪ¤Ë¤Ï¡¢¤½¤ì¤¾¤ì¤Î¥³¥ó¥Ô¥å¡¼¥¿¤Ë¸ÇÍ­¤Î¥«¥¹¥¿¥Þ¥¤¥º¤Ï
+.B ETCDIR/dhclient.conf
+¥¹¥¯¥ê¥×¥È¤Ç¹Ô¤¦¤Ù¤­¤Ç¤¹¡£
+.B ETCDIR/dhclient.conf
+¤Î¥«¥¹¥¿¥Þ¥¤¥ºÌµ¤·¤Ë¤Ç¤­¤Ê¤¤¥«¥¹¥¿¥Þ¥¤¥º¤ä¡¢
+Æþ¤È½Ð¤Î¥Õ¥Ã¥¯¤Î»ÈÍѤǤϤǤ­¤Ê¤¤¥«¥¹¥¿¥Þ¥¤¥º¤Ëµ¤¤Å¤¤¤¿¾ì¹ç¤Ë¤Ï¡¢
+¥Ð¥°¥ì¥Ý¡¼¥È¤òÁ÷¤Ã¤Æ¤¯¤À¤µ¤¤¡£
+.SH ¥Õ¥Ã¥¯
+³«»Ï»þ¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï¤Þ¤º¥·¥§¥ë´Ø¿ô¤òÄêµÁ¤·¤Þ¤¹¡£¤½¤Î´Ø¿ô¤Ï
+.B make_resolv_conf
+¤Ç¤¢¤ê¡¢¸å¤Ë
+.B /etc/resolv.conf
+¥Õ¥¡¥¤¥ë¤òºîÀ®¤¹¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥ÈÆ°ºî¤ò¥ª¡¼¥Ð¥é¥¤¥É¤¹¤ë¤Ë¤Ï¡¢
+¤³¤Î´Ø¿ô¤òÆþ¤Î¥Õ¥Ã¥¯¥¹¥¯¥ê¥×¥È¤ÇºÆÄêµÁ¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+make_resolv_conf ´Ø¿ô¤ÎÄêµÁ¤Î¸å¡¢¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï
+¼Â¹Ô²Äǽ¤Ê
+.B ETCDIR/dhclient-enter-hooks
+¥¹¥¯¥ê¥×¥È¤Î¸ºß¤ò¸¡ºº¤·¡¢
+¸ºß¤¹¤ë¾ì¹ç¤Ë¤Ï Bourne ¥·¥§¥ë¤Î '.' ¥³¥Þ¥ó¥É¤ò»ÈÍѤ·¤Æ
+ËÜ¥¹¥¯¥ê¥×¥È¤ò¥¤¥ó¥é¥¤¥ó¤Çµ¯Æ°¤·¤Þ¤¹¡£
+Áàºî¤Çµ­½Ò¤µ¤ì¤Æ¤¤¤ë¤¹¤Ù¤Æ¤Î´Ä¶­¤¬ËÜ¥¹¥¯¥ê¥×¥È¤Ç»ÈÍѲÄǽ¤Ç¤¢¤ê¡¢
+¥¹¥¯¥ê¥×¥È¤ÎÆ°ºî¤ÎÊѹ¹¤¬É¬Íפʾì¹ç¤Ë¤Ï´Ä¶­¤Î½¤Àµ¤¬µö¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+¥¹¥¯¥ê¥×¥È¼Â¹ÔÃæ¤Ë¥¨¥é¡¼¤¬È¯À¸¤·¤¿¾ì¹ç¡¢
+exit_status ÊÑ¿ô¤òÈó 0 ÃͤËÀßÄꤹ¤ë¤³¤È¤¬²Äǽ¤Ç¤¢¤ê¡¢
+¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È½ªÎ»Ä¾¸å¤Ë
+.B CLIENTBINDIR/dhclient-script
+¤Ï¤½¤Î¥¨¥é¡¼¥³¡¼¥É¤Ç½ªÎ»¤·¤Þ¤¹¡£
+.PP
+¤¹¤Ù¤Æ¤Î½èÍý¤Î´°Î»¸å¤Ë¡¢
+.B CLIENTBINDIR/dhclient-script
+¤Ï¼Â¹Ô²Äǽ¤Ê
+.B ETCDIR/dhclient-exit-hooks
+¥¹¥¯¥ê¥×¥È¤Î¸ºß¤ò¸¡ºº¤·¡¢Â¸ºß¤¹¤ë¾ì¹ç¤Ë¤Ï '.' ¥³¥Þ¥ó¥É¤Ç¤³¤ì¤òµ¯Æ°¤·¤Þ¤¹¡£
+dhclient-script ¤Î
+½ªÎ»¾õÂÖ¤Ï dhclient-exit-hooks ¤Î exit_status ¥·¥§¥ëÊÑ¿ô¤ËÅϤµ¤ì¡¢
+µ¯Æ°¤µ¤ì¤¿»Å»ö¤Ë¥¹¥¯¥ê¥×¥È¤¬À®¸ù¤·¤¿¾ì¹ç¤Ë¤ÏÃͤϾï¤Ë 0 ¤Ë¤Ê¤ê¤Þ¤¹¡£
+dhclient-enter-hooks ¤Î¹à¤ÇÁ°½Ò¤·¤¿¤½¤Î¾¤Î´Ä¶­¤â°ú¤­·Ñ¤¬¤ì¤Þ¤¹¡£
+.B ETCDIR/dhclient-exit-hooks
+¤Ï exit_status ¤Ë¼ê¤ò²Ã¤¨¤Æ
+dhclient-script ¤ÎÌá¤êÃͤòÊѹ¹¤Ç¤­¤Þ¤¹¡£
+.SH Áàºî
+dhclient ¤¬¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤òµ¯Æ°¤¹¤ëɬÍפ¬¤¢¤ë¤È¤­¡¢
+ÍÍ¡¹¤ÊÊÑ¿ô¤ò´Ä¶­¤ËÄêµÁ¤·¤Æ¤«¤é
+.B CLIENTBINDIR/dhclient-script
+¤òµ¯Æ°¤·¤Þ¤¹¡£
+¤¹¤Ù¤Æ¤Î¾ì¹ç¤Ë¤ª¤¤¤Æ¡¢$reason ¤Ë¤Ï¥¹¥¯¥ê¥×¥È¤¬µ¯Æ°¤µ¤ì¤ëÍýͳ̾¤¬ÀßÄꤵ¤ì¤Þ¤¹¡£
+¼¡¤ÎÍýͳ¤¬¸½ºßÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹:
+MEDIUM, PREINIT, BOUND, RENEW, REBIND, REBOOT,
+EXPIRE, FAIL, TIMEOUT¡£
+.PP
+.SH MEDIUM
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥á¥Ç¥£¥¢¥¿¥¤¥×¤ÎÀßÄê¤òµá¤á¤Æ¤¤¤Þ¤¹¡£
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¡¢¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+.SH PREINIT
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+¼ÂºÝ¤Î¥¢¥É¥ì¥¹¤ò¼õ¤±¼è¤ëÁ°¤Ë¥Ñ¥±¥Ã¥È¤òÁ÷¿®¤¹¤ëÌÜŪ¤Ç¡¢
+Í×µáÄ̤ê¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ÀßÄꤵ¤ì¤ë¤³¤È¤òµá¤á¤Æ¤¤¤Þ¤¹¡£
+BSD ¤Î¥½¥±¥Ã¥È¥é¥¤¥Ö¥é¥ê¤ò»ÈÍѤ¹¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢
+IP ¥¢¥É¥ì¥¹ 0.0.0.0 ¤«¤Ä¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹ 255.255.255.255 ¤Ç¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+¾¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢
+¼ÂºÝ¤Ë IP ¥¢¥É¥ì¥¹¤òÍ¿¤¨¤ë¤³¤È¤Ê¤¯Ã±¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤³¤È¤Ç
+¼Â¸½¤µ¤ì¤ë¤Ç¤·¤ç¤¦¡£
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¡¢¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+.PP
+IP ¥¨¥¤¥ê¥¢¥¹¤¬ dhclient.conf ¤ÇÀë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢
+¤³¤Î¥¢¥É¥ì¥¹¤¬ $alias_ip_address ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+ËÜ IP ¥¢¥É¥ì¥¹¤Ø¤Î·ÐÏ©¤È¤È¤â¤Ë¡¢
+ËÜ IP ¥¢¥É¥ì¥¹¤òÂоݥ¤¥ó¥¿¥Õ¥§¡¼¥¹¤«¤éºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+.SH BOUND
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¿·¥¢¥É¥ì¥¹¤Ø¤Î½é´ü¤Î·ë¹ç¤ò´°Î»¤·¤Þ¤·¤¿¡£
+¿·¤·¤¤ IP ¥¢¥É¥ì¥¹¤Ï $new_ip_address ¤ÇÅϤµ¤ì¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤Ï $interface ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ï $medium ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+¥µ¡¼¥Ð¤«¤é³ÍÆÀ¤·¤¿¥ª¥×¥·¥ç¥ó¤Ï¡¢\fBdhcp-options\fR ¤ÇÀë¸À¤µ¤ì¤Æ¤¤¤ë
+¥ª¥×¥·¥ç¥ó̾¤ÇÅϤµ¤ì¤Þ¤¹¡£
+Îã³°¤È¤·¤Æ¡¢ Í­¸ú¤Ê¥·¥§¥ëÊÑ¿ô¤È¤¹¤ë¤¿¤á¤Ë
+¥À¥Ã¥·¥å ('-') ¤Ï¥¢¥ó¥À¥¹¥³¥¢('_')¤ÇÃÖ¤­´¹¤¨¤é¤ì¡¢
+ÊÑ¿ô̾¤Ï new_ ¤Ç³«»Ï¤·¤Þ¤¹¡£
+Î㤨¤Ð¡¢¿·¤·¤¤¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤Ï $new_subnet_mask ¤ÇÅϤµ¤ì¤Þ¤¹¡£
+.PP
+¥¢¥É¥ì¥¹¤ò¼ÂºÝ¤ËÀßÄꤹ¤ëÁ°¤Ë¡¢dhclient-script ¤Ï²¿¤é¤«¤ÎÊýË¡¤Ç
+¤½¤Î¥¢¥É¥ì¥¹¤ËÂФ·¤Æ ARP ¤ò¹Ô¤¤¡¢ÊÖ»ö¤ò¼õ¤±¼è¤Ã¤¿¾ì¹ç¤Ë¤ÏÈó 0 ¤ÎÃͤÇ
+½ªÎ»¤¹¤ë¤Ù¤­¤Ç¤¹¡£¤³¤Î¾ì¹ç¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCPDECLINE ¥á¥Ã¥»¡¼¥¸¤ò¥µ¡¼¥Ð
+¤ËÁ÷¿®¤·¡¢°ã¤¦¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Þ¤¹¡£
+¤³¤Îºî¶È¤Ï RENEW, REBIND, REBOOT ¾õÂ֤ǤâƱÍͤ˹Ԥ¤¤Þ¤¹¤¬¡¢
+ɬ¤º¤·¤âɬÍפǤϤʤ¯¡¢¼ÂºÝ¹¥¤Þ¤·¤¯¤Ê¤¤¤Ç¤·¤ç¤¦¡£
+.PP
+·ë¹ç¤¬´°Î»¤¹¤ë¤È¡¢
+¥Í¥Ã¥È¥ï¡¼¥¯¤Ë´Ø¤¹¤ë¿¤¯¤Î¥Ñ¥é¥á¡¼¥¿¤òÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£
+$new_domain_name ¤ª¤è¤Ó $new_domain_name_servers
+(¤³¤ì¤Ë¤ÏÊ£¿ô¤Î¥µ¡¼¥Ð¤ò¶õÇò¤Ç¶èÀڤäÆÎóµó¤·¤Æ¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó) ¤ò»ÈÍѤ·¤Æ¡¢
+¿·¤·¤¤ /etc/resolv.conf ¤òºîÀ®¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤Ï¡¢$new_routers ¤ò»ÈÍѤ·¤ÆÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+ÀÅŪ·ÐÏ©¤Ï¡¢$new_static_routes ¤ò»ÈÍѤ·¤ÆÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£
+.PP
+IP ¥¨¥¤¥ê¥¢¥¹¤¬Àë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤³¤ÇÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+¥¨¥¤¥ê¥¢¥¹¤Î IP ¥¢¥É¥ì¥¹¤Ï $alias_ip_address ¤È¤·¤Æµ­½Ò¤µ¤ì¡¢
+¥¨¥¤¥ê¥¢¥¹ÍѤËÀßÄꤵ¤ì¤ë¾¤Î DHCP ¥ª¥×¥·¥ç¥ó (Î㤨¤Ð¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯) ¤Ï
+Á°½Ò¤Î¤è¤¦¤ËÊÑ¿ô¤ÇÅϤµ¤ì¤Þ¤¹¤¬¡¢
+$new_ ¤Ç³«»Ï¤¹¤ë¤Î¤Ç¤Ï¤Ê¤¯ $alias_ ¤Ç³«»Ï¤·¤Þ¤¹¡£
+¥¨¥¤¥ê¥¢¥¹¤Î IP ¥¢¥É¥ì¥¹¤¬·ë¹ç¤µ¤ì¤¿ IP ¥¢¥É¥ì¥¹ ($new_ip_address) ¤È
+Ʊ¤¸¾ì¹ç¡¢¤³¤ì¤ò»ÈÍѤ·¤Æ¤Ï¤Ê¤é¤Ê¤¤¤³¤È¤ËÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤Ê¤¼¤Ê¤é¡¢¤³¤Î¾ì¹ç¤Ë¤Ï¾¤Î¥¨¥¤¥ê¥¢¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤¬Àµ¤·¤¯¤Ê¤¤²ÄǽÀ­¤¬¤¢¤ë
+¤«¤é¤Ç¤¹¡£
+.SH RENEW
+·ë¹ç¤¬¹¹¿·¤µ¤ì¤ë¤È¡¢¥¹¥¯¥ê¥×¥È¤Ï BOUND ¤ÈƱÍͤ˸ƤФì¤Þ¤¹¤¬¡¢
+$new_ ¤Ç³«»Ï¤¹¤ëÁ´ÊÑ¿ô¤Ë²Ã¤¨¤Æ $old ¤Ç³«»Ï¤¹¤ëÊ̤ÎÊÑ¿ô¤ÎÁȤ¬¤¢¤ë¤È¤¤¤¦
+Îã³°¤¬¤¢¤ê¤Þ¤¹¡£
+Êѹ¹¤µ¤ì¤¿²ÄǽÀ­¤¬¤¢¤ë±Ê³Ū¤ÊÀßÄê¤Ï¡¢ºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+Î㤨¤Ð¡¢·ë¹ç¤µ¤ì¤¿¥¢¥É¥ì¥¹¤ËÂФ¹¤ë¥í¡¼¥«¥ë·ÐÏ©¤¬ÀßÄꤵ¤ì¤¿¾ì¹ç¡¢
+¸Å¤¤¥í¡¼¥«¥ë·ÐÏ©¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤¬Êѹ¹¤µ¤ì¤¿¾ì¹ç¡¢¸Å¤¤¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+ÀÅŪ·ÐÏ©¤¬Êѹ¹¤µ¤ì¤¿¾ì¹ç¡¢¸Å¤¤¤â¤Î¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+¤½¤Î¾¤Ë¤Ä¤¤¤Æ¤Ï¡¢BOUND ¤ÈƱÍͤ˽èÍý²Äǽ¤Ç¤¹¡£
+.SH REBIND
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢¿·µ¬ DHCP ¥µ¡¼¥Ð¤ËºÆ·ë¹ç¤µ¤ì¤Þ¤·¤¿¡£
+¤³¤ì¤Ï RENEW ¤ÈƱÍͤ˰·¤¨¤Þ¤¹¤¬¡¢IP ¥¢¥É¥ì¥¹¤¬ÊѤï¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢
+ARP ɽ¤ò¥¯¥ê¥¢¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+.SH REBOOT
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥ê¥Ö¡¼¥È¸å¤Ë¸µ¤Î¥¢¥É¥ì¥¹¤òºÆ³ÍÆÀ¤¹¤ë¤³¤È¤ËÀ®¸ù¤·¤Þ¤·¤¿¡£
+¤³¤ì¤Ï BOUND ¤ÈƱÍͤ˽èÍý²Äǽ¤Ç¤¹¡£
+.SH EXPIRE
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¥ê¡¼¥¹¹¹¿·¤È¿·µ¬¥ê¡¼¥¹³ÍÆÀ¤Ë¼ºÇÔ¤·¡¢
+¥ê¡¼¥¹¤Î´ü¸Â¤¬ÀÚ¤ì¤Þ¤·¤¿¡£
+ÂÐ¾Ý IP ¥¢¥É¥ì¥¹¤ò²òÊü¤¹¤ëɬÍפ¬¤¢¤ê¡¢
+RENEW ¤ª¤è¤Ó REBIND ¤ÈƱÍͤˡ¢´ØÏ¢¤¹¤ë¥Ñ¥é¥á¡¼¥¿¤òºï½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+.SH FAIL
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCP ¥µ¡¼¥Ð¤ËÀܳ¤Ç¤­¤º¡¢
+¤Þ¤¿¸¡ºº¤·¤¿ IP ¥¢¥É¥ì¥¹¤Ë¤ÏÍ­¸ú¤Ê¤â¤Î¤Ï¤¢¤ê¤Þ¤»¤ó¤Ç¤·¤¿¡£
+ºÇ¸å¤Ë¸¡ºº¤·¤¿¥ê¡¼¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤Ï¡¢ÀßÄê²ò½ü¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+¤³¤ì¤Ï¡¢EXPIRE ¤ÈƱÍͤ˰·¤¨¤Þ¤¹¡£
+.SH TIMEOUT
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤É¤Î DHCP ¥µ¡¼¥Ð¤Ë¤âÀܳ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿¡£
+¤·¤«¤·¤Ê¤¬¤é¡¢¸Å¤¤¥ê¡¼¥¹¤¬¼±Ê̤µ¤ì¡¢
+BOUND ¤ÈƱÍͤˡ¢¤³¤Î¸Å¤¤¥ê¡¼¥¹¤Î¥Ñ¥é¥á¡¼¥¿¤¬ÅϤµ¤ì¤Þ¤·¤¿¡£
+¥¯¥é¥¤¥¢¥ó¥È¤ÎÀßÄꥹ¥¯¥ê¥×¥È¤Ï¡¢¤³¤Î¥Ñ¥é¥á¡¼¥¿¤ò¸¡ºº¤·¡¢
+¤³¤ì¤¬Í­¸ú¤Ç¤¢¤ë¤È¿®¤¸¤ëÍýͳ¤¬¤¢¤ë¤Ê¤é¤Ð¡¢ÃÍ 0 ¤Ç½ªÎ»¤¹¤Ù¤­¤Ç¤¹¡£
+¤½¤¦¤Ç¤Ê¤¤¤Ê¤é¤Ð¡¢Èó 0 ¤ÎÃͤǽªÎ»¤¹¤Ù¤­¤Ç¤¹¡£
+.PP
+¥ê¡¼¥¹¤ò¸¡ºº¤¹¤ëÄ̾ï¤ÎÊýË¡¤Ï¡¢REBIND ¤ÈƱÍͤ˥ͥåȥ¥¯¤òÀßÄꤷ¤Æ
+(Ê£¿ô¤Î¥ê¡¼¥¹¤ò¸¡ºº¤¹¤ë¤¿¤á¤Ë¸Æ¤Ð¤ì¤ë¤³¤È¤¬¤¢¤ë¤«¤é¤Ç¤¹)¡¢
+$routers ¤ÇÄêµÁ¤µ¤ì¤ëºÇ½é¤Î¥ë¡¼¥¿¤Ë ping ¤¹¤ë¤³¤È¤Ç¤¹¡£
+±þÅú¤ò¼õ¿®¤·¤¿¾ì¹ç¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¸½ºßÀܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤ËÂФ·¤Æ¡¢¥ê¡¼¥¹¤¬Í­¸ú¤Ç¤¹¡£
+$new_static_routers ¤Ë²Ã¤¨¤Æ
+$new_routers ¤ËÎóµó¤µ¤ì¤Æ¤¤¤ëÁ´¥ë¡¼¥¿¤Ë ping ¤ò»î¤¹¤è¤¦¤Ë¤Ê¤ì¤Ð¡¢
+´°Á´À­¤¬Áý¤¹¤Ç¤·¤ç¤¦¡£¤·¤«¤·¡¢¸½ºß¤Î¥¹¥¯¥ê¥×¥È¤Ï¤½¤¦¤Ê¤Ã¤Æ¤¤¤Þ¤»¤ó¡£
+.SH ´ØÏ¢¥Õ¥¡¥¤¥ë
+Îà»÷¤·¤¿¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤ËÂФ¹¤ë¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤Ï
+»÷¤Æ¤¤¤¿¤êÁ´¤¯Æ±¤¸¤«¤â¤·¤ì¤Þ¤»¤ó¤¬¡¢°ìÈ̤ˤϡ¢
+³Æ¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥àÍѤ˳ơ¹¤Î¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤¬¤¢¤ë¤Ù¤­¤Ç¤¹¡£
+Internet Systems Consortium ¤Î DHCP ÇÛÉۤ˴ޤޤì¤ë¥¹¥¯¥ê¥×¥È¥Õ¥¡¥¤¥ë¤Ï¡¢
+client/scripts °Ê²¼¤ÎÇÛÉۥĥ꡼¤Ë¤¢¤ê¡¢
+Æ°ºîÂоݥª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à̾¤Ë¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£
+.SH ¥Ð¥°
+Ê£¿ô¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢
+¥µ¡¼¥Ð¤¬Ä󶡤¹¤ëÀßÄê¥Ñ¥é¥á¡¼¥¿Æ±»Î¤¬
+¾×Æͤ·¤Ê¤¤¤è¤¦¤Ë¤¹¤ëÌÀ³Î¤ÊÊýË¡¤Ï¤¢¤ê¤Þ¤»¤ó¡£
+Î㤨¤Ð¡¢
+ɸ½à¤Î dhclient-script ¤Ï /etc/resolv.conf ¤òºÆÅÙ½ñ¤­´¹¤¨¤Æ¤·¤Þ¤¤¤Þ¤¹¡£
+¤¹¤Ê¤ï¤Á¡¢Ê£¿ô¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢
+¤¢¤ë¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃÍ¤Ë /etc/resolv.conf ¤¬½é´ü²½¤µ¤ì¤¿¸å¤Ë¡¢
+Ê̤Υµ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃͤ˽é´ü²½¤µ¤ì¤ë¤È¤¤¤¦Æ°ºî¤ò·«¤êÊÖ¤·¤Þ¤¹¡£
+¤É¤Á¤é¤Î¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ë¾ðÊó¤âÍ­¸ú¤Ç¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢
+¼ÂºÝ¾åÌäÂê¤È¤Ï¤Ê¤é¤Ê¤¤¤â¤Î¤Î¡¢º®Íð¤Î¤â¤È¤Ë¤Ê¤ê¤¨¤Þ¤¹¡£
+.SH ´ØÏ¢¹àÌÜ
+dhclient.conf(5), dhclient.leases(5), dhclient(8)
+.SH ºî¼Ô
+.B dhclient-script(8)
+¤Ï Ted Lemon ¤¬
+Vixie Enterprises ¤È¶¨ÎϤ·¤Æ Internet Systems Consortium ¤Î¤¿¤á¤Ë
+½ñ¤­¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢
+.B https://www.isc.org
+¤ò¤´Í÷¤¯¤À¤µ¤¤¡£
+Vixie Enterprises ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢
+.B http://www.vix.com
+¤ò¤´Í÷¤¯¤À¤µ¤¤¡£
diff --git a/doc/ja_JP.eucJP/dhclient.8 b/doc/ja_JP.eucJP/dhclient.8
new file mode 100644
index 0000000..61ac6a4
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhclient.8
@@ -0,0 +1,365 @@
+.\" $Id: dhclient.8,v 1.3.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" Portions copyright (c) 2000 David E. O'Brien.
+.\" All rights reserved.
+.\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.8,v 1.8.2.3 2002/04/11 10:16:45 murray Exp %
+.\"
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man8/dhclient.8,v 1.8 2002/05/21 03:46:48 horikawa Exp $
+.\" WORD: Dynamic Host Configuration Protocol (DHCP) ưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë
+.\" WORD: lease ¥ê¡¼¥¹ [dhclient.8]
+.\" WORD: mobile host °ÜÆ°¥Û¥¹¥È
+.\" WORD: limited broadcast address ¥ê¥ß¥Æ¥Ã¥É¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹
+.\" WORD: networking framework ¥Í¥Ã¥È¥ï¡¼¥­¥ó¥°¥Õ¥ì¡¼¥à¥ï¡¼¥¯
+.\" WORD: housekeeping chores ȬȚ
+.TH dhclient 8
+.SH ̾¾Î
+dhclient - ưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë¤Î¥¯¥é¥¤¥¢¥ó¥È
+.SH ½ñ¼°
+.B dhclient
+[
+.B -p
+.I port
+]
+[
+.B -D
+]
+[
+.B -d
+]
+[
+.B -q
+]
+[
+.B -1
+]
+[
+.B -r
+]
+[
+.B -lf
+.B lease-file
+]
+[
+.B -pf
+.I pid-file
+]
+[
+.B -cf
+.I config-file
+]
+[
+.B -sf
+.I script-file
+]
+[
+.B -s
+server
+]
+[
+.B -g
+relay
+]
+[
+.B -n
+]
+[
+.B -nw
+]
+[
+.B -w
+]
+[
+.I if0
+[
+.I ...ifN
+]
+]
+.SH ²òÀâ
+Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤¢¤ë dhclient
+¤ÏưŪ¥Û¥¹¥ÈÀßÄê¥×¥í¥È¥³¥ë (DHCP: Dynamic Host Configuration Protocol)
+¤Þ¤¿¤Ï BOOTP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤Æ¡¢¤¢¤ë¤¤¤Ï
+¤³¤ì¤é¤Î¥×¥í¥È¥³¥ë¤¬¼ºÇÔ¤·¤¿¾ì¹ç¤Ë¤Ï¥¢¥É¥ì¥¹¤òÀÅŪ¤Ë³ä¤êÅö¤Æ¤Æ¡¢
+1 ¤Ä°Ê¾å¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ëÊýË¡¤òÄ󶡤·¤Þ¤¹¡£
+.SH Áàºî
+.PP
+DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¡¢1 ¤Ä°Ê¾å¤Î¥µ¥Ö¥Í¥Ã¥È¤Ë³ä¤êÅö¤Æ¤ë¤³¤È¤Î¤Ç¤­¤ë
+IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò´ÉÍý¤¹¤ëÃæ±û¥µ¡¼¥Ð¤Ë¡¢¥Û¥¹¥È¤¬¥¢¥¯¥»¥¹¤Ç¤­¤Þ¤¹¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¥ê¥¹¥È¤«¤é¥¢¥É¥ì¥¹¤òÍ׵ᤷ¤Æ¡¢
+¤½¤ì¤ò¥Í¥Ã¥È¥ï¡¼¥¯ÄÌ¿®¤Î°ì»þŪ¤ÊÅÚÂæ¤ËÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤Þ¤¿ DHCP ¥×¥í¥È¥³¥ë¤Ï¡¢¥Ç¥Õ¥©¥ë¥È¥ë¡¼¥¿¤Î¾ì½ê¤ä¥Í¡¼¥à¥µ¡¼¥Ð¤Î¾ì½ê¤Ê¤É¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬Àܳ¤·¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Ë´Ø¤¹¤ë½ÅÍפʾðÊó¤ò
+¥¯¥é¥¤¥¢¥ó¥È¤Ë¾ÜºÙ¤ËÃΤ餻¤ëµ¡¹½¤âÄ󶡤·¤Þ¤¹¡£
+.PP
+µ¯Æ°»þ¤Ë dhclient ¤Ï
+.IR dhclient.conf
+¤«¤éÀßÄê»Ø¼¨¤òÆɤ߼è¤ê¤Þ¤¹¡£
+¤½¤ì¤«¤é¸½ºß¤Î¥·¥¹¥Æ¥à¤ËÁȤ߹þ¤Þ¤ì¤Æ¤¤¤ë
+¤¹¤Ù¤Æ¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥ê¥¹¥È¤ò¼èÆÀ¤·¤Þ¤¹¡£
+³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÂФ· dhclient ¤Ï DHCP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤ÆÀßÄê¤ò»î¤ß¤Þ¤¹¡£
+.PP
+¥·¥¹¥Æ¥à¥ê¥Ö¡¼¥È¤ä¥µ¡¼¥ÐºÆµ¯Æ°¤ÎºÝ¤Ë¥ê¡¼¥¹¤ò¼º¤ï¤Ê¤¤¤è¤¦¤Ë¡¢
+dhclient ¤Ï³ä¤êÅö¤Æ¤é¤ì¤¿¥ê¡¼¥¹¤Î¥ê¥¹¥È¤ò
+dhclient.leases(5) ¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¤Þ¤¹¡£
+µ¯Æ°»þ¡¢dhclient.conf ¥Õ¥¡¥¤¥ë¤òÆɤ߼è¤Ã¤¿¸å¡¢
+dhclient ¤Ï dhclient.leases ¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ó¤Ç¡¢
+³ä¤êÅö¤Æ¤é¤ì¤¿¥ê¡¼¥¹¤Ë´Ø¤¹¤ë¥á¥â¥ê¤ò¹¹¿·¤·¤Þ¤¹¡£
+.PP
+¿·¤·¤¤¥ê¡¼¥¹¤ò¼èÆÀ¤¹¤ë¤È¡¢dhclient.leases ¥Õ¥¡¥¤¥ë¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤é¤ì¤Þ¤¹¡£
+¥Õ¥¡¥¤¥ë¤¬¶Ëü¤ËÂ礭¤¯¤Ê¤ë¤Î¤òËɤ°¤¿¤á¤Ë¡¢
+dhclient ¤Ï»þ¤ª¤ê¥³¥¢ÆâÉô¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é
+¿·µ¬¤Ë dhclient.leases ¥Õ¥¡¥¤¥ë¤òºîÀ®¤·¤Þ¤¹¡£
+¸Å¤¤ dhclient.leases ¥Õ¥¡¥¤¥ë¤Ï¡¢
+dhclient ¤¬¼¡¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤òºî¤êÂؤ¨¤ë¤Þ¤Ç¡¢
+.IR dhclient.leases~
+¤È¤¤¤¦Ì¾Á°¤ÇÊݸ¤µ¤ì¤Þ¤¹¡£
+.PP
+dhclient ¤¬ºÇ½é¤Ëµ¯Æ°¤µ¤ì¤¿¤È¤­
+(°ìÈÌŪ¤Ë¤Ï¥·¥¹¥Æ¥à¥Ö¡¼¥È½é´ü²áÄø¤Î´Ö) ¤Ë DHCP ¥µ¡¼¥Ð¤¬ÍøÍѤǤ­¤Ê¤±¤ì¤Ð¡¢
+¸Å¤¤¥ê¡¼¥¹¤Ï»Ä¤µ¤ì¤Þ¤¹¡£
+¤½¤Î¾ì¹ç¡¢dhclient.leases ¥Õ¥¡¥¤¥ë¤«¤é
+¤Þ¤À´ü¸Â¤ÎÀÚ¤ì¤Æ¤¤¤Ê¤¤¸Å¤¤¥ê¡¼¥¹¤ò¸¡ºº¤·¡¢
+Í­¸ú¤Ç¤¢¤ë¤ÈȽÃǤµ¤ì¤ì¤Ð¡¢¤½¤ì¤é¤Î´ü¸Â¤¬ÀÚ¤ì¤ë¤«
+¤Þ¤¿¤Ï DHCP ¥µ¡¼¥Ð¤¬ÍøÍѤǤ­¤ë¤è¤¦¤Ë¤Ê¤ë¤Þ¤Ç¡¢¤½¤Î¥ê¡¼¥¹¤ò»È¤¤¤Þ¤¹¡£
+.PP
+DHCP ¥µ¡¼¥Ð¤¬Â¸ºß¤·¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ë»þ¤ª¤ê¥¢¥¯¥»¥¹¤¹¤ëɬÍפ¬
+¤¢¤ë¤è¤¦¤Ê°ÜÆ°¥Û¥¹¥È¤Ï¡¢¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¾å¤Î¸ÇÄꥢ¥É¥ì¥¹¤Î¥ê¡¼¥¹¤ò
+¤¢¤é¤«¤¸¤áÆɤ߹þ¤ó¤Ç¤ª¤¯¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤¬¤É¤ì¤âÀ®¸ù¤·¤Ê¤«¤Ã¤¿¾ì¹ç¡¢
+dhclient ¤Ï¤½¤ÎÀÅŪ¤Ê¥ê¡¼¥¹¤¬Í­¸ú¤Ç¤¢¤ë¤«¸¡¾Ú¤·¡¢
+Í­¸ú¤Ç¤¢¤ì¤Ð¼¡¤ËºÆµ¯Æ°¤µ¤ì¤ë¤Þ¤Ç¤½¤Î¥ê¡¼¥¹¤ò»È¤¤¤Þ¤¹¡£
+.PP
+¤Þ¤¿°ÜÆ°¥Û¥¹¥È¤Ï¡¢DHCP ¤ÏÍøÍѤǤ­¤Ê¤¤¤¬ BOOTP ¤Ê¤éÍøÍѤǤ­¤ë¤è¤¦¤Ê
+¥Í¥Ã¥È¥ï¡¼¥¯¤Ø°ÜÆ°¤¹¤ë¤³¤È¤â¤¢¤ë¤Ç¤·¤ç¤¦¡£
+¤½¤Î¤è¤¦¤Ê¾ì¹ç¤Ï¡¢¸Å¤¤¥ê¡¼¥¹¤ò½ç¼¡»î¤¹¤è¤ê¤â¡¢
+¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¤Î´ÉÍý¼Ô¤ÈÁêÃ̤·¤Æ
+BOOTP ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¥¨¥ó¥È¥ê¤òºîÀ®¤·¤Æ¤â¤é¤¤¡¢
+¤½¤Î¥Í¥Ã¥È¥ï¡¼¥¯¾å¤ÇÁÇÁ᤯¥Ö¡¼¥È¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤È¤è¤¤¤Ç¤·¤ç¤¦¡£
+.SH ¥³¥Þ¥ó¥É¥é¥¤¥ó
+.PP
+dhclient ¤¬ÀßÄꤷ¤è¤¦¤È¤¹¤ë¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î̾Á°¤ò
+¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ç»ØÄê¤Ç¤­¤Þ¤¹¡£
+¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹Ì¾¤¬»ØÄꤵ¤ì¤Ê¤±¤ì¤Ð¡¢
+dhclient ¤Ï¤¹¤Ù¤Æ¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¼±Ê̤·¡¢
+²Äǽ¤Ê¤éÈó¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï½ü¤¤¤Æ¡¢
+¤½¤ì¤¾¤ì¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤷ¤è¤¦¤È¤·¤Þ¤¹¡£
+.PP
+.B dhclient.conf(5)
+¥Õ¥¡¥¤¥ëÃæ¤Î̾Á°¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ØÄꤹ¤ë¤³¤È¤â²Äǽ¤Ç¤¹¡£
+¤³¤ÎÊýË¡¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ØÄꤷ¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+ÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ç»ØÄꤷ¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤â¤·¤¯¤Ï¥³¥Þ¥ó¥É¹Ô¤Ç
+»ØÄꤷ¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤É¤Á¤é¤«¤À¤±¤òÀßÄꤹ¤ë¤Ç¤·¤ç¤¦¡£
+.PP
+.B -D
+¥Õ¥é¥°¤ò»ØÄꤹ¤ë¤È¡¢
+.B dhclient
+¤¬
+.B dhclient-script
+¤ÈÁȤ߹ç¤ï¤»¤Æ»ÈÍѤ¹¤ë¤¿¤á¤ËºîÀ®¤·¤¿¥¹¥¯¥ê¥×¥È¤ò¡¢
+.IR /tmp
+¤ËÊݸ¤µ¤»¤Þ¤¹¡£
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬É¸½à¥Ý¡¼¥È (¥Ý¡¼¥ÈÈÖ¹æ 68) °Ê³°¤Î¥Ý¡¼¥È¤Ç
+ÂÔµ¡¤ª¤è¤ÓÁ÷¿®¤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï
+.B -p
+¥Õ¥é¥°¤¬»È¤¨¤Þ¤¹¡£
+¤³¤Î¥Õ¥é¥°¤Ë³¤±¤Æ¡¢dhclient ¤¬»È¤¦ udp ¥Ý¡¼¥ÈÈÖ¹æ¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤ì¤Ï¼ç¤È¤·¤Æ¥Ç¥Ð¥Ã¥°ÌÜŪ¤Ç¤ÏÍ­ÍѤǤ¹¡£
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÂÔµ¡¤ª¤è¤ÓÁ÷¿®¤¹¤ë¤¿¤á¤Ë»ÈÍѤ¹¤ë¥Ý¡¼¥È¤Ë
+¥Ç¥Õ¥©¥ë¥È¤È¤Ï°ã¤¦¥Ý¡¼¥È¤ò»ØÄꤹ¤ë¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï
+¤â¤¦ 1 ¤ÄÊ̤ÎÁ÷¿®Àè¥Ý¡¼¥È¤â»ÈÍѤ·¤Þ¤¹¡£¤½¤ÎÁ÷¿®Àè¥Ý¡¼¥È¤Ï¡¢
+»ØÄꤷ¤¿Á÷¿®Àè¥Ý¡¼¥È¤è¤ê¤âÂ礭¤ÊÈÖ¹æ¤ò»ý¤Ã¤¿¤â¤Î¤Ç¤¹¡£
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï IP ¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤·¤Æ¤¤¤Ê¤¤´Ö
+Ǥ°Õ¤Î¥×¥í¥È¥³¥ë¥á¥Ã¥»¡¼¥¸¤ò¥ê¥ß¥Æ¥Ã¥É¥Ö¥í¡¼¥É¥­¥ã¥¹¥È
+¥¢¥É¥ì¥¹¤Ç¤¢¤ë 255.255.255.255 ¤Ø¤ÈÁ÷¿®¤·¤Þ¤¹¡£
+¥Ç¥Ð¥Ã¥°ÌÜŪ¤Ç¡¢¥µ¡¼¥Ð¤¬¤³¤ì¤é¤Î¥á¥Ã¥»¡¼¥¸¤ò¤É¤³¤«Ê̤Υ¢¥É¥ì¥¹¤Ø
+Á÷¿®¤·¤¿Êý¤¬ÊØÍø¤Ê¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£
+.B -s
+¥Õ¥é¥°¤Î¸å¤ËÁ÷¿®Àè¤Î IP ¥¢¥É¥ì¥¹¤â¤·¤¯¤Ï¥É¥á¥¤¥ó̾¤ò¤Ä¤±¤Æ»ØÄê
+¤Ç¤­¤Þ¤¹¡£
+¥Æ¥¹¥ÈÌÜŪ¤Ç¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷¿®¤¹¤ëÁ´¤Æ¤Î¥Ñ¥±¥Ã¥È¤Î
+giaddr ¥Õ¥£¡¼¥ë¥É¤ò
+.B -g
+¥Õ¥é¥°¤ËÁ÷¿®Àè¤Î IP ¥¢¥É¥ì¥¹¤ò³¤±¤¿·Á¤ò»ÈÍѤ¹¤ë¤³¤È¤ÇÀßÄꤹ¤ë
+¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤³¤ì¤Ï¥Æ¥¹¥ÈÌÜŪ¤Î»þ¤Î¤ßÍ­ÍѤʤâ¤Î¤Ç¤¢¤ê¡¢
+·ø¼Â¤µ¤ä»È¤¤¤ä¤¹¤µ¤òµá¤á¤ë¾õ¶·¤ÇÆ°ºî¤¹¤ë¤³¤È¤òÁÛÄꤷ¤Æ¤Ï
+¤¤¤±¤Þ¤»¤ó¡£
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾磻¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤Þ¤Ç¤Ï
+¥Õ¥©¥¢¥°¥é¥¦¥ó¥É¤ÇÆ°ºî¤·¡¢¤½¤Î¸å¥Ð¥Ã¥¯¥°¥é¥¦¥ó¥É¤ÇÆ°ºî
+¤¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£dhclient ¤ò¾ï¤Ë¥Õ¥©¥¢¥°¥é¥¦¥ó¥É¤Î
+¥×¥í¥»¥¹¤È¤·¤ÆÆ°ºî¤µ¤»¤ë¤¿¤á¤Ë¤Ï¡¢
+.B -d
+¥Õ¥é¥°¤ò»ØÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£¤³¤ì¤Ï¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬
+¥Ç¥Ð¥Ã¥¬¤Î¤â¤È¤ÇÆ°ºî¤·¤Æ¤¤¤ë¾ì¹ç¤ä¡¢System V ¥·¥¹¥Æ¥à¤Î
+inittab ¤Î³°Â¦¤ÇÆ°ºî¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤ÏÍ­¸ú¤Ê¤â¤Î¤Ç¤¹¡£
+.PP
+¤³¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï¤Ïµ¯Æ°¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤·¡¢¥¢¥É¥ì¥¹¤ò
+³ÍÆÀ¤¹¤ë¤Þ¤Çɸ½à¥¨¥é¡¼½ÐÎϤ˥ץí¥È¥³¥ë¥·¡¼¥±¥ó¥¹¤ò
+½ñ¤­½Ð¤·¤Þ¤¹¡£¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤·¤¿¸å¤Ï
+.B syslog (3)
+¥Õ¥¡¥·¥ê¥Æ¥£¤ò»ÈÍѤ·¤Æ¥á¥Ã¥»¡¼¥¸¤Î¥í¥°¤ò¼è¤ë¤À¤±¤Ë¤Ê¤ê¤Þ¤¹¡£
+.B -q
+¥Õ¥é¥°¤ò»ÈÍѤ¹¤ë¤È¡¢¥¨¥é¡¼°Ê³°¤Î¥á¥Ã¥»¡¼¥¸¤òɸ½à¥¨¥é¡¼½ÐÎϤË
+½ñ¤­½Ð¤µ¤Ê¤¤¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£
+.PP
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCP ¥×¥í¥È¥³¥ë¤ÇµÁ̳¤Å¤±¤é¤ì¤Æ¤¤¤Ê¤¤¤¿¤á¡¢
+Ä̾ï¤Ï¸½ºß¼èÆÀ¤·¤Æ¤¤¤ë¥ê¡¼¥¹¤ò³«Êü¤¹¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£
+¤¿¤À¡¢¥±¡¼¥Ö¥ë ISP ¤Î¤Ê¤«¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬
+³ä¤êÅö¤Æ¤é¤ì¤¿IP ¥¢¥É¥ì¥¹¤ò³«Êü¤·¤¿¤¤¾ì¹ç¤Ë¤Ï¡¢¥µ¡¼¥Ð¤Ë
+ÄÌÃΤ¹¤ë¤è¤¦¤ËµÁ̳¤Å¤±¤Æ¤¤¤ë¤È¤³¤í¤â¤¢¤ê¤Þ¤¹¡£
+.B -r
+¥Õ¥é¥°¤òÍѤ¤¤ë¤È¡¢ÌÀ¼¨Åª¤Ë¸½ºß¤Î¥ê¡¼¥¹¤ò³«Êü¤·¡¢¤¤¤Ã¤¿¤ó
+¥ê¡¼¥¹¤ò³«Êü¤¹¤ë¤È¥¯¥é¥¤¥¢¥ó¥È¤Ï½ªÎ»¤·¤Þ¤¹¡£
+.PP
+.B -1
+¥Õ¥é¥°¤ò»ØÄꤹ¤ë¤È¡¢
+dhclient ¤Ï¤Ò¤È¤Ä¤Î¥ê¡¼¥¹¤ËÂФ· 1 ÅÙ¤À¤±¤·¤«¼èÆÀ¤ò»î¤ß¤Þ¤»¤ó¡£
+¤â¤·¼èÆÀ¤Ë¼ºÇÔ¤¹¤ì¤Ð dhclient ¤Ï½ªÎ»¥³¡¼¥É 2 ¤Ç½ªÎ»¤·¤Þ¤¹¡£
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Ä̾ï¤ÏÀßÄê¾ðÊó¤ò
+.B ETCDIR/dhclient.conf
+¤«¤é¡¢¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò
+.B DBDIR/dhclient.leases
+¤«¤é¼èÆÀ¤·¡¢¼«Ê¬¤Î¥×¥í¥»¥¹ ID ¤ò
+.B RUNDIR/dhclient.pid
+¤È¤¤¤¦Ì¾Á°¤Î¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¡¢
+¤½¤·¤Æ¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò
+.B CLIENTBINDIR/dhclient-script
+¤ò»ÈÍѤ·¤ÆÀßÄꤷ¤Þ¤¹¡£
+¤³¤ì¤é¤Î¥Õ¥¡¥¤¥ë¤ËÊ̤Î̾Á°¤ò»ØÄꤷ¤¿¤ê¡¢Ê̤ξì½ê¤ò
+»ØÄꤷ¤¿¤ê¤¹¤ë¤Ë¤Ï¡¢¤½¤ì¤¾¤ì
+.B -cf,
+.B -lf,
+.B -pf
+¤ª¤è¤Ó
+.B -sf
+¥Õ¥é¥°¤ò¡¢¸å¤í¤Ë¥Õ¥¡¥¤¥ë̾¤ò³¤±¤ë·Á¤Ç»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤¡£
+¤³¤ÎÊýË¡¤Ï¡¢Î㤨¤Ð DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬µ¯Æ°¤·¤¿¤È¤­¤Ë
+.B DBDIR
+¤â¤·¤¯¤Ï
+.B RUNDIR
+¤¬¤Þ¤À¥Þ¥¦¥ó¥È¤µ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¤Ë¤ÏÆäËÍ­ÍѤʤâ¤Î¤Ë
+¤Ê¤êÆÀ¤Þ¤¹¡£
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ÀßÄꤹ¤Ù¤­¥Í¥Ã¥È¥ï¡¼¥¯
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òƱÄê¤Ç¤­¤Ê¤¤¾ì¹ç¡¢Ä̾ï¤Ï½ªÎ»¤·¤Þ¤¹¡£
+¥é¥Ã¥×¥È¥Ã¥×¥³¥ó¥Ô¥å¡¼¥¿¤ä¥Û¥Ã¥È¥¹¥ï¥Ã¥×²Äǽ¤Ê I/O ¥Ð¥¹¤ò
+»ý¤Ã¤¿¥³¥ó¥Ô¥å¡¼¥¿¤Ç¤Ï¡¢¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬
+¥·¥¹¥Æ¥àµ¯Æ°¸å¤ËÄɲ䵤ì¤ë¤³¤È¤¬¤¢¤êÆÀ¤Þ¤¹¡£
+.B -w
+¥Õ¥é¥°¤òÍѤ¤¤ë¤È¡¢¤½¤Î¤è¤¦¤Ê¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬ 1 ¤Ä¤â
+¸«¤Ä¤«¤é¤Ê¤¤¤È¤­¤Ë¤â¥¯¥é¥¤¥¢¥ó¥È¤¬½ªÎ»¤·¤Ê¤¤¤è¤¦¤Ë¤Ç¤­¤Þ¤¹¡£
+¸å¤Ç
+.B omshell (8)
+¥×¥í¥°¥é¥à¤ò»ÈÍѤ·¤Æ¡¢¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬Äɲ䵤줿¤ê
+ºï½ü¤µ¤ì¤¿¤ê¤·¤¿¤³¤È¤ò¥¯¥é¥¤¥¢¥ó¥È¤ËÄÌÃΤ¹¤ë¤³¤È¤¬¤Ç¤­¡¢
+¤³¤ì¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¤¬¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¾å¤Î
+IP ¥¢¥É¥ì¥¹¤òÀßÄꤹ¤ë¤è¤¦»î¤ß¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.PP
+.B -n
+¥Õ¥é¥°¤òÍѤ¤¤ë¤³¤È¤Ç¡¢¤É¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤âÀßÄꤷ¤è¤¦¤È
+¤·¤Ê¤¤¤è¤¦¤Ë DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò»Ø¼¨¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤³¤Î¥Õ¥é¥°¤Ï¡¢¤­¤Ã¤È
+.B -w
+¥Õ¥é¥°¤È¶¦¤Ë»ÈÍѤ¹¤ë¤ÈÍ­ÍѤǤ·¤ç¤¦¡£
+.PP
+IP ¥¢¥É¥ì¥¹¤ò³ÍÆÀ¤¹¤ë¤Þ¤ÇÂԤĤΤǤϤʤ¯¡¢Â¨ºÂ¤Ë¥Ç¡¼¥â¥ó¤È
+¤Ê¤ë¤è¤¦¤Ë¥¯¥é¥¤¥¢¥ó¥È¤ò»Ø¼¨¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+.B -nw
+¥Õ¥é¥°¤òÍ¿¤¨¤ë¤È²Äǽ¤Ç¤¹¡£
+.SH ÀßÄê
+dhclient.conf(5) ¥Õ¥¡¥¤¥ë¤Î½ñ¼°¤ÏÊ̤˲òÀ⤵¤ì¤Æ¤¤¤Þ¤¹¡£
+.SH OMAPI
+¤³¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Æ°ºîÃæ¤Ë¤½¤ÎÆ°ºî¤òÄä»ß¤µ¤»¤ë
+¤³¤È¤Ê¤¯¼«Ê¬¼«¿È¤òÀ©¸æ¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Îµ¡Ç½¤òÄ󶡤·¤Æ¤¤¤Þ¤¹¡£
+¤³¤Îµ¡Ç½¤Ï¡¢¥ê¥â¡¼¥È¥ª¥Ö¥¸¥§¥¯¥ÈÁàºî API ¤Ç¤¢¤ë OMAPI ¤ò
+ÍѤ¤¤ÆÄ󶡤µ¤ì¤Æ¤¤¤Þ¤¹¡£OMAPI ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢TCP/IP ¤ò
+»ÈÍѤ·¤Æ¤³¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÀܳ¤·¤Þ¤¹¡£¤½¤·¤Æ¡¢
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¸½ºß¤Î¾õÂÖ¤ò¸¡ºº¤Ç¤­¡¢¤½¤Î¾õÂÖ¤òÊѹ¹¤¹¤ë¤³¤È¤¬
+¤Ç¤­¤Þ¤¹¡£
+.PP
+¥æ¡¼¥¶¥×¥í¥°¥é¥à¤Ç¤Ï¡¢´ðÁäˤ¢¤ë OMAPI ¥×¥í¥È¥³¥ë¤òľÀܼÂÁõ¤¹¤ë
+¤Î¤Ç¤Ï¤Ê¤¯¡¢dhcpctl API ¤â¤·¤¯¤Ï OMAPI ¤½¤Î¤â¤Î¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£
+dhcpctl ¤Ï¡¢OMAPI ¤¬¼«Æ°¤Ç¹Ô¤Ã¤Æ¤Ï¤¯¤ì¤Ê¤¤»¨»ö¤Î¤¤¤¯¤Ä¤«¤ò°·¤¦
+¥é¥Ã¥Ñ¤Ç¤¹¡£dhcpctl ¤ª¤è¤Ó OMAPI ¤Ë¤Ä¤¤¤Æ¤Ï
+\fBdhcpctl(3)\fR ¤ª¤è¤Ó \fBomapi(3)\fR ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+¥¯¥é¥¤¥¢¥ó¥È¤òÍѤ¤¤Æ¤ä¤ê¤¿¤¤¤³¤È¤Î¤Û¤È¤ó¤É¤Ï¡¢ÆÃÊÌ¤Ê¥×¥í¥°¥é¥à¤ò
+½ñ¤«¤Ê¤¯¤È¤â \fBomshell(1)\fR ¥³¥Þ¥ó¥É¤ò»ÈÍѤ·¤ÆľÀܼ¸½¤Ç¤­¤ë
+¤â¤Î¤Ç¤¹¡£
+.SH À©¸æ¥ª¥Ö¥¸¥§¥¯¥È
+À©¸æ¥ª¥Ö¥¸¥§¥¯¥È¤ò»ÈÍѤ¹¤ë¤È¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò½ªÎ»¤µ¤»¡¢
+ÊÝ»ý¤·¤Æ¤¤¤ë¥ê¡¼¥¹¤ò¤¹¤Ù¤Æ³«Êü¤·¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Äɲä·¤¿
+DNS ¥ì¥³¡¼¥É¤ò¤¹¤Ù¤Æ¾Ãµî¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£
+¤Þ¤¿¡¢¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ·¤Æ¤¤¤ë
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎÀßÄê¤ò½ü¤¯¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤â¤Ê¤ê¤Þ¤¹¡£
+¤½¤Î¸å¤Ç¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤òºÆµ¯Æ°¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òºÆÀßÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£Ä̾¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤Ë
+Æþ¤ëÁ°¤ä¥é¥Ã¥×¥È¥Ã¥×¥³¥ó¥Ô¥å¡¼¥¿¤Ç¤Ï¥¹¥ê¡¼¥×¤¹¤ëÁ°¤Ë
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¤ë¤Ç¤·¤ç¤¦¡£
+¤½¤·¤Æ¡¢ÅŸ»¤¬Ìá¤Ã¤Æ¤­¤¿¸å¤Ç DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò²óÉü¤µ¤»¤ë
+¤Ç¤·¤ç¤¦¡£¤³¤¦¤¹¤ë¤³¤È¤Ç¡¢¥³¥ó¥Ô¥å¡¼¥¿¤¬¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤ä
+¥¹¥ê¡¼¥×Ãæ¤Ë¤Ï PC ¥«¡¼¥É¤òÄä»ß¤µ¤»¤Æ¤ª¤­¡¢¥³¥ó¥Ô¥å¡¼¥¿¤¬
+¥Ï¥¤¥Ð¥Í¡¼¥·¥ç¥ó¤ä¥¹¥ê¡¼¥×¤«¤éÉüµ¢¤·¤¿¤é°ÊÁ°¤Î¾õÂÖ¤Ë
+ºÆÅÙ½é´ü²½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¤è¤¦¤Ë¤Ê¤ë¤Î¤Ç¤¹¡£
+.PP
+À©¸æ¥ª¥Ö¥¸¥§¥¯¥È¤Ë¤Ï°À­¤¬ 1 ¤Ä¤¢¤ê¤Þ¤¹¡£¤½¤ì¤Ï¾õÂÖ°À­¤Ç¤¹¡£
+¥¯¥é¥¤¥¢¥ó¥È¤ò½ªÎ»¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖ°À­¤ò 2 ¤Ë
+ÀßÄꤷ¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Æ°Åª¤Ë DHCPRELEASE ¤ò¹Ô¤¦¤Ç¤·¤ç¤¦¡£
+¥¯¥é¥¤¥¢¥ó¥È¤ò°ì»þÄä»ß¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖ°À­¤ò
+3 ¤ËÀßÄꤷ¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤òÉüµ¢¤µ¤»¤ë¤Ë¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î
+¾õÂÖ°À­¤ò 4 ¤ËÀßÄꤷ¤Þ¤¹¡£
+.SH ´ØÏ¢¥Õ¥¡¥¤¥ë
+.B CLIENTBINDIR/dhclient-script,
+.B ETCDIR/dhclient.conf, DBDIR/dhclient.leases, RUNDIR/dhclient.pid,
+.B DBDIR/dhclient.leases~
+.SH ´ØÏ¢¹àÌÜ
+dhclient.conf(5), dhclient.leases(5), dhclient-script(8)
+.SH ºî¼Ô
+.B dhclient(8)
+¤Ï Ted Lemon ¤¬
+Vixie Enterprises ¤È¶¨ÎϤ·¤Æ Internet Systems Consortium ¤Î¤¿¤á¤Ë
+½ñ¤­¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢
+.B https://www.isc.org
+¤ò¤´Í÷¤¯¤À¤µ¤¤¡£
+Vixie Enterprises ¤Ë¤Ä¤¤¤Æ¤è¤ê¾Ü¤·¤¯¤Ï¡¢
+.B http://www.vix.com
+¤ò¤´Í÷¤¯¤À¤µ¤¤¡£
+.PP
+ËÜ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢Elliot Poger ¤¬
+Stanford Âç³Ø¤Î MosquitoNet ¥×¥í¥¸¥§¥¯¥È¤Ë»²²Ã¤·¤Æ¤¤¤ë´Ö¤Ë¡¢
+Linux ¤Ç¤ÎÍøÍѤ˺ݤ·ÂçÉý¤Ë½¤Àµ¡¢²þÎɤò¹Ô¤¤¤Þ¤·¤¿¡£
+.PP
+¸½ºß¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï¡¢Elliot ¤Ë¤è¤ë Linux ¤Ç¤Î²þÎɤËÉ餦¤È¤³¤í¤¬Â礭¤¤¤Ç¤¹¤¬¡¢
+Internet Systems Consortium ¤Î DHCP ¥µ¡¼¥Ð¤¬»È¤¦¤â¤Î¤ÈƱ¤¸
+¥Í¥Ã¥È¥ï¡¼¥­¥ó¥°¥Õ¥ì¡¼¥à¥ï¡¼¥¯¤òÍѤ¤¤ë¤è¤¦¤Ë¡¢Ted Lemon ¤¬
+ÂçÉý¤ÊºÆÊÔÀ®¤äÉôʬŪ¤Ê½ñ¤­´¹¤¨¤ò¹Ô¤¤¤Þ¤·¤¿¡£
+¥·¥¹¥Æ¥àÆÃÍ­¤ÎÀßÄꥳ¡¼¥É¤ÎÂçÉôʬ¤Ï¥·¥§¥ë¥¹¥¯¥ê¥×¥È¤Ë°Ü¤µ¤ì¤¿¤Î¤Ç¡¢
+¤è¤ê¿¤¯¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Î¥µ¥Ý¡¼¥È¤¬²Ã¤¨¤é¤ì¤ë¤Ë¤Ä¤ì¡¢
+¥·¥¹¥Æ¥àÆÃÍ­¤ÎÀßÄꥳ¡¼¥É¤ò¤½¤Î¥ª¥Ú¥ì¡¼¥Æ¥£¥ó¥°¥·¥¹¥Æ¥à¤Ë
+°Ü¿¢¤·¤¿¤ê´ÉÍý¤·¤¿¤ê¤¹¤ëɬÍפϤʤ¯¤Ê¤ë¤Ç¤·¤ç¤¦¡£
+Âå¤ï¤ê¤Ë¡¢¥·¥§¥ë¥¹¥¯¥ê¥×¥È¤¬´Ä¶­¤Ë¹ç¤Ã¤¿¥Ä¡¼¥ë¤ò¸Æ¤Ó½Ð¤·¤Æ
+¤½¤ÎÌÜŪ¤ò²Ì¤¿¤·¤Æ¤¯¤ì¤Þ¤¹¡£
+.PP
diff --git a/doc/ja_JP.eucJP/dhclient.conf.5 b/doc/ja_JP.eucJP/dhclient.conf.5
new file mode 100644
index 0000000..e69bba3
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhclient.conf.5
@@ -0,0 +1,625 @@
+.\" $Id: dhclient.conf.5,v 1.3.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.\"
+.\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.conf.5,v 1.7.2.1 2002/04/11 10:16:46 murray Exp %
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhclient.conf.5,v 1.6 2002/05/03 03:23:30 horikawa Exp $
+.\" WORD: lease ¥ê¡¼¥¹(¥¢¥É¥ì¥¹¤ÎÂßÍ¿)[dhclient.conf.5]
+.\" WORD: lease discovery request ¥ê¡¼¥¹È¯¸«Í×µá[dhclient.conf.5]
+.\" WORD: offer (¥ê¡¼¥¹Ä󶡤Î)¿½¤·½Ð¡¢Ä󶡿½¤·½Ð[dhclient.conf.5]
+.TH dhclient.conf 5
+.SH ̾¾Î
+dhclient.conf - DHCP ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë
+.SH ²òÀâ
+dhclient.conf ¥Õ¥¡¥¤¥ë¤Ë¤Ï
+Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤¢¤ë
+.IR dhclient
+¤ÎÀßÄê¾ðÊ󤬴ޤޤì¤Þ¤¹¡£
+.PP
+dhclient.conf ¤Ï¼«Í³·Á¼°¤Î ASCII ¥Æ¥­¥¹¥È¥Õ¥¡¥¤¥ë¤Ç¤¹¡£
+¤³¤Î¥Õ¥¡¥¤¥ë¤Ï dhclient ¤ËÁȤ߹þ¤Þ¤ì¤¿ºÆµ¢²¼¹ß¥Ñ¡¼¥¶¤Ë²òÀϤµ¤ì¤Þ¤¹¡£
+¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢À°·Á¤ÎÌÜŪ¤Ç¥¿¥Ö¤ä²þ¹Ô¤ò;ʬ¤Ë´Þ¤á¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+¥Õ¥¡¥¤¥ëÃæ¤Î¥­¡¼¥ï¡¼¥É¤Ç¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
+(¥¯¥©¡¼¥ÈÆâ¤Ï½ü¤¤¤Æ) ¥Õ¥¡¥¤¥ëÃæ¤Î¤É¤³¤Ç¤â¥³¥á¥ó¥È¤òÃÖ¤¯¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¥³¥á¥ó¥È¤Ïʸ»ú # ¤Ç»Ï¤Þ¤ê¡¢¹ÔËö¤Ç½ª¤ï¤ê¤Þ¤¹¡£
+.PP
+dhclient.conf ¥Õ¥¡¥¤¥ë¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¤µ¤Þ¤¶¤Þ¤ÊÆ°ºî¤òÀßÄê¤Ç¤­¤Þ¤¹¡£
+¤½¤ì¤é¤Ë¤Ï¡¢¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥°¡¢¥µ¡¼¥Ð¤ËÂФ·¤ÆÍ׵᤹¤ë¾ðÊó¡¢
+¥µ¡¼¥Ð¤ËÂФ·¤Æɬ¿Ü¤È¤µ¤ì¤ë¾ðÊó¡¢
+¥µ¡¼¥Ð¤¬¾ðÊó¤òÄ󶡤·¤Ê¤«¤Ã¤¿¾ì¹ç¤ËÍѤ¤¤ë¥Ç¥Õ¥©¥ë¥È¡¢
+¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿¾ðÊó¤ò¾å½ñ¤­¤¹¤ëÃÍ¡¢
+¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿¾ðÊó¤ËÁ°ÃÖ¤ä¸åÃÖ¤¹¤ëÃͤʤɤ¬¤¢¤ê¤Þ¤¹¡£
+¤Þ¤¿¡¢DHCP ¥µ¡¼¥Ð¤ò»ý¤¿¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ç»È¤¦¥¢¥É¥ì¥¹¤Ç¤¢¤Ã¤Æ¤â¡¢
+¤¢¤é¤«¤¸¤áÀßÄê¥Õ¥¡¥¤¥ë¤Ç½é´ü²½¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+.SH ¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥°
+¥¯¥é¥¤¥¢¥ó¥È¤Î¥¿¥¤¥ß¥ó¥°Æ°ºî¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ëɬÍפϤ¢¤ê¤Þ¤»¤ó¡£
+¥æ¡¼¥¶¤¬¥¿¥¤¥ß¥ó¥°ÀßÄê¤ò¹Ô¤ï¤Ê¤±¤ì¤Ð¡¢
+¥µ¡¼¥Ð¤Ë̵Ãá½ø¤ËÉé²Ù¤òÍ¿¤¨¤¿¤ê¤»¤ºÅ¬»þ¹¹¿·¤ò¹Ô¤¦¤è¤¦¤Ê¡¢
+½¼Ê¬¤ËŬÀڤʥ¿¥¤¥ß¥ó¥°Æ°ºî¤¬¥Ç¥Õ¥©¥ë¥È¤ÇÍѤ¤¤é¤ì¤Þ¤¹¡£
+.PP
+¤·¤«¤·¡¢É¬Íפ˱þ¤¸¤Æ¡¢
+¼¡¤Îʸ¤ò»ØÄꤷ¤Æ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥¿¥¤¥ß¥ó¥°Æ°ºî¤òÄ´Àá¤Ç¤­¤Þ¤¹:
+.PP
+.B timeout
+.I ʸ
+.PP
+.B timeout
+.I time
+.B ;
+.PP
+.I timeout
+ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò·è¤á¤ë»î¤ß¤ò³«»Ï¤·¤Æ¤«¤é¡¢
+¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤¹¤ë¤³¤È¤¬
+¤Ç¤­¤Ê¤¤¤ÈȽÃǤ¹¤ë¤Þ¤Ç¤Ë·Ð²á¤¹¤Ù¤­»þ´Ö¤ò·è¤á¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¤³¤Î¥¿¥¤¥à¥¢¥¦¥ÈÃÍ¤Ï 60 ÉäǤ¹¡£
+¤³¤Î¥¿¥¤¥à¥¢¥¦¥ÈÃͤ¬²á¤®¤¿¸å¤Ï¡¢
+¤â¤·ÀÅŪ¤Ê¥ê¡¼¥¹¤¬ÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤ë¤«¡¢
+¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤Þ¤À´ü¸ÂÀÚ¤ì¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤¬»Ä¤Ã¤Æ¤¤¤ì¤Ð¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤ì¤é¤Î¥ê¡¼¥¹¤ò¤Ò¤È¤Ä¤º¤Ä¸¡¾Ú¤·¤Æ¤ß¤Æ¡¢
+¤¤¤º¤ì¤«¤¬Í­¸ú¤Ê¤è¤¦¤Ç¤¢¤ì¤Ð¤½¤Î¥ê¡¼¥¹¤Î¥¢¥É¥ì¥¹¤ò»È¤¤¤Þ¤¹¡£
+¤â¤·ÀÅŪ¤Ê¥ê¡¼¥¹¤â¡¢¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î´ü¸Â¤ÎÀÚ¤ì¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤Ç
+Í­¸ú¤Ê¤â¤Î¤â¸ºß¤·¤Ê¤±¤ì¤Ð¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤ÏÄêµÁ¤µ¤ì¤¿ retry ´Ö³Ö¤Î¸å¤Ç¥×¥í¥È¥³¥ë¤òºÆ³«¤µ¤»¤Þ¤¹¡£
+.PP
+.B retry
+.I ʸ
+.PP
+ \fBretry \fItime\fR\fB;\fR
+.PP
+.I retry
+ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ DHCP ¥µ¡¼¥Ð¤¬Â¸ºß¤·¤Ê¤¤¤ÈȽÃǤ·¤Æ¤«¤é
+ºÆ¤Ó DHCP ¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤ò»î¤ß¤ë¤Þ¤Ç¤Î´Ö¤Ë¡¢·Ð²á¤¹¤ë¤Ù¤­»þ´Ö¤ò·è¤á¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢¤³¤ì¤Ï 5 ʬ¤Ç¤¹¡£
+.PP
+.B select-timeout
+.I ʸ
+.PP
+ \fBselect-timeout \fItime\fR\fB;\fR
+.PP
+¤¢¤ë¥Í¥Ã¥È¥ï¡¼¥¯¾å¤Ç¡¢Ê£¿ô¤Î DHCP ¥µ¡¼¥Ð¤¬¥µ¡¼¥Ó¥¹¤òÄ󶡤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹
+(¤½¤ÎÊý¤¬Ë¾¤Þ¤·¤¤¤È¤¤¤¦°Õ¸«¤â¤¢¤ê¤Þ¤¹)¡£
+¤½¤Î¾ì¹ç¡¢ºÇ½é¤Î¥ê¡¼¥¹È¯¸«¥á¥Ã¥»¡¼¥¸ (lease discovery message)
+¤Ø¤Î±þÅú¤È¤·¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬Ê£¿ô¤Î¥ê¡¼¥¹Ä󶡤、·½Ð¤ò¼õ¤±¤ë¤³¤È¤â¤¢¤êÆÀ¤Þ¤¹¡£
+¤½¤ì¤é¤Î¤¦¤Á¡¢¤¢¤ëÄ󶡤¬Â¾¤ÎÄ󶡤è¤ê¤â¹¥¤Þ¤·¤¤¤«¤â¤·¤ì¤Þ¤»¤ó
+(Î㤨¤Ð¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬°ÊÁ°»ÈÍѤ·¤Æ¤¤¤¿¥¢¥É¥ì¥¹¤¬¤¢¤ëÄ󶡤˴ޤޤì¤Æ¤¤¤ë¤¬¡¢
+¾¤ÎÄ󶡤ˤϴޤޤì¤Ê¤¤¤Ê¤É)¡£
+.PP
+.I select-timeout
+¤Ï¥¯¥é¥¤¥¢¥ó¥È¤¬ºÇ½é¤Î¥ê¡¼¥¹È¯¸«Í×µá
+¤òÁ÷¿®¤·¤Æ¡¢
+¾¯¤Ê¤¯¤È¤â 1 ¤Ä¤ÎÄ󶡿½¤·½Ð¤ò¼õ¤±¤¿¾ì¹ç¡¢
+¥µ¡¼¥Ð¤«¤é¤ÎÄ󶡿½¤·½ÐÂÔ¤Á¤ò¤ä¤á¤ë¤Þ¤Ç¤Î»þ´Ö¤Ç¤¹¡£
+¤â¤·
+.I select-timeout
+¤¬ÀÚ¤ì¤ë¤Þ¤Ç¤Ë¤É¤³¤«¤é¤âÄ󶡿½¤·½Ð¤ò¼õ¤±¼è¤ì¤Ê¤±¤ì¤Ð¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤Î¤¢¤ÈºÇ½é¤ËÅþÃ夹¤ëÄ󶡿½¤·½Ð¤ò¼õ¤±Æþ¤ì¤Þ¤¹¡£
+.PP
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢select-timeout ÃÍ¤Ï 0 ÉäǤ¹¡£
+¤Ä¤Þ¤ê¥¯¥é¥¤¥¢¥ó¥È¤ÏºÇ½é¤Ë¼õ¤±¼è¤ëÄ󶡿½¤·½Ð¤ò¼õ¤±Æþ¤ì¤Þ¤¹¡£
+.PP
+.B reboot
+.I ʸ
+.PP
+ \fBreboot \fItime\fR\fB;\fR
+.PP
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ºÆµ¯Æ°¤¹¤ë¤È¡¢
+ºÇ¸å¤ËÊÝ»ý¤·¤Æ¤¤¤¿¥¢¥É¥ì¥¹¤ò¤Þ¤º¼èÆÀ¤·Ä¾¤½¤¦¤È¤·¤Þ¤¹¡£
+¤³¤ì¤ò INIT-REBOOT (½é´ü¥ê¥Ö¡¼¥È) ¾õÂ֤ȸƤӤޤ¹¡£
+ºÇ¸å¤ËÆ°ºî¤·¤Æ¤¤¤¿¤È¤­¤ÈƱ¤¸¥Í¥Ã¥È¥ï¡¼¥¯¤Ë
+¥¯¥é¥¤¥¢¥ó¥È¤¬¤Þ¤ÀÀܳ¤·¤Æ¤¤¤ì¤Ð¡¢¤³¤ì¤¬ºÇ¤âÁÇÁᤤµ¯Æ°Ë¡¤È¤Ê¤ê¤Þ¤¹¡£
+.I reboot
+ʸ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ºÇ½é¤Ë¸Å¤¤¥¢¥É¥ì¥¹¤ÎºÆ¼èÆÀ¤ò»î¤ß¤Æ¤«¤é¡¢
+¤¢¤­¤é¤á¤Æ¿·¤·¤¤¥¢¥É¥ì¥¹¤òȯ¸«¤·¤è¤¦¤È¤¹¤ë¤Þ¤Ç¤Ë¡¢
+·Ð²á¤¹¤Ù¤­»þ´Ö¤òÀßÄꤷ¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¡¢reboot ¥¿¥¤¥à¥¢¥¦¥ÈÃÍ¤Ï 10 ÉäǤ¹¡£
+.PP
+.B backoff-cutoff
+.I ʸ
+.PP
+ \fBbackoff-cutoff \fItime\fR\fB;\fR
+.PP
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢»Ø¿ôŪ¤Ê°ì»þÂàÈò (backoff) ¥¢¥ë¥´¥ê¥º¥à¤ò¡¢¤¢¤ëÄøÅÙ¤Î
+Íð¿ôÉÕ¤­¤Ç»ÈÍѤ·¤Þ¤¹¡£¤³¤ì¤Ï¡¢Â¿¤¯¤Î¥¯¥é¥¤¥¢¥ó¥È¤¬Æ±»þ¤Ë¼«Ê¬¤òÀßÄꤷ¤è¤¦
+¤È¤·¤¿¤È¤­¤Ç¤â¡¢¥ê¥¯¥¨¥¹¥È¤¬¥í¥Ã¥¯¤·¤Æ¤·¤Þ¤¦¤³¤È¤¬¤Ê¤¤¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ç¤¹¡£
+.I backoff-cutoff
+ʸ¤Ï¡¢°ì»þÂàÈò¤Ëµö¤µ¤ì¤¿ºÇÂç»þ´Ö¤ò·èÄꤷ¤Þ¤¹¡£¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï 2 ʬ¤Ç¤¹¡£
+.PP
+.B initial-interval
+.I ʸ
+.PP
+ \fBinitial-interval \fItime\fR\fB;\fR
+.PP
+.I initial-interval
+ʸ¤Ï¡¢¥µ¡¼¥Ð¤Ø¤ÎºÇ½é¤Î¥¢¥¯¥»¥¹¤Î»î¤ß¤«¤é¼¡¤Î»î¤ß¤Þ¤Ç¤Î´Ö¤Î»þ´Ö¤ò
+ÀßÄꤷ¤Þ¤¹¡£¥á¥Ã¥»¡¼¥¸¤Î´Ö³Ö¤Ï¡¢¥á¥Ã¥»¡¼¥¸¤ò 1 ²óÁ÷¿®¤¹¤ë¤¿¤Ó¤Ë¡¢
+¸½ºß¤Î´Ö³Ö¤Ë 0 ¤«¤é 1 ¤Î´Ö¤ÎÍð¿ôÃͤò¾è¤¸¤¿¤â¤Î¤Î 2 Çܤò¡¢¸½ºß¤Î´Ö³Ö¤Ë
+²Ã¤¨¤¿¤â¤Î¤Ë¤Ê¤ê¤Þ¤¹¡£
+¤³¤ÎÃͤ¬ backoff-cutoff Ãͤè¤êÂ礭¤¯¤Ê¤ë¤È¡¢¤³¤Î»þ´Ö¤¬ÀßÄꤵ¤ì¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï 10 ÉäǤ¹¡£
+.SH ¥ê¡¼¥¹Í×µá¤È¥ê¥¯¥¨¥¹¥È
+DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤ËÂФ·¡¢ÆÃÄê¤Î¾ðÊó¤òÁ÷¤ë¤è¤¦
+Í׵ᤷ¤¿¤ê¡¢¼õ¤±Æþ¤ì½àÈ÷¤Î¤Ç¤­¤Æ¤¤¤Ê¤¤Â¾¤Î¾ðÊó¤ÏÁ÷¤é¤Ê¤¤¤è¤¦¤ËÍ׵ᤷ¤¿¤ê
+¤Ç¤­¤Þ¤¹¡£
+¤Þ¤¿¡¢¥µ¡¼¥Ð¤«¤é¤ÎÄ󶡿½¤·½Ð¤Ë¥¯¥é¥¤¥¢¥ó¥È¤ÎɬÍפȤ¹¤ë¾ðÊ󤬴ޤޤì¤Ê¤¤
+¾ì¹ç¤ä¡¢Ä󶡤µ¤ì¤¿¾ðÊ󤬽¼Ê¬¤Ç¤Ê¤¤¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Ä󶡿½¤·½Ð¤ò
+µñÈݤ¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+.PP
+DHCP ¥µ¡¼¥Ð¤¬ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷¤ëÄ󶡿½¤·½Ð¤Ë´Þ¤Þ¤ì¤ë¥Ç¡¼¥¿¤Ë¤Ï¡¢
+¤µ¤Þ¤¶¤Þ¤Ê¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£
+ÆäËÍ×µá¤Ç¤­¤ë¥Ç¡¼¥¿¤Ï \fIDHCP ¥ª¥×¥·¥ç¥ó\fR ¤È¸Æ¤Ð¤ì¤ë¤â¤Î¤Ç¤¹¡£
+DHCP ¥ª¥×¥·¥ç¥ó¤Ï
+ \fBdhcp-options(5)\fR
+¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+.PP
+.B request
+.I ʸ
+.PP
+ \fBrequest [ \fIoption\fR ] [\fB,\fI ... \fIoption\fR ]\fB;\fR
+.PP
+request ʸ¤ò»ØÄꤹ¤ë¤³¤È¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥µ¡¼¥Ð¤ËÂФ·¡¢¤½¤Î
+¥¯¥é¥¤¥¢¥ó¥È¤Ë±þÅú¤¹¤ë¤Ê¤é¤Ð¡¢»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ÎÃͤòÁ÷¤ë¤è¤¦
+Í׵᤹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£
+request ʸ¤Ë¤Ï¥ª¥×¥·¥ç¥ó̾¤À¤±¤ò»ØÄꤷ¡¢¥ª¥×¥·¥ç¥ó¥Ñ¥é¥á¡¼¥¿¤Ï»ØÄꤷ¤Þ¤»¤ó¡£
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï
+subnet-mask, broadcast-address, time-offset, routers,
+domain-name, domain-name-servers, host-name
+¥ª¥×¥·¥ç¥ó¤òÍ׵ᤷ¤Þ¤¹¡£
+.PP
+¾ì¹ç¤Ë¤è¤Ã¤Æ¤ÏÍ×µá¥ê¥¹¥È¤òÁ´¤¯Á÷¤é¤Ê¤¤¤³¤È¤¬Ë¾¤Þ¤·¤¤¤³¤È¤â¤¢¤ê¤Þ¤¹¡£
+¤½¤¦¤¹¤ë¤¿¤á¤Ë¤Ï¡¢Ã±½ã¤Ë¥Ñ¥é¥á¡¼¥¿¤ò»ØÄꤷ¤Ê¤¤ request ʸ¤ò½ñ¤¤¤Æ²¼¤µ¤¤:
+.PP
+.nf
+ request;
+.fi
+.PP
+.B require
+.I ʸ
+.PP
+ \fBrequire [ \fIoption\fR ] [\fB,\fI ... \fIoption ]\fB;\fR
+.PP
+require ʸ¤Ë¤Ï¡¢¤¢¤ëÄ󶡿½¤·½Ð¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬¼õ¤±Æþ¤ì¤ë¤¿¤á¤Ë
+¥µ¡¼¥Ð¤¬Á÷¤ë¤Ù¤­¥ª¥×¥·¥ç¥ó¤òÎóµó¤·¤Þ¤¹¡£
+Îóµó¤µ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¹¤Ù¤Æ¤ò´Þ¤Þ¤Ê¤¤Ä󶡿½¤·½Ð¤Ï̵»ë¤µ¤ì¤Þ¤¹¡£
+.PP
+.B send
+.I ʸ
+.PP
+ \fBsend { [ \fIoption declaration\fR ]
+[\fB,\fI ... \fIoption declaration\fR ]\fB}\fR
+.PP
+send ʸ¤ò»ØÄꤹ¤ë¤³¤È¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ò»ØÄꤷ¤¿Ãͤǥµ¡¼¥Ð¤ËÁ÷¿®¤¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£
+¤³¤³¤Ç»ØÄê¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ï¡¢
+\fBdhcp-options(5)\fR ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥ª¥×¥·¥ç¥óÀë¸À¤¹¤Ù¤Æ¤Ç¤¹¡£
+DHCP ¥×¥í¥È¥³¥ë¤Ç¾ï¤ËÁ÷¤é¤ì¤ë¥ª¥×¥·¥ç¥ó¤Ï
+¤³¤³¤Ë»ØÄꤹ¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£Ã¢¤·¡¢
+\fBrequested-lease-time\fR ¥ª¥×¥·¥ç¥ó¤ò¥Ç¥Õ¥©¥ë¥È¤Î¥ê¡¼¥¹»þ´Ö (2 »þ´Ö)
+°Ê³°¤ÎÃͤǻØÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤¹¡£¤³¤Îʸ¤ò»È¤¦Â¾¤Î¾ì¹ç¤È¤·¤ÆÌÀ¤é¤«¤Ê
+¤â¤Î¤Ï¡¢¼«Ê¬¤ÈÊ̤μïÎà¤Î¥¯¥é¥¤¥¢¥ó¥È¤È¤ò¶èÊ̤Ǥ­¤ë¤è¤¦¤Ê
+¾ðÊó¤ò¡¢¥µ¡¼¥Ð¤ËÂФ·Á÷¿®¤¹¤ë¾ì¹ç¤Ç¤¹¡£
+.SH ưŪ DNS
+¸½ºß¡¢¥ê¡¼¥¹¤¬³ÍÆÀ¤µ¤ì¤¿ºÝ¤Ë DNS ¤Î¹¹¿·¤ò¹Ô¤¦¤¿¤á¤Î¡¢
+Èó¾ï¤Ë¸ÂÄêŪ¤Ê¥µ¥Ý¡¼¥È¤¬¥¯¥é¥¤¥¢¥ó¥È¤Ë¤¢¤ê¤Þ¤¹¡£
+¤³¤ì¤Ï¥×¥í¥È¥¿¥¤¥×Ū¤Ê¤â¤Î¤Ç¤¢¤ê¡¢
+¤ª¤½¤é¤¯¤¢¤Ê¤¿¤¬»×¤Ã¤Æ¤¤¤ë¤è¤¦¤Ë¤ÏÆ°¤­¤Þ¤»¤ó¡£
+¤â¤·¡¢¤¢¤Ê¤¿¤¬¶öÁ³¤Ë¤â¼«Ê¬¤Î¤È¤³¤í¤Î DNS ¥µ¡¼¥Ð¤Î´ÉÍý¼Ô¤Ç¤¢¤ë¤È¤¤¤¦¤Ê¤é¡¢
+¤½¤Î¾ì¹ç¤Ë¸Â¤Ã¤Æ¤ÏÆ°¤­¤Þ¤¹¡£¤È¤Æ¤â¤¢¤ê¤½¤¦¤Ë¤Ê¤¤¤³¤È¤Ç¤¹¤¬¡£
+.PP
+¤³¤ì¤òÆ°ºî¤µ¤»¤ë¤¿¤á¤Ë¤Ï¡¢DHCP ¥µ¡¼¥Ð¤ÎÃæ¤Ç
+¸°¤È¥¾¡¼¥ó¤òÀë¸À¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹ (¾ÜºÙ¤Ï \fBdhcpd.conf\fR(5) ¤ò»²¾È)¡£
+¤Þ¤¿¡¢¼¡¤Î¤è¤¦¤Ë¥¯¥é¥¤¥¢¥ó¥È¤Ç fqdn ¥ª¥×¥·¥ç¥ó¤òÀßÄꤹ¤ëɬÍפ¬¤¢¤ê¤Þ¤¹:
+.PP
+.nf
+ send fqdn.fqdn "grosse.fugue.com.";
+ send fqdn.encoded on;
+ send fqdn.server-update off;
+.fi
+.PP
+\fIfqdn.fqdn\fR ¥ª¥×¥·¥ç¥ó¤Ï \fBɬ¤º\fR ´°Á´¤Ê¥É¥á¥¤¥ó̾¤Ç¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+¹¹¿·¤¹¤ë¥¾¡¼¥ó¤ËÂФ¹¤ë¥¾¡¼¥óʸ¤ò \fBɬ¤º\fR ÄêµÁ¤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+\fIfqdn.encoded\fR ¥ª¥×¥·¥ç¥ó¤Ï¡¢»ÈÍѤ·¤Æ¤¤¤ë DHCP ¥µ¡¼¥Ð¤Ë¤è¤Ã¤Æ¤Ï¡¢
+\fIon\fR ¤« \fIoff\fR ¤ËÀßÄꤹ¤ëɬÍפ¬¤¢¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£
+.PP
+.B no-client-updates
+.I ʸ
+.PP
+ \fBno-client-updates [ \fIflag\fR ] \fB;\fR
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜ DNS ¤Î¹¹¿·¤ò¹Ô¤¦¤è¤ê¤â¡¢
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È (\fBdhclient-script(8)\fR »²¾È) ¤ÎÃæ¤Ç
+DNS ¤Î¹¹¿·¤ò¹Ô¤¤¤¿¤¤¾ì¹ç
+(Î㤨¤Ð¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜ¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Ê¤¤
+SIG(0) ǧ¾Ú¤ò»ÈÍѤ·¤¿¤¤¾ì¹ç)
+¤Ë¤Ï¡¢\fBno-client-updates\fR ʸ¤ò»È¤Ã¤Æ¡¢¹¹¿·¤ò¹Ô¤ï¤Ê¤¤¤è¤¦¤Ë
+¥¯¥é¥¤¥¢¥ó¥È¤Ë¶µ¤¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¹¹¿·¤¹¤ë¤³¤È¤ò˾¤Þ¤Ê¤¤¾ì¹ç¤Ï \fIflag\fR ¤ò \fBtrue\fR ¤Ë¤·¡¢
+¹¹¿·¤¹¤ë¤³¤È¤ò˾¤à¾ì¹ç¤Ï \fIflag\fR ¤ò \fBfalse\fR ¤Ë¤¹¤ë¤³¤È¤Ë¤Ê¤ê¤Þ¤¹¡£
+¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï DNS ¤Î¹¹¿·¤ò¹Ô¤¤¤Þ¤¹¡£
+.PP
+.SH ¥ª¥×¥·¥ç¥ó½¤¾þ»Ò
+¤½¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ë¤È¤Ã¤Æ¼ÂºÝ¤Ë¤ÏŬÀڤǤʤ¤
+¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤ò¼õ¤±¼è¤Ã¤¿¤ê¡¢É¬ÍפʾðÊó¤ò¼õ¤±¼è¤é¤Ê¤«¤Ã¤¿¤ê
+¤¹¤ë¾ì¹ç¤Ç¡¢¤«¤Ä¡¢¤½¤ì¤é¤Î¾ðÊó¤ËÍøÍѲÄǽ¤Ê¥Ç¥Õ¥©¥ë¥È¤ÎÃͤ¬
+¥¯¥é¥¤¥¢¥ó¥È¦¤Ë¸ºß¤¹¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£
+¤Þ¤¿¡¢ÍøÍѲÄǽ¤Ç¤Ï¤¢¤ë¤¬¥í¡¼¥«¥ë¤Î¾ðÊó¤ÇÊ䤦ɬÍפΤ¢¤ë¾ðÊó¤ò
+¥¯¥é¥¤¥¢¥ó¥È¤¬¼õ¤±¤È¤ë¾ì¹ç¤â¤¢¤ê¤Þ¤¹¡£
+¤³¤¦¤¤¤¦¾ì¹ç¤ò°·¤¦¤¿¤á¤Ë¡¢
+¤¤¤¯¤Ä¤«¤Î¥ª¥×¥·¥ç¥ó½¤¾þ»Ò¤¬ÍøÍѤǤ­¤Þ¤¹¡£
+.PP
+.B default
+.I ʸ
+.PP
+ \fBdefault [ \fIoption declaration\fR ] \fB;\fR
+.PP
+¤¢¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Ä¤¤¤Æ¡¢
+¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ëÃͤò¥¯¥é¥¤¥¢¥ó¥È¤¬»È¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¤¬¡¢
+¤â¤·¥µ¡¼¥Ð¤«¤éÃͤ¬Ä󶡤µ¤ì¤Ê¤±¤ì¤Ð
+²¿¤é¤«¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤò»È¤¦É¬Íפ¬¤¢¤ë¾ì¹ç¡¢
+¤½¤ì¤é¤ÎÃͤò
+.B default
+ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.PP
+.B supersede
+.I ʸ
+.PP
+ \fBsupersede [ \fIoption declaration\fR ] \fB;\fR
+.PP
+¤¢¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Ä¤¤¤Æ¡¢
+¤É¤Î¤è¤¦¤ÊÃͤ¬¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤Æ¤â¡¢
+¾ï¤Ë¥í¡¼¥«¥ë¤ÇÀßÄꤵ¤ì¤¿Ãͤò»È¤ï¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¾ì¹ç¡¢
+¤½¤ì¤é¤ÎÃͤò
+.B supersede
+ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.PP
+.B prepend
+.I ʸ
+.PP
+ \fBprepend [ \fIoption declaration\fR ] \fB;\fR
+.PP
+¤¢¤ë¥ª¥×¥·¥ç¥ó¤Î½¸¹ç¤Ë¤Ä¤¤¤Æ¡¢¤Þ¤º¥æ¡¼¥¶¤¬Ä󶡤¹¤ëÃͤò»È¤¤¡¢
+¤½¤Î¼¡¤Ë¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿Ãͤ¬¤¢¤ì¤Ð¤½¤ì¤ò»È¤¦¾ì¹ç¡¢
+¤½¤ì¤é¤ÎÃͤò
+.B prepend
+ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.B prepend
+ʸ¤ÏÊ£¿ô¤ÎÃͤò¼è¤ë¤³¤È¤Î¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Î¤ßÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤³¤ÎÀ©Ìó¤Ï¶¯À©¤µ¤ì¤ë¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢
+¤³¤ì¤ò̵»ë¤·¤¿¾ì¹ç¡¢¤É¤Î¤è¤¦¤ÊµóÆ°¤Ë¤Ê¤ë¤«¤ÏͽÁۤǤ­¤Þ¤»¤ó¡£
+.PP
+.B append
+.I ʸ
+.PP
+ \fBappend [ \fIoption declaration\fR ] \fB;\fR
+.PP
+¤¢¤ë¥ª¥×¥·¥ç¥ó¤Î½¸¹ç¤Ë¤Ä¤¤¤Æ¡¢¤Þ¤º¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤¿Ãͤò»È¤¤¡¢
+¤½¤Î¼¡¤Ë¥æ¡¼¥¶¤¬Ä󶡤¹¤ëÃͤ¬¤¢¤ì¤Ð¤½¤ì¤â»È¤¦¾ì¹ç¡¢
+¤½¤ì¤é¤ÎÃͤò
+.B append
+ʸ¤ÇÄêµÁ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.B append
+ʸ¤ÏÊ£¿ô¤ÎÃͤò¼è¤ë¤³¤È¤Î¤Ç¤­¤ë¥ª¥×¥·¥ç¥ó¤Ë¤Î¤ßÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤³¤ÎÀ©Ìó¤Ï¶¯À©¤µ¤ì¤ë¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢
+¤â¤·°ãÈ¿¤¹¤ë¤Èͽ´ü¤Ç¤­¤Ê¤¤·ë²Ì¤È¤Ê¤ê¤Þ¤¹¡£
+.SH ¥ê¡¼¥¹Àë¸À
+.PP
+.B lease
+.I Àë¸À
+.PP
+ \fBlease {\fR \fIlease-declaration\fR [ ... \fIlease-declaration ] \fB}\fR
+.PP
+¤¢¤ë»þ´Ö (\fB¥×¥í¥È¥³¥ë¤Î¥¿¥¤¥ß¥ó¥°\fR »²¾È) ¤Î¸å¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï
+¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤ËÀ®¸ù¤·¤½¤¦¤Ë¤Ê¤¤¤ÈȽÃǤ¹¤ë¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£
+¤½¤Î»þÅÀ¤Ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Ê¬¤¬»ý¤Ã¤Æ¤¤¤ë¡¢¸Å¤¤¥ê¡¼¥¹¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò
+¸«¤Æ¡¢»þ´ÖÀÚ¤ì¤Ë¤Ê¤Ã¤Æ¤¤¤Ê¤¤¥ê¡¼¥¹¤ò½ç¤ËÄ´¤Ù¡¢¤½¤³¤Ëµó¤¬¤Ã¤Æ¤¤¤ë
+¥ë¡¼¥¿¤Ë ping ¤ò¹Ô¤Ã¤Æ¡¢¤½¤ì¤¬ÍøÍѲÄǽ¤Ê¥ê¡¼¥¹¤«¤É¤¦¤«¤òÄ´¤Ù¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ó¥¹¤ä BOOTP ¥µ¡¼¥Ó¥¹¤¬Â¸ºß¤·¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Î¤¿¤á¤Ë¡¢
+1 ¤Ä°Ê¾å¤Î \fI¸ÇÄê\fR ¥ê¡¼¥¹¤ò¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤·¤Æ¤ª¤¤¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼«Æ°Åª¤ËÀßÄê¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+¤³¤ì¤Ï
+.B lease
+ʸ¤Ç¹Ô¤¤¤Þ¤¹¡£
+.PP
+Ãí°Õ: lease ʸ¤Ï¡¢DHCP ¥µ¡¼¥Ð¤«¤é¼õ¤±¼è¤Ã¤¿¥ê¡¼¥¹¤òµ­Ï¿¤¹¤ë¤¿¤á¤Ë¡¢
+dhclient.leases ¥Õ¥¡¥¤¥ë¤Ç¤â»È¤ï¤ì¤Þ¤¹¡£
+°Ê²¼¤ËÀâÌÀ¤¹¤ë¥ê¡¼¥¹ÍѤΥ·¥ó¥¿¥Ã¥¯¥¹¤Ë¤Ï
+dhclient.leases ¥Õ¥¡¥¤¥ë¤Ç¤Î¤ßɬÍפʤâ¤Î¤â¤¢¤ê¤Þ¤¹¡£
+ÀâÌÀ¤ò´°Á´¤Ê¤â¤Î¤Ë¤¹¤ë¤¿¤á¡¢¤½¤Î¤è¤¦¤Ê¥·¥ó¥¿¥Ã¥¯¥¹¤â¤³¤³¤Çµ­½Ò¤·¤Þ¤¹¡£
+.PP
+lease ʸ¤Ï¡¢¥ê¡¼¥¹¥­¡¼¥ï¡¼¥É¡¢º¸Ãæ³ç¸Ì¡¢1 ¤Ä°Ê¾å¤Î¥ê¡¼¥¹Àë¸Àʸ¡¢
+±¦Ãæ³ç¸Ì¤¬Â³¤¤¤¿¤â¤Î¤Ç¹½À®¤µ¤ì¤Þ¤¹¡£
+¥ê¡¼¥¹Àë¸À¤È¤·¤Æ¡¢¼¡¤Î¤â¤Î¤¬²Äǽ¤Ç¤¹:
+.PP
+ \fBbootp;\fR
+.PP
+.B bootp
+ʸ¤Ï¡¢¥ê¡¼¥¹¤¬ DHCP ¥×¥í¥È¥³¥ë¤Ç¤Ï¤Ê¤¯¡¢
+BOOTP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤Æ¼èÆÀ¤µ¤ì¤¿¤³¤È¤ò¼¨¤·¤Þ¤¹¡£
+¤³¤Îʸ¤ò¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤Ë»ØÄꤹ¤ëɬÍפÏÁ´¤¯¤¢¤ê¤Þ¤»¤ó¡£
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¹½Ê¸¤ò¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¥Õ¥¡¥¤¥ëÆâ¤Ç»È¤¤¤Þ¤¹¡£
+.PP
+ \fBinterface\fR \fB"\fR\fIstring\fR\fB";\fR
+.PP
+.B interface
+¥ê¡¼¥¹Ê¸¤Ï¡¢¤½¤Î¥ê¡¼¥¹¤òÍ­¸ú¤È¤¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¼¨¤·¤Þ¤¹¡£
+¤³¤ì¤¬ÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î¥ê¡¼¥¹¤Ï¡¢»ØÄꤵ¤ì¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹
+¾å¤Ç¤Î¤ß»ÈÍѤµ¤ì¤Þ¤¹¡£
+¥µ¡¼¥Ð¤«¤é¥ê¡¼¥¹¤ò¼õ¤±¼è¤Ã¤¿¤È¤­¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¾ï¤Ë¤½¤Î¥ê¡¼¥¹¤ò¼õ¤±¼è¤Ã¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹ÈÖ¹æ¤òµ­Ï¿¤·¤Þ¤¹¡£
+dhclient.conf ¥Õ¥¡¥¤¥ë¤Ç»öÁ°¤Ë¥ê¡¼¥¹¤òÄêµÁ¤·¤Æ¤¤¤ë¾ì¹ç¡¢Í׵ᤵ¤ì¤Æ¤Ê¤¤
+¤Î¤Ç¤¹¤¬¡¢¤½¤Î¥ê¡¼¥¹¤Ç¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤â¤¢¤ï¤»¤Æ»ØÄꤷ¤Ê¤±¤ì¤Ð
+¤Ê¤ê¤Þ¤»¤ó¡£
+.PP
+ \fBfixed-address\fR \fIip-address\fR\fB;\fR
+.PP
+.B fixed-address
+ʸ¤ÏÆÃÄê¤Î¥ê¡¼¥¹¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤹ¤ëºÝ¤Ë»È¤¤¤Þ¤¹¡£
+¤³¤ì¤Ï¤¹¤Ù¤Æ¤Î lease ʸ¤ËɬÍפǤ¹¡£
+IP ¥¢¥É¥ì¥¹¤Ï (12.34.56.78 ¤Î¤è¤¦¤Ë) ¥É¥Ã¥ÈÉÕ¤­ 4 ¤ÄÁÈ·Á¼°¤Ç
+»ØÄꤷ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+.PP
+ \fBfilename "\fR\fIstring\fR\fB";\fR
+.PP
+.B filename
+ʸ¤Ï»ÈÍѤ¹¤ë¥Ö¡¼¥È¥Õ¥¡¥¤¥ë̾¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤ì¤Ïɸ½àŪ¤Ê¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï»È¤ï¤ì¤Þ¤»¤ó¤¬¡¢
+ÀâÌÀ¤Î´°Á´¤ò´ü¤¹¤¿¤á¤Ë¤³¤³¤Ë´Þ¤á¤Æ¤¢¤ê¤Þ¤¹¡£
+.PP
+ \fBserver-name "\fR\fIstring\fR\fB";\fR
+.PP
+.B server-name
+ʸ¤Ï»ÈÍѤ¹¤ë¥Ö¡¼¥È¥µ¡¼¥Ð̾¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤ì¤âɸ½àŪ¤Ê¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï»È¤ï¤ì¤Þ¤»¤ó¡£
+.PP
+ \fBoption\fR \fIoption-declaration\fR\fB;\fR
+.PP
+.B option
+ʸ¤Ï¡¢¥µ¡¼¥Ð¤«¤éÄ󶡤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¤ÎÃͤò»ØÄꤹ¤ë¤Î¤Ë»È¤¤¤Þ¤¹¡£
+¤¢¤ë¤¤¤Ï¡¢dhclient.conf ¤Ç»öÁ°ÄêµÁ¥ê¡¼¥¹¤¬Àë¸À¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï¡¢
+¤½¤Î»öÁ°ÄêµÁ¥ê¡¼¥¹¤¬»È¤ï¤ì¤ëºÝ¤Ë¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç»ÈÍѤ·¤Æ
+Íߤ·¤¤Ãͤò»ØÄꤷ¤Þ¤¹¡£
+.PP
+ \fBscript "\fIscript-name\fB";\fR
+.PP
+.B script
+ʸ¤Ï dhcp ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Î¥Ñ¥¹Ì¾¤ò»ØÄꤹ¤ë¤Î¤Ë»È¤¤¤Þ¤¹¡£
+¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¢¥¢¥É¥ì¥¹¤òÍ׵ᤷ¤¿¤ê¡¢°ÊÁ°¤ËÄ󶡤µ¤ì¤¿¥¢¥É¥ì¥¹¤ò
+»î¤·¤¿¤ê¡¢
+¥ê¡¼¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎºÇ½ªÀßÄê¤ò¹Ô¤Ã¤¿¤ê¤¹¤ëÁ°¤Ë¡¢
+dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î½é´üÀßÄê¤ò¹Ô¤¦¤Î¤Ë»È¤¤¤Þ¤¹¡£
+¥ê¡¼¥¹¤¬¼èÆÀ¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢
+»öÁ°ÄêµÁ¥ê¡¼¥¹¤¬Â¸ºß¤¹¤ë¾ì¹ç¡¢¤½¤ì¤é¤ò»î¤¹¤¿¤á¤Ë¤³¤Î¥¹¥¯¥ê¥×¥È¤¬»È¤ï¤ì¤Þ¤¹¡£
+¤Þ¤¿¡¢Í­¸ú¤Ê¥ê¡¼¥¹¤¬¤Ò¤È¤Ä¤âÆÀ¤é¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¤Ç¤â¡¢¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¢
+1 ²ó¤Ï¸Æ¤Ó½Ð¤µ¤ì¤Þ¤¹¡£
+¤è¤ê¾Ü¤·¤¯¤Ï¡¢
+.B dhclient-script(8)
+¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+ \fBvendor option space "\fIname\fB";\fR
+.PP
+.B vendor option space
+ʸ¤Ï¡¢vendor-encapsulate-options ¥ª¥×¥·¥ç¥ó¤ò¼õ¿®¤·¤¿¾ì¹ç¡¢
+Éü¹æ²½¤Ë¤É¤Î¥ª¥×¥·¥ç¥ó¶õ´Ö¤ò»ÈÍѤ¹¤ë¤Ù¤­¤«¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+¥µ¡¼¥Ð¤«¤é¤Î¥Ù¥ó¥À¥ª¥×¥·¥ç¥ó¤ÎÆÃÄê¤Î¥¯¥é¥¹¤òÍ׵᤹¤ë¤¿¤á¤Ë¡¢
+\fIdhcp-vendor-identifier\fR ¤ò»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¾ÜºÙ¤Ï
+.B dhcp-options(5)
+¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+ \fBmedium "\fImedia setup\fB";\fR
+.PP
+.B medium
+ʸ¤Ï¡¢Àܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥¿¥¤¥×¤ò¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬
+¼«Æ°Åª¤ËȽÃǤǤ­¤Ê¤¤¤è¤¦¤Ê¥·¥¹¥Æ¥à¤Ç»È¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+ʸ»úÎó media setup ¤Ï¥·¥¹¥Æ¥à°Í¸¤Î¥Ñ¥é¥á¡¼¥¿¤Ç¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹½é´ü²½¤ÎºÝ¤Ë dhcp ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤ËÅϤµ¤ì¤Þ¤¹¡£
+Unix ¤ª¤è¤Ó Unix É÷¤Î¥·¥¹¥Æ¥à¤Ç¤Ï¡¢
+¤³¤Î°ú¿ô¤Ï¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë¤È¤­¤Ë ifconfig ¥³¥Þ¥ó¥É¥é¥¤¥ó¤Ë
+ÅϤµ¤ì¤Þ¤¹¡£
+.PP
+¥ê¡¼¥¹¤òÆÀ¤ë¤¿¤á¤Ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀßÄꤹ¤ë
+ºÝ¤Ë¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬¥á¥Ç¥£¥¢¥¿¥¤¥× (
+.B media
+ʸ¤ò»²¾È) ¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¤³¤Î¥Ñ¥é¥á¡¼¥¿¤ò
+¼«Æ°Åª¤ËÀë¸À¤·¤Þ¤¹¡£¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¥á¥Ç¥£¥¢¥¿¥¤¥×¤Î
+ÀßÄê¤òɬÍפȤ¹¤ë¾ì¹ç¤Ï (¤¹¤ë¾ì¹ç¤Ë¸Â¤ê)¡¢¤³¤Îʸ¤ò»öÁ°ÄêµÁ¥ê¡¼¥¹¤Ç
+»ÈÍѤ·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+.PP
+ \fBrenew\fR \fIdate\fB;\fR
+.PP
+ \fBrebind\fR \fIdate\fB;\fR
+.PP
+ \fBexpire\fR \fIdate\fB;\fR
+.PP
+\fBrenew\fR ʸ¤Ï¡¢¸½ºß»ÈÍÑÃæ¤Î¥ê¡¼¥¹¤ò¹¹¿· (renew) ¤¹¤ë¤¿¤á¤Ë¡¢
+dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍÑÃæ¤Î¥ê¡¼¥¹¤òÄ󶡤·¤Æ¤¯¤ì¤¿¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤Î
+»î¤ß¤ò³«»Ï¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤òÄêµÁ¤·¤Þ¤¹¡£\fBrebind\fR ʸ¤Ï¡¢
+¥ê¡¼¥¹¤ò¹¹¿·¤¹¤ë¤¿¤á¤Ë¡¢dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬ \fI¤¤¤º¤ì¤«¤Î\fR dhcp
+¥µ¡¼¥Ð¤Ø¤Î¥¢¥¯¥»¥¹¤Î»î¤ß¤ò³«»Ï¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤òÄêµÁ¤·¤Þ¤¹¡£
+\fBexpire\fR ʸ¤Ï¡¢¥ê¡¼¥¹¤Î¹¹¿·¤Î¤¿¤á¤Ë¥µ¡¼¥Ð¤Ë¥¢¥¯¥»¥¹¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¡¢
+dhcp ¥¯¥é¥¤¥¢¥ó¥È¤¬¤½¤Î¥ê¡¼¥¹¤Î»ÈÍѤòÄä»ß¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤Æü»þ¤ò
+ÄêµÁ¤·¤Þ¤¹¡£
+.PP
+¤³¤ì¤é¤ÎÀë¸À¤Ï¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬ÆÀ¤¿¥ê¡¼¥¹Ãæ¤Ç¤Ï¼«Æ°Åª¤ËÀßÄꤵ¤ì¤Þ¤¹¡£
+»öÁ°ÄêµÁ¥ê¡¼¥¹¤Î¤¦¤Á¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ËÍ­¸ú´ü¸Â¤¬²á¤®¤¿¤â¤Î¤ò»ÈÍѤ·¤Æ
+Íߤ·¤¯¤Ê¤¤¤â¤Î¤ÎÃæ¤Ç¤Ï¡¢¤³¤ì¤é¤ÎÀë¸À¤òÀßÄꤷ¤Æ¤ª¤¯É¬Íפ¬¤¢¤ê¤Þ¤¹¡£
+.PP
+date ¤Ï°Ê²¼¤Î¤è¤¦¤Ë»ØÄꤷ¤Þ¤¹¡£
+.PP
+ \fI<weekday> <year>\fB/\fI<month>\fB/\fI<day>
+<hour>\fB:\fI<minute>\fB:\fI<second>\fR
+.PP
+weekday ¤Ï¡¢¿Í´Ö¤¬¸«¤Æ¥ê¡¼¥¹´ü¸Â¤ò¤ï¤«¤ê¤ä¤¹¤¯¤¹¤ë¤¿¤á¤Ë¸ºß¤·¤Þ¤¹¡£
+¤³¤ì¤Ï¡¢0 ¤«¤é 6 ¤Þ¤Ç¤Î¿ô»ú¤Ç»ØÄꤷ¤Þ¤¹¡£0 ¤ÏÆüÍËÆü¤Ç¤¹¡£year ¤ÏÀ¤µª
+¹þ¤ß¤Ç»ØÄꤷ¤Þ¤¹¡£¤Ç¤¹¤«¤é¡¢ËÜÅö¤ËŤ¤¥ê¡¼¥¹¤òÊ̤ˤ¹¤ë¤È¡¢É¬¤º 4 ·å¤Ë
+¤Ê¤ë¤Ï¤º¤Ç¤¹¡£month ¤Ï 1 (1 ·î¤òɽ¤·¤Þ¤¹) ¤«¤é»Ï¤Þ¤ë¿ô»ú¤Ç»ØÄꤷ¤Þ¤¹¡£
+day ¤ÏƱÍÍ¤Ë 1 ¤«¤é»Ï¤Þ¤ë (·î¤Ë¤ª¤±¤ë) Æü¤È¤·¤Æ»ØÄꤷ¤Þ¤¹¡£hour ¤Ï¡¢
+0 ¤«¤é 23 ¤Î´Ö¤Î¿ô»ú¤Ç¤¹¡£minute ¤È second ¤Ï¤È¤â¤Ë 0 ¤«¤é 59 ¤Î´Ö¤Î
+¿ô»ú¤ò»ØÄꤷ¤Þ¤¹¡£
+.SH ¥¨¥¤¥ê¥¢¥¹Àë¸À
+ \fBalias { \fI declarations ... \fB}\fR
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬ TCP/IP ¥í¡¼¥ß¥ó¥° (roaming) ¥×¥í¥È¥³¥ë¤ò¼Â¹Ô¤·¤Æ
+¤¤¤ë¾ì¹ç¡¢DHCP ¤òÍѤ¤¤ÆÆÀ¤é¤ì¤ë¥ê¡¼¥¹¤À¤±¤Ç¤Ê¤¯¡¢»öÁ°¤ËÄêµÁ¤µ¤ì¤¿
+IP ¥¨¥¤¥ê¥¢¥¹¤â¡¢¼«Ê¬¤¬»ÈÍѤ¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÀßÄꤹ¤ëɬÍפ¬¤¢¤ë
+¾ì¹ç¤¬¤¢¤ê¤Þ¤¹¡£Internet Systems Consortium ÈÇ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+¸ÇÄꥢ¥É¥ì¥¹Ä¾ÀÜ»ØÄê¤Î¥í¡¼¥ß¥ó¥°¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤»¤ó¤¬¡¢¤½¤Î¼ï¤Î¼Â¸³
+¤¬¤Ç¤­¤ë¤è¤¦¤Ë¡¢¤³¤Î dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+.B alias
+Àë¸À¤ò»È¤Ã¤Æ IP ¥¨¥¤¥ê¥¢¥¹¤òÀßÄꤹ¤ë½àÈ÷¤Ï¤Ç¤­¤Æ¤¤¤Þ¤¹¡£
+.PP
+alias Àë¸À¤Ï lease Àë¸À¤Ë»÷¤Æ¤¤¤Þ¤¹¡£Ã¢¤·¡¢É¸½à¤Î
+¥¯¥é¥¤¥¢¥ó¥ÈÀßÄꥹ¥¯¥ê¥×¥È¤Ç¤Ï¡¢subnet-mask ¥ª¥×¥·¥ç¥ó°Ê³°¤Î
+¥ª¥×¥·¥ç¥ó¤È¡¢³Æ¼ïÍ­¸ú´ü¸Â (expiry times) ¤¬Ìµ»ë¤µ¤ì¤ëÅÀ¤¬°Û¤Ê¤ê¤Þ¤¹¡£
+ÉáÄ̤Πalias Àë¸À¤Ç¤Ï¡¢ interface Àë¸À¡¢IP ¥¨¥¤¥ê¥¢¥¹¤Î¤¿¤á¤Î
+¸ÇÄꥢ¥É¥ì¥¹Àë¸À¡¢subnet-mask ¥ª¥×¥·¥ç¥ó¤ò´Þ¤ß¤Þ¤¹¡£alias Àë¸À¤Ë¤Ï
+medium ʸ¤Ï·è¤·¤Æ´Þ¤Þ¤ì¤Æ¤Ï¤Ê¤ê¤Þ¤»¤ó¡£
+.SH ¤½¤Î¾¤ÎÀë¸À
+ \fBreject \fIip-address\fB;\fR
+.PP
+.B reject
+ʸ¤Ë¤è¤ê¡¢DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï»ØÄꤷ¤¿¥¢¥É¥ì¥¹¤ò¥µ¡¼¥Ð¼±Ê̻ҤȤ·¤Æ»ÈÍѤ¹¤ë
+¥µ¡¼¥Ð¤«¤é¤ÎÄ󶡿½¤·½Ð¤òµñÈݤ¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£É¸½à¤Ë½àµò¤·¤Ê¤¤ dhcp
+¥µ¡¼¥Ð¤äÀßÄê¤ò´Ö°ã¤¨¤Æ¤¤¤ë dhcp ¥µ¡¼¥Ð¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¤¬ÀßÄꤵ¤ì¤Ê¤¤
+¤è¤¦¤Ë¤¹¤ë¤¿¤á¤Ë¡¢¤³¤Îʸ¤ò»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤·¤«¤·¤Ê¤¬¤é¡¢¤³¤ì¤Ï
+ºÇ¸å¤ÎÉð´ï¤È¤¹¤ë¤Ù¤­¤Ç¤¹¡£¤³¤ì¤ËÀèΩ¤Á¡¢Éå¤Ã¤¿ DHCP ¥µ¡¼¥Ð¤òÄɤ¤¤«¤±¤Æ
+¤½¤ì¤òľ¤¹Êý¤¬¤è¤¤¤Ç¤¹¡£
+.PP
+ \fBinterface "\fIname\fB" { \fIdeclarations ... \fB }
+.PP
+Ê£¿ô¤Î¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò»ý¤Ä¥¯¥é¥¤¥¢¥ó¥È¤Î¾ì¹ç¡¢DHCP ¤Ç
+ÀßÄꤵ¤ì¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë¤è¤Ã¤Æ°Û¤Ê¤ëÆ°ºî¤ò¤µ¤»¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤¬
+¤¢¤ê¤Þ¤¹¡£lease Àë¸À¤È alias Àë¸À¤ò½ü¤¯¤¹¤Ù¤Æ¤Î¥¿¥¤¥ß¥ó¥°¥Ñ¥é¥á¡¼¥¿
+¤ÈÀë¸À¤ò¡¢interface Àë¸À¤Ç°Ï¤à¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤½¤Î¾ì¹ç¡¢°Ï¤Þ¤ì¤¿
+¥Ñ¥é¥á¡¼¥¿¤Ï»ØÄꤷ¤¿Ì¾Á°¤Ë¹çÃפ¹¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë¤Î¤ßŬÍѤµ¤ì¤Þ¤¹¡£
+interface Àë¸À¤ò»ý¤¿¤Ê¤¤¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢¤¹¤Ù¤Æ¤Î interface Àë¸À¤Î
+³°Â¦¤ÇÀë¸À¤µ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¡¢¤â¤·¤¯¤Ï¥Ç¥Õ¥©¥ë¥È¤ÎÀßÄ꤬ŬÍѤµ¤ì¤Þ¤¹¡£
+.PP
+ \fBpseudo "\fIname\fR" "\fIreal-name\fB" { \fIdeclarations ... \fB }
+.PP
+¾õ¶·¤Ë¤è¤Ã¤Æ¤Ï²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÀë¸À¤·¡¢
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤¿¤á¤ÎÀßÄê¤ò¼èÆÀ¤¹¤ë¤è¤¦¤Ë¤¹¤ë¤È
+ÊØÍø¤Ë¤Ê¤êÆÀ¤Þ¤¹¡£
+Ä̾ï DHCP ¥¯¥é¥¤¥¢¥ó¥È¤¬¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë³Æ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢
+¤½¤Î¥ê¡¼¥¹¤ò³ÍÆÀ¤·´ÉÍý¤¹¤ë¤¿¤á¤Ë¡¢
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¾õÂÖµ¡³£¤ò¼Â¹Ô¤·¤Æ¤¤¤Þ¤¹¡£
+²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ï¡¢\fIreal-name\fR ¤È̾ÉÕ¤±¤é¤ì¤¿¥¤¥ó¥¿¥Õ¥§¡¼¥¹¾å¤Ç
+²ÔƯ¤·¤Æ¤¤¤ë¡¢¤Þ¤µ¤·¤¯¤â¤¦°ì¤Ä¤Î¾õÂÖµ¡³£¤Ç¤¹¡£
+¤³¤Îµ¡Ç½¤ò»ÈÍѤ¹¤ë¾ì¹ç¡¢
+²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤È¼ÂºÝ¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ÎξÊý¤ËÂФ·¤Æ
+¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻ҤòÄ󶡤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+¤Þ¤¿¡¢»ÈÍѤ·¤¿¤¤ IP ¥¢¥É¥ì¥¹¤ËÂФ¹¤ë²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹ÍѤË
+ʬΥ¤µ¤ì¤¿¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤òÄ󶡤·¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹:
+.PP
+.nf
+ interface "ep0" {
+ send dhcp-client-identifier "my-client-ep0";
+ }
+ pseudo "secondary" "ep0" {
+ send dhcp-client-identifier "my-client-ep0-secondary";
+ script "/etc/dhclient-secondary";
+ }
+.fi
+.PP
+²¾ÁÛ¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¤¿¤á¤Î¥¯¥é¥¤¥¢¥ó¥È¥¹¥¯¥ê¥×¥È¤Ï
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÍ­¸ú¤Ë¤·¤¿¤ê̵¸ú¤Ë¤·¤¿¤ê¤¹¤ëÀßÄê¤ò¤¹¤ë¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£
+Æäˡ¢¥ê¡¼¥¹¤Î³ÍÆÀ¤ä¹¹¿·¤Î¾õÂÖ¡¢¤½¤·¤Æ¥ê¡¼¥¹¤Î´ü¸ÂÀÚ¤ì¤Î¾õÂÖ¤ò
+¼è¤ê°·¤¦¤¿¤á¤Ë¤Ï¡¢¤½¤Î¤³¤È¤¬É¬ÍפǤ¹¡£
+¾ÜºÙ¤Ï \fBdhclient-script(8)\fR ¤ò»²¾È¤·¤Æ²¼¤µ¤¤¡£
+.PP
+ \fBmedia "\fImedia setup\fB"\fI [ \fB, "\fImedia setup\fB", \fI... ]\fB;\fR
+.PP
+.B media
+ʸ¤Ï¡¢IP ¥¢¥É¥ì¥¹¼èÆÀÃæ¤Ë»ÈÍѤ¬»î¤ß¤é¤ì¤ë¡¢¥á¥Ç¥£¥¢ÀßÄê¥Ñ¥é¥á¡¼¥¿¤ò 1 ¤Ä
+°Ê¾åÄêµÁ¤·¤Þ¤¹¡£dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¥ê¥¹¥ÈÃæ¤Î³Æ media setup ʸ»úÎó¤ò
+½ç¼¡»ÈÍѤ·¡¢¤¢¤ë¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ò¤½¤ì¤ÇÀßÄꤷ¡¢¥Ö¡¼¥È¤ò»î¤ß¤Þ¤¹¡£
+ÂÌÌܤʤé¤Ð¼¡¤Î media setup ʸ»úÎó¤ò»ÈÍѤ·¤Þ¤¹¡£¤³¤Îʸ¤Ï¡¢
+¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò¸¡½Ð¤¹¤ëǽÎϤò»ý¤¿¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Ë
+ÂФ·¤ÆÍøÍѤǤ­¤Þ¤¹¡£¥µ¡¼¥Ð¤Ø¤Î¥ê¥¯¥¨¥¹¥È¤¬¤Ç¤­±þÅú¤¬ÆÀ¤é¤ì¤ë¤â¤Î
+¤Ê¤é¤Ð¡¢¤É¤Î¤è¤¦¤Ê¥á¥Ç¥£¥¢¥¿¥¤¥×¤Ç¤â¤¿¤Ö¤óÀµÅö¤Ç¤¹ (ÊݾڤϤ·¤Þ¤»¤ó¤¬)¡£
+.PP
+media setup ¤Ï¥¢¥É¥ì¥¹¼èÆÀ¤Î½é´ü¥Õ¥§¡¼¥º (DHCPDISCOVER ¥Ñ¥±¥Ã¥È¤È
+DHCPOFFER ¥Ñ¥±¥Ã¥È)¤Ç¤Î¤ß»ÈÍѤµ¤ì¤Þ¤¹¡£¤Ò¤È¤¿¤Ó¥¢¥É¥ì¥¹¤¬¼èÆÀ¤µ¤ì¤ë¤È¡¢
+dhcp ¥¯¥é¥¤¥¢¥ó¥È¤Ï¤½¤Î¥¢¥É¥ì¥¹¤ò¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ëµ­Ï¿¤·¡¢
+¤½¤Î¥¢¥É¥ì¥¹¤òÆÀ¤ëºÝ¤ËÍѤ¤¤¿¥á¥Ç¥£¥¢¥¿¥¤¥×¤òµ­Ï¿¤·¤Þ¤¹¡£¥¯¥é¥¤¥¢¥ó¥È¤¬
+¥ê¡¼¥¹¤ò¹¹¿·¤·¤è¤¦¤È¤¹¤ëºÝ¤Ë¤Ï¾ï¤Ë¡¢¤½¤ì¤ÈƱ¤¸¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò»ÈÍѤ·¤Þ¤¹¡£
+¥ê¡¼¥¹¤ò´ü¸ÂÀÚ¤ì¤Ë¤·¤Æ¤Ï¤¸¤á¤Æ¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¥á¥Ç¥£¥¢¥¿¥¤¥×¤ò½ç¤Ë»î¤¹
+¾õÂÖ¤ËÌá¤ê¤Þ¤¹¡£
+.\"X .SH SAMPLE ... man-jp ɸ½à¤Ï¤Ê¤ó¤À¤Ã¤¿¤Ã¤±
+.SH »ÈÍÑÎã
+°Ê²¼¤ÎÀßÄê¥Õ¥¡¥¤¥ë¤Ï¡¢NetBSD 1.3 ¤ò¼Â¹Ô¤¹¤ë¤¢¤ë¥é¥Ã¥×¥È¥Ã¥×¥Þ¥·¥ó¤Ç
+»ÈÍѤµ¤ì¤Æ¤¤¤ë¤â¤Î¤Ç¤¹¡£¤³¤Î¥Þ¥·¥ó¤Ï¡¢IP ¥¨¥¤¥ê¥¢¥¹¤È¤·¤Æ 192.5.5.213¡¢
+¥¤¥ó¥¿¥Õ¥§¡¼¥¹ ep0 (3Com 3C589C) ¤ò¤Ò¤È¤Ä»ý¤Ã¤Æ¤¤¤Þ¤¹¡£¤³¤Î¥¯¥é¥¤¥¢¥ó¥È
+¤Ï¡¢DHCP ³èÆ°¤¬¤Û¤È¤ó¤É¤Ê¤¤¥Í¥Ã¥È¥ï¡¼¥¯¤Ç»þ´Ö¤ÎÂçÉôʬ¤ò¾ÃÈñ¤¹¤ë¤³¤È¤¬
+¤ï¤«¤Ã¤Æ¤¤¤ë¤Î¤Ç¡¢¥Ö¡¼¥È´Ö³Ö¤Ï¥Ç¥Õ¥©¥ë¥ÈÃͤ«¤é¤¤¤¯¤Ö¤ó¾®¤µ¤¯¤·¤Æ
+¤¢¤ê¤Þ¤¹¡£¤³¤Î¥Þ¥·¥ó¤ÏÊ£¿ô¥Í¥Ã¥È¥ï¡¼¥¯´Ö¤Ç¥í¡¼¥ß¥ó¥° (°ÜÆ°) ¤·¤Þ¤¹¡£
+
+.nf
+
+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 "CLIENTBINDIR/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;
+}
+.fi
+¤³¤ì¤Ï dhclient.conf ¥Õ¥¡¥¤¥ë¤È¤·¤Æ¤ÏÈó¾ï¤ËÊ£»¨¤Ê¤â¤Î¤Ç¤¹¡£°ìÈ̤ˡ¢
+³§¤µ¤ó¤¬»ÈÍѤ¹¤ë¤â¤Î¤Ï¤Ï¤ë¤«¤Ë´Êñ¤Ê¤Ï¤º¤Ç¤¹¡£Â¿¤¯¤Î¾ì¹ç¡¢dhclient.conf
+¥Õ¥¡¥¤¥ë¤È¤·¤Æ¶õ¤Î¥Õ¥¡¥¤¥ë¤òÀ¸À®¤¹¤ë¤À¤±¤Ç½½Ê¬¤Ê¤Ï¤º¤Ç¤¹¡£
+¤Ä¤Þ¤ê¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤǤ褤¤Î¤¬ÉáÄ̤Ǥ¹¡£
+.SH ´ØÏ¢¹àÌÜ
+dhcp-options(5), dhclient.leases(5), dhclient(8), RFC2132,
+RFC2131
+.SH ºî¼Ô
+.B dhclient(8)
+¤Ï Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç Ted Lemon ¤¬½ñ¤­¤Þ¤·¤¿¡£
+ËÜ¥×¥í¥¸¥§¥¯¥È¤Î´ð¶â¤Ï Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢
+.B https://www.isc.org
+¤Ë¤¢¤ê¤Þ¤¹¡£
diff --git a/doc/ja_JP.eucJP/dhclient.leases.5 b/doc/ja_JP.eucJP/dhclient.leases.5
new file mode 100644
index 0000000..3d00d84
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhclient.leases.5
@@ -0,0 +1,62 @@
+.\" $Id: dhclient.leases.5,v 1.3.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1997-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie
+.\" Enterprises. To learn more about Internet Systems Consortium,
+.\" see ``https://www.isc.org/''. To learn more about Vixie
+.\" Enterprises, see ``http://www.vix.com''.
+.\"
+.\"
+.\" %FreeBSD: src/contrib/isc-dhcp/client/dhclient.leases.5,v 1.2.4.1 2002/04/11 10:16:46 murray Exp %
+.\"
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhclient.leases.5,v 1.6 2002/05/05 20:40:23 horikawa Exp $
+.TH dhclient.leases 5
+.SH ̾¾Î
+dhclient.leases - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹
+.SH ²òÀâ
+Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢
+³ÍÆÀ¤·¤¿¥ê¡¼¥¹¤Î¤¦¤Á¤Þ¤ÀÍ­¸ú¤Ç¤¢¤ë¤â¤Î¤ò´ÉÍý¤¹¤ë¤¿¤á¤Î¡¢
+±Ê³Ū¤Ê¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÊÝ»ý¤·¤Þ¤¹¡£
+¤³¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ï¡¢¼«Í³·Á¼°¤Î ASCII ¥Õ¥¡¥¤¥ë¤Ç¤¢¤ê¡¢
+¥ê¡¼¥¹ 1 ¤Ä¤Ë¤Ä¤­Í­¸ú¤ÊÀë¸À¤ò 1 ¤Ä´Þ¤ß¤Þ¤¹¡£
+¤¢¤ë¥ê¡¼¥¹¤ËÂФ·¤ÆÊ£¿ô¤ÎÀë¸À¤¬Åо줹¤ë¾ì¹ç¡¢
+¥Õ¥¡¥¤¥ëÃæ¤ÎºÇ¸å¤Î¤â¤Î¤¬»ÈÍѤµ¤ì¤Þ¤¹¡£
+¤³¤Î¥Õ¥¡¥¤¥ë¤Ï¥í¥°¤È¤·¤Æ½ñ¤­¹þ¤Þ¤ì¤Þ¤¹¤Î¤Ç¡¢
+¤³¤Î¤è¤¦¤Ê¾õÂ֤ˤʤ뤳¤È¤Ï°Û¾ï¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£
+.PP
+¥ê¡¼¥¹Àë¸À¤Î½ñ¼°¤Ï¡¢
+.B dhclient.conf(5)
+¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+.SH ´ØÏ¢¥Õ¥¡¥¤¥ë
+.B DBDIR/dhclient.leases
+.SH ´ØÏ¢¹àÌÜ
+dhclient(8), dhcp-options(5), dhclient.conf(5),
+RFC2132, RFC2131
+.SH ºî¼Ô
+.B dhclient(8)
+¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£
+ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢
+.B https://www.isc.org
+¤Ë¤¢¤ê¤Þ¤¹¡£
diff --git a/doc/ja_JP.eucJP/dhcp-eval.5 b/doc/ja_JP.eucJP/dhcp-eval.5
new file mode 100644
index 0000000..2b6eb53
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhcp-eval.5
@@ -0,0 +1,488 @@
+.\" $Id: dhcp-eval.5,v 1.4.24.1 2009-11-20 01:49:01 sar Exp $
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhcp-eval.5,v 1.2 2002/05/23 04:17:13 horikawa Exp $
+.TH dhcp-eval 5
+.SH ̾¾Î
+dhcp-eval - ISC DHCP ¤Ë¤ª¤±¤ë¾ò·ïÉÕ¤­É¾²Á
+.SH ²òÀâ
+Internet Systems Consortium ¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢¤É¤Á¤é¤â
+¼õ¿®¤¹¤ë¥Ñ¥±¥Ã¥È¤Ë°Í¸¤·¤¿¾ò·ïÉÕ¤­Æ°ºî¤ò¹Ô¤¦Ç½ÎϤò»ý¤Á¤Þ¤¹¡£
+¾ò·ïÉÕ¤­Æ°ºî¤Îʸˡ¤ò¤³¤³¤Ë¼¨¤·¤Þ¤¹¡£
+.SH »²¾È: ¾ò·ïÉÕ¤­Æ°ºî
+¾ò·ïÉÕ¤­Æ°ºî¤Ï¡¢if, else, elsif ʸ¤ò»ÈÍѤ·¤Æ»ØÄꤷ¤Þ¤¹¡£
+¾ò·ïʸ¤Ï¡¢Ä̾ïʸ (option ʸ) ¤¬Åоì²Äǽ¤Ê¾ì½ê¤Ï¤É¤³¤Ë¤Ç¤âÅоì²Äǽ¤Ç¤¢¤ê¡¢
+¤Þ¤¿¤³¤Î¤è¤¦¤Êʸ¤ò³ç¤ë¤³¤È¤â²Äǽ¤Ç¤¹¡£
+¥µ¡¼¥Ð¤Ë¤ª¤±¤ë¾ò·ïʸ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¤³¤È¤¬Â¿¤¤¤Ç¤·¤ç¤¦:
+.PP
+.nf
+if option dhcp-user-class = "accounting" {
+ max-lease-time 17600;
+ option domain-name "accounting.example.org";
+ option domain-name-servers ns1.accounting.example.org,
+ ns2.accounting.example.org;
+} elsif option dhcp-user-class = "sales" {
+ max-lease-time 17600;
+ option domain-name "sales.example.org";
+ option domain-name-servers ns1.sales.example.org,
+ ns2.sales.example.org;
+} elsif option dhcp-user-class = "engineering" {
+ max-lease-time 17600;
+ option domain-name "engineering.example.org";
+ option domain-name-servers ns1.engineering.example.org,
+ ns2.engineering.example.org;
+} else {
+ max-lease-time 600;
+ option domain-name "misc.example.org";
+ option domain-name-servers ns1.misc.example.org,
+ ns2.misc.example.org;
+}
+.fi
+.PP
+¥¯¥é¥¤¥¢¥ó¥È¦¤Ç¤Ï¡¢¾ò·ïÉÕ¤­É¾²Á¤ÎÎã¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¤Ç¤·¤ç¤¦:
+.PP
+.nf
+# example.org ¤Ï¥Õ¥¡¥¤¥ä¥¦¥©¡¼¥ë¤Ç DNS ¤ò¥Õ¥£¥ë¥¿¤¹¤ë¤Î¤Ç¡¢
+# example.org ¥Í¥Ã¥È¥ï¡¼¥¯¤Ë·Ò¤¬¤ë¤È¤­¤Î¤ß¡¢¤½¤Î DNS ¥µ¡¼¥Ð¤ò»ÈÍѤ·¤Þ¤¹¡£
+# example.org ¤Ë·Ò¤¬¤ë¤Î¤Ç¤Ï¤Ê¤¤¾ì¹ç¡¢¼«¸Ê¤Î DNS ¥µ¡¼¥Ð¤òÍ¥Àè»ÈÍѤ·¤Þ¤¹¡£
+if not option domain-name = "example.org" {
+ prepend domain-name-servers 127.0.0.1;
+}
+.fi
+.PP
+.B if
+ʸ¤È
+.B elsif
+·Ñ³ʸ¤Ï¡¢°ú¿ô¤È¤·¤Æ¥Ö¡¼¥ë¼°¤ò¼è¤ê¤Þ¤¹¡£
+¤Ä¤Þ¤ê¡¢¤³¤ì¤é¤Îʸ¤Ï¡¢É¾²Á¤µ¤ì¤ë¤È¥Ö¡¼¥ëÃͤηë²Ì¤òÀ¸À®¤¹¤ë¼°¤ò¼è¤ê¤Þ¤¹¡£
+¼°¤Îɾ²Á·ë²Ì¤¬¿¿¤Ë¤Ê¤ë¤È¡¢
+.B if
+ʸ¤Îľ¸å¤Î¥Ö¥ì¡¼¥¹¤Ç³ç¤é¤ì¤¿Ê¸¤¬¼Â¹Ô¤µ¤ì¡¢¸å³¤¹¤ë
+.B elsif
+¤È
+.B else
+¤ÎÀá¤Ï¥¹¥­¥Ã¥×¤µ¤ì¤Þ¤¹¡£
+¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢É¾²Á·ë²Ì¤¬¿¿¤Ë¤Ê¤ë elsif Àá¤Ë½Ð²ñ¤¦¤Þ¤Ç¡¢¸å³¤¹¤ë³Æ
+.B elsif
+Àá¤Î¼°¤¬¥Á¥§¥Ã¥¯¤µ¤ì¤Þ¤¹¡£
+¤½¤Î¤è¤¦¤ÊÀ᤬¸«ÉÕ¤«¤ë¤È¡¢Ä¾¸å¤Î¥Ö¥ì¡¼¥¹Ãæ¤Îʸ¤¬¼Â¹Ô¤µ¤ì¡¢¸å³¤¹¤ë
+.B elsif
+¤È
+.B else
+¤ÎÀá¤Ï¥¹¥­¥Ã¥×¤µ¤ì¤Þ¤¹¡£
+¤¹¤Ù¤Æ¤Î
+.B if
+¤ª¤è¤Ó
+.B elsif
+¤ÎÀ᤬¥Á¥§¥Ã¥¯¤µ¤ì¤¿¤â¤Î¤Î¤É¤Î¼°¤â¿¿¤Ë¤Ê¤é¤Ê¤¤¾ì¹ç¤Ç¡¢
+.B else
+À᤬¸ºß¤¹¤ë¾ì¹ç¡¢
+.B else
+¤Îľ¸å¤Î¥Ö¥ì¡¼¥¹Ãæ¤Îʸ¤¬É¾²Á¤µ¤ì¤Þ¤¹¡£
+¾ò·ï¤Ë¤ª¤¤¤Æ¤Ï¡¢É¾²Á·ë²Ì¤¬¶õ¤Ë¤Ê¤ë¥Ö¡¼¥ë¼°¤Ïµ¶¤È¤·¤Æ°·¤ï¤ì¤Þ¤¹¡£
+.SH ¥Ö¡¼¥ë¼°
+°Ê²¼¤Ï¡¢DHCP ÇÛÉÛʪ¤Ç¸½ºß¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¥Ö¡¼¥ë¼°¤Î°ìÍ÷¤Ç¤¹¡£
+.PP
+.I data-expression-1 \fB=\fI data-expression-2\fR
+.RS 0.25i
+.PP
+\fB=\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢2 ¸Ä¤Î¥Ç¡¼¥¿¼°¤òÈæ³Ó¤·¡¢Î¾¼Ô¤¬Æ±¤¸¾ì¹ç¤Ï¿¿¤òÊÖ¤·¡¢
+Ʊ°ì¤Ç¤Ê¤¤¾ì¹ç¤Ïµ¶¤òÊÖ¤·¤Þ¤¹¡£
+º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.I boolean-expression-1 \fBand\fI boolean-expression-2\fR
+.PP
+.RS 0.25i
+\fBand\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢º¸ÊդΥ֡¼¥ë¼°¤È±¦ÊդΥ֡¼¥ë¼°¤ÎξÊý¤Îɾ²Á·ë²Ì¤¬
+¿¿¤Î¾ì¹ç¡¢¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.I boolean-expression-1 \fBor\fI boolean-expression-2\fR
+.PP
+.RS 0.25i
+\fBor\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢º¸ÊդΥ֡¼¥ë¼°¤È±¦ÊդΥ֡¼¥ë¼°¤Î¤¤¤º¤ì¤«¤Îɾ²Á·ë²Ì¤¬
+¿¿¤Î¾ì¹ç¡¢¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+º¸Êդ⤷¤¯¤Ï±¦ÊդΤ¤¤º¤ì¤«¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B not \fIboolean-expression
+.PP
+.RS 0.25i
+\fBnot\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢\fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬µ¶¤Î¾ì¹ç¡¢
+¿¿¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+¤Þ¤¿¡¢\fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬¿¿¤Î¾ì¹ç¡¢µ¶¤Èɾ²Á¤µ¤ì¤Þ¤¹¡£
+\fIboolean-expression\fR ¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B exists \fIoption-name\fR
+.PP
+.RS 0.25i
+\fBexists\fR ¼°¤Ï¡¢½èÍýÂоݤÎÆþÎÏ DCHP ¥Ñ¥±¥Ã¥ÈÃæ¤Ë¡¢
+»ØÄꤵ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¬Â¸ºß¤¹¤ë¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£
+.RE
+.B known
+.PP
+.RS 0.25i
+\fBknown\fR ¼°¤Ï¡¢Í×µáÂбþÃæ¤Î¥¯¥é¥¤¥¢¥ó¥È¤¬´ûÃΤξì¹ç¡¢
+¤¹¤Ê¤ï¤Á¥Û¥¹¥ÈÀë¸À¤¬¤¢¤ë¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£
+.RE
+.B static
+.PP
+.RS 0.25i
+\fBstatic\fR ¼°¤Ï¡¢Í×µáÂбþÃæ¤Î¥¯¥é¥¤¥¢¥ó¥È¤Ø¤Î¥ê¡¼¥¹³ä¤êÅö¤Æ¤¬¡¢
+ÀÅŪ¥¢¥É¥ì¥¹³ä¤êÅö¤Æ¤Ë¤è¤ë¤â¤Î¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¿¿¤òÊÖ¤·¤Þ¤¹¡£
+.RE
+.SH ¥Ç¡¼¥¿¼°
+Á°½Ò¤Î¥Ö¡¼¥ë¼°¤Ï¡¢¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤Ë°Í¸¤·¤Þ¤¹¡£
+¥Ç¡¼¥¿¼°¤ò¤³¤³¤Ë¼¨¤·¤Þ¤¹¡£
+.PP
+.B substring (\fIdata-expr\fB, \fIoffset\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+\fBsubstring\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Ç¡¼¥¿¼°¤òɾ²Á¤·¡¢
+ɾ²Á·ë²ÌÃæ¤Î \fIoffset\fR ¥Ð¥¤¥È¤«¤é³«»Ï¤·¤Æ \fIlength\fR ¥Ð¥¤¥È·Ñ³¤¹¤ë
+¥µ¥Ö¥¹¥È¥ê¥ó¥°¤òÊÖ¤·¤Þ¤¹¡£
+\fIoffset\fR ¤È \fIlength\fR ¤Ï¶¦¤Ë¿ôÃͼ°¤Ç¤¹¡£
+\fIdata-expr\fR, \fIoffset\fR, \fIlength\fR ¤Î¤¤¤º¤ì¤«¤¬¶õ¤Èɾ²Á¤µ¤ì¤ë¾ì¹ç¡¢
+·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+\fIoffset\fR ¤¬¡¢É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤ÎŤµ°Ê¾å¤Ç¤¢¤ë¾ì¹ç¡¢
+Ťµ 0 ¤Î¥Ç¡¼¥¿Ê¸»úÎó¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+\fIlength\fI ¤¬¡¢É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤Î \fIoffset\fR ¤è¤ê¸å¤ÎŤµ¤è¤êÂ礭¤¤¾ì¹ç¡¢
+ɾ²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤Î \fIoffset\fR ¤«¤é½ªÃ¼¤Þ¤Ç¤ÎÁ´¥Ç¡¼¥¿¤ò´Þ¤à
+¥Ç¡¼¥¿Ê¸»úÎó¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B suffix (\fIdata-expr\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+\fBsuffix\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢\fIdata-expr\fR ¤òɾ²Á¤·¡¢
+ɾ²Á·ë²Ì¤ÎºÇ¸å¤Î \fIlength\fR ¥Ð¥¤¥È¤òÊÖ¤·¤Þ¤¹¡£
+\fIlength\fR ¤Ï¿ôÃͼ°¤Ç¤¹¡£
+\fIdata-expr\fR ¤Þ¤¿¤Ï \fIlength\fR ¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢
+·ë²Ì¤â¤Þ¤¿¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+\fIsuffix\fR
+(ÌõÃí: \fIlength\fR ¤¬Àµ¤·¤¤¤È»×¤ï¤ì¤Þ¤¹)
+¤Îɾ²Á·ë²Ì¤¬É¾²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤ÎŤµ¤è¤êÂ礭¤¤¾ì¹ç¡¢
+ɾ²Á¤µ¤ì¤¿¥Ç¡¼¥¿¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+.\" horikawa@jp.FreeBSD.org 2002/04/29
+.RE
+.PP
+.B option \fIoption-name\fR
+.PP
+.RS 0.25i
+\fBoption\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥µ¡¼¥Ð¤¬±þÅú½èÍýÃæ¤Î¥Ñ¥±¥Ã¥È¤ÎÃæ¤Î¡¢
+»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ÎÆâÍƤòÊÖ¤·¤Þ¤¹¡£
+.RE
+.PP
+.B config-option \fIoption-name\fR
+.PP
+.RS 0.25i
+\fBconfig-option\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢»ØÄꤷ¤¿¥ª¥×¥·¥ç¥ó¤ËÂФ·¡¢
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Þ¤¿¤Ï¥µ¡¼¥Ð¤¬Á÷½Ð¤¹¤ë¤è¤¦ÀßÄꤵ¤ì¤¿ÃͤòÊÖ¤·¤Þ¤¹¡£
+.RE
+.PP
+.B hardware
+.PP
+.RS 0.25i
+\fBhardware\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤òÊÖ¤·¤Þ¤¹¡£
+¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤ÎºÇ½é¤ÎÍ×ÁǤϡ¢
+Âоݥѥ±¥Ã¥È¤¬¼¨¤¹¥Í¥Ã¥È¥ï¡¼¥¯¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤Î¥¿¥¤¥×¤Ç¤¢¤ê¡¢
+¸å³¤¹¤ëÍ×ÁǤϡ¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ê¥ó¥¯ÁØ¥¢¥É¥ì¥¹¤Ç¤¹¡£
+¥Ñ¥±¥Ã¥È¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¤â¤·¤¯¤Ï RFC2131 \fIhlen\fR ¥Õ¥£¡¼¥ë¥É¤¬Ìµ¸ú¤Ê¾ì¹ç¡¢
+·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+¥Ï¡¼¥É¥¦¥§¥¢¥¿¥¤¥×¤Ë¤Ï¡¢¥¤¡¼¥µ¥Í¥Ã¥È (1)¡¢¥È¡¼¥¯¥ó¥ê¥ó¥° (6)¡¢
+FDDI (8) ¤¬´Þ¤Þ¤ì¤Þ¤¹¡£
+¥Ï¡¼¥É¥¦¥§¥¢¥¿¥¤¥×¤Ï IETF ¤Ë¤è¤Ã¤Æµ¬Äꤵ¤ì¡¢
+¤É¤Î¤è¤¦¤Ë¥¿¥¤¥×¤Î¿ôÃͤ¬ÄêµÁ¤µ¤ì¤ë¤«¤Î¾ÜºÙ¤Ï RFC2131
+(ISC DHCP ÇÛÉÛʪ¤Ç¤Ï¡¢doc/ ¥µ¥Ö¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤¢¤ê¤Þ¤¹) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B packet (\fIoffset\fB, \fIlength\fB)\fR
+.PP
+.RS 0.25i
+\fBpacket\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢Âоݥѥ±¥Ã¥È¤Î»ØÄêÉôʬ¤òÊÖ¤¹¤«¡¢
+Âоݥѥ±¥Ã¥È¤¬Ìµ¤¤Ê¸Ì®¤Ç¤Ï¶õ¤òÊÖ¤·¤Þ¤¹¡£
+\fIoffset\fR ¤È \fIlength\fR ¤Ï¡¢
+\fBsubstring\fR ¥ª¥Ú¥ì¡¼¥¿¤ÈƱÍͤˡ¢¥Ñ¥±¥Ã¥È¤ÎÆâÍƤËŬÍѤµ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.I string
+.PP
+.RS 0.25i
+¥¯¥©¡¼¥È¤Ç³ç¤é¤ì¤¿¥¹¥È¥ê¥ó¥°¤Ï¥Ç¡¼¥¿¼°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¢¤ê¡¢
+¥¯¥©¡¼¥È¤Î´Ö¤ò ASCII ¥¨¥ó¥³¡¼¥É¤·¤¿¤Î¥Æ¥­¥¹¥È¤òÊÖ¤·¤Þ¤¹¡£
+¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å ('\\') ʸ»ú¤Ï C ¥×¥í¥°¥é¥à¤Î¤è¤¦¤ËÆÃÊÌ°·¤¤¤µ¤ì¤Þ¤¹:
+¤¹¤Ê¤ï¤Á '\\t' ¤Ï¥¿¥Ö¤ò¡¢'\\r' ¤ÏÉü²þ¤ò¡¢'\\n' ¤Ï²þ¹Ô¤ò¡¢'\\b' ¤Ï¥Ù¥ë¤ò
+°ÕÌ£¤·¤Þ¤¹¡£
+8 ¿Ê¿ôÃÍ¤Ï '\\nnn' ¤Ç»ØÄê²Äǽ¤Ç¤¢¤ê¡¢nnn ¤Ï 0 °Ê¾å 0377 °Ê²¼¤Î 8 ¿Ê¿ôÃͤǤ¹¡£
+16 ¿Ê¿ôÃÍ¤Ï '\\xnn' ¤Ç»ØÄê²Äǽ¤Ç¤¢¤ê¡¢nn ¤Ï 0 °Ê¾å 0xff °Ê²¼¤Î 16 ¿Ê¿ôÃͤǤ¹¡£
+.\" ÃͤÎÈϰϤθí¤ê¤Ë¤Ä¤¤¤Æ¤Ï¡¢Murray ·Ðͳ¤Ç¥ì¥Ý¡¼¥ÈºÑ
+.\" horikawa@jp.FreeBSD.org 2002/05/01
+.RE
+.PP
+.I colon-separated hexadecimal list
+.PP
+.RS 0.25i
+¥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿ 16 ¿Ê¿ô¤Î¥ª¥¯¥Æ¥Ã¥ÈÃͤΥꥹ¥È¤ò¡¢
+¥Ç¡¼¥¿¼°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¹¡£
+.RE
+.PP
+.B concat (\fIdata-expr1\fB, ..., \fIdata-exprN\fB)\fR
+.RS 0.25i
+¼°¤¬É¾²Á¤µ¤ì¡¢³Æɾ²Á·ë²Ì¤¬¥µ¥Ö¼°¤Î½çÈÖ¤ËÏ¢·ë¤µ¤ì¤Þ¤¹¡£
+¥µ¥Ö¼°¤Î¤¤¤º¤ì¤«¤Îɾ²Á·ë²Ì¤¬¶õ¤Ë¤Ê¤ë¾ì¹ç¡¢Ï¢·ë¤Î·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B reverse (\fInumeric-expr1\fB, \fIdata-expr2\fB)\fR
+.RS 0.25i
+2 ¸Ä¤Î¼°¤¬É¾²Á¤µ¤ì¡¢¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬¤½¤Î¾ì¤Çȿž¤µ¤ì¤Þ¤¹¡£
+ȿž¤Ï¡¢¿ôÃͼ°¤Ç»ØÄꤵ¤ì¤ëÂ礭¤µ¤Îñ°Ì¤Ç¹Ô¤ï¤ì¤Þ¤¹¡£
+Î㤨¤Ð¡¢¿ôÃͼ°¤Îɾ²Á·ë²Ì¤¬ 4 ¤Î¾ì¹ç¤Ç¡¢
+¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬ 12 ¥Ð¥¤¥È¤Ë¤Ê¤ë¾ì¹ç¡¢
+reverse ¼°¤Îɾ²Á·ë²Ì¤Ï¡¢¼¡¤Î¤è¤¦¤Ê 12 ¥Ð¥¤¥È¤Î¥Ç¡¼¥¿¤Ë¤Ê¤ê¤Þ¤¹¡£
+¤¹¤Ê¤ï¤Á¡¢ÆþÎϤκǸå¤Î 4 ¥Ð¥¤¥È¡¢¿¿Ãæ¤Î 4¥Ð¥¤¥È¡¢ºÇ½é¤Î 4 ¥Ð¥¤¥È¤Î
+½ç¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B leased-address
+.RS 0.25i
+¤¤¤«¤Ê¤ëʸ̮¤Ë¤ª¤¤¤Æ¤â¡¢
+Í×µá½èÍýÂоݤȤʤäƤ¤¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ë IP ¥¢¥É¥ì¥¹¤¬³ä¤êÅö¤ÆºÑ¤Î¾ì¹ç¡¢
+¤½¤Î IP ¥¢¥É¥ì¥¹¤¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B binary-to-ascii (\fInumeric-expr1\fB, \fInumeric-expr2\fB,
+.B \fIdata-expr1\fB,\fR \fIdata-expr2\fB)\fR
+.RS 0.25i
+data-expr2 ¤Îɾ²Á·ë²Ì¤ò¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°¤ËÊÑ´¹¤·¤Þ¤¹¡£
+¤³¤Î¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°Ãæ¤Ç¤Ï¡¢
+data-expr2 ¤Îɾ²Á·ë²Ì¤Î³ÆÍ×ÁǤ¬¡¢1 ¸Ä¤Î¿ôÃͤˤʤê¤Þ¤¹¡£
+³Æ¿ôÃͤϡ¢¤½¤ì¤¾¤ì¡¢data-expr1 ¤Îɾ²Á·ë²Ì¤Ë¤è¤Ã¤Æ¶èÀÚ¤é¤ì¤Þ¤¹¡£
+numeric-expr1 ¤Îɾ²Á·ë²Ì¤Ï¡¢´ð¿ô (2 ¤«¤é 16) ¤Ç¤¢¤ê¡¢
+¤³¤Î´ð¿ô¤Ë¿ôÃͤ¬ÊÑ´¹¤µ¤ì¤Þ¤¹¡£
+numeric-expr2 ¤Îɾ²Á·ë²Ì¤Ï¡¢³Æ¿ôÃͤΥӥåÈÉý¤Ç¤¢¤ê¡¢
+8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£
+.PP
+ºÇ½é¤Î 3 ¸Ä¤Î¥¿¥¤¥×¤Î¼°¤ÎÎã¤È¤·¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ IP ¥¢¥É¥ì¥¹ÍѤÎ
+PTR ¥ì¥³¡¼¥É¤Î̾Á°¤òÀ¸À®¤¹¤ë¤¿¤á¤Ë»ÈÍѲÄǽ¤Ê¼°¤ò¼¨¤·¤Þ¤¹
+.RE
+.PP
+.nf
+ concat (binary-to-ascii (10, 8, ".",
+ reverse (1, leased-address)),
+ ".in-addr.arpa.");
+
+.fi
+.PP
+.B encode-int (\fInumeric-expr\fB, \fIwidth\fB)\fR
+.RS 0.25i
+¿ôÃͼ°¤¬É¾²Á¤µ¤ì¡¢»ØÄꤵ¤ì¤¿Éý¤Î¥Ç¡¼¥¿¥¹¥È¥ê¥ó¥°¤Ë
+¥Í¥Ã¥È¥ï¡¼¥¯¥Ð¥¤¥È½ç (ºÇ¾å°Ì¥Ð¥¤¥È¤¬ºÇ½é) ¤Ç¥¨¥ó¥³¡¼¥É¤µ¤ì¤Þ¤¹¡£
+¿ôÃͼ°¤Îɾ²Á·ë²Ì¤¬¶õ¤ÎÃͤˤʤë¾ì¹ç¡¢·ë²Ì¤â¤Þ¤¿¶õ¤Ç¤¹¡£
+.RE
+.\" ¤³¤Î ".RE" ¤¬Ìµ¤¤¤È¡¢¥¤¥ó¥Ç¥ó¥È¤¬Àµ¤·¤¯¤Ê¤¤¤Ç¤¹
+.\" horikawa@jp.FreeBSD.org 2002/04/29
+.PP
+.B pick-first-value (\fIdata-expr1\fR [ ... \fIexpr\fRn ] \fB)\fR
+.RS 0.25i
+pick-first-value ´Ø¿ô¤Ï¡¢Ç¤°Õ¸Ä¤Î¥Ç¡¼¥¿¼°¤ò¼è¤êÆÀ¤Þ¤¹¡£
+¥ê¥¹¥È¤ÎÀèƬ¤«¤é³Æ¼°¤¬É¾²Á¤µ¤ì¡¢
+ɾ²Á·ë²Ì¤¬¶õ¤Ç¤Ï¤Ê¤¤¼°¤¬¸«ÉÕ¤«¤ë¤Þ¤Ç¤³¤ì¤¬Â³¤­¤Þ¤¹¡£
+¤³¤Î¼°¤¬ÊÖ¤µ¤ì¡¢¤³¤Î¼°¤Ë¸å³¤¹¤ë¼°¤Ïɾ²Á¤µ¤ì¤Þ¤»¤ó¡£
+¤¹¤Ù¤Æ¤Î¼°¤Îɾ²Á·ë²Ì¤¬¶õ¤Î¾ì¹ç¡¢¶õ¤ÎÃͤ¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B host-decl-name
+.RS 0.25i
+host-decl-name ´Ø¿ô¤Ï¡¢¸½ºßÍ×µá½èÍýÂоݤȤʤäƤ¤¤ë¥¯¥é¥¤¥¢¥ó¥È¤Ë¥Þ¥Ã¥Á¤¹¤ë¡¢
+¥Û¥¹¥ÈÀë¸À¤Î̾Á°¤òÊÖ¤·¤Þ¤¹¡£
+¤É¤Î¥Û¥¹¥ÈÀë¸À¤â¥Þ¥Ã¥Á¤·¤Ê¤¤¾ì¹ç¡¢·ë²Ì¤Ï¶õ¤Ë¤Ê¤ê¤Þ¤¹¡£
+.RE
+.SH ¿ôÃͼ°
+¿ôÃͼ°¤Ï¡¢É¾²Á·ë²Ì¤¬À°¿ô¤Ë¤Ê¤ë¼°¤Ç¤¹¡£
+°ìÈ̤ˡ¢À°¿ô¤ÎºÇÂ祵¥¤¥º¤¬ 32 ¥Ó¥Ã¥È̤Ëþ¤Ç¤¢¤ë¤È²¾Äꤹ¤Ù¤­¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢
+À°¿ô¤ÎÀºÅÙ¤¬ 32 ¥Ó¥Ã¥È¤ò±Û¤¨¤ë¤³¤È¤Ï¤¢¤êÆÀ¤Þ¤¹¡£
+.PP
+.B extract-int (\fIdata-expr\fB, \fIwidth\fB)\fR
+.PP
+.RS 0.25i
+\fBextract-int\fR ¥ª¥Ú¥ì¡¼¥¿¤Ï¡¢¥Í¥Ã¥È¥ï¡¼¥¯¥Ð¥¤¥È½ç¤ÎÀ°¿ô¤ò¡¢
+»ØÄꤷ¤¿¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤«¤é¼è¤ê½Ð¤·¤Þ¤¹¡£
+Éý¤Ï¡¢¼è¤ê½Ð¤¹À°¿ô¤Î¥Ó¥Ã¥ÈÉý¤Ç¤¹¡£
+¸½ºß¡¢¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ëÉý¤Ï 8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£
+¥Ç¡¼¥¿¼°¤Îɾ²Á·ë²Ì¤¬¡¢»ØÄꤷ¤¿Â礭¤µ¤ÎÀ°¿ô¤È¼è¤ê½Ð¤¹¤Î¤Ë
+½½Ê¬¤Ê¥Ó¥Ã¥È¤òÄ󶡤·¤Ê¤¤¾ì¹ç¡¢¶õ¤ÎÃͤ¬ÊÖ¤µ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B lease-time
+.PP
+.RS 0.25i
+¸½ºß¤Î¥ê¡¼¥¹¤Î´ü´Ö¤Ç¤¹¡£
+¤¹¤Ê¤ï¤Á¡¢¸½ºß¤Î»þ¹ï¤È¥ê¡¼¥¹¤Î´ü¸Â¤¬ÀÚ¤ì¤ë»þ¹ï¤È¤Îº¹¤Ç¤¹¡£
+.RE
+.PP
+.I number
+.PP
+.RS 0.25i
+0 ¤«¤éɽ¸½²Äǽ¤ÊºÇÂ祵¥¤¥º¤ÎÈϰϤÎǤ°Õ¤Î¿ôÃͤò¡¢¿ôÃͼ°¤È¤·¤Æ»ØÄê²Äǽ¤Ç¤¹¡£
+.RE
+.PP
+.B client-state
+.PP
+.RS 0.25i
+½èÍýÂоݤΥ¯¥é¥¤¥¢¥ó¥È¤Î¸½ºß¤Î¾õÂ֤Ǥ¹¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥ÈÀßÄê¥Õ¥¡¥¤¥ë¤Ë¤ª¤¤¤Æ¤Î¤ßÍ­ÍѤǤ¹¡£
+¼è¤êÆÀ¤ëÃͤϼ¡¤ÎÄ̤ê¤Ç¤¹:
+.TP 2
+.I \(bu
+Booting - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï INIT ¾õÂ֤Ǥ¢¤ê¡¢
+IP ¥¢¥É¥ì¥¹¤ò¤Þ¤À»ý¤Á¤Þ¤»¤ó¡£
+¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPDISCOVER ¤Ç¤¢¤ê¡¢
+¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£
+.TP
+.I \(bu
+Reboot - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï INIT-REBOOT ¾õÂ֤Ǥ¹¡£
+IP ¥¢¥É¥ì¥¹¤ò»ý¤Á¤Þ¤¹¤¬¤Þ¤À»ÈÍѤ·¤Æ¤¤¤Þ¤»¤ó¡£
+¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¤Ç¤¢¤ê¡¢
+¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£
+±þÅú¤¬²¿¤âʹ¤³¤¨¤Ê¤¤¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¤³¤Î¥¢¥É¥ì¥¹¤Ë¥Ð¥¤¥ó¥É¤·¡¢
+BOUND ¾õÂÖ¤ËÁ«°Ü¤·¤Þ¤¹¡£
+.TP
+.I \(bu
+Select - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï SELECTING ¾õÂ֤Ǥ¹¡£
+¾¯¤Ê¤¯¤È¤â 1 ¸Ä¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤Ï¼õ¿®¤·¤Þ¤·¤¿¤¬¡¢
+¾¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤ò¾¤Î¥µ¡¼¥Ð¤«¤é¼õ¤±¼è¤ë¤«¤É¤¦¤«ÂԤäƤ¤¤Þ¤¹¡£
+SELECTING ¾õÂ֤Ǥϥá¥Ã¥»¡¼¥¸¤ÏÁ÷¿®¤µ¤ì¤Þ¤»¤ó¡£
+.TP
+.I \(bu
+Request - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï REQUESTING ¾õÂ֤Ǥ¹¡£
+¾¯¤Ê¤¯¤È¤â 1 ¸Ä¤Î DHCPOFFER ¥á¥Ã¥»¡¼¥¸¤ò¼õ¿®¤·¡¢
+¤½¤Î¤¦¤Á¤Î¤É¤ì¤òÍ׵᤹¤ë¤«ÁªÂò¤·¤Þ¤·¤¿¡£
+¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢
+¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£
+.TP
+.I \(bu
+Bound - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï BOUND ¾õÂ֤Ǥ¹¡£
+IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤¤¤Þ¤¹¡£
+¤³¤Î¾õÂ֤Ǥϥá¥Ã¥»¡¼¥¸¤ÏÁ÷¿®¤µ¤ì¤Þ¤»¤ó¡£
+.TP
+.I \(bu
+Renew - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï RENEWING ¾õÂ֤Ǥ¹¡£
+IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤ª¤ê¡¢¤³¤ì¤ò¹¹¿·¤¹¤ë¤¿¤á¤Ë¥µ¡¼¥Ð¤ËÀܳ¤ò»î¤ß¤Æ¤¤¤Þ¤¹¡£
+¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢
+¤³¤ì¤Ï¥µ¡¼¥Ð¤ËľÀÜ¥æ¥Ë¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£
+.TP
+.I \(bu
+Rebind - DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï REBINDING ¾õÂ֤Ǥ¹¡£
+IP ¥¢¥É¥ì¥¹¤ò½êÍ­¤·¤Æ¤ª¤ê¡¢
+¤³¤ì¤ò¹¹¿·¤¹¤ë¤¿¤á¤ËǤ°Õ¤Î¥µ¡¼¥Ð¤ËÀܳ¤ò»î¤ß¤Æ¤¤¤Þ¤¹¡£
+¼¡¤ËÁ÷¿®¤µ¤ì¤ë¥á¥Ã¥»¡¼¥¸¤Ï DHCPREQUEST ¥á¥Ã¥»¡¼¥¸¤Ç¤¢¤ê¡¢
+¤³¤ì¤Ï¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¤µ¤ì¤Þ¤¹¡£
+.RE
+.SH »²¾È: ¥í¥°
+¥í¥°Ê¸¤ò»ÈÍѤ·¤Æ¡¢É¸½à¥í¥°¥Á¥ã¥Í¥ë¤Ë¾ðÊó¤òÁ÷¿®²Äǽ¤Ç¤¹¡£
+¥í¥°Ê¸¤Ï¡¢¾Êά²Äǽ¤Ê priority
+(\fBfatal\fR, \fBerror\fR, \fBinfo\fR, \fBdebug\fR ¤Î¤¤¤º¤ì¤«) ¤È¡¢
+¥Ç¡¼¥¿¼°¤ò¼è¤ê¤Þ¤¹¡£
+.PP
+.B log (\fIpriority\fB, \fIdata-expr\fB)\fR
+.\" "\FB" ¤Ï "\fB" ¤¬Àµ¤·¤¤
+.\" horikawa@jp.FreeBSD.org 2002/04/29
+.PP
+¥í¥°Ê¸¤Ï¡¢Ã±°ì¤Î¥Ç¡¼¥¿¼°°ú¿ô¤Î¤ß¼è¤ê¤Þ¤¹¡£
+Ê£¿ô¤Î¥Ç¡¼¥¿Ãͤò½ÐÎϤ·¤¿¤¤¾ì¹ç¡¢
+\fBconcat\fR ¥ª¥Ú¥ì¡¼¥¿¤ò»ÈÍѤ·¤Æ¤½¤ì¤é¤òÏ¢·ë¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+.RE
+.SH »²¾È: ưŪ¤Ê DNS ¹¹¿·
+.PP
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢
+ưŪ¤Ë¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò¹¹¿·¤¹¤ëǽÎϤ¬¤¢¤ê¤Þ¤¹¡£
+ÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ë¡¢¤É¤Î¤è¤¦¤Ë¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò¹¹¿·¤·¤ÆÍߤ·¤¤¤«¡¢
+ÄêµÁ²Äǽ¤Ç¤¹¡£
+¹¹¿·¤Ï RFC 2136 ¤Ë½¾¤Ã¤Æ¤¤¤ë¤¿¤á¡¢
+RFC 2136 ¤ò¥µ¥Ý¡¼¥È¤¹¤ë DNS ¥µ¡¼¥Ð¤Ï¡¢
+DHCP ¥µ¡¼¥Ð¤«¤é¤Î¹¹¿·¤ò¼õ¤±ÉÕ¤±²Äǽ¤È»×¤ï¤ì¤Þ¤¹¡£
+.SH ¥»¥­¥å¥ê¥Æ¥£
+TSIG ¤ª¤è¤Ó DNSSEC ¤Ï¤Þ¤À¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+DHCP ¥µ¡¼¥Ð¤Þ¤¿¤Ï¥¯¥é¥¤¥¢¥ó¥È¤«¤é¤Î¹¹¿·¤ò¼õ¤±ÉÕ¤±¤ë¤è¤¦¤Ë
+DNS ¥µ¡¼¥Ð¤òÀßÄꤹ¤ë¾ì¹ç¡¢¸¢¸Â¤Î̵¤¤¹¹¿·¤ËÂФ·¤Æ
+DNS ¥µ¡¼¥Ð¤ò»¯¤¹¤³¤È¤Ë¤Ê¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£
+¤³¤ì¤òÈò¤±¤ë¤¿¤á¤Ëº£¤¹¤°¤Ç¤­¤ëºÇÎɤÎÊýË¡¤Ï¡¢
+IP ¥¢¥É¥ì¥¹¥Ù¡¼¥¹¤Î¥Ñ¥±¥Ã¥È¥Õ¥£¥ë¥¿¤ò»ÈÍѤ·¤Æ¡¢
+¸¢¸Â¤Î̵¤¤¥Û¥¹¥È¤«¤é¤Î¹¹¿·Í×µáȯ¹Ô¤òÍ޻ߤ¹¤ë¤³¤È¤Ç¤¹¡£
+ÌÀ¤é¤«¤Ë¡¢¸½¾õ¤Ç¤Ï¥¯¥é¥¤¥¢¥ó¥È¤Î¹¹¿·¤ËÂФ¹¤ë¥»¥­¥å¥ê¥Æ¥£¤òÄ󶡤¹¤ëÊýË¡¤Ï
+¤¢¤ê¤Þ¤»¤ó¡£
+¤³¤Î¤¿¤á¤Ë¤Ï TSIG ¤« DNSSEC ¤¬É¬ÍפǤ¹¤¬¡¢
+¤³¤Î DHCP ÇÛÉÛʪ¤Ë¤Ï¤Þ¤À´Þ¤Þ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+.PP
+ưŪ DNS (DDNS) ¹¹¿·¤Ï¡¢\fBdns-update\fR ¼°¤ò»ÈÍѤ¹¤ë¤³¤È¤Ç¼Â¹Ô¤µ¤ì¤Þ¤¹¡£
+\fBdns-update\fR ¼°¤Ï¡¢¥Ö¡¼¥ë¼°¤Ç¤¢¤ê¡¢4 ¸Ä¤Î¥Ñ¥é¥á¡¼¥¿¤ò¼è¤ê¤Þ¤¹¡£
+¹¹¿·¤ËÀ®¸ù¤¹¤ë¤È¡¢·ë²Ì¤Ï¿¿¤Ë¤Ê¤ê¤Þ¤¹¡£
+¼ºÇÔ¤¹¤ë¤È¡¢·ë²Ì¤Ïµ¶¤Ë¤Ê¤ê¤Þ¤¹¡£
+4 ¸Ä¤Î¥Ñ¥é¥á¡¼¥¿¤Ï¡¢¥ê¥½¡¼¥¹¥ì¥³¡¼¥É¥¿¥¤¥× (RR)¡¢
+RR ¤Îº¸ÊÕ¡¢RR ¤Î±¦ÊÕ¡¢¥ì¥³¡¼¥É¤ËŬÍѤµ¤ì¤ë¤Ù¤­ ttl ¤Ç¤¹¡£
+¤³¤Î´Ø¿ô¤ÎºÇ¤â´Êñ¤Ê»ÈÍÑÎã¤Ï¡¢dhcpd.conf ¥Õ¥¡¥¤¥ë¤Î»²¾ÈÀá¤Ë¤¢¤ê¡¢
+¤Ê¤Ë¤¬µ¯¤­¤ë¤«µ­½Ò¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+¤³¤ÎÎã¤Ç¤Ï¡¢Ê£¿ô¤Î¼°¤¬»ÈÍѤµ¤ì¤Æ¡¢
+\fBdns-update\fR ÍѤΰú¿ô¤¬ºîÀ®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+.PP
+Îã¤ÎÃæ¤Ç¤Ï¡¢ºÇ½é¤Î \fBdns-update\fR ¼°¤Ø¤Î 1 ÈÖÌܤΰú¿ô¤Ï¡¢
+A RR ¥¿¥¤¥×¤Ëɾ²Á¤µ¤ì¤ë¥Ç¡¼¥¿¼°¤Ç¤¹¡£
+2 ÈÖÌܤΰú¿ô¤Ï¡¢DHCP host-name ¥ª¥×¥·¥ç¥ó¤È
+¥í¡¼¥«¥ë¥É¥á¥¤¥ó¡¢¤³¤Î¾ì¹ç "ssd.example.net"¡¢
+¤ò´Þ¤à¥Æ¥­¥¹¥È¥¹¥È¥ê¥ó¥°¤òÏ¢·ë¤¹¤ë¤³¤È¤Ç¡¢¹½ÃÛ¤µ¤ì¤Þ¤¹¡£
+3 ÈÖÌܤΰú¿ô¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥¢¥É¥ì¥¹¤ò¡¢
+32 ¥Ó¥Ã¥È¤Î¿ôÃͤ«¤é³Æ¥Ð¥¤¥È¤ò "." ¤Ç¶èÀڤä¿ ASCII ʸ»úÎó¤ËÊÑ´¹¤¹¤ë¤³¤È¤Ç¡¢
+¹½ÃÛ¤µ¤ì¤Þ¤¹¡£
+4 ÈÖÌܤΰú¿ô TTL ¤Ï¡¢¥ê¡¼¥¹¤Î»Ä¤ê»þ´Ö¤Ç¤¹
+(¤³¤ì¤ÏËÜÅö¤ÏÀµ¤·¤¯¤¢¤ê¤Þ¤»¤ó¡£
+¤Ê¤¼¤Ê¤é DNS ¥µ¡¼¥Ð¤Ï¡¢Í×µá¤ËÂФ·¤Æ¤¤¤Ä¤â¤³¤Î TTL Ãͤò½ÐÎϤ·¤Æ¤·¤Þ¤¦¤«¤é¤Ç¤¹¡£
+¤³¤ì¤Ï¡¢¥ê¡¼¥¹´ü¸ÂÀÚ¤ì¤Î¿ôÉÃÁ°¤Ç¤¢¤Ã¤Æ¤â¤Ç¤¹)¡£
+.PP
+ºÇ½é¤Î \fBdns-update\fR ʸ¤¬À®¸ù¤¹¤ë¤È¡¢
+°ú¤­Â³¤¤¤Æ 2 ÈÖÌܤι¹¿·¤Ë¤è¤ê PTR RR ¤¬¥¤¥ó¥¹¥È¡¼¥ë¤µ¤ì¤Þ¤¹¡£
+PTR ¥ì¥³¡¼¥É¤Î¥¤¥ó¥¹¥È¡¼¥ë¤Ï¡¢A RR ¤Î¥¤¥ó¥¹¥È¡¼¥ë¤ÈƱÍͤǤ¹¤¬¡¢
+¥ì¥³¡¼¥É¤Îº¸Êդϥ꡼¥¹¤µ¤ì¤¿¥¢¥É¥ì¥¹¤òµÕ¤Ë¤·¤Æ ".in-addr.arpa" ¤È
+·ë¹ç¤µ¤ì¤¿¤â¤Î¤Ç¤¹¡£
+±¦Êդϡ¢¥¢¥É¥ì¥¹¤Î¥ê¡¼¥¹Äó¶¡À襯¥é¥¤¥¢¥ó¥È¤Î¡¢´°Á´¤Ê·Á¤Ç¤Î¥É¥á¥¤¥ó̾¤Ç¤¹¡£
+.SH ´ØÏ¢¹àÌÜ
+dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8),
+dhclient(8), RFC2132, RFC2131
+.SH ºî¼Ô
+Internet Systems Consortium DHCP Distribution
+¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£
+ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢
+.B https://www.isc.org
+¤Ë¤¢¤ê¤Þ¤¹¡£
diff --git a/doc/ja_JP.eucJP/dhcp-options.5 b/doc/ja_JP.eucJP/dhcp-options.5
new file mode 100644
index 0000000..2eaeb61
--- /dev/null
+++ b/doc/ja_JP.eucJP/dhcp-options.5
@@ -0,0 +1,1582 @@
+.\" $Id: dhcp-options.5,v 1.3.24.3 2011-01-20 23:46:46 sar Exp $
+.\"
+.\" Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.\"
+.\" %FreeBSD: src/contrib/isc-dhcp/common/dhcp-options.5,v 1.2.2.1 2002/04/11 10:16:46 murray Exp %
+.\" $FreeBSD: doc/ja_JP.eucJP/man/man5/dhcp-options.5,v 1.11 2002/05/21 03:51:52 horikawa Exp $
+.\" WORD: Dynamic Host Configuration Protocol ưŪ¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë
+.\" WORD: Path MTU Discovery ¥Ñ¥¹ MTU õº÷
+.\" WORD: Router Discovery ¥ë¡¼¥¿Ãµº÷
+.\" WORD: Router Solicitation ¥ë¡¼¥¿Í×ÀÁ
+.\" WORD: Mask Discovery ¥Þ¥¹¥¯Ãµº÷
+.\"
+.TH dhcp-options 5
+.SH ̾¾Î
+dhcp-options - ưŪ¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë¤Î¥ª¥×¥·¥ç¥ó
+.SH ²òÀâ
+ưŪ¥Û¥¹¥È¹½À®¥×¥í¥È¥³¥ë (DHCP: Dynamic Host Configuration Protocol) ¤ò
+»ÈÍѤ¹¤ë¤³¤È¤Ë¤è¤ê¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï DHCP ¥µ¡¼¥Ð¤«¤é¡¢¥Í¥Ã¥È¥ï¡¼¥¯ÀßÄê¤ä
+¥Í¥Ã¥È¥ï¡¼¥¯¾å¤ÇÍøÍѲÄǽ¤ÊÍÍ¡¹¤Ê¥µ¡¼¥Ó¥¹¤Ë¤Ä¤¤¤Æµ­½Ò¤·¤Æ¤¤¤ë
+.B ¥ª¥×¥·¥ç¥ó
+¤ò¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.B dhcpd(8)
+¤ä
+.B dhclient(8)
+¤òÀßÄꤹ¤ë¤È¤­¤Ë¡¢¤·¤Ð¤·¤Ð¥ª¥×¥·¥ç¥ó¤òÀë¸À¤¹¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£
+¤³¤³¤Ç¤Ï¡¢¥ª¥×¥·¥ç¥ó¤òÀë¸À¤¹¤ëʸˡ¡¢
+¤½¤·¤ÆÀë¸À²Äǽ¤Ê¥ª¥×¥·¥ç¥ó¤Î̾Á°¤È½ñ¼°¤òʸ½ñ²½¤·¤Æ¤¤¤Þ¤¹¡£
+.SH ¥ê¥Õ¥¡¥ì¥ó¥¹: ¥ª¥×¥·¥ç¥óʸ
+.PP
+DHCP \fIoption\fR ʸ¤Ï¡¢¾ï¤Ë¥­¡¼¥ï¡¼¥É \fIoption\fR ¤Ç³«»Ï¤·¡¢
+ñ°ì¤Î¥ª¥×¥·¥ç¥ó̾¤¬Â³¤­¡¢¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤¬Â³¤­¤Þ¤¹¡£
+¥ª¥×¥·¥ç¥ó¤Î̾Á°¤È¥Ç¡¼¥¿¤Î½ñ¼°¤Ï¸å½Ò¤·¤Þ¤¹¡£
+¤¹¤Ù¤Æ¤Î DHCP ¥ª¥×¥·¥ç¥ó¤òÌÖÍåŪ¤Ë»ØÄꤹ¤ëɬÍפϤʤ¯¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤ËɬÍפʥª¥×¥·¥ç¥ó¤Î¤ß¤ò»ØÄꤷ¤Þ¤¹¡£
+.PP
+¥ª¥×¥·¥ç¥ó¥Ç¡¼¥¿¤Ë¤Ï¡¢¼¡¤Î¤è¤¦¤ËÍÍ¡¹¤Ê½ñ¼°¤¬¤¢¤ê¤Þ¤¹:
+.PP
+.B ip-address
+¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢ÌÀ¼¨Åª¤Ê IP ¥¢¥É¥ì¥¹ (Î㤨¤Ð 239.254.197.10) ¤Þ¤¿¤Ï
+¥É¥á¥¤¥ó̾ (Î㤨¤Ð haagen.isc.org) ¤Î¤É¤Á¤é¤Ç¤â»ØÄê²Äǽ¤Ç¤¹¡£
+¥É¥á¥¤¥ó̾¤Ç»ØÄꤹ¤ë¾ì¹ç¡¢
+¤½¤Î¥É¥á¥¤¥ó̾¤ò²ò·è¤¹¤ë¤Èñ°ì¤Î IP ¥¢¥É¥ì¥¹¤Ë¤Ê¤ë¤è¤¦¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+.B int32
+¥Ç¡¼¥¿¥¿¥¤¥×¤ÏÉä¹æÉÕ¤­ 32 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£
+.B uint32
+¥Ç¡¼¥¿¥¿¥¤¥×¤ÏÉä¹æ̵¤· 32 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£
+.B int16
+¤ª¤è¤Ó
+.B uint16
+¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢Éä¹æÉÕ¤­¤ª¤è¤ÓÉä¹æ̵¤·¤Î 16 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£
+.B int8
+¤ª¤è¤Ó
+.B uint8
+¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢Éä¹æÉÕ¤­¤ª¤è¤ÓÉä¹æ̵¤·¤Î 8 ¥Ó¥Ã¥ÈÀ°¿ô¤ò»ØÄꤷ¤Þ¤¹¡£
+Éä¹æ̵¤· 8 ¥Ó¥Ã¥ÈÀ°¿ô¤Ï¡¢¥ª¥¯¥Æ¥Ã¥È¤È¸Æ¤Ð¤ì¤ë¤³¤È¤â¤¢¤ê¤Þ¤¹¡£
+.PP
+.B text
+¥Ç¡¼¥¿¥¿¥¤¥×¤Ï NVT ASCII ʸ»úÎó¤ò»ØÄꤷ¤Þ¤¹¡£
+ʸ»úÎó¤Ï¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤ëɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+Î㤨¤Ð root-path ¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ëʸˡ¤Ï¡¢¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£
+.nf
+.sp 1
+option root-path "10.0.1.4:/var/tmp/rootfs";
+.fi
+.PP
+.B domain-name
+¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£
+ʸ»úÎó¤ò¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤Ã¤Æ¤¤¤±¤Þ¤»¤ó¡£
+¤³¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢Â¾¤Î´û¸¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ë¤Ï»È¤ï¤ì¤Þ¤»¤ó¡£
+¥É¥á¥¤¥ó̾¤Ï¡¢text ¥ª¥×¥·¥ç¥ó¤Ç¤¢¤ë¤«¤Î¤è¤¦¤ËÊÝ»ý¤µ¤ì¤Þ¤¹¡£
+.\" text ¥Ç¡¼¥¿¥¿¥¤¥×¤Ç¤¢¤ë¤«¤Î¤è¤¦¤Ë?
+.\" metal
+.PP
+.B flag
+¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¥Ö¡¼¥ëÃͤò»ØÄꤷ¤Þ¤¹¡£
+¥Ö¡¼¥ëÃÍ¤Ï true ¤Þ¤¿¤Ï false ¤Î¤¤¤º¤ì¤«¤Ç¤¹
+(¤â¤·¤¯¤Ï¡¢on ¤Þ¤¿¤Ï off ¤ÎÊý¤¬Ê¬¤«¤ê¤ä¤¹¤±¤ì¤Ð¡¢¤³¤Á¤é¤Ç¤â¤«¤Þ¤¤¤Þ¤»¤ó)¡£
+.PP
+.B string
+¥Ç¡¼¥¿¥¿¥¤¥×¤Ï¡¢¥À¥Ö¥ë¥¯¥©¡¼¥È¤Ç³ç¤é¤ì¤ë NVT ASCII ʸ»úÎ󤫡¢
+¥³¥í¥ó¶èÀÚ¤ê¤Î 16 ¿Ê¿ô¤Ç»ØÄꤵ¤ì¤ë¥ª¥¯¥Æ¥Ã¥È¤ÎϢ³¤Î¤¤¤º¤ì¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹:
+.nf
+.sp 1
+ option dhcp-client-identifier "CLIENT-FOO";
+¤â¤·¤¯¤Ï
+ option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
+.fi
+.SH ¼°¤òÍѤ¤¤¿¥ª¥×¥·¥ç¥óÃͤÎÀßÄê
+.\" metal
+¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤¹¤ë¤¤¤¯¤Ä¤«¤ÎÃͤò¡¢DHCP ¥ª¥×¥·¥ç¥ó¤ÎÃͤòÀßÄꤹ¤ë¤Î¤Ë
+»È¤¨¤ë¤ÈÊØÍø¤Ê¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£
+¤³¤ì¤ò¤¹¤ë¤Ë¤Ï¼°¤Îɾ²Á¤¬ÍøÍѤǤ­¤Þ¤¹¡£
+.B dhcp-eval(5)
+¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë¼°¤Î½ñ¤­Êý¤¬½Ò¤Ù¤é¤ì¤Æ¤¤¤Þ¤¹¡£
+ɾ²Á¤Î·ë²Ì¤ò¥ª¥×¥·¥ç¥ó¤ËÂåÆþ¤¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¤ò¼¡¤Î¤è¤¦¤ËÄêµÁ¤·¤Þ¤¹:
+.nf
+.sp 1
+ \fBoption \fImy-option \fB= \fIexpression \fB;\fR
+.fi
+.PP
+Î㤨¤Ð¼¡¤Î¤è¤¦¤Ë¤·¤Þ¤¹:
+.nf
+.sp 1
+ option hostname = binary-to-ascii (16, 8, "-",
+ substring (hardware, 1, 6));
+.fi
+.SH ɸ½à DHCP ¥ª¥×¥·¥ç¥ó
+¼¡¤Ë¼¨¤¹ÍÍ¡¹¤Ê¥ª¥×¥·¥ç¥ó¤Ë´Ø¤¹¤ëµ­½Ò¤Ï¡¢
+DHCP ¥ª¥×¥·¥ç¥ó¤Ë´Ø¤¹¤ëºÇ¿·¤Î IETF ¥É¥é¥Õ¥Èʸ½ñ¤«¤é¤Î¤â¤Î¤Ç¤¹¡£
+̾Á°¤¬·ÇºÜ¤µ¤ì¤Æ¤¤¤Ê¤¤¥ª¥×¥·¥ç¥ó¤Ï¡¢¤Þ¤À¼ÂÁõ¤µ¤ì¤Æ¤¤¤Ê¤¤¤«¤â¤·¤ì¤Þ¤»¤ó¤¬¡¢
+ÀßÄê¥Õ¥¡¥¤¥ë¤ËÄêµÁ¤¹¤ë¤³¤È¤Ç¡¢¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤ò»È¤¨¤ë¤«¤â¤·¤ì¤Þ¤»¤ó¡£
+¾Ü¤·¤¯¤Ï¡¢¤³¤ÎÀè¤Î¡Ö¿·µ¬¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ¡×¤«¤é³¤¯µ­½Ò¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+¤³¤³¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¥ª¥×¥·¥ç¥ó¤Î¤¦¤Á¤Î¤¤¤¯¤Ä¤«¤Ï¡¢DHCP ¥µ¡¼¥Ð¤â¤·¤¯¤Ï
+¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¼«Æ°Åª¤ËÀ¸À®¤µ¤ì¤ë¤â¤Î¤Ç¡¢¥æ¡¼¥¶¤Ë¤ÏÀßÄê¤Ç¤­¤Þ¤»¤ó¡£
+¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢¼õ¿®Â¦¤Î DHCP ¥×¥í¥È¥³¥ë¥¨¡¼¥¸¥§¥ó¥È
+(¥µ¡¼¥Ð¤â¤·¤¯¤Ï¥¯¥é¥¤¥¢¥ó¥È) ¤ÎÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Î¡¢Î㤨¤Ð¾ò·ï¼°¤Ê¤É¤Ç
+»È¤ï¤ì¤Þ¤¹¡£
+¤·¤«¤·¤³¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢Á÷¿®Â¦¤Î¥¨¡¼¥¸¥§¥ó¥È¤ÎÀßÄê¥Õ¥¡¥¤¥ëÃæ¤Ç¤Ï
+»È¤ï¤ì¤ë¤³¤È¤Ï¤¢¤ê¤Þ¤»¤ó¡£
+¤È¤¤¤¦¤Î¤â¡¢¤½¤ÎÃͤϡ¢ÀßÄê¥Õ¥¡¥¤¥ë¤¬½èÍý¤µ¤ì¤¿¸å¤Ë·èÄꤵ¤ì¤ë¤«¤é¤Ç¤¹¡£
+°Ê¹ß¤Îµ­½Ò¤Ë¤ª¤¤¤Æ¡¢¤½¤Î¤è¤¦¤Ê¥ª¥×¥·¥ç¥ó¤Ë¤Ï
+¡Ö¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡×¤Èµ­¤µ¤ì¤Þ¤¹¡£
+.PP
+ɸ½à¥ª¥×¥·¥ç¥ó¤ò¼¨¤·¤Þ¤¹:
+.PP
+.B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Àܳ¤µ¤ì¤Æ¤¤¤ë IP ¥Í¥Ã¥È¥ï¡¼¥¯¤ÎÁ´¥µ¥Ö¥Í¥Ã¥È¤¬
+»ÈÍѤ¹¤ë MTU ¤¬¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Ä¾ÀÜÀܳ¤µ¤ì¤Æ¤¤¤ë¥µ¥Ö¥Í¥Ã¥È¤Î MTU ¤È
+Ʊ¤¸¤Ç¤¢¤ë¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬²¾Äꤷ¤Æ¤è¤¤¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢Á´¥µ¥Ö¥Í¥Ã¥È¤ÏƱ°ì¤Î MTU ¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢Ä¾ÀÜÀܳ¤µ¤ì¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥µ¥Ö¥Í¥Ã¥È¤Ë¤Ï¡¢¤è¤ê¾®¤µ¤Ê MTU ¤ò
+»ý¤Ä¤â¤Î¤¬¤¢¤ë¤È¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬²¾Äꤹ¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ARP ¥­¥ã¥Ã¥·¥å¥¨¥ó¥È¥ê¤Î¥¿¥¤¥à¥¢¥¦¥È¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBbootfile-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢µ¯Æ°¥Õ¥¡¥¤¥ë¤ò»ØÄꤹ¤ë¤¿¤á¤Ë»ÈÍѤ·¤Þ¤¹¡£
+¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢
+¤³¤ì¤Ï \fBfilename\fR Àë¸À¤ÈƱ¤¸¸ú²Ì¤ò»ý¤Á¤Þ¤¹¡£
+BOOTP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤â¤Î¤Ï¾¯¤Ê¤¤¤Ç¤·¤ç¤¦¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¤Ï¥µ¥Ý¡¼¥È¤¹¤ë¤â¤Î¤¬¤¢¤ê¡¢
+¼ÂºÝɬ¿Ü¤È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBboot-size\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥǥե©¥ë¥È¤Î¥Ö¡¼¥È¥¤¥á¡¼¥¸¤ÎŤµ¤ò¡¢
+512 ¥ª¥¯¥Æ¥Ã¥È¥Ö¥í¥Ã¥¯¿ô¤Ç»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBbroadcast-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¤Ç»ÈÍѤµ¤ì¤Æ¤¤¤ë
+¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£
+ÀµÅö¤Ê¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¥¢¥É¥ì¥¹¤ÎÃͤϡ¢STD 3 (RFC1122) ¤Î 3.2.1.3 Àá¤Ë
+µ¬Äꤵ¤ì¤Æ¤¤¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+¥¯¥Ã¥­¡¼¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê
+RFC 865 ¥¯¥Ã¥­¡¼¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBdefault-ip-ttl\fR \fIuint8;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Ç¡¼¥¿¥°¥é¥à¤òÁ÷½Ð¤¹¤ë¤È¤­¤Ë»ÈÍѤ¹¤Ù¤­¡¢
+¥Ç¥Õ¥©¥ë¥È¤ÎÀ¸Â¸»þ´Ö (TTL) ¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ TCP ¥»¥°¥á¥ó¥È¤òÁ÷½Ð¤¹¤ë¤È¤­¤Ë»ÈÍѤ¹¤Ù¤­¡¢
+¥Ç¥Õ¥©¥ë¥È¤Î TTL ¤ò»ØÄꤷ¤Þ¤¹¡£
+ºÇ¾®ÃÍ¤Ï 1 ¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤ò»È¤Ã¤Æ¡¢¥Û¥¹¥ÈÀë¸ÀÃæ¤Ç DHCP ¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻Ҥò
+»ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤³¤Î¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻ҤǾȹç¤ò¹Ô¤¦¤³¤È¤Ç¡¢
+dhcpd ¤Ï¤½¤Î¥Û¥¹¥È¤Î¥ì¥³¡¼¥É¤òȯ¸«¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.PP
+.\" metal
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ÎÃæ¤Ë¤Ï¡¢ASCII ¥Æ¥­¥¹¥È¤Ë¤è¤Ã¤Æ¥¯¥é¥¤¥¢¥ó¥È¼±Ê̻Ҥ¬
+ÀßÄꤵ¤ì¤¿¾ì¹ç¡¢¤½¤Î ASCII ¥Æ¥­¥¹¥È¤ÎÀèƬ¤Ë 0 ¤ò¤Ä¤±¤ë¤â¤Î¤¬¤¢¤ë¤³¤È¤Ë
+Ãí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤½¤Î¾ì¹ç¡¢
+.nf
+
+ option dhcp-client-identifier "foo";
+
+¤Ç¤Ï¤Ê¤¯¡¢°Ê²¼¤Î¤è¤¦¤Ëµ­½Ò¤¹¤ëɬÍפ¬¤¢¤ë¤Ç¤·¤ç¤¦¡£
+
+ option dhcp-client-identifier "\\0foo";
+.fi
+.RE
+.PP
+.B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥ÈÍ×µá (DHCPDISCOVER ¤Þ¤¿¤Ï DHCPREQUEST) ¤ÎÃæ¤Ç¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ IP ¥¢¥É¥ì¥¹¤Î¥ê¡¼¥¹»þ´Ö¤òÍ׵᤹¤ë¤¿¤á¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+¤Þ¤¿¥µ¡¼¥Ð±þÅú (DHCPOFFER) ¤ÎÃæ¤Ç¡¢DHCP ¥µ¡¼¥Ð¤¬Ä󼨤·¤¿¤¤¥ê¡¼¥¹»þ´Ö¤ò
+»ØÄꤹ¤ë¤Î¤Ë¤â¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï»È¤ï¤ì¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥Ð¤Ç¤Ï¥æ¡¼¥¶¤¬Ä¾ÀÜÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.B dhcpd.conf(5)
+¤Î \fImax-lease-time\fR ¤È \fidefault-lease-time\fR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤ò
+»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBdhcp-max-message-size\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤¬¥¯¥é¥¤¥¢¥ó¥È¤Ë
+Á÷½Ð¤¹¤ë¤¹¤Ù¤Æ¤Î±þÅú¤ÎºÇÂ祵¥¤¥º¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤ÇÀßÄꤵ¤ì¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ dhcp-max-message-size ¥ª¥×¥·¥ç¥ó¤ò
+Á÷¿®¤·¤Æ¤³¤Ê¤«¤Ã¤¿ºÝ¤Ë¡¢¤³¤Î¥µ¡¼¥Ð¤ÇÀßÄꤵ¤ì¤¿Ãͤ¬»ÈÍѤµ¤ì¤Þ¤¹¡£
+¤³¤ì¤Ï¡¢BOOTP ±þÅú¤Ç¤â DHCP ±þÅú¤ÈƱÍͤËÆ°ºî¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBdhcp-message\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¾ã³²¤¬µ¯¤­¤¿»þ¤Ë¡¢DHCP ¥µ¡¼¥Ð¤¬ DHCPNAK ¥á¥Ã¥»¡¼¥¸Ãæ¤Ç
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ø¥¨¥é¡¼¥á¥Ã¥»¡¼¥¸¤òÄ󶡤¹¤ë¤Î¤Ë»ÈÍѤ·¤Þ¤¹¡£
+¤Þ¤¿¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Ä󼨤µ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¤òµñÈݤ·¤¿Íýͳ¤ò¼¨¤¹¤¿¤á¤Ë¡¢
+DHCPDECLINE ¥á¥Ã¥»¡¼¥¸Ãæ¤ÇËÜ¥ª¥×¥·¥ç¥ó¤ò»È¤¦¤³¤È¤â¤¢¤ê¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.RE
+.PP
+.B option \fBdhcp-message-type\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Îξ¼Ô¤«¤éÁ÷½Ð¤µ¤ì¡¢
+DHCP ¥Ñ¥±¥Ã¥È¤¬´Þ¤ó¤Ç¤¤¤ë DHCP ¥á¥Ã¥»¡¼¥¸¤Î¥¿¥¤¥×¤ò»ØÄꤷ¤Þ¤¹¡£
+ËÜ¥ª¥×¥·¥ç¥ó¤¬¼è¤êÆÀ¤ëÃͤϡ¢°Ê²¼¤Î¤È¤ª¤ê¤Ç¤¹ (RFC2132 ¤è¤ê¤½¤Î¤Þ¤ÞÈ´¿è)¡£
+.PP
+.nf
+ 1 DHCPDISCOVER
+ 2 DHCPOFFER
+ 3 DHCPREQUEST
+ 4 DHCPDECLINE
+ 5 DHCPACK
+ 6 DHCPNAK
+ 7 DHCPRELEASE
+ 8 DHCPINFORM
+.fi
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.B option \fBdhcp-option-overload\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢DHCP 'sname' ¤â¤·¤¯¤Ï 'file' ¥Õ¥£¡¼¥ë¥É¤¬¡¢
+DHCP ¥ª¥×¥·¥ç¥ó¤òÊÝ»ý¤¹¤ë¤¿¤á¤ËµÍ¤á¹þ¤ß²á¤®¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤³¤È¤ò
+¼¨¤¹¤Î¤Ë»È¤ï¤ì¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ð¤Ï¡¢ÊֵѤµ¤ì¤¿¥Ñ¥é¥á¡¼¥¿¤¬¡¢¥ª¥×¥·¥ç¥ó¤ËÄ̾ï³ä¤êÅö¤Æ¤é¤ì¤¿
+¶õ´Ö¤òĶ²á¤·¤¿¾ì¹ç¡¢ËÜ¥ª¥×¥·¥ç¥ó¤òÁÞÆþ¤·¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤¬Â¸ºß¤·¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢É¸½à¤Î¥ª¥×¥·¥ç¥ó¥Õ¥£¡¼¥ë¥É¤Î
+²ò¼á¤¬½ªÎ»¤·¤¿¸å¡¢»ØÄꤵ¤ì¤¿Éղåե£¡¼¥ë¥É¤Î²ò¼á¤ò¹Ô¤¤¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤ÎÀµÅö¤ÊÃͤϡ¢°Ê²¼¤ÎÄ̤ê¤Ç¤¹:
+.PP
+.nf
+ 1 'file' ¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹
+ 2 'sname' ¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹
+ 3 ξÊý¤Î¥Õ¥£¡¼¥ë¥É¤¬¡¢¥ª¥×¥·¥ç¥óÊÝ»ý¤Ë»ÈÍѤµ¤ì¤Æ¤Þ¤¹
+.fi
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.PP
+.B option \fBdhcp-parameter-request-list\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢
+¥µ¡¼¥Ð¤ËÊÖÅú¤ò´õ˾¤¹¤ë¥ª¥×¥·¥ç¥ó¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬»ØÄꤷ¤Þ¤¹¡£
+Ä̾ï ISC DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¡¢\fIrequest\fR ʸ¤òÍѤ¤¤Æ¹Ô¤ï¤ì¤Þ¤¹¡£
+ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤é»ØÄꤵ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¡¢Ä̾ï DHCP ¥µ¡¼¥Ð¤Ï¡¢
+¥¹¥³¡¼¥×Æâ¤ÇÍ­¸ú¤«¤Ä±þÅú¤Ë¼ý¤Þ¤ë¤¹¤Ù¤Æ¤Î¥ª¥×¥·¥ç¥ó¤òÊÖ¤·¤Þ¤¹¡£
+ËÜ¥ª¥×¥·¥ç¥ó¤¬¥µ¡¼¥Ð¾å¤Ç»ØÄꤵ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤Ï¤½¤Î»ØÄꤵ¤ì¤¿¥ª¥×¥·¥ç¥ó¤ò
+ÊÖ¤·¤Þ¤¹¡£
+¤³¤ì¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬Í׵ᤷ¤Ê¤«¤Ã¤¿¥ª¥×¥·¥ç¥ó¤ò¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ë
+¶¯À©¤¹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+¤Þ¤¿¡¢Ä̾掠¡¼¥Ð¤¬ÊÖ¤¹¥ª¥×¥·¥ç¥ó¤Î¥»¥Ã¥È¤ò¤µ¤é¤ËÀ©¸Â¤¹¤ëɬÍפΤ¢¤ë
+¥¯¥é¥¤¥¢¥ó¥È¤ËÂФ·¤Æ¡¢DHCP ¥µ¡¼¥Ð¤Î±þÅú¤òÄ´À°¤¹¤ë¤Î¤Ë¤â»ÈÍѤµ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é REBINDING ¾õÂÖ¤Ë
+°Ü¹Ô¤¹¤ë¤Þ¤Ç¤Î»þ´Ö¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.PP
+.B option \fBdhcp-renewal-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¢¥É¥ì¥¹¤ò¼èÆÀ¤·¤Æ¤«¤é RENEWING ¾õÂÖ¤Ë
+°Ü¹Ô¤¹¤ë¤Þ¤Ç¤Î»þ´Ö¤òÉÿô¤Ç»ØÄꤷ¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.PP
+.B option \fBdhcp-requested-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢DHCPDISCOVER Æâ¤ÇÆÃÄê¤Î IP ¥¢¥É¥ì¥¹¤¬
+³ä¤êÅö¤Æ¤é¤ì¤ë¤³¤È¤òÍ׵᤹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.PP
+.B option \fBdhcp-server-identifier\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢DHCPOFFER ¤È DHCPREQUEST ¥á¥Ã¥»¡¼¥¸Ãæ¤Ç»ÈÍѤµ¤ì¡¢
+¤Þ¤¿ DHCPACK ¤È DHCPNAK ¥á¥Ã¥»¡¼¥¸Ãæ¤Ë¤â´Þ¤Þ¤ì¤ë¤³¤È¤¬¤¢¤ê¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ð¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ (ÌõÃí: Ê£¿ô¥µ¡¼¥Ð¤«¤é¤Î) ¥ê¡¼¥¹¤ÎÄ󼨤ò
+¶èÊ̤Ǥ­¤ë¤è¤¦¡¢DHCPOFFER ¤ËËÜ¥ª¥×¥·¥ç¥ó¤ò´Þ¤á¤Þ¤¹¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Ø¥æ¥Ë¥­¥ã¥¹¥È¤¹¤ë¤¹¤Ù¤Æ¤Î DHCP ¥á¥Ã¥»¡¼¥¸¤Î
+°¸À襢¥É¥ì¥¹¤È¤·¤Æ 'server identifier' ¥Õ¥£¡¼¥ë¥É¤ÎÆâÍƤò»ÈÍѤ·¤Þ¤¹¡£
+¤Þ¤¿ DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢DHCPREQUEST ¥á¥Ã¥»¡¼¥¸Ãæ¤ËËÜ¥ª¥×¥·¥ç¥ó¤ò´Þ¤á¡¢
+Ê£¿ô¤Î¥ê¡¼¥¹¤ÎÄ󼨤Τɤì¤ò¼õ¤±Æþ¤ì¤¿¤«¤ò¼¨¤·¤Þ¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬Ä¾ÀÜÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.B \fIdhcpd.conf(5)
+¤Î \fIserver-identifier\fR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+.RE
+.PP
+.B option \fBdomain-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à¤ò»ÈÍѤ·¤Æ¥Û¥¹¥È̾¤ò²ò·è¤¹¤ë¤È¤­¤Ë
+¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBdomain-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+domain-name-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê
+¥É¥á¥¤¥ó¥Í¡¼¥à¥·¥¹¥Æ¥à (STD 13, RFC 1035) ¤Î¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBextensions-path\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Äɲ媥ץ·¥ç¥ó¤ò´Þ¤à¥Õ¥¡¥¤¥ë¤Î¥Õ¥¡¥¤¥ë̾¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤ÎÄɲ媥ץ·¥ç¥ó¤Ï¡¢RFC2132 ¤Çµ¬Äꤵ¤ì¤Æ¤¤¤ë DHCP ¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤Ë±è¤Ã¤Æ
+²ò¼á¤µ¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBfinger-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+Finger ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê Finger ¤Î¥ê¥¹¥È¤ò
+»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê X Window System ¥Õ¥©¥ó¥È¥µ¡¼¥Ð¤ò
+»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBhost-name\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤Î̾Á°¤Ï¡¢¥í¡¼¥«¥ë¥É¥á¥¤¥ó̾¤Ë½¤¾þ¤µ¤ì¤Æ¤¤¤Æ¤â¡¢¤¤¤Ê¤¯¤Æ¤â¤«¤Þ¤¤¤»¤ó
+(¥É¥á¥¤¥ó̾¤ò»ØÄꤹ¤ë¤Ë¤Ï¡¢domain-name ¥ª¥×¥·¥ç¥ó¤Î»ÈÍѤò¤ª´«¤á¤·¤Þ¤¹)¡£
+ʸ»ú½¸¹ç¤ÎÀ©Ìó¤Ë¤Ä¤¤¤Æ¤Ï RFC 1035 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+¥¯¥é¥¤¥¢¥ó¥È¥Þ¥·¥ó¤Î¥Û¥¹¥È̾¤¬ÀßÄꤵ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç (¤¹¤Ê¤ï¤Á
+.B rc.conf(5)
+¤Ç¶õʸ»úÎó¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç) ¤Î¤ß¡¢
+.B dhclient-script(8)
+¤¬ËÜ¥ª¥×¥·¥ç¥ó¤òº½Å¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤¬¥¤¡¼¥µ¥Í¥Ã¥È¤Ç¤¢¤ë¾ì¹ç¤Ë¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬¥¤¡¼¥µ¥Í¥Ã¥È¥Ð¡¼¥¸¥ç¥ó 2 (RFC 894) ¤È
+IEEE 802.3 (RFC 1042) ¤Î¤É¤Á¤é¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ RFC 894 ¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò
+°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ RFC 1042 ¤Î¥«¥×¥»¥ë²½¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò
+°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+];
+.RS 0.25i
+.PP
+ien116-name-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê IEN 116 ¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+impress-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê Imagen Impress ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBinterface-mtu\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¤³¤Î¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤ËÂФ·¤Æ»ÈÍѤ¹¤ë MTU ¤ò»ØÄꤷ¤Þ¤¹¡£
+MTU ¤ËÂФ¹¤ëºÇ¾®¤ÎÀµÅöÃÍ¤Ï 68 ¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBip-forwarding\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢¥Ñ¥±¥Ã¥È¤òžÁ÷¤¹¤ë¤è¤¦¤Ë
+¼«Ê¬¤Î IP ÁؤòÀßÄꤹ¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï IP žÁ÷¤ò̵¸ú¤Ë¤¹¤ë¤³¤È¤ò°ÕÌ£¤·¡¢
+ÃÍ true ¤Ï IP žÁ÷¤òÍ­¸ú¤Ë¤¹¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBirc-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+IRC ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê IRC ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+log-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê MIT-LCS UDP ¥í¥°¥µ¡¼¥Ð¤Î
+¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+LPR ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 1179 ¥é¥¤¥ó¥×¥ê¥ó¥¿¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBmask-supplier\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ICMP ¤ò»ÈÍѤ·¤¿¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯Í×µá¤ËÂФ·¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬±þÅú¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ºÆÁȤßΩ¤Æ¤Î½àÈ÷¤ò¤¹¤Ù¤­
+ºÇÂç¥Ç¡¼¥¿¥°¥é¥à¥µ¥¤¥º¤ò»ØÄꤷ¤Þ¤¹¡£
+ºÇ¾®¤ÎÀµÅöÃÍ¤Ï 576 ¤Ç¤¹¡£
+.\" The minimum value legal value is 576.
+.\" The minimum legal value is 576. ¤«¤Ê (horikawa@jp.freebsd.org 19990404)
+.RE
+.PP
+.B option \fBmerit-dump\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥¯¥é¥Ã¥·¥å¤¹¤ë¤È¤­¤Î
+¥¯¥é¥¤¥¢¥ó¥È¤Î¥³¥¢¥¤¥á¡¼¥¸¤¬¥À¥ó¥×¤µ¤ì¤ë¥Õ¥¡¥¤¥ë¤Î¥Ñ¥¹Ì¾¤ò»ØÄꤷ¤Þ¤¹¡£
+¥Ñ¥¹¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê¥â¥Ð¥¤¥ë IP ¥Û¡¼¥à¥¨¡¼¥¸¥§¥ó¥È¤Î
+IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤¿¤À¤·¡¢Ä̾泌¡¼¥¸¥§¥ó¥È¤Ï 1 ¤Ä¤Ç¤·¤ç¤¦¡£
+.RE
+.PP
+.B option \fBnds-context\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+nds-context ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥¯¥é¥¤¥¢¥ó¥È¤Î¤¿¤á¤ÎºÇ½é¤Î
+NetWare ¥Ç¥£¥ì¥¯¥È¥ê¥µ¡¼¥Ó¥¹¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+nds-servers ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+nds-tree-name ¥ª¥×¥·¥ç¥ó¤Ï¡¢NDS ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­ NDS ¥Ä¥ê¡¼¤Î
+̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+NetBIOS ¥Ç¡¼¥¿¥°¥é¥àÇÛÉÛ¥µ¡¼¥Ð (NBDD) ¥ª¥×¥·¥ç¥ó¤Ï¡¢
+RFC 1001/1002 ¤Î NBDD ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...]\fB;\fR
+.RS 0.25i
+.PP
+NetBIOS ¥Í¡¼¥à¥µ¡¼¥Ð (NBNS) ¥ª¥×¥·¥ç¥ó¤Ï¡¢ RFC 1001/1002 ¤Î
+NBNS ¥Í¡¼¥à¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë»ØÄꤷ¤Þ¤¹¡£
+¸½ºß¤Ç¤Ï¡¢NetBIOS ¥Í¡¼¥à¥µ¡¼¥Ó¥¹¤Ï WINS ¤È¸Æ¤Ð¤ì¤ë¤³¤È¤ÎÊý¤¬Â¿¤¤¤Ç¤¹¡£
+netbios-name-servers ¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤Æ¡¢WINS ¥µ¡¼¥Ð¤ò»ØÄê²Äǽ¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+NetBIOS ¥Î¡¼¥É¥¿¥¤¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢
+ÀßÄê²Äǽ¤Ê NetBIOS over TCP/IP ¥¯¥é¥¤¥¢¥ó¥È¤ò¡¢
+RFC 1001/1002 ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¤è¤¦¤ËÀßÄꤷ¤Þ¤¹¡£
+ÃͤÏñ°ì¤Î¥ª¥¯¥Æ¥Ã¥È¤È¤·¤Æ»ØÄꤵ¤ì¡¢¥¯¥é¥¤¥¢¥ó¥È¥¿¥¤¥×¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.PP
+»ÈÍѲÄǽ¤Ê¥Î¡¼¥É¥¿¥¤¥×¤Ï¼¡¤ÎÄ̤ê¤Ç¤¹:
+.PP
+.TP 5
+.I 1
+B ¥Î¡¼¥É: ¥Ö¥í¡¼¥É¥­¥ã¥¹¥È - WINS ̵¤·
+.TP
+.I 2
+P ¥Î¡¼¥É: ¥Ô¥¢ - WINS ¤Î¤ß
+.TP
+.I 4
+M ¥Î¡¼¥É: ¥ß¥Ã¥¯¥¹ - ¥Ö¥í¡¼¥É¥­¥ã¥¹¥È¸å¤Ë WINS
+.TP
+.I 8
+H ¥Î¡¼¥É: ¥Ï¥¤¥Ö¥ê¥Ã¥É - WINS ¸å¤Ë¥Ö¥í¡¼¥É¥­¥ã¥¹¥È
+.RE
+.PP
+.B option \fBnetbios-scope\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+NetBIOS ¥¹¥³¡¼¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1001/1002 ¤Ëµ¬Äꤵ¤ì¤Æ¤¤¤ë¤è¤¦¤Ë¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Î NetBIOS over TCP/IP ¥¹¥³¡¼¥×¥Ñ¥é¥á¡¼¥¿¤ò»ØÄꤷ¤Þ¤¹¡£
+ʸ»ú½¸¹ç¤ÎÀ©Ìó¤Ë¤Ä¤¤¤Æ¤Ï RFC1001, RFC1002, RFC1035 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBnis-domain\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î NIS (Sun Network Information Services)
+¥É¥á¥¤¥ó¤ò»ØÄꤷ¤Þ¤¹¡£
+¥É¥á¥¤¥ó¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NIS ¥µ¡¼¥Ð¤ò¼¨¤¹ IP ¥¢¥É¥ì¥¹¤Î
+¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBnisplus-domain\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î NIS+ ¥É¥á¥¤¥ó¤Î̾Á°¤ò»ØÄꤷ¤Þ¤¹¡£
+¥É¥á¥¤¥ó¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NIS+ ¥µ¡¼¥Ð¤ò¼¨¤¹ IP ¥¢¥É¥ì¥¹¤Î
+¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+NNTP ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NNTP ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBnon-local-source-routing\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Èó¥í¡¼¥«¥ë¤Ê»ØÄê·ÐÏ© (non-local source route) ¤ò»ý¤Ä
+¥Ç¡¼¥¿¥°¥é¥à¤òžÁ÷¤¹¤ë¤è¤¦¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¼«Ê¬¤Î IP ÁؤòÀßÄꤹ¤Ù¤­¤«¤ò
+»ØÄꤷ¤Þ¤¹ (ËܹàÌܤˤĤ¤¤Æ¤Ï [4] ¤Î 3.3.5 Àá¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤)¡£
+ÃÍ false ¤Ï¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¥°¥é¥à¤ÎžÁ÷¤òµö²Ä¤·¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¡¢
+ÃÍ true ¤ÏžÁ÷µö²Ä¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê NTP (RFC 1035) ¥µ¡¼¥Ð¤ò¼¨¤¹
+IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBnwip-domain\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­ NetWare/IP ¥É¥á¥¤¥ó¤Î̾Á°¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+NetWare/IP ¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥµ¥Ö¥ª¥×¥·¥ç¥ó¤Î¥·¡¼¥±¥ó¥¹¤Ç¤¹¡£
+¾Ü¤·¤¯¤Ï RFC2242 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+Ä̾ËÜ¥ª¥×¥·¥ç¥ó¤ÏÆÃÄê¤Î NetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò»ØÄꤹ¤ë¤³¤È¤Ç
+ÀßÄꤵ¤ì¤Þ¤¹¡£
+¤µ¤é¤Ê¤ë¾ðÊó¤Ï¡ÖNetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó¡×¤Î¾Ï¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBpath-mtu-aging-timeout\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1191 ¤ÇÄêµÁ¤µ¤ì¤ëµ¡¹½¤Çȯ¸«¤µ¤ì¤¿¥Ñ¥¹ MTU ÃͤÎ
+¥¨¡¼¥¸¥ó¥°¤Ë»ÈÍѤ¹¤ë¥¿¥¤¥à¥¢¥¦¥È (ÉÃñ°Ì) ¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBpath-mtu-plateau-table\fR \fIuint16\fR [\fB,\fR \fIuint16\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1191 ¤ÇÄêµÁ¤µ¤ì¤ë¥Ñ¥¹ MTU õº÷ (Path MTU Discovery)
+¼Â»Ü»þ¤Ë»ÈÍѤµ¤ì¤ë MTU ¤Î¥µ¥¤¥º¤Îɽ¤ò»ØÄꤷ¤Þ¤¹¡£
+ɽ¤Î½ñ¼°¤Ï¡¢ºÇ¾®¤«¤é½ç¤ËºÇÂç¤Þ¤Ç¤Î¡¢16 ¥Ó¥Ã¥ÈÉä¹æ̵¤·À°¿ô¤Î¥ê¥¹¥È¤Ç¤¹¡£
+ºÇ¾® MTU ¤Ï 68 ¤è¤ê¾®¤µ¤¯¤Æ¤Ï¤Ê¤ê¤Þ¤»¤ó¡£
+.RE
+.PP
+.B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ ICMP ¤ò»ÈÍѤ·¤Æ¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯Ãµº÷¤ò
+¼Â»Ü¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Þ¥¹¥¯Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥Þ¥¹¥¯Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.nf
+.B option \fBpolicy-filter\fR \fIip-address ip-address\fR
+ [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR
+.RE
+.fi
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Èó¥í¡¼¥«¥ë¤Ê»ØÄê·ÐÏ©À©¸æ¤ËÂФ¹¤ë¥Ý¥ê¥·¥Õ¥£¥ë¥¿¤ò»ØÄꤷ¤Þ¤¹¡£
+¥Õ¥£¥ë¥¿¤Ï¡¢IP ¥¢¥É¥ì¥¹¤È¥Þ¥¹¥¯¤ÎÁȤΥꥹ¥È¤«¤é¤Ê¤ê¡¢
+ÅþÃ夹¤ë»ØÄê·ÐÏ©À©¸æ¤µ¤ì¤¿¥Ç¡¼¥¿¥°¥é¥àÍѤΥե£¥ë¥¿¤È¤Ê¤ë
+°¸Àè/¥Þ¥¹¥¯¤ÎÁȤò»ØÄꤷ¤Þ¤¹¡£
+.PP
+¼¡¥Û¥Ã¥×¥¢¥É¥ì¥¹¤¬¥Õ¥£¥ë¥¿¤Î¤¤¤º¤ì¤Ë¤âŬ¹ç¤·¤Ê¤¤»ØÄê·ÐÏ©À©¸æ¤µ¤ì¤¿
+¥Ç¡¼¥¿¥°¥é¥à¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÇË´þ¤¹¤Ù¤­¤Ç¤¹¡£
+.PP
+¤µ¤é¤Ê¤ë¾ðÊó¤Ï STD 3 (RFC1122) ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBpop-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+POP3 ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê POP3 ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBresource-location-servers\fR \fIip-address\fR
+ [\fB, \fR\fIip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê
+RFC 887 ¥ê¥½¡¼¥¹¥í¥±¡¼¥·¥ç¥ó¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBroot-path\fR \fItext\fB;\fR\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ë¡¼¥È¥Ç¥£¥¹¥¯¤¬´Þ¤Þ¤ì¤ë¥Ñ¥¹Ì¾¤ò»ØÄꤷ¤Þ¤¹¡£
+¥Ñ¥¹¤Î½ñ¼°¤Ï¡¢NVT ASCII ʸ»ú½¸¹ç¤Îʸ»ú¤«¤é¤Ê¤ëʸ»úÎó¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBrouter-discovery\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 1256 ¤ÇÄêµÁ¤µ¤ì¤ë¥ë¡¼¥¿Ãµº÷ (Router Discovery) µ¡¹½¤ò
+»ÈÍѤ·¤Æ¡¢¥ë¡¼¥¿¤òÍ×ÀÁ¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥ë¡¼¥¿Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¥ë¡¼¥¿Ãµº÷¤ò¼Â»Ü¤¹¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBrouter-solicitation-address\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥ë¡¼¥¿Í×ÀÁ¤ÎÁ÷½ÐÀ襢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option routers \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+routers ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¾å¤Ë¤¢¤ë¥ë¡¼¥¿¤Î
+IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥ë¡¼¥¿¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option slp-directory-agent \fIboolean ip-address
+[\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢2 ¤Ä¤Î¹àÌܤò»ØÄꤷ¤Þ¤¹:
+1 ¤Ä°Ê¾å¤Î¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È
+(Service Location Protocol Directory Agent) ¤Î IP ¥¢¥É¥ì¥¹¤È¡¢
+¤³¤ì¤é¤Î¥¢¥É¥ì¥¹¤Î»ÈÍѤ¬¶¯À©Åª¤«¤É¤¦¤«¤Ç¤¹¡£
+ºÇ½é¤Î¥Ö¡¼¥ëÃͤ¬ true ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢¤¿¤ÀÍ¿¤¨¤é¤ì¤¿
+IP ¥¢¥É¥ì¥¹¤Î¤ß¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£
+Ãͤ¬ false ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Î
+ǽưŪ¤â¤·¤¯¤Ï¼õưŪ¤Ê¥Þ¥ë¥Á¥­¥ã¥¹¥Èõº÷¤òÄɲäǹԤäƤ⹽¤¤¤Þ¤»¤ó
+(¾Ü¤·¤¯¤Ï RFC2165 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤)¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤È slp-service-scope ¥ª¥×¥·¥ç¥ó¤Ë¤ª¤¤¤Æ¡¢
+¡ÖSLP ¥¨¡¼¥¸¥§¥ó¥È¡×¤È¤Ï¡¢DHCP ¥×¥í¥È¥³¥ë¤òÍѤ¤¤ÆÀßÄꤵ¤ì¤¿¥Þ¥·¥ó¾å¤Ç
+Æ°ºî¤·¤Æ¤¤¤ë¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¥¨¡¼¥¸¥§¥ó¥È¤ò»Ø¤·¤Æ¤¤¤ë¤³¤È¤Ë
+Ãí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£
+.PP
+¤Þ¤¿¡¢¤¤¤¯¤Ä¤«¤Î´ë¶È¤Ï SLP ¤ò NDS ¤È¸Æ¤ó¤Ç¤¤¤ë¤³¤È¤âµ¤¤òÉÕ¤±¤Æ¤¯¤À¤µ¤¤¡£
+¤â¤· NDS ¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È¤¬¤¢¤ê¡¢¤½¤Î¥¢¥É¥ì¥¹¤òÀßÄꤹ¤ëɬÍפ¬
+¤¢¤ë¾ì¹ç¤Ï¡¢ slp-directory-agent ¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¤Ï¤º¤Ç¤¹¡£
+.RE
+.PP
+.B option slp-service-scope \fIboolean text\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+¥µ¡¼¥Ó¥¹¥í¥±¡¼¥·¥ç¥ó¥×¥í¥È¥³¥ë¤Î¥µ¡¼¥Ó¥¹¥¹¥³¡¼¥×¥ª¥×¥·¥ç¥ó¤Ï¡¢
+2 ¤Ä¤Î¹àÌܤò»ØÄꤷ¤Þ¤¹:
+SLP ÍѤΥµ¡¼¥Ó¥¹¥¹¥³¡¼¥×¤Î¥ê¥¹¥È¤È¡¢¤³¤Î¥ê¥¹¥È¤Î»ÈÍѤ¬¶¯À©Åª¤«¤É¤¦¤«¤Ç¤¹¡£
+ºÇ½é¤Î¥Ö¡¼¥ëÃͤ¬ true ¤Ç¤¢¤ì¤Ð¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ë¤è¤ê
+Ä󶡤µ¤ì¤ë¥¹¥³¡¼¥×¤Î¥ê¥¹¥È¤Î¤ß¤ò»ÈÍѤ¹¤Ù¤­¤Ç¤¹¡£
+¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤ÇÄ󶡤µ¤ì¤ë¥ê¥¹¥È¤ËÍ¥À褷¤Æ¡¢
+¤½¤ì¤¾¤ì¤Î¸ÇͭŪ¤ÎÀßÄê¤ò»È¤Ã¤Æ¤â¹½¤¤¤Þ¤»¤ó¡£
+.PP
+text ʸ»úÎó¤Ï¡¢SLP ¥¨¡¼¥¸¥§¥ó¥È¤¬»ÈÍѤ¹¤Ù¤­¥¹¥³¡¼¥×¤Î¡¢¥³¥ó¥Þ¶èÀÚ¤ê¤Î
+¥ê¥¹¥È¤È¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤³¤ì¤Ï¾Êά²Äǽ¤Ç¡¢¤½¤Î¾ì¹ç SLP ¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢¼«Ê¬¤¬ÃΤäƤ¤¤ë
+¤¹¤Ù¤Æ¤Î¥Ç¥£¥ì¥¯¥È¥ê¥¨¡¼¥¸¥§¥ó¥È¤Î¥¹¥³¡¼¥×¤Î°ì³ç¥ê¥¹¥È¤ò»È¤¤¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBsmtp-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+SMTP ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê SMTP ¥µ¡¼¥Ð¤Î¥ê¥¹¥È¤ò
+»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.nf
+.B option \fBstatic-routes\fR \fIip-address ip-address\fR
+ [\fB,\fR \fIip-address ip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬·ÐÏ©¥­¥ã¥Ã¥·¥å¤ËÁȤ߹þ¤à¤Ù¤­
+ÀÅŪ·ÐÏ©¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+Ʊ¤¸°¸Àè¤ËÂФ·¤ÆÊ£¿ô¤Î·ÐÏ©¤¬»ØÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢
+Í¥ÀèÅÙ¤¬Ä㤯¤Ê¤ë½ç½ø¤Ç¥ê¥¹¥È¤µ¤ì¤Þ¤¹¡£
+.PP
+·ÐÏ©¤Ï IP ¥¢¥É¥ì¥¹¤ÎÁȤΥꥹ¥È¤«¤é¤Ê¤ê¤Þ¤¹¡£
+ºÇ½é¤Î¥¢¥É¥ì¥¹¤Ï°¸À襢¥É¥ì¥¹¤Ç¤¢¤ê¡¢
+2 ÈÖÌܤΥ¢¥É¥ì¥¹¤Ï¤½¤Î°¸Àè¤ËÂФ¹¤ë¥ë¡¼¥¿¤Î¥¢¥É¥ì¥¹¤Ç¤¹¡£
+.PP
+¥Ç¥Õ¥©¥ë¥È·ÐÏ© (0.0.0.0) ¤Ï¡¢ÀÅŪ·ÐÏ©¤ËÂФ·¤Æ¤ÏÉÔÀµ¤Ê°¸Àè¤Ç¤¹¡£
+¥Ç¥Õ¥©¥ë¥È·ÐÏ©¤ò»ØÄꤹ¤ë¤Ë¤Ï¡¢
+.B routers
+¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤¡£
+¤Þ¤¿¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¹¥ì¥¹¤Ê IP ·ÐÏ©À©¸æ¤ò°Õ¿Þ¤·¤¿¤â¤Î¤Ç¤Ï
+¤Ê¤¤¤³¤È¤ËÃí°Õ¤·¤Æ²¼¤µ¤¤¡£
+¤³¤ì¤Ï¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò´Þ¤ó¤Ç¤¤¤Þ¤»¤ó¡£
+¸½ºß¡¢¥¯¥é¥¹¥ì¥¹¤Ê IP ·ÐÏ©À©¸æ¤Ï¡¢¤â¤Ã¤È¤â¹­¤¯Å¸³«¤µ¤ì¤Æ¤¤¤ë
+·ÐÏ©À©¸æɸ½à¤Ê¤Î¤Ç¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¼Â¼ÁŪ¤Ë̵°ÕÌ£¤Ç¤¹¡£
+¤½¤·¤Æ¡¢¥Þ¥¤¥¯¥í¥½¥Õ¥È DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ò¤Ï¤¸¤á¤È¤¹¤ë¤è¤¯ÃΤé¤ì¤¿
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤Ï¼ÂÁõ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+.RE
+.PP
+.nf
+.B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fB;\fR
+.fi
+.RS 0.25i
+.PP
+StreetTalk Directory Assistance (STDA) ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê STDA ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+StreetTalk ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê StreetTalk ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option subnet-mask \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥ó¤Ï¡¢RFC 950 ¤Ë½¾¤Ã¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò»ØÄꤷ¤Þ¤¹¡£
+¥¹¥³¡¼¥×Ãæ¤Î¤É¤³¤Ë¤â¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥ó¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤¾ì¹ç¡¢
+ºÇ½ª¼êÃʤȤ·¤Æ¡¢dhcpd ¤Ï¡¢¥¢¥É¥ì¥¹¤ò³ä¤êÅö¤Æ¤è¤¦¤È¤·¤Æ¤¤¤ë
+¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥µ¥Ö¥Í¥Ã¥ÈÀë¸À¤«¤é¡¢¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ò»ÈÍѤ·¤Þ¤¹¡£
+¤·¤«¤·¡¢¥¢¥É¥ì¥¹¤ò³ä¤êÅö¤Æ¤è¤¦¤È¤·¤Æ¤¤¤ë¥Í¥Ã¥È¥ï¡¼¥¯¤Î¥¹¥³¡¼¥×Ãæ¤Î
+.I ¤É¤Î¤è¤¦¤Ê
+¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¥ª¥×¥·¥ç¥óÀë¸À¤Ç¤¢¤Ã¤Æ¤â¡¢
+¥µ¥Ö¥Í¥Ã¥ÈÀë¸À¤Ç»ØÄꤵ¤ì¤¿¥µ¥Ö¥Í¥Ã¥È¥Þ¥¹¥¯¤ËÍ¥À褷¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBsubnet-selection\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+(Í׵᤬½Ð¤µ¤ì¤¿¥µ¥Ö¥Í¥Ã¥È¤Ë¤Ä¤Ê¤¬¤ì¤¿¥ê¥ì¡¼¥µ¡¼¥Ð¤Î¥¢¥É¥ì¥¹¤Ë´ð¤Å¤¤¤Æ)
+Ä̾ïÁªÂò¤µ¤ì¤ë¤Ç¤¢¤í¤¦¤â¤Î¤Ç¤Ï¤Ê¤¤¥µ¥Ö¥Í¥Ã¥È¤Î¥¢¥É¥ì¥¹¤¬É¬Íפʾì¹ç¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤·¤Þ¤¹¡£
+RFC3011 ¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤³¤Î¥µ¡¼¥Ð¤Ë¤ª¤¤¤Æ»È¤ï¤ì¤ë¥ª¥×¥·¥ç¥ó¥Ê¥ó¥Ð¤Ï 118 ¤Ç¤¹¡£
+¤³¤Î¥Ê¥ó¥Ð¤Ï°ÊÁ°¤«¤é¤º¤Ã¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤¿¥Ê¥ó¥Ð¤Ç¤Ï¤Ê¤¯¡¢
+°ã¤¦Ãͤò»ÈÍѤ¹¤ë¥¯¥é¥¤¥¢¥ó¥È¤â¸ºß¤¹¤ë¤³¤È¤ËÃí°Õ¤·¤Æ¤¯¤À¤µ¤¤¡£
+¤³¤Î¥ª¥×¥·¥ç¥ó¤Î»ÈÍѤϾ¯¡¹¼Â¸³Åª¤Ç¤¢¤ë¤È¹Í¤¨¤ë¤Ù¤­¤Ç¤·¤ç¤¦!
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥Ð¤Ç¤Ï¥æ¡¼¥¶¤¬ÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
+.PP
+.RE
+.PP
+.B option \fBswap-server\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î¥¹¥ï¥Ã¥×¥µ¡¼¥Ð¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¸Å¤¤¼ÂÁõ¤È¤Î¸ß´¹À­¤Î¤¿¤á¤Ë¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤È°ì½ï¤Ë¡¢
+TCP ¥­¡¼¥×¥¢¥é¥¤¥Ö¥á¥Ã¥»¡¼¥¸¤ò¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷¤ë¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤òÁ÷¤ë¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥´¥ß¤Î¥ª¥¯¥Æ¥Ã¥È¤òÁ÷¤ë¤Ù¤­¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBtcp-keepalive-interval\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Î TCP ¤¬¥­¡¼¥×¥¢¥é¥¤¥Ö (keepalive)
+¥á¥Ã¥»¡¼¥¸¤ò TCP Àܳ¾å¤ËÁ÷¿®¤¹¤ëÁ°¤ËÂԤĤ٤­´Ö³Ö (ÉÃñ°Ì) ¤ò»ØÄꤷ¤Þ¤¹¡£
+»þ´Ö¤Ï 32 ¥Ó¥Ã¥ÈÉä¹æ̵¤·À°¿ô¤Ç»ØÄꤷ¤Þ¤¹¡£
+ÃÍ 0 ¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤¬ÌÀ¼¨Åª¤ËÍ׵ᤷ¤Ê¤¤¸Â¤ê¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬
+Àܳ¾å¤Ë¥­¡¼¥×¥¢¥é¥¤¥Ö¥á¥Ã¥»¡¼¥¸¤òÀ¸À®¤¹¤Ù¤­¤Ç¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBtftp-server-name\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï TFTP ¥µ¡¼¥Ð¤ò»ØÄꤹ¤ë¤Î¤Ë»ÈÍѤµ¤ì¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬
+¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï \fBserver-name\fR Àë¸À¤ÈƱ¤¸¸ú²Ì¤ò»ý¤Á¤Þ¤¹¡£
+BOOTP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤ò¥µ¥Ý¡¼¥È¤·¤Ê¤¤¤Ç¤·¤ç¤¦¡£
+DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ë¤è¤Ã¤Æ¤Ï¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¡¢
+¼ÂºÝɬ¿Ü¤È¤·¤Æ¤¤¤ë¤â¤Î¤¬¤¢¤ê¤Þ¤¹¡£
+.RE
+.PP
+.B option time-offset \fIint32\fR\fB;\fR
+.RS 0.25i
+.PP
+time-offset ¥ª¥×¥·¥ç¥ó¤Ï¡¢¶¨ÄêÀ¤³¦»þ (UTC) ¤ò´ðÅÀ¤È¤·¤Æ¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Î¥µ¥Ö¥Í¥Ã¥È¤Î¥ª¥Õ¥»¥Ã¥È¤òÉäǻØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option time-servers \fIip-address\fR [, \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+time-server ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê RFC 868 »þ¹ï¥µ¡¼¥Ð¤Î
+¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢ARP ¥×¥í¥È¥³¥ë»ÈÍÑ»þ¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤÎ
+¥Í¥´¥·¥¨¡¼¥·¥ç¥ó (RFC 893 [14]) ¤ò¤¹¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+ÃÍ false ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤò»î¤ß¤ë¤Ù¤­¤Ç¤Ê¤¤¤È°ÕÌ£¤·¤Þ¤¹¡£
+ÃÍ true ¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬¥È¥ì¥¤¥é»ÈÍѤò»î¤ß¤ë¤Ù¤­¤Ç¤¢¤ë¤È°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBuap-servers\fR \fItext\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶Ç§¾Ú¥×¥í¥È¥³¥ë (UAP) ¤ËÊñ¤Þ¤ì¤¿Ç§¾ÚÍ×µá¤ò
+½èÍý¤¹¤ëǽÎϤΤ¢¤ë¥æ¡¼¥¶Ç§¾Ú¥µ¡¼¥Ó¥¹¤ò¤½¤ì¤¾¤ì»Ø¤·¤Æ¤¤¤ë
+URL ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+UAP ¥µ¡¼¥Ð¤Ï HTTP 1.1 Àܳ¤â SSLv3 Àܳ¤â¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤¿ URL ¤Ë¥Ý¡¼¥ÈÉôʬ¤¬´Þ¤Þ¤ì¤Æ¤Ê¤¤¾ì¹ç¤Ï¡¢
+Ä̾ï¤Î¥Ç¥Õ¥©¥ë¥È¥Ý¡¼¥È¤¬²¾Äꤵ¤ì¤Þ¤¹
+(¤Ä¤Þ¤ê http ¤Ë¤Ï 80 ÈÖ¡¢https ¤Ë¤Ï 443 ÈÖ)¡£
+¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤¿ URL ¤Ë¥Ñ¥¹¤ÎÉôʬ¤¬´Þ¤Þ¤ì¤Æ¤Ê¤¤¾ì¹ç¤Ï¡¢
+¥Ñ¥¹¤Ï /uap ¤È²¾Äꤵ¤ì¤Þ¤¹¡£
+2 ¤Ä°Ê¾å¤Î URL ¤¬¤³¤Î¥ê¥¹¥È¤Ë»ØÄꤵ¤ì¤¿¾ì¹ç¡¢URL ¤Ï¶õÇò¤Ç¶èÀÚ¤é¤ì¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBuser-class\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤¬¼±Ê̾ðÊó¤ò¥¯¥é¥¤¥¢¥ó¥È¤Ë»ØÄꤹ¤ë¼êÃʤȤ·¤Æ¡¢
+¤¤¤¯¤Ä¤«¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç»È¤ï¤ì¤Þ¤¹¡£
+¤³¤ì¤Ï vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤ÈƱÍͤ˻Ȥï¤ì¤Þ¤¹¤¬¡¢
+¤½¤ÎÃͤϡ¢¥Ù¥ó¥À¤Ç¤Ï¤Ê¤¯¡¢¥æ¡¼¥¶¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤Þ¤¹¡£
+ºÇ¶á¤Î¤Û¤È¤ó¤É¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¤³¤Î¼±Ê̻ҤËÃͤò»ØÄꤹ¤ë¤¿¤á¤Î
+¥æ¡¼¥¶¥¤¥ó¥¿¥Õ¥§¡¼¥¹¤òÈ÷¤¨¤Æ¤¤¤Þ¤¹¡£
+¤³¤Î¼±Ê̻Ҥϡ¢Ä̾ï¥Æ¥­¥¹¥Èʸ»úÎó¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBvendor-class-identifier\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Ù¥ó¥À¥¿¥¤¥×¤ä¡¢²Äǽ¤Ç¤¢¤ì¤Ð DHCP ¥¯¥é¥¤¥¢¥ó¥È¤ÎÀßÄê¤ò
+¼±Ê̤¹¤ë¤¿¤á¤Ë¡¢¤¤¤¯¤Ä¤«¤Î DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ç»È¤ï¤ì¤Þ¤¹¡£
+¤³¤Î¾ðÊó¤ÎÆâÍƤϡ¢¥Ù¥ó¥À¸ÇÍ­¤Î¥Ð¥¤¥Èʸ»úÎó¤Ç¡¢É¸½à¤Ç¤Ïµ¬Äꤵ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+¥¯¥é¥¤¥¢¥ó¥È¤¬Á÷½Ð¤¹¤ë¥Ù¥ó¥À¥¯¥é¥¹¼±Ê̻Ҥò³Îǧ¤¹¤ë¤Ë¤Ï¡¢
+°Ê²¼¤ÎÀßÄê¤ò DHCP ¥µ¡¼¥ÐÀßÄê¥Õ¥¡¥¤¥ë¤Ë²Ã¤¨¤Æ¤¯¤À¤µ¤¤:
+.nf
+.PP
+set vendor-class option vendor-class-identifier;
+.fi
+.PP
+¤³¤ÎÀßÄê¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Î¥ê¡¼¥¹¥Ç¡¼¥¿¥Ù¡¼¥¹¥Õ¥¡¥¤¥ëÃæ¤Î¡¢
+°Ê²¼¤Î¤è¤¦¤Ê set ʸ¤ò»ý¤Ä vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤ò
+Á÷¤Ã¤Æ¤¯¤ë¥¯¥é¥¤¥¢¥ó¥È¤¹¤Ù¤Æ¤Î¥¨¥ó¥È¥ê¤ËºîÍѤ·¤Þ¤¹¡£
+.nf
+.PP
+set vendor-class "SUNW.Ultra-5_10";
+.fi
+.PP
+vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ä̾ï DHCP Server ¤Ë¤è¤Ã¤Æ¡¢
+.B vendor-encapsulated-options
+¥ª¥×¥·¥ç¥óÃæ¤ÇÊÖ¤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¤ò·èÄꤹ¤ë¤Î¤Ë»È¤ï¤ì¤Þ¤¹¡£
+¤µ¤é¤Ê¤ë¾ðÊó¤Ï¡¢dhcpd.conf ¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Î VENDOR ENCAPSULATED OPTIONS ¤Î
+¾Ï¤ò»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBvendor-encapsulated-options\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+.\" metal
+\fBvendor-encapsulated-options\fR ¥ª¥×¥·¥ç¥ó¤Ï¡¢1 ¤Ä¤Î¥Ù¥ó¥À¸ÇÍ­ÃÍ¡¢
+¤â¤·¤¯¤Ï 1 ¤Ä¤Þ¤¿¤Ï¤½¤ì°Ê¾å¤Î¥Ù¥ó¥À¸ÇÍ­¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò´Þ¤ß¤Þ¤¹¡£
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ä̾ï DHCP ¥µ¡¼¥Ð¤ÎÀßÄê¥Õ¥¡¥¤¥ë¤ÇÀßÄꤵ¤ì¤ë¤â¤Î¤Ç¤Ï
+¤¢¤ê¤Þ¤»¤ó¡£
+Ä̾ï¤Ï¡¢¥Ù¥ó¥À¥¯¥é¥¹¤¬¥Ù¥ó¥ÀËè¤ËÄêµÁ¤µ¤ì¡¢
+¥Ù¥ó¥À¥¯¥é¥¹¥µ¥Ö¥ª¥×¥·¥ç¥ó¤¬ÄêµÁ¤µ¤ì¡¢¤½¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ÎÃͤ¬
+ÄêµÁ¤µ¤ì¡¢DHCP ¥µ¡¼¥Ð¤Ï¤½¤ì¤é¤ò¤â¤È¤Ë±þÅú¤òÁȤ߾夲¤Þ¤¹¡£
+.PP
+¤è¤¯ÃΤé¤ì¤¿ DHCP ¥¯¥é¥¤¥¢¥ó¥È¥Ù¥ó¥À (º£¤Î¤È¤³¤í Microsoft Windows
+2000 DHCP ¥¯¥é¥¤¥¢¥ó¥È) ¸þ¤±¤Î¤¤¤¯¤Ä¤«¤Î¥Ç¥Õ¥©¥ë¥È¤ÎÆ°ºî¤Ç¤Ï¡¢
+¤³¤Î¥ª¥×¥·¥ç¥ó¤Ï¼«Æ°Åª¤ËÀßÄꤵ¤ì¤Þ¤¹¤¬¡¢¤½¤Î¾¤Î¤â¤Î¤Ë´Ø¤·¤Æ¤Ï¡¢
+¼êÆ°¤ÇÀßÄꤷ¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+¾ÜºÙ¤Ï \fIdhcpd.conf\fI ¤Î VENDOR ENCAPSULATED OPTIONS ¤Î¾Ï¤ò
+»²¾È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBwww-server\fR \fIip-address\fR [\fB,\fR
+\fIip-address\fR... ]\fB;\fR
+.RS 0.25i
+.PP
+WWW ¥µ¡¼¥Ð¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê WWW ¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.PP
+.B option \fBx-display-manager\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+]\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÍøÍѲÄǽ¤Ê X Window System
+¥Ç¥£¥¹¥×¥ì¥¤¥Þ¥Í¡¼¥¸¥ã¤ò¼Â¹Ô¤·¤Æ¤¤¤ë¥·¥¹¥Æ¥à¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¥¢¥É¥ì¥¹¤Ï¡¢Í¥À褵¤ì¤ë¤â¤Î¤«¤é½ç¤Ë¥ê¥¹¥È¤·¤Æ¤¯¤À¤µ¤¤¡£
+.RE
+.SH ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¾ðÊ󥪥ץ·¥ç¥ó
+.\" metal
+IETF ¥É¥é¥Õ¥È draft-ietf-dhc-agent-options-11.txt ¤Ë¤Ï¡¢
+DHCP ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤¬ DHCP ¥Ñ¥±¥Ã¥È¤ò DHCP ¥µ¡¼¥Ð¤ËžÁ÷¤¹¤ëºÝ¡¢
+DHCP ¥Ñ¥±¥Ã¥È¤ËÉղ乤뤳¤È¤Î¤Ç¤­¤ë°ìÏ¢¤Î¥«¥×¥»¥ë²½¤µ¤ì¤¿¥ª¥×¥·¥ç¥ó¤¬
+ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¡¢¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ë´ð¤Å¤­¡¢¥¢¥É¥ì¥¹³äÅö¤Î·èÄê
+(¤ä¡¢¤½¤Î¾¤ÎȽÃÇ) ¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¤Þ¤¿¥µ¡¼¥Ð¤Ï¡¢¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤òÄ̤·¤ÆÊÖ¤µ¤ì¤ë¤É¤Î¥Ñ¥±¥Ã¥È¤Ë¤â
+¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤òÆþ¤ì¤ÆÊÖ¤·¤Þ¤¹¡£
+¤³¤ì¤Ë¤è¤Ã¤Æ¥ê¥ì¡¼¥¨¡¼¥¸¥§¥ó¥È¤Ï¡¢ÇÛÁ÷¤ä¥¢¥«¥¦¥ó¥Æ¥£¥ó¥°¤Ê¤É¤ò
+¹Ô¤¦¤¿¤á¤Ë¡¢¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ë´Þ¤Þ¤ì¤ë¾ðÊó¤òÍøÍѤǤ­¤Þ¤¹¡£
+.PP
+¸½ºß¤Î¥É¥é¥Õ¥È¤Ë¤Ï 2 ¤Ä¤Î¥ª¥×¥·¥ç¥ó¤¬ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ð¤Ç¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö̾
+"agent" ¤Î¤¢¤È¤Ë¥Ô¥ê¥ª¥É¤ò¤Ä¤±¡¢¤½¤Î¸å¤Ë¥ª¥×¥·¥ç¥ó̾¤ò³¤±¤Æ¤¯¤À¤µ¤¤¡£
+¥µ¡¼¥Ð¤Ç¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤòÄêµÁ¤¹¤ë¤³¤È¤Ï¡¢
+Ä̾濫¤Þ¤êÍ­¸ú¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢µöÍƤµ¤ì¤Æ¤¤¤Þ¤¹¡£
+¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ç¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+.PP
+.B option \fBagent.circuit-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+circuit-id ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤Ø¤Î DHCP ¥Ñ¥±¥Ã¥È¤ò
+¼õ¤±¼è¤Ã¤¿¥µ¡¼¥­¥Ã¥È¤ò¼¨¤¹¡¢¥¨¡¼¥¸¥§¥ó¥È¥í¡¼¥«¥ë¤Ê¥µ¡¼¥­¥Ã¥È¼±Ê̻Ҥò
+¥¨¥ó¥³¡¼¥É¤·¤Æ¤¤¤Þ¤¹¡£
+¤³¤ì¤Ï¡¢DHCP ±þÅú¤òŬÀڤʥµ¡¼¥­¥Ã¥È¤Ø¤ÈÁ÷¤êÊÖ¤»¤ë¤è¤¦¡¢
+¥¨¡¼¥¸¥§¥ó¥È¤Ë¤è¤Ã¤Æ»È¤ï¤ì¤ë¤³¤È¤ò°Õ¿Þ¤·¤Æ¤¤¤Þ¤¹¡£
+¸½ºß¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤ÎÄêµÁ¤Ï¥Ù¥ó¥À°Í¸¤È¤Ê¤Ã¤Æ¤ª¤ê¡¢
+¿ʬ¤³¤Î¤Þ¤Þ»Ä¤µ¤ì¤ë¤Ç¤·¤ç¤¦¡£
+¤·¤«¤·¾­Í褳¤Î½ñ¼°¤¬É¸½à²½¤µ¤ì¤ë²ÄǽÀ­¤â¡¢¸½ºß¤Î¥É¥é¥Õ¥È¤Ë¤Ï»Ä¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBagent.remote-id\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+remote-id ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ï¡¢¥µ¡¼¥­¥Ã¥È¤Î½ªÃ¼¤Î¥ê¥â¡¼¥È¥Û¥¹¥È¤Î
+¾ðÊó¤ò¥¨¥ó¥³¡¼¥É¤·¤Æ¤¤¤Þ¤¹¡£
+¤³¤ì¤Ë´Þ¤Þ¤ì¤ë¤Ç¤¢¤í¤¦¾ðÊó¤Ï¡¢¼¡¤Î¤è¤¦¤Ê¤â¤Î¤Ç¤¹¡£
+¸Æ½Ð¸µ ID ¾ðÊ󡢥桼¥¶Ì¾¾ðÊó¡¢¥ê¥â¡¼¥È ATM ¥¢¥É¥ì¥¹¡¢¥±¡¼¥Ö¥ë¥â¥Ç¥à ID¡¢
+¤½¤Î¾¤ÎƱÍͤʾðÊó¡£
+¸¶Â§Åª¤Ë¤Ï¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Î°ÕÌ£¤Ï¤Á¤ã¤ó¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+¤·¤«¤·Ä̾¥µ¡¼¥­¥Ã¥È¤ÎÆÃÄê¤Î¥ê¥â¡¼¥È¥¨¥ó¥É¤ËÂФ·¤Æ°ì°Õ¤Ç¤¢¤ë¤è¤¦
+´ÉÍý¾åÊݾڤµ¤ì¤¿¡¢¤Ê¤ó¤é¤«¤Î¥ª¥Ö¥¸¥§¥¯¥È¤È¹Í¤¨¤ë¤Ù¤­¤â¤Î¤Ç¤¹¡£
+.RE
+.SH ¥¯¥é¥¤¥¢¥ó¥È FQDN ¥µ¥Ö¥ª¥×¥·¥ç¥ó
+.\" metal
+¸½ºß¡¢¥¤¥ó¥¿¡¼¥Í¥Ã¥È¥É¥é¥Õ¥È draft-ietf-dhc-fqdn-option-00.txt ¤Ç
+ÄêµÁ¤µ¤ì¤Æ¤¤¤ë¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥ó¤Ï¡¢¤Þ¤Àɸ½à¤È¤Ê¤Ã¤Æ¤Ï¤¤¤Þ¤»¤ó¡£
+¤·¤«¤·¤¹¤Ç¤Ë½½Ê¬¹­¤¯ÍøÍѤµ¤ì¤Æ¤ª¤ê¡¢²æ¡¹¤â¤³¤ì¤ò¼ÂÁõ¤·¤Æ¤¤¤Þ¤¹¡£
+¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤¬Ê£»¨¤Ê¤¿¤á¡¢¤³¤³¤Ç¤Ï¡¢Ã±ÆȤΥª¥×¥·¥ç¥ó¤Ç¤Ï¤Ê¤¯¡¢
+¥µ¥Ö¥ª¥×¥·¥ç¥ó¶õ´Ö¤Ë¼ÂÁõ¤·¤Æ¤¤¤Þ¤¹¡£
+°ìÈÌŪ¤Ë¤Ï¡¢ËÜ¥ª¥×¥·¥ç¥ó¤Ï¡¢¥æ¡¼¥¶¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤ë¤â¤Î¤Ç¤Ï¤Ê¤¯¡¢
+¼«Æ° DNS ¹¹¿·¥·¥¹¥Æ¥à¤Î°ìÉô¤È¤·¤Æ»È¤ï¤ì¤ë¤Ù¤­¤â¤Î¤Ç¤¹¡£
+.PP
+.B option fqdn.no-client-update \fIflag\fB;
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¤³¤ì¤¬ true ¤Ç¤¢¤ì¤Ð¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¼«Ê¬¤Î A ¥ì¥³¡¼¥É¤ò¹¹¿·¤·¤Ê¤¤¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+¥µ¡¼¥Ð¤«¤é¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï
+¼«Ê¬¤Î A ¥ì¥³¡¼¥É¤ò¹¹¿·¤¹¤ë \fI¤Ù¤­¤Ç¤Ï¤Ê¤¤\fR ¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option fqdn.server-update \fIflag\fB;
+.RS 0.25i
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤¬¥¯¥é¥¤¥¢¥ó¥È¤«¤é¥µ¡¼¥Ð¤ËÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢
+¥µ¡¼¥Ð¤Ë¥¯¥é¥¤¥¢¥ó¥È¤Î A ¥ì¥³¡¼¥É¤Î¹¹¿·¤òÍ׵ᤷ¤Æ¤¤¤Þ¤¹¡£
+¥µ¡¼¥Ð¤«¤éÁ÷½Ð¤µ¤ì¤¿¾ì¹ç¡¢¥µ¡¼¥Ð¤¬¥¯¥é¥¤¥¢¥ó¥È¤Î A ¥ì¥³¡¼¥É¤ò
+¹¹¿·¤·¤¿ (¤â¤·¤¯¤Ï¤â¤¦¤¹¤°¹¹¿·¤¹¤ë¤È¤³¤í) ¤Ç¤¢¤ë¤³¤È¤ò°ÕÌ£¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option fqdn.encoded \fIflag\fB;
+.RS 0.25i
+.PP
+true ¤Ç¤¢¤Ã¤¿»þ¡¢¥ª¥×¥·¥ç¥ó¤Ë´Þ¤Þ¤ì¤ë¥É¥á¥¤¥ó̾¤¬¡¢
+¤¿¤À¤Î ASCII ¥Æ¥­¥¹¥È¤Ç¤Ï¤Ê¤¯¡¢DNS ¥ï¥¤¥ä¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç
+¥¨¥ó¥³¡¼¥É¤µ¤ì¤Æ¤¤¤ë¤³¤È¤ò¼¨¤·¤Æ¤Þ¤¹¡£
+¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢¼«Ê¬¤¬ FQDN ¥ª¥×¥·¥ç¥ó¤Î DNS ¥ï¥¤¥ä¥Õ¥©¡¼¥Þ¥Ã¥È¤ò
+¥µ¥Ý¡¼¥È¤·¤Æ¤Ê¤¤¾ì¹ç¡¢Ä̾盧¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò false ¤ËÀßÄꤷ¤Þ¤¹¡£
+¥µ¡¼¥Ð¤Ï¾ï¤Ë¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ÀßÄꤷ¤¿¤Î¤ÈƱ¤¸ÃͤòÀßÄꤷ¤ÆÊÖ¤¹¤Ù¤­¤Ç¤¹¡£
+¤³¤ÎÃͤ¬ÀßÄê¥Õ¥¡¥¤¥ë¤ËÀßÄꤵ¤ì¤Æ¤¤¤¿»þ¤Ï¡¢\fIfqdn.fqdn\fR ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò
+¥¨¥ó¥³¡¼¥É¤¹¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤òÀ©¸æ¤·¤Þ¤¹¡£
+.RE
+.PP
+.B option fqdn.rcode1 \fIflag\fB;
+.PP
+.B option fqdn.rcode2 \fIflag\fB;
+.RS 0.25i
+.PP
+¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤Ï¤½¤ì¤¾¤ì¡¢A ¥ì¥³¡¼¥É¤È PTR ¥ì¥³¡¼¥É¤Î¹¹¿··ë²Ì¤ò¼¨¤·¤Þ¤¹¡£
+¤³¤ì¤é¤Ï¡¢DHCP ¥µ¡¼¥Ð¤«¤é DHCP ¥¯¥é¥¤¥¢¥ó¥È¤Ø¤Î¤ßÁ÷¤é¤ì¤Þ¤¹¡£
+¤³¤ì¤é¤Î¥Õ¥£¡¼¥ë¥É¤ÎÃͤϡ¢DNS ¥×¥í¥È¥³¥ëµ¬³Ê¤Ë¤è¤êÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+.RE
+.PP
+.B option fqdn.fqdn \fItext\fB;
+.RS 0.25i
+.PP
+¥¯¥é¥¤¥¢¥ó¥È¤¬»ÈÍѤò˾¤à¥É¥á¥¤¥ó̾¤ò»ØÄꤷ¤Þ¤¹¡£
+¤³¤ì¤Ï´°Á´½¤¾þ¤µ¤ì¤¿¥É¥á¥¤¥ó̾¤Ç¤â¡¢Ã±°ì¤Î¥é¥Ù¥ë¤Ç¤â¹½¤¤¤Þ¤»¤ó¡£
+¤â¤·Ì¾Á°¤Ë '.' ʸ»ú¤¬´Þ¤Þ¤ì¤Ê¤±¤ì¤Ð¡¢¤½¤Î̾Á°¤Ï´°Á´½¤¾þ¤µ¤ì¤Æ¤ª¤é¤º¡¢
+¥µ¡¼¥Ð¤ÏÄ̾¥í¡¼¥«¥ë¤ËÄêµÁ¤µ¤ì¤¿¥É¥á¥¤¥óÃæ¤Î¤½¤Î̾Á°¤ò¹¹¿·¤·¤Þ¤¹¡£
+.RE
+.PP
+¤â¤·¤³¤ì¤é¤Î¥µ¥Ö¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ·¤è¤¦¤È»×¤Ã¤Æ¤¤¤ë¤Î¤Ç¤¢¤ì¤Ð¡¢
+¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥ó¤Î¥É¥é¥Õ¥È (¤â¤·¤¯¤Ï¡¢É¸½à¤Ë¤Ê¤Ã¤¿»þ¤Ï¤½¤Îɸ½à)
+¤ò»²¾È¤¹¤ë¤³¤È¤ò¶¯¤¯¿ä¾©¤·¤Þ¤¹¡£
+¤³¤Îʸ½ñ¤Ï¡¢¤½¤Î¥É¥é¥Õ¥È¤ËÈæ¤Ù¤ÆÂ绨ÇĤÇÉÔ´°Á´¤Ç¤¢¤ê¡¢
+¥¯¥é¥¤¥¢¥ó¥È FQDN ¥ª¥×¥·¥ç¥óµ¬³Ê¤ò¤¹¤Ç¤ËÍý²ò¤·¤Æ¤¤¤ë¿Í¤Ë»²¾È¤µ¤ì¤ë¤³¤È¤ò
+ñ¤Ë°Õ¿Þ¤·¤Æ¤¤¤ë¤â¤Î¤Ç¤¹¡£
+.SH NetWare/IP ¥µ¥Ö¥ª¥×¥·¥ç¥ó
+.\" metal
+RFC2242 ¤Ï¡¢Novell ¤Î NetWare/IP ¥¯¥é¥¤¥¢¥ó¥ÈÍѤΥ«¥×¥»¥ë²½¤µ¤ì¤¿
+¥ª¥×¥·¥ç¥ó¤ÎÁȤòÄêµÁ¤·¤Æ¤¤¤Þ¤¹¡£
+DHCP ¥µ¡¼¥Ð¤Ë¤ª¤¤¤Æ¤³¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò»ÈÍѤ¹¤ë¤Ë¤Ï¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö̾
+"nwip" ¤Î¸å¤Ë¥Ô¥ê¥ª¥É¤ò¤Ä¤±¡¢¤½¤Î¸å¤Ë¥ª¥×¥·¥ç¥ó̾¤ò³¤±¤Æ¤¯¤À¤µ¤¤¡£
+°Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬»ØÄê¤Ç¤­¤Þ¤¹:
+.PP
+.B option \fBnwip.nsq-broadcast\fR \fIflag\fR\fB;\fR
+.RS 0.25i
+.PP
+true ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¥¯¥é¥¤¥¢¥ó¥È¤Ï¡¢NetWare/IP ¥µ¡¼¥Ð¤Î°ÌÃÖ¤ò
+õ¤¹¤Î¤Ë NetWare Nearest Server Query ¤ò»È¤¦¤Ù¤­¤Ç¤¹¡£
+ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤¬ false ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢¤â¤·¤¯¤Ï»ØÄꤵ¤ì¤Ê¤«¤Ã¤¿¾ì¹ç¤Î
+Novell ¥¯¥é¥¤¥¢¥ó¥È¤ÎÆ°ºî¤Ïµ¬Äꤵ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+.PP
+.RE
+.B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ë¤Ï¡¢5 ¤Ä¤Þ¤Ç¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¤½¤ì¤¾¤ì¤Î¥¢¥É¥ì¥¹¤Ï¡¢NetWare ¥É¥á¥¤¥ó SAP/RIP ¥µ¡¼¥Ð (DSS) ¤Î
+IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fR\fB;\fR
+.RS 0.25i
+.PP
+ËÜ¥µ¥Ö¥ª¥×¥·¥ç¥ó¤Ë¤Ï¡¢5 ¤Ä¤Þ¤Ç¤Î IP ¥¢¥É¥ì¥¹¤Î¥ê¥¹¥È¤ò»ØÄꤷ¤Þ¤¹¡£
+¤½¤ì¤¾¤ì¤Î¥¢¥É¥ì¥¹¤Ï¡¢¶áÀܤΠNetWare IP ¥µ¡¼¥Ð
+(Nearest NetWare IP Server) ¤Î IP ¥¢¥É¥ì¥¹¤Ç¤¹¡£
+.RE
+.PP
+.B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+µ¯Æ°»þ¤Ë¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Í¿¤¨¤é¤ì¤¿ DSS ¥µ¡¼¥Ð¤È
+²¿²óÄÌ¿®¤ò»î¤ß¤ë¤Ù¤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+µ¯Æ°»þ¤Ë¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤¬¡¢Í¿¤¨¤é¤ì¤¿ DSS ¥µ¡¼¥Ð¤È
+ÄÌ¿®¤ò³ÎΩ¤¹¤ë»þ¤Ë¡¢¥ê¥È¥é¥¤¤Î´Ö²¿ÉÃÂԤĤ٤­¤«¤ò»ØÄꤷ¤Þ¤¹¡£
+.RE
+.PP
+.B option \fBnwip.nwip-1-1\fR \fIuint8\fR\fB;\fR
+.RS 0.25i
+.PP
+true ¤Ç¤¢¤Ã¤¿¾ì¹ç¡¢NetWare/IP ¥¯¥é¥¤¥¢¥ó¥È¤Ï NetWare/IP
+¥Ð¡¼¥¸¥ç¥ó 1.1 ¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¤Ù¤­¤Ç¤¹¡£
+¤³¤ì¤Ï¡¢¥¯¥é¥¤¥¢¥ó¥È¤¬ NetWare/IP ¥Ð¡¼¥¸¥ç¥ó 1.1 ¤Î¥µ¡¼¥Ð¤È
+ÄÌ¿®¤¹¤ë»þ¤Î¤ßɬÍפǤ¹¡£
+.RE
+.PP
+.B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR
+.RS 0.25i
+.PP
+NetWare/IP ¥É¥á¥¤¥ó¤Î¥×¥é¥¤¥Þ¥ê¥É¥á¥¤¥ó SAP/RIP ¥µ¡¼¥Ó¥¹¥µ¡¼¥Ð
+(DSS) ¤Î IP ¥¢¥É¥ì¥¹¤ò»ØÄꤷ¤Þ¤¹¡£
+¥»¥«¥ó¥À¥ê DSS ¥µ¡¼¥Ð¤ÎÀßÄê»þ¤Ë¡¢NetWare/IP ´ÉÍý¥æ¡¼¥Æ¥£¥ê¥Æ¥£¤Ï¡¢
+¤³¤ÎÃͤò¥×¥é¥¤¥Þ¥ê DSS ¥µ¡¼¥Ð¤È¤·¤Æ»ÈÍѤ·¤Þ¤¹¡£
+.RE
+.SH ¿·µ¬¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ
+.\" metal
+Internet Systems Consortium DHCP ¥¯¥é¥¤¥¢¥ó¥È¤È¥µ¡¼¥Ð¤Ï¡¢
+¿·µ¬¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤¹¤ëµ¡¹½¤âÄ󶡤·¤Æ¤¤¤Þ¤¹¡£
+¤½¤ì¤¾¤ì¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ï¡¢Ì¾Á°¤È¥³¡¼¥É¡¢¹½Â¤¤ò»ý¤Ã¤Æ¤¤¤Þ¤¹¡£
+̾Á°¤Ï¡¢»ÈÍѼԤ¬¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Î¤Ë»ÈÍѤµ¤ì¤Þ¤¹¡£
+¥³¡¼¥É¤Ï¡¢DHCP ¥µ¡¼¥Ð¤È¥¯¥é¥¤¥¢¥ó¥È¤¬¥ª¥×¥·¥ç¥ó¤ò»²¾È¤¹¤ë¤Î¤Ë
+»ÈÍѤ¹¤ëÈÖ¹æ¤Ç¤¹¡£
+¹½Â¤¤Ï¡¢¥ª¥×¥·¥ç¥ó¤ÎÆâÍƤ¬¤É¤Î¤è¤¦¤Ê¤â¤Î¤«¤òµ­½Ò¤·¤Æ¤¤¤Þ¤¹¡£
+.PP
+¿·µ¬¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤¹¤ë¤Ë¤Ï¡¢Â¾¤Î¥ª¥×¥·¥ç¥ó¤Ç¤Ï»È¤ï¤ì¤Æ¤¤¤Ê¤¤Ì¾Á°¤ò
+Áª¤ÖɬÍפ¬¤¢¤ê¤Þ¤¹¡£
+Î㤨¤Ð¡¢"host-name" ¤È¸À¤¦Ì¾Á°¤Ï»ÈÍѤǤ­¤Þ¤»¤ó¡£
+¤È¤¤¤¦¤Î¤â¡¢¤³¤Î¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë½Ð¤Æ¤­¤¿¤è¤¦¤Ë¡¢
+DHCP ¥×¥í¥È¥³¥ë¤¬´û¤Ë host-name ¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤·¤Æ¤¤¤ë¤«¤é¤Ç¤¹¡£
+¤³¤Î¥Þ¥Ë¥å¥¢¥ë¥Ú¡¼¥¸¤Ë½Ð¤Æ¤­¤Æ¤¤¤Ê¤¤¥ª¥×¥·¥ç¥ó̾¤Ê¤é¤Ð
+»È¤Ã¤Æ¤â¹½¤¤¤Þ¤»¤ó¤¬¡¢¾­Íè½Ð¤Æ¤¯¤ë¥ª¥×¥·¥ç¥ó¤È½Å¤Ê¤é¤Ê¤¤¤è¤¦¤Ë¡¢
+¥ª¥×¥·¥ç¥ó̾¤ÎºÇ½é¤ËÆȼ«¤Îʸ»úÎó¤ò¤Ä¤±¤ë¤³¤È¤Ï¡¢Â¿Ê¬¤¤¤¤¹Í¤¨¤Ç¤·¤ç¤¦¡£
+Î㤨¤Ð¡¢¸ø¼°¤Î DHCP ¥ª¥×¥·¥ç¥ó¤Ë¤Ï "local" ¤Ç»Ï¤Þ¤ë¤â¤Î¤¬¤Ê¤¤¤Î¤Ç¡¢
+"local-host-name" ¤È¸À¤¦Ì¾Á°¤Ï¡¢¤¤¤¯¤é¤«°Â¿´¤·¤ÆÄêµÁ¤Ç¤­¤ë¤Ç¤·¤ç¤¦¡£
+.PP
+̾Á°¤òÁªÂò¤·¤¿¤é¡¢¼¡¤Ï¥³¡¼¥É¤òÁª¤Ð¤Í¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+DHCP ¥ª¥×¥·¥ç¥ó¤Î 128 ¤«¤é 256 ¤Þ¤Ç¤Î¥³¡¼¥É¤Ï¡¢
+¥µ¥¤¥È¥í¡¼¥«¥ë¥ª¥×¥·¥ç¥óÍѤȤ·¤ÆͽÌ󤵤ì¤Æ¤¤¤ë¤Î¤Ç¡¢
+¤³¤ÎÃæ¤Î¥³¡¼¥É¤Ê¤é¤É¤ì¤Ç¤âÁª¤Ö¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¼ÂºÝ¤Ë¤Ï¡¢¥×¥í¥È¥³¥ë¤ò¾¯¡¹¤¢¤¤¤Þ¤¤¤Ë²ò¼á¤·¤Æ¤¤¤ë¥Ù¥ó¥À¤¬¤¢¤ê¡¢
+128 ¤è¤êÂ礭¤¤ÃͤΥª¥×¥·¥ç¥ó¥³¡¼¥É¤ò»ÈÍѤ·¤Æ¤¤¤Þ¤¹¡£
+¤³¤ÎÌäÂê¤òËÜÅö¤Ë²óÈò¤¹¤ëÊýË¡¤Ï¤¢¤ê¤Þ¤»¤ó¤¬¡¢
+¼ÂºÝ¤Ë¤Ï¤½¤¦Â礭¤ÊÌäÂê¤ò°ú¤­µ¯¤³¤¹¤â¤Î¤Ç¤Ï¤Ê¤¤¤Ç¤·¤ç¤¦¡£
+.PP
+¥ª¥×¥·¥ç¥ó¤Î¹½Â¤¤È¤Ï¡¢Ã±¤Ë¥ª¥×¥·¥ç¥ó¤Î¥Ç¡¼¥¿¤¬É½¸½¤µ¤ì¤Æ¤¤¤ë·Á¼°¤Ç¤¹¡£
+¸½ºß ISC DHCP ¥µ¡¼¥Ð¤Ï¡¢À°¿ô¡¢¥Ö¡¼¥ëÃÍ¡¢Ê¸»úÎ󤽤·¤Æ IP ¥¢¥É¥ì¥¹¤È¤¤¤Ã¤¿¡¢
+¤¤¤¯¤Ä¤«¤Îñ½ã¤Ê¥Ç¡¼¥¿·¿¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤ª¤ê¡¢
+¤Þ¤¿Ã±°ì¥Ç¡¼¥¿·¿¤ÎÇÛÎó¤ä¸ÇÄê½ç¤Î¥Ç¡¼¥¿·¿Îó¤ÎÇÛÎó¤òÄêµÁ¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+.PP
+¿·µ¬¥ª¥×¥·¥ç¥ó¤Ï¡¢°Ê²¼¤Î¤è¤¦¤ËÀë¸À¤µ¤ì¤Þ¤¹:
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.I definition
+.B ;
+.PP
+.I new-name
+¤È
+.I new-code
+¤ÎÃͤϡ¢¿·µ¬¥ª¥×¥·¥ç¥óÍѤˤ¢¤Ê¤¿¤¬Áª¤ó¤À¤â¤Î¤Ç¤¹¡£
+.I definition
+¤Ï¡¢¥ª¥×¥·¥ç¥ó¤Î¹½Â¤¤ÎÄêµÁ¤Ç¤¹¡£
+.PP
+°Ê²¼¤Îñ½ã¤Ê¥ª¥×¥·¥ç¥ó¤Î·¿ÄêµÁ¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤¹:
+.PP
+.B ¥Ö¡¼¥ëÃÍ
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B boolean
+.B ;
+.PP
+¥Ö¡¼¥ë·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢on ¤Þ¤¿¤Ï off (¤â¤·¤¯¤Ï true ¤« false) ¤ÎÃͤò
+»ý¤Ä¥Õ¥é¥°¤Ç¤¹¡£
+¥Ö¡¼¥ë·¿¤Î»ÈÍÑÎã¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹:
+.nf
+
+option use-zephyr code 180 = boolean;
+option use-zephyr on;
+
+.fi
+.B À°¿ô
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.I sign
+.B integer
+.I width
+.B ;
+.PP
+\fIsign\fR ¥È¡¼¥¯¥ó¤Ï¡¢¶õÇò¡¢\fIunsigned\fR¡¢\fIsigned\fR ¤Î¤¤¤º¤ì¤«¤Ç¤¹¡£
+width ¤Ï 8, 16, 32 ¤Î¤¤¤º¤ì¤«¤Ç¡¢À°¿ô¤Î bit ¿ô¤ò¼¨¤·¤Þ¤¹¡£
+Î㤨¤Ð¡¢°Ê²¼¤Î 2 ¹Ô¤Ï¡¢sql-connection-max ¥ª¥×¥·¥ç¥ó¤ÎÄêµÁ¤È
+»ÈÍÑË¡¤ò¼¨¤·¤Þ¤¹:
+.nf
+
+option sql-connection-max code 192 = unsigned integer 16;
+option sql-connection-max 1536;
+
+.fi
+.B IP ¥¢¥É¥ì¥¹
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B ip-address
+.B ;
+.PP
+IP ¥¢¥É¥ì¥¹·¿¤Î¹½Â¤¤ò»ý¤Ä¥ª¥×¥·¥ç¥ó¤Ï¡¢¥É¥á¥¤¥ó̾¤â¤·¤¯¤Ï
+¥É¥Ã¥È¶èÀÚ¤ê¤Î 4 À°¿ô¤Çɽ¸½¤µ¤ì¤Þ¤¹¡£
+°Ê²¼¤Ï¡¢IP ¥¢¥É¥ì¥¹·¿¤Î»ÈÍÑÎã¤Ç¤¹:
+.nf
+
+option sql-server-address code 193 = ip-address;
+option sql-server-address sql.example.com;
+
+.fi
+.PP
+.B ¥Æ¥­¥¹¥È
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B text
+.B ;
+.PP
+¥Æ¥­¥¹¥È·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢ASCII ¥Æ¥­¥¹¥Èʸ»úÎó¤ò¥¨¥ó¥³¡¼¥É¤·¤Þ¤¹¡£
+Î㤨¤Ð:
+.nf
+
+option sql-default-connection-name code 194 = text;
+option sql-default-connection-name "PRODZA";
+
+.fi
+.PP
+.B ¥Ç¡¼¥¿Ê¸»úÎó
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B string
+.B ;
+.PP
+¥Ç¡¼¥¿Ê¸»úÎ󷿤Υª¥×¥·¥ç¥ó¤Ï¡¢ËܼÁŪ¤Ë¤Ïñ¤Ê¤ë¥Ð¥¤¥È¤Î½¸¹çÂΤǤ¹¡£
+¥Æ¥­¥¹¥È·¿¤Î¤è¤¦¤Ë¥¯¥ª¡¼¥È¤µ¤ì¤¿¥Æ¥­¥¹¥È¤Ç»ØÄꤵ¤ì¤ë¤«¡¢
+¤â¤·¤¯¤Ï¥³¥í¥ó¶èÀÚ¤ê¤Î 16 ¿Ê¿ô¤Î¥ê¥¹¥È¤Ç»ØÄꤵ¤ì¤Þ¤¹¡£
+¤³¤Î»þ¥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿Ãæ¿È¤Ï¡¢0 ¤«¤é FF ¤Î´Ö¤ÎÃͤǤʤ±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó¡£
+Î㤨¤Ð:
+.nf
+
+option sql-identification-token code 195 = string;
+option sql-identification-token 17:23:19:a6:42:ea:99:7c:22;
+
+.fi
+.PP
+.B ¥«¥×¥»¥ë²½
+.PP
+.B option
+.I new-name
+.B code
+.I new-code
+.B =
+.B encapsulate
+.I identifier
+.B ;
+.PP
+¥«¥×¥»¥ë²½·¿¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢\fIidentifier\fR ¤Ç»ØÄꤵ¤ì¤¿
+¥ª¥×¥·¥ç¥ó¶õ´Ö¤ÎÃæ¿È¤ò¥«¥×¥»¥ë²½¤·¤Þ¤¹¡£
+¸½ºß DHCP ¥×¥í¥È¥³¥ë¤Ë¸ºß¤¹¤ë¥«¥×¥»¥ë²½¥ª¥×¥·¥ç¥ó¤ÎÎã¤Ï¡¢
+vendor-encapsulated-options ¥ª¥×¥·¥ç¥ó¡¢netware-suboptions ¥ª¥×¥·¥ç¥ó¡¢
+relay-agent-information ¥ª¥×¥·¥ç¥ó¤Ê¤É¤Ç¤¹¡£
+.nf
+
+option space local;
+option local.demo code 1 = text;
+option local-encapsulation code 197 = encapsulate local;
+option local.demo "demo";
+
+.fi
+.PP
+.B ÇÛÎó
+.PP
+¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Æ¥­¥¹¥È·¿¤È¥Ç¡¼¥¿Ê¸»úÎ󷿰ʳ°¤Î¾å½Ò¤Î¤¤¤«¤Ê¤ë¥Ç¡¼¥¿·¿¤Î
+ÇÛÎó¤â´Þ¤à¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+¥Æ¥­¥¹¥È·¿¤È¥Ç¡¼¥¿Ê¸»úÎ󷿤ϡ¢¸½ºßÇÛÎó¤Ç¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+ÇÛÎóÄêµÁ¤ÎÎã¤Ï°Ê²¼¤ÎÄ̤ê¤Ç¤¹:
+.nf
+
+option kerberos-servers code 200 = array of ip-address;
+option kerberos-servers 10.20.10.1, 10.20.11.1;
+
+.fi
+.B ¥ì¥³¡¼¥É
+.PP
+¥ª¥×¥·¥ç¥ó¤Ï¡¢¥Ç¡¼¥¿·¿¤ÎÎó¤Ç¹½À®¤µ¤ì¤ë¥Ç¡¼¥¿¹½Â¤¤ò´Þ¤à¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+¤³¤ì¤Ï¤·¤Ð¤·¤Ð¥ì¥³¡¼¥É·¿¤È¸Æ¤Ð¤ì¤Þ¤¹¡£
+Î㤨¤Ð:
+.nf
+
+option contrived-001 code 201 = { boolean, integer 32, text };
+option contrived-001 on 1772 "contrivance";
+
+.fi
+¤Þ¤¿¥ì¥³¡¼¥É¤ÎÇÛÎó¤Î¥ª¥×¥·¥ç¥ó¤ò»ý¤Ä¤³¤È¤â¤Ç¤­¤Þ¤¹¡£
+Î㤨¤Ð:
+.nf
+
+option new-static-routes code 201 = array of {
+ ip-address, ip-address, ip-address, integer 8 };
+option static-routes
+ 10.0.0.0 255.255.255.0 net-0-rtr.example.com 1,
+ 10.0.1.0 255.255.255.0 net-1-rtr.example.com 1,
+ 10.2.0.0 255.255.224.0 net-2-0-rtr.example.com 3;
+
+.fi
+.SH ¥Ù¥ó¥À¥«¥×¥»¥ë²½¥ª¥×¥·¥ç¥ó
+.\" metal
+DHCP ¥×¥í¥È¥³¥ë¤Ë¤Ï¡¢\fB vendor-encapsulated-options\fR ¥ª¥×¥·¥ç¥ó¤¬
+ÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£
+¥Ù¥ó¥À¤Ï¡¢¤³¤Î¥ª¥×¥·¥ç¥ó¤Ë¤è¤Ã¤Æ¡¢¥Ù¥ó¥À¸ÇÍ­¤Î¥ª¥×¥·¥ç¥ó¤ò
+ɸ½à DHCP ¥ª¥×¥·¥ç¥ó¤Ë´Þ¤á¤ÆÁ÷½Ð¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.B vendor-encapsulated-options
+¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤Ï¡¢½ñ¼°¤¬µ¬Äꤵ¤ì¤Æ¤¤¤Ê¤¤°ìÏ¢¤Î¥Ð¥¤¥ÈÎó¡¢
+¤â¤·¤¯¤Ï°ìÏ¢¤Î¥ª¥×¥·¥ç¥óÎó¤Ç¤¹¡£
+¥ª¥×¥·¥ç¥óÎóÃæ¤Î¤½¤ì¤¾¤ì¤Î¥ª¥×¥·¥ç¥ó¤Ï¡¢1 ¥Ð¥¤¥È¤Î¥Ù¥ó¥À¸ÇÍ­¤Î
+¥ª¥×¥·¥ç¥ó¥³¡¼¥É¤Î¸å¤Ë 1 ¥Ð¥¤¥È¤Î¥Ç¡¼¥¿Ä¹¡¢
+¤½¤·¤Æ¤½¤Î¥Ç¡¼¥¿Ä¹¤Ç»ØÄꤵ¤ì¤¿Â礭¤µ¤Î¥Ç¡¼¥¿¤¬Â³¤¤¤¿¤â¤Î¤Ç¹½À®¤µ¤ì¤Þ¤¹
+(¥Ç¡¼¥¿Ä¹¤Ë¤Ï¡¢¥Ç¡¼¥¿Ä¹¼«¿È¤ä¥ª¥×¥·¥ç¥ó¥³¡¼¥É¤Ï´Þ¤Þ¤ì¤Þ¤»¤ó)¡£
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤ÎÃͤϡ¢2 ¤Ä¤ÎÊýË¡¤Î¤¤¤º¤ì¤«¤ÇÀßÄꤵ¤ì¤Þ¤¹¡£
+1 ÈÖÌܤÎÊýË¡¤Ï¡¢Ã±¤Ë¥Ç¡¼¥¿¤òľÀÜ»ØÄꤹ¤ë¤â¤Î¤Ç¤¹¡£
+¥Ç¡¼¥¿¤Î»ØÄê¤Ë¤Ï¡¢¥Æ¥­¥¹¥Èʸ»úÎ󤫥³¥í¥ó¤Ç¶èÀÚ¤é¤ì¤¿ 16 ¿Ê¿ôÃͤòÍѤ¤¤Þ¤¹¡£
+Î㤨¤Ð:
+.PP
+.nf
+option vendor-encapsulated-options
+ 2:4:AC:11:41:1:
+ 3:12:73:75:6e:64:68:63:70:2d:73:65:72:76:65:72:31:37:2d:31:
+ 4:12:2f:65:78:70:6f:72:74:2f:72:6f:6f:74:2f:69:38:36:70:63;
+.fi
+.PP
+ËÜ¥ª¥×¥·¥ç¥ó¤òÀßÄꤹ¤ë 2 ÈÖÌܤÎÊýË¡¤Ï¡¢DHCP ¥µ¡¼¥Ð¤Ë
+¥Ù¥ó¥À¸ÇÍ­¥ª¥×¥·¥ç¥ó¥Ð¥Ã¥Õ¥¡¤òºîÀ®¤µ¤»¤ë¤È¤¤¤¦¤â¤Î¤Ç¤¹¡£
+¤³¤ì¤ò¤¹¤ë¤Ë¤Ï¡¢°Ê²¼¤Î 4 ¤Ä¤Î¤³¤È¤ò¤¹¤ëɬÍפ¬¤¢¤ê¤Þ¤¹:
+¥ª¥×¥·¥ç¥ó¶õ´Ö¤òÄêµÁ¤·¡¢¤½¤Î¥ª¥×¥·¥ç¥ó¶õ´ÖÆâ¤Ë¥ª¥×¥·¥ç¥ó¤òÄêµÁ¤·¡¢
+¤½¤ì¤é¤ØÃͤò³ä¤ê¿¶¤ê¡¢ºÇ¸å¤Ë¤½¤Î¥ª¥×¥·¥ç¥ó¶õ´Ö¤¬
+.B vendor-encapsulated-options
+¥ª¥×¥·¥ç¥ó¤ÎÀ¸À®¤Ë»ÈÍѤµ¤ì¤ë¤³¤È¤ò»ØÄꤷ¤Þ¤¹¡£
+.PP
+¥Ù¥ó¥À¥ª¥×¥·¥ç¥ó¤¬³ÊǼ¤µ¤ì¤ë¥ª¥×¥·¥ç¥ó¶õ´Ö¤ò¿·µ¬¤ËÄêµÁ¤¹¤ë¤Ë¤Ï¡¢
+\fRoption space\fP ʸ¤ò»ÈÍѤ·¤Þ¤¹:
+.PP
+.B option
+.B space
+.I name
+.B ;
+.PP
+¤³¤Îʸ½ñ¤Ë¤³¤ì¤Þ¤Ç½ñ¤«¤ì¤Æ¤¤¤ë¤è¤¦¤Ë¡¢
+¤³¤Î name ¤Ï¡¢¥ª¥×¥·¥ç¥óÄêµÁ¤Ç»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+Î㤨¤Ð:
+.nf
+
+option space SUNW;
+option SUNW.server-address code 2 = ip-address;
+option SUNW.server-name code 3 = text;
+option SUNW.root-path code 4 = text;
+
+.fi
+°ìÅÙ¡¢¥ª¥×¥·¥ç¥ó¶õ´Ö¤È¥ª¥×¥·¥ç¥ó¤Î½ñ¼°¤òÄêµÁ¤·¤¿¤é¡¢
+¤½¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ÎÃͤòÄêµÁ¤¹¤ë¥¹¥³¡¼¥×¤òÀßÄê¤Ç¤­¡¢
+¤½¤ì¤é¤Î¥ª¥×¥·¥ç¥ó¤ò¤¤¤Ä»È¤¦¤«¤ò»ØÄꤹ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+Î㤨¤Ð¡¢2 ¤Ä¤Î°Û¤Ê¤ë¥¯¥é¥¹¤Î¥¯¥é¥¤¥¢¥ó¥È¤ò°·¤¤¤¿¤¤¤È¤·¤Þ¤·¤ç¤¦¡£
+Á°½Ò¤ÎÎã¤Ç¼¨¤·¤¿¥ª¥×¥·¥ç¥ó¶õ´Ö¤ÎÄêµÁ¤ò»È¤Ã¤Æ¡¢°Ê²¼¤Î¤è¤¦¤Ë¡¢
+¥¯¥é¥¤¥¢¥ó¥È¤«¤éÁ÷¤é¤ì¤Æ¤­¤¿ vendor-class-identifier ¥ª¥×¥·¥ç¥ó¤Ë´ð¤Å¤¤¤Æ¡¢
+°Û¤Ê¤ë¥ª¥×¥·¥ç¥ó¤ÎÃͤò°Û¤Ê¤ë¥¯¥é¥¤¥¢¥ó¥È¤ËÁ÷½Ð¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.PP
+.nf
+class "vendor-classes" {
+ match option vendor-class-identifier;
+}
+
+option SUNW.server-address 172.17.65.1;
+option SUNW.server-name "sundhcp-server17-1";
+
+subclass "vendor-classes" "SUNW.Ultra-5_10" {
+ vendor-option-space SUNW;
+ option SUNW.root-path "/export/root/sparc";
+}
+
+subclass "vendor-classes" "SUNW.i86pc" {
+ vendor-option-space SUNW;
+ option SUNW.root-path "/export/root/i86pc";
+}
+.fi
+.PP
+Àè¤ÎÎã¤Ç¸«¤¿¤è¤¦¤Ë¡¢Ä̾ï¤Î¥¹¥³¡¼¥×¥ë¡¼¥ë¤òŬÍѤ¹¤ë¤³¤È¤Ç¡¢
+¥°¥í¡¼¥Ð¥ë¤ÊÃͤò¥°¥í¡¼¥Ð¥ë¥¹¥³¡¼¥×Ãæ¤ËÄêµÁ¤Ç¤­¡¢
+ÆÃÄê¤Î¥¯¥é¥¹¤Ë¸ÇÍ­¤ÎÃͤÀ¤±¤ò¥í¡¼¥«¥ë¥¹¥³¡¼¥×¤ËÄêµÁ¤Ç¤­¤Þ¤¹¡£
+\fBvendor-option-space\fR Àë¸À¤ò»È¤¦¤³¤È¤Ç¡¢
+.B vendor-encapsulated-options
+¥ª¥×¥·¥ç¥ó¤ò¹½À®¤¹¤ë¤Î¤Ë¡¢SUNW ¥ª¥×¥·¥ç¥ó¶õ´ÖÆâ¤Î¥ª¥×¥·¥ç¥ó¤ò»È¤¦¤è¤¦
+DHCP ¥µ¡¼¥Ð¤Ë»Ø¼¨¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+.SH ´ØÏ¢¹àÌÜ
+dhclient.conf(5), dhcp-eval(5),
+dhclient(8), RFC2132, RFC2131
+.SH ºî¼Ô
+Internet Systems Consortium DHCP Distribution
+¤Ï¡¢Vixie Labs ¤È¤Î·ÀÌó¤Î¤â¤È¤Ç¡¢Ted Lemon ¤¬µ­½Ò¤·¤Þ¤·¤¿¡£
+ËÜ¥×¥í¥¸¥§¥¯¥È¤Î»ñ¶â¤Ï¡¢Internet Systems Consortium ¤¬Ä󶡤·¤Þ¤·¤¿¡£
+Internet Systems Consortium ¤Ë´Ø¤¹¤ë¾ðÊó¤Ï¡¢
+.B https://www.isc.org
+¤Ë¤¢¤ê¤Þ¤¹¡£
diff --git a/dst/Makefile.am b/dst/Makefile.am
new file mode 100644
index 0000000..8937fe8
--- /dev/null
+++ b/dst/Makefile.am
@@ -0,0 +1,8 @@
+AM_CPPFLAGS = -DMINIRES_LIB -DHMAC_MD5
+
+lib_LIBRARIES = libdst.a
+
+libdst_a_SOURCES = dst_support.c dst_api.c hmac_link.c md5_dgst.c \
+ base64.c prandom.c
+
+EXTRA_DIST = dst_internal.h md5.h md5_locl.h
diff --git a/dst/Makefile.in b/dst/Makefile.in
new file mode 100644
index 0000000..35c35b0
--- /dev/null
+++ b/dst/Makefile.in
@@ -0,0 +1,439 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = dst
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLIBRARIES_INSTALL = $(INSTALL_DATA)
+LIBRARIES = $(lib_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libdst_a_AR = $(AR) $(ARFLAGS)
+libdst_a_LIBADD =
+am_libdst_a_OBJECTS = dst_support.$(OBJEXT) dst_api.$(OBJEXT) \
+ hmac_link.$(OBJEXT) md5_dgst.$(OBJEXT) base64.$(OBJEXT) \
+ prandom.$(OBJEXT)
+libdst_a_OBJECTS = $(am_libdst_a_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libdst_a_SOURCES)
+DIST_SOURCES = $(libdst_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -DMINIRES_LIB -DHMAC_MD5
+lib_LIBRARIES = libdst.a
+libdst_a_SOURCES = dst_support.c dst_api.c hmac_link.c md5_dgst.c \
+ base64.c prandom.c
+
+EXTRA_DIST = dst_internal.h md5.h md5_locl.h
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign dst/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign dst/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLIBRARIES: $(lib_LIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+ @$(POST_INSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ p=$(am__strip_dir) \
+ echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \
+ $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \
+ else :; fi; \
+ done
+
+uninstall-libLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLIBRARIES:
+ -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+libdst.a: $(libdst_a_OBJECTS) $(libdst_a_DEPENDENCIES)
+ -rm -f libdst.a
+ $(libdst_a_AR) libdst.a $(libdst_a_OBJECTS) $(libdst_a_LIBADD)
+ $(RANLIB) libdst.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dst_api.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dst_support.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hmac_link.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_dgst.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prandom.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-libLIBRARIES
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLIBRARIES ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-libLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/dst/base64.c b/dst/base64.c
new file mode 100644
index 0000000..1c6d59b
--- /dev/null
+++ b/dst/base64.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#if !defined(LINT) && !defined(CODECENTER)
+static const char rcsid[] = "$Id: base64.c,v 1.5.6.1 2009-11-20 01:49:01 sar Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/socket.h>
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "arpa/nameser.h"
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC 1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int
+b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
+ size_t datalength = 0;
+ u_char input[3];
+ u_char output[4];
+ size_t i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+ Assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* skips all whitespace anywhere.
+ converts characters, four at a time, starting at (or after)
+ src from base - 64 numbers into three 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+b64_pton(src, target, targsize)
+ char const *src;
+ u_char *target;
+ size_t targsize;
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (isspace(ch)) /* Skip whitespace anywhere. */
+ continue;
+
+ if (ch == Pad64)
+ break;
+
+ pos = strchr(Base64, ch);
+ if (pos == 0) /* A non-base64 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base64) << 2;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 4;
+ target[tarindex+1] = ((pos - Base64) & 0x0f)
+ << 4 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 2;
+ target[tarindex+1] = ((pos - Base64) & 0x03)
+ << 6;
+ }
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64);
+ }
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ break;
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64)
+ return (-1);
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ return (-1);
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
diff --git a/dst/dst_api.c b/dst/dst_api.c
new file mode 100644
index 0000000..8925c66
--- /dev/null
+++ b/dst/dst_api.c
@@ -0,0 +1,1089 @@
+#ifndef LINT
+static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/dst_api.c,v 1.9 2009-10-29 00:46:48 sar Exp $";
+#endif
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+/*
+ * This file contains the interface between the DST API and the crypto API.
+ * This is the only file that needs to be changed if the crypto system is
+ * changed. Exported functions are:
+ * void dst_init() Initialize the toolkit
+ * int dst_check_algorithm() Function to determines if alg is supported.
+ * int dst_compare_keys() Function to compare two keys for equality.
+ * int dst_sign_data() Incremental signing routine.
+ * int dst_verify_data() Incremental verify routine.
+ * int dst_generate_key() Function to generate new KEY
+ * DST_KEY *dst_read_key() Function to retrieve private/public KEY.
+ * void dst_write_key() Function to write out a key.
+ * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST
+ * KEY structure.
+ * int dst_key_to_dnskey() Function to return a public key in DNS
+ * format binary
+ * DST_KEY *dst_buffer_to_key() Convert a data in buffer to KEY
+ * int *dst_key_to_buffer() Writes out DST_KEY key material in buffer
+ * void dst_free_key() Releases all memory referenced by key structure
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "arpa/nameser.h"
+
+#include "dst_internal.h"
+
+/* static variables */
+static int done_init = 0;
+dst_func *dst_t_func[DST_MAX_ALGS];
+const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n";
+const char *dst_path = "";
+
+/* internal I/O functions */
+static DST_KEY *dst_s_read_public_key(const char *in_name,
+ const unsigned in_id, int in_alg);
+static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key,
+ unsigned in_id, int in_alg);
+static int dst_s_write_public_key(const DST_KEY *key);
+static int dst_s_write_private_key(const DST_KEY *key);
+
+/* internal function to set up data structure */
+static DST_KEY *dst_s_get_key_struct(const char *name, const int alg,
+ const u_int32_t flags, const int protocol,
+ const int bits);
+
+/*
+ * dst_init
+ * This function initializes the Digital Signature Toolkit.
+ * Right now, it just checks the DSTKEYPATH environment variable.
+ * Parameters
+ * none
+ * Returns
+ * none
+ */
+void
+dst_init()
+{
+ char *s;
+ unsigned len;
+
+ if (done_init != 0)
+ return;
+ done_init = 1;
+
+ s = getenv("DSTKEYPATH");
+ len = 0;
+ if (s) {
+ struct stat statbuf;
+
+ len = strlen(s);
+ if (len > PATH_MAX) {
+ EREPORT(("%s is longer than %d characters, ignoring\n",
+ s, PATH_MAX));
+ } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
+ EREPORT(("%s is not a valid directory\n", s));
+ } else {
+ char *dp = (char *) malloc(len + 2);
+ int l;
+ memcpy(dp, s, len + 1);
+ l = strlen (dp);
+ if (dp[l - 1] != '/') {
+ dp[l + 1] = 0;
+ dp[l] = '/';
+ }
+ dst_path = dp;
+ }
+ }
+ memset(dst_t_func, 0, sizeof(dst_t_func));
+ /* first one is selected */
+#if 0
+ dst_bsafe_init();
+ dst_rsaref_init();
+#endif
+ dst_hmac_md5_init();
+#if 0
+ dst_eay_dss_init();
+ dst_cylink_init();
+#endif
+}
+
+/*
+ * dst_check_algorithm
+ * This function determines if the crypto system for the specified
+ * algorithm is present.
+ * Parameters
+ * alg 1 KEY_RSA
+ * 3 KEY_DSA
+ * 157 KEY_HMAC_MD5
+ * future algorithms TBD and registered with IANA.
+ * Returns
+ * 1 - The algorithm is available.
+ * 0 - The algorithm is not available.
+ */
+int
+dst_check_algorithm(const int alg)
+{
+ return (dst_t_func[alg] != NULL);
+}
+
+/*
+ * dst_s_get_key_struct
+ * This function allocates key structure and fills in some of the
+ * fields of the structure.
+ * Parameters:
+ * name: the name of the key
+ * alg: the algorithm number
+ * flags: the dns flags of the key
+ * protocol: the dns protocol of the key
+ * bits: the size of the key
+ * Returns:
+ * NULL if error
+ * valid pointer otherwise
+ */
+static DST_KEY *
+dst_s_get_key_struct(const char *name, const int alg, const u_int32_t flags,
+ const int protocol, const int bits)
+{
+ DST_KEY *new_key = NULL;
+
+ if (dst_check_algorithm(alg)) /* make sure alg is available */
+ new_key = (DST_KEY *) malloc(sizeof(*new_key));
+ if (new_key == NULL)
+ return (NULL);
+
+ memset(new_key, 0, sizeof(*new_key));
+ new_key->dk_key_name = strdup(name);
+ new_key->dk_alg = alg;
+ new_key->dk_flags = flags;
+ new_key->dk_proto = protocol;
+ new_key->dk_KEY_struct = NULL;
+ new_key->dk_key_size = bits;
+ new_key->dk_func = dst_t_func[alg];
+ return (new_key);
+}
+
+/*
+ * dst_compare_keys
+ * Compares two keys for equality.
+ * Parameters
+ * key1, key2 Two keys to be compared.
+ * Returns
+ * 0 The keys are equal.
+ * non-zero The keys are not equal.
+ */
+
+int
+dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+ if (key1 == key2)
+ return (0);
+ if (key1 == NULL || key2 == NULL)
+ return (4);
+ if (key1->dk_alg != key2->dk_alg)
+ return (1);
+ if (key1->dk_key_size != key2->dk_key_size)
+ return (2);
+ if (key1->dk_id != key2->dk_id)
+ return (3);
+ return (key1->dk_func->compare(key1, key2));
+}
+
+
+/*
+ * dst_sign_data
+ * An incremental signing function. Data is signed in steps.
+ * First the context must be initialized (SIG_MODE_INIT).
+ * Then data is hashed (SIG_MODE_UPDATE). Finally the signature
+ * itself is created (SIG_MODE_FINAL). This function can be called
+ * once with INIT, UPDATE and FINAL modes all set, or it can be
+
+ * called separately with a different mode set for each step. The
+ * UPDATE step can be repeated.
+ * Parameters
+ * mode A bit mask used to specify operation(s) to be performed.
+ * SIG_MODE_INIT 1 Initialize digest
+ * SIG_MODE_UPDATE 2 Add data to digest
+ * SIG_MODE_FINAL 4 Generate signature
+ * from signature
+ * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL
+ * data Data to be signed.
+ * len The length in bytes of data to be signed.
+ * in_key Contains a private key to sign with.
+ * KEY structures should be handled (created, converted,
+ * compared, stored, freed) by the DST.
+ * signature
+ * The location to which the signature will be written.
+ * sig_len Length of the signature field in bytes.
+ * Return
+ * 0 Successful INIT or Update operation
+ * >0 success FINAL (sign) operation
+ * <0 failure
+ */
+
+int
+dst_sign_data(const int mode, DST_KEY *in_key, void **context,
+ const u_char *data, const unsigned len,
+ u_char *signature, const unsigned sig_len)
+{
+ DUMP(data, mode, len, "dst_sign_data()");
+
+ if (mode & SIG_MODE_FINAL &&
+ (in_key->dk_KEY_struct == NULL || signature == NULL))
+ return (MISSING_KEY_OR_SIGNATURE);
+
+ if (in_key->dk_func && in_key->dk_func->sign)
+ return (in_key->dk_func->sign(mode, in_key, context, data, len,
+ signature, sig_len));
+ return (UNKNOWN_KEYALG);
+}
+
+
+/*
+ * dst_verify_data
+ * An incremental verify function. Data is verified in steps.
+ * First the context must be initialized (SIG_MODE_INIT).
+ * Then data is hashed (SIG_MODE_UPDATE). Finally the signature
+ * is verified (SIG_MODE_FINAL). This function can be called
+ * once with INIT, UPDATE and FINAL modes all set, or it can be
+ * called separately with a different mode set for each step. The
+ * UPDATE step can be repeated.
+ * Parameters
+ * mode Operations to perform this time.
+ * SIG_MODE_INIT 1 Initialize digest
+ * SIG_MODE_UPDATE 2 add data to digest
+ * SIG_MODE_FINAL 4 verify signature
+ * SIG_MODE_ALL
+ * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL)
+ * data Data to pass through the hash function.
+ * len Length of the data in bytes.
+ * in_key Key for verification.
+ * signature Location of signature.
+ * sig_len Length of the signature in bytes.
+ * Returns
+ * 0 Verify success
+ * Non-Zero Verify Failure
+ */
+
+int
+dst_verify_data(const int mode, DST_KEY *in_key, void **context,
+ const u_char *data, const unsigned len,
+ const u_char *signature, const unsigned sig_len)
+{
+ DUMP(data, mode, len, "dst_verify_data()");
+ if (mode & SIG_MODE_FINAL &&
+ (in_key->dk_KEY_struct == NULL || signature == NULL))
+ return (MISSING_KEY_OR_SIGNATURE);
+
+ if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL)
+ return (UNSUPPORTED_KEYALG);
+ return (in_key->dk_func->verify(mode, in_key, context, data, len,
+ signature, sig_len));
+}
+
+
+/*
+ * dst_read_private_key
+ * Access a private key. First the list of private keys that have
+ * already been read in is searched, then the key accessed on disk.
+ * If the private key can be found, it is returned. If the key cannot
+ * be found, a null pointer is returned. The options specify required
+ * key characteristics. If the private key requested does not have
+ * these characteristics, it will not be read.
+ * Parameters
+ * in_keyname The private key name.
+ * in_id The id of the private key.
+ * options DST_FORCE_READ Read from disk - don't use a previously
+ * read key.
+ * DST_CAN_SIGN The key must be usable for signing.
+ * DST_NO_AUTHEN The key must be usable for authentication.
+ * DST_STANDARD Return any key
+ * Returns
+ * NULL If there is no key found in the current directory or
+ * this key has not been loaded before.
+ * !NULL Success - KEY structure returned.
+ */
+
+DST_KEY *
+dst_read_key(const char *in_keyname, const unsigned in_id,
+ const int in_alg, const int type)
+{
+ char keyname[PATH_MAX];
+ DST_KEY *dg_key = NULL, *pubkey = NULL;
+
+ if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */
+ EREPORT(("dst_read_private_key(): Algorithm %d not supported\n",
+ in_alg));
+ return (NULL);
+ }
+ if ((type && (DST_PUBLIC | DST_PRIVATE)) == 0)
+ return (NULL);
+ if (in_keyname == NULL) {
+ EREPORT(("dst_read_private_key(): Null key name passed in\n"));
+ return (NULL);
+ } else
+ strcpy(keyname, in_keyname);
+
+ /* before I read in the public key, check if it is allowed to sign */
+ if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL)
+ return (NULL);
+
+ if (type == DST_PUBLIC)
+ return pubkey;
+
+ if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg,
+ pubkey->dk_flags, pubkey->dk_proto,
+ 0)))
+ return (dg_key);
+ /* Fill in private key and some fields in the general key structure */
+ if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id,
+ pubkey->dk_alg) == 0)
+ dg_key = dst_free_key(dg_key);
+
+ pubkey = dst_free_key(pubkey);
+ return (dg_key);
+}
+
+int
+dst_write_key(const DST_KEY *key, const int type)
+{
+ int pub = 0, priv = 0;
+
+ if (key == NULL)
+ return (0);
+ if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */
+ EREPORT(("dst_write_key(): Algorithm %d not supported\n",
+ key->dk_alg));
+ return (UNSUPPORTED_KEYALG);
+ }
+ if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0)
+ return (0);
+
+ if (type & DST_PUBLIC)
+ if ((pub = dst_s_write_public_key(key)) < 0)
+ return (pub);
+ if (type & DST_PRIVATE)
+ if ((priv = dst_s_write_private_key(key)) < 0)
+ return (priv);
+ return (priv+pub);
+}
+
+/*
+ * dst_write_private_key
+ * Write a private key to disk. The filename will be of the form:
+ * K<key->dk_name>+<key->dk_alg>+<key->dk_id>.<private key suffix>.
+ * If there is already a file with this name, an error is returned.
+ *
+ * Parameters
+ * key A DST managed key structure that contains
+ * all information needed about a key.
+ * Return
+ * >= 0 Correct behavior. Returns length of encoded key value
+ * written to disk.
+ * < 0 error.
+ */
+
+static int
+dst_s_write_private_key(const DST_KEY *key)
+{
+ u_char encoded_block[RAW_KEY_SIZE];
+ char file[PATH_MAX];
+ unsigned len;
+ FILE *fp;
+
+ /* First encode the key into the portable key format */
+ if (key == NULL)
+ return (-1);
+ if (key->dk_KEY_struct == NULL)
+ return (0); /* null key has no private key */
+
+ if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) {
+ EREPORT(("dst_write_private_key(): Unsupported operation %d\n",
+ key->dk_alg));
+ return (-5);
+ } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block,
+ sizeof(encoded_block))) <= 0) {
+ EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len));
+ return (-8);
+ }
+ /* Now I can create the file I want to use */
+ dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg,
+ PRIVATE_KEY, PATH_MAX);
+
+ /* Do not overwrite an existing file */
+ if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) {
+ int nn;
+ if ((nn = fwrite(encoded_block, 1, len, fp)) != len) {
+ EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n",
+ file, out_len, nn, errno));
+ return (-5);
+ }
+ fclose(fp);
+ } else {
+ EREPORT(("dst_write_private_key(): Can not create file %s\n"
+ ,file));
+ return (-6);
+ }
+ memset(encoded_block, 0, len);
+ return (len);
+}
+
+/*
+*
+ * dst_read_public_key
+ * Read a public key from disk and store in a DST key structure.
+ * Parameters
+ * in_name K<in_name><in_id>.<public key suffix> is the
+ * filename of the key file to be read.
+ * Returns
+ * NULL If the key does not exist or no name is supplied.
+ * NON-NULL Initialized key structure if the key exists.
+ */
+
+static DST_KEY *
+dst_s_read_public_key(const char *in_name, const unsigned in_id, int in_alg)
+{
+ unsigned flags, len;
+ int proto, alg, dlen;
+ int c;
+ char name[PATH_MAX], enckey[RAW_KEY_SIZE];
+ unsigned char *notspace;
+ u_char deckey[RAW_KEY_SIZE];
+ FILE *fp;
+
+ if (in_name == NULL) {
+ EREPORT(("dst_read_public_key(): No key name given\n"));
+ return (NULL);
+ }
+ if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY,
+ PATH_MAX) == -1) {
+ EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n",
+ in_name, in_id, PUBLIC_KEY));
+ return (NULL);
+ }
+ /*
+ * Open the file and read it's formatted contents up to key
+ * File format:
+ * domain.name [ttl] [IN] KEY <flags> <protocol> <algorithm> <key>
+ * flags, proto, alg stored as decimal (or hex numbers FIXME).
+ * (FIXME: handle parentheses for line continuation.)
+ */
+ if ((fp = dst_s_fopen(name, "r", 0)) == NULL) {
+ EREPORT(("dst_read_public_key(): Public Key not found %s\n",
+ name));
+ return (NULL);
+ }
+ /* Skip domain name, which ends at first blank */
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ /* Skip blank to get to next field */
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+
+ /* Skip optional TTL -- if initial digit, skip whole word. */
+ if (isdigit(c)) {
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ }
+ /* Skip optional "IN" */
+ if (c == 'I' || c == 'i') {
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ }
+ /* Locate and skip "KEY" */
+ if (c != 'K' && c != 'k') {
+ EREPORT(("\"KEY\" doesn't appear in file: %s", name));
+ return NULL;
+ }
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ ungetc(c, fp); /* return the character to the input field */
+ /* Handle hex!! FIXME. */
+
+ if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) {
+ EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n"
+ ,name));
+ return (NULL);
+ }
+ /* read in the key string */
+ if ((fgets(enckey, sizeof(enckey), fp) == NULL) &&
+ (ferror(fp) != 0)) {
+ EREPORT(("dst_read_public_kety(): Error reading key\n"));
+ return (NULL);
+ }
+
+ /* If we aren't at end-of-file, something is wrong. */
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ if (!feof(fp)) {
+ EREPORT(("Key too long in file: %s", name));
+ return NULL;
+ }
+ fclose(fp);
+
+ if ((len = strlen(enckey)) <= 0)
+ return (NULL);
+
+ /* discard \n */
+ enckey[--len] = '\0';
+
+ /* remove leading spaces */
+ for (notspace = (unsigned char *)enckey; isspace(*notspace); len--)
+ notspace++;
+
+ dlen = b64_pton((char *)notspace, deckey, sizeof(deckey));
+ if (dlen < 0) {
+ EREPORT(("dst_read_public_key: bad return from b64_pton = %d",
+ dlen));
+ return (NULL);
+ }
+ /* store key and info in a key structure that is returned */
+/* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey,
+ dlen);*/
+ return dst_buffer_to_key(in_name, alg,
+ flags, proto, deckey, (unsigned)dlen);
+}
+
+
+/*
+ * dst_write_public_key
+ * Write a key to disk in DNS format.
+ * Parameters
+ * key Pointer to a DST key structure.
+ * Returns
+ * 0 Failure
+ * 1 Success
+ */
+
+static int
+dst_s_write_public_key(const DST_KEY *key)
+{
+ FILE *fp;
+ char filename[PATH_MAX];
+ u_char out_key[RAW_KEY_SIZE];
+ char enc_key[RAW_KEY_SIZE];
+ int len = 0;
+
+ memset(out_key, 0, sizeof(out_key));
+ if (key == NULL) {
+ EREPORT(("dst_write_public_key(): No key specified \n"));
+ return (0);
+ } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0)
+ return (0);
+
+ /* Make the filename */
+ if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id,
+ key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) {
+ EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n",
+ key->dk_key_name, key->dk_id, PUBLIC_KEY));
+ return (0);
+ }
+ /* create public key file */
+ if ((fp = dst_s_fopen(filename, "w+", 0644)) == NULL) {
+ EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n",
+ filename, errno));
+ return (0);
+ }
+ /*write out key first base64 the key data */
+ if (key->dk_flags & DST_EXTEND_FLAG)
+ b64_ntop(&out_key[6],
+ (unsigned)(len - 6), enc_key, sizeof(enc_key));
+ else
+ b64_ntop(&out_key[4],
+ (unsigned)(len - 4), enc_key, sizeof(enc_key));
+ fprintf(fp, "%s IN KEY %d %d %d %s\n",
+ key->dk_key_name,
+ key->dk_flags, key->dk_proto, key->dk_alg, enc_key);
+ fclose(fp);
+ return (1);
+}
+
+
+/*
+ * dst_dnskey_to_public_key
+ * This function converts the contents of a DNS KEY RR into a DST
+ * key structure.
+ * Parameters
+ * len Length of the RDATA of the KEY RR RDATA
+ * rdata A pointer to the the KEY RR RDATA.
+ * in_name Key name to be stored in key structure.
+ * Returns
+ * NULL Failure
+ * NON-NULL Success. Pointer to key structure.
+ * Caller's responsibility to free() it.
+ */
+
+DST_KEY *
+dst_dnskey_to_key(const char *in_name,
+ const u_char *rdata, const unsigned len)
+{
+ DST_KEY *key_st;
+ int alg ;
+ int start = DST_KEY_START;
+
+ if (rdata == NULL || len <= DST_KEY_ALG) /* no data */
+ return (NULL);
+ alg = (u_int8_t) rdata[DST_KEY_ALG];
+ if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+ EREPORT(("dst_dnskey_to_key(): Algorithm %d not supported\n",
+ alg));
+ return (NULL);
+ }
+ if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL)
+ return (NULL);
+
+ if (in_name == NULL)
+ return (NULL);
+ key_st->dk_flags = dst_s_get_int16(rdata);
+ key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT];
+ if (key_st->dk_flags & DST_EXTEND_FLAG) {
+ u_int32_t ext_flags;
+ ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]);
+ key_st->dk_flags = key_st->dk_flags | (ext_flags << 16);
+ start += 2;
+ }
+ /*
+ * now point to the beginning of the data representing the encoding
+ * of the key
+ */
+ if (key_st->dk_func && key_st->dk_func->from_dns_key) {
+ if (key_st->dk_func->from_dns_key(key_st, &rdata[start],
+ len - start) > 0)
+ return (key_st);
+ } else
+ EREPORT(("dst_dnskey_to_public_key(): unsupported alg %d\n",
+ alg));
+
+ SAFE_FREE(key_st);
+ return (key_st);
+}
+
+
+/*
+ * dst_public_key_to_dnskey
+ * Function to encode a public key into DNS KEY wire format
+ * Parameters
+ * key Key structure to encode.
+ * out_storage Location to write the encoded key to.
+ * out_len Size of the output array.
+ * Returns
+ * <0 Failure
+ * >=0 Number of bytes written to out_storage
+ */
+
+int
+dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage,
+ const unsigned out_len)
+{
+ u_int16_t val;
+ int loc = 0;
+ int enc_len = 0;
+ if (key == NULL)
+ return (-1);
+
+ if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */
+ EREPORT(("dst_key_to_dnskey(): Algorithm %d not supported\n",
+ key->dk_alg));
+ return (UNSUPPORTED_KEYALG);
+ }
+ memset(out_storage, 0, out_len);
+ val = (u_int16_t)(key->dk_flags & 0xffff);
+ out_storage[0] = (val >> 8) & 0xff;
+ out_storage[1] = val & 0xff;
+ loc += 2;
+
+ out_storage[loc++] = (u_char) key->dk_proto;
+ out_storage[loc++] = (u_char) key->dk_alg;
+
+ if (key->dk_flags > 0xffff) { /* Extended flags */
+ val = (u_int16_t)((key->dk_flags >> 16) & 0xffff);
+ out_storage[loc] = (val >> 8) & 0xff;
+ out_storage[loc+1] = val & 0xff;
+ loc += 2;
+ }
+ if (key->dk_KEY_struct == NULL)
+ return (loc);
+ if (key->dk_func && key->dk_func->to_dns_key) {
+ enc_len = key->dk_func->to_dns_key(key,
+ (u_char *) &out_storage[loc],
+ out_len - loc);
+ if (enc_len > 0)
+ return (enc_len + loc);
+ else
+ return (-1);
+ } else
+ EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n",
+ key->dk_alg));
+ return (-1);
+}
+
+
+/*
+ * dst_buffer_to_key
+ * Function to encode a string of raw data into a DST key
+ * Parameters
+ * alg The algorithm (HMAC only)
+ * key A pointer to the data
+ * keylen The length of the data
+ * Returns
+ * NULL an error occurred
+ * NON-NULL the DST key
+ */
+DST_KEY *
+dst_buffer_to_key(const char *key_name, /* name of the key */
+ const int alg, /* algorithm */
+ const unsigned flags, /* dns flags */
+ const int protocol, /* dns protocol */
+ const u_char *key_buf, /* key in dns wire fmt */
+ const unsigned key_len) /* size of key */
+{
+
+ DST_KEY *dkey = NULL;
+
+ if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+ EREPORT(("dst_buffer_to_key(): Algorithm %d not supported\n", alg));
+ return (NULL);
+ }
+
+ dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1);
+
+ if (dkey == NULL)
+ return (NULL);
+ if (dkey->dk_func != NULL &&
+ dkey->dk_func->from_dns_key != NULL) {
+ if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) {
+ EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n"));
+ return (dst_free_key(dkey));
+ }
+ return (dkey);
+ }
+ return (NULL);
+}
+
+int
+dst_key_to_buffer(DST_KEY *key, u_char *out_buff, unsigned buf_len)
+{
+ int len;
+ /* this function will extract the secret of HMAC into a buffer */
+ if(key == NULL)
+ return (0);
+ if(key->dk_func != NULL && key->dk_func != NULL) {
+ len = key->dk_func->to_dns_key(key, out_buff, buf_len);
+ if (len < 0)
+ return (0);
+ return (len);
+ }
+ return (0);
+}
+
+
+/*
+ * dst_s_read_private_key_file
+ * Function reads in private key from a file.
+ * Fills out the KEY structure.
+ * Parameters
+ * name Name of the key to be read.
+ * pk_key Structure that the key is returned in.
+ * in_id Key identifier (tag)
+ * Return
+ * 1 if everything works
+ * 0 if there is any problem
+ */
+
+static int
+dst_s_read_private_key_file(char *name, DST_KEY *pk_key, unsigned in_id,
+ int in_alg)
+{
+ int cnt, alg, len, major, minor, file_major, file_minor;
+ int id;
+ char filename[PATH_MAX];
+ u_char in_buff[RAW_KEY_SIZE];
+ char *p;
+ FILE *fp;
+
+ if (name == NULL || pk_key == NULL) {
+ EREPORT(("dst_read_private_key_file(): No key name given\n"));
+ return (0);
+ }
+ /* Make the filename */
+ if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY,
+ PATH_MAX) == -1) {
+ EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n",
+ name, in_id, PRIVATE_KEY));
+ return (0);
+ }
+ /* first check if we can find the key file */
+ if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) {
+ EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n",
+ filename, dst_path[0] ? dst_path :
+ (char *) getcwd(NULL, PATH_MAX - 1)));
+ return (0);
+ }
+ /* now read the header info from the file */
+ if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) {
+ fclose(fp);
+ EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n",
+ filename));
+ return (0);
+ }
+ /* decrypt key */
+ fclose(fp);
+ if (memcmp(in_buff, "Private-key-format: v", 20) != 0)
+ goto fail;
+ len = cnt;
+ p = (char *)in_buff;
+
+ if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) {
+ EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name));
+ goto fail;
+ }
+ /* read in file format */
+ sscanf(p, "%d.%d", &file_major, &file_minor);
+ sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor);
+ if (file_major < 1) {
+ EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n",
+ file_major, file_minor, name));
+ goto fail;
+ } else if (file_major > major || file_minor > minor)
+ EREPORT((
+ "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n",
+ name, file_major, file_minor));
+
+ while (*p++ != '\n') ; /* skip to end of line */
+
+ if (!dst_s_verify_str((const char **) &p, "Algorithm: "))
+ goto fail;
+
+ if (sscanf(p, "%d", &alg) != 1)
+ goto fail;
+ while (*p++ != '\n') ; /* skip to end of line */
+
+ if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name))
+ SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name));
+ pk_key->dk_key_name = (char *) strdup(name);
+
+ /* allocate and fill in key structure */
+ if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL)
+ goto fail;
+
+ id = pk_key->dk_func->from_file_fmt(pk_key, (char *)p,
+ (unsigned)(&in_buff[len] - (u_char *)p));
+ if (id < 0)
+ goto fail;
+
+ /* Make sure the actual key tag matches the input tag used in the filename
+ */
+ if (id != in_id) {
+ EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id));
+ goto fail;
+ }
+ pk_key->dk_id = (u_int16_t) id;
+ pk_key->dk_alg = alg;
+ memset(in_buff, 0, (unsigned)cnt);
+ return (1);
+
+ fail:
+ memset(in_buff, 0, (unsigned)cnt);
+ return (0);
+}
+
+
+/*
+ * dst_generate_key
+ * Generate and store a public/private keypair.
+ * Keys will be stored in formatted files.
+ * Parameters
+ * name Name of the new key. Used to create key files
+ * K<name>+<alg>+<id>.public and K<name>+<alg>+<id>.private.
+ * bits Size of the new key in bits.
+ * exp What exponent to use:
+ * 0 use exponent 3
+ * non-zero use Fermant4
+ * flags The default value of the DNS Key flags.
+ * The DNS Key RR Flag field is defined in RFC 2065,
+ * section 3.3. The field has 16 bits.
+ * protocol
+ * Default value of the DNS Key protocol field.
+ * The DNS Key protocol field is defined in RFC 2065,
+ * section 3.4. The field has 8 bits.
+ * alg What algorithm to use. Currently defined:
+ * KEY_RSA 1
+ * KEY_DSA 3
+ * KEY_HMAC 157
+ * out_id The key tag is returned.
+ *
+ * Return
+ * NULL Failure
+ * non-NULL the generated key pair
+ * Caller frees the result, and its dk_name pointer.
+ */
+DST_KEY *
+dst_generate_key(const char *name, const int bits, const int exp,
+ const unsigned flags, const int protocol, const int alg)
+{
+ DST_KEY *new_key = NULL;
+ int res;
+ if (name == NULL)
+ return (NULL);
+
+ if (!dst_check_algorithm(alg)) { /* make sure alg is available */
+ EREPORT(("dst_generate_key(): Algorithm %d not supported\n", alg));
+ return (NULL);
+ }
+
+ new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits);
+ if (new_key == NULL)
+ return (NULL);
+ if (bits == 0) /* null key we are done */
+ return (new_key);
+ if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) {
+ EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n",
+ alg));
+ return (dst_free_key(new_key));
+ }
+ if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) {
+ EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n",
+ new_key->dk_key_name, new_key->dk_alg,
+ new_key->dk_key_size, exp));
+ return (dst_free_key(new_key));
+ }
+ return (new_key);
+}
+
+
+/*
+ * dst_free_key
+ * Release all data structures pointed to by a key structure.
+ * Parameters
+ * f_key Key structure to be freed.
+ */
+
+DST_KEY *
+dst_free_key(DST_KEY *f_key)
+{
+
+ if (f_key == NULL)
+ return (f_key);
+ if (f_key->dk_func && f_key->dk_func->destroy)
+ f_key->dk_KEY_struct =
+ f_key->dk_func->destroy(f_key->dk_KEY_struct);
+ else {
+ EREPORT(("dst_free_key(): Unknown key alg %d\n",
+ f_key->dk_alg));
+ free(f_key->dk_KEY_struct); /* SHOULD NOT happen */
+ }
+ if (f_key->dk_KEY_struct) {
+ free(f_key->dk_KEY_struct);
+ f_key->dk_KEY_struct = NULL;
+ }
+ if (f_key->dk_key_name)
+ SAFE_FREE(f_key->dk_key_name);
+ SAFE_FREE(f_key);
+ return (NULL);
+}
+
+/*
+ * dst_sig_size
+ * Return the maximum size of signature from the key specified in bytes
+ * Parameters
+ * key
+ * Returns
+ * bytes
+ */
+int
+dst_sig_size(DST_KEY *key) {
+ switch (key->dk_alg) {
+ case KEY_HMAC_MD5:
+ return (16);
+ case KEY_HMAC_SHA1:
+ return (20);
+ case KEY_RSA:
+ return (key->dk_key_size + 7) / 8;
+ case KEY_DSA:
+ return (40);
+ default:
+ EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg));
+ return -1;
+ }
+}
+
+/*
+ * dst_random
+ * function that multiplexes number of random number generators
+ * Parameters
+ * mode: select the random number generator
+ * wanted is how many bytes of random data are requested
+ * outran is a buffer of size at least wanted for the output data
+ *
+ * Returns
+ * number of bytes written to outran
+ */
+int
+dst_random(const int mode, unsigned wanted, u_char *outran)
+{
+ u_int32_t *buff = NULL, *bp = NULL;
+ int i;
+ if (wanted <= 0 || outran == NULL)
+ return (0);
+
+ switch (mode) {
+ case DST_RAND_SEMI:
+ bp = buff = (u_int32_t *) malloc(wanted+sizeof(u_int32_t));
+ for (i = 0; i < wanted; i+= sizeof(u_int32_t), bp++) {
+ *bp = dst_s_quick_random(i);
+ }
+ memcpy(outran, buff, (unsigned)wanted);
+ SAFE_FREE(buff);
+ return (wanted);
+ case DST_RAND_STD:
+ return (dst_s_semi_random(outran, wanted));
+ case DST_RAND_KEY:
+ return (dst_s_random(outran, wanted));
+ case DST_RAND_DSS:
+ default:
+ /* need error case here XXX OG */
+ return (0);
+ }
+}
+
diff --git a/dst/dst_internal.h b/dst/dst_internal.h
new file mode 100644
index 0000000..4ee9772
--- /dev/null
+++ b/dst/dst_internal.h
@@ -0,0 +1,171 @@
+#ifndef DST_INTERNAL_H
+#define DST_INTERNAL_H
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+#include <limits.h>
+#include <sys/param.h>
+
+#ifndef PATH_MAX
+# ifdef POSIX_PATH_MAX
+# define PATH_MAX POSIX_PATH_MAX
+# else
+# define PATH_MAX 255 /* this is the value of POSIX_PATH_MAX */
+# endif
+#endif
+
+typedef struct dst_key {
+ char *dk_key_name; /* name of the key */
+ int dk_key_size; /* this is the size of the key in bits */
+ int dk_proto; /* what protocols this key can be used for */
+ int dk_alg; /* algorithm number from key record */
+ unsigned dk_flags; /* and the flags of the public key */
+ unsigned dk_id; /* identifier of the key */
+ void *dk_KEY_struct; /* pointer to key in crypto pkg fmt */
+ struct dst_func *dk_func; /* point to crypto pgk specific function table */
+} DST_KEY;
+#define HAS_DST_KEY
+
+#include <isc-dhcp/dst.h>
+/*
+ * define what crypto systems are supported for RSA,
+ * BSAFE is preferred over RSAREF; only one can be set at any time
+ */
+#if defined(BSAFE) && defined(RSAREF)
+# error "Cannot have both BSAFE and RSAREF defined"
+#endif
+
+/* Declare dst_lib specific constants */
+#define KEY_FILE_FORMAT "1.2"
+
+/* suffixes for key file names */
+#define PRIVATE_KEY "private"
+#define PUBLIC_KEY "key"
+
+/* error handling */
+#ifdef REPORT_ERRORS
+#define EREPORT(str) printf str
+#else
+#define EREPORT(str)
+#endif
+
+/* use our own special macro to FRRE memory */
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(a) if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}
+#define SAFE_FREE2(a,s) if (a != NULL && s > 0){memset(a,0, s);free(a); a=NULL;}
+#endif
+
+typedef struct dst_func {
+ int (*sign)(const int mode, DST_KEY *key, void **context,
+ const u_int8_t *data, const unsigned len,
+ u_int8_t *signature, const unsigned sig_len);
+ int (*verify)(const int mode, DST_KEY *key, void **context,
+ const u_int8_t *data, const unsigned len,
+ const u_int8_t *signature, const unsigned sig_len);
+ int (*compare)(const DST_KEY *key1, const DST_KEY *key2);
+ int (*generate)(DST_KEY *key, int parms);
+ void *(*destroy)(void *key);
+ /* conversion functions */
+ int (*to_dns_key)(const DST_KEY *key, u_int8_t *out,
+ const unsigned out_len);
+ int (*from_dns_key)(DST_KEY *key, const u_int8_t *str,
+ const unsigned str_len);
+ int (*to_file_fmt)(const DST_KEY *key, char *out,
+ const unsigned out_len);
+ int (*from_file_fmt)(DST_KEY *key, const char *out,
+ const unsigned out_len);
+
+} dst_func;
+
+extern dst_func *dst_t_func[DST_MAX_ALGS];
+extern const char *key_file_fmt_str;
+extern const char *dst_path;
+
+#ifndef DST_HASH_SIZE
+#define DST_HASH_SIZE 20 /* RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */
+#endif
+
+#if 0
+int dst_bsafe_init(void);
+int dst_rsaref_init(void);
+#endif
+
+int dst_hmac_md5_init(void);
+
+#if 0
+int dst_cylink_init(void);
+int dst_eay_dss_init(void);
+#endif
+
+/* support functions */
+/* base64 to bignum conversion routines */
+int dst_s_conv_bignum_u8_to_b64( char *out_buf, const unsigned out_len,
+ const char *header,
+ const u_int8_t *bin_data,
+ const unsigned bin_len);
+int dst_s_conv_bignum_b64_to_u8( const char **buf, u_int8_t *loc,
+ const unsigned loclen) ;
+/* from higher level support routines */
+int dst_s_calculate_bits( const u_int8_t *str, const int max_bits);
+int dst_s_verify_str( const char **buf, const char *str);
+
+
+/* conversion between dns names and key file names */
+size_t dst_s_filename_length( const char *name, const char *suffix);
+int dst_s_build_filename( char *filename, const char *name,
+ unsigned id, int alg, const char *suffix,
+ size_t filename_length);
+
+FILE *dst_s_fopen (const char *filename, const char *mode, unsigned perm);
+
+/* from file prandom.c */
+int dst_s_random( u_int8_t *output, unsigned size);
+int dst_s_semi_random( u_int8_t *output, unsigned size);
+u_int32_t dst_s_quick_random( int inc);
+void dst_s_quick_random_set( u_int32_t val, u_int32_t cnt);
+
+/*
+ * read and write network byte order into u_int?_t
+ * all of these should be retired
+ */
+u_int16_t dst_s_get_int16( const u_int8_t *buf);
+void dst_s_put_int16( u_int8_t *buf, const u_int16_t val);
+
+u_int32_t dst_s_get_int32( const u_int8_t *buf);
+void dst_s_put_int32( u_int8_t *buf, const u_int32_t val);
+
+#ifdef DUMP
+# undef DUMP
+# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d)
+#else
+# define DUMP(a,b,c,d)
+#endif
+
+#if defined (MINIRES_LIB)
+#define b64_pton MRb64_pton
+#define b64_ntop MRb64_ntop
+
+int b64_pton (char const *, unsigned char *, size_t);
+int b64_ntop (unsigned char const *, size_t, char *, size_t);
+
+#define USE_MD5
+#endif
+
+
+#endif /* DST_INTERNAL_H */
diff --git a/dst/dst_support.c b/dst/dst_support.c
new file mode 100644
index 0000000..ab89bda
--- /dev/null
+++ b/dst/dst_support.c
@@ -0,0 +1,465 @@
+static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/dst_support.c,v 1.6.6.1 2009-11-20 01:49:01 sar Exp $";
+
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <memory.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "arpa/nameser.h"
+
+#include "dst_internal.h"
+
+/*
+ * dst_s_conv_bignum_u8_to_b64
+ * This function converts binary data stored as a u_char[] to a
+ * base-64 string. Leading zeroes are discarded. If a header is
+ * supplied, it is prefixed to the input prior to encoding. The
+ * output is \n\0 terminated (the \0 is not included in output length).
+ * Parameters
+ * out_buf binary data to convert
+ * header character string to prefix to the output (label)
+ * bin_data binary data
+ * bin_len size of binary data
+ * Return
+ * -1 not enough space in output work area
+ * 0 no output
+ * >0 number of bytes written to output work area
+ */
+
+int
+dst_s_conv_bignum_u8_to_b64(char *out_buf, const unsigned out_len,
+ const char *header, const u_char *bin_data,
+ const unsigned bin_len)
+{
+ const u_char *bp = bin_data;
+ char *op = out_buf;
+ unsigned lenh = 0, len64 = 0;
+ unsigned local_in_len = bin_len;
+ unsigned local_out_len = out_len;
+
+ if (bin_data == NULL) /* no data no */
+ return (0);
+
+ if (out_buf == NULL || out_len <= 0) /* no output_work area */
+ return (-1);
+
+ /* suppress leading \0 */
+ for (; (*bp == 0x0) && (local_in_len > 0); local_in_len--)
+ bp++;
+
+ if (header) { /* add header to output string */
+ lenh = strlen(header);
+ if (lenh < out_len)
+ memcpy(op, header, lenh);
+ else
+ return (-1);
+ local_out_len -= lenh;
+ op += lenh;
+ }
+ len64 = b64_ntop(bp, local_in_len, op, local_out_len - 2);
+ if (len64 < 0)
+ return (-1);
+ op += len64++;
+ *(op++) = '\n'; /* put CR in the output */
+ *op = '\0'; /* make sure output is 0 terminated */
+ return (lenh + len64);
+}
+
+
+/*
+ * dst_s_verify_str()
+ * Validate that the input string(*str) is at the head of the input
+ * buffer(**buf). If so, move the buffer head pointer (*buf) to
+ * the first byte of data following the string(*str).
+ * Parameters
+ * buf Input buffer.
+ * str Input string.
+ * Return
+ * 0 *str is not the head of **buff
+ * 1 *str is the head of **buff, *buf is is advanced to
+ * the tail of **buf.
+ */
+
+int
+dst_s_verify_str(const char **buf, const char *str)
+{
+ unsigned b, s;
+ if (*buf == NULL) /* error checks */
+ return (0);
+ if (str == NULL || *str == '\0')
+ return (1);
+
+ b = strlen(*buf); /* get length of strings */
+ s = strlen(str);
+ if (s > b || strncmp(*buf, str, s)) /* check if same */
+ return (0); /* not a match */
+ (*buf) += s; /* advance pointer */
+ return (1);
+}
+
+
+/*
+ * dst_s_conv_bignum_b64_to_u8
+ * Read a line of base-64 encoded string from the input buffer,
+ * convert it to binary, and store it in an output area. The
+ * input buffer is read until reaching a newline marker or the
+ * end of the buffer. The binary data is stored in the last X
+ * number of bytes of the output area where X is the size of the
+ * binary output. If the operation is successful, the input buffer
+ * pointer is advanced. This procedure does not do network to host
+ * byte order conversion.
+ * Parameters
+ * buf Pointer to encoded input string. Pointer is updated if
+ * function is successful.
+ * loc Output area.
+ * loclen Size in bytes of output area.
+ * Return
+ * >0 Return = number of bytes of binary data stored in loc.
+ * 0 Failure.
+ */
+
+int
+dst_s_conv_bignum_b64_to_u8(const char **buf,
+ u_char *loc, const unsigned loclen)
+{
+ unsigned blen;
+ char *bp;
+ u_char bstr[RAW_KEY_SIZE];
+
+ if (buf == NULL || *buf == NULL) { /* error checks */
+ EREPORT(("dst_s_conv_bignum_b64_to_u8: null input buffer.\n"));
+ return (0);
+ }
+ bp = strchr(*buf, '\n'); /* find length of input line */
+ if (bp != NULL)
+ *bp = '\0';
+
+ blen = b64_pton(*buf, bstr, sizeof(bstr));
+ if (blen <= 0) {
+ EREPORT(("dst_s_conv_bignum_b64_to_u8: decoded value is null.\n"));
+ return (0);
+ }
+ else if (loclen < blen) {
+ EREPORT(("dst_s_conv_bignum_b64_to_u8: decoded value is longer than output buffer.\n"));
+ return (0);
+ }
+ if (bp)
+ *buf = bp; /* advancing buffer past \n */
+ memset(loc, 0, loclen - blen); /* clearing unused output area */
+ memcpy(loc + loclen - blen, bstr, blen); /* write last blen bytes */
+ return (blen);
+}
+
+
+/*
+ * dst_s_calculate_bits
+ * Given a binary number represented in a u_char[], determine
+ * the number of significant bits used.
+ * Parameters
+ * str An input character string containing a binary number.
+ * max_bits The maximum possible significant bits.
+ * Return
+ * N The number of significant bits in str.
+ */
+
+int
+dst_s_calculate_bits(const u_char *str, const int max_bits)
+{
+ const u_char *p = str;
+ u_char i, j = 0x80;
+ int bits;
+ for (bits = max_bits; *p == 0x00 && bits > 0; p++)
+ bits -= 8;
+ for (i = *p; (i & j) != j; j >>= 1)
+ bits--;
+ return (bits);
+}
+
+
+/*
+ * calculates a checksum used in kmt for a id.
+ * takes an array of bytes and a length.
+ * returns a 16 bit checksum.
+ */
+u_int16_t
+dst_s_id_calc(const u_char *key, const unsigned keysize)
+{
+ u_int32_t ac;
+ const u_char *kp = key;
+ unsigned size = keysize;
+
+ if (!key)
+ return 0;
+
+ for (ac = 0; size > 1; size -= 2, kp += 2)
+ ac += ((*kp) << 8) + *(kp + 1);
+
+ if (size > 0)
+ ac += ((*kp) << 8);
+ ac += (ac >> 16) & 0xffff;
+
+ return (ac & 0xffff);
+}
+
+/*
+ * dst_s_dns_key_id() Function to calculated DNSSEC footprint from KEY record
+ * rdata (all of record)
+ * Input:
+ * dns_key_rdata: the raw data in wire format
+ * rdata_len: the size of the input data
+ * Output:
+ * the key footprint/id calculated from the key data
+ */
+u_int16_t
+dst_s_dns_key_id(const u_char *dns_key_rdata, const unsigned rdata_len)
+{
+ unsigned key_data = 4;
+
+ if (!dns_key_rdata || (rdata_len < key_data))
+ return 0;
+
+ /* check the extended parameters bit in the DNS Key RR flags */
+ if (dst_s_get_int16(dns_key_rdata) & DST_EXTEND_FLAG)
+ key_data += 2;
+
+ /* compute id */
+ if (dns_key_rdata[3] == KEY_RSA) /* Algorithm RSA */
+ return dst_s_get_int16((const u_char *)
+ &dns_key_rdata[rdata_len - 3]);
+ else
+ /* compute a checksum on the key part of the key rr */
+ return dst_s_id_calc(&dns_key_rdata[key_data],
+ (rdata_len - key_data));
+}
+
+/*
+ * dst_s_get_int16
+ * This routine extracts a 16 bit integer from a two byte character
+ * string. The character string is assumed to be in network byte
+ * order and may be unaligned. The number returned is in host order.
+ * Parameter
+ * buf A two byte character string.
+ * Return
+ * The converted integer value.
+ */
+
+u_int16_t
+dst_s_get_int16(const u_char *buf)
+{
+ register u_int16_t a = 0;
+ a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1]));
+ return (a);
+}
+
+
+/*
+ * dst_s_get_int32
+ * This routine extracts a 32 bit integer from a four byte character
+ * string. The character string is assumed to be in network byte
+ * order and may be unaligned. The number returned is in host order.
+ * Parameter
+ * buf A four byte character string.
+ * Return
+ * The converted integer value.
+ */
+
+u_int32_t
+dst_s_get_int32(const u_char *buf)
+{
+ register u_int32_t a = 0;
+ a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) |
+ ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3]));
+ return (a);
+}
+
+
+/*
+ * dst_s_put_int16
+ * Take a 16 bit integer and store the value in a two byte
+ * character string. The integer is assumed to be in network
+ * order and the string is returned in host order.
+ *
+ * Parameters
+ * buf Storage for a two byte character string.
+ * val 16 bit integer.
+ */
+
+void
+dst_s_put_int16(u_int8_t *buf, const u_int16_t val)
+{
+ buf[0] = (u_int8_t)(val >> 8);
+ buf[1] = (u_int8_t)(val);
+}
+
+
+/*
+ * dst_s_put_int32
+ * Take a 32 bit integer and store the value in a four byte
+ * character string. The integer is assumed to be in network
+ * order and the string is returned in host order.
+ *
+ * Parameters
+ * buf Storage for a four byte character string.
+ * val 32 bit integer.
+ */
+
+void
+dst_s_put_int32(u_int8_t *buf, const u_int32_t val)
+{
+ buf[0] = (u_int8_t)(val >> 24);
+ buf[1] = (u_int8_t)(val >> 16);
+ buf[2] = (u_int8_t)(val >> 8);
+ buf[3] = (u_int8_t)(val);
+}
+
+
+/*
+ * dst_s_filename_length
+ *
+ * This function returns the number of bytes needed to hold the
+ * filename for a key file. '/', '\' and ':' are not allowed.
+ * form: K<keyname>+<alg>+<id>.<suffix>
+ *
+ * Returns 0 if the filename would contain either '\', '/' or ':'
+ */
+size_t
+dst_s_filename_length(const char *name, const char *suffix)
+{
+ if (name == NULL)
+ return (0);
+ if (strrchr(name, '\\'))
+ return (0);
+ if (strrchr(name, '/'))
+ return (0);
+ if (strrchr(name, ':'))
+ return (0);
+ if (suffix == NULL)
+ return (0);
+ if (strrchr(suffix, '\\'))
+ return (0);
+ if (strrchr(suffix, '/'))
+ return (0);
+ if (strrchr(suffix, ':'))
+ return (0);
+ return (1 + strlen(name) + 6 + strlen(suffix));
+}
+
+
+/*
+ * dst_s_build_filename ()
+ * Builds a key filename from the key name, it's id, and a
+ * suffix. '\', '/' and ':' are not allowed. fA filename is of the
+ * form: K<keyname><id>.<suffix>
+ * form: K<keyname>+<alg>+<id>.<suffix>
+ *
+ * Returns -1 if the conversion fails:
+ * if the filename would be too long for space allotted
+ * if the filename would contain a '\', '/' or ':'
+ * Returns 0 on success
+ */
+
+int
+dst_s_build_filename(char *filename, const char *name, unsigned id,
+ int alg, const char *suffix, size_t filename_length)
+{
+ unsigned my_id;
+ if (filename == NULL)
+ return (-1);
+ memset(filename, 0, filename_length);
+ if (name == NULL)
+ return (-1);
+ if (suffix == NULL)
+ return (-1);
+ if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix))
+ return (-1);
+ my_id = id;
+ sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id,
+ (const char *) suffix);
+ if (strrchr(filename, '/'))
+ return (-1);
+ if (strrchr(filename, '\\'))
+ return (-1);
+ if (strrchr(filename, ':'))
+ return (-1);
+ return (0);
+}
+
+/*
+ * dst_s_fopen ()
+ * Open a file in the dst_path directory. If perm is specified, the
+ * file is checked for existence first, and not opened if it exists.
+ * Parameters
+ * filename File to open
+ * mode Mode to open the file (passed directly to fopen)
+ * perm File permission, if creating a new file.
+ * Returns
+ * NULL Failure
+ * NON-NULL (FILE *) of opened file.
+ */
+FILE *
+dst_s_fopen(const char *filename, const char *mode, unsigned perm)
+{
+ FILE *fp;
+ char pathname[PATH_MAX];
+ unsigned plen = sizeof(pathname);
+
+ if (*dst_path != '\0') {
+ strcpy(pathname, dst_path);
+ plen -= strlen(pathname);
+ }
+ else
+ pathname[0] = '\0';
+
+ if (plen > strlen(filename))
+ strncpy(&pathname[PATH_MAX - plen], filename, plen-1);
+ else
+ return (NULL);
+
+ fp = fopen(pathname, mode);
+ if (perm)
+ chmod(pathname, perm);
+ return (fp);
+}
+
+#if 0
+void
+dst_s_dump(const int mode, const u_char *data, const int size,
+ const char *msg)
+{
+ if (size > 0) {
+#ifdef LONG_TEST
+ static u_char scratch[1000];
+ int n ;
+ n = b64_ntop(data, scratch, size, sizeof(scratch));
+ printf("%s: %x %d %s\n", msg, mode, n, scratch);
+#else
+ printf("%s,%x %d\n", msg, mode, size);
+#endif
+ }
+}
+#endif
diff --git a/dst/hmac_link.c b/dst/hmac_link.c
new file mode 100644
index 0000000..a56ae79
--- /dev/null
+++ b/dst/hmac_link.c
@@ -0,0 +1,496 @@
+#ifdef HMAC_MD5
+#ifndef LINT
+static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/hmac_link.c,v 1.5.6.1 2009-11-20 01:49:01 sar Exp $";
+#endif
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+
+/*
+ * This file contains an implementation of the HMAC-MD5 algorithm.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "arpa/nameser.h"
+
+#include "dst_internal.h"
+
+#ifdef USE_MD5
+# include "md5.h"
+# ifndef _MD5_H_
+# define _MD5_H_ 1 /* make sure we do not include rsaref md5.h file */
+# endif
+#endif
+
+#define HMAC_LEN 64
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+#define MD5_LEN 16
+
+
+typedef struct hmackey {
+ u_char hk_ipad[64], hk_opad[64];
+} HMAC_Key;
+
+
+/**************************************************************************
+ * dst_hmac_md5_sign
+ * Call HMAC signing functions to sign a block of data.
+ * There are three steps to signing, INIT (initialize structures),
+ * UPDATE (hash (more) data), FINAL (generate a signature). This
+ * routine performs one or more of these steps.
+ * Parameters
+ * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ * priv_key key to use for signing.
+ * context the context to be used in this digest
+ * data data to be signed.
+ * len length in bytes of data.
+ * signature location to store signature.
+ * sig_len size of the signature location
+ * returns
+ * N Success on SIG_MODE_FINAL = returns signature length in bytes
+ * 0 Success on SIG_MODE_INIT and UPDATE
+ * <0 Failure
+ */
+
+static int
+dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context,
+ const u_char *data, const unsigned len,
+ u_char *signature, const unsigned sig_len)
+{
+ HMAC_Key *key;
+ int sign_len = 0;
+ MD5_CTX *ctx = NULL;
+
+ if (mode & SIG_MODE_INIT)
+ ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+ else if (context)
+ ctx = (MD5_CTX *) *context;
+ if (ctx == NULL)
+ return (-1);
+
+ if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+ return (-1);
+ key = (HMAC_Key *) d_key->dk_KEY_struct;
+
+ if (mode & SIG_MODE_INIT) {
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+ }
+
+ if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+ MD5Update(ctx, (const unsigned char *)data, len);
+
+ if (mode & SIG_MODE_FINAL) {
+ if (signature == NULL || sig_len < MD5_LEN)
+ return (SIGN_FINAL_FAILURE);
+ MD5Final(signature, ctx);
+
+ /* perform outer MD5 */
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_opad, HMAC_LEN);
+ MD5Update(ctx, signature, MD5_LEN);
+ MD5Final(signature, ctx);
+ sign_len = MD5_LEN;
+ SAFE_FREE(ctx);
+ }
+ else {
+ if (context == NULL)
+ return (-1);
+ *context = (void *) ctx;
+ }
+ return (sign_len);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_verify()
+ * Calls HMAC verification routines. There are three steps to
+ * verification, INIT (initialize structures), UPDATE (hash (more) data),
+ * FINAL (generate a signature). This routine performs one or more of
+ * these steps.
+ * Parameters
+ * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ * dkey key to use for verify.
+ * data data signed.
+ * len length in bytes of data.
+ * signature signature.
+ * sig_len length in bytes of signature.
+ * returns
+ * 0 Success
+ * <0 Failure
+ */
+
+static int
+dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context,
+ const u_char *data, const unsigned len,
+ const u_char *signature, const unsigned sig_len)
+{
+ HMAC_Key *key;
+ MD5_CTX *ctx = NULL;
+
+ if (mode & SIG_MODE_INIT)
+ ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+ else if (context)
+ ctx = (MD5_CTX *) *context;
+ if (ctx == NULL)
+ return (-1);
+
+ if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+ return (-1);
+
+ key = (HMAC_Key *) d_key->dk_KEY_struct;
+ if (mode & SIG_MODE_INIT) {
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+ }
+ if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+ MD5Update(ctx, (const unsigned char *)data, len);
+
+ if (mode & SIG_MODE_FINAL) {
+ u_char digest[MD5_LEN];
+ if (signature == NULL || key == NULL || sig_len != MD5_LEN)
+ return (VERIFY_FINAL_FAILURE);
+ MD5Final(digest, ctx);
+
+ /* perform outer MD5 */
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_opad, HMAC_LEN);
+ MD5Update(ctx, digest, MD5_LEN);
+ MD5Final(digest, ctx);
+
+ SAFE_FREE(ctx);
+ if (memcmp(digest, signature, MD5_LEN) != 0)
+ return (VERIFY_FINAL_FAILURE);
+ }
+ else {
+ if (context == NULL)
+ return (-1);
+ *context = (void *) ctx;
+ }
+ return (0);
+}
+
+
+/**************************************************************************
+ * dst_buffer_to_hmac_md5
+ * Converts key from raw data to an HMAC Key
+ * This function gets in a pointer to the data
+ * Parameters
+ * hkey the HMAC key to be filled in
+ * key the key in raw format
+ * keylen the length of the key
+ * Return
+ * 0 Success
+ * <0 Failure
+ */
+static int
+dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const unsigned keylen)
+{
+ int i;
+ HMAC_Key *hkey = NULL;
+ MD5_CTX ctx;
+ unsigned local_keylen = keylen;
+
+ if (dkey == NULL || key == NULL || keylen < 0)
+ return (-1);
+
+ if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL)
+ return (-2);
+
+ memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad));
+ memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad));
+
+ /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */
+ if (keylen > HMAC_LEN) {
+ u_char tk[MD5_LEN];
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const unsigned char *)key, keylen);
+ MD5Final(tk, &ctx);
+ memset((void *) &ctx, 0, sizeof(ctx));
+ key = tk;
+ local_keylen = MD5_LEN;
+ }
+ /* start out by storing key in pads */
+ memcpy(hkey->hk_ipad, key, local_keylen);
+ memcpy(hkey->hk_opad, key, local_keylen);
+
+ /* XOR key with hk_ipad and opad values */
+ for (i = 0; i < HMAC_LEN; i++) {
+ hkey->hk_ipad[i] ^= HMAC_IPAD;
+ hkey->hk_opad[i] ^= HMAC_OPAD;
+ }
+ dkey->dk_key_size = local_keylen;
+ dkey->dk_KEY_struct = (void *) hkey;
+ return (1);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_key_to_file_format
+ * Encodes an HMAC Key into the portable file format.
+ * Parameters
+ * hkey HMAC KEY structure
+ * buff output buffer
+ * buff_len size of output buffer
+ * Return
+ * 0 Failure - null input hkey
+ * -1 Failure - not enough space in output area
+ * N Success - Length of data returned in buff
+ */
+
+static int
+dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff,
+ const unsigned buff_len)
+{
+ char *bp;
+ int i;
+ unsigned len, b_len, key_len;
+ u_char key[HMAC_LEN];
+ HMAC_Key *hkey;
+
+ if (dkey == NULL || dkey->dk_KEY_struct == NULL)
+ return (0);
+ if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str))
+ return (-1); /* no OR not enough space in output area */
+
+ hkey = (HMAC_Key *) dkey->dk_KEY_struct;
+ memset(buff, 0, buff_len); /* just in case */
+ /* write file header */
+ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC");
+
+ bp = (char *) strchr(buff, '\0');
+ b_len = buff_len - (bp - buff);
+
+ memset(key, 0, HMAC_LEN);
+ for (i = 0; i < HMAC_LEN; i++)
+ key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+ for (i = HMAC_LEN - 1; i >= 0; i--)
+ if (key[i] != 0)
+ break;
+ key_len = i + 1;
+
+ strcat(bp, "Key: ");
+ bp += strlen("Key: ");
+ b_len = buff_len - (bp - buff);
+
+ len = b64_ntop(key, key_len, bp, b_len);
+ if (len < 0)
+ return (-1);
+ bp += len;
+ *(bp++) = '\n';
+ *bp = '\0';
+ b_len = buff_len - (bp - buff);
+
+ return (buff_len - b_len);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_key_from_file_format
+ * Converts contents of a key file into an HMAC key.
+ * Parameters
+ * hkey structure to put key into
+ * buff buffer containing the encoded key
+ * buff_len the length of the buffer
+ * Return
+ * n >= 0 Foot print of the key converted
+ * n < 0 Error in conversion
+ */
+
+static int
+dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff,
+ const unsigned buff_len)
+{
+ const char *p = buff, *eol;
+ u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode
+ * it should probably be fixed rather than doing
+ * this
+ */
+ u_char *tmp;
+ unsigned key_len, len;
+
+ if (dkey == NULL)
+ return (-2);
+ if (buff == NULL)
+ return (-1);
+
+ memset(key, 0, sizeof(key));
+
+ if (!dst_s_verify_str(&p, "Key: "))
+ return (-3);
+
+ eol = strchr(p, '\n');
+ if (eol == NULL)
+ return (-4);
+ len = eol - p;
+ tmp = malloc(len + 2);
+ memcpy(tmp, p, len);
+ *(tmp + len) = 0x0;
+ key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /* see above */
+ SAFE_FREE2(tmp, len + 2);
+
+ if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) {
+ return (-6);
+ }
+ return (0);
+}
+
+/*
+ * dst_hmac_md5_to_dns_key()
+ * function to extract hmac key from DST_KEY structure
+ * input:
+ * in_key: HMAC-MD5 key
+ * output:
+ * out_str: buffer to write ot
+ * out_len: size of output buffer
+ * returns:
+ * number of bytes written to output buffer
+ */
+static int
+dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str,
+ const unsigned out_len)
+{
+
+ HMAC_Key *hkey;
+ int i;
+
+ if (in_key == NULL || in_key->dk_KEY_struct == NULL ||
+ out_len <= in_key->dk_key_size || out_str == NULL)
+ return (-1);
+
+ hkey = (HMAC_Key *) in_key->dk_KEY_struct;
+ for (i = 0; i < in_key->dk_key_size; i++)
+ out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+ return (i);
+}
+
+/**************************************************************************
+ * dst_hmac_md5_compare_keys
+ * Compare two keys for equality.
+ * Return
+ * 0 The keys are equal
+ * NON-ZERO The keys are not equal
+ */
+
+static int
+dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+ HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct;
+ HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct;
+ return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN);
+}
+
+/**************************************************************************
+ * dst_hmac_md5_free_key_structure
+ * Frees all (none) dynamically allocated structures in hkey
+ */
+
+static void *
+dst_hmac_md5_free_key_structure(void *key)
+{
+ HMAC_Key *hkey = key;
+ SAFE_FREE(hkey);
+ return (NULL);
+}
+
+
+/***************************************************************************
+ * dst_hmac_md5_generate_key
+ * Creates a HMAC key of size size with a maximum size of 63 bytes
+ * generating a HMAC key larger than 63 bytes makes no sense as that key
+ * is digested before use.
+ */
+
+static int
+dst_hmac_md5_generate_key(DST_KEY *key, const int nothing)
+{
+ u_char *buff;
+ int n;
+ unsigned size, len;
+
+ if (key == NULL || key->dk_alg != KEY_HMAC_MD5)
+ return (0);
+ size = (key->dk_key_size + 7) / 8; /* convert to bytes */
+ if (size <= 0)
+ return(0);
+
+ len = size > 64 ? 64 : size;
+ buff = malloc(len+8);
+
+ n = dst_random(DST_RAND_SEMI, len, buff);
+ n += dst_random(DST_RAND_KEY, len, buff);
+ if (n <= len) { /* failed getting anything */
+ SAFE_FREE2(buff, len);
+ return (-1);
+ }
+ n = dst_buffer_to_hmac_md5(key, buff, len);
+ SAFE_FREE2(buff, len);
+ if (n <= 0)
+ return (n);
+ return (1);
+}
+
+/*
+ * dst_hmac_md5_init() Function to answer set up function pointers for HMAC
+ * related functions
+ */
+int
+dst_hmac_md5_init()
+{
+ if (dst_t_func[KEY_HMAC_MD5] != NULL)
+ return (1);
+ dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func));
+ if (dst_t_func[KEY_HMAC_MD5] == NULL)
+ return (0);
+ memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func));
+ dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign;
+ dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify;
+ dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys;
+ dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key;
+ dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure;
+ dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key;
+ dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5;
+ dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format;
+ dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format;
+ return (1);
+}
+
+#else
+int
+dst_hmac_md5_init(){
+ return (0);
+}
+#endif
+
+
+
+
+
+
+
diff --git a/dst/md5.h b/dst/md5.h
new file mode 100644
index 0000000..05ea3eb
--- /dev/null
+++ b/dst/md5.h
@@ -0,0 +1,123 @@
+/* crypto/md/md5.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#ifndef HEADER_MD5_H
+#define HEADER_MD5_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MD5_CBLOCK 64
+#define MD5_LBLOCK 16
+#define MD5_BLOCK 16
+#define MD5_LAST_BLOCK 56
+#define MD5_LENGTH_BLOCK 8
+#define MD5_DIGEST_LENGTH 16
+
+typedef struct MD5state_st
+ {
+ unsigned long A,B,C,D;
+ unsigned long Nl,Nh;
+ unsigned long data[MD5_LBLOCK];
+ int num;
+ } MD5_CTX;
+
+#ifndef NOPROTO
+void MD5_Init(MD5_CTX *c);
+void MD5_Update(MD5_CTX *c, const unsigned char *data, unsigned long len);
+void MD5_Final(unsigned char *md, MD5_CTX *c);
+unsigned char *MD5(unsigned char *d, unsigned long n, unsigned char *md);
+#else
+void MD5_Init();
+void MD5_Update();
+void MD5_Final();
+unsigned char *MD5();
+#endif
+
+/* to provide backward compatibleness to RSAREF calls ogud@tis.com 1997/11/14 */
+#define MD5Init(c) MD5_Init(c)
+#define MD5Update(c,data, len) MD5_Update(c,data,len)
+#define MD5Final(md, c) MD5_Final(md, c)
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/dst/md5_dgst.c b/dst/md5_dgst.c
new file mode 100644
index 0000000..4dc0f69
--- /dev/null
+++ b/dst/md5_dgst.c
@@ -0,0 +1,396 @@
+/* crypto/md/md5_dgst.c */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "md5_locl.h"
+#include "cdefs.h"
+#include "osdep.h"
+
+#ifdef USE_MD5 /* Added by ogud@tis.com 1998/1/26 */
+
+const char *MD5_version="MD5 part of SSLeay 0.8.1 19-Jul-1997";
+
+/* Implemented from RFC1321 The MD5 Message-Digest Algorithm
+ */
+
+#define INIT_DATA_A (unsigned long)0x67452301L
+#define INIT_DATA_B (unsigned long)0xefcdab89L
+#define INIT_DATA_C (unsigned long)0x98badcfeL
+#define INIT_DATA_D (unsigned long)0x10325476L
+
+#ifndef NOPROTO
+static void md5_block(MD5_CTX *c, unsigned long *p);
+#else
+static void md5_block();
+#endif
+
+void MD5_Init(c)
+MD5_CTX *c;
+ {
+ c->A=INIT_DATA_A;
+ c->B=INIT_DATA_B;
+ c->C=INIT_DATA_C;
+ c->D=INIT_DATA_D;
+ c->Nl=0;
+ c->Nh=0;
+ c->num=0;
+ }
+
+void MD5_Update(c, data, len)
+MD5_CTX *c;
+const register unsigned char *data;
+unsigned long len;
+ {
+ register ULONG *p;
+ int sw,sc;
+ ULONG l;
+
+ if (len == 0) return;
+
+ l=(c->Nl+(len<<3))&0xffffffffL;
+ /* 95-05-24 eay Fixed a bug with the overflow handling, thanks to
+ * Wei Dai <weidai@eskimo.com> for pointing it out. */
+ if (l < c->Nl) /* overflow */
+ c->Nh++;
+ c->Nh+=(len>>29);
+ c->Nl=l;
+
+ if (c->num != 0)
+ {
+ p=c->data;
+ sw=c->num>>2;
+ sc=c->num&0x03;
+
+ if ((c->num+len) >= MD5_CBLOCK)
+ {
+ l= p[sw];
+ p_c2l(data,l,sc);
+ p[sw++]=l;
+ for (; sw<MD5_LBLOCK; sw++)
+ {
+ c2l(data,l);
+ p[sw]=l;
+ }
+ len-=(MD5_CBLOCK-c->num);
+
+ md5_block(c,p);
+ c->num=0;
+ /* drop through and do the rest */
+ }
+ else
+ {
+ int ew,ec;
+
+ c->num+=(int)len;
+ if ((sc+len) < 4) /* ugly, add char's to a word */
+ {
+ l= p[sw];
+ p_c2l_p(data,l,sc,len);
+ p[sw]=l;
+ }
+ else
+ {
+ ew=(c->num>>2);
+ ec=(c->num&0x03);
+ l= p[sw];
+ p_c2l(data,l,sc);
+ p[sw++]=l;
+ for (; sw < ew; sw++)
+ { c2l(data,l); p[sw]=l; }
+ if (ec)
+ {
+ c2l_p(data,l,ec);
+ p[sw]=l;
+ }
+ }
+ return;
+ }
+ }
+ /* we now can process the input data in blocks of MD5_CBLOCK
+ * chars and save the leftovers to c->data. */
+ p=c->data;
+ while (len >= MD5_CBLOCK)
+ {
+#if defined(L_ENDIAN) || defined(B_ENDIAN)
+ memcpy(p,data,MD5_CBLOCK);
+ data+=MD5_CBLOCK;
+#ifdef B_ENDIAN
+ for (sw=(MD5_LBLOCK/4); sw; sw--)
+ {
+ Endian_Reverse32(p[0]);
+ Endian_Reverse32(p[1]);
+ Endian_Reverse32(p[2]);
+ Endian_Reverse32(p[3]);
+ p+=4;
+ }
+#endif
+#else
+ for (sw=(MD5_LBLOCK/4); sw; sw--)
+ {
+ c2l(data,l); *(p++)=l;
+ c2l(data,l); *(p++)=l;
+ c2l(data,l); *(p++)=l;
+ c2l(data,l); *(p++)=l;
+ }
+#endif
+ p=c->data;
+ md5_block(c,p);
+ len-=MD5_CBLOCK;
+ }
+ sc=(int)len;
+ c->num=sc;
+ if (sc)
+ {
+ sw=sc>>2; /* words to copy */
+#ifdef L_ENDIAN
+ p[sw]=0;
+ memcpy(p,data,sc);
+#else
+ sc&=0x03;
+ for ( ; sw; sw--)
+ { c2l(data,l); *(p++)=l; }
+ c2l_p(data,l,sc);
+ *p=l;
+#endif
+ }
+ }
+
+static void md5_block(c, X)
+MD5_CTX *c;
+register ULONG *X;
+ {
+ register ULONG A,B,C,D;
+
+ A=c->A;
+ B=c->B;
+ C=c->C;
+ D=c->D;
+
+ /* Round 0 */
+ LOCL_R0(A,B,C,D,X[ 0], 7,0xd76aa478L);
+ LOCL_R0(D,A,B,C,X[ 1],12,0xe8c7b756L);
+ LOCL_R0(C,D,A,B,X[ 2],17,0x242070dbL);
+ LOCL_R0(B,C,D,A,X[ 3],22,0xc1bdceeeL);
+ LOCL_R0(A,B,C,D,X[ 4], 7,0xf57c0fafL);
+ LOCL_R0(D,A,B,C,X[ 5],12,0x4787c62aL);
+ LOCL_R0(C,D,A,B,X[ 6],17,0xa8304613L);
+ LOCL_R0(B,C,D,A,X[ 7],22,0xfd469501L);
+ LOCL_R0(A,B,C,D,X[ 8], 7,0x698098d8L);
+ LOCL_R0(D,A,B,C,X[ 9],12,0x8b44f7afL);
+ LOCL_R0(C,D,A,B,X[10],17,0xffff5bb1L);
+ LOCL_R0(B,C,D,A,X[11],22,0x895cd7beL);
+ LOCL_R0(A,B,C,D,X[12], 7,0x6b901122L);
+ LOCL_R0(D,A,B,C,X[13],12,0xfd987193L);
+ LOCL_R0(C,D,A,B,X[14],17,0xa679438eL);
+ LOCL_R0(B,C,D,A,X[15],22,0x49b40821L);
+ /* Round 1 */
+ LOCL_R1(A,B,C,D,X[ 1], 5,0xf61e2562L);
+ LOCL_R1(D,A,B,C,X[ 6], 9,0xc040b340L);
+ LOCL_R1(C,D,A,B,X[11],14,0x265e5a51L);
+ LOCL_R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL);
+ LOCL_R1(A,B,C,D,X[ 5], 5,0xd62f105dL);
+ LOCL_R1(D,A,B,C,X[10], 9,0x02441453L);
+ LOCL_R1(C,D,A,B,X[15],14,0xd8a1e681L);
+ LOCL_R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L);
+ LOCL_R1(A,B,C,D,X[ 9], 5,0x21e1cde6L);
+ LOCL_R1(D,A,B,C,X[14], 9,0xc33707d6L);
+ LOCL_R1(C,D,A,B,X[ 3],14,0xf4d50d87L);
+ LOCL_R1(B,C,D,A,X[ 8],20,0x455a14edL);
+ LOCL_R1(A,B,C,D,X[13], 5,0xa9e3e905L);
+ LOCL_R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L);
+ LOCL_R1(C,D,A,B,X[ 7],14,0x676f02d9L);
+ LOCL_R1(B,C,D,A,X[12],20,0x8d2a4c8aL);
+ /* Round 2 */
+ LOCL_R2(A,B,C,D,X[ 5], 4,0xfffa3942L);
+ LOCL_R2(D,A,B,C,X[ 8],11,0x8771f681L);
+ LOCL_R2(C,D,A,B,X[11],16,0x6d9d6122L);
+ LOCL_R2(B,C,D,A,X[14],23,0xfde5380cL);
+ LOCL_R2(A,B,C,D,X[ 1], 4,0xa4beea44L);
+ LOCL_R2(D,A,B,C,X[ 4],11,0x4bdecfa9L);
+ LOCL_R2(C,D,A,B,X[ 7],16,0xf6bb4b60L);
+ LOCL_R2(B,C,D,A,X[10],23,0xbebfbc70L);
+ LOCL_R2(A,B,C,D,X[13], 4,0x289b7ec6L);
+ LOCL_R2(D,A,B,C,X[ 0],11,0xeaa127faL);
+ LOCL_R2(C,D,A,B,X[ 3],16,0xd4ef3085L);
+ LOCL_R2(B,C,D,A,X[ 6],23,0x04881d05L);
+ LOCL_R2(A,B,C,D,X[ 9], 4,0xd9d4d039L);
+ LOCL_R2(D,A,B,C,X[12],11,0xe6db99e5L);
+ LOCL_R2(C,D,A,B,X[15],16,0x1fa27cf8L);
+ LOCL_R2(B,C,D,A,X[ 2],23,0xc4ac5665L);
+ /* Round 3 */
+ LOCL_R3(A,B,C,D,X[ 0], 6,0xf4292244L);
+ LOCL_R3(D,A,B,C,X[ 7],10,0x432aff97L);
+ LOCL_R3(C,D,A,B,X[14],15,0xab9423a7L);
+ LOCL_R3(B,C,D,A,X[ 5],21,0xfc93a039L);
+ LOCL_R3(A,B,C,D,X[12], 6,0x655b59c3L);
+ LOCL_R3(D,A,B,C,X[ 3],10,0x8f0ccc92L);
+ LOCL_R3(C,D,A,B,X[10],15,0xffeff47dL);
+ LOCL_R3(B,C,D,A,X[ 1],21,0x85845dd1L);
+ LOCL_R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL);
+ LOCL_R3(D,A,B,C,X[15],10,0xfe2ce6e0L);
+ LOCL_R3(C,D,A,B,X[ 6],15,0xa3014314L);
+ LOCL_R3(B,C,D,A,X[13],21,0x4e0811a1L);
+ LOCL_R3(A,B,C,D,X[ 4], 6,0xf7537e82L);
+ LOCL_R3(D,A,B,C,X[11],10,0xbd3af235L);
+ LOCL_R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL);
+ LOCL_R3(B,C,D,A,X[ 9],21,0xeb86d391L);
+
+ c->A+=A&0xffffffffL;
+ c->B+=B&0xffffffffL;
+ c->C+=C&0xffffffffL;
+ c->D+=D&0xffffffffL;
+ }
+
+void MD5_Final(md, c)
+unsigned char *md;
+MD5_CTX *c;
+ {
+ register int i,j;
+ register ULONG l;
+ register ULONG *p;
+ static unsigned char end[4]={0x80,0x00,0x00,0x00};
+ unsigned char *cp=end;
+
+ /* c->num should definitely have room for at least one more byte. */
+ p=c->data;
+ j=c->num;
+ i=j>>2;
+
+ /* purify often complains about the following line as an
+ * Uninitialized Memory Read. While this can be true, the
+ * following p_c2l macro will reset l when that case is true.
+ * This is because j&0x03 contains the number of 'valid' bytes
+ * already in p[i]. If and only if j&0x03 == 0, the UMR will
+ * occur but this is also the only time p_c2l will do
+ * l= *(cp++) instead of l|= *(cp++)
+ * Many thanks to Alex Tang <altitude@cic.net> for pickup this
+ * 'potential bug' */
+#ifdef PURIFY
+ if ((j&0x03) == 0) p[i]=0;
+#endif
+ l=p[i];
+ p_c2l(cp,l,j&0x03);
+ p[i]=l;
+ i++;
+ /* i is the next 'undefined word' */
+ if (c->num >= MD5_LAST_BLOCK)
+ {
+ for (; i<MD5_LBLOCK; i++)
+ p[i]=0;
+ md5_block(c,p);
+ i=0;
+ }
+ for (; i<(MD5_LBLOCK-2); i++)
+ p[i]=0;
+ p[MD5_LBLOCK-2]=c->Nl;
+ p[MD5_LBLOCK-1]=c->Nh;
+ md5_block(c,p);
+ cp=md;
+ l=c->A; l2c(l,cp);
+ l=c->B; l2c(l,cp);
+ l=c->C; l2c(l,cp);
+ l=c->D; l2c(l,cp);
+
+ /* clear stuff, md5_block may be leaving some stuff on the stack
+ * but I'm not worried :-) */
+ c->num=0;
+/* memset((char *)&c,0,sizeof(c));*/
+ }
+
+#ifdef undef
+int printit(l)
+unsigned long *l;
+ {
+ int i,ii;
+
+ for (i=0; i<2; i++)
+ {
+ for (ii=0; ii<8; ii++)
+ {
+ fprintf(stderr,"%08lx ",l[i*8+ii]);
+ }
+ fprintf(stderr,"\n");
+ }
+ }
+#endif
+#endif /* USE_MD5 */
diff --git a/dst/md5_locl.h b/dst/md5_locl.h
new file mode 100644
index 0000000..07f440a
--- /dev/null
+++ b/dst/md5_locl.h
@@ -0,0 +1,211 @@
+/* crypto/md/md5_locl.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * 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 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/*
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "md5.h"
+
+#define ULONG unsigned long
+#define UCHAR unsigned char
+#define UINT unsigned int
+
+#if defined(NOCONST)
+#define const
+#endif
+
+#undef c2l
+#define c2l(c,l) (l = ((unsigned long)(*((c)++))) , \
+ l|=(((unsigned long)(*((c)++)))<< 8), \
+ l|=(((unsigned long)(*((c)++)))<<16), \
+ l|=(((unsigned long)(*((c)++)))<<24))
+
+#undef p_c2l
+#define p_c2l(c,l,n) { \
+ switch (n) { \
+ case 0: l =((unsigned long)(*((c)++))); \
+ case 1: l|=((unsigned long)(*((c)++)))<< 8; \
+ case 2: l|=((unsigned long)(*((c)++)))<<16; \
+ case 3: l|=((unsigned long)(*((c)++)))<<24; \
+ } \
+ }
+
+/* NOTE the pointer is not incremented at the end of this */
+#undef c2l_p
+#define c2l_p(c,l,n) { \
+ l=0; \
+ (c)+=n; \
+ switch (n) { \
+ case 3: l =((unsigned long)(*(--(c))))<<16; \
+ case 2: l|=((unsigned long)(*(--(c))))<< 8; \
+ case 1: l|=((unsigned long)(*(--(c)))) ; \
+ } \
+ }
+
+#undef p_c2l_p
+#define p_c2l_p(c,l,sc,len) { \
+ switch (sc) \
+ { \
+ case 0: l =((unsigned long)(*((c)++))); \
+ if (--len == 0) break; \
+ case 1: l|=((unsigned long)(*((c)++)))<< 8; \
+ if (--len == 0) break; \
+ case 2: l|=((unsigned long)(*((c)++)))<<16; \
+ } \
+ }
+
+#undef l2c
+#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>24)&0xff))
+
+/* NOTE - c is not incremented as per l2c */
+#undef l2cn
+#define l2cn(l1,l2,c,n) { \
+ c+=n; \
+ switch (n) { \
+ case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+ case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+ case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \
+ case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+ case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+ case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \
+ } \
+ }
+
+/* A nice byte order reversal from Wei Dai <weidai@eskimo.com> */
+#if defined(WIN32)
+/* 5 instructions with rotate instruction, else 9 */
+#define Endian_Reverse32(a) \
+ { \
+ unsigned long l=(a); \
+ (a)=((ROTATE(l,8)&0x00FF00FF)|(ROTATE(l,24)&0xFF00FF00)); \
+ }
+#else
+/* 6 instructions with rotate instruction, else 8 */
+#define Endian_Reverse32(a) \
+ { \
+ unsigned long l=(a); \
+ l=(((l&0xFF00FF00)>>8L)|((l&0x00FF00FF)<<8L)); \
+ (a)=ROTATE(l,16L); \
+ }
+#endif
+/*
+#define F(x,y,z) (((x) & (y)) | ((~(x)) & (z)))
+#define G(x,y,z) (((x) & (z)) | ((y) & (~(z))))
+*/
+
+/* As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
+ * simplified to the code below. Wei attributes these optimizations
+ * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel.
+ */
+#define F(x,y,z) ((((y) ^ (z)) & (x)) ^ (z))
+#define G(x,y,z) ((((x) ^ (y)) & (z)) ^ (y))
+#define H(x,y,z) ((x) ^ (y) ^ (z))
+#define I(x,y,z) (((x) | (~(z))) ^ (y))
+
+#undef ROTATE
+#if defined(WIN32)
+#define ROTATE(a,n) _lrotl(a,n)
+#else
+#define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+#define LOCL_R0(a,b,c,d,k,s,t) { \
+ a+=((k)+(t)+F((b),(c),(d))); \
+ a=ROTATE(a,s); \
+ a+=b; };\
+
+#define LOCL_R1(a,b,c,d,k,s,t) { \
+ a+=((k)+(t)+G((b),(c),(d))); \
+ a=ROTATE(a,s); \
+ a+=b; };
+
+#define LOCL_R2(a,b,c,d,k,s,t) { \
+ a+=((k)+(t)+H((b),(c),(d))); \
+ a=ROTATE(a,s); \
+ a+=b; };
+
+#define LOCL_R3(a,b,c,d,k,s,t) { \
+ a+=((k)+(t)+I((b),(c),(d))); \
+ a=ROTATE(a,s); \
+ a+=b; };
diff --git a/dst/prandom.c b/dst/prandom.c
new file mode 100644
index 0000000..4de3fe4
--- /dev/null
+++ b/dst/prandom.c
@@ -0,0 +1,957 @@
+#ifndef LINT
+static const char rcsid[] = "$Header: /proj/cvs/prod/DHCP/dst/prandom.c,v 1.8.6.1 2009-11-20 01:49:01 sar Exp $";
+#endif
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ * Portions Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS 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 THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#define NEED_PRAND_CONF
+
+#include "cdefs.h"
+#include "osdep.h"
+#include "dst_internal.h"
+#include "arpa/nameser.h"
+
+
+#ifndef DST_NUM_HASHES
+#define DST_NUM_HASHES 4
+#endif
+#ifndef DST_NUMBER_OF_COUNTERS
+#define DST_NUMBER_OF_COUNTERS 5 /* 32 * 5 == 160 == SHA(1) > MD5 */
+#endif
+
+/*
+ * the constant below is a prime number to make fixed data structures like
+ * stat and time wrap over blocks. This adds certain randomness to what is
+ * in each digested block.
+ * The prime number 2879 has the special property that when
+ * divided by 2,4 and 6 the result is also a prime numbers
+ */
+
+#ifndef DST_RANDOM_BLOCK_SIZE
+#define DST_RANDOM_BLOCK_SIZE 2879
+#endif
+
+/*
+ * This constant dictates how many bits we shift to the right before using a
+ */
+#ifndef DST_SHIFT
+#define DST_SHIFT 9
+#endif
+
+/*
+ * An initializer that is as bad as any other with half the bits set
+ */
+#ifndef DST_RANDOM_PATTERN
+#define DST_RANDOM_PATTERN 0x8765CA93
+#endif
+/*
+ * things must have changed in the last 3600 seconds to be used
+ */
+#define MAX_OLD 3600
+
+/*
+ * Define a single set of configuration for prand stuff. A superset
+ * works okay (failed commands return no data, missing directories
+ * are skipped, and so on.
+ */
+static const char *cmds[] = {
+ "/usr/bin/netstat -an 2>&1",
+ "/usr/sbin/netstat -an 2>&1",
+ "/usr/etc/netstat -an 2>&1",
+ "/bin/netstat -an 2>&1",
+ "/usr/ucb/netstat -an 2>&1",
+
+ /* AIX */
+ "/bin/ps -ef 2>&1",
+ "/bin/df 2>&1",
+ "/usr/bin/uptime 2>&1",
+ "/usr/bin/printenv 2>&1",
+ "/usr/bin/netstat -s 2>&1",
+ "/usr/bin/w 2>&1",
+ /* Tru64 */
+ "/usr/bin/dig com. soa +ti=1 +retry=0 2>&1",
+ "/usr/sbin/arp -an 2>&1",
+ "/usr/ucb/uptime 2>&1",
+ "/bin/iostat 2>&1",
+ /* BSD */
+ "/bin/ps -axlw 2>&1",
+ "/usr/sbin/iostat 2>&1",
+ "/usr/sbin/vmstat 2>&1",
+ /* FreeBSD */
+ "/usr/bin/vmstat 2>&1",
+ "/usr/bin/w 2>&1",
+ /* HP/UX */
+ "/usr/bin/ps -ef 2>&1",
+ /* IRIX */
+ "/usr/etc/arp -a 2>&1",
+ "/usr/bsd/uptime 2>&1",
+ "/usr/bin/printenv 2>&1",
+ "/usr/bsd/w 2>&1",
+ /* Linux */
+ "/sbin/arp -an 2>&1",
+ "/usr/bin/vmstat 2>&1",
+ /* NetBSD */
+ /* OpenBSD */
+ /* QNX */
+ "/bin/ps -a 2>&1",
+ "/bin/sin 2>&1",
+ "/bin/sin fds 2>&1",
+ "/bin/sin memory 2>&1",
+ /* Solaris */
+ "/usr/ucb/uptime 2>&1",
+ "/usr/ucb/netstat -an 2>&1",
+
+ "/usr/bin/netstat -an 2>&1",
+ "/usr/sbin/netstat -an 2>&1",
+ "/usr/etc/netstat -an 2>&1",
+ "/bin/netstat -an 2>&1",
+ "/usr/ucb/netstat -an 2>&1",
+ NULL
+};
+
+static const char *dirs[] = {
+ "/tmp",
+ "/var/tmp",
+ ".",
+ "/",
+ "/var/spool",
+ "/var/adm",
+ "/dev",
+ "/var/spool/mail",
+ "/var/mail",
+ "/home",
+ "/usr/home",
+ NULL
+};
+
+static const char *files[] = {
+ "/var/adm/messages",
+ "/var/adm/wtmp",
+ "/var/adm/lastlog",
+ "/var/log/messages",
+ "/var/log/wtmp",
+ "/var/log/lastlog",
+ "/proc/stat",
+ "/proc/rtc",
+ "/proc/meminfo",
+ "/proc/interrupts",
+ "/proc/self/status",
+ "/proc/ipstats",
+ "/proc/dumper",
+ "/proc/self/as",
+ NULL
+};
+
+/*
+ * these two data structure are used to process input data into digests,
+ *
+ * The first structure contains a pointer to a DST HMAC key
+ * the variables accompanying are used for
+ * step : select every step byte from input data for the hash
+ * block: number of data elements going into each hash
+ * digested: number of data elements digested so far
+ * curr: offset into the next input data for the first byte.
+ */
+typedef struct hash {
+ DST_KEY *key;
+ void *ctx;
+ int digested, block, step, curr;
+} prand_hash;
+
+/*
+ * This data structure controls number of hashes and keeps track of
+ * overall progress in generating correct number of bytes of output.
+ * output : array to store the output data in
+ * needed : how many bytes of output are needed
+ * filled : number of bytes in output so far.
+ * bytes : total number of bytes processed by this structure
+ * file_digest : the HMAC key used to digest files.
+ */
+typedef struct work {
+ unsigned needed, filled, bytes;
+ u_char *output;
+ prand_hash *hash[DST_NUM_HASHES];
+ DST_KEY *file_digest;
+} dst_work;
+
+
+/*
+ * forward function declarations
+ */
+static int get_dev_random(u_char *output, unsigned size);
+static int do_time(dst_work *work);
+static int do_ls(dst_work *work);
+static int unix_cmd(dst_work *work);
+static int digest_file(dst_work *work);
+
+static void force_hash(dst_work *work, prand_hash *hash);
+static int do_hash(dst_work *work, prand_hash *hash, const u_char *input,
+ unsigned size);
+static int my_digest(dst_work *tmp, const u_char *input, unsigned size);
+static prand_hash *get_hmac_key(int step, int block);
+
+static unsigned own_random(dst_work *work);
+
+
+/*
+ * variables used in the quick random number generator
+ */
+static u_int32_t ran_val = DST_RANDOM_PATTERN;
+static u_int32_t ran_cnt = (DST_RANDOM_PATTERN >> 10);
+
+/*
+ * setting the quick_random generator to particular values or if both
+ * input parameters are 0 then set it to initial values
+ */
+
+void
+dst_s_quick_random_set(u_int32_t val, u_int32_t cnt)
+{
+ ran_val = (val == 0) ? DST_RANDOM_PATTERN : val;
+ ran_cnt = (cnt == 0) ? (DST_RANDOM_PATTERN >> 10) : cnt;
+}
+
+/*
+ * this is a quick and random number generator that seems to generate quite
+ * good distribution of data
+ */
+u_int32_t
+dst_s_quick_random(int inc)
+{
+ ran_val = ((ran_val >> 13) ^ (ran_val << 19)) ^
+ ((ran_val >> 7) ^ (ran_val << 25));
+ if (inc > 0) /* only increasing values accepted */
+ ran_cnt += inc;
+ ran_val += ran_cnt++;
+ return (ran_val);
+}
+
+/*
+ * get_dev_random: Function to read /dev/random reliably
+ * this function returns how many bytes where read from the device.
+ * port_after.h should set the control variable HAVE_DEV_RANDOM
+ */
+static int
+get_dev_random(u_char *output, unsigned size)
+{
+#ifdef HAVE_DEV_RANDOM
+ struct stat st;
+ int n = 0, fd = -1, s;
+
+ s = stat("/dev/random", &st);
+ if (s == 0 && S_ISCHR(st.st_mode)) {
+ if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) != -1) {
+ if ((n = read(fd, output, size)) < 0)
+ n = 0;
+ close(fd);
+ }
+ return (n);
+ }
+#endif
+ return (0);
+}
+
+/*
+ * Portable way of getting the time values if gettimeofday is missing
+ * then compile with -DMISSING_GETTIMEOFDAY time() is POSIX compliant but
+ * gettimeofday() is not.
+ * Time of day is predictable, we are looking for the randomness that comes
+ * the last few bits in the microseconds in the timer are hard to predict when
+ * this is invoked at the end of other operations
+ */
+struct timeval *mtime;
+static int
+do_time(dst_work *work)
+{
+ int cnt = 0;
+ static u_char tmp[sizeof(struct timeval) + sizeof(struct timezone)];
+ struct timezone *zone;
+
+ zone = (struct timezone *) tmp;
+ mtime = (struct timeval *)(tmp + sizeof(struct timezone));
+ gettimeofday(mtime, zone);
+ cnt = sizeof(tmp);
+ my_digest(work, tmp, sizeof(tmp));
+
+ return (cnt);
+}
+
+/*
+ * this function simulates the ls command, but it uses stat which gives more
+ * information and is harder to guess
+ * Each call to this function will visit the next directory on the list of
+ * directories, in a circular manner.
+ * return value is the number of bytes added to the temp buffer
+ *
+ * do_ls() does not visit subdirectories
+ * if attacker has access to machine it can guess most of the values seen
+ * thus it is important to only visit directories that are frequently updated
+ * Attacker that has access to the network can see network traffic
+ * when NFS mounted directories are accessed and know exactly the data used
+ * but may not know exactly in what order data is used.
+ * Returns the number of bytes that where returned in stat structures
+ */
+static int
+do_ls(dst_work *work)
+{
+ struct dir_info {
+ uid_t uid;
+ gid_t gid;
+ off_t size;
+ time_t atime, mtime, ctime;
+ };
+ static struct dir_info dir_info;
+ struct stat buf;
+ struct dirent *entry;
+ static int i = 0;
+ static unsigned long d_round = 0;
+ struct timeval tv;
+ int n = 0, tb_i = 0, out = 0;
+ unsigned dir_len;
+
+ char file_name[1024];
+ u_char tmp_buff[1024];
+ DIR *dir = NULL;
+
+ if (dirs[i] == NULL) /* if at the end of the list start over */
+ i = 0;
+ if (stat(dirs[i++], &buf)) /* directory does not exist */
+ return (0);
+
+ gettimeofday(&tv,NULL);
+ if (d_round == 0)
+ d_round = tv.tv_sec - MAX_OLD;
+ else if (i==1) /* if starting a new round cut what we accept */
+ d_round += (tv.tv_sec - d_round)/2;
+
+ if (buf.st_atime < d_round)
+ return (0);
+
+ EREPORT(("do_ls i %d filled %4d in_temp %4d\n",
+ i-1, work->filled, work->in_temp));
+ memcpy(tmp_buff, &buf, sizeof(buf));
+ tb_i += sizeof(buf);
+
+
+ if ((dir = opendir(dirs[i-1])) == NULL)/* open it for read */
+ return (0);
+ strcpy(file_name, dirs[i-1]);
+ dir_len = strlen(file_name);
+ file_name[dir_len++] = '/';
+ while ((entry = readdir(dir))) {
+ unsigned len = strlen(entry->d_name);
+ out += len;
+ if (my_digest(work, (u_char *)entry->d_name, len))
+ break;
+
+ memcpy(&file_name[dir_len], entry->d_name, len);
+ file_name[dir_len + len] = 0x0;
+ /* for all entries in dir get the stats */
+ if (stat(file_name, &buf) == 0) {
+ n++; /* count successful stat calls */
+ /* copy non static fields */
+ dir_info.uid += buf.st_uid;
+ dir_info.gid += buf.st_gid;
+ dir_info.size += buf.st_size;
+ dir_info.atime += buf.st_atime;
+ dir_info.mtime += buf.st_mtime;
+ dir_info.ctime += buf.st_ctime;
+ out += sizeof(dir_info);
+ if(my_digest(work, (u_char *)&dir_info,
+ sizeof(dir_info)))
+ break;
+ }
+ }
+ closedir(dir); /* done */
+ out += do_time(work); /* add a time stamp */
+ return (out);
+}
+
+
+/*
+ * unix_cmd()
+ * this function executes the a command from the cmds[] list of unix commands
+ * configured in the prand_conf.h file
+ * return value is the number of bytes added to the randomness temp buffer
+ *
+ * it returns the number of bytes that where read in
+ * if more data is needed at the end time is added to the data.
+ * This function maintains a state to selects the next command to run
+ * returns the number of bytes read in from the command
+ */
+static int
+unix_cmd(dst_work *work)
+{
+ static int cmd_index = 0;
+ int cnt = 0, n;
+ FILE *pipe;
+ u_char buffer[4096];
+
+ if (cmds[cmd_index] == NULL)
+ cmd_index = 0;
+ EREPORT(("unix_cmd() i %d filled %4d in_temp %4d\n",
+ cmd_index, work->filled, work->in_temp));
+ pipe = popen(cmds[cmd_index++], "r"); /* execute the command */
+
+ while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0) {
+ cnt += n; /* process the output */
+ if (my_digest(work, buffer, (unsigned)n))
+ break;
+ /* this adds some randomness to the output */
+ cnt += do_time(work);
+ }
+ while ((n = fread(buffer, sizeof(char), sizeof(buffer), pipe)) > 0)
+ ; /* drain the pipe */
+ pclose(pipe);
+ return (cnt); /* read how many bytes where read in */
+}
+
+/*
+ * digest_file() This function will read a file and run hash over it
+ * input is a file name
+ */
+static int
+digest_file(dst_work *work)
+{
+ static int f_cnt = 0;
+ static unsigned long f_round = 0;
+ FILE *fp;
+ void *ctx;
+ const char *name;
+ int no, i;
+ struct stat st;
+ struct timeval tv;
+ u_char buf[1024];
+
+ if (f_round == 0 || files[f_cnt] == NULL || work->file_digest == NULL)
+ if (gettimeofday(&tv, NULL)) /* only do this if needed */
+ return (0);
+ if (f_round == 0) /* first time called set to one hour ago */
+ f_round = (tv.tv_sec - MAX_OLD);
+ name = files[f_cnt++];
+ if (files[f_cnt] == NULL) { /* end of list of files */
+ if(f_cnt <= 1) /* list is too short */
+ return (0);
+ f_cnt = 0; /* start again on list */
+ f_round += (tv.tv_sec - f_round)/2; /* set new cutoff */
+ work->file_digest = dst_free_key(work->file_digest);
+ }
+ if (work->file_digest == NULL) {
+ work->file_digest = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0,
+ (u_char *)&tv, sizeof(tv));
+ if (work->file_digest == NULL)
+ return (0);
+ }
+ if (access(name, R_OK) || stat(name, &st))
+ return (0); /* no such file or not allowed to read it */
+ if (strncmp(name, "/proc/", 6) && st.st_mtime < f_round)
+ return(0); /* file has not changed recently enough */
+ if (dst_sign_data(SIG_MODE_INIT, work->file_digest, &ctx,
+ NULL, 0, NULL, 0)) {
+ work->file_digest = dst_free_key(work->file_digest);
+ return (0);
+ }
+ if ((fp = fopen(name, "r")) == NULL)
+ return (0);
+ for (no = 0; (i = fread(buf, sizeof(*buf), sizeof(buf), fp)) > 0;
+ no += i)
+ dst_sign_data(SIG_MODE_UPDATE, work->file_digest, &ctx,
+ buf, (unsigned)i, NULL, 0);
+
+ fclose(fp);
+ if (no >= 64) {
+ i = dst_sign_data(SIG_MODE_FINAL, work->file_digest, &ctx,
+ NULL, 0, &work->output[work->filled],
+ DST_HASH_SIZE);
+ if (i > 0)
+ work->filled += i;
+ }
+ else if (i > 0)
+ my_digest(work, buf, (unsigned)i);
+ my_digest(work, (const u_char *)name, strlen(name));
+ return (no + strlen(name));
+}
+
+/*
+ * function to perform the FINAL and INIT operation on a hash if allowed
+ */
+static void
+force_hash(dst_work *work, prand_hash *hash)
+{
+ int i = 0;
+
+ /*
+ * if more than half a block then add data to output
+ * otherwise add the digest to the next hash
+ */
+ if ((hash->digested * 2) > hash->block) {
+ i = dst_sign_data(SIG_MODE_FINAL, hash->key, &hash->ctx,
+ NULL, 0, &work->output[work->filled],
+ DST_HASH_SIZE);
+
+ hash->digested = 0;
+ dst_sign_data(SIG_MODE_INIT, hash->key, &hash->ctx,
+ NULL, 0, NULL, 0);
+ if (i > 0)
+ work->filled += i;
+ }
+ return;
+}
+
+/*
+ * This function takes the input data does the selection of data specified
+ * by the hash control block.
+ * The step variable in the work structure determines which 1/step bytes
+ * are used,
+ *
+ */
+static int
+do_hash(dst_work *work, prand_hash *hash, const u_char *input, unsigned size)
+{
+ const u_char *tmp = input;
+ u_char *tp, *abuf = (u_char *)0;
+ int i, n;
+ unsigned needed, avail, dig, cnt = size;
+ unsigned tmp_size = 0;
+
+ if (cnt <= 0 || input == NULL)
+ return (0);
+
+ if (hash->step > 1) { /* if using subset of input data */
+ tmp_size = size / hash->step + 2;
+ abuf = tp = malloc(tmp_size);
+ tmp = tp;
+ for (cnt = 0, i = hash->curr; i < size; i += hash->step, cnt++)
+ *(tp++) = input[i];
+ /* calculate the starting point in the next input set */
+ hash->curr = (hash->step - (i - size)) % hash->step;
+ }
+ /* digest the data in block sizes */
+ for (n = 0; n < cnt; n += needed) {
+ avail = (cnt - n);
+ needed = hash->block - hash->digested;
+ dig = (avail < needed) ? avail : needed;
+ dst_sign_data(SIG_MODE_UPDATE, hash->key, &hash->ctx,
+ &tmp[n], dig, NULL, 0);
+ hash->digested += dig;
+ if (hash->digested >= hash->block)
+ force_hash(work, hash);
+ if (work->needed < work->filled) {
+ if (abuf)
+ SAFE_FREE2(abuf, tmp_size);
+ return (1);
+ }
+ }
+ if (tmp_size > 0)
+ SAFE_FREE2(abuf, tmp_size);
+ return (0);
+}
+
+/*
+ * Copy data from INPUT for length SIZE into the work-block TMP.
+ * If we fill the work-block, digest it; then,
+ * if work-block needs more data, keep filling with the rest of the input.
+ */
+static int
+my_digest(dst_work *work, const u_char *input, unsigned size)
+{
+
+ int i, full = 0;
+ static unsigned counter;
+
+ counter += size;
+ /* first do each one of the hashes */
+ for (i = 0; i < DST_NUM_HASHES && full == 0; i++)
+ full = do_hash(work, work->hash[i], input, size) +
+ do_hash(work, work->hash[i], (u_char *) &counter,
+ sizeof(counter));
+/*
+ * if enough data has be generated do final operation on all hashes
+ * that have enough date for that
+ */
+ for (i = 0; full && (i < DST_NUM_HASHES); i++)
+ force_hash(work, work->hash[i]);
+
+ return (full);
+}
+
+/*
+ * this function gets some semi random data and sets that as an HMAC key
+ * If we get a valid key this function returns that key initialized
+ * otherwise it returns NULL;
+ */
+static prand_hash *
+get_hmac_key(int step, int block)
+{
+
+ u_char *buff;
+ int temp = 0, n = 0;
+ unsigned size = 70;
+ DST_KEY *new_key = NULL;
+ prand_hash *new = NULL;
+
+ /* use key that is larger than digest algorithms (64) for key size */
+ buff = malloc(size);
+ if (buff == NULL)
+ return (NULL);
+ /* do not memset the allocated memory to get random bytes there */
+ /* time of day is somewhat random especially in the last bytes */
+ gettimeofday((struct timeval *) &buff[n], NULL);
+ n += sizeof(struct timeval);
+
+/* get some semi random stuff in here stir it with micro seconds */
+ if (n < size) {
+ temp = dst_s_quick_random((int) buff[n - 1]);
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+/* get the pid of this process and its parent */
+ if (n < size) {
+ temp = (int) getpid();
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+ if (n < size) {
+ temp = (int) getppid();
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+/* get the user ID */
+ if (n < size) {
+ temp = (int) getuid();
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+#ifndef GET_HOST_ID_MISSING
+ if (n < size) {
+ temp = (int) gethostid();
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+#endif
+/* get some more random data */
+ if (n < size) {
+ temp = dst_s_quick_random((int) buff[n - 1]);
+ memcpy(&buff[n], &temp, sizeof(temp));
+ n += sizeof(temp);
+ }
+/* covert this into a HMAC key */
+ new_key = dst_buffer_to_key("", KEY_HMAC_MD5, 0, 0, buff, size);
+ SAFE_FREE(buff);
+
+/* get the control structure */
+ if ((new = malloc(sizeof(prand_hash))) == NULL)
+ return (NULL);
+ new->digested = new->curr = 0;
+ new->step = step;
+ new->block = block;
+ new->key = new_key;
+ if (dst_sign_data(SIG_MODE_INIT, new_key, &new->ctx, NULL, 0, NULL, 0))
+ return (NULL);
+
+ return (new);
+}
+
+/*
+ * own_random()
+ * This function goes out and from various sources tries to generate enough
+ * semi random data that a hash function can generate a random data.
+ * This function will iterate between the two main random source sources,
+ * information from programs and directories in random order.
+ * This function return the number of bytes added to the random output buffer.
+ */
+static unsigned
+own_random(dst_work *work)
+{
+ int dir = 0, b;
+ int bytes, n, cmd = 0, dig = 0;
+ int start =0;
+/*
+ * now get the initial seed to put into the quick random function from
+ * the address of the work structure
+ */
+ bytes = (int) getpid();
+/*
+ * proceed while needed
+ */
+ while (work->filled < work->needed) {
+ EREPORT(("own_random r %08x b %6d t %6d f %6d\n",
+ ran_val, bytes, work->in_temp, work->filled));
+/* pick a random number in the range of 0..7 based on that random number
+ * perform some operations that yield random data
+ */
+ start = work->filled;
+ n = (dst_s_quick_random(bytes) >> DST_SHIFT) & 0x07;
+ switch (n) {
+ case 0:
+ case 3:
+ if (sizeof(cmds) > 2 *sizeof(*cmds)) {
+ b = unix_cmd(work);
+ cmd += b;
+ }
+ break;
+
+ case 1:
+ case 7:
+ if (sizeof(dirs) > 2 *sizeof(*dirs)) {
+ b = do_ls(work);
+ dir += b;
+ }
+ break;
+
+ case 4:
+ case 5:
+ /* retry getting data from /dev/random */
+ b = get_dev_random(&work->output[work->filled],
+ work->needed - work->filled);
+ if (b > 0)
+ work->filled += b;
+ break;
+
+ case 6:
+ if (sizeof(files) > 2 * sizeof(*files)) {
+ b = digest_file(work);
+ dig += b;
+ }
+ break;
+
+ case 2:
+ default: /* to make sure we make some progress */
+ work->output[work->filled++] = 0xff &
+ dst_s_quick_random(bytes);
+ b = 1;
+ break;
+ }
+ if (b > 0)
+ bytes += b;
+ }
+ return (work->filled);
+}
+
+
+/*
+ * dst_s_random() This function will return the requested number of bytes
+ * of randomness to the caller it will use the best available sources of
+ * randomness.
+ * The current order is to use /dev/random, precalculated randomness, and
+ * finally use some system calls and programs to generate semi random data
+ * that is then digested to generate randomness.
+ * This function is thread safe as each thread uses its own context, but
+ * concurrent treads will affect each other as they update shared state
+ * information.
+ * It is strongly recommended that this function be called requesting a size
+ * that is not a multiple of the output of the hash function used.
+ *
+ * If /dev/random is not available this function is not suitable to generate
+ * large amounts of data, rather it is suitable to seed a pseudo-random
+ * generator
+ * Returns the number of bytes put in the output buffer
+ */
+int
+dst_s_random(u_char *output, unsigned size)
+{
+ int n = 0, i;
+ unsigned s;
+ static u_char old_unused[DST_HASH_SIZE * DST_NUM_HASHES];
+ static unsigned unused = 0;
+
+ if (size <= 0 || output == NULL)
+ return (0);
+
+ if (size >= 2048)
+ return (-1);
+ /*
+ * Read from /dev/random
+ */
+ n = get_dev_random(output, size);
+ /*
+ * If old data is available and needed use it
+ */
+ if (n < size && unused > 0) {
+ unsigned need = size - n;
+ if (unused <= need) {
+ memcpy(output, old_unused, unused);
+ n += unused;
+ unused = 0;
+ } else {
+ memcpy(output, old_unused, need);
+ n += need;
+ unused -= need;
+ memcpy(old_unused, &old_unused[need], unused);
+ }
+ }
+ /*
+ * If we need more use the simulated randomness here.
+ */
+ if (n < size) {
+ dst_work *my_work = (dst_work *) malloc(sizeof(dst_work));
+ if (my_work == NULL)
+ return (n);
+ my_work->needed = size - n;
+ my_work->filled = 0;
+ my_work->output = (u_char *) malloc(my_work->needed +
+ DST_HASH_SIZE *
+ DST_NUM_HASHES);
+ my_work->file_digest = NULL;
+ if (my_work->output == NULL)
+ return (n);
+ memset(my_work->output, 0x0, my_work->needed);
+/* allocate upto 4 different HMAC hash functions out of order */
+#if DST_NUM_HASHES >= 3
+ my_work->hash[2] = get_hmac_key(3, DST_RANDOM_BLOCK_SIZE / 2);
+#endif
+#if DST_NUM_HASHES >= 2
+ my_work->hash[1] = get_hmac_key(7, DST_RANDOM_BLOCK_SIZE / 6);
+#endif
+#if DST_NUM_HASHES >= 4
+ my_work->hash[3] = get_hmac_key(5, DST_RANDOM_BLOCK_SIZE / 4);
+#endif
+ my_work->hash[0] = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE);
+ if (my_work->hash[0] == NULL) /* if failure bail out */
+ return (n);
+ s = own_random(my_work);
+/* if more generated than needed store it for future use */
+ if (s >= my_work->needed) {
+ EREPORT(("dst_s_random(): More than needed %d >= %d\n",
+ s, my_work->needed));
+ memcpy(&output[n], my_work->output, my_work->needed);
+ n += my_work->needed;
+ /* saving unused data for next time */
+ unused = s - my_work->needed;
+ memcpy(old_unused, &my_work->output[my_work->needed],
+ unused);
+ } else {
+ /* XXXX This should not happen */
+ EREPORT(("Not enough %d >= %d\n", s, my_work->needed));
+ memcpy(&output[n], my_work->output, s);
+ n += my_work->needed;
+ }
+
+/* delete the allocated work area */
+ for (i = 0; i < DST_NUM_HASHES; i++) {
+ dst_free_key(my_work->hash[i]->key);
+ SAFE_FREE(my_work->hash[i]);
+ }
+ SAFE_FREE(my_work->output);
+ SAFE_FREE(my_work);
+ }
+ return (n);
+}
+
+/*
+ * A random number generator that is fast and strong
+ * this random number generator is based on HASHing data,
+ * the input to the digest function is a collection of <NUMBER_OF_COUNTERS>
+ * counters that is incremented between digest operations
+ * each increment operation amortizes to 2 bits changed in that value
+ * for 5 counters thus the input will amortize to have 10 bits changed
+ * The counters are initially set using the strong random function above
+ * the HMAC key is selected by the same method as the HMAC keys for the
+ * strong random function.
+ * Each set of counters is used for 2^25 operations
+ *
+ * returns the number of bytes written to the output buffer
+ * or negative number in case of error
+ */
+int
+dst_s_semi_random(u_char *output, unsigned size)
+{
+ static u_int32_t counter[DST_NUMBER_OF_COUNTERS];
+ static u_char semi_old[DST_HASH_SIZE];
+ static int semi_loc = 0, cnt = 0;
+ static unsigned hb_size = 0;
+ static DST_KEY *my_key = NULL;
+ prand_hash *hash;
+ unsigned out = 0;
+ unsigned i;
+ int n;
+
+ if (output == NULL || size <= 0)
+ return (-2);
+
+/* check if we need a new key */
+ if (my_key == NULL || cnt > (1 << 25)) { /* get HMAC KEY */
+ if (my_key)
+ my_key->dk_func->destroy(my_key);
+ if ((hash = get_hmac_key(1, DST_RANDOM_BLOCK_SIZE)) == NULL)
+ return (0);
+ my_key = hash->key;
+/* check if the key works stir the new key using some old random data */
+ hb_size = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
+ (u_char *) counter, sizeof(counter),
+ semi_old, sizeof(semi_old));
+ if (hb_size <= 0) {
+ EREPORT(("dst_s_semi_random() Sign of alg %d failed %d\n",
+ my_key->dk_alg, hb_size));
+ return (-1);
+ }
+/* new set the counters to random values */
+ dst_s_random((u_char *) counter, sizeof(counter));
+ cnt = 0;
+ }
+/* if old data around use it first */
+ if (semi_loc < hb_size) {
+ if (size <= hb_size - semi_loc) { /* need less */
+ memcpy(output, &semi_old[semi_loc], size);
+ semi_loc += size;
+ return (size); /* DONE */
+ } else {
+ out = hb_size - semi_loc;
+ memcpy(output, &semi_old[semi_loc], out);
+ semi_loc += out;
+ }
+ }
+/* generate more random stuff */
+ while (out < size) {
+ /*
+ * modify at least one bit by incrementing at least one counter
+ * based on the last bit of the last counter updated update
+ * the next one.
+ * minimally this operation will modify at least 1 bit,
+ * amortized 2 bits
+ */
+ for (n = 0; n < DST_NUMBER_OF_COUNTERS; n++)
+ i = (int) counter[n]++;
+
+ i = dst_sign_data(SIG_MODE_ALL, my_key, NULL,
+ (u_char *) counter, hb_size,
+ semi_old, sizeof(semi_old));
+ if (i != hb_size)
+ EREPORT(("HMAC SIGNATURE FAILURE %d\n", i));
+ cnt++;
+ if (size - out < i) /* Not all data is needed */
+ semi_loc = i = size - out;
+ memcpy(&output[out], semi_old, i);
+ out += i;
+ }
+ return (out);
+}
diff --git a/includes/Makefile.am b/includes/Makefile.am
new file mode 100644
index 0000000..4a462d5
--- /dev/null
+++ b/includes/Makefile.am
@@ -0,0 +1,11 @@
+nobase_include_HEADERS = omapip/alloc.h omapip/buffer.h omapip/convert.h \
+ omapip/hash.h omapip/isclib.h omapip/omapip.h \
+ omapip/omapip_p.h omapip/result.h omapip/trace.h \
+ isc-dhcp/dst.h
+
+EXTRA_DIST = cdefs.h ctrace.h dhcp.h dhcp6.h dhcpd.h dhctoken.h failover.h \
+ heap.h inet.h minires.h osdep.h site.h statement.h tree.h \
+ t_api.h \
+ arpa/nameser.h arpa/nameser_compat.h \
+ netinet/if_ether.h netinet/ip.h netinet/ip_icmp.h netinet/udp.h
+
diff --git a/includes/Makefile.in b/includes/Makefile.in
new file mode 100644
index 0000000..a9a89d2
--- /dev/null
+++ b/includes/Makefile.in
@@ -0,0 +1,403 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = includes
+DIST_COMMON = $(nobase_include_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(includedir)"
+nobase_includeHEADERS_INSTALL = $(install_sh_DATA)
+HEADERS = $(nobase_include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+nobase_include_HEADERS = omapip/alloc.h omapip/buffer.h omapip/convert.h \
+ omapip/hash.h omapip/isclib.h omapip/omapip.h \
+ omapip/omapip_p.h omapip/result.h omapip/trace.h \
+ isc-dhcp/dst.h
+
+EXTRA_DIST = cdefs.h ctrace.h dhcp.h dhcp6.h dhcpd.h dhctoken.h failover.h \
+ heap.h inet.h minires.h osdep.h site.h statement.h tree.h \
+ t_api.h \
+ arpa/nameser.h arpa/nameser_compat.h \
+ netinet/if_ether.h netinet/ip.h netinet/ip_icmp.h netinet/udp.h
+
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign includes/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign includes/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status includes/config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ cd $(top_srcdir) && $(AUTOHEADER)
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+install-nobase_includeHEADERS: $(nobase_include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @$(am__vpath_adj_setup) \
+ list='$(nobase_include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ $(am__vpath_adj) \
+ echo " $(nobase_includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(nobase_includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-nobase_includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @$(am__vpath_adj_setup) \
+ list='$(nobase_include_HEADERS)'; for p in $$list; do \
+ $(am__vpath_adj) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(HEADERS) config.h
+installdirs:
+ for dir in "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-nobase_includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-nobase_includeHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ ctags distclean distclean-generic distclean-hdr distclean-tags \
+ distdir dvi dvi-am html html-am info info-am install \
+ install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-nobase_includeHEADERS install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-nobase_includeHEADERS
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/includes/arpa/nameser.h b/includes/arpa/nameser.h
new file mode 100644
index 0000000..f262694
--- /dev/null
+++ b/includes/arpa/nameser.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+/*
+ * $Id: nameser.h,v 1.6.24.1 2009-11-20 01:49:01 sar Exp $
+ */
+
+#ifndef _ARPA_NAMESER_H_
+#define _ARPA_NAMESER_H_
+
+/*
+ * Revision information. This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not
+ * compare for equality; rather, use it to determine whether your libbind.a
+ * contains a new enough lib/nameser/ to support the feature you need.
+ */
+
+#define __NAMESER 19991006 /* New interface version stamp. */
+
+/*
+ * Define constants based on RFC 883, RFC 1034, RFC 1035
+ */
+#define NS_PACKETSZ 512 /* maximum packet size */
+#define NS_MAXDNAME 1025 /* maximum domain name */
+#define NS_MAXCDNAME 255 /* maximum compressed domain name */
+#define NS_MAXLABEL 63 /* maximum length of domain label */
+#define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */
+#define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */
+#define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
+#define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */
+#define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */
+#define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */
+#define NS_INADDRSZ 4 /* IPv4 T_A */
+#define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */
+#define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */
+#define NS_DEFAULTPORT 53 /* For both TCP and UDP. */
+
+/*
+ * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord()
+ * in synch with it.
+ */
+typedef enum __ns_sect {
+ ns_s_qd = 0, /* Query: Question. */
+ ns_s_zn = 0, /* Update: Zone. */
+ ns_s_an = 1, /* Query: Answer. */
+ ns_s_pr = 1, /* Update: Prerequisites. */
+ ns_s_ns = 2, /* Query: Name servers. */
+ ns_s_ud = 2, /* Update: Update. */
+ ns_s_ar = 3, /* Query|Update: Additional records. */
+ ns_s_max = 4
+} ns_sect;
+
+/*
+ * This is a message handle. It is caller allocated and has no dynamic data.
+ * This structure is intended to be opaque to all but ns_parse.c, thus the
+ * leading _'s on the member names. Use the accessor functions, not the _'s.
+ */
+typedef struct __ns_msg {
+ const u_int8_t *_msg, *_eom;
+ u_int16_t _id, _flags, _counts[ns_s_max];
+ const u_int8_t *_sections[ns_s_max];
+ ns_sect _sect;
+ int _rrnum;
+ const u_int8_t *_ptr;
+} ns_msg;
+
+/* Private data structure - do not use from outside library. */
+struct _ns_flagdata { int mask, shift; };
+extern struct _ns_flagdata _ns_flagdata[];
+
+/* Accessor macros - this is part of the public interface. */
+#define ns_msg_getflag(handle, flag) ( \
+ ((handle)._flags & _ns_flagdata[flag].mask) \
+ >> _ns_flagdata[flag].shift \
+ )
+#define ns_msg_id(handle) ((handle)._id + 0)
+#define ns_msg_base(handle) ((handle)._msg + 0)
+#define ns_msg_end(handle) ((handle)._eom + 0)
+#define ns_msg_size(handle) ((handle)._eom - (handle)._msg)
+#define ns_msg_count(handle, section) ((handle)._counts[section] + 0)
+
+/*
+ * This is a parsed record. It is caller allocated and has no dynamic data.
+ */
+typedef struct __ns_rr {
+ char name[NS_MAXDNAME];
+ u_int16_t type;
+ u_int16_t rr_class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ const u_int8_t *rdata;
+} ns_rr;
+
+/* Accessor macros - this is part of the public interface. */
+#define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".")
+#define ns_rr_type(rr) ((ns_type)((rr).type + 0))
+#define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0))
+#define ns_rr_ttl(rr) ((rr).ttl + 0)
+#define ns_rr_rdlen(rr) ((rr).rdlength + 0)
+#define ns_rr_rdata(rr) ((rr).rdata + 0)
+
+/*
+ * These don't have to be in the same order as in the packet flags word,
+ * and they can even overlap in some cases, but they will need to be kept
+ * in synch with ns_parse.c:ns_flagdata[].
+ */
+typedef enum __ns_flag {
+ ns_f_qr, /* Question/Response. */
+ ns_f_opcode, /* Operation code. */
+ ns_f_aa, /* Authoritative Answer. */
+ ns_f_tc, /* Truncation occurred. */
+ ns_f_rd, /* Recursion Desired. */
+ ns_f_ra, /* Recursion Available. */
+ ns_f_z, /* MBZ. */
+ ns_f_ad, /* Authentic Data (DNSSEC). */
+ ns_f_cd, /* Checking Disabled (DNSSEC). */
+ ns_f_rcode, /* Response code. */
+ ns_f_max
+} ns_flag;
+
+/*
+ * Currently defined opcodes.
+ */
+typedef enum __ns_opcode {
+ ns_o_query = 0, /* Standard query. */
+ ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */
+ ns_o_status = 2, /* Name server status query (unsupported). */
+ /* Opcode 3 is undefined/reserved. */
+ ns_o_notify = 4, /* Zone change notification. */
+ ns_o_update = 5, /* Zone update message. */
+ ns_o_max = 6
+} ns_opcode;
+
+/*
+ * Currently defined response codes.
+ */
+typedef enum __ns_rcode {
+ ns_r_noerror = 0, /* No error occurred. */
+ ns_r_formerr = 1, /* Format error. */
+ ns_r_servfail = 2, /* Server failure. */
+ ns_r_nxdomain = 3, /* Name error. */
+ ns_r_notimpl = 4, /* Unimplemented. */
+ ns_r_refused = 5, /* Operation refused. */
+ /* these are for BIND_UPDATE */
+ ns_r_yxdomain = 6, /* Name exists */
+ ns_r_yxrrset = 7, /* RRset exists */
+ ns_r_nxrrset = 8, /* RRset does not exist */
+ ns_r_notauth = 9, /* Not authoritative for zone */
+ ns_r_notzone = 10, /* Zone of record different from zone section */
+ ns_r_max = 11,
+ /* The following are TSIG extended errors */
+ ns_r_badsig = 16,
+ ns_r_badkey = 17,
+ ns_r_badtime = 18
+} ns_rcode;
+
+/* BIND_UPDATE */
+typedef enum __ns_update_operation {
+ ns_uop_delete = 0,
+ ns_uop_add = 1,
+ ns_uop_max = 2
+} ns_update_operation;
+
+/*
+ * This structure is used for TSIG authenticated messages
+ */
+struct ns_tsig_key {
+ char name[NS_MAXDNAME], alg[NS_MAXDNAME];
+ unsigned char *data;
+ unsigned len;
+};
+typedef struct ns_tsig_key ns_tsig_key;
+
+/*
+ * This structure is used for TSIG authenticated TCP messages
+ */
+struct ns_tcp_tsig_state {
+ int counter;
+ struct dst_key *key;
+ void *ctx;
+ unsigned char sig[NS_PACKETSZ];
+ unsigned siglen;
+};
+typedef struct ns_tcp_tsig_state ns_tcp_tsig_state;
+
+#define NS_TSIG_FUDGE 300
+#define NS_TSIG_TCP_COUNT 100
+#define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT"
+
+#define NS_TSIG_ERROR_NO_TSIG -10
+#define NS_TSIG_ERROR_NO_SPACE -11
+#define NS_TSIG_ERROR_FORMERR -12
+
+/*
+ * Currently defined type values for resources and queries.
+ */
+typedef enum __ns_type {
+ ns_t_invalid = 0, /* Cookie. */
+ ns_t_a = 1, /* Host address. */
+ ns_t_ns = 2, /* Authoritative server. */
+ ns_t_md = 3, /* Mail destination. */
+ ns_t_mf = 4, /* Mail forwarder. */
+ ns_t_cname = 5, /* Canonical name. */
+ ns_t_soa = 6, /* Start of authority zone. */
+ ns_t_mb = 7, /* Mailbox domain name. */
+ ns_t_mg = 8, /* Mail group member. */
+ ns_t_mr = 9, /* Mail rename name. */
+ ns_t_null = 10, /* Null resource record. */
+ ns_t_wks = 11, /* Well known service. */
+ ns_t_ptr = 12, /* Domain name pointer. */
+ ns_t_hinfo = 13, /* Host information. */
+ ns_t_minfo = 14, /* Mailbox information. */
+ ns_t_mx = 15, /* Mail routing information. */
+ ns_t_txt = 16, /* Text strings. */
+ ns_t_rp = 17, /* Responsible person. */
+ ns_t_afsdb = 18, /* AFS cell database. */
+ ns_t_x25 = 19, /* X_25 calling address. */
+ ns_t_isdn = 20, /* ISDN calling address. */
+ ns_t_rt = 21, /* Router. */
+ ns_t_nsap = 22, /* NSAP address. */
+ ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */
+ ns_t_sig = 24, /* Security signature. */
+ ns_t_key = 25, /* Security key. */
+ ns_t_px = 26, /* X.400 mail mapping. */
+ ns_t_gpos = 27, /* Geographical position (withdrawn). */
+ ns_t_aaaa = 28, /* Ip6 Address. */
+ ns_t_loc = 29, /* Location Information. */
+ ns_t_nxt = 30, /* Next domain (security). */
+ ns_t_eid = 31, /* Endpoint identifier. */
+ ns_t_nimloc = 32, /* Nimrod Locator. */
+ ns_t_srv = 33, /* Server Selection. */
+ ns_t_atma = 34, /* ATM Address */
+ ns_t_naptr = 35, /* Naming Authority PoinTeR */
+ ns_t_kx = 36, /* Key Exchange */
+ ns_t_cert = 37, /* Certification record */
+ ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */
+ ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */
+ ns_t_sink = 40, /* Kitchen sink (experimentatl) */
+ ns_t_opt = 41, /* EDNS0 option (meta-RR) */
+ ns_t_tsig = 250, /* Transaction signature. */
+ ns_t_ixfr = 251, /* Incremental zone transfer. */
+ ns_t_axfr = 252, /* Transfer zone of authority. */
+ ns_t_mailb = 253, /* Transfer mailbox records. */
+ ns_t_maila = 254, /* Transfer mail agent records. */
+ ns_t_any = 255, /* Wildcard match. */
+ ns_t_zxfr = 256, /* BIND-specific, nonstandard. */
+ ns_t_max = 65536
+} ns_type;
+
+/* Exclusively a QTYPE? (not also an RTYPE) */
+#define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \
+ (t) == ns_t_mailb || (t) == ns_t_maila)
+/* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */
+#define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt)
+/* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */
+#define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t))
+#define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr)
+#define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \
+ (t) == ns_t_zxfr)
+
+/*
+ * Values for class field
+ */
+typedef enum __ns_class {
+ ns_c_invalid = 0, /* Cookie. */
+ ns_c_in = 1, /* Internet. */
+ ns_c_2 = 2, /* unallocated/unsupported. */
+ ns_c_chaos = 3, /* MIT Chaos-net. */
+ ns_c_hs = 4, /* MIT Hesiod. */
+ /* Query class values which do not appear in resource records */
+ ns_c_none = 254, /* for prereq. sections in update requests */
+ ns_c_any = 255, /* Wildcard match. */
+ ns_c_max = 65536
+} ns_class;
+
+/* DNSSEC constants. */
+
+typedef enum __ns_key_types {
+ ns_kt_rsa = 1, /* key type RSA/MD5 */
+ ns_kt_dh = 2, /* Diffie Hellman */
+ ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */
+ ns_kt_private = 254 /* Private key type starts with OID */
+} ns_key_types;
+
+typedef enum __ns_cert_types {
+ cert_t_pkix = 1, /* PKIX (X.509v3) */
+ cert_t_spki = 2, /* SPKI */
+ cert_t_pgp = 3, /* PGP */
+ cert_t_url = 253, /* URL private type */
+ cert_t_oid = 254 /* OID private type */
+} ns_cert_types;
+
+/* Flags field of the KEY RR rdata. */
+#define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */
+#define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */
+#define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */
+#define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */
+#define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */
+/* The type bits can also be interpreted independently, as single bits: */
+#define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */
+#define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */
+#define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */
+#define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */
+#define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */
+#define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */
+#define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */
+#define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */
+#define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */
+#define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */
+#define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */
+#define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */
+#define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */
+#define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */
+#define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */
+#define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */
+#define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \
+ NS_KEY_RESERVED4 | \
+ NS_KEY_RESERVED5 | \
+ NS_KEY_RESERVED8 | \
+ NS_KEY_RESERVED9 | \
+ NS_KEY_RESERVED10 | \
+ NS_KEY_RESERVED11 )
+#define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */
+
+/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */
+#define NS_ALG_MD5RSA 1 /* MD5 with RSA */
+#define NS_ALG_DH 2 /* Diffie Hellman KEY */
+#define NS_ALG_DSA 3 /* DSA KEY */
+#define NS_ALG_DSS NS_ALG_DSA
+#define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */
+#define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */
+
+/* Protocol values */
+/* value 0 is reserved */
+#define NS_KEY_PROT_TLS 1
+#define NS_KEY_PROT_EMAIL 2
+#define NS_KEY_PROT_DNSSEC 3
+#define NS_KEY_PROT_IPSEC 4
+#define NS_KEY_PROT_ANY 255
+
+/* Signatures */
+#define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */
+#define NS_MD5RSA_MAX_BITS 2552
+ /* Total of binary mod and exp */
+#define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3)
+ /* Max length of text sig block */
+#define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4)
+#define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8)
+#define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8)
+
+#define NS_DSA_SIG_SIZE 41
+#define NS_DSA_MIN_SIZE 213
+#define NS_DSA_MAX_BYTES 405
+
+/* Offsets into SIG record rdata to find various values */
+#define NS_SIG_TYPE 0 /* Type flags */
+#define NS_SIG_ALG 2 /* Algorithm */
+#define NS_SIG_LABELS 3 /* How many labels in name */
+#define NS_SIG_OTTL 4 /* Original TTL */
+#define NS_SIG_EXPIR 8 /* Expiration time */
+#define NS_SIG_SIGNED 12 /* Signature time */
+#define NS_SIG_FOOT 16 /* Key footprint */
+#define NS_SIG_SIGNER 18 /* Domain name of who signed it */
+
+/* How RR types are represented as bit-flags in NXT records */
+#define NS_NXT_BITS 8
+#define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS)))
+#define NS_NXT_MAX 127
+
+/*
+ * Inline versions of get/put short/long. Pointer is advanced.
+ */
+#define NS_GET16(s, cp) do { \
+ register u_int8_t *t_cp = (u_int8_t *)(cp); \
+ (s) = ((u_int16_t)t_cp[0] << 8) \
+ | ((u_int16_t)t_cp[1]) \
+ ; \
+ (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_GET32(l, cp) do { \
+ register u_int8_t *t_cp = (u_int8_t *)(cp); \
+ (l) = ((u_int32_t)t_cp[0] << 24) \
+ | ((u_int32_t)t_cp[1] << 16) \
+ | ((u_int32_t)t_cp[2] << 8) \
+ | ((u_int32_t)t_cp[3]) \
+ ; \
+ (cp) += NS_INT32SZ; \
+} while (0)
+
+#define NS_PUT16(s, cp) do { \
+ register u_int16_t t_s = (u_int16_t)(s); \
+ register u_int8_t *t_cp = (u_int8_t *)(cp); \
+ *t_cp++ = t_s >> 8; \
+ *t_cp = t_s; \
+ (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_PUT32(l, cp) do { \
+ register u_int32_t t_l = (u_int32_t)(l); \
+ register u_int8_t *t_cp = (u_int8_t *)(cp); \
+ *t_cp++ = t_l >> 24; \
+ *t_cp++ = t_l >> 16; \
+ *t_cp++ = t_l >> 8; \
+ *t_cp = t_l; \
+ (cp) += NS_INT32SZ; \
+} while (0)
+
+#include <arpa/nameser_compat.h>
+
+#endif /* !_ARPA_NAMESER_H_ */
diff --git a/includes/arpa/nameser_compat.h b/includes/arpa/nameser_compat.h
new file mode 100644
index 0000000..7b5298a
--- /dev/null
+++ b/includes/arpa/nameser_compat.h
@@ -0,0 +1,183 @@
+/* Copyright (c) 1983, 1989
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * from nameser.h 8.1 (Berkeley) 6/2/93
+ * $Id: nameser_compat.h,v 1.2 2000-01-27 23:28:08 mellon Exp $
+ */
+
+#ifndef _ARPA_NAMESER_COMPAT_
+#define _ARPA_NAMESER_COMPAT_
+
+/*
+ * Structure for query header. The order of the fields is machine- and
+ * compiler-dependent, depending on the byte/bit order and the layout
+ * of bit fields. We use bit fields only in int variables, as this
+ * is all ANSI requires. This requires a somewhat confusing rearrangement.
+ */
+
+typedef struct {
+ unsigned id :16; /* query identification number */
+#if BYTE_ORDER == BIG_ENDIAN
+ /* fields in third byte */
+ unsigned qr: 1; /* response flag */
+ unsigned opcode: 4; /* purpose of message */
+ unsigned aa: 1; /* authoritive answer */
+ unsigned tc: 1; /* truncated message */
+ unsigned rd: 1; /* recursion desired */
+ /* fields in fourth byte */
+ unsigned ra: 1; /* recursion available */
+ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
+ unsigned ad: 1; /* authentic data from named */
+ unsigned cd: 1; /* checking disabled by resolver */
+ unsigned rcode :4; /* response code */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+ /* fields in third byte */
+ unsigned rd :1; /* recursion desired */
+ unsigned tc :1; /* truncated message */
+ unsigned aa :1; /* authoritive answer */
+ unsigned opcode :4; /* purpose of message */
+ unsigned qr :1; /* response flag */
+ /* fields in fourth byte */
+ unsigned rcode :4; /* response code */
+ unsigned cd: 1; /* checking disabled by resolver */
+ unsigned ad: 1; /* authentic data from named */
+ unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra :1; /* recursion available */
+#endif
+ /* remaining bytes */
+ unsigned qdcount :16; /* number of question entries */
+ unsigned ancount :16; /* number of answer entries */
+ unsigned nscount :16; /* number of authority entries */
+ unsigned arcount :16; /* number of resource entries */
+} HEADER;
+
+#define PACKETSZ NS_PACKETSZ
+#define MAXDNAME NS_MAXDNAME
+#define MAXCDNAME NS_MAXCDNAME
+#define MAXLABEL NS_MAXLABEL
+#define HFIXEDSZ NS_HFIXEDSZ
+#define QFIXEDSZ NS_QFIXEDSZ
+#define RRFIXEDSZ NS_RRFIXEDSZ
+#define INT32SZ NS_INT32SZ
+#define INT16SZ NS_INT16SZ
+#define INADDRSZ NS_INADDRSZ
+#define IN6ADDRSZ NS_IN6ADDRSZ
+#define INDIR_MASK NS_CMPRSFLGS
+#define NAMESERVER_PORT NS_DEFAULTPORT
+
+#define S_ZONE ns_s_zn
+#define S_PREREQ ns_s_pr
+#define S_UPDATE ns_s_ud
+#define S_ADDT ns_s_ar
+
+#define QUERY ns_o_query
+#define IQUERY ns_o_iquery
+#define STATUS ns_o_status
+#define NS_NOTIFY_OP ns_o_notify
+#define NS_UPDATE_OP ns_o_update
+
+#define NOERROR ns_r_noerror
+#define FORMERR ns_r_formerr
+#define SERVFAIL ns_r_servfail
+#define NXDOMAIN ns_r_nxdomain
+#define NOTIMP ns_r_notimpl
+#define REFUSED ns_r_refused
+#define YXDOMAIN ns_r_yxdomain
+#define YXRRSET ns_r_yxrrset
+#define NXRRSET ns_r_nxrrset
+#define NOTAUTH ns_r_notauth
+#define NOTZONE ns_r_notzone
+/*#define BADSIG ns_r_badsig*/
+/*#define BADKEY ns_r_badkey*/
+/*#define BADTIME ns_r_badtime*/
+
+
+#define DELETE ns_uop_delete
+#define ADD ns_uop_add
+
+#define T_A ns_t_a
+#define T_NS ns_t_ns
+#define T_MD ns_t_md
+#define T_MF ns_t_mf
+#define T_CNAME ns_t_cname
+#define T_SOA ns_t_soa
+#define T_MB ns_t_mb
+#define T_MG ns_t_mg
+#define T_MR ns_t_mr
+#define T_NULL ns_t_null
+#define T_WKS ns_t_wks
+#define T_PTR ns_t_ptr
+#define T_HINFO ns_t_hinfo
+#define T_MINFO ns_t_minfo
+#define T_MX ns_t_mx
+#define T_TXT ns_t_txt
+#define T_RP ns_t_rp
+#define T_AFSDB ns_t_afsdb
+#define T_X25 ns_t_x25
+#define T_ISDN ns_t_isdn
+#define T_RT ns_t_rt
+#define T_NSAP ns_t_nsap
+#define T_NSAP_PTR ns_t_nsap_ptr
+#define T_SIG ns_t_sig
+#define T_KEY ns_t_key
+#define T_PX ns_t_px
+#define T_GPOS ns_t_gpos
+#define T_AAAA ns_t_aaaa
+#define T_LOC ns_t_loc
+#define T_NXT ns_t_nxt
+#define T_EID ns_t_eid
+#define T_NIMLOC ns_t_nimloc
+#define T_SRV ns_t_srv
+#define T_ATMA ns_t_atma
+#define T_NAPTR ns_t_naptr
+#define T_TSIG ns_t_tsig
+#define T_IXFR ns_t_ixfr
+#define T_AXFR ns_t_axfr
+#define T_MAILB ns_t_mailb
+#define T_MAILA ns_t_maila
+#define T_ANY ns_t_any
+
+#define C_IN ns_c_in
+#define C_CHAOS ns_c_chaos
+#define C_HS ns_c_hs
+/* BIND_UPDATE */
+#define C_NONE ns_c_none
+#define C_ANY ns_c_any
+
+#define GETSHORT NS_GET16
+#define GETLONG NS_GET32
+#define PUTSHORT NS_PUT16
+#define PUTLONG NS_PUT32
+
+#endif /* _ARPA_NAMESER_COMPAT_ */
diff --git a/includes/cdefs.h b/includes/cdefs.h
new file mode 100644
index 0000000..2f61ed7
--- /dev/null
+++ b/includes/cdefs.h
@@ -0,0 +1,60 @@
+/* cdefs.h
+
+ Standard C definitions... */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ * Copyright (c) 1995 RadioMail Corporation. All rights reserved.
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for Internet Systems Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#if !defined (__ISC_DHCP_CDEFS_H__)
+#define __ISC_DHCP_CDEFS_H__
+/* Delete attributes if not gcc or not the right version of gcc. */
+#if !defined(__GNUC__) || __GNUC__ < 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || defined (darwin)
+#define __attribute__(x)
+#endif
+
+/* The following macro handles the case of unwanted return values. In
+ * GCC one can specify an attribute for a function to generate a warning
+ * if the return value of the function is ignored and one can't dispose of
+ * the warning by the use of void. In conjunction with the use of -Werror
+ * these warnings prohibit the compilation of the package. This macro
+ * allows us to assign the return value to a variable and then ignore it.
+ */
+#if !defined(__GNUC__) || (__GNUC__ < 4)
+#define IGNORE_RET(x) (void) x
+#else
+#define IGNORE_RET(x) \
+ do { \
+ int ignore_return; \
+ ignore_return = x; \
+ } while (0)
+#endif
+
+#endif /* __ISC_DHCP_CDEFS_H__ */
diff --git a/includes/config.h.in b/includes/config.h.in
new file mode 100644
index 0000000..2e52f7d
--- /dev/null
+++ b/includes/config.h.in
@@ -0,0 +1,265 @@
+/* includes/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to compile debug-only DHCP software. */
+#undef DEBUG
+
+/* Define to queue multiple DHCPACK replies per fsync. */
+#undef DELAYED_ACK
+
+/* Define to BIG_ENDIAN for MSB (Motorola or SPARC CPUs) or LITTLE_ENDIAN for
+ LSB (Intel CPUs). */
+#undef DHCP_BYTE_ORDER
+
+/* Define to 1 to include DHCPv6 support. */
+#undef DHCPv6
+
+/* Define to any value to chroot() prior to loading config. */
+#undef EARLY_CHROOT
+
+/* Define to include execute() config language support. */
+#undef ENABLE_EXECUTE
+
+/* Define to include Failover Protocol support. */
+#undef FAILOVER_PROTOCOL
+
+/* Define to nothing if C supports flexible array members, and to 1 if it does
+ not. That way, with a declaration like `struct s { int n; double
+ d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99
+ compilers. When computing the size of such an object, don't use 'sizeof
+ (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)'
+ instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with
+ MSVC and with C++ compilers. */
+#undef FLEXIBLE_ARRAY_MEMBER
+
+/* Define to 1 to use the Berkeley Packet Filter interface code. */
+#undef HAVE_BPF
+
+/* Define to 1 if you have the /dev/random file. */
+#undef HAVE_DEV_RANDOM
+
+/* Define to 1 to use DLPI interface code. */
+#undef HAVE_DLPI
+
+/* Define to 1 if you have the <ifaddrs.h> header file. */
+#undef HAVE_IFADDRS_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#undef HAVE_LINUX_TYPES_H
+
+/* Define to 1 to use the Linux Packet Filter interface code. */
+#undef HAVE_LPF
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <net/if6.h> header file. */
+#undef HAVE_NET_IF6_H
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+#undef HAVE_NET_IF_DL_H
+
+/* Define to 1 if you have the <regex.h> header file. */
+#undef HAVE_REGEX_H
+
+/* Define to 1 if the sockaddr structure has a length field. */
+#undef HAVE_SA_LEN
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if the system has 'struct if_laddrconf'. */
+#undef ISC_PLATFORM_HAVEIF_LADDRCONF
+
+/* Define to 1 if the system has 'struct if_laddrreq'. */
+#undef ISC_PLATFORM_HAVEIF_LADDRREQ
+
+/* Define to 1 if the system has 'struct lifnum'. */
+#undef ISC_PLATFORM_HAVELIFNUM
+
+/* Define to 1 if the inet_aton() function is missing. */
+#undef NEED_INET_ATON
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to any value to include Ari's PARANOIA patch. */
+#undef PARANOIA
+
+/* The size of `struct iaddr *', as computed by sizeof. */
+#undef SIZEOF_STRUCT_IADDR_P
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to include server activity tracing support. */
+#undef TRACING
+
+/* Define to 1 to use the standard BSD socket API. */
+#undef USE_SOCKETS
+
+/* Define to 1 to enable IPv4 packet info support. */
+#undef USE_V4_PKTINFO
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* File for dhclient6 leases. */
+#undef _PATH_DHCLIENT6_DB
+
+/* File for dhclient6 process information. */
+#undef _PATH_DHCLIENT6_PID
+
+/* File for dhclient leases. */
+#undef _PATH_DHCLIENT_DB
+
+/* File for dhclient process information. */
+#undef _PATH_DHCLIENT_PID
+
+/* File for dhcpd6 leases. */
+#undef _PATH_DHCPD6_DB
+
+/* File for dhcpd6 process information. */
+#undef _PATH_DHCPD6_PID
+
+/* File for dhcpd leases. */
+#undef _PATH_DHCPD_DB
+
+/* File for dhcpd process information. */
+#undef _PATH_DHCPD_PID
+
+/* File for dhcrelay6 process information. */
+#undef _PATH_DHCRELAY6_PID
+
+/* File for dhcrelay process information. */
+#undef _PATH_DHCRELAY_PID
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Enable extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int16_t
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int32_t
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int64_t
+
+/* Define to the type of a signed integer type of width exactly 8 bits if such
+ a type exists and the standard includes do not define it. */
+#undef int8_t
+
+/* Define a type for 16-bit unsigned integers. */
+#undef u_int16_t
+
+/* Define a type for 32-bit unsigned integers. */
+#undef u_int32_t
+
+/* Define a type for 64-bit unsigned integers. */
+#undef u_int64_t
+
+/* Define a type for 8-bit unsigned integers. */
+#undef u_int8_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint8_t
diff --git a/includes/ctrace.h b/includes/ctrace.h
new file mode 100644
index 0000000..c3f0314
--- /dev/null
+++ b/includes/ctrace.h
@@ -0,0 +1,77 @@
+/* trace.h
+
+ Definitions for dhcp tracing facility... */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
+ * about Internet Systems Consortium, see https://www.isc.org/. To
+ * learn more about Nominum, Inc., see ``http://www.nominum.com''.
+ */
+
+typedef struct {
+ struct in_addr primary_address;
+ u_int32_t index;
+ struct hardware hw_address;
+ char name [IFNAMSIZ];
+} trace_interface_packet_t;
+
+typedef struct {
+ u_int32_t index;
+ struct iaddr from;
+ u_int16_t from_port;
+ struct hardware hfrom;
+ u_int8_t havehfrom;
+} trace_inpacket_t;
+
+typedef struct {
+ u_int32_t index;
+ struct iaddr from;
+ struct iaddr to;
+ u_int16_t to_port;
+ struct hardware hto;
+ u_int8_t havehto;
+} trace_outpacket_t;
+
+void trace_interface_register (trace_type_t *, struct interface_info *);
+void trace_interface_input (trace_type_t *, unsigned, char *);
+void trace_interface_stop (trace_type_t *);
+void trace_inpacket_stash (struct interface_info *,
+ struct dhcp_packet *, unsigned, unsigned int,
+ struct iaddr, struct hardware *);
+void trace_inpacket_input (trace_type_t *, unsigned, char *);
+void trace_inpacket_stop (trace_type_t *);
+void trace_outpacket_input (trace_type_t *, unsigned, char *);
+void trace_outpacket_stop (trace_type_t *);
+ssize_t trace_packet_send (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+void trace_icmp_input_input (trace_type_t *, unsigned, char *);
+void trace_icmp_input_stop (trace_type_t *);
+void trace_icmp_output_input (trace_type_t *, unsigned, char *);
+void trace_icmp_output_stop (trace_type_t *);
+void trace_seed_stash (trace_type_t *, unsigned);
+void trace_seed_input (trace_type_t *, unsigned, char *);
+void trace_seed_stop (trace_type_t *);
diff --git a/includes/dhcp.h b/includes/dhcp.h
new file mode 100644
index 0000000..1af2adf
--- /dev/null
+++ b/includes/dhcp.h
@@ -0,0 +1,202 @@
+/* dhcp.h
+
+ Protocol structures... */
+
+/*
+ * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises. To learn more
+ * about Internet Systems Consortium, see ``https://www.isc.org''.
+ * To learn more about Vixie Enterprises, see ``http://www.vix.com''.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#define DHCP_UDP_OVERHEAD (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 BOOTP_MIN_LEN 300
+
+#define DHCP_MTU_MAX 1500
+#define DHCP_MTU_MIN 576
+
+#define DHCP_MAX_OPTION_LEN (DHCP_MTU_MAX - DHCP_FIXED_LEN)
+#define DHCP_MIN_OPTION_LEN (DHCP_MTU_MIN - DHCP_FIXED_LEN)
+
+struct dhcp_packet {
+ u_int8_t op; /* 0: Message opcode/type */
+ u_int8_t htype; /* 1: Hardware addr type (net/if_types.h) */
+ u_int8_t hlen; /* 2: Hardware addr length */
+ u_int8_t hops; /* 3: Number of relay agent hops from client */
+ u_int32_t xid; /* 4: Transaction ID */
+ u_int16_t secs; /* 8: Seconds since client started looking */
+ u_int16_t flags; /* 10: Flag bits */
+ struct in_addr ciaddr; /* 12: Client IP address (if already in use) */
+ struct in_addr yiaddr; /* 16: Client IP address */
+ struct in_addr siaddr; /* 18: IP address of next server to talk to */
+ struct in_addr giaddr; /* 20: DHCP relay agent IP address */
+ unsigned char chaddr [16]; /* 24: Client hardware address */
+ char sname [DHCP_SNAME_LEN]; /* 40: Server name */
+ char file [DHCP_FILE_LEN]; /* 104: Boot filename */
+ unsigned char options [DHCP_MAX_OPTION_LEN];
+ /* 212: 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 10Mbps */
+#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_VENDOR_CLASS_IDENTIFIER 60
+#define DHO_DHCP_CLIENT_IDENTIFIER 61
+#define DHO_NWIP_DOMAIN_NAME 62
+#define DHO_NWIP_SUBOPTIONS 63
+#define DHO_USER_CLASS 77
+#define DHO_FQDN 81
+#define DHO_DHCP_AGENT_OPTIONS 82
+#define DHO_AUTHENTICATE 90 /* RFC3118, was 210 */
+#define DHO_CLIENT_LAST_TRANSACTION_TIME 91
+#define DHO_ASSOCIATED_IP 92
+#define DHO_SUBNET_SELECTION 118 /* RFC3011! */
+#define DHO_DOMAIN_SEARCH 119 /* RFC3397 */
+#define DHO_VIVCO_SUBOPTIONS 124
+#define DHO_VIVSO_SUBOPTIONS 125
+
+#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
+#define DHCPLEASEQUERY 10
+#define DHCPLEASEUNASSIGNED 11
+#define DHCPLEASEUNKNOWN 12
+#define DHCPLEASEACTIVE 13
+
+
+/* Relay Agent Information option subtypes: */
+#define RAI_CIRCUIT_ID 1
+#define RAI_REMOTE_ID 2
+#define RAI_AGENT_ID 3
+#define RAI_LINK_SELECT 5
+
+/* FQDN suboptions: */
+#define FQDN_NO_CLIENT_UPDATE 1
+#define FQDN_SERVER_UPDATE 2
+#define FQDN_ENCODED 3
+#define FQDN_RCODE1 4
+#define FQDN_RCODE2 5
+#define FQDN_HOSTNAME 6
+#define FQDN_DOMAINNAME 7
+#define FQDN_FQDN 8
+#define FQDN_SUBOPTION_COUNT 8
+
+/* Enterprise Suboptions: */
+#define VENDOR_ISC_SUBOPTIONS 2495
+
+#endif /* DHCP_H */
+
diff --git a/includes/dhcp6.h b/includes/dhcp6.h
new file mode 100644
index 0000000..920be80
--- /dev/null
+++ b/includes/dhcp6.h
@@ -0,0 +1,213 @@
+/* dhcp6.h
+
+ DHCPv6 Protocol structures... */
+
+/*
+ * Copyright (c) 2006-2010 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+
+/* DHCPv6 Option codes: */
+
+#define D6O_CLIENTID 1 /* RFC3315 */
+#define D6O_SERVERID 2
+#define D6O_IA_NA 3
+#define D6O_IA_TA 4
+#define D6O_IAADDR 5
+#define D6O_ORO 6
+#define D6O_PREFERENCE 7
+#define D6O_ELAPSED_TIME 8
+#define D6O_RELAY_MSG 9
+/* Option code 10 unassigned. */
+#define D6O_AUTH 11
+#define D6O_UNICAST 12
+#define D6O_STATUS_CODE 13
+#define D6O_RAPID_COMMIT 14
+#define D6O_USER_CLASS 15
+#define D6O_VENDOR_CLASS 16
+#define D6O_VENDOR_OPTS 17
+#define D6O_INTERFACE_ID 18
+#define D6O_RECONF_MSG 19
+#define D6O_RECONF_ACCEPT 20
+#define D6O_SIP_SERVERS_DNS 21 /* RFC3319 */
+#define D6O_SIP_SERVERS_ADDR 22 /* RFC3319 */
+#define D6O_NAME_SERVERS 23 /* RFC3646 */
+#define D6O_DOMAIN_SEARCH 24 /* RFC3646 */
+#define D6O_IA_PD 25 /* RFC3633 */
+#define D6O_IAPREFIX 26 /* RFC3633 */
+#define D6O_NIS_SERVERS 27 /* RFC3898 */
+#define D6O_NISP_SERVERS 28 /* RFC3898 */
+#define D6O_NIS_DOMAIN_NAME 29 /* RFC3898 */
+#define D6O_NISP_DOMAIN_NAME 30 /* RFC3898 */
+#define D6O_SNTP_SERVERS 31 /* RFC4075 */
+#define D6O_INFORMATION_REFRESH_TIME 32 /* RFC4242 */
+#define D6O_BCMCS_SERVER_D 33 /* RFC4280 */
+#define D6O_BCMCS_SERVER_A 34 /* RFC4280 */
+/* 35 is unassigned */
+#define D6O_GEOCONF_CIVIC 36 /* RFC4776 */
+#define D6O_REMOTE_ID 37 /* RFC4649 */
+#define D6O_SUBSCRIBER_ID 38 /* RFC4580 */
+#define D6O_CLIENT_FQDN 39 /* RFC4704 */
+#define D6O_PANA_AGENT 40 /* paa-option */
+#define D6O_NEW_POSIX_TIMEZONE 41 /* RFC4833 */
+#define D6O_NEW_TZDB_TIMEZONE 42 /* RFC4833 */
+#define D6O_ERO 43 /* RFC4994 */
+#define D6O_LQ_QUERY 44 /* RFC5007 */
+#define D6O_CLIENT_DATA 45 /* RFC5007 */
+#define D6O_CLT_TIME 46 /* RFC5007 */
+#define D6O_LQ_RELAY_DATA 47 /* RFC5007 */
+#define D6O_LQ_CLIENT_LINK 48 /* RFC5007 */
+
+/*
+ * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
+ */
+#define STATUS_Success 0
+#define STATUS_UnspecFail 1
+#define STATUS_NoAddrsAvail 2
+#define STATUS_NoBinding 3
+#define STATUS_NotOnLink 4
+#define STATUS_UseMulticast 5
+#define STATUS_NoPrefixAvail 6
+#define STATUS_UnknownQueryType 7
+#define STATUS_MalformedQuery 8
+#define STATUS_NotConfigured 9
+#define STATUS_NotAllowed 10
+
+/*
+ * DHCPv6 message types, defined in section 5.3 of RFC 3315
+ */
+#define DHCPV6_SOLICIT 1
+#define DHCPV6_ADVERTISE 2
+#define DHCPV6_REQUEST 3
+#define DHCPV6_CONFIRM 4
+#define DHCPV6_RENEW 5
+#define DHCPV6_REBIND 6
+#define DHCPV6_REPLY 7
+#define DHCPV6_RELEASE 8
+#define DHCPV6_DECLINE 9
+#define DHCPV6_RECONFIGURE 10
+#define DHCPV6_INFORMATION_REQUEST 11
+#define DHCPV6_RELAY_FORW 12
+#define DHCPV6_RELAY_REPL 13
+#define DHCPV6_LEASEQUERY 14
+#define DHCPV6_LEASEQUERY_REPLY 15
+
+extern const char *dhcpv6_type_names[];
+extern const int dhcpv6_type_name_max;
+
+/* DUID type definitions (RFC3315 section 9).
+ */
+#define DUID_LLT 1
+#define DUID_EN 2
+#define DUID_LL 3
+
+/* Offsets into IA_*'s where Option spaces commence. */
+#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+#define IA_TA_OFFSET 4 /* IAID only, 4 octets */
+#define IA_PD_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+
+/* Offset into IAADDR's where Option spaces commence. */
+#define IAADDR_OFFSET 24
+
+/* Offset into IAPREFIX's where Option spaces commence. */
+#define IAPREFIX_OFFSET 25
+
+/* Offset into LQ_QUERY's where Option spaces commence. */
+#define LQ_QUERY_OFFSET 17
+
+/*
+ * DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315
+ */
+#define All_DHCP_Relay_Agents_and_Servers "FF02::1:2"
+#define All_DHCP_Servers "FF05::1:3"
+
+/*
+ * DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007)
+ */
+
+#define SOL_MAX_DELAY 1
+#define SOL_TIMEOUT 1
+#define SOL_MAX_RT 120
+#define REQ_TIMEOUT 1
+#define REQ_MAX_RT 30
+#define REQ_MAX_RC 10
+#define CNF_MAX_DELAY 1
+#define CNF_TIMEOUT 1
+#define CNF_MAX_RT 4
+#define CNF_MAX_RD 10
+#define REN_TIMEOUT 10
+#define REN_MAX_RT 600
+#define REB_TIMEOUT 10
+#define REB_MAX_RT 600
+#define INF_MAX_DELAY 1
+#define INF_TIMEOUT 1
+#define INF_MAX_RT 120
+#define REL_TIMEOUT 1
+#define REL_MAX_RC 5
+#define DEC_TIMEOUT 1
+#define DEC_MAX_RC 5
+#define REC_TIMEOUT 2
+#define REC_MAX_RC 8
+#define HOP_COUNT_LIMIT 32
+#define LQ6_TIMEOUT 1
+#define LQ6_MAX_RT 10
+#define LQ6_MAX_RC 5
+
+/*
+ * Normal packet format, defined in section 6 of RFC 3315
+ */
+struct dhcpv6_packet {
+ unsigned char msg_type;
+ unsigned char transaction_id[3];
+ unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Offset into DHCPV6 Reply packets where Options spaces commence. */
+#define REPLY_OPTIONS_INDEX 4
+
+/*
+ * Relay packet format, defined in section 7 of RFC 3315
+ */
+struct dhcpv6_relay_packet {
+ unsigned char msg_type;
+ unsigned char hop_count;
+ unsigned char link_address[16];
+ unsigned char peer_address[16];
+ unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Leasequery query-types (RFC 5007) */
+
+#define LQ6QT_BY_ADDRESS 1
+#define LQ6QT_BY_CLIENTID 2
+
+/*
+ * DUID time starts 2000-01-01.
+ * This constant is the number of seconds since 1970-01-01,
+ * when the Unix epoch began.
+ */
+#define DUID_TIME_EPOCH 946684800
+
+/* Information-Request Time option (RFC 4242) */
+
+#define IRT_DEFAULT 86400
+#define IRT_MINIMUM 600
+
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
new file mode 100644
index 0000000..99d4387
--- /dev/null
+++ b/includes/dhcpd.h
@@ -0,0 +1,3552 @@
+/* dhcpd.h
+
+ Definitions for dhcpd... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "config.h"
+
+#ifndef __CYGWIN32__
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <netdb.h>
+#else
+#define fd_set cygwin_fd_set
+#include <sys/types.h>
+#endif
+#include <stddef.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <net/if.h>
+#undef FDDI
+#include <net/route.h>
+#include <net/if_arp.h>
+#if HAVE_NET_IF_DL_H
+# include <net/if_dl.h>
+#endif
+
+#include <setjmp.h>
+
+#include "cdefs.h"
+#include "osdep.h"
+
+#include "arpa/nameser.h"
+
+#include "minires.h"
+
+struct hash_table;
+typedef struct hash_table group_hash_t;
+typedef struct hash_table universe_hash_t;
+typedef struct hash_table option_name_hash_t;
+typedef struct hash_table option_code_hash_t;
+typedef struct hash_table dns_zone_hash_t;
+typedef struct hash_table lease_ip_hash_t;
+typedef struct hash_table lease_id_hash_t;
+typedef struct hash_table host_hash_t;
+typedef struct hash_table class_hash_t;
+
+typedef time_t TIME;
+
+#ifndef EOL
+#define EOL '\n'
+#endif
+
+#include <omapip/isclib.h>
+#include <omapip/result.h>
+
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "statement.h"
+#include "tree.h"
+#include "inet.h"
+#include "dhctoken.h"
+
+#include <omapip/omapip_p.h>
+
+#if defined(LDAP_CONFIGURATION)
+# include <ldap.h>
+# include <sys/utsname.h> /* for uname() */
+#endif
+
+#if !defined (BYTE_NAME_HASH_SIZE)
+# define BYTE_NAME_HASH_SIZE 401 /* Default would be ridiculous. */
+#endif
+#if !defined (BYTE_CODE_HASH_SIZE)
+# define BYTE_CODE_HASH_SIZE 254 /* Default would be ridiculous. */
+#endif
+
+/* Although it is highly improbable that a 16-bit option space might
+ * actually use 2^16 actual defined options, it is the worst case
+ * scenario we must prepare for. Having 4 options per bucket in this
+ * case is pretty reasonable.
+ */
+#if !defined (WORD_NAME_HASH_SIZE)
+# define WORD_NAME_HASH_SIZE 20479
+#endif
+#if !defined (WORD_CODE_HASH_SIZE)
+# define WORD_CODE_HASH_SIZE 16384
+#endif
+
+/* Not only is it improbable that the 32-bit spaces might actually use 2^32
+ * defined options, it is infeasible. It would be best for this kind of
+ * space to be dynamically sized. Instead we size it at the word hash's
+ * level.
+ */
+#if !defined (QUAD_NAME_HASH_SIZE)
+# define QUAD_NAME_HASH_SIZE WORD_NAME_HASH_SIZE
+#endif
+#if !defined (QUAD_CODE_HASH_SIZE)
+# define QUAD_CODE_HASH_SIZE WORD_CODE_HASH_SIZE
+#endif
+
+#if !defined (DNS_HASH_SIZE)
+# define DNS_HASH_SIZE 0 /* Default. */
+#endif
+
+/* Default size to use for name/code hashes on user-defined option spaces. */
+#if !defined (DEFAULT_SPACE_HASH_SIZE)
+# define DEFAULT_SPACE_HASH_SIZE 11
+#endif
+
+#if !defined (NWIP_HASH_SIZE)
+# define NWIP_HASH_SIZE 17 /* A really small table. */
+#endif
+
+#if !defined (FQDN_HASH_SIZE)
+# define FQDN_HASH_SIZE 13 /* A ridiculously small table. */
+#endif
+
+/* I really doubt a given installation is going to have more than a few
+ * hundred vendors involved.
+ */
+#if !defined (VIVCO_HASH_SIZE)
+# define VIVCO_HASH_SIZE 127
+#endif
+
+#if !defined (VIVSO_HASH_SIZE)
+# define VIVSO_HASH_SIZE VIVCO_HASH_SIZE
+#endif
+
+#if !defined (VSIO_HASH_SIZE)
+# define VSIO_HASH_SIZE VIVCO_HASH_SIZE
+#endif
+
+#if !defined (VIV_ISC_HASH_SIZE)
+# define VIV_ISC_HASH_SIZE 3 /* An incredulously small table. */
+#endif
+
+#if !defined (UNIVERSE_HASH_SIZE)
+# define UNIVERSE_HASH_SIZE 13 /* A really small table. */
+#endif
+
+#if !defined (GROUP_HASH_SIZE)
+# define GROUP_HASH_SIZE 0 /* Default. */
+#endif
+
+/* At least one person has indicated they use ~20k host records.
+ */
+#if !defined (HOST_HASH_SIZE)
+# define HOST_HASH_SIZE 22501
+#endif
+
+/* We have user reports of use of ISC DHCP numbering leases in the 200k's.
+ *
+ * We also have reports of folks using 10.0/8 as a dynamic range. The
+ * following is something of a compromise between the two. At the ~2-3
+ * hundred thousand leases, there's ~2-3 leases to search in each bucket.
+ */
+#if !defined (LEASE_HASH_SIZE)
+# define LEASE_HASH_SIZE 100003
+#endif
+
+/* It is not known what the worst case subclass hash size is. We estimate
+ * high, I think.
+ */
+#if !defined (SCLASS_HASH_SIZE)
+# define SCLASS_HASH_SIZE 12007
+#endif
+
+#if !defined (AGENT_HASH_SIZE)
+# define AGENT_HASH_SIZE 11 /* A really small table. */
+#endif
+
+/* The server hash size is used for both names and codes. There aren't
+ * many (roughly 50 at the moment), so we use a smaller table. If we
+ * use a 1:1 table size, then we get name collisions due to poor name
+ * hashing. So we use double the space we need, which drastically
+ * reduces collisions.
+ */
+#if !defined (SERVER_HASH_SIZE)
+# define SERVER_HASH_SIZE (2*(sizeof(server_options) / sizeof(struct option)))
+#endif
+
+
+/* How many options are likely to appear in a single packet? */
+#if !defined (OPTION_HASH_SIZE)
+# define OPTION_HASH_SIZE 17
+# define OPTION_HASH_PTWO 32 /* Next power of two above option hash. */
+# define OPTION_HASH_EXP 5 /* The exponent for that power of two. */
+#endif
+
+#define compute_option_hash(x) \
+ (((x) & (OPTION_HASH_PTWO - 1)) + \
+ (((x) >> OPTION_HASH_EXP) & \
+ (OPTION_HASH_PTWO - 1))) % OPTION_HASH_SIZE;
+
+enum dhcp_shutdown_state {
+ shutdown_listeners,
+ shutdown_omapi_connections,
+ shutdown_drop_omapi_connections,
+ shutdown_dhcp,
+ shutdown_done
+};
+
+/* Client FQDN option, failover FQDN option, etc. */
+typedef struct {
+ u_int8_t codes [2];
+ unsigned length;
+ u_int8_t *data;
+} ddns_fqdn_t;
+
+#include "failover.h"
+
+/* A parsing context. */
+
+struct parse {
+ int lexline;
+ int lexchar;
+ char *token_line;
+ char *prev_line;
+ char *cur_line;
+ const char *tlname;
+ int eol_token;
+
+ /*
+ * In order to give nice output when we have a parsing error
+ * in our file, we keep track of where we are in the line so
+ * that we can show the user.
+ *
+ * We need to keep track of two lines, because we can look
+ * ahead, via the "peek" function, to the next line sometimes.
+ *
+ * The "line1" and "line2" variables act as buffers for this
+ * information. The "lpos" variable tells us where we are in the
+ * line.
+ *
+ * When we "put back" a character from the parsing context, we
+ * do not want to have the character appear twice in the error
+ * output. So, we set a flag, the "ugflag", which the
+ * get_char() function uses to check for this condition.
+ */
+ char line1 [81];
+ char line2 [81];
+ int lpos;
+ int line;
+ int tlpos;
+ int tline;
+ enum dhcp_token token;
+ int ugflag;
+ char *tval;
+ int tlen;
+ char tokbuf [1500];
+
+ int warnings_occurred;
+ int file;
+ char *inbuf;
+ size_t bufix, buflen;
+ size_t bufsiz;
+
+ struct parse *saved_state;
+
+#if defined(LDAP_CONFIGURATION)
+ /*
+ * LDAP configuration uses a call-back to iteratively read config
+ * off of the LDAP repository.
+ * XXX: The token stream can not be rewound reliably, so this must
+ * be addressed for DHCPv6 support.
+ */
+ int (*read_function)(struct parse *);
+#endif
+};
+
+/* Variable-length array of data. */
+
+struct string_list {
+ struct string_list *next;
+ char string [1];
+};
+
+/* A name server, from /etc/resolv.conf. */
+struct name_server {
+ struct name_server *next;
+ struct sockaddr_in addr;
+ TIME rcdate;
+};
+
+/* A domain search list element. */
+struct domain_search_list {
+ struct domain_search_list *next;
+ char *domain;
+ TIME rcdate;
+};
+
+/* Option tag structures are used to build chains of option tags, for
+ when we're sure we're not going to have enough of them to justify
+ maintaining an array. */
+
+struct option_tag {
+ struct option_tag *next;
+ u_int8_t data [1];
+};
+
+/* An agent option structure. We need a special structure for the
+ Relay Agent Information option because if more than one appears in
+ a message, we have to keep them separate. */
+
+struct agent_options {
+ struct agent_options *next;
+ int length;
+ struct option_tag *first;
+};
+
+struct option_cache {
+ int refcnt;
+ struct option_cache *next;
+ struct expression *expression;
+ struct option *option;
+ struct data_string data;
+
+ #define OPTION_HAD_NULLS 0x00000001
+ u_int32_t flags;
+};
+
+struct option_state {
+ int refcnt;
+ int universe_count;
+ int site_universe;
+ int site_code_min;
+ void *universes [1];
+};
+
+/* A dhcp packet and the pointers to its option values. */
+struct packet {
+ struct dhcp_packet *raw;
+ int refcnt;
+ unsigned packet_length;
+ int packet_type;
+
+ unsigned char dhcpv6_msg_type; /* DHCPv6 message type */
+
+ /* DHCPv6 transaction ID */
+ unsigned char dhcpv6_transaction_id[3];
+
+ /* DHCPv6 relay information */
+ unsigned char dhcpv6_hop_count;
+ struct in6_addr dhcpv6_link_address;
+ struct in6_addr dhcpv6_peer_address;
+
+ /* DHCPv6 packet containing this one, or NULL if none */
+ struct packet *dhcpv6_container_packet;
+
+ int options_valid;
+ int client_port;
+ struct iaddr client_addr;
+ struct interface_info *interface; /* Interface on which packet
+ was received. */
+ struct hardware *haddr; /* Physical link address
+ of local sender (maybe gateway). */
+
+ /* Information for relay agent options (see
+ draft-ietf-dhc-agent-options-xx.txt). */
+ u_int8_t *circuit_id; /* Circuit ID of client connection. */
+ int circuit_id_len;
+ u_int8_t *remote_id; /* Remote ID of client. */
+ int remote_id_len;
+
+ int got_requested_address; /* True if client sent the
+ dhcp-requested-address option. */
+
+ struct shared_network *shared_network;
+ struct option_state *options;
+
+#if !defined (PACKET_MAX_CLASSES)
+# define PACKET_MAX_CLASSES 5
+#endif
+ int class_count;
+ struct class *classes [PACKET_MAX_CLASSES];
+
+ int known;
+ int authenticated;
+
+ /* If we stash agent options onto the packet option state, to pretend
+ * options we got in a previous exchange were still there, we need
+ * to signal this in a reliable way.
+ */
+ isc_boolean_t agent_options_stashed;
+
+ /*
+ * ISC_TRUE if packet received unicast (as opposed to multicast).
+ * Only used in DHCPv6.
+ */
+ isc_boolean_t unicast;
+};
+
+/* A network interface's MAC address. */
+
+struct hardware {
+ u_int8_t hlen;
+ u_int8_t hbuf [17];
+};
+
+#if defined(LDAP_CONFIGURATION)
+# define LDAP_BUFFER_SIZE 8192
+# define LDAP_METHOD_STATIC 0
+# define LDAP_METHOD_DYNAMIC 1
+#if defined (LDAP_USE_SSL)
+# define LDAP_SSL_OFF 0
+# define LDAP_SSL_ON 1
+# define LDAP_SSL_TLS 2
+# define LDAP_SSL_LDAPS 3
+#endif
+
+/* This is a tree of the current configuration we are building from LDAP */
+struct ldap_config_stack {
+ LDAPMessage * res; /* Pointer returned from ldap_search */
+ LDAPMessage * ldent; /* Current item in LDAP that we're processing.
+ in res */
+ int close_brace; /* Put a closing } after we're through with
+ this item */
+ int processed; /* We set this flag if this base item has been
+ processed. After this base item is processed,
+ we can start processing the children */
+ struct ldap_config_stack *children;
+ struct ldap_config_stack *next;
+};
+#endif
+
+typedef enum {
+ server_startup = 0,
+ server_running = 1,
+ server_shutdown = 2,
+ server_hibernate = 3,
+ server_awaken = 4
+} control_object_state_t;
+
+typedef struct {
+ OMAPI_OBJECT_PREAMBLE;
+ control_object_state_t state;
+} dhcp_control_object_t;
+
+/* Lease states: */
+#define FTS_FREE 1
+#define FTS_ACTIVE 2
+#define FTS_EXPIRED 3
+#define FTS_RELEASED 4
+#define FTS_ABANDONED 5
+#define FTS_RESET 6
+#define FTS_BACKUP 7
+typedef u_int8_t binding_state_t;
+
+/* FTS_LAST is the highest value that is valid for a lease binding state. */
+#define FTS_LAST FTS_BACKUP
+
+/* A dhcp lease declaration structure. */
+struct lease {
+ OMAPI_OBJECT_PREAMBLE;
+ struct lease *next;
+ struct lease *n_uid, *n_hw;
+
+ struct iaddr ip_addr;
+ TIME starts, ends, sort_time;
+ char *client_hostname;
+ struct binding_scope *scope;
+ struct host_decl *host;
+ struct subnet *subnet;
+ struct pool *pool;
+ struct class *billing_class;
+ struct option_chain_head *agent_options;
+
+ struct executable_statement *on_expiry;
+ struct executable_statement *on_commit;
+ struct executable_statement *on_release;
+
+ unsigned char *uid;
+ unsigned short uid_len;
+ unsigned short uid_max;
+ unsigned char uid_buf [7];
+ struct hardware hardware_addr;
+
+ u_int8_t flags;
+# define STATIC_LEASE 1
+# define BOOTP_LEASE 2
+# define RESERVED_LEASE 4
+# define MS_NULL_TERMINATION 8
+# define ON_UPDATE_QUEUE 16
+# define ON_ACK_QUEUE 32
+# define ON_QUEUE (ON_UPDATE_QUEUE | ON_ACK_QUEUE)
+# define UNICAST_BROADCAST_HACK 64
+# define ON_DEFERRED_QUEUE 128
+
+/* Persistent flags are to be preserved on a given lease structure. */
+# define PERSISTENT_FLAGS (ON_ACK_QUEUE | ON_UPDATE_QUEUE)
+/* Ephemeral flags are to be preserved on a given lease (copied etc). */
+# define EPHEMERAL_FLAGS (MS_NULL_TERMINATION | \
+ UNICAST_BROADCAST_HACK | \
+ RESERVED_LEASE | \
+ BOOTP_LEASE)
+
+ /*
+ * The lease's binding state is its current state. The next binding
+ * state is the next state this lease will move into by expiration,
+ * or timers in general. The desired binding state is used on lease
+ * updates; the caller is attempting to move the lease to the desired
+ * binding state (and this may either succeed or fail, so the binding
+ * state must be preserved).
+ *
+ * The 'rewind' binding state is used in failover processing. It
+ * is used for an optimization when out of communications; it allows
+ * the server to "rewind" a lease to the previous state acknowledged
+ * by the peer, and progress forward from that point.
+ */
+ binding_state_t binding_state;
+ binding_state_t next_binding_state;
+ binding_state_t desired_binding_state;
+ binding_state_t rewind_binding_state;
+
+ struct lease_state *state;
+
+ /*
+ * 'tsfp' is more of an 'effective' tsfp. It may be calculated from
+ * stos+mclt for example if it's an expired lease and the server is
+ * in partner-down state. 'atsfp' is zeroed whenever a lease is
+ * updated - and only set when the peer acknowledges it. This
+ * ensures every state change is transmitted.
+ */
+ TIME tstp; /* Time sent to partner. */
+ TIME tsfp; /* Time sent from partner. */
+ TIME atsfp; /* Actual time sent from partner. */
+ TIME cltt; /* Client last transaction time. */
+ u_int32_t last_xid; /* XID we sent in this lease's BNDUPD */
+ struct lease *next_pending;
+
+ /*
+ * A pointer to the state of the ddns update for this lease.
+ * It should be set while the update is in progress and cleared
+ * when the update finishes. It can be used to cancel the
+ * update if we want to do a different update.
+ */
+ struct dhcp_ddns_cb *ddns_cb;
+};
+
+struct lease_state {
+ struct lease_state *next;
+
+ struct interface_info *ip;
+
+ struct packet *packet; /* The incoming packet. */
+
+ TIME offered_expiry;
+
+ struct option_state *options;
+ struct data_string parameter_request_list;
+ int max_message_size;
+ unsigned char expiry[4], renewal[4], rebind[4];
+ struct data_string filename, server_name;
+ int got_requested_address;
+ int got_server_identifier;
+ struct shared_network *shared_network; /* Shared network of interface
+ on which request arrived. */
+
+ u_int32_t xid;
+ u_int16_t secs;
+ u_int16_t bootp_flags;
+ struct in_addr ciaddr;
+ struct in_addr siaddr;
+ struct in_addr giaddr;
+ u_int8_t hops;
+ u_int8_t offer;
+ struct iaddr from;
+};
+
+#define ROOT_GROUP 0
+#define HOST_DECL 1
+#define SHARED_NET_DECL 2
+#define SUBNET_DECL 3
+#define CLASS_DECL 4
+#define GROUP_DECL 5
+#define POOL_DECL 6
+
+/* Possible modes in which discover_interfaces can run. */
+
+#define DISCOVER_RUNNING 0
+#define DISCOVER_SERVER 1
+#define DISCOVER_UNCONFIGURED 2
+#define DISCOVER_RELAY 3
+#define DISCOVER_REQUESTED 4
+
+/* DDNS_UPDATE_STYLE enumerations. */
+#define DDNS_UPDATE_STYLE_NONE 0
+#define DDNS_UPDATE_STYLE_AD_HOC 1
+#define DDNS_UPDATE_STYLE_INTERIM 2
+
+/* Server option names. */
+
+#define SV_DEFAULT_LEASE_TIME 1
+#define SV_MAX_LEASE_TIME 2
+#define SV_MIN_LEASE_TIME 3
+#define SV_BOOTP_LEASE_CUTOFF 4
+#define SV_BOOTP_LEASE_LENGTH 5
+#define SV_BOOT_UNKNOWN_CLIENTS 6
+#define SV_DYNAMIC_BOOTP 7
+#define SV_ALLOW_BOOTP 8
+#define SV_ALLOW_BOOTING 9
+#define SV_ONE_LEASE_PER_CLIENT 10
+#define SV_GET_LEASE_HOSTNAMES 11
+#define SV_USE_HOST_DECL_NAMES 12
+#define SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE 13
+#define SV_MIN_SECS 14
+#define SV_FILENAME 15
+#define SV_SERVER_NAME 16
+#define SV_NEXT_SERVER 17
+#define SV_AUTHORITATIVE 18
+#define SV_VENDOR_OPTION_SPACE 19
+#define SV_ALWAYS_REPLY_RFC1048 20
+#define SV_SITE_OPTION_SPACE 21
+#define SV_ALWAYS_BROADCAST 22
+#define SV_DDNS_DOMAIN_NAME 23
+#define SV_DDNS_HOST_NAME 24
+#define SV_DDNS_REV_DOMAIN_NAME 25
+#define SV_LEASE_FILE_NAME 26
+#define SV_PID_FILE_NAME 27
+#define SV_DUPLICATES 28
+#define SV_DECLINES 29
+#define SV_DDNS_UPDATES 30
+#define SV_OMAPI_PORT 31
+#define SV_LOCAL_PORT 32
+#define SV_LIMITED_BROADCAST_ADDRESS 33
+#define SV_REMOTE_PORT 34
+#define SV_LOCAL_ADDRESS 35
+#define SV_OMAPI_KEY 36
+#define SV_STASH_AGENT_OPTIONS 37
+#define SV_DDNS_TTL 38
+#define SV_DDNS_UPDATE_STYLE 39
+#define SV_CLIENT_UPDATES 40
+#define SV_UPDATE_OPTIMIZATION 41
+#define SV_PING_CHECKS 42
+#define SV_UPDATE_STATIC_LEASES 43
+#define SV_LOG_FACILITY 44
+#define SV_DO_FORWARD_UPDATES 45
+#define SV_PING_TIMEOUT 46
+#define SV_RESERVE_INFINITE 47
+#define SV_DDNS_CONFLICT_DETECT 48
+#define SV_LEASEQUERY 49
+#define SV_ADAPTIVE_LEASE_TIME_THRESHOLD 50
+#define SV_DO_REVERSE_UPDATES 51
+#define SV_FQDN_REPLY 52
+#define SV_PREFER_LIFETIME 53
+#define SV_DHCPV6_LEASE_FILE_NAME 54
+#define SV_DHCPV6_PID_FILE_NAME 55
+#define SV_LIMIT_ADDRS_PER_IA 56
+#define SV_LIMIT_PREFS_PER_IA 57
+#define SV_DELAYED_ACK 58
+#define SV_MAX_ACK_DELAY 59
+#if defined(LDAP_CONFIGURATION)
+# define SV_LDAP_SERVER 60
+# define SV_LDAP_PORT 61
+# define SV_LDAP_USERNAME 62
+# define SV_LDAP_PASSWORD 63
+# define SV_LDAP_BASE_DN 64
+# define SV_LDAP_METHOD 65
+# define SV_LDAP_DEBUG_FILE 66
+# define SV_LDAP_DHCP_SERVER_CN 67
+# define SV_LDAP_REFERRALS 68
+#if defined (LDAP_USE_SSL)
+# define SV_LDAP_SSL 69
+# define SV_LDAP_TLS_REQCERT 70
+# define SV_LDAP_TLS_CA_FILE 71
+# define SV_LDAP_TLS_CA_DIR 72
+# define SV_LDAP_TLS_CERT 73
+# define SV_LDAP_TLS_KEY 74
+# define SV_LDAP_TLS_CRLCHECK 75
+# define SV_LDAP_TLS_CIPHERS 76
+# define SV_LDAP_TLS_RANDFILE 77
+#endif
+#endif
+
+#if !defined (DEFAULT_PING_TIMEOUT)
+# define DEFAULT_PING_TIMEOUT 1
+#endif
+
+#if !defined (DEFAULT_DELAYED_ACK)
+# define DEFAULT_DELAYED_ACK 28 /* default SO_SNDBUF size / 576 bytes */
+#endif
+
+#if !defined (DEFAULT_ACK_DELAY_SECS)
+# define DEFAULT_ACK_DELAY_SECS 0
+#endif
+
+#if !defined (DEFAULT_ACK_DELAY_USECS)
+# define DEFAULT_ACK_DELAY_USECS 250000 /* 1/4 of a second */
+#endif
+
+#if !defined (DEFAULT_MIN_ACK_DELAY_USECS)
+# define DEFAULT_MIN_ACK_DELAY_USECS 10000 /* 1/100 second */
+#endif
+
+#if defined(LDAP_CONFIGURATION)
+# define SV_LDAP_SERVER 60
+# define SV_LDAP_PORT 61
+# define SV_LDAP_USERNAME 62
+# define SV_LDAP_PASSWORD 63
+# define SV_LDAP_BASE_DN 64
+# define SV_LDAP_METHOD 65
+# define SV_LDAP_DEBUG_FILE 66
+# define SV_LDAP_DHCP_SERVER_CN 67
+# define SV_LDAP_REFERRALS 68
+#if defined (LDAP_USE_SSL)
+# define SV_LDAP_SSL 69
+# define SV_LDAP_TLS_REQCERT 70
+# define SV_LDAP_TLS_CA_FILE 71
+# define SV_LDAP_TLS_CA_DIR 72
+# define SV_LDAP_TLS_CERT 73
+# define SV_LDAP_TLS_KEY 74
+# define SV_LDAP_TLS_CRLCHECK 75
+# define SV_LDAP_TLS_CIPHERS 76
+# define SV_LDAP_TLS_RANDFILE 77
+#endif
+#endif
+
+#if !defined (DEFAULT_DEFAULT_LEASE_TIME)
+# define DEFAULT_DEFAULT_LEASE_TIME 43200
+#endif
+
+#if !defined (DEFAULT_MIN_LEASE_TIME)
+# define DEFAULT_MIN_LEASE_TIME 300
+#endif
+
+#if !defined (DEFAULT_MAX_LEASE_TIME)
+# define DEFAULT_MAX_LEASE_TIME 86400
+#endif
+
+#if !defined (DEFAULT_DDNS_TTL)
+# define DEFAULT_DDNS_TTL 3600
+#endif
+#if !defined (MAX_DEFAULT_DDNS_TTL)
+# define MAX_DEFAULT_DDNS_TTL 3600
+#endif
+
+#if !defined (MIN_LEASE_WRITE)
+# define MIN_LEASE_WRITE 15
+#endif
+
+/* Client option names */
+
+#define CL_TIMEOUT 1
+#define CL_SELECT_INTERVAL 2
+#define CL_REBOOT_TIMEOUT 3
+#define CL_RETRY_INTERVAL 4
+#define CL_BACKOFF_CUTOFF 5
+#define CL_INITIAL_INTERVAL 6
+#define CL_BOOTP_POLICY 7
+#define CL_SCRIPT_NAME 8
+#define CL_REQUESTED_OPTIONS 9
+#define CL_REQUESTED_LEASE_TIME 10
+#define CL_SEND_OPTIONS 11
+#define CL_MEDIA 12
+#define CL_REJECT_LIST 13
+
+#ifndef CL_DEFAULT_TIMEOUT
+# define CL_DEFAULT_TIMEOUT 60
+#endif
+
+#ifndef CL_DEFAULT_SELECT_INTERVAL
+# define CL_DEFAULT_SELECT_INTERVAL 0
+#endif
+
+#ifndef CL_DEFAULT_REBOOT_TIMEOUT
+# define CL_DEFAULT_REBOOT_TIMEOUT 10
+#endif
+
+#ifndef CL_DEFAULT_RETRY_INTERVAL
+# define CL_DEFAULT_RETRY_INTERVAL 300
+#endif
+
+#ifndef CL_DEFAULT_BACKOFF_CUTOFF
+# define CL_DEFAULT_BACKOFF_CUTOFF 120
+#endif
+
+#ifndef CL_DEFAULT_INITIAL_INTERVAL
+# define CL_DEFAULT_INITIAL_INTERVAL 10
+#endif
+
+#ifndef CL_DEFAULT_BOOTP_POLICY
+# define CL_DEFAULT_BOOTP_POLICY P_ACCEPT
+#endif
+
+#ifndef CL_DEFAULT_REQUESTED_OPTIONS
+# define CL_DEFAULT_REQUESTED_OPTIONS \
+ { DHO_SUBNET_MASK, \
+ DHO_BROADCAST_ADDRESS, \
+ DHO_TIME_OFFSET, \
+ DHO_ROUTERS, \
+ DHO_DOMAIN_NAME, \
+ DHO_DOMAIN_NAME_SERVERS, \
+ DHO_HOST_NAME }
+#endif
+
+struct group_object {
+ OMAPI_OBJECT_PREAMBLE;
+
+ struct group_object *n_dynamic;
+ struct group *group;
+ char *name;
+ int flags;
+#define GROUP_OBJECT_DELETED 1
+#define GROUP_OBJECT_DYNAMIC 2
+#define GROUP_OBJECT_STATIC 4
+};
+
+/* Group of declarations that share common parameters. */
+struct group {
+ struct group *next;
+
+ int refcnt;
+ struct group_object *object;
+ struct subnet *subnet;
+ struct shared_network *shared_network;
+ int authoritative;
+ struct executable_statement *statements;
+};
+
+/* A dhcp host declaration structure. */
+struct host_decl {
+ OMAPI_OBJECT_PREAMBLE;
+ struct host_decl *n_ipaddr;
+ struct host_decl *n_dynamic;
+ char *name;
+ struct hardware interface;
+ struct data_string client_identifier;
+ struct option *host_id_option;
+ struct data_string host_id;
+ /* XXXSK: fixed_addr should be an array of iaddr values,
+ not an option_cache, but it's referenced in a lot of
+ places, so we'll leave it for now. */
+ struct option_cache *fixed_addr;
+ struct iaddrcidrnetlist *fixed_prefix;
+ struct group *group;
+ struct group_object *named_group;
+ struct data_string auth_key_id;
+ int flags;
+#define HOST_DECL_DELETED 1
+#define HOST_DECL_DYNAMIC 2
+#define HOST_DECL_STATIC 4
+};
+
+struct permit {
+ struct permit *next;
+ enum {
+ permit_unknown_clients,
+ permit_known_clients,
+ permit_authenticated_clients,
+ permit_unauthenticated_clients,
+ permit_all_clients,
+ permit_dynamic_bootp_clients,
+ permit_class,
+ permit_after
+ } type;
+ struct class *class;
+ TIME after; /* date after which this clause applies */
+};
+
+struct pool {
+ OMAPI_OBJECT_PREAMBLE;
+ struct pool *next;
+ struct group *group;
+ struct shared_network *shared_network;
+ struct permit *permit_list;
+ struct permit *prohibit_list;
+ struct lease *active;
+ struct lease *expired;
+ struct lease *free;
+ struct lease *backup;
+ struct lease *abandoned;
+ struct lease *reserved;
+ TIME next_event_time;
+ int lease_count;
+ int free_leases;
+ int backup_leases;
+ int index;
+ TIME valid_from; /* deny pool use before this date */
+ TIME valid_until; /* deny pool use after this date */
+
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *failover_peer;
+#endif
+};
+
+struct shared_network {
+ OMAPI_OBJECT_PREAMBLE;
+ struct shared_network *next;
+ char *name;
+
+#define SHARED_IMPLICIT 1 /* This network was synthesized. */
+ int flags;
+
+ struct subnet *subnets;
+ struct interface_info *interface;
+ struct pool *pools;
+ struct ipv6_pool **ipv6_pools; /* NULL-terminated array */
+ int last_ipv6_pool; /* offset of last IPv6 pool
+ used to issue a lease */
+ struct group *group;
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *failover_peer;
+#endif
+};
+
+struct subnet {
+ OMAPI_OBJECT_PREAMBLE;
+ struct subnet *next_subnet;
+ struct subnet *next_sibling;
+ struct shared_network *shared_network;
+ struct interface_info *interface;
+ struct iaddr interface_address;
+ struct iaddr net;
+ struct iaddr netmask;
+ int prefix_len; /* XXX: currently for IPv6 only */
+ struct group *group;
+};
+
+struct collection {
+ struct collection *next;
+
+ const char *name;
+ struct class *classes;
+};
+
+/* Used as an argument to parse_clasS_decl() */
+#define CLASS_TYPE_VENDOR 0
+#define CLASS_TYPE_USER 1
+#define CLASS_TYPE_CLASS 2
+#define CLASS_TYPE_SUBCLASS 3
+
+/* XXX classes must be reference-counted. */
+struct class {
+ OMAPI_OBJECT_PREAMBLE;
+ struct class *nic; /* Next in collection. */
+ struct class *superclass; /* Set for spawned classes only. */
+ char *name; /* Not set for spawned classes. */
+
+ /* A class may be configured to permit a limited number of leases. */
+ int lease_limit;
+ int leases_consumed;
+ struct lease **billed_leases;
+
+ /* If nonzero, class has not been saved since it was last
+ modified. */
+ int dirty;
+
+ /* Hash table containing subclasses. */
+ class_hash_t *hash;
+ struct data_string hash_string;
+
+ /* Expression used to match class. */
+ struct expression *expr;
+
+ /* Expression used to compute subclass identifiers for spawning
+ and to do subclass matching. */
+ struct expression *submatch;
+ int spawning;
+
+ struct group *group;
+
+ /* Statements to execute if class matches. */
+ struct executable_statement *statements;
+
+#define CLASS_DECL_DELETED 1
+#define CLASS_DECL_DYNAMIC 2
+#define CLASS_DECL_STATIC 4
+#define CLASS_DECL_SUBCLASS 8
+
+ int flags;
+};
+
+/* DHCP client lease structure... */
+struct client_lease {
+ struct client_lease *next; /* Next lease in list. */
+ TIME expiry, renewal, rebind; /* Lease timeouts. */
+ struct iaddr address; /* Address being leased. */
+ char *server_name; /* Name of boot server. */
+ char *filename; /* Name of file we're supposed to boot. */
+ struct string_list *medium; /* Network medium. */
+ struct auth_key *key; /* Key used in basic DHCP authentication. */
+
+ unsigned int is_static : 1; /* If set, lease is from config file. */
+ unsigned int is_bootp: 1; /* If set, lease was acquired with BOOTP. */
+
+ struct option_state *options; /* Options supplied with lease. */
+};
+
+/* DHCPv6 lease structures */
+struct dhc6_addr {
+ struct dhc6_addr *next;
+ struct iaddr address;
+ u_int8_t plen;
+
+ /* Address state flags. */
+ #define DHC6_ADDR_DEPREFFED 0x01
+ #define DHC6_ADDR_EXPIRED 0x02
+ u_int8_t flags;
+
+ TIME starts;
+ u_int32_t preferred_life;
+ u_int32_t max_life;
+
+ struct option_state *options;
+};
+
+struct dhc6_ia {
+ struct dhc6_ia *next;
+ unsigned char iaid[4];
+ u_int16_t ia_type;
+
+ TIME starts;
+ u_int32_t renew;
+ u_int32_t rebind;
+ struct dhc6_addr *addrs;
+
+ struct option_state *options;
+};
+
+struct dhc6_lease {
+ struct dhc6_lease *next;
+ struct data_string server_id;
+
+ isc_boolean_t released;
+ int score;
+ u_int8_t pref;
+
+ unsigned char dhcpv6_transaction_id[3];
+ struct dhc6_ia *bindings;
+
+ struct option_state *options;
+};
+
+/* Possible states in which the client can be. */
+enum dhcp_state {
+ S_REBOOTING = 1,
+ S_INIT = 2,
+ S_SELECTING = 3,
+ S_REQUESTING = 4,
+ S_BOUND = 5,
+ S_RENEWING = 6,
+ S_REBINDING = 7,
+ S_STOPPED = 8
+};
+
+/* Authentication and BOOTP policy possibilities (not all values work
+ for each). */
+enum policy { P_IGNORE, P_ACCEPT, P_PREFER, P_REQUIRE, P_DONT };
+
+/* Configuration information from the config file... */
+struct client_config {
+ /*
+ * When a message has been received, run these statements
+ * over it.
+ */
+ struct group *on_receipt;
+
+ /*
+ * When a message is sent, run these statements.
+ */
+ struct group *on_transmission;
+
+ struct option **required_options; /* Options that MUST be present. */
+ struct option **requested_options; /* Options to request (ORO/PRL). */
+
+ TIME timeout; /* Start to panic if we don't get a
+ lease in this time period when
+ SELECTING. */
+ TIME initial_delay; /* Set initial delay before first
+ transmission. */
+ TIME initial_interval; /* All exponential backoff intervals
+ start here. */
+ TIME retry_interval; /* If the protocol failed to produce
+ an address before the timeout,
+ try the protocol again after this
+ many seconds. */
+ TIME select_interval; /* Wait this many seconds from the
+ first DHCPDISCOVER before
+ picking an offered lease. */
+ TIME reboot_timeout; /* When in INIT-REBOOT, wait this
+ long before giving up and going
+ to INIT. */
+ TIME backoff_cutoff; /* When doing exponential backoff,
+ never back off to an interval
+ longer than this amount. */
+ u_int32_t requested_lease; /* Requested lease time, if user
+ doesn't configure one. */
+ struct string_list *media; /* Possible network media values. */
+ char *script_name; /* Name of config script. */
+ char *vendor_space_name; /* Name of config script. */
+ enum policy bootp_policy;
+ /* Ignore, accept or prefer BOOTP
+ responses. */
+ enum policy auth_policy;
+ /* Require authentication, prefer
+ authentication, or don't try to
+ authenticate. */
+ struct string_list *medium; /* Current network medium. */
+
+ struct iaddrmatchlist *reject_list; /* Servers to reject. */
+
+ int omapi_port; /* port on which to accept OMAPI
+ connections, or -1 for no
+ listener. */
+ int do_forward_update; /* If nonzero, and if we have the
+ information we need, update the
+ A record for the address we get. */
+};
+
+/* Per-interface state used in the dhcp client... */
+/* XXX: consider union {}'ing this for v4/v6. */
+struct client_state {
+ struct client_state *next;
+ struct interface_info *interface;
+ char *name;
+
+ /* Common values. */
+ struct client_config *config; /* Client configuration. */
+ struct string_list *env; /* Client script environment. */
+ int envc; /* Number of entries in environment. */
+ struct option_state *sent_options; /* Options we sent. */
+ enum dhcp_state state; /* Current state for this interface. */
+ TIME last_write; /* Last time this state was written. */
+
+ /* DHCPv4 values. */
+ struct client_lease *active; /* Currently active lease. */
+ struct client_lease *new; /* New lease. */
+ struct client_lease *offered_leases; /* Leases offered to us. */
+ struct client_lease *leases; /* Leases we currently hold. */
+ struct client_lease *alias; /* Alias lease. */
+
+ struct iaddr destination; /* Where to send packet. */
+ u_int32_t xid; /* Transaction ID. */
+ u_int16_t secs; /* secs value from DHCPDISCOVER. */
+ TIME first_sending; /* When was first copy sent? */
+ TIME interval; /* What's the current resend interval? */
+ struct string_list *medium; /* Last media type tried. */
+ struct dhcp_packet packet; /* Outgoing DHCP packet. */
+ unsigned packet_length; /* Actual length of generated packet. */
+
+ struct iaddr requested_address; /* Address we would like to get. */
+
+ /* DHCPv6 values. */
+ unsigned char dhcpv6_transaction_id[3];
+ u_int8_t refresh_type;
+
+ struct dhc6_lease *active_lease;
+ struct dhc6_lease *old_lease;
+ struct dhc6_lease *advertised_leases;
+ struct dhc6_lease *selected_lease;
+ struct dhc6_lease *held_leases;
+
+ struct timeval start_time;
+ u_int16_t elapsed;
+ int txcount;
+
+ /* See RFC3315 section 14. */
+ TIME RT; /* In hundredths of seconds. */
+ TIME IRT; /* In hundredths of seconds. */
+ TIME MRC; /* Count. */
+ TIME MRT; /* In hundredths of seconds. */
+ TIME MRD; /* In seconds, relative. */
+ TIME next_MRD; /* In seconds, absolute. */
+
+ /* Rather than a state, we use a function that shifts around
+ * depending what stage of life the v6 state machine is in.
+ * This is where incoming packets are dispatched to (sometimes
+ * a no-op).
+ */
+ void (*v6_handler)(struct packet *, struct client_state *);
+
+ /*
+ * A pointer to the state of the ddns update for this lease.
+ * It should be set while the update is in progress and cleared
+ * when the update finishes. It can be used to cancel the
+ * update if we want to do a different update.
+ */
+ struct dhcp_ddns_cb *ddns_cb;
+};
+
+struct envadd_state {
+ struct client_state *client;
+ const char *prefix;
+};
+
+struct dns_update_state {
+ struct client_state *client;
+ struct iaddr address;
+ int dns_update_timeout;
+};
+
+/* Information about each network interface. */
+
+struct interface_info {
+ OMAPI_OBJECT_PREAMBLE;
+ struct interface_info *next; /* Next interface in list... */
+ struct shared_network *shared_network;
+ /* Networks connected to this interface. */
+ struct hardware hw_address; /* Its physical address. */
+ struct in_addr *addresses; /* Addresses associated with this
+ * interface.
+ */
+ int address_count; /* Number of addresses stored. */
+ int address_max; /* Size of addresses buffer. */
+ struct in6_addr *v6addresses; /* IPv6 addresses associated with
+ this interface. */
+ int v6address_count; /* Number of IPv6 addresses associated
+ with this interface. */
+ int v6address_max; /* Maximum number of IPv6 addresses
+ we can store in current buffer. */
+
+ u_int8_t *circuit_id; /* Circuit ID associated with this
+ interface. */
+ unsigned circuit_id_len; /* Length of Circuit ID, if there
+ is one. */
+ u_int8_t *remote_id; /* Remote ID associated with this
+ interface (if any). */
+ unsigned remote_id_len; /* Length of Remote ID. */
+
+ char name [IFNAMSIZ]; /* Its name... */
+ int index; /* Its if_nametoindex(). */
+ int rfdesc; /* Its read file descriptor. */
+ int wfdesc; /* Its write file descriptor, if
+ different. */
+ unsigned char *rbuf; /* Read buffer, if required. */
+ unsigned int rbuf_max; /* Size of read buffer. */
+ size_t rbuf_offset; /* Current offset into buffer. */
+ size_t rbuf_len; /* Length of data in buffer. */
+
+ struct ifreq *ifp; /* Pointer to ifreq struct. */
+ int configured; /* If set to 1, interface has at least
+ * one valid IP address.
+ */
+ u_int32_t flags; /* Control flags... */
+#define INTERFACE_REQUESTED 1
+#define INTERFACE_AUTOMATIC 2
+#define INTERFACE_RUNNING 4
+#define INTERFACE_DOWNSTREAM 8
+#define INTERFACE_UPSTREAM 16
+#define INTERFACE_STREAMS (INTERFACE_DOWNSTREAM | INTERFACE_UPSTREAM)
+
+ /* Only used by DHCP client code. */
+ struct client_state *client;
+# if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE) || \
+ defined(USE_DLPI_HWADDR)
+ int dlpi_sap_length;
+ struct hardware dlpi_broadcast_addr;
+# endif /* DLPI_SEND || DLPI_RECEIVE */
+ struct hardware anycast_mac_addr;
+};
+
+struct hardware_link {
+ struct hardware_link *next;
+ char name [IFNAMSIZ];
+ struct hardware address;
+};
+
+struct leasequeue {
+ struct leasequeue *prev;
+ struct leasequeue *next;
+ struct lease *lease;
+};
+
+typedef void (*tvref_t)(void *, void *, const char *, int);
+typedef void (*tvunref_t)(void *, const char *, int);
+struct timeout {
+ struct timeout *next;
+ struct timeval when;
+ void (*func) (void *);
+ void *what;
+ tvref_t ref;
+ tvunref_t unref;
+ isc_timer_t *isc_timeout;
+};
+
+struct eventqueue {
+ struct eventqueue *next;
+ void (*handler)(void *);
+};
+
+struct protocol {
+ struct protocol *next;
+ int fd;
+ void (*handler) (struct protocol *);
+ void *local;
+};
+
+struct dns_query; /* forward */
+
+struct dns_wakeup {
+ struct dns_wakeup *next; /* Next wakeup in chain. */
+ void (*func) (struct dns_query *);
+};
+
+struct dns_question {
+ u_int16_t type; /* Type of query. */
+ u_int16_t class; /* Class of query. */
+ unsigned char data [1]; /* Query data. */
+};
+
+struct dns_answer {
+ u_int16_t type; /* Type of answer. */
+ u_int16_t class; /* Class of answer. */
+ int count; /* Number of answers. */
+ unsigned char *answers[1]; /* Pointers to answers. */
+};
+
+struct dns_query {
+ struct dns_query *next; /* Next query in hash bucket. */
+ u_int32_t hash; /* Hash bucket index. */
+ TIME expiry; /* Query expiry time (zero if not yet
+ answered. */
+ u_int16_t id; /* Query ID (also hash table index) */
+ caddr_t waiters; /* Pointer to list of things waiting
+ on this query. */
+
+ struct dns_question *question; /* Question, internal format. */
+ struct dns_answer *answer; /* Answer, internal format. */
+
+ unsigned char *query; /* Query formatted for DNS server. */
+ unsigned len; /* Length of entire query. */
+ int sent; /* The query has been sent. */
+ struct dns_wakeup *wakeups; /* Wakeups to call if this query is
+ answered. */
+ struct name_server *next_server; /* Next server to try. */
+ int backoff; /* Current backoff, in seconds. */
+};
+
+struct dns_zone {
+ int refcnt;
+ TIME timeout;
+ char *name;
+ struct option_cache *primary;
+ struct option_cache *secondary;
+ struct auth_key *key;
+};
+
+struct icmp_state {
+ OMAPI_OBJECT_PREAMBLE;
+ int socket;
+ void (*icmp_handler) (struct iaddr, u_int8_t *, int);
+};
+
+#include "ctrace.h"
+
+/* Bitmask of dhcp option codes. */
+typedef unsigned char option_mask [16];
+
+/* DHCP Option mask manipulation macros... */
+#define OPTION_ZERO(mask) (memset (mask, 0, 16))
+#define OPTION_SET(mask, bit) (mask [bit >> 8] |= (1 << (bit & 7)))
+#define OPTION_CLR(mask, bit) (mask [bit >> 8] &= ~(1 << (bit & 7)))
+#define OPTION_ISSET(mask, bit) (mask [bit >> 8] & (1 << (bit & 7)))
+#define OPTION_ISCLR(mask, bit) (!OPTION_ISSET (mask, bit))
+
+/* An option occupies its length plus two header bytes (code and
+ length) for every 255 bytes that must be stored. */
+#define OPTION_SPACE(x) ((x) + 2 * ((x) / 255 + 1))
+
+/* Default path to dhcpd config file. */
+#ifdef DEBUG
+#undef _PATH_DHCPD_CONF
+#define _PATH_DHCPD_CONF "dhcpd.conf"
+#undef _PATH_DHCPD_DB
+#define _PATH_DHCPD_DB "dhcpd.leases"
+#undef _PATH_DHCPD6_DB
+#define _PATH_DHCPD6_DB "dhcpd6.leases"
+#undef _PATH_DHCPD_PID
+#define _PATH_DHCPD_PID "dhcpd.pid"
+#undef _PATH_DHCPD6_PID
+#define _PATH_DHCPD6_PID "dhcpd6.pid"
+#else /* !DEBUG */
+
+#ifndef _PATH_DHCPD_CONF
+#define _PATH_DHCPD_CONF "/etc/dhcpd.conf"
+#endif /* DEBUG */
+
+#ifndef _PATH_DHCPD_DB
+#define _PATH_DHCPD_DB LOCALSTATEDIR"/db/dhcpd.leases"
+#endif
+
+#ifndef _PATH_DHCPD6_DB
+#define _PATH_DHCPD6_DB LOCALSTATEDIR"/db/dhcpd6.leases"
+#endif
+
+#ifndef _PATH_DHCPD_PID
+#define _PATH_DHCPD_PID LOCALSTATEDIR"/run/dhcpd.pid"
+#endif
+
+#ifndef _PATH_DHCPD6_PID
+#define _PATH_DHCPD6_PID LOCALSTATEDIR"/run/dhcpd6.pid"
+#endif
+
+#endif /* DEBUG */
+
+#ifndef _PATH_DHCLIENT_CONF
+#define _PATH_DHCLIENT_CONF "/etc/dhclient.conf"
+#endif
+
+#ifndef _PATH_DHCLIENT_SCRIPT
+#define _PATH_DHCLIENT_SCRIPT "/sbin/dhclient-script"
+#endif
+
+#ifndef _PATH_DHCLIENT_PID
+#define _PATH_DHCLIENT_PID LOCALSTATEDIR"/run/dhclient.pid"
+#endif
+
+#ifndef _PATH_DHCLIENT6_PID
+#define _PATH_DHCLIENT6_PID LOCALSTATEDIR"/run/dhclient6.pid"
+#endif
+
+#ifndef _PATH_DHCLIENT_DB
+#define _PATH_DHCLIENT_DB LOCALSTATEDIR"/db/dhclient.leases"
+#endif
+
+#ifndef _PATH_DHCLIENT6_DB
+#define _PATH_DHCLIENT6_DB LOCALSTATEDIR"/db/dhclient6.leases"
+#endif
+
+#ifndef _PATH_RESOLV_CONF
+#define _PATH_RESOLV_CONF "/etc/resolv.conf"
+#endif
+
+#ifndef _PATH_DHCRELAY_PID
+#define _PATH_DHCRELAY_PID LOCALSTATEDIR"/run/dhcrelay.pid"
+#endif
+
+#ifndef _PATH_DHCRELAY6_PID
+#define _PATH_DHCRELAY6_PID LOCALSTATEDIR"/run/dhcrelay6.pid"
+#endif
+
+#ifndef DHCPD_LOG_FACILITY
+#define DHCPD_LOG_FACILITY LOG_DAEMON
+#endif
+
+#define MAX_TIME 0x7fffffff
+#define MIN_TIME 0
+
+ /* these are referenced */
+typedef struct hash_table ia_hash_t;
+typedef struct hash_table iasubopt_hash_t;
+
+ /* IAADDR/IAPREFIX lease */
+
+struct iasubopt {
+ int refcnt; /* reference count */
+ struct in6_addr addr; /* IPv6 address/prefix */
+ u_int8_t plen; /* iaprefix prefix length */
+ binding_state_t state; /* state */
+ struct binding_scope *scope; /* "set var = value;" */
+ time_t hard_lifetime_end_time; /* time address expires */
+ time_t soft_lifetime_end_time; /* time ephemeral expires */
+ u_int32_t prefer; /* cached preferred lifetime */
+ u_int32_t valid; /* cached valid lifetime */
+ struct ia_xx *ia; /* IA for this lease */
+ struct ipv6_pool *ipv6_pool; /* pool for this lease */
+/*
+ * For now, just pick an arbitrary time to keep old hard leases
+ * around (value in seconds).
+ */
+#define EXPIRED_IPV6_CLEANUP_TIME (60*60)
+
+ int heap_index; /* index into heap, or -1
+ (internal use only) */
+
+ /*
+ * A pointer to the state of the ddns update for this lease.
+ * It should be set while the update is in progress and cleared
+ * when the update finishes. It can be used to cancel the
+ * update if we want to do a different update.
+ */
+ struct dhcp_ddns_cb *ddns_cb;
+
+};
+
+struct ia_xx {
+ int refcnt; /* reference count */
+ struct data_string iaid_duid; /* from the client */
+ u_int16_t ia_type; /* IA_XX */
+ int num_iasubopt; /* number of IAADDR/PREFIX */
+ int max_iasubopt; /* space available for IAADDR/PREFIX */
+ time_t cltt; /* client last transaction time */
+ struct iasubopt **iasubopt; /* pointers to the IAADDR/IAPREFIXs */
+};
+
+extern ia_hash_t *ia_na_active;
+extern ia_hash_t *ia_ta_active;
+extern ia_hash_t *ia_pd_active;
+
+struct ipv6_pool {
+ int refcnt; /* reference count */
+ u_int16_t pool_type; /* IA_xx */
+ struct in6_addr start_addr; /* first IPv6 address */
+ int bits; /* number of bits, CIDR style */
+ int units; /* allocation unit in bits */
+ iasubopt_hash_t *leases; /* non-free leases */
+ int num_active; /* count of active leases */
+ isc_heap_t *active_timeouts; /* timeouts for active leases */
+ int num_inactive; /* count of inactive leases */
+ isc_heap_t *inactive_timeouts; /* timeouts for expired or
+ released leases */
+ struct shared_network *shared_network; /* shared_network for
+ this pool */
+ struct subnet *subnet; /* subnet for this pool */
+};
+
+/* Flags and state for dhcp_ddns_cb_t */
+#define DDNS_UPDATE_ADDR 0x01
+#define DDNS_UPDATE_PTR 0x02
+#define DDNS_INCLUDE_RRSET 0x04
+#define DDNS_CONFLICT_OVERRIDE 0x08
+#define DDNS_CLIENT_DID_UPDATE 0x10
+#define DDNS_EXECUTE_NEXT 0x20
+#define DDNS_ABORT 0x40
+
+/*
+ * The following two groups are separate and we could reuse
+ * values but not reusing them may be useful in the future.
+ */
+#define DDNS_STATE_CLEANUP 0 // The previous step failed, cleanup
+
+#define DDNS_STATE_ADD_FW_NXDOMAIN 1
+#define DDNS_STATE_ADD_FW_YXDHCID 2
+#define DDNS_STATE_ADD_PTR 3
+
+#define DDNS_STATE_REM_FW_YXDHCID 17
+#define DDNS_STATE_REM_FW_NXRR 18
+#define DDNS_STATE_REM_PTR 19
+
+/*
+ * Flags for the dns print function
+ */
+#define DDNS_PRINT_INBOUND 1
+#define DDNS_PRINT_OUTBOUND 0
+
+struct dhcp_ddns_cb;
+
+typedef void (*ddns_action_t)(struct dhcp_ddns_cb *ddns_cb,
+ isc_result_t result);
+
+typedef struct dhcp_ddns_cb {
+ struct data_string fwd_name;
+ struct data_string rev_name;
+ struct data_string dhcid;
+ struct iaddr address;
+ int address_type;
+
+ unsigned long ttl;
+
+ unsigned char zone_name[DHCP_MAXDNS_WIRE];
+ isc_sockaddrlist_t zone_server_list;
+ isc_sockaddr_t zone_addrs[DHCP_MAXNS];
+ int zone_addr_count;
+ struct dns_zone *zone;
+
+ int flags;
+ TIME timeout;
+ int state;
+ ddns_action_t cur_func;
+
+ struct dhcp_ddns_cb * next_op;
+
+ /* Lease or client state that triggered the ddns operation */
+ void *lease;
+ struct binding_scope **scope;
+
+ void *transaction;
+ void *dataspace;
+} dhcp_ddns_cb_t;
+
+extern struct ipv6_pool **pools;
+extern int num_pools;
+
+/* External definitions... */
+
+HASH_FUNCTIONS_DECL (group, const char *, struct group_object, group_hash_t)
+HASH_FUNCTIONS_DECL (universe, const char *, struct universe, universe_hash_t)
+HASH_FUNCTIONS_DECL (option_name, const char *, struct option,
+ option_name_hash_t)
+HASH_FUNCTIONS_DECL (option_code, const unsigned *, struct option,
+ option_code_hash_t)
+HASH_FUNCTIONS_DECL (dns_zone, const char *, struct dns_zone, dns_zone_hash_t)
+HASH_FUNCTIONS_DECL(lease_ip, const unsigned char *, struct lease,
+ lease_ip_hash_t)
+HASH_FUNCTIONS_DECL(lease_id, const unsigned char *, struct lease,
+ lease_id_hash_t)
+HASH_FUNCTIONS_DECL (host, const unsigned char *, struct host_decl, host_hash_t)
+HASH_FUNCTIONS_DECL (class, const char *, struct class, class_hash_t)
+
+/* options.c */
+
+extern struct option *vendor_cfg_option;
+int parse_options (struct packet *);
+int parse_option_buffer (struct option_state *, const unsigned char *,
+ unsigned, struct universe *);
+struct universe *find_option_universe (struct option *, const char *);
+int parse_encapsulated_suboptions (struct option_state *, struct option *,
+ const unsigned char *, unsigned,
+ struct universe *, const char *);
+int cons_options (struct packet *, struct dhcp_packet *, struct lease *,
+ struct client_state *,
+ int, struct option_state *, struct option_state *,
+ struct binding_scope **,
+ int, int, int, struct data_string *, const char *);
+int fqdn_universe_decode (struct option_state *,
+ const unsigned char *, unsigned, struct universe *);
+struct option_cache *
+lookup_fqdn6_option(struct universe *universe, struct option_state *options,
+ unsigned code);
+void
+save_fqdn6_option(struct universe *universe, struct option_state *options,
+ struct option_cache *oc, isc_boolean_t appendp);
+void
+delete_fqdn6_option(struct universe *universe, struct option_state *options,
+ int code);
+void
+fqdn6_option_space_foreach(struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *stuff,
+ void (*func)(struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *));
+int
+fqdn6_option_space_encapsulate(struct data_string *result,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *universe);
+int
+fqdn6_universe_decode(struct option_state *options,
+ const unsigned char *buffer, unsigned length,
+ struct universe *u);
+int append_option(struct data_string *dst, struct universe *universe,
+ struct option *option, struct data_string *src);
+int
+store_options(int *ocount,
+ unsigned char *buffer, unsigned buflen, unsigned index,
+ struct packet *packet, struct lease *lease,
+ struct client_state *client_state,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ unsigned *priority_list, int priority_len,
+ unsigned first_cutoff, int second_cutoff, int terminate,
+ const char *vuname);
+int store_options6(char *, int, struct option_state *, struct packet *,
+ const int *, struct data_string *);
+int format_has_text(const char *);
+int format_min_length(const char *, struct option_cache *);
+const char *pretty_print_option (struct option *, const unsigned char *,
+ unsigned, int, int);
+int pretty_escape(char **, char *, const unsigned char **,
+ const unsigned char *);
+int get_option (struct data_string *, struct universe *,
+ struct packet *, struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct option_state *, struct binding_scope **, unsigned,
+ const char *, int);
+void set_option (struct universe *, struct option_state *,
+ struct option_cache *, enum statement_op);
+struct option_cache *lookup_option (struct universe *,
+ struct option_state *, unsigned);
+struct option_cache *lookup_hashed_option (struct universe *,
+ struct option_state *,
+ unsigned);
+struct option_cache *next_hashed_option(struct universe *,
+ struct option_state *,
+ struct option_cache *);
+int save_option_buffer (struct universe *, struct option_state *,
+ struct buffer *, unsigned char *, unsigned,
+ unsigned, int);
+int append_option_buffer(struct universe *, struct option_state *,
+ struct buffer *, unsigned char *, unsigned,
+ unsigned, int);
+void build_server_oro(struct data_string *, struct option_state *,
+ const char *, int);
+void save_option(struct universe *, struct option_state *,
+ struct option_cache *);
+void also_save_option(struct universe *, struct option_state *,
+ struct option_cache *);
+void save_hashed_option(struct universe *, struct option_state *,
+ struct option_cache *, isc_boolean_t appendp);
+void delete_option (struct universe *, struct option_state *, int);
+void delete_hashed_option (struct universe *,
+ struct option_state *, int);
+int option_cache_dereference (struct option_cache **,
+ const char *, int);
+int hashed_option_state_dereference (struct universe *,
+ struct option_state *,
+ const char *, int);
+int store_option (struct data_string *,
+ struct universe *, struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct option_cache *);
+int option_space_encapsulate (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct data_string *);
+int hashed_option_space_encapsulate (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *);
+int nwip_option_space_encapsulate (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *);
+int fqdn_option_space_encapsulate (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *);
+void suboption_foreach (struct packet *, struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct universe *, void *,
+ void (*) (struct option_cache *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *),
+ struct option_cache *, const char *);
+void option_space_foreach (struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *,
+ void (*) (struct option_cache *,
+ struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *));
+void hashed_option_space_foreach (struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *,
+ void (*) (struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *));
+int linked_option_get (struct data_string *, struct universe *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *, struct option_state *,
+ struct option_state *, struct binding_scope **,
+ unsigned);
+int linked_option_state_dereference (struct universe *,
+ struct option_state *,
+ const char *, int);
+void save_linked_option(struct universe *, struct option_state *,
+ struct option_cache *, isc_boolean_t appendp);
+void linked_option_space_foreach (struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *,
+ void (*) (struct option_cache *,
+ struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *));
+int linked_option_space_encapsulate (struct data_string *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *);
+void delete_linked_option (struct universe *, struct option_state *, int);
+struct option_cache *lookup_linked_option (struct universe *,
+ struct option_state *, unsigned);
+void do_packet (struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ unsigned int, struct iaddr, struct hardware *);
+void do_packet6(struct interface_info *, const char *,
+ int, int, const struct iaddr *, isc_boolean_t);
+int packet6_len_okay(const char *, int);
+
+int add_option(struct option_state *options,
+ unsigned int option_num,
+ void *data,
+ unsigned int data_len);
+
+/* dhcpd.c */
+extern struct timeval cur_tv;
+#define cur_time cur_tv.tv_sec
+
+extern int ddns_update_style;
+
+extern const char *path_dhcpd_conf;
+extern const char *path_dhcpd_db;
+extern const char *path_dhcpd_pid;
+
+extern int dhcp_max_agent_option_packet_length;
+extern struct eventqueue *rw_queue_empty;
+
+int main(int, char **);
+void postconf_initialization(int);
+void postdb_startup(void);
+void cleanup (void);
+void lease_pinged (struct iaddr, u_int8_t *, int);
+void lease_ping_timeout (void *);
+int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia);
+extern enum dhcp_shutdown_state shutdown_state;
+isc_result_t dhcp_io_shutdown (omapi_object_t *, void *);
+isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
+ control_object_state_t newstate);
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_ackqueue(void);
+#endif
+
+/* conflex.c */
+isc_result_t new_parse (struct parse **, int,
+ char *, unsigned, const char *, int);
+isc_result_t end_parse (struct parse **);
+isc_result_t save_parse_state(struct parse *cfile);
+isc_result_t restore_parse_state(struct parse *cfile);
+enum dhcp_token next_token (const char **, unsigned *, struct parse *);
+enum dhcp_token peek_token (const char **, unsigned *, struct parse *);
+enum dhcp_token next_raw_token(const char **rval, unsigned *rlen,
+ struct parse *cfile);
+enum dhcp_token peek_raw_token(const char **rval, unsigned *rlen,
+ struct parse *cfile);
+
+/* confpars.c */
+void parse_trace_setup (void);
+isc_result_t readconf (void);
+isc_result_t read_conf_file (const char *, struct group *, int, int);
+#if defined (TRACING)
+void trace_conf_input (trace_type_t *, unsigned, char *);
+void trace_conf_stop (trace_type_t *ttype);
+#endif
+isc_result_t conf_file_subparse (struct parse *, struct group *, int);
+isc_result_t lease_file_subparse (struct parse *);
+int parse_statement (struct parse *, struct group *, int,
+ struct host_decl *, int);
+#if defined (FAILOVER_PROTOCOL)
+void parse_failover_peer (struct parse *, struct group *, int);
+void parse_failover_state_declaration (struct parse *,
+ dhcp_failover_state_t *);
+void parse_failover_state (struct parse *,
+ enum failover_state *, TIME *);
+#endif
+int permit_list_match (struct permit *, struct permit *);
+void parse_pool_statement (struct parse *, struct group *, int);
+int parse_lbrace (struct parse *);
+void parse_host_declaration (struct parse *, struct group *);
+int parse_class_declaration (struct class **, struct parse *,
+ struct group *, int);
+void parse_shared_net_declaration (struct parse *, struct group *);
+void parse_subnet_declaration (struct parse *,
+ struct shared_network *);
+void parse_subnet6_declaration (struct parse *,
+ struct shared_network *);
+void parse_group_declaration (struct parse *, struct group *);
+int parse_fixed_addr_param (struct option_cache **,
+ struct parse *, enum dhcp_token);
+int parse_lease_declaration (struct lease **, struct parse *);
+int parse_ip6_addr(struct parse *, struct iaddr *);
+int parse_ip6_addr_expr(struct expression **, struct parse *);
+int parse_ip6_prefix(struct parse *, struct iaddr *, u_int8_t *);
+void parse_address_range (struct parse *, struct group *, int,
+ struct pool *, struct lease **);
+void parse_address_range6(struct parse *cfile, struct group *group);
+void parse_prefix6(struct parse *cfile, struct group *group);
+void parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl);
+void parse_ia_na_declaration(struct parse *);
+void parse_ia_ta_declaration(struct parse *);
+void parse_ia_pd_declaration(struct parse *);
+void parse_server_duid(struct parse *cfile);
+void parse_server_duid_conf(struct parse *cfile);
+
+/* ddns.c */
+int ddns_updates(struct packet *, struct lease *, struct lease *,
+ struct iasubopt *, struct iasubopt *, struct option_state *);
+int ddns_removals(struct lease *, struct iasubopt *, struct dhcp_ddns_cb *);
+#if defined (TRACING)
+void trace_ddns_init(void);
+#endif
+
+/* parse.c */
+void add_enumeration (struct enumeration *);
+struct enumeration *find_enumeration (const char *, int);
+struct enumeration_value *find_enumeration_value (const char *, int,
+ unsigned *,
+ const char *);
+void skip_to_semi (struct parse *);
+void skip_to_rbrace (struct parse *, int);
+int parse_semi (struct parse *);
+int parse_string (struct parse *, char **, unsigned *);
+char *parse_host_name (struct parse *);
+int parse_ip_addr_or_hostname (struct expression **,
+ struct parse *, int);
+void parse_hardware_param (struct parse *, struct hardware *);
+void parse_lease_time (struct parse *, TIME *);
+unsigned char *parse_numeric_aggregate (struct parse *,
+ unsigned char *, unsigned *,
+ int, int, unsigned);
+void convert_num (struct parse *, unsigned char *, const char *,
+ int, unsigned);
+TIME parse_date (struct parse *);
+TIME parse_date_core(struct parse *);
+isc_result_t parse_option_name (struct parse *, int, int *,
+ struct option **);
+void parse_option_space_decl (struct parse *);
+int parse_option_code_definition (struct parse *, struct option *);
+int parse_base64 (struct data_string *, struct parse *);
+int parse_cshl (struct data_string *, struct parse *);
+int parse_executable_statement (struct executable_statement **,
+ struct parse *, int *,
+ enum expression_context);
+int parse_executable_statements (struct executable_statement **,
+ struct parse *, int *,
+ enum expression_context);
+int parse_zone (struct dns_zone *, struct parse *);
+int parse_key (struct parse *);
+int parse_on_statement (struct executable_statement **,
+ struct parse *, int *);
+int parse_switch_statement (struct executable_statement **,
+ struct parse *, int *);
+int parse_case_statement (struct executable_statement **,
+ struct parse *, int *,
+ enum expression_context);
+int parse_if_statement (struct executable_statement **,
+ struct parse *, int *);
+int parse_boolean_expression (struct expression **,
+ struct parse *, int *);
+int parse_boolean (struct parse *);
+int parse_data_expression (struct expression **,
+ struct parse *, int *);
+int parse_numeric_expression (struct expression **,
+ struct parse *, int *);
+int parse_dns_expression (struct expression **, struct parse *, int *);
+int parse_non_binary (struct expression **, struct parse *, int *,
+ enum expression_context);
+int parse_expression (struct expression **, struct parse *, int *,
+ enum expression_context,
+ struct expression **, enum expr_op);
+int parse_option_data(struct expression **expr, struct parse *cfile,
+ int lookups, struct option *option);
+int parse_option_statement (struct executable_statement **,
+ struct parse *, int,
+ struct option *, enum statement_op);
+int parse_option_token (struct expression **, struct parse *,
+ const char **, struct expression *, int, int);
+int parse_allow_deny (struct option_cache **, struct parse *, int);
+int parse_auth_key (struct data_string *, struct parse *);
+int parse_warn (struct parse *, const char *, ...)
+ __attribute__((__format__(__printf__,2,3)));
+struct expression *parse_domain_list(struct parse *cfile, int);
+
+
+/* tree.c */
+#if defined (NSUPDATE)
+extern struct __res_state resolver_state;
+extern int resolver_inited;
+#endif
+
+extern struct binding_scope *global_scope;
+pair cons (caddr_t, pair);
+int make_const_option_cache (struct option_cache **, struct buffer **,
+ u_int8_t *, unsigned, struct option *,
+ const char *, int);
+int make_host_lookup (struct expression **, const char *);
+int enter_dns_host (struct dns_host_entry **, const char *);
+int make_const_data (struct expression **,
+ const unsigned char *, unsigned, int, int,
+ const char *, int);
+int make_const_int (struct expression **, unsigned long);
+int make_concat (struct expression **,
+ struct expression *, struct expression *);
+int make_encapsulation (struct expression **, struct data_string *);
+int make_substring (struct expression **, struct expression *,
+ struct expression *, struct expression *);
+int make_limit (struct expression **, struct expression *, int);
+int make_let (struct executable_statement **, const char *);
+int option_cache (struct option_cache **, struct data_string *,
+ struct expression *, struct option *,
+ const char *, int);
+int evaluate_expression (struct binding_value **, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct expression *,
+ const char *, int);
+int binding_value_dereference (struct binding_value **, const char *, int);
+#if defined (NSUPDATE_OLD)
+int evaluate_dns_expression (ns_updrec **, struct packet *,
+ struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct expression *);
+#endif
+int evaluate_boolean_expression (int *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct expression *);
+int evaluate_data_expression (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct expression *, const char *, int);
+int evaluate_numeric_expression (unsigned long *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct expression *);
+int evaluate_option_cache (struct data_string *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct option_cache *,
+ const char *, int);
+int evaluate_boolean_option_cache (int *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct option_cache *,
+ const char *, int);
+int evaluate_boolean_expression_result (int *,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct expression *);
+void expression_dereference (struct expression **, const char *, int);
+int is_dns_expression (struct expression *);
+int is_boolean_expression (struct expression *);
+int is_data_expression (struct expression *);
+int is_numeric_expression (struct expression *);
+int is_compound_expression (struct expression *);
+int op_precedence (enum expr_op, enum expr_op);
+enum expression_context expression_context (struct expression *);
+enum expression_context op_context (enum expr_op);
+int write_expression (FILE *, struct expression *, int, int, int);
+struct binding *find_binding (struct binding_scope *, const char *);
+int free_bindings (struct binding_scope *, const char *, int);
+int binding_scope_dereference (struct binding_scope **,
+ const char *, int);
+int fundef_dereference (struct fundef **, const char *, int);
+int data_subexpression_length (int *, struct expression *);
+int expr_valid_for_context (struct expression *, enum expression_context);
+struct binding *create_binding (struct binding_scope **, const char *);
+int bind_ds_value (struct binding_scope **,
+ const char *, struct data_string *);
+int find_bound_string (struct data_string *,
+ struct binding_scope *, const char *);
+int unset (struct binding_scope *, const char *);
+int data_string_sprintfa(struct data_string *ds, const char *fmt, ...);
+
+/* dhcp.c */
+extern int outstanding_pings;
+extern int max_outstanding_acks;
+extern int max_ack_delay_secs;
+extern int max_ack_delay_usecs;
+
+void dhcp (struct packet *);
+void dhcpdiscover (struct packet *, int);
+void dhcprequest (struct packet *, int, struct lease *);
+void dhcprelease (struct packet *, int);
+void dhcpdecline (struct packet *, int);
+void dhcpinform (struct packet *, int);
+void nak_lease (struct packet *, struct iaddr *cip);
+void ack_lease (struct packet *, struct lease *,
+ unsigned int, TIME, char *, int, struct host_decl *);
+void delayed_ack_enqueue(struct lease *);
+void commit_leases_readerdry(void *);
+void flush_ackqueue(void *);
+void dhcp_reply (struct lease *);
+int find_lease (struct lease **, struct packet *,
+ struct shared_network *, int *, int *, struct lease *,
+ const char *, int);
+int mockup_lease (struct lease **, struct packet *,
+ struct shared_network *,
+ struct host_decl *);
+void static_lease_dereference (struct lease *, const char *, int);
+
+int allocate_lease (struct lease **, struct packet *,
+ struct pool *, int *);
+int permitted (struct packet *, struct permit *);
+int locate_network (struct packet *);
+int parse_agent_information_option (struct packet *, int, u_int8_t *);
+unsigned cons_agent_information_options (struct option_state *,
+ struct dhcp_packet *,
+ unsigned, unsigned);
+void get_server_source_address(struct in_addr *from,
+ struct option_state *options,
+ struct packet *packet);
+
+/* dhcpleasequery.c */
+void dhcpleasequery (struct packet *, int);
+void dhcpv6_leasequery (struct data_string *, struct packet *);
+
+/* dhcpv6.c */
+isc_boolean_t server_duid_isset(void);
+void copy_server_duid(struct data_string *ds, const char *file, int line);
+void set_server_duid(struct data_string *new_duid);
+isc_result_t set_server_duid_from_option(void);
+void set_server_duid_type(int type);
+isc_result_t generate_new_server_duid(void);
+isc_result_t get_client_id(struct packet *, struct data_string *);
+void dhcpv6(struct packet *);
+
+/* bootp.c */
+void bootp (struct packet *);
+
+/* memory.c */
+extern int (*group_write_hook) (struct group_object *);
+extern struct group *root_group;
+extern group_hash_t *group_name_hash;
+isc_result_t delete_group (struct group_object *, int);
+isc_result_t supersede_group (struct group_object *, int);
+int clone_group (struct group **, struct group *, const char *, int);
+int write_group (struct group_object *);
+
+/* salloc.c */
+void relinquish_lease_hunks (void);
+struct lease *new_leases (unsigned, const char *, int);
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_lease_states (void);
+#endif
+OMAPI_OBJECT_ALLOC_DECL (lease, struct lease, dhcp_type_lease)
+OMAPI_OBJECT_ALLOC_DECL (class, struct class, dhcp_type_class)
+OMAPI_OBJECT_ALLOC_DECL (subclass, struct class, dhcp_type_subclass)
+OMAPI_OBJECT_ALLOC_DECL (pool, struct pool, dhcp_type_pool)
+OMAPI_OBJECT_ALLOC_DECL (host, struct host_decl, dhcp_type_host)
+
+/* alloc.c */
+OMAPI_OBJECT_ALLOC_DECL (subnet, struct subnet, dhcp_type_subnet)
+OMAPI_OBJECT_ALLOC_DECL (shared_network, struct shared_network,
+ dhcp_type_shared_network)
+OMAPI_OBJECT_ALLOC_DECL (group_object, struct group_object, dhcp_type_group)
+OMAPI_OBJECT_ALLOC_DECL (dhcp_control,
+ dhcp_control_object_t, dhcp_type_control)
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_pairs (void);
+void relinquish_free_expressions (void);
+void relinquish_free_binding_values (void);
+void relinquish_free_option_caches (void);
+void relinquish_free_packets (void);
+#endif
+
+int option_chain_head_allocate (struct option_chain_head **,
+ const char *, int);
+int option_chain_head_reference (struct option_chain_head **,
+ struct option_chain_head *,
+ const char *, int);
+int option_chain_head_dereference (struct option_chain_head **,
+ const char *, int);
+int group_allocate (struct group **, const char *, int);
+int group_reference (struct group **, struct group *, const char *, int);
+int group_dereference (struct group **, const char *, int);
+struct dhcp_packet *new_dhcp_packet (const char *, int);
+struct protocol *new_protocol (const char *, int);
+struct lease_state *new_lease_state (const char *, int);
+struct domain_search_list *new_domain_search_list (const char *, int);
+struct name_server *new_name_server (const char *, int);
+void free_name_server (struct name_server *, const char *, int);
+struct option *new_option (const char *, const char *, int);
+int option_reference(struct option **dest, struct option *src,
+ const char * file, int line);
+int option_dereference(struct option **dest, const char *file, int line);
+struct universe *new_universe (const char *, int);
+void free_universe (struct universe *, const char *, int);
+void free_domain_search_list (struct domain_search_list *,
+ const char *, int);
+void free_lease_state (struct lease_state *, const char *, int);
+void free_protocol (struct protocol *, const char *, int);
+void free_dhcp_packet (struct dhcp_packet *, const char *, int);
+struct client_lease *new_client_lease (const char *, int);
+void free_client_lease (struct client_lease *, const char *, int);
+struct permit *new_permit (const char *, int);
+void free_permit (struct permit *, const char *, int);
+pair new_pair (const char *, int);
+void free_pair (pair, const char *, int);
+int expression_allocate (struct expression **, const char *, int);
+int expression_reference (struct expression **,
+ struct expression *, const char *, int);
+void free_expression (struct expression *, const char *, int);
+int binding_value_allocate (struct binding_value **,
+ const char *, int);
+int binding_value_reference (struct binding_value **,
+ struct binding_value *,
+ const char *, int);
+void free_binding_value (struct binding_value *, const char *, int);
+int fundef_allocate (struct fundef **, const char *, int);
+int fundef_reference (struct fundef **,
+ struct fundef *, const char *, int);
+int option_cache_allocate (struct option_cache **, const char *, int);
+int option_cache_reference (struct option_cache **,
+ struct option_cache *, const char *, int);
+int buffer_allocate (struct buffer **, unsigned, const char *, int);
+int buffer_reference (struct buffer **, struct buffer *,
+ const char *, int);
+int buffer_dereference (struct buffer **, const char *, int);
+int dns_host_entry_allocate (struct dns_host_entry **,
+ const char *, const char *, int);
+int dns_host_entry_reference (struct dns_host_entry **,
+ struct dns_host_entry *,
+ const char *, int);
+int dns_host_entry_dereference (struct dns_host_entry **,
+ const char *, int);
+int option_state_allocate (struct option_state **, const char *, int);
+int option_state_reference (struct option_state **,
+ struct option_state *, const char *, int);
+int option_state_dereference (struct option_state **,
+ const char *, int);
+void data_string_copy(struct data_string *, const struct data_string *,
+ const char *, int);
+void data_string_forget (struct data_string *, const char *, int);
+void data_string_truncate (struct data_string *, int);
+int executable_statement_allocate (struct executable_statement **,
+ const char *, int);
+int executable_statement_reference (struct executable_statement **,
+ struct executable_statement *,
+ const char *, int);
+int packet_allocate (struct packet **, const char *, int);
+int packet_reference (struct packet **,
+ struct packet *, const char *, int);
+int packet_dereference (struct packet **, const char *, int);
+int binding_scope_allocate (struct binding_scope **,
+ const char *, int);
+int binding_scope_reference (struct binding_scope **,
+ struct binding_scope *,
+ const char *, int);
+int dns_zone_allocate (struct dns_zone **, const char *, int);
+int dns_zone_reference (struct dns_zone **,
+ struct dns_zone *, const char *, int);
+
+/* print.c */
+#define DEFAULT_TIME_FORMAT 0
+#define LOCAL_TIME_FORMAT 1
+extern int db_time_format;
+char *quotify_string (const char *, const char *, int);
+char *quotify_buf (const unsigned char *, unsigned, const char *, int);
+char *print_base64 (const unsigned char *, unsigned, const char *, int);
+char *print_hw_addr (const int, const int, const unsigned char *);
+void print_lease (struct lease *);
+void dump_raw (const unsigned char *, unsigned);
+void dump_packet_option (struct option_cache *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct universe *, void *);
+void dump_packet (struct packet *);
+void hash_dump (struct hash_table *);
+char *print_hex (unsigned, const u_int8_t *, unsigned, unsigned);
+void print_hex_only (unsigned, const u_int8_t *, unsigned, char *);
+void print_hex_or_string (unsigned, const u_int8_t *, unsigned, char *);
+#define print_hex_1(len, data, limit) print_hex(len, data, limit, 0)
+#define print_hex_2(len, data, limit) print_hex(len, data, limit, 1)
+#define print_hex_3(len, data, limit) print_hex(len, data, limit, 2)
+char *print_dotted_quads (unsigned, const u_int8_t *);
+char *print_dec_1 (unsigned long);
+char *print_dec_2 (unsigned long);
+void print_expression (const char *, struct expression *);
+int token_print_indent_concat (FILE *, int, int,
+ const char *, const char *, ...);
+int token_indent_data_string (FILE *, int, int, const char *, const char *,
+ struct data_string *);
+int token_print_indent (FILE *, int, int,
+ const char *, const char *, const char *);
+void indent_spaces (FILE *, int);
+#if defined (NSUPDATE)
+void print_dns_status (int, struct dhcp_ddns_cb *, isc_result_t);
+#endif
+const char *print_time(TIME);
+
+void get_hw_addr(const char *name, struct hardware *hw);
+
+/* socket.c */
+#if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
+ || defined (USE_SOCKET_FALLBACK)
+int if_register_socket(struct interface_info *, int, int *);
+#endif
+
+#if defined (USE_SOCKET_FALLBACK) && !defined (USE_SOCKET_SEND)
+void if_reinitialize_fallback (struct interface_info *);
+void if_register_fallback (struct interface_info *);
+ssize_t send_fallback (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+ssize_t send_fallback6(struct interface_info *, struct packet *,
+ struct dhcp_packet *, size_t, struct in6_addr,
+ struct sockaddr_in6 *, struct hardware *);
+#endif
+
+#ifdef USE_SOCKET_SEND
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+#endif
+ssize_t send_packet6(struct interface_info *, const unsigned char *, size_t,
+ struct sockaddr_in6 *);
+#ifdef USE_SOCKET_RECEIVE
+void if_reinitialize_receive (struct interface_info *);
+void if_register_receive (struct interface_info *);
+void if_deregister_receive (struct interface_info *);
+ssize_t receive_packet (struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+#endif
+
+#if defined (USE_SOCKET_FALLBACK)
+isc_result_t fallback_discard (omapi_object_t *);
+#endif
+
+#if defined (USE_SOCKET_SEND)
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+
+void if_register6(struct interface_info *info, int do_multicast);
+ssize_t receive_packet6(struct interface_info *interface,
+ unsigned char *buf, size_t len,
+ struct sockaddr_in6 *from, struct in6_addr *to_addr,
+ unsigned int *if_index);
+void if_deregister6(struct interface_info *info);
+
+
+/* bpf.c */
+#if defined (USE_BPF_SEND) || defined (USE_BPF_RECEIVE)
+int if_register_bpf (struct interface_info *);
+#endif
+#ifdef USE_BPF_SEND
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#ifdef USE_BPF_RECEIVE
+void if_reinitialize_receive (struct interface_info *);
+void if_register_receive (struct interface_info *);
+void if_deregister_receive (struct interface_info *);
+ssize_t receive_packet (struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#if defined (USE_BPF_SEND)
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+
+/* lpf.c */
+#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
+int if_register_lpf (struct interface_info *);
+#endif
+#ifdef USE_LPF_SEND
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#ifdef USE_LPF_RECEIVE
+void if_reinitialize_receive (struct interface_info *);
+void if_register_receive (struct interface_info *);
+void if_deregister_receive (struct interface_info *);
+ssize_t receive_packet (struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#if defined (USE_LPF_SEND)
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+
+/* nit.c */
+#if defined (USE_NIT_SEND) || defined (USE_NIT_RECEIVE)
+int if_register_nit (struct interface_info *);
+#endif
+
+#ifdef USE_NIT_SEND
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#ifdef USE_NIT_RECEIVE
+void if_reinitialize_receive (struct interface_info *);
+void if_register_receive (struct interface_info *);
+void if_deregister_receive (struct interface_info *);
+ssize_t receive_packet (struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+#endif
+#if defined (USE_NIT_SEND)
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+
+/* dlpi.c */
+#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE)
+int if_register_dlpi (struct interface_info *);
+#endif
+
+#ifdef USE_DLPI_SEND
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *,
+ struct packet *, struct dhcp_packet *, size_t,
+ struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+#ifdef USE_DLPI_RECEIVE
+void if_reinitialize_receive (struct interface_info *);
+void if_register_receive (struct interface_info *);
+void if_deregister_receive (struct interface_info *);
+ssize_t receive_packet (struct interface_info *,
+ unsigned char *, size_t,
+ struct sockaddr_in *, struct hardware *);
+#endif
+
+
+/* raw.c */
+#ifdef USE_RAW_SEND
+void if_reinitialize_send (struct interface_info *);
+void if_register_send (struct interface_info *);
+void if_deregister_send (struct interface_info *);
+ssize_t send_packet (struct interface_info *, struct packet *,
+ struct dhcp_packet *, size_t, struct in_addr,
+ struct sockaddr_in *, struct hardware *);
+int can_unicast_without_arp (struct interface_info *);
+int can_receive_unicast_unconfigured (struct interface_info *);
+int supports_multiple_interfaces (struct interface_info *);
+void maybe_setup_fallback (void);
+#endif
+
+/* discover.c */
+extern struct interface_info *interfaces,
+ *dummy_interfaces, *fallback_interface;
+extern struct protocol *protocols;
+extern int quiet_interface_discovery;
+isc_result_t interface_setup (void);
+void interface_trace_setup (void);
+
+extern struct in_addr limited_broadcast;
+extern int local_family;
+extern struct in_addr local_address;
+extern struct in6_addr local_address6;
+
+extern u_int16_t local_port;
+extern u_int16_t remote_port;
+extern int (*dhcp_interface_setup_hook) (struct interface_info *,
+ struct iaddr *);
+extern int (*dhcp_interface_discovery_hook) (struct interface_info *);
+extern isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
+
+extern void (*bootp_packet_handler) (struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ unsigned int,
+ struct iaddr, struct hardware *);
+extern void (*dhcpv6_packet_handler)(struct interface_info *,
+ const char *, int,
+ int, const struct iaddr *, isc_boolean_t);
+extern struct timeout *timeouts;
+extern omapi_object_type_t *dhcp_type_interface;
+#if defined (TRACING)
+extern trace_type_t *interface_trace;
+extern trace_type_t *inpacket_trace;
+extern trace_type_t *outpacket_trace;
+#endif
+extern struct interface_info **interface_vector;
+extern int interface_count;
+extern int interface_max;
+isc_result_t interface_initialize(omapi_object_t *, const char *, int);
+void discover_interfaces(int);
+int setup_fallback (struct interface_info **, const char *, int);
+int if_readsocket (omapi_object_t *);
+void reinitialize_interfaces (void);
+
+/* dispatch.c */
+void set_time(TIME);
+struct timeval *process_outstanding_timeouts (struct timeval *);
+void dispatch (void);
+isc_result_t got_one(omapi_object_t *);
+isc_result_t got_one_v6(omapi_object_t *);
+isc_result_t interface_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *, omapi_typed_data_t *);
+isc_result_t interface_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *, omapi_value_t **);
+isc_result_t interface_destroy (omapi_object_t *, const char *, int);
+isc_result_t interface_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t interface_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+
+void add_timeout (struct timeval *, void (*) (void *), void *,
+ tvref_t, tvunref_t);
+void cancel_timeout (void (*) (void *), void *);
+void cancel_all_timeouts (void);
+void relinquish_timeouts (void);
+
+OMAPI_OBJECT_ALLOC_DECL (interface,
+ struct interface_info, dhcp_type_interface)
+
+/* tables.c */
+extern char *default_option_format;
+extern struct universe dhcp_universe;
+extern struct universe dhcpv6_universe;
+extern struct universe nwip_universe;
+extern struct universe fqdn_universe;
+extern struct universe vsio_universe;
+extern int dhcp_option_default_priority_list [];
+extern int dhcp_option_default_priority_list_count;
+extern const char *hardware_types [256];
+extern int universe_count, universe_max;
+extern struct universe **universes;
+extern universe_hash_t *universe_hash;
+void initialize_common_option_spaces (void);
+extern struct universe *config_universe;
+
+/* stables.c */
+#if defined (FAILOVER_PROTOCOL)
+extern failover_option_t null_failover_option;
+extern failover_option_t skip_failover_option;
+extern struct failover_option_info ft_options [];
+extern u_int32_t fto_allowed [];
+extern int ft_sizes [];
+extern const char *dhcp_flink_state_names [];
+#endif
+extern const char *binding_state_names [];
+
+extern struct universe agent_universe;
+extern struct universe server_universe;
+
+extern struct enumeration ddns_styles;
+extern struct enumeration syslog_enum;
+void initialize_server_option_spaces (void);
+
+/* inet.c */
+struct iaddr subnet_number (struct iaddr, struct iaddr);
+struct iaddr ip_addr (struct iaddr, struct iaddr, u_int32_t);
+struct iaddr broadcast_addr (struct iaddr, struct iaddr);
+u_int32_t host_addr (struct iaddr, struct iaddr);
+int addr_eq (struct iaddr, struct iaddr);
+int addr_match(struct iaddr *, struct iaddrmatch *);
+int addr_cmp(const struct iaddr *a1, const struct iaddr *a2);
+int addr_or(struct iaddr *result,
+ const struct iaddr *a1, const struct iaddr *a2);
+int addr_and(struct iaddr *result,
+ const struct iaddr *a1, const struct iaddr *a2);
+isc_boolean_t is_cidr_mask_valid(const struct iaddr *addr, int bits);
+isc_result_t range2cidr(struct iaddrcidrnetlist **result,
+ const struct iaddr *lo, const struct iaddr *hi);
+isc_result_t free_iaddrcidrnetlist(struct iaddrcidrnetlist **result);
+const char *piaddr (struct iaddr);
+char *piaddrmask(struct iaddr *, struct iaddr *);
+char *piaddrcidr(const struct iaddr *, unsigned int);
+u_int16_t validate_port(char *);
+
+/* dhclient.c */
+extern int nowait;
+
+extern int wanted_ia_na;
+extern int wanted_ia_ta;
+extern int wanted_ia_pd;
+
+extern const char *path_dhclient_conf;
+extern const char *path_dhclient_db;
+extern const char *path_dhclient_pid;
+extern char *path_dhclient_script;
+extern int interfaces_requested;
+extern struct data_string default_duid;
+extern int duid_type;
+
+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_release (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_stop (void *);
+void state_panic (void *);
+
+void bind_lease (struct client_state *);
+
+void make_client_options (struct client_state *,
+ struct client_lease *, u_int8_t *,
+ struct option_cache *, struct iaddr *,
+ struct option **, struct option_state **);
+void make_discover (struct client_state *, struct client_lease *);
+void make_request (struct client_state *, struct client_lease *);
+void make_decline (struct client_state *, struct client_lease *);
+void make_release (struct client_state *, struct client_lease *);
+
+void destroy_client_lease (struct client_lease *);
+void rewrite_client_leases (void);
+void write_lease_option (struct option_cache *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct universe *, void *);
+int write_client_lease (struct client_state *, struct client_lease *, int, int);
+isc_result_t write_client6_lease(struct client_state *client,
+ struct dhc6_lease *lease,
+ int rewrite, int sync);
+int dhcp_option_ev_name (char *, size_t, struct option *);
+
+void script_init (struct client_state *, const char *,
+ struct string_list *);
+void client_option_envadd (struct option_cache *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct universe *, void *);
+void script_write_params (struct client_state *, const char *,
+ struct client_lease *);
+int script_go (struct client_state *);
+void client_envadd (struct client_state *,
+ const char *, const char *, const char *, ...)
+ __attribute__((__format__(__printf__,4,5)));
+
+struct client_lease *packet_to_lease (struct packet *, struct client_state *);
+void go_daemon (void);
+void write_client_pid_file (void);
+void client_location_changed (void);
+void do_release (struct client_state *);
+int dhclient_interface_shutdown_hook (struct interface_info *);
+int dhclient_interface_discovery_hook (struct interface_info *);
+isc_result_t dhclient_interface_startup_hook (struct interface_info *);
+void dhclient_schedule_updates(struct client_state *client,
+ struct iaddr *addr, int offset);
+void client_dns_update_timeout (void *cp);
+isc_result_t client_dns_update(struct client_state *client,
+ dhcp_ddns_cb_t *ddns_cb);
+void client_dns_remove(struct client_state *client, struct iaddr *addr);
+
+void dhcpv4_client_assignments(void);
+void dhcpv6_client_assignments(void);
+
+/* dhc6.c */
+void form_duid(struct data_string *duid, const char *file, int line);
+void dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line);
+void start_init6(struct client_state *client);
+void start_info_request6(struct client_state *client);
+void start_confirm6(struct client_state *client);
+void start_release6(struct client_state *client);
+void start_selecting6(struct client_state *client);
+void unconfigure6(struct client_state *client, const char *reason);
+
+/* db.c */
+int write_lease (struct lease *);
+int write_host (struct host_decl *);
+int write_server_duid(void);
+#if defined (FAILOVER_PROTOCOL)
+int write_failover_state (dhcp_failover_state_t *);
+#endif
+int db_printable (const unsigned char *);
+int db_printable_len (const unsigned char *, unsigned);
+isc_result_t write_named_billing_class(const void *, unsigned, void *);
+void write_billing_classes (void);
+int write_billing_class (struct class *);
+void commit_leases_timeout (void *);
+void commit_leases_readerdry(void *);
+int commit_leases (void);
+void db_startup (int);
+int new_lease_file (void);
+int group_writer (struct group_object *);
+int write_ia(const struct ia_xx *);
+
+/* packet.c */
+u_int32_t checksum (unsigned char *, unsigned, u_int32_t);
+u_int32_t wrapsum (u_int32_t);
+void assemble_hw_header (struct interface_info *, unsigned char *,
+ unsigned *, struct hardware *);
+void assemble_udp_ip_header (struct interface_info *, unsigned char *,
+ unsigned *, u_int32_t, u_int32_t,
+ u_int32_t, unsigned char *, unsigned);
+ssize_t decode_hw_header (struct interface_info *, unsigned char *,
+ unsigned, struct hardware *);
+ssize_t decode_udp_ip_header (struct interface_info *, unsigned char *,
+ unsigned, struct sockaddr_in *,
+ unsigned, unsigned *);
+
+/* ethernet.c */
+void assemble_ethernet_header (struct interface_info *, unsigned char *,
+ unsigned *, struct hardware *);
+ssize_t decode_ethernet_header (struct interface_info *,
+ unsigned char *,
+ unsigned, struct hardware *);
+
+/* tr.c */
+void assemble_tr_header (struct interface_info *, unsigned char *,
+ unsigned *, struct hardware *);
+ssize_t decode_tr_header (struct interface_info *,
+ unsigned char *,
+ unsigned, struct hardware *);
+
+/* dhxpxlt.c */
+void convert_statement (struct parse *);
+void convert_host_statement (struct parse *, jrefproto);
+void convert_host_name (struct parse *, jrefproto);
+void convert_class_statement (struct parse *, jrefproto, int);
+void convert_class_decl (struct parse *, jrefproto);
+void convert_lease_time (struct parse *, jrefproto, char *);
+void convert_shared_net_statement (struct parse *, jrefproto);
+void convert_subnet_statement (struct parse *, jrefproto);
+void convert_subnet_decl (struct parse *, jrefproto);
+void convert_host_decl (struct parse *, jrefproto);
+void convert_hardware_decl (struct parse *, jrefproto);
+void convert_hardware_addr (struct parse *, jrefproto);
+void convert_filename_decl (struct parse *, jrefproto);
+void convert_servername_decl (struct parse *, jrefproto);
+void convert_ip_addr_or_hostname (struct parse *, jrefproto, int);
+void convert_fixed_addr_decl (struct parse *, jrefproto);
+void convert_option_decl (struct parse *, jrefproto);
+void convert_lease_statement (struct parse *, jrefproto);
+void convert_address_range (struct parse *, jrefproto);
+void convert_date (struct parse *, jrefproto, char *);
+void convert_numeric_aggregate (struct parse *, jrefproto, int, int, int, int);
+void indent (int);
+
+/* route.c */
+void add_route_direct (struct interface_info *, struct in_addr);
+void add_route_net (struct interface_info *, struct in_addr, struct in_addr);
+void add_route_default_gateway (struct interface_info *, struct in_addr);
+void remove_routes (struct in_addr);
+void remove_if_route (struct interface_info *, struct in_addr);
+void remove_all_if_routes (struct interface_info *);
+void set_netmask (struct interface_info *, struct in_addr);
+void set_broadcast_addr (struct interface_info *, struct in_addr);
+void set_ip_address (struct interface_info *, struct in_addr);
+
+/* clparse.c */
+isc_result_t read_client_conf (void);
+int read_client_conf_file (const char *,
+ struct interface_info *, struct client_config *);
+void read_client_leases (void);
+void parse_client_statement (struct parse *, struct interface_info *,
+ struct client_config *);
+int parse_X (struct parse *, u_int8_t *, unsigned);
+int parse_option_list (struct parse *, struct option ***);
+void parse_interface_declaration (struct parse *,
+ struct client_config *, char *);
+int interface_or_dummy (struct interface_info **, const char *);
+void make_client_state (struct client_state **);
+void make_client_config (struct client_state *, struct client_config *);
+void parse_client_lease_statement (struct parse *, int);
+void parse_client_lease_declaration (struct parse *,
+ struct client_lease *,
+ struct interface_info **,
+ struct client_state **);
+int parse_option_decl (struct option_cache **, struct parse *);
+void parse_string_list (struct parse *, struct string_list **, int);
+int parse_ip_addr (struct parse *, struct iaddr *);
+int parse_ip_addr_with_subnet(struct parse *, struct iaddrmatch *);
+void parse_reject_statement (struct parse *, struct client_config *);
+
+/* icmp.c */
+OMAPI_OBJECT_ALLOC_DECL (icmp_state, struct icmp_state, dhcp_type_icmp)
+extern struct icmp_state *icmp_state;
+void icmp_startup (int, void (*) (struct iaddr, u_int8_t *, int));
+int icmp_readsocket (omapi_object_t *);
+int icmp_echorequest (struct iaddr *);
+isc_result_t icmp_echoreply (omapi_object_t *);
+
+/* dns.c */
+#if defined (NSUPDATE)
+isc_result_t find_tsig_key (ns_tsig_key **, const char *, struct dns_zone *);
+void tkey_free (ns_tsig_key **);
+#endif
+isc_result_t enter_dns_zone (struct dns_zone *);
+isc_result_t dns_zone_lookup (struct dns_zone **, const char *);
+int dns_zone_dereference (struct dns_zone **, const char *, int);
+#if defined (NSUPDATE)
+#define FIND_FORWARD 0
+#define FIND_REVERSE 1
+isc_result_t find_cached_zone (dhcp_ddns_cb_t *, int);
+void forget_zone (struct dns_zone **);
+void repudiate_zone (struct dns_zone **);
+//void cache_found_zone (ns_class, char *, struct in_addr *, int);
+int get_dhcid (struct data_string *, int, const u_int8_t *, unsigned);
+void dhcid_tolease (struct data_string *, struct data_string *);
+isc_result_t dhcid_fromlease (struct data_string *, struct data_string *);
+isc_result_t ddns_update_fwd(struct data_string *, struct iaddr,
+ struct data_string *, unsigned long, unsigned,
+ unsigned);
+isc_result_t ddns_remove_fwd(struct data_string *,
+ struct iaddr, struct data_string *);
+#endif /* NSUPDATE */
+
+/* resolv.c */
+extern char path_resolv_conf [];
+extern struct name_server *name_servers;
+extern struct domain_search_list *domains;
+
+void read_resolv_conf (TIME);
+struct name_server *first_name_server (void);
+
+/* inet_addr.c */
+#ifdef NEED_INET_ATON
+int inet_aton (const char *, struct in_addr *);
+#endif
+
+/* class.c */
+extern int have_billing_classes;
+struct class unknown_class;
+struct class known_class;
+struct collection default_collection;
+struct collection *collections;
+extern struct executable_statement *default_classification_rules;
+
+void classification_setup (void);
+void classify_client (struct packet *);
+int check_collection (struct packet *, struct lease *, struct collection *);
+void classify (struct packet *, struct class *);
+isc_result_t unlink_class (struct class **class);
+isc_result_t find_class (struct class **, const char *,
+ const char *, int);
+int unbill_class (struct lease *, struct class *);
+int bill_class (struct lease *, struct class *);
+
+/* execute.c */
+int execute_statements (struct binding_value **result,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct executable_statement *);
+void execute_statements_in_scope (struct binding_value **result,
+ struct packet *, struct lease *,
+ struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct group *, struct group *);
+int executable_statement_dereference (struct executable_statement **,
+ const char *, int);
+void write_statements (FILE *, struct executable_statement *, int);
+int find_matching_case (struct executable_statement **,
+ struct packet *, struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct expression *, struct executable_statement *);
+int executable_statement_foreach (struct executable_statement *,
+ int (*) (struct executable_statement *,
+ void *, int), void *, int);
+
+/* comapi.c */
+extern omapi_object_type_t *dhcp_type_group;
+extern omapi_object_type_t *dhcp_type_shared_network;
+extern omapi_object_type_t *dhcp_type_subnet;
+extern omapi_object_type_t *dhcp_type_control;
+extern dhcp_control_object_t *dhcp_control_object;
+
+void dhcp_common_objects_setup (void);
+
+isc_result_t dhcp_group_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_group_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_group_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_group_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_group_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_group_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_group_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_group_remove (omapi_object_t *,
+ omapi_object_t *);
+
+isc_result_t dhcp_control_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_control_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_control_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_control_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_control_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_control_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_control_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_control_remove (omapi_object_t *,
+ omapi_object_t *);
+
+isc_result_t dhcp_subnet_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_subnet_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_subnet_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_subnet_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_subnet_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_subnet_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_subnet_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_subnet_remove (omapi_object_t *,
+ omapi_object_t *);
+
+isc_result_t dhcp_shared_network_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_shared_network_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_shared_network_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_shared_network_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_shared_network_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_shared_network_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_shared_network_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_shared_network_remove (omapi_object_t *,
+ omapi_object_t *);
+
+/* omapi.c */
+extern int (*dhcp_interface_shutdown_hook) (struct interface_info *);
+
+extern omapi_object_type_t *dhcp_type_lease;
+extern omapi_object_type_t *dhcp_type_pool;
+extern omapi_object_type_t *dhcp_type_class;
+extern omapi_object_type_t *dhcp_type_subclass;
+
+#if defined (FAILOVER_PROTOCOL)
+extern omapi_object_type_t *dhcp_type_failover_state;
+extern omapi_object_type_t *dhcp_type_failover_link;
+extern omapi_object_type_t *dhcp_type_failover_listener;
+#endif
+
+void dhcp_db_objects_setup (void);
+
+isc_result_t dhcp_lease_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_lease_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_lease_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_lease_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_lease_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_lease_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_lease_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_lease_remove (omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_host_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_host_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_host_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_host_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_host_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_host_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_host_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_host_remove (omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_pool_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_pool_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_pool_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_pool_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_pool_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_pool_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_pool_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_pool_remove (omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_class_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_class_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_class_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_class_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_class_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_class_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_class_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_class_remove (omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_subclass_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_subclass_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_subclass_destroy (omapi_object_t *, const char *, int);
+isc_result_t dhcp_subclass_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_subclass_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_subclass_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *);
+isc_result_t dhcp_subclass_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_subclass_remove (omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_interface_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_interface_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_interface_destroy (omapi_object_t *,
+ const char *, int);
+isc_result_t dhcp_interface_signal_handler (omapi_object_t *,
+ const char *,
+ va_list ap);
+isc_result_t dhcp_interface_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_interface_lookup (omapi_object_t **,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_interface_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_interface_remove (omapi_object_t *,
+ omapi_object_t *);
+void interface_stash (struct interface_info *);
+void interface_snorf (struct interface_info *, int);
+
+isc_result_t binding_scope_set_value (struct binding_scope *, int,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t binding_scope_get_value (omapi_value_t **,
+ struct binding_scope *,
+ omapi_data_string_t *);
+isc_result_t binding_scope_stuff_values (omapi_object_t *,
+ struct binding_scope *);
+
+void register_eventhandler(struct eventqueue **, void (*handler)(void *));
+void unregister_eventhandler(struct eventqueue **, void (*handler)(void *));
+void trigger_event(struct eventqueue **);
+
+/* mdb.c */
+
+extern struct subnet *subnets;
+extern struct shared_network *shared_networks;
+extern host_hash_t *host_hw_addr_hash;
+extern host_hash_t *host_uid_hash;
+extern host_hash_t *host_name_hash;
+extern lease_id_hash_t *lease_uid_hash;
+extern lease_ip_hash_t *lease_ip_addr_hash;
+extern lease_id_hash_t *lease_hw_addr_hash;
+
+extern omapi_object_type_t *dhcp_type_host;
+
+extern int numclasseswritten;
+
+
+isc_result_t enter_class (struct class *, int, int);
+isc_result_t delete_class (struct class *, int);
+isc_result_t enter_host (struct host_decl *, int, int);
+isc_result_t delete_host (struct host_decl *, int);
+void change_host_uid(struct host_decl *host, const char *data, int len);
+int find_hosts_by_haddr (struct host_decl **, int,
+ const unsigned char *, unsigned,
+ const char *, int);
+int find_hosts_by_uid (struct host_decl **, const unsigned char *,
+ unsigned, const char *, int);
+int find_hosts_by_option(struct host_decl **, struct packet *,
+ struct option_state *, const char *, int);
+int find_host_for_network (struct subnet **, struct host_decl **,
+ struct iaddr *, struct shared_network *);
+void new_address_range (struct parse *, struct iaddr, struct iaddr,
+ struct subnet *, struct pool *,
+ struct lease **);
+isc_result_t dhcp_lease_free (omapi_object_t *, const char *, int);
+isc_result_t dhcp_lease_get (omapi_object_t **, const char *, int);
+int find_grouped_subnet (struct subnet **, struct shared_network *,
+ struct iaddr, const char *, int);
+int find_subnet(struct subnet **, struct iaddr, const char *, int);
+void enter_shared_network (struct shared_network *);
+void new_shared_network_interface (struct parse *,
+ struct shared_network *,
+ const char *);
+int subnet_inner_than(const struct subnet *, const struct subnet *, int);
+void enter_subnet (struct subnet *);
+void enter_lease (struct lease *);
+int supersede_lease (struct lease *, struct lease *, int, int, int);
+void make_binding_state_transition (struct lease *);
+int lease_copy (struct lease **, struct lease *, const char *, int);
+void release_lease (struct lease *, struct packet *);
+void abandon_lease (struct lease *, const char *);
+void dissociate_lease (struct lease *);
+void pool_timer (void *);
+int find_lease_by_uid (struct lease **, const unsigned char *,
+ unsigned, const char *, int);
+int find_lease_by_hw_addr (struct lease **, const unsigned char *,
+ unsigned, const char *, int);
+int find_lease_by_ip_addr (struct lease **, struct iaddr,
+ const char *, int);
+void uid_hash_add (struct lease *);
+void uid_hash_delete (struct lease *);
+void hw_hash_add (struct lease *);
+void hw_hash_delete (struct lease *);
+int write_leases (void);
+int write_leases6(void);
+int lease_enqueue (struct lease *);
+isc_result_t lease_instantiate(const void *, unsigned, void *);
+void expire_all_pools (void);
+void dump_subnets (void);
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void free_everything (void);
+#endif
+
+/* nsupdate.c */
+char *ddns_rev_name (struct lease *, struct lease_state *, struct packet *);
+char *ddns_fwd_name (struct lease *, struct lease_state *, struct packet *);
+int nsupdateA (const char *, const unsigned char *, u_int32_t, int);
+int nsupdatePTR (const char *, const unsigned char *, u_int32_t, int);
+void nsupdate (struct lease *, struct lease_state *, struct packet *, int);
+int updateA (const struct data_string *, const struct data_string *,
+ unsigned int, struct lease *);
+int updatePTR (const struct data_string *, const struct data_string *,
+ unsigned int, struct lease *);
+int deleteA (const struct data_string *, const struct data_string *,
+ struct lease *);
+int deletePTR (const struct data_string *, const struct data_string *,
+ struct lease *);
+
+/* failover.c */
+#if defined (FAILOVER_PROTOCOL)
+extern dhcp_failover_state_t *failover_states;
+void dhcp_failover_startup (void);
+int dhcp_failover_write_all_states (void);
+isc_result_t enter_failover_peer (dhcp_failover_state_t *);
+isc_result_t find_failover_peer (dhcp_failover_state_t **,
+ const char *, const char *, int);
+isc_result_t dhcp_failover_link_initiate (omapi_object_t *);
+isc_result_t dhcp_failover_link_signal (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_failover_link_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_failover_link_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_failover_link_destroy (omapi_object_t *,
+ const char *, int);
+isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_failover_listen (omapi_object_t *);
+
+isc_result_t dhcp_failover_listener_signal (omapi_object_t *,
+ const char *,
+ va_list);
+isc_result_t dhcp_failover_listener_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t dhcp_failover_listener_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_failover_listener_destroy (omapi_object_t *,
+ const char *, int);
+isc_result_t dhcp_failover_listener_stuff (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_failover_register (omapi_object_t *);
+isc_result_t dhcp_failover_state_signal (omapi_object_t *,
+ const char *, va_list);
+isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *,
+ const char *);
+isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state);
+isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *,
+ enum failover_state);
+isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *,
+ failover_message_t *);
+void dhcp_failover_pool_rebalance (void *);
+void dhcp_failover_pool_check (struct pool *);
+int dhcp_failover_state_pool_check (dhcp_failover_state_t *);
+void dhcp_failover_timeout (void *);
+void dhcp_failover_send_contact (void *);
+isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *);
+isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *);
+int dhcp_failover_queue_update (struct lease *, int);
+int dhcp_failover_send_acks (dhcp_failover_state_t *);
+void dhcp_failover_toack_queue_timeout (void *);
+int dhcp_failover_queue_ack (dhcp_failover_state_t *, failover_message_t *msg);
+void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *, struct lease *);
+isc_result_t dhcp_failover_state_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+void dhcp_failover_keepalive (void *);
+void dhcp_failover_reconnect (void *);
+void dhcp_failover_startup_timeout (void *);
+void dhcp_failover_link_startup_timeout (void *);
+void dhcp_failover_listener_restart (void *);
+void dhcp_failover_auto_partner_down(void *vs);
+isc_result_t dhcp_failover_state_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t dhcp_failover_state_destroy (omapi_object_t *,
+ const char *, int);
+isc_result_t dhcp_failover_state_stuff (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_failover_state_lookup (omapi_object_t **,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t dhcp_failover_state_create (omapi_object_t **,
+ omapi_object_t *);
+isc_result_t dhcp_failover_state_remove (omapi_object_t *,
+ omapi_object_t *);
+int dhcp_failover_state_match (dhcp_failover_state_t *, u_int8_t *, unsigned);
+int dhcp_failover_state_match_by_name(dhcp_failover_state_t *,
+ failover_option_t *);
+const char *dhcp_failover_reject_reason_print (int);
+const char *dhcp_failover_state_name_print (enum failover_state);
+const char *dhcp_failover_message_name (unsigned);
+const char *dhcp_failover_option_name (unsigned);
+failover_option_t *dhcp_failover_option_printf (unsigned, char *,
+ unsigned *,
+ unsigned,
+ const char *, ...)
+ __attribute__((__format__(__printf__,5,6)));
+failover_option_t *dhcp_failover_make_option (unsigned, char *,
+ unsigned *, unsigned, ...);
+isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *,
+ omapi_object_t *, int, u_int32_t, ...);
+isc_result_t dhcp_failover_send_connect (omapi_object_t *);
+isc_result_t dhcp_failover_send_connectack (omapi_object_t *,
+ dhcp_failover_state_t *,
+ int, const char *);
+isc_result_t dhcp_failover_send_disconnect (omapi_object_t *,
+ int, const char *);
+isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *,
+ struct lease *);
+isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *,
+ failover_message_t *,
+ int, const char *);
+isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *);
+isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *, int);
+isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *);
+isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t *);
+isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *);
+isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *,
+ failover_message_t *);
+isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *,
+ failover_message_t *);
+isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *,
+ int);
+isc_result_t dhcp_failover_process_update_request (dhcp_failover_state_t *,
+ failover_message_t *);
+isc_result_t dhcp_failover_process_update_request_all (dhcp_failover_state_t *,
+ failover_message_t *);
+isc_result_t dhcp_failover_process_update_done (dhcp_failover_state_t *,
+ failover_message_t *);
+void ia_remove_all_lease(struct ia_xx *ia, const char *file, int line);
+void dhcp_failover_recover_done (void *);
+void failover_print (char *, unsigned *, unsigned, const char *);
+void update_partner (struct lease *);
+int load_balance_mine (struct packet *, dhcp_failover_state_t *);
+int peer_wants_lease (struct lease *);
+binding_state_t normal_binding_state_transition_check (struct lease *,
+ dhcp_failover_state_t *,
+ binding_state_t,
+ u_int32_t);
+binding_state_t
+conflict_binding_state_transition_check (struct lease *,
+ dhcp_failover_state_t *,
+ binding_state_t, u_int32_t);
+int lease_mine_to_reallocate (struct lease *);
+
+OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_state, dhcp_failover_state_t,
+ dhcp_type_failover_state)
+OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_listener, dhcp_failover_listener_t,
+ dhcp_type_failover_listener)
+OMAPI_OBJECT_ALLOC_DECL (dhcp_failover_link, dhcp_failover_link_t,
+ dhcp_type_failover_link)
+#endif /* FAILOVER_PROTOCOL */
+
+const char *binding_state_print (enum failover_state);
+
+/* ldap.c */
+#if defined(LDAP_CONFIGURATION)
+extern struct enumeration ldap_methods;
+#if defined (LDAP_USE_SSL)
+extern struct enumeration ldap_ssl_usage_enum;
+extern struct enumeration ldap_tls_reqcert_enum;
+extern struct enumeration ldap_tls_crlcheck_enum;
+#endif
+isc_result_t ldap_read_config (void);
+int find_haddr_in_ldap (struct host_decl **, int, unsigned,
+ const unsigned char *, const char *, int);
+int find_subclass_in_ldap (struct class *, struct class **,
+ struct data_string *);
+#endif
+
+/* mdb6.c */
+HASH_FUNCTIONS_DECL(ia, unsigned char *, struct ia_xx, ia_hash_t)
+HASH_FUNCTIONS_DECL(iasubopt, struct in6_addr *, struct iasubopt,
+ iasubopt_hash_t)
+
+isc_result_t iasubopt_allocate(struct iasubopt **iasubopt,
+ const char *file, int line);
+isc_result_t iasubopt_reference(struct iasubopt **iasubopt,
+ struct iasubopt *src,
+ const char *file, int line);
+isc_result_t iasubopt_dereference(struct iasubopt **iasubopt,
+ const char *file, int line);
+
+isc_result_t ia_make_key(struct data_string *key, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line);
+isc_result_t ia_allocate(struct ia_xx **ia, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line);
+isc_result_t ia_reference(struct ia_xx **ia, struct ia_xx *src,
+ const char *file, int line);
+isc_result_t ia_dereference(struct ia_xx **ia,
+ const char *file, int line);
+isc_result_t ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
+ const char *file, int line);
+void ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
+ const char *file, int line);
+isc_boolean_t ia_equal(const struct ia_xx *a, const struct ia_xx *b);
+
+isc_result_t ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
+ const struct in6_addr *start_addr,
+ int bits, int units,
+ const char *file, int line);
+isc_result_t ipv6_pool_reference(struct ipv6_pool **pool,
+ struct ipv6_pool *src,
+ const char *file, int line);
+isc_result_t ipv6_pool_dereference(struct ipv6_pool **pool,
+ const char *file, int line);
+isc_result_t create_lease6(struct ipv6_pool *pool,
+ struct iasubopt **addr,
+ unsigned int *attempts,
+ const struct data_string *uid,
+ time_t soft_lifetime_end_time);
+isc_result_t add_lease6(struct ipv6_pool *pool,
+ struct iasubopt *lease,
+ time_t valid_lifetime_end_time);
+isc_result_t renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease);
+isc_result_t expire_lease6(struct iasubopt **leasep,
+ struct ipv6_pool *pool, time_t now);
+isc_result_t release_lease6(struct ipv6_pool *pool, struct iasubopt *lease);
+isc_result_t decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease);
+isc_boolean_t lease6_exists(const struct ipv6_pool *pool,
+ const struct in6_addr *addr);
+isc_result_t mark_lease_unavailble(struct ipv6_pool *pool,
+ const struct in6_addr *addr);
+
+isc_result_t create_prefix6(struct ipv6_pool *pool,
+ struct iasubopt **pref,
+ unsigned int *attempts,
+ const struct data_string *uid,
+ time_t soft_lifetime_end_time);
+isc_boolean_t prefix6_exists(const struct ipv6_pool *pool,
+ const struct in6_addr *pref, u_int8_t plen);
+
+isc_result_t add_ipv6_pool(struct ipv6_pool *pool);
+isc_result_t find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
+ const struct in6_addr *addr);
+isc_boolean_t ipv6_in_pool(const struct in6_addr *addr,
+ const struct ipv6_pool *pool);
+
+isc_result_t renew_leases(struct ia_xx *ia);
+isc_result_t release_leases(struct ia_xx *ia);
+isc_result_t decline_leases(struct ia_xx *ia);
+void schedule_lease_timeout(struct ipv6_pool *pool);
+void schedule_all_ipv6_lease_timeouts();
+
+void mark_hosts_unavailable(void);
+void mark_phosts_unavailable(void);
+void mark_interfaces_unavailable(void);
+
+dhcp_ddns_cb_t *ddns_cb_alloc(const char *file, int line);
+void ddns_cb_free (dhcp_ddns_cb_t *ddns_cb, const char *file, int line);
+void ddns_cb_forget_zone (dhcp_ddns_cb_t *ddns_cb);
+
+//void *key_from_zone(struct dns_zone *zone);
+
+isc_result_t
+ddns_modify_fwd(dhcp_ddns_cb_t *ddns_cb);
+
+isc_result_t
+ddns_modify_ptr(dhcp_ddns_cb_t *ddns_cb);
+
+void
+ddns_cancel(dhcp_ddns_cb_t *ddns_cb);
+
+#define MAX_ADDRESS_STRING_LEN \
+ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
diff --git a/includes/dhctoken.h b/includes/dhctoken.h
new file mode 100644
index 0000000..dc28f39
--- /dev/null
+++ b/includes/dhctoken.h
@@ -0,0 +1,370 @@
+/* dhctoken.h
+
+ Tokens for config file lexer and parser. */
+
+/*
+ * Copyright (c) 2004,2007-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+enum dhcp_token {
+ SEMI = ';',
+ DOT = '.',
+ COLON = ':',
+ COMMA = ',',
+ SLASH = '/',
+ LBRACE = '{',
+ RBRACE = '}',
+ LPAREN = '(',
+ RPAREN = ')',
+ EQUAL = '=',
+ TILDE = '~',
+ BANG = '!',
+ PERCENT = '%',
+ PLUS = '+',
+ MINUS = '-',
+ ASTERISK = '*',
+ AMPERSAND = '&',
+ PIPE = '|',
+ CARET = '^',
+ ENDOFLINE = '\n',
+ QUESTIONMARK = '?',
+
+ HOST = 256,
+ FIRST_TOKEN = HOST,
+ HARDWARE = 257,
+ FILENAME = 258,
+ FIXED_ADDR = 259,
+ OPTION = 260,
+ ETHERNET = 261,
+ STRING = 262,
+ NUMBER = 263,
+ NUMBER_OR_NAME = 264,
+ NAME = 265,
+ TIMESTAMP = 266,
+ STARTS = 267,
+ ENDS = 268,
+ UID = 269,
+ CLASS = 270,
+ LEASE = 271,
+ RANGE = 272,
+ PACKET = 273,
+ CIADDR = 274,
+ YIADDR = 275,
+ SIADDR = 276,
+ GIADDR = 277,
+ SUBNET = 278,
+ NETMASK = 279,
+ DEFAULT_LEASE_TIME = 280,
+ MAX_LEASE_TIME = 281,
+ VENDOR_CLASS = 282,
+ USER_CLASS = 283,
+ SHARED_NETWORK = 284,
+ SERVER_NAME = 285,
+ DYNAMIC_BOOTP = 286,
+ SERVER_IDENTIFIER = 287,
+ DYNAMIC_BOOTP_LEASE_CUTOFF = 288,
+ DYNAMIC_BOOTP_LEASE_LENGTH = 289,
+ BOOT_UNKNOWN_CLIENTS = 290,
+ NEXT_SERVER = 291,
+ TOKEN_RING = 292,
+ GROUP = 293,
+ ONE_LEASE_PER_CLIENT = 294,
+ GET_LEASE_HOSTNAMES = 295,
+ USE_HOST_DECL_NAMES = 296,
+ SEND = 297,
+ CLIENT_IDENTIFIER = 298,
+ REQUEST = 299,
+ REQUIRE = 300,
+ TIMEOUT = 301,
+ RETRY = 302,
+ SELECT_TIMEOUT = 303,
+ SCRIPT = 304,
+ INTERFACE = 305,
+ RENEW = 306,
+ REBIND = 307,
+ EXPIRE = 308,
+ UNKNOWN_CLIENTS = 309,
+ ALLOW = 310,
+ DENY = 312,
+ BOOTING = 313,
+ DEFAULT = 314,
+ MEDIA = 315,
+ MEDIUM = 316,
+ ALIAS = 317,
+ REBOOT = 318,
+ TOKEN_ABANDONED = 319,
+ BACKOFF_CUTOFF = 320,
+ INITIAL_INTERVAL = 321,
+ NAMESERVER = 322,
+ DOMAIN = 323,
+ SEARCH = 324,
+ SUPERSEDE = 325,
+ APPEND = 326,
+ PREPEND = 327,
+ HOSTNAME = 328,
+ CLIENT_HOSTNAME = 329,
+ REJECT = 330,
+ USE_LEASE_ADDR_FOR_DEFAULT_ROUTE = 331,
+ MIN_LEASE_TIME = 332,
+ MIN_SECS = 333,
+ AND = 334,
+ OR = 335,
+ SUBSTRING = 337,
+ SUFFIX = 338,
+ CHECK = 339,
+ EXTRACT_INT = 340,
+ IF = 341,
+ TOKEN_ADD = 342,
+ BREAK = 343,
+ ELSE = 344,
+ ELSIF = 345,
+ SUBCLASS = 346,
+ MATCH = 347,
+ SPAWN = 348,
+ WITH = 349,
+ EXISTS = 350,
+ POOL = 351,
+ UNKNOWN = 352,
+ CLIENTS = 353,
+ KNOWN = 354,
+ AUTHENTICATED = 355,
+ UNAUTHENTICATED = 356,
+ ALL = 357,
+ DYNAMIC = 358,
+ MEMBERS = 359,
+ OF = 360,
+ PSEUDO = 361,
+ LIMIT = 362,
+ BILLING = 363,
+ PEER = 364,
+ FAILOVER = 365,
+ MY = 366,
+ PARTNER = 367,
+ PRIMARY = 368,
+ SECONDARY = 369,
+ IDENTIFIER = 370,
+ PORT = 371,
+ MAX_TRANSMIT_IDLE = 372,
+ MAX_RESPONSE_DELAY = 373,
+ PARTNER_DOWN = 374,
+ NORMAL = 375,
+ COMMUNICATIONS_INTERRUPTED = 376,
+ POTENTIAL_CONFLICT = 377,
+ RECOVER = 378,
+ TOKEN_FDDI = 379,
+ AUTHORITATIVE = 380,
+ TOKEN_NOT = 381,
+ AUTHENTICATION = 383,
+ IGNORE = 384,
+ ACCEPT = 385,
+ PREFER = 386,
+ DONT = 387,
+ CODE = 388,
+ ARRAY = 389,
+ BOOLEAN = 390,
+ INTEGER = 391,
+ SIGNED = 392,
+ UNSIGNED = 393,
+ IP_ADDRESS = 394,
+ TEXT = 395,
+ STRING_TOKEN = 396,
+ SPACE = 397,
+ CONCAT = 398,
+ ENCODE_INT = 399,
+ REVERSE = 402,
+ LEASED_ADDRESS = 403,
+ BINARY_TO_ASCII = 404,
+ PICK = 405,
+ CONFIG_OPTION = 406,
+ HOST_DECL_NAME = 407,
+ ON = 408,
+ EXPIRY = 409,
+ RELEASE = 410,
+ COMMIT = 411,
+ DNS_UPDATE = 412,
+ LEASE_TIME = 413,
+ STATIC = 414,
+ NEVER = 415,
+ INFINITE = 416,
+ TOKEN_DELETED = 417,
+ UPDATED_DNS_RR = 418,
+ DNS_DELETE = 419,
+ DUPLICATES = 420,
+ DECLINES = 421,
+ TSTP = 422,
+ TSFP = 423,
+ OWNER = 424,
+ IS = 425,
+ HBA = 426,
+ MAX_UNACKED_UPDATES = 427,
+ MCLT = 428,
+ SPLIT = 429,
+ AT = 430,
+ TOKEN_NO = 431,
+ TOKEN_DELETE = 432,
+ NS_UPDATE = 433,
+ UPDATE = 434,
+ SWITCH = 435,
+ CASE = 436,
+ NS_FORMERR = 437,
+ NS_NOERROR = 438,
+ NS_NOTAUTH = 439,
+ NS_NOTIMP = 440,
+ NS_NOTZONE = 441,
+ NS_NXDOMAIN = 442,
+ NS_NXRRSET = 443,
+ NS_REFUSED = 444,
+ NS_SERVFAIL = 445,
+ NS_YXDOMAIN = 446,
+ NS_YXRRSET = 447,
+ TOKEN_NULL = 448,
+ TOKEN_SET = 449,
+ DEFINED = 450,
+ UNSET = 451,
+ EVAL = 452,
+ LET = 453,
+ FUNCTION = 454,
+ DEFINE = 455,
+ ZONE = 456,
+ KEY = 457,
+ SECRET = 458,
+ ALGORITHM = 459,
+ LOAD = 460,
+ BALANCE = 461,
+ TOKEN_MAX = 462,
+ SECONDS = 463,
+ ADDRESS = 464,
+ RESOLUTION_INTERRUPTED = 465,
+ STATE = 466,
+ UNKNOWN_STATE = 567,
+ CLTT = 568,
+ INCLUDE = 569,
+ BINDING = 570,
+ TOKEN_FREE = 571,
+ TOKEN_ACTIVE = 572,
+ TOKEN_EXPIRED = 573,
+ TOKEN_RELEASED = 574,
+ TOKEN_RESET = 575,
+ TOKEN_BACKUP = 576,
+ TOKEN_RESERVED = 577,
+ TOKEN_BOOTP = 578,
+ TOKEN_NEXT = 579,
+ OMAPI = 580,
+ LOG = 581,
+ FATAL = 582,
+ ERROR = 583,
+ TOKEN_DEBUG = 584,
+ INFO = 585,
+ RETURN = 586,
+ PAUSED = 587,
+ RECOVER_DONE = 588,
+ SHUTDOWN = 589,
+ STARTUP = 590,
+ ENCAPSULATE = 591,
+ VENDOR = 592,
+ CLIENT_STATE = 593,
+ INIT_REBOOT = 594,
+ TOKEN_INIT = 595,
+ SELECT = 596,
+ BOUND = 597,
+ RENEWING = 598,
+ REBINDING = 599,
+ RECONTACT_INTERVAL = 600,
+ CLIENT_UPDATES = 601,
+ TOKEN_NEW = 601,
+ TRANSMISSION = 602,
+ TOKEN_CLOSE = 603,
+ TOKEN_CREATE = 604,
+ TOKEN_OPEN = 605,
+ TOKEN_HELP = 606,
+ END_OF_FILE = 607,
+ RECOVER_WAIT = 608,
+ TOKEN_SERVER = 609,
+ CONNECT = 610,
+ REMOVE = 611,
+ REFRESH = 612,
+ DOMAIN_NAME = 613,
+ DO_FORWARD_UPDATE = 614,
+ KNOWN_CLIENTS = 615,
+ ATSFP = 616,
+ LCASE = 617,
+ UCASE = 618,
+ WIDTH = 619,
+ LENGTH = 620,
+ HASH = 621,
+ SIZE = 622,
+ EPOCH = 623,
+ DB_TIME_FORMAT = 624,
+ LOCAL = 625,
+ MAX_LEASE_MISBALANCE = 626,
+ MAX_LEASE_OWNERSHIP = 627,
+ MAX_BALANCE = 628,
+ MIN_BALANCE = 629,
+ DOMAIN_LIST = 630,
+ LEASEQUERY = 631,
+ EXECUTE = 632,
+ IP6_ADDRESS = 633,
+ FIXED_ADDR6 = 634,
+ COMPRESSED = 635,
+ SUBNET6 = 636,
+ HOST_IDENTIFIER = 637,
+ IA_NA = 638,
+ IA_TA = 639,
+ IA_PD = 640,
+ IAADDR = 641,
+ IAPREFIX = 642,
+ LEASE6 = 643,
+ PREFERRED_LIFE = 644,
+ MAX_LIFE = 645,
+ DEFAULT_DUID = 646,
+ SERVER_DUID = 647,
+ LLT = 648,
+ EN = 649,
+ LL = 650,
+ RANGE6 = 651,
+ WHITESPACE = 652,
+ TOKEN_ALSO = 653,
+ AFTER = 654,
+ ZEROLEN = 655,
+ TEMPORARY = 656,
+ PREFIX6 = 657,
+ FIXED_PREFIX6 = 658,
+ ANYCAST_MAC = 659,
+ CONFLICT_DONE = 660,
+ AUTO_PARTNER_DOWN = 661,
+ GETHOSTNAME = 662,
+ REWIND = 663,
+ INITIAL_DELAY = 664,
+ GETHOSTBYNAME = 665
+};
+
+#define is_identifier(x) ((x) >= FIRST_TOKEN && \
+ (x) != STRING && \
+ (x) != NUMBER && \
+ (x) != END_OF_FILE)
diff --git a/includes/failover.h b/includes/failover.h
new file mode 100644
index 0000000..1db0e68
--- /dev/null
+++ b/includes/failover.h
@@ -0,0 +1,400 @@
+/* failover.h
+
+ Definitions for address trees... */
+
+/*
+ * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2000-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#if defined (FAILOVER_PROTOCOL)
+struct failover_option_info {
+ int code;
+ const char *name;
+ enum { FT_UINT8, FT_IPADDR, FT_UINT32, FT_BYTES, FT_TEXT_OR_BYTES,
+ FT_DDNS, FT_DDNS1, FT_UINT16, FT_TEXT,
+ FT_UNDEF, FT_DIGEST } type;
+ int num_present;
+ int offset;
+ u_int32_t bit;
+};
+
+typedef struct {
+ unsigned count;
+ u_int8_t *data;
+} failover_option_t;
+
+/* Failover configuration defaults. */
+#ifndef DEFAULT_MAX_BALANCE_TIME
+# define DEFAULT_MAX_BALANCE_TIME 3600
+#endif
+
+#ifndef DEFAULT_MIN_BALANCE_TIME
+# define DEFAULT_MIN_BALANCE_TIME 60
+#endif
+
+#ifndef DEFAULT_MAX_LEASE_MISBALANCE
+# define DEFAULT_MAX_LEASE_MISBALANCE 15
+#endif
+
+#ifndef DEFAULT_MAX_LEASE_OWNERSHIP
+# define DEFAULT_MAX_LEASE_OWNERSHIP 10
+#endif
+
+#ifndef DEFAULT_MAX_FLYING_UPDATES
+# define DEFAULT_MAX_FLYING_UPDATES 100
+#endif
+
+#ifndef DEFAULT_MAX_RESPONSE_DELAY
+# define DEFAULT_MAX_RESPONSE_DELAY 20
+#endif
+
+/*
+ * IANA has assigned ports 647 ("dhcp-failover") and 847 ("dhcp-failover2").
+ * Of these, only port 647 is mentioned in the -12 draft revision. We're not
+ * sure if they are supposed to indicate primary and secondary? No matter,
+ * we'll stick to the -12 draft revision level.
+ */
+#ifndef DEFAULT_FAILOVER_PORT
+# define DEFAULT_FAILOVER_PORT 647
+#endif
+
+#define FM_OFFSET(x) (long)(&(((failover_message_t *)0) -> x))
+
+/* All of the below definitions are mandated by draft-ietf-dhc-failover-12.
+ * The Sections referenced are Sections within that document of that
+ * version, and may be different in other documents of other versions.
+ */
+
+/* Failover message options from Section 12: */
+#define FTO_ADDRESSES_TRANSFERRED 1
+#define FTB_ADDRESSES_TRANSFERRED 0x00000002
+#define FTO_ASSIGNED_IP_ADDRESS 2
+#define FTB_ASSIGNED_IP_ADDRESS 0x00000004
+#define FTO_BINDING_STATUS 3
+#define FTB_BINDING_STATUS 0x00000008
+#define FTO_CLIENT_IDENTIFIER 4
+#define FTB_CLIENT_IDENTIFIER 0x00000010
+#define FTO_CHADDR 5
+#define FTB_CHADDR 0x00000020
+#define FTO_CLTT 6
+#define FTB_CLTT 0x00000040
+#define FTO_REPLY_OPTIONS 7
+#define FTB_REPLY_OPTIONS 0x00000080
+#define FTO_REQUEST_OPTIONS 8
+#define FTB_REQUEST_OPTIONS 0x00000100
+#define FTO_DDNS 9
+#define FTB_DDNS 0x00000200
+#define FTO_DELAYED_SERVICE 10
+#define FTB_DELAYED_SERVICE 0x00000400
+#define FTO_HBA 11
+#define FTB_HBA 0x00000800
+#define FTO_IP_FLAGS 12
+#define FTB_IP_FLAGS 0x00001000
+#define FTO_LEASE_EXPIRY 13
+#define FTB_LEASE_EXPIRY 0x00002000
+#define FTO_MAX_UNACKED 14
+#define FTB_MAX_UNACKED 0x00004000
+#define FTO_MCLT 15
+#define FTB_MCLT 0x00008000
+#define FTO_MESSAGE 16
+#define FTB_MESSAGE 0x00010000
+#define FTO_MESSAGE_DIGEST 17
+#define FTB_MESSAGE_DIGEST 0x00020000
+#define FTO_POTENTIAL_EXPIRY 18
+#define FTB_POTENTIAL_EXPIRY 0x00040000
+#define FTO_RECEIVE_TIMER 19
+#define FTB_RECEIVE_TIMER 0x00080000
+#define FTO_PROTOCOL_VERSION 20
+#define FTB_PROTOCOL_VERSION 0x00100000
+#define FTO_REJECT_REASON 21
+#define FTB_REJECT_REASON 0x00200000
+#define FTO_RELATIONSHIP_NAME 22
+#define FTB_RELATIONSHIP_NAME 0x00400000
+#define FTO_SERVER_FLAGS 23
+#define FTB_SERVER_FLAGS 0x00800000
+#define FTO_SERVER_STATE 24
+#define FTB_SERVER_STATE 0x01000000
+#define FTO_STOS 25
+#define FTB_STOS 0x02000000
+#define FTO_TLS_REPLY 26
+#define FTB_TLS_REPLY 0x04000000
+#define FTO_TLS_REQUEST 27
+#define FTB_TLS_REQUEST 0x08000000
+#define FTO_VENDOR_CLASS 28
+#define FTB_VENDOR_CLASS 0x10000000
+#define FTO_VENDOR_OPTIONS 29
+#define FTB_VENDOR_OPTIONS 0x20000000
+
+#define FTO_MAX FTO_VENDOR_OPTIONS
+
+/* Failover protocol message types from Section 6.1: */
+#define FTM_POOLREQ 1
+#define FTM_POOLRESP 2
+#define FTM_BNDUPD 3
+#define FTM_BNDACK 4
+#define FTM_CONNECT 5
+#define FTM_CONNECTACK 6
+#define FTM_UPDREQALL 7
+#define FTM_UPDDONE 8
+#define FTM_UPDREQ 9
+#define FTM_STATE 10
+#define FTM_CONTACT 11
+#define FTM_DISCONNECT 12
+
+/* Reject reasons from Section 12.21: */
+#define FTR_ILLEGAL_IP_ADDR 1
+#define FTR_FATAL_CONFLICT 2
+#define FTR_MISSING_BINDINFO 3
+#define FTR_TIMEMISMATCH 4
+#define FTR_INVALID_MCLT 5
+#define FTR_MISC_REJECT 6
+#define FTR_DUP_CONNECTION 7
+#define FTR_INVALID_PARTNER 8
+#define FTR_TLS_UNSUPPORTED 9
+#define FTR_TLS_UNCONFIGURED 10
+#define FTR_TLS_REQUIRED 11
+#define FTR_DIGEST_UNSUPPORTED 12
+#define FTR_DIGEST_UNCONFIGURED 13
+#define FTR_VERSION_MISMATCH 14
+#define FTR_OUTDATED_BIND_INFO 15
+#define FTR_LESS_CRIT_BIND_INFO 16
+#define FTR_NO_TRAFFIC 17
+#define FTR_HBA_CONFLICT 18
+#define FTR_IP_NOT_RESERVED 19
+#define FTR_IP_DIGEST_FAILURE 20
+#define FTR_IP_MISSING_DIGEST 21
+#define FTR_UNKNOWN 254
+
+/* Message size limitations defined in Section 6.1: */
+#define DHCP_FAILOVER_MIN_MESSAGE_SIZE 12
+#define DHCP_FAILOVER_MAX_MESSAGE_SIZE 2048
+
+/* Failover server flags from Section 12.23: */
+#define FTF_SERVER_STARTUP 1
+
+/* DDNS flags from Section 12.9. These are really their names. */
+#define FTF_DDNS_C 0x0001
+#define FTF_DDNS_A 0x0002
+#define FTF_DDNS_D 0x0004
+#define FTF_DDNS_P 0x0008
+
+/* FTO_IP_FLAGS contents from Section 12.12: */
+#define FTF_IP_FLAG_RESERVE 0x0001
+#define FTF_IP_FLAG_BOOTP 0x0002
+
+/* FTO_MESSAGE_DIGEST Type Codes from Section 12.17: */
+#define FTT_MESSAGE_DIGEST_HMAC_MD5 0x01
+
+typedef struct failover_message {
+ int refcnt;
+ struct failover_message *next;
+
+ int options_present;
+
+ u_int32_t time;
+ u_int32_t xid;
+ u_int8_t type;
+
+ /* One-byte options. */
+ u_int8_t binding_status;
+ u_int8_t delayed_service;
+ u_int8_t protocol_version;
+ u_int8_t reject_reason;
+ u_int8_t server_flags;
+ u_int8_t server_state;
+ u_int8_t tls_reply;
+ u_int8_t tls_request;
+
+ /* Two-byte options. */
+ u_int16_t ip_flags;
+
+ /* Four-byte options. */
+ u_int32_t addresses_transferred;
+ u_int32_t assigned_addr;
+ u_int32_t cltt;
+ u_int32_t expiry;
+ u_int32_t max_unacked;
+ u_int32_t mclt;
+ u_int32_t potential_expiry;
+ u_int32_t receive_timer;
+ u_int32_t stos;
+
+ /* Arbitrary field options. */
+ failover_option_t chaddr;
+ failover_option_t client_identifier;
+ failover_option_t hba;
+ failover_option_t message;
+ failover_option_t message_digest;
+ failover_option_t relationship_name;
+ failover_option_t reply_options;
+ failover_option_t request_options;
+ failover_option_t vendor_class;
+ failover_option_t vendor_options;
+
+ /* Special contents options. */
+ ddns_fqdn_t ddns;
+} failover_message_t;
+
+typedef struct {
+ OMAPI_OBJECT_PREAMBLE;
+ struct option_cache *peer_address;
+ unsigned peer_port;
+ int options_present;
+ enum dhcp_flink_state {
+ dhcp_flink_start,
+ dhcp_flink_message_length_wait,
+ dhcp_flink_message_wait,
+ dhcp_flink_disconnected,
+ dhcp_flink_state_max
+ } state;
+ failover_message_t *imsg;
+ struct _dhcp_failover_state *state_object;
+ u_int16_t imsg_len;
+ unsigned imsg_count;
+ u_int8_t imsg_payoff; /* Pay*load* offset. :') */
+ u_int32_t xid;
+} dhcp_failover_link_t;
+
+typedef struct _dhcp_failover_listener {
+ OMAPI_OBJECT_PREAMBLE;
+ struct _dhcp_failover_listener *next;
+ omapi_addr_t address;
+} dhcp_failover_listener_t;
+#endif /* FAILOVER_PROTOCOL */
+
+/* A failover peer's running state. */
+enum failover_state {
+ unknown_state = 0, /* XXX: Not a standard state. */
+ startup = 1,
+ normal = 2,
+ communications_interrupted = 3,
+ partner_down = 4,
+ potential_conflict = 5,
+ recover = 6,
+ paused = 7,
+ shut_down = 8,
+ recover_done = 9,
+ resolution_interrupted = 10,
+ conflict_done = 11,
+
+ /* Draft revision 12 of the failover protocol documents a RECOVER-WAIT
+ * state, but does not enumerate its value in the section 12.24
+ * table. ISC DHCP 3.0.x used value 254 even though the state was
+ * not documented at all. For the time being, we will continue to use
+ * this value.
+ */
+ recover_wait = 254
+};
+
+/* Service states are simplifications of failover states, particularly
+ useful because the startup state isn't actually implementable as a
+ separate failover state without maintaining a state stack. */
+
+enum service_state {
+ unknown_service_state,
+ cooperating,
+ not_cooperating,
+ service_partner_down,
+ not_responding,
+ service_startup
+};
+
+#if defined (FAILOVER_PROTOCOL)
+typedef struct _dhcp_failover_config {
+ struct option_cache *address;
+ int port;
+ u_int32_t max_flying_updates;
+ enum failover_state state;
+ TIME stos;
+ u_int32_t max_response_delay;
+} dhcp_failover_config_t;
+
+typedef struct _dhcp_failover_state {
+ OMAPI_OBJECT_PREAMBLE;
+ struct _dhcp_failover_state *next;
+ char *name; /* Name of this failover instance. */
+ dhcp_failover_config_t me; /* My configuration. */
+ dhcp_failover_config_t partner; /* Partner's configuration. */
+ enum failover_state saved_state; /* Saved state during startup. */
+ struct data_string server_identifier; /* Server identifier (IP addr) */
+ u_int32_t mclt;
+
+ u_int8_t *hba; /* Hash bucket array for load balancing. */
+ int load_balance_max_secs;
+
+ u_int32_t max_lease_misbalance, max_lease_ownership;
+ u_int32_t max_balance, min_balance;
+ TIME last_balance, sched_balance;
+
+ u_int32_t auto_partner_down;
+
+ enum service_state service_state;
+ const char *nrr; /* Printable reason why we're in the
+ not_responding service state (empty
+ string if we are responding. */
+
+ dhcp_failover_link_t *link_to_peer; /* Currently-established link
+ to peer. */
+
+ enum {
+ primary, secondary
+ } i_am; /* We are primary or secondary in this relationship. */
+
+ TIME last_packet_sent; /* Timestamp on last packet we sent. */
+ TIME last_timestamp_received; /* The last timestamp we sent that
+ has been returned by our partner. */
+ TIME skew; /* The skew between our clock and our partner's. */
+ struct lease *update_queue_head; /* List of leases we haven't sent
+ to peer. */
+ struct lease *update_queue_tail;
+
+ struct lease *ack_queue_head; /* List of lease updates the peer
+ hasn't yet acked. */
+ struct lease *ack_queue_tail;
+
+ struct lease *send_update_done; /* When we get a BNDACK for this
+ lease, send an UPDDONE message. */
+ int cur_unacked_updates; /* Number of updates we've sent
+ that have not yet been acked. */
+
+ /* List of messages which we haven't
+ acked yet. */
+ failover_message_t *toack_queue_head;
+ failover_message_t *toack_queue_tail;
+ int pending_acks; /* Number of messages in the toack
+ queue. */
+ int pool_count; /* Number of pools referencing this
+ failover state object. */
+ int curUPD; /* If an UPDREQ* message is in motion,
+ this value indicates which one. */
+ u_int32_t updxid; /* XID of UPDREQ* message in action. */
+} dhcp_failover_state_t;
+
+#define DHCP_FAILOVER_VERSION 1
+#endif /* FAILOVER_PROTOCOL */
diff --git a/includes/heap.h b/includes/heap.h
new file mode 100644
index 0000000..e5e5819
--- /dev/null
+++ b/includes/heap.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2001 Internet Software Consortium.
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: heap.h,v 1.3 2007-05-19 19:16:25 dhankins Exp $ */
+
+#ifndef ISC_HEAP_H
+#define ISC_HEAP_H 1
+
+/*! \file isc/heap.h */
+
+/*%
+ * The comparision function returns ISC_TRUE if the first argument has
+ * higher priority than the second argument, and ISC_FALSE otherwise.
+ */
+typedef isc_boolean_t (*isc_heapcompare_t)(void *, void *);
+
+/*%
+ * The index function allows the client of the heap to receive a callback
+ * when an item's index number changes. This allows it to maintain
+ * sync with its external state, but still delete itself, since deletions
+ * from the heap require the index be provided.
+ */
+typedef void (*isc_heapindex_t)(void *, unsigned int);
+
+/*%
+ * The heapaction function is used when iterating over the heap.
+ *
+ * NOTE: The heap structure CANNOT BE MODIFIED during the call to
+ * isc_heap_foreach().
+ */
+typedef void (*isc_heapaction_t)(void *, void *);
+
+typedef struct isc_heap isc_heap_t;
+
+isc_result_t
+isc_heap_create(isc_heapcompare_t compare,
+ isc_heapindex_t index, unsigned int size_increment,
+ isc_heap_t **heapp);
+/*!<
+ * \brief Create a new heap. The heap is implemented using a space-efficient
+ * storage method. When the heap elements are deleted space is not freed
+ * but will be reused when new elements are inserted.
+ *
+ * Requires:
+ *\li "mctx" is valid.
+ *\li "compare" is a function which takes two void * arguments and
+ * returns ISC_TRUE if the first argument has a higher priority than
+ * the second, and ISC_FALSE otherwise.
+ *\li "index" is a function which takes a void *, and an unsigned int
+ * argument. This function will be called whenever an element's
+ * index value changes, so it may continue to delete itself from the
+ * heap. This option may be NULL if this functionality is unneeded.
+ *\li "size_increment" is a hint about how large the heap should grow
+ * when resizing is needed. If this is 0, a default size will be
+ * used, which is currently 1024, allowing space for an additional 1024
+ * heap elements to be inserted before adding more space.
+ *\li "heapp" is not NULL, and "*heap" is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS - success
+ *\li ISC_R_NOMEMORY - insufficient memory
+ */
+
+void
+isc_heap_destroy(isc_heap_t **heapp);
+/*!<
+ * \brief Destroys a heap.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ */
+
+isc_result_t
+isc_heap_insert(isc_heap_t *heap, void *elt);
+/*!<
+ * \brief Inserts a new element into a heap.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ */
+
+void
+isc_heap_delete(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Deletes an element from a heap, by element index.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void
+isc_heap_increased(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Indicates to the heap that an element's priority has increased.
+ * This function MUST be called whenever an element has increased in priority.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void
+isc_heap_decreased(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Indicates to the heap that an element's priority has decreased.
+ * This function MUST be called whenever an element has decreased in priority.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ */
+
+void *
+isc_heap_element(isc_heap_t *heap, unsigned int index);
+/*!<
+ * \brief Returns the element for a specific element index.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "index" is a valid element index, as provided by the "index" callback
+ * provided during heap creation.
+ *
+ * Returns:
+ *\li A pointer to the element for the element index.
+ */
+
+void
+isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap);
+/*!<
+ * \brief Iterate over the heap, calling an action for each element. The
+ * order of iteration is not sorted.
+ *
+ * Requires:
+ *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t.
+ *\li "action" is not NULL, and is a function which takes two arguments.
+ * The first is a void *, representing the element, and the second is
+ * "uap" as provided to isc_heap_foreach.
+ *\li "uap" is a caller-provided argument, and may be NULL.
+ *
+ * Note:
+ *\li The heap structure CANNOT be modified during this iteration. The only
+ * safe function to call while iterating the heap is isc_heap_element().
+ */
+
+#endif /* ISC_HEAP_H */
diff --git a/includes/inet.h b/includes/inet.h
new file mode 100644
index 0000000..a59099d
--- /dev/null
+++ b/includes/inet.h
@@ -0,0 +1,85 @@
+/* inet.h
+
+ Portable definitions for internet addresses */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* An internet address of up to 128 bits. */
+
+struct iaddr {
+ unsigned len;
+ unsigned char iabuf [16];
+};
+
+struct iaddrlist {
+ struct iaddrlist *next;
+ struct iaddr addr;
+};
+
+
+/* struct iaddrmatch - used to compare a host IP against a subnet spec
+ *
+ * There is a space/speed tradeoff here implied by the use of a second
+ * struct iaddr to hold the mask; while using an unsigned (byte!) to
+ * represent the subnet prefix length would be more memory efficient,
+ * it makes run-time mask comparisons more expensive. Since such
+ * entries are used currently only in restricted circumstances
+ * (wanting to reject a subnet), the decision is in favour of run-time
+ * efficiency.
+ */
+
+struct iaddrmatch {
+ struct iaddr addr;
+ struct iaddr mask;
+};
+
+/* its list ... */
+
+struct iaddrmatchlist {
+ struct iaddrmatchlist *next;
+ struct iaddrmatch match;
+};
+
+
+/*
+ * Structure to store information about a CIDR network.
+ */
+
+struct iaddrcidrnet {
+ struct iaddr lo_addr;
+ int bits;
+};
+
+struct iaddrcidrnetlist {
+ struct iaddrcidrnetlist *next;
+ struct iaddrcidrnet cidrnet;
+};
+
diff --git a/includes/isc-dhcp/dst.h b/includes/isc-dhcp/dst.h
new file mode 100644
index 0000000..65c54d2
--- /dev/null
+++ b/includes/isc-dhcp/dst.h
@@ -0,0 +1,142 @@
+#ifndef DST_H
+#define DST_H
+
+#ifndef HAS_DST_KEY
+typedef struct dst_key {
+ char *dk_key_name; /* name of the key */
+ int dk_key_size; /* this is the size of the key in bits */
+ int dk_proto; /* what protocols this key can be used for */
+ int dk_alg; /* algorithm number from key record */
+ unsigned dk_flags; /* and the flags of the public key */
+ unsigned dk_id; /* identifier of the key */
+} DST_KEY;
+#endif /* HAS_DST_KEY */
+
+/*
+ * DST Crypto API defintions
+ */
+void dst_init(void);
+int dst_check_algorithm(const int);
+
+int dst_sign_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */
+ DST_KEY *in_key, /* the key to use */
+ void **context, /* pointer to state structure */
+ const u_char *data, /* data to be signed */
+ const unsigned len, /* length of input data */
+ u_char *signature, /* buffer to write signature to */
+ const unsigned sig_len); /* size of output buffer */
+
+int dst_verify_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */
+ DST_KEY *in_key, /* the key to use */
+ void **context, /* pointer to state structure */
+ const u_char *data, /* data to be verified */
+ const unsigned len, /* length of input data */
+ const u_char *signature,/* buffer containing signature */
+ const unsigned sig_len); /* length of signature */
+
+
+DST_KEY *dst_read_key(const char *in_name, /* name of key */
+ const unsigned in_id, /* key tag identifier */
+ const int in_alg, /* key algorithm */
+ const int key_type); /* Private/PublicKey wanted*/
+
+int dst_write_key(const DST_KEY *key, /* key to write out */
+ const int key_type); /* Public/Private */
+
+DST_KEY *dst_dnskey_to_key(const char *in_name, /* KEY record name */
+ const u_char *key, /* KEY RDATA */
+ const unsigned len); /* size of input buffer*/
+
+
+int dst_key_to_dnskey(const DST_KEY *key, /* key to translate */
+ u_char *out_storage, /* output buffer */
+ const unsigned out_len); /* size of out_storage*/
+
+
+DST_KEY *dst_buffer_to_key(const char *key_name, /* name of the key */
+ const int alg, /* algorithm */
+ const unsigned flags, /* dns flags */
+ const int protocol, /* dns protocol */
+ const u_char *key_buf, /* key in dns wire fmt */
+ const unsigned key_len); /* size of key */
+
+
+int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, unsigned buf_len);
+
+DST_KEY *dst_generate_key(const char *name, /* name of new key */
+ const int bits, /* size of new key */
+ const int exp, /* alg dependent parameter*/
+ const unsigned flags, /* key DNS flags */
+ const int protocol, /* key DNS protocol */
+ const int alg); /* key algorithm to generate */
+
+DST_KEY *dst_free_key(DST_KEY *f_key);
+int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2);
+
+int dst_sig_size(DST_KEY *key);
+
+int dst_random(const int mode, unsigned wanted, u_char *outran);
+
+
+/* support for dns key tags/ids */
+u_int16_t dst_s_dns_key_id(const u_char *dns_key_rdata,
+ const unsigned rdata_len);
+u_int16_t dst_s_id_calc(const u_char *key_data, const unsigned key_len);
+
+/* Used by callers as well as by the library. */
+#define RAW_KEY_SIZE 8192 /* large enough to store any key */
+
+/* DST_API control flags */
+/* These are used used in functions dst_sign_data and dst_verify_data */
+#define SIG_MODE_INIT 1 /* initalize digest */
+#define SIG_MODE_UPDATE 2 /* add data to digest */
+#define SIG_MODE_FINAL 4 /* generate/verify signature */
+#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL)
+
+/* Flags for dst_read_private_key() */
+#define DST_FORCE_READ 0x1000000
+#define DST_CAN_SIGN 0x010F
+#define DST_NO_AUTHEN 0x8000
+#define DST_EXTEND_FLAG 0x1000
+#define DST_STANDARD 0
+#define DST_PRIVATE 0x2000000
+#define DST_PUBLIC 0x4000000
+#define DST_RAND_SEMI 1
+#define DST_RAND_STD 2
+#define DST_RAND_KEY 3
+#define DST_RAND_DSS 4
+
+
+/* DST algorithm codes */
+#define KEY_RSA 1
+#define KEY_DH 2
+#define KEY_DSA 3
+#define KEY_PRIVATE 254
+#define KEY_EXPAND 255
+#define KEY_HMAC_MD5 157
+#define KEY_HMAC_SHA1 158
+#define UNKNOWN_KEYALG 0
+#define DST_MAX_ALGS KEY_HMAC_SHA1
+
+/* DST constants to locations in KEY record changes in new KEY record */
+#define DST_FLAGS_SIZE 2
+#define DST_KEY_PROT 2
+#define DST_KEY_ALG 3
+#define DST_EXT_FLAG 4
+#define DST_KEY_START 4
+
+#ifndef SIGN_F_NOKEY
+#define SIGN_F_NOKEY 0xC000
+#endif
+
+/* error codes from dst routines */
+#define SIGN_INIT_FAILURE (-23)
+#define SIGN_UPDATE_FAILURE (-24)
+#define SIGN_FINAL_FAILURE (-25)
+#define VERIFY_INIT_FAILURE (-26)
+#define VERIFY_UPDATE_FAILURE (-27)
+#define VERIFY_FINAL_FAILURE (-28)
+#define MISSING_KEY_OR_SIGNATURE (-30)
+#define UNSUPPORTED_KEYALG (-31)
+
+#endif /* DST_H */
diff --git a/includes/minires.h b/includes/minires.h
new file mode 100644
index 0000000..3555bbb
--- /dev/null
+++ b/includes/minires.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ */
+#ifndef MINIRES_H
+#define MINIRES_H
+
+#include "cdefs.h"
+#include "osdep.h"
+
+/*
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * <viraj_bais@ccm.fm.intel.com>
+ */
+
+int MRns_name_compress(const char *, u_char *, size_t, const unsigned char **,
+ const unsigned char **);
+int MRns_name_unpack(const unsigned char *, const unsigned char *,
+ const unsigned char *, unsigned char *, size_t);
+int MRns_name_pack (const unsigned char *, unsigned char *,
+ unsigned, const unsigned char **, const unsigned char **);
+int MRns_name_ntop(const unsigned char *, char *, size_t);
+int MRns_name_pton(const char *, u_char *, size_t);
+
+#endif /* MINIRES_H */
diff --git a/includes/netinet/if_ether.h b/includes/netinet/if_ether.h
new file mode 100644
index 0000000..8de8023
--- /dev/null
+++ b/includes/netinet/if_ether.h
@@ -0,0 +1,66 @@
+/* $NetBSD: if_ether.h,v 1.20 1995/06/12 00:47:27 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)if_ether.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef netinet_if_ether_h
+#define netinet_if_ether_h
+
+/*
+ * Ethernet address - 6 octets
+ * this is only used by the ethers(3) functions.
+ */
+struct ether_addr {
+ u_int8_t ether_addr_octet[6];
+};
+
+/*
+ * Structure of a 10Mb/s Ethernet header.
+ */
+#define ETHER_ADDR_LEN 6
+
+struct isc_ether_header {
+ u_int8_t ether_dhost[ETHER_ADDR_LEN];
+ u_int8_t ether_shost[ETHER_ADDR_LEN];
+ u_int16_t ether_type;
+};
+
+#define ETHERTYPE_PUP 0x0200 /* PUP protocol */
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#define ETHERTYPE_ARP 0x0806 /* address resolution protocol */
+
+#define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof (u_int16_t))
+
+#endif
diff --git a/includes/netinet/ip.h b/includes/netinet/ip.h
new file mode 100644
index 0000000..0a1e358
--- /dev/null
+++ b/includes/netinet/ip.h
@@ -0,0 +1,163 @@
+/* $NetBSD: ip.h,v 1.9 1995/05/15 01:22:44 cgd Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ *
+ * We declare ip_len and ip_off to be short, rather than u_short
+ * pragmatically since otherwise unsigned comparisons can result
+ * against negative integers quite easily, and fail in subtle ways.
+ */
+struct ip {
+ u_int8_t ip_fvhl; /* header length, version */
+ u_int8_t ip_tos; /* type of service */
+ int16_t ip_len; /* total length */
+ u_int16_t ip_id; /* identification */
+ int16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* dont fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ u_int8_t ip_ttl; /* time to live */
+ u_int8_t ip_p; /* protocol */
+ u_int16_t ip_sum; /* checksum */
+ struct in_addr ip_src, ip_dst; /* source and dest address */
+};
+
+#define IP_V(iph) ((iph)->ip_fvhl >> 4)
+#define IP_HL(iph) (((iph)->ip_fvhl & 0x0F) << 2)
+#define IP_V_SET(iph,x) ((iph)->ip_fvhl = ((iph)->ip_fvhl & 0x0F) | ((x) << 4))
+#define IP_HL_SET(iph,x) ((iph)->ip_fvhl = \
+ ((iph)->ip_fvhl & 0xF0) | (((x) >> 2) & 0x0F))
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+/* IPTOS_LOWCOST 0x02 XXX */
+
+/*
+ * Definitions for IP precedence (also in ip_tos) (hopefully unused)
+ */
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ u_int8_t ipt_code; /* IPOPT_TS */
+ u_int8_t ipt_len; /* size of structure (variable) */
+ u_int8_t ipt_ptr; /* index of current entry */
+ u_int8_t ipt_flg_oflw; /* flags, see below, overflow counter */
+ union ipt_timestamp {
+ u_int32_t ipt_time[1];
+ struct ipt_ta {
+ struct in_addr ipt_addr;
+ u_int32_t ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
diff --git a/includes/netinet/ip_icmp.h b/includes/netinet/ip_icmp.h
new file mode 100644
index 0000000..8fffb58
--- /dev/null
+++ b/includes/netinet/ip_icmp.h
@@ -0,0 +1,182 @@
+/* $NetBSD: ip_icmp.h,v 1.11 1996/08/03 15:48:18 neil Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ */
+
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+/*
+ * Internal of an ICMP Router Advertisement
+ */
+struct icmp_ra_addr {
+ u_int32_t ira_addr;
+ u_int32_t ira_preference;
+};
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ u_int8_t icmp_type; /* type of message, see below */
+ u_int8_t icmp_code; /* type sub code */
+ u_int16_t icmp_cksum; /* ones complement cksum of struct */
+ union {
+ u_int8_t ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ int16_t icd_id;
+ int16_t icd_seq;
+ } ih_idseq;
+ int32_t ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu {
+ int16_t ipm_void;
+ int16_t ipm_nextmtu;
+ } ih_pmtu;
+ struct ih_rtradv {
+ u_int8_t irt_num_addrs;
+ u_int8_t irt_wpa;
+ u_int16_t irt_lifetime;
+ } ih_rtradv;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+#define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs
+#define icmp_wpa icmp_hun.ih_rtradv.irt_wpa
+#define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime
+ union {
+ struct id_ts {
+ u_int32_t its_otime;
+ u_int32_t its_rtime;
+ u_int32_t its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ struct icmp_ra_addr id_radv;
+ u_int32_t id_mask;
+ int8_t id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_radv icmp_dun.id_mask
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (u_int32_t)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+#ifdef _KERNEL
+void icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *));
+void icmp_input __P((struct mbuf *, ...));
+void icmp_reflect __P((struct mbuf *));
+void icmp_send __P((struct mbuf *, struct mbuf *));
+int icmp_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+#endif
+
diff --git a/includes/netinet/udp.h b/includes/netinet/udp.h
new file mode 100644
index 0000000..5f1cd7d
--- /dev/null
+++ b/includes/netinet/udp.h
@@ -0,0 +1,70 @@
+/* $NetBSD: udp.h,v 1.6 1995/04/13 06:37:10 cgd Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Portions Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 2000-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ u_int16_t uh_sport; /* source port */
+ u_int16_t uh_dport; /* destination port */
+ u_int16_t uh_ulen; /* udp length */
+ u_int16_t uh_sum; /* udp checksum */
+};
diff --git a/includes/omapip/alloc.h b/includes/omapip/alloc.h
new file mode 100644
index 0000000..b9e6b85
--- /dev/null
+++ b/includes/omapip/alloc.h
@@ -0,0 +1,111 @@
+/* alloc.h
+
+ Definitions for the object management API protocol memory allocation... */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+isc_result_t omapi_buffer_new (omapi_buffer_t **, const char *, int);
+isc_result_t omapi_buffer_reference (omapi_buffer_t **,
+ omapi_buffer_t *, const char *, int);
+isc_result_t omapi_buffer_dereference (omapi_buffer_t **, const char *, int);
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+#define DMDOFFSET (sizeof (struct dmalloc_preamble))
+#define DMLFSIZE 16
+#define DMUFSIZE 16
+#define DMDSIZE (DMDOFFSET + DMLFSIZE + DMUFSIZE)
+
+struct dmalloc_preamble {
+ struct dmalloc_preamble *prev, *next;
+ const char *file;
+ int line;
+ size_t size;
+ unsigned long generation;
+ unsigned char low_fence [DMLFSIZE];
+};
+#else
+#define DMDOFFSET 0
+#define DMDSIZE 0
+#endif
+
+/* rc_history flags... */
+#define RC_LEASE 1
+#define RC_MISC 2
+
+#if defined (DEBUG_RC_HISTORY)
+#if !defined (RC_HISTORY_MAX)
+# define RC_HISTORY_MAX 256
+#endif
+
+#if !defined (RC_HISTORY_FLAGS)
+# define RC_HISTORY_FLAGS (RC_LEASE | RC_MISC)
+#endif
+
+struct rc_history_entry {
+ const char *file;
+ int line;
+ void *reference;
+ void *addr;
+ int refcnt;
+};
+
+#define rc_register(x, l, r, y, z, d, f) do { \
+ if (RC_HISTORY_FLAGS & ~(f)) { \
+ rc_history [rc_history_index].file = (x); \
+ rc_history [rc_history_index].line = (l); \
+ rc_history [rc_history_index].reference = (r); \
+ rc_history [rc_history_index].addr = (y); \
+ rc_history [rc_history_index].refcnt = (z); \
+ rc_history_next (d); \
+ } \
+ } while (0)
+#define rc_register_mdl(r, y, z, d, f) \
+ rc_register (__FILE__, __LINE__, r, y, z, d, f)
+#else
+#define rc_register(file, line, reference, addr, refcnt, d, f)
+#define rc_register_mdl(reference, addr, refcnt, d, f)
+#endif
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+extern struct dmalloc_preamble *dmalloc_list;
+extern unsigned long dmalloc_outstanding;
+extern unsigned long dmalloc_longterm;
+extern unsigned long dmalloc_generation;
+extern unsigned long dmalloc_cutoff_generation;
+#endif
+
+#if defined (DEBUG_RC_HISTORY)
+extern struct rc_history_entry rc_history [RC_HISTORY_MAX];
+extern int rc_history_index;
+extern int rc_history_count;
+#endif
diff --git a/includes/omapip/buffer.h b/includes/omapip/buffer.h
new file mode 100644
index 0000000..ada82e0
--- /dev/null
+++ b/includes/omapip/buffer.h
@@ -0,0 +1,83 @@
+/* buffer.h
+
+ Definitions for the object management API protocol buffering... */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* OMAPI buffers are ring buffers, which means that the beginning of the
+ buffer and the end of the buffer chase each other around. As long as
+ the tail never catches up to the head, there's room in the buffer for
+ data.
+
+ - If the tail and the head are equal, the buffer is empty.
+
+ - If the tail is less than the head, the contents of the buffer
+ are the bytes from the head to the end of buffer, and in addition,
+ the bytes between the beginning of the buffer and the tail, not
+ including the byte addressed by the tail.
+
+ - If the tail is greater than the head, then the buffer contains
+ valid bytes starting with the byte addressed by the head, and
+ ending with the byte before the byte addressed by the tail.
+
+ There will always be at least one byte of waste, because the tail can't
+ increase so that it's equal to the head (that would represent an empty
+ buffer. */
+#define OMAPI_BUF_SIZE 4048
+typedef struct _omapi_buffer {
+ struct _omapi_buffer *next; /* Buffers can be chained. */
+ u_int32_t refcnt; /* Buffers are reference counted. */
+ u_int16_t head, tail; /* Buffers are organized in a ring. */
+ char buf [OMAPI_BUF_SIZE]; /* The actual buffer is included in
+ the buffer data structure. */
+} omapi_buffer_t;
+
+#define BUFFER_BYTES_FREE(x) \
+ ((x) -> tail > (x) -> head \
+ ? sizeof ((x) -> buf) - ((x) -> tail - (x) -> head) \
+ : (x) -> head - (x) -> tail)
+
+#define BYTES_IN_BUFFER(x) \
+ ((x) -> tail > (x) -> head \
+ ? (x) -> tail - (x) -> head - 1 \
+ : sizeof ((x) -> buf) - ((x) -> head - (x) -> tail) - 1)
+
+isc_result_t omapi_connection_require (omapi_object_t *, unsigned);
+isc_result_t omapi_connection_copyout (unsigned char *,
+ omapi_object_t *, unsigned);
+isc_result_t omapi_connection_copyin (omapi_object_t *,
+ const unsigned char *, unsigned);
+isc_result_t omapi_connection_flush (omapi_object_t *);
+isc_result_t omapi_connection_get_uint32 (omapi_object_t *, u_int32_t *);
+isc_result_t omapi_connection_put_uint32 (omapi_object_t *, u_int32_t);
+isc_result_t omapi_connection_get_uint16 (omapi_object_t *, u_int16_t *);
+isc_result_t omapi_connection_put_uint16 (omapi_object_t *, u_int32_t);
+
diff --git a/includes/omapip/convert.h b/includes/omapip/convert.h
new file mode 100644
index 0000000..73b3ca3
--- /dev/null
+++ b/includes/omapip/convert.h
@@ -0,0 +1,52 @@
+/* convert.h
+
+ Safe copying of integers into and out of a non-aligned memory buffer. */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef OMAPI_CONVERT_H
+#define OMAPI_CONVERT_H
+
+u_int32_t getULong (const unsigned char *);
+int32_t getLong (const unsigned char *);
+u_int32_t getUShort (const unsigned char *);
+int32_t getShort (const unsigned char *);
+u_int32_t getUChar (const unsigned char *);
+void putULong (unsigned char *, u_int32_t);
+void putLong (unsigned char *, int32_t);
+void putUShort (unsigned char *, u_int32_t);
+void putShort (unsigned char *, int32_t);
+void putUChar (unsigned char *, u_int32_t);
+int converted_length (const unsigned char *, unsigned int, unsigned int);
+int binary_to_ascii (unsigned char *, const unsigned char *,
+ unsigned int, unsigned int);
+
+#endif /* OMAPI_CONVERT_H */
diff --git a/includes/omapip/hash.h b/includes/omapip/hash.h
new file mode 100644
index 0000000..9914561
--- /dev/null
+++ b/includes/omapip/hash.h
@@ -0,0 +1,167 @@
+/* hash.h
+
+ Definitions for hashing... */
+
+/*
+ * Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef OMAPI_HASH_H
+#define OMAPI_HASH_H
+
+#if !defined (DEFAULT_HASH_SIZE)
+# define DEFAULT_HASH_SIZE 9973
+#endif
+
+#if !defined (KEY_HASH_SIZE)
+# define KEY_HASH_SIZE 1009
+#endif
+
+/* The purpose of the hashed_object_t struct is to not match anything else. */
+typedef struct {
+ int foo;
+} hashed_object_t;
+
+typedef isc_result_t (*hash_foreach_func)(const void *, unsigned, void *);
+typedef int (*hash_reference) (hashed_object_t **, hashed_object_t *,
+ const char *, int);
+typedef int (*hash_dereference) (hashed_object_t **, const char *, int);
+
+struct hash_bucket {
+ struct hash_bucket *next;
+ const unsigned char *name;
+ unsigned len;
+ hashed_object_t *value;
+};
+
+typedef int (*hash_comparator_t)(const void *, const void *, size_t);
+
+struct hash_table {
+ unsigned hash_count;
+ hash_reference referencer;
+ hash_dereference dereferencer;
+ hash_comparator_t cmp;
+ unsigned (*do_hash)(const void *, unsigned, unsigned);
+
+ /* This must remain the last entry in this table. */
+ struct hash_bucket *buckets [1];
+};
+
+struct named_hash {
+ struct named_hash *next;
+ const char *name;
+ struct hash_table *hash;
+};
+
+#define HASH_FUNCTIONS_DECL(name, bufarg, type, hashtype) \
+void name##_hash_add (hashtype *, bufarg, unsigned, type *, \
+ const char *, int); \
+void name##_hash_delete (hashtype *, bufarg, unsigned, \
+ const char *, int); \
+int name##_hash_lookup (type **, hashtype *, bufarg, unsigned, \
+ const char *, int); \
+unsigned char * name##_hash_report(hashtype *); \
+int name##_hash_foreach (hashtype *, hash_foreach_func); \
+int name##_new_hash (hashtype **, unsigned, const char *, int); \
+void name##_free_hash_table (hashtype **, const char *, int);
+
+
+#define HASH_FUNCTIONS(name, bufarg, type, hashtype, ref, deref, hasher) \
+void name##_hash_add (hashtype *table, \
+ bufarg buf, unsigned len, type *ptr, \
+ const char *file, int line) \
+{ \
+ add_hash ((struct hash_table *)table, buf, \
+ len, (hashed_object_t *)ptr, file, line); \
+} \
+ \
+void name##_hash_delete (hashtype *table, bufarg buf, unsigned len, \
+ const char *file, int line) \
+{ \
+ delete_hash_entry ((struct hash_table *)table, buf, len, \
+ file, line); \
+} \
+ \
+int name##_hash_lookup (type **ptr, hashtype *table, \
+ bufarg buf, unsigned len, const char *file, int line) \
+{ \
+ return hash_lookup ((hashed_object_t **)ptr, \
+ (struct hash_table *)table, \
+ buf, len, file, line); \
+} \
+ \
+unsigned char * name##_hash_report(hashtype *table) \
+{ \
+ return hash_report((struct hash_table *)table); \
+} \
+ \
+int name##_hash_foreach (hashtype *table, hash_foreach_func func) \
+{ \
+ return hash_foreach ((struct hash_table *)table, \
+ func); \
+} \
+ \
+int name##_new_hash (hashtype **tp, unsigned c, const char *file, int line) \
+{ \
+ return new_hash ((struct hash_table **)tp, \
+ (hash_reference)ref, (hash_dereference)deref, c, \
+ hasher, file, line); \
+} \
+ \
+void name##_free_hash_table (hashtype **table, const char *file, int line) \
+{ \
+ free_hash_table ((struct hash_table **)table, file, line); \
+}
+
+void relinquish_hash_bucket_hunks (void);
+int new_hash_table (struct hash_table **, unsigned, const char *, int);
+void free_hash_table (struct hash_table **, const char *, int);
+struct hash_bucket *new_hash_bucket (const char *, int);
+void free_hash_bucket (struct hash_bucket *, const char *, int);
+int new_hash(struct hash_table **,
+ hash_reference, hash_dereference, unsigned,
+ unsigned (*do_hash)(const void *, unsigned, unsigned),
+ const char *, int);
+unsigned do_string_hash(const void *, unsigned, unsigned);
+unsigned do_case_hash(const void *, unsigned, unsigned);
+unsigned do_id_hash(const void *, unsigned, unsigned);
+unsigned do_number_hash(const void *, unsigned, unsigned);
+unsigned do_ip4_hash(const void *, unsigned, unsigned);
+unsigned char *hash_report(struct hash_table *);
+void add_hash (struct hash_table *,
+ const void *, unsigned, hashed_object_t *,
+ const char *, int);
+void delete_hash_entry (struct hash_table *, const void *,
+ unsigned, const char *, int);
+int hash_lookup (hashed_object_t **, struct hash_table *,
+ const void *, unsigned, const char *, int);
+int hash_foreach (struct hash_table *, hash_foreach_func);
+int casecmp (const void *s, const void *t, size_t len);
+
+#endif /* OMAPI_HASH_H */
diff --git a/includes/omapip/isclib.h b/includes/omapip/isclib.h
new file mode 100644
index 0000000..ddefeb5
--- /dev/null
+++ b/includes/omapip/isclib.h
@@ -0,0 +1,122 @@
+/* isclib.h
+
+ connections to the isc and dns libraries */
+
+/*
+ * Copyright (c) 2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ *
+ */
+
+#ifndef ISCLIB_H
+#define ISCLIB_H
+
+#include "config.h"
+
+#include <syslog.h>
+
+#define MAXWIRE 256
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/lib.h>
+#include <isc/app.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/socket.h>
+#include <isc/sockaddr.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/heap.h>
+#include <isc/random.h>
+
+#include <dns/client.h>
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/lib.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/secalg.h>
+#include <dns/tsec.h>
+
+#include <dst/dst.h>
+
+#include "result.h"
+
+
+/*
+ * DHCP context structure
+ * This holds the libisc information for a dhcp entity
+ */
+
+typedef struct dhcp_context {
+ isc_mem_t *mctx;
+ isc_appctx_t *actx;
+ int actx_started;
+ isc_taskmgr_t *taskmgr;
+ isc_task_t *task;
+ isc_socketmgr_t *socketmgr;
+ isc_timermgr_t *timermgr;
+#if defined (NSUPDATE)
+ dns_client_t *dnsclient;
+#endif
+} dhcp_context_t;
+
+extern dhcp_context_t dhcp_gbl_ctx;
+
+#define DHCP_MAXDNS_WIRE 256
+#define DHCP_MAXNS 3
+#define DHCP_HMAC_MD5_NAME "HMAC-MD5.SIG-ALG.REG.INT."
+
+isc_result_t dhcp_isc_name(unsigned char *namestr,
+ dns_fixedname_t *namefix,
+ dns_name_t **name);
+
+isc_result_t
+isclib_make_dst_key(char *inname,
+ char *algorithm,
+ unsigned char *secret,
+ int length,
+ dst_key_t **dstkey);
+
+isc_result_t dhcp_context_create(void);
+void isclib_cleanup(void);
+
+#endif /* ISCLIB_H */
diff --git a/includes/omapip/omapip.h b/includes/omapip/omapip.h
new file mode 100644
index 0000000..cbdc40d
--- /dev/null
+++ b/includes/omapip/omapip.h
@@ -0,0 +1,621 @@
+/* omapip.h
+
+ Definitions for the object management API and protocol... */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef _OMAPIP_H_
+#define _OMAPIP_H_
+#include "result.h"
+#include <stdarg.h>
+
+#include <dns/tsec.h>
+
+typedef unsigned int omapi_handle_t;
+
+struct __omapi_object;
+typedef struct __omapi_object omapi_object_t;
+
+typedef enum {
+ omapi_datatype_int,
+ omapi_datatype_string,
+ omapi_datatype_data,
+ omapi_datatype_object
+} omapi_datatype_t;
+
+typedef struct {
+ int refcnt;
+ omapi_datatype_t type;
+ union {
+ struct {
+ unsigned len;
+#define OMAPI_TYPED_DATA_NOBUFFER_LEN (sizeof (int) + \
+ sizeof (omapi_datatype_t) + \
+ sizeof (int))
+ unsigned char value [1];
+ } buffer;
+#define OMAPI_TYPED_DATA_OBJECT_LEN (sizeof (int) + \
+ sizeof (omapi_datatype_t) + \
+ sizeof (omapi_object_t *))
+ omapi_object_t *object;
+#define OMAPI_TYPED_DATA_REF_LEN (sizeof (int) + \
+ sizeof (omapi_datatype_t) + \
+ 3 * sizeof (void *))
+ struct {
+ void *ptr;
+ isc_result_t (*reference) (void *,
+ void *, const char *, int);
+ isc_result_t (*dereference) (void *,
+ const char *, int);
+ } ref;
+#define OMAPI_TYPED_DATA_INT_LEN (sizeof (int) + \
+ sizeof (omapi_datatype_t) + \
+ sizeof (int))
+ int integer;
+ } u;
+} omapi_typed_data_t;
+
+typedef struct {
+ int refcnt;
+ unsigned len;
+#define OMAPI_DATA_STRING_EMPTY_SIZE (2 * sizeof (int))
+ unsigned char value [1];
+} omapi_data_string_t;
+
+typedef struct {
+ int refcnt;
+ omapi_data_string_t *name;
+ omapi_typed_data_t *value;
+} omapi_value_t;
+
+typedef struct __omapi_object_type_t {
+ const char *name;
+ struct __omapi_object_type_t *next;
+
+ isc_result_t (*set_value) (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+ isc_result_t (*get_value) (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *, omapi_value_t **);
+ isc_result_t (*destroy) (omapi_object_t *, const char *, int);
+ isc_result_t (*signal_handler) (omapi_object_t *,
+ const char *, va_list);
+ isc_result_t (*stuff_values) (omapi_object_t *,
+ omapi_object_t *, omapi_object_t *);
+ isc_result_t (*lookup) (omapi_object_t **, omapi_object_t *,
+ omapi_object_t *);
+ isc_result_t (*create) (omapi_object_t **, omapi_object_t *);
+ isc_result_t (*remove) (omapi_object_t *, omapi_object_t *);
+ isc_result_t (*freer) (omapi_object_t *, const char *, int);
+ isc_result_t (*allocator) (omapi_object_t **, const char *, int);
+ isc_result_t (*sizer) (size_t);
+ size_t size;
+ int rc_flag;
+ isc_result_t (*initialize) (omapi_object_t *, const char *, int);
+} omapi_object_type_t;
+
+#define OMAPI_OBJECT_PREAMBLE \
+ omapi_object_type_t *type; \
+ int refcnt; \
+ omapi_handle_t handle; \
+ omapi_object_t *outer, *inner
+
+/* The omapi handle structure. */
+struct __omapi_object {
+ OMAPI_OBJECT_PREAMBLE;
+};
+
+/* The port on which applications should listen for OMAPI connections. */
+#define OMAPI_PROTOCOL_PORT 7911
+
+typedef struct {
+ unsigned addrtype;
+ unsigned addrlen;
+ unsigned char address [16];
+ unsigned port;
+} omapi_addr_t;
+
+typedef struct {
+ int refcnt;
+ unsigned count;
+ omapi_addr_t *addresses;
+} omapi_addr_list_t;
+
+typedef struct auth_key {
+ OMAPI_OBJECT_PREAMBLE;
+ char *name;
+ char *algorithm;
+ omapi_data_string_t *key;
+ dns_tsec_t *tsec_key;
+} omapi_auth_key_t;
+
+#define OMAPI_CREATE 1
+#define OMAPI_UPDATE 2
+#define OMAPI_EXCL 4
+#define OMAPI_NOTIFY_PROTOCOL 8
+
+#define OMAPI_OBJECT_ALLOC(name, stype, type) \
+isc_result_t name##_allocate (stype **p, const char *file, int line) \
+{ \
+ return omapi_object_allocate ((omapi_object_t **)p, \
+ type, 0, file, line); \
+} \
+ \
+isc_result_t name##_reference (stype **pptr, stype *ptr, \
+ const char *file, int line) \
+{ \
+ return omapi_object_reference ((omapi_object_t **)pptr, \
+ (omapi_object_t *)ptr, file, line); \
+} \
+ \
+isc_result_t name##_dereference (stype **ptr, const char *file, int line) \
+{ \
+ return omapi_object_dereference ((omapi_object_t **)ptr, file, line); \
+}
+
+#define OMAPI_OBJECT_ALLOC_DECL(name, stype, type) \
+isc_result_t name##_allocate (stype **p, const char *file, int line); \
+isc_result_t name##_reference (stype **pptr, stype *ptr, \
+ const char *file, int line); \
+isc_result_t name##_dereference (stype **ptr, const char *file, int line);
+
+typedef isc_result_t (*omapi_array_ref_t) (char **, char *, const char *, int);
+typedef isc_result_t (*omapi_array_deref_t) (char **, const char *, int);
+
+/* An extensible array type. */
+typedef struct {
+ char **data;
+ omapi_array_ref_t ref;
+ omapi_array_deref_t deref;
+ int count;
+ int max;
+} omapi_array_t;
+
+#define OMAPI_ARRAY_TYPE(name, stype) \
+isc_result_t name##_array_allocate (omapi_array_t **p, \
+ const char *file, int line) \
+{ \
+ return (omapi_array_allocate \
+ (p, \
+ (omapi_array_ref_t)name##_reference, \
+ (omapi_array_deref_t)name##_dereference, \
+ file, line)); \
+} \
+ \
+isc_result_t name##_array_free (omapi_array_t **p, \
+ const char *file, int line) \
+{ \
+ return omapi_array_free (p, file, line); \
+} \
+ \
+isc_result_t name##_array_extend (omapi_array_t *pptr, stype *ptr, int *index,\
+ const char *file, int line) \
+{ \
+ return omapi_array_extend (pptr, (char *)ptr, index, file, line); \
+} \
+ \
+isc_result_t name##_array_set (omapi_array_t *pptr, stype *ptr, int index, \
+ const char *file, int line) \
+{ \
+ return omapi_array_set (pptr, (char *)ptr, index, file, line); \
+} \
+ \
+isc_result_t name##_array_lookup (stype **ptr, omapi_array_t *pptr, \
+ int index, const char *file, int line) \
+{ \
+ return omapi_array_lookup ((char **)ptr, pptr, index, file, line); \
+}
+
+#define OMAPI_ARRAY_TYPE_DECL(name, stype) \
+isc_result_t name##_array_allocate (omapi_array_t **, const char *, int); \
+isc_result_t name##_array_free (omapi_array_t **, const char *, int); \
+isc_result_t name##_array_extend (omapi_array_t *, stype *, int *, \
+ const char *, int); \
+isc_result_t name##_array_set (omapi_array_t *, \
+ stype *, int, const char *, int); \
+isc_result_t name##_array_lookup (stype **, \
+ omapi_array_t *, int, const char *, int)
+
+#define omapi_array_foreach_begin(array, stype, var) \
+ { \
+ int omapi_array_foreach_index; \
+ stype *var = (stype *)0; \
+ for (omapi_array_foreach_index = 0; \
+ array && \
+ omapi_array_foreach_index < (array) -> count; \
+ omapi_array_foreach_index++) { \
+ if ((array) -> data [omapi_array_foreach_index]) { \
+ ((*(array) -> ref) \
+ ((char **)&var, \
+ (array) -> data [omapi_array_foreach_index],\
+ MDL));
+
+#define omapi_array_foreach_end(array, stype, var) \
+ (*(array) -> deref) ((char **)&var, MDL); \
+ } \
+ } \
+ }
+
+isc_result_t omapi_protocol_connect (omapi_object_t *,
+ const char *, unsigned, omapi_object_t *);
+isc_result_t omapi_connect_list (omapi_object_t *, omapi_addr_list_t *,
+ omapi_addr_t *);
+isc_result_t omapi_protocol_listen (omapi_object_t *, unsigned, int);
+isc_boolean_t omapi_protocol_authenticated (omapi_object_t *);
+isc_result_t omapi_protocol_configure_security (omapi_object_t *,
+ isc_result_t (*)
+ (omapi_object_t *,
+ omapi_addr_t *),
+ isc_result_t (*)
+ (omapi_object_t *,
+ omapi_auth_key_t *));
+isc_result_t omapi_protocol_accept (omapi_object_t *);
+isc_result_t omapi_protocol_send_intro (omapi_object_t *, unsigned, unsigned);
+isc_result_t omapi_protocol_ready (omapi_object_t *);
+isc_result_t omapi_protocol_add_auth (omapi_object_t *, omapi_object_t *,
+ omapi_handle_t);
+isc_result_t omapi_protocol_lookup_auth (omapi_object_t **, omapi_object_t *,
+ omapi_handle_t);
+isc_result_t omapi_protocol_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_protocol_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_protocol_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+
+isc_result_t omapi_protocol_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_protocol_send_message (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_protocol_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_protocol_listener_set_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_protocol_listener_get_value (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_protocol_listener_destroy (omapi_object_t *,
+ const char *, int);
+isc_result_t omapi_protocol_listener_signal (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_protocol_listener_stuff (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_protocol_send_status (omapi_object_t *, omapi_object_t *,
+ isc_result_t, unsigned, const char *);
+isc_result_t omapi_protocol_send_open (omapi_object_t *, omapi_object_t *,
+ const char *, omapi_object_t *,
+ unsigned);
+isc_result_t omapi_protocol_send_update (omapi_object_t *, omapi_object_t *,
+ unsigned, omapi_object_t *);
+
+isc_result_t omapi_connect (omapi_object_t *, const char *, unsigned);
+isc_result_t omapi_disconnect (omapi_object_t *, int);
+int omapi_connection_readfd (omapi_object_t *);
+int omapi_connection_writefd (omapi_object_t *);
+isc_result_t omapi_connection_connect (omapi_object_t *);
+isc_result_t omapi_connection_reader (omapi_object_t *);
+isc_result_t omapi_connection_writer (omapi_object_t *);
+isc_result_t omapi_connection_reaper (omapi_object_t *);
+isc_result_t omapi_connection_output_auth_length (omapi_object_t *,
+ unsigned *);
+isc_result_t omapi_connection_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_connection_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_connection_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_connection_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_connection_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_connection_write_typed_data (omapi_object_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_connection_put_name (omapi_object_t *, const char *);
+isc_result_t omapi_connection_put_string (omapi_object_t *, const char *);
+isc_result_t omapi_connection_put_handle (omapi_object_t *c,
+ omapi_object_t *h);
+
+isc_result_t omapi_listen (omapi_object_t *, unsigned, int);
+isc_result_t omapi_listen_addr (omapi_object_t *,
+ omapi_addr_t *, int);
+isc_result_t omapi_listener_accept (omapi_object_t *);
+int omapi_listener_readfd (omapi_object_t *);
+isc_result_t omapi_accept (omapi_object_t *);
+isc_result_t omapi_listener_configure_security (omapi_object_t *,
+ isc_result_t (*)
+ (omapi_object_t *,
+ omapi_addr_t *));
+isc_result_t omapi_listener_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_listener_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_listener_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_listener_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_listener_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+
+isc_result_t omapi_register_io_object (omapi_object_t *,
+ int (*)(omapi_object_t *),
+ int (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *));
+isc_result_t omapi_reregister_io_object (omapi_object_t *,
+ int (*)(omapi_object_t *),
+ int (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *),
+ isc_result_t (*)(omapi_object_t *));
+isc_result_t omapi_unregister_io_object (omapi_object_t *);
+isc_result_t omapi_dispatch (struct timeval *);
+isc_result_t omapi_wait_for_completion (omapi_object_t *, struct timeval *);
+isc_result_t omapi_one_dispatch (omapi_object_t *, struct timeval *);
+isc_result_t omapi_io_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_io_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *, omapi_value_t **);
+isc_result_t omapi_io_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_io_signal_handler (omapi_object_t *, const char *, va_list);
+isc_result_t omapi_io_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_waiter_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *,
+ void *),
+ void *p);
+
+isc_result_t omapi_generic_new (omapi_object_t **, const char *, int);
+isc_result_t omapi_generic_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_generic_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_generic_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_generic_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_generic_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_generic_clear_flags (omapi_object_t *);
+
+isc_result_t omapi_message_new (omapi_object_t **, const char *, int);
+isc_result_t omapi_message_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_message_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_message_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_message_signal_handler (omapi_object_t *,
+ const char *, va_list);
+isc_result_t omapi_message_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_message_register (omapi_object_t *);
+isc_result_t omapi_message_unregister (omapi_object_t *);
+isc_result_t omapi_message_process (omapi_object_t *, omapi_object_t *);
+
+OMAPI_OBJECT_ALLOC_DECL (omapi_auth_key,
+ omapi_auth_key_t, omapi_type_auth_key)
+isc_result_t omapi_auth_key_new (omapi_auth_key_t **, const char *, int);
+isc_result_t omapi_auth_key_destroy (omapi_object_t *, const char *, int);
+isc_result_t omapi_auth_key_enter (omapi_auth_key_t *);
+isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **, const char *);
+isc_result_t omapi_auth_key_lookup (omapi_object_t **,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_auth_key_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_auth_key_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+
+extern omapi_object_type_t *omapi_type_connection;
+extern omapi_object_type_t *omapi_type_listener;
+extern omapi_object_type_t *omapi_type_io_object;
+extern omapi_object_type_t *omapi_type_generic;
+extern omapi_object_type_t *omapi_type_protocol;
+extern omapi_object_type_t *omapi_type_protocol_listener;
+extern omapi_object_type_t *omapi_type_waiter;
+extern omapi_object_type_t *omapi_type_remote;
+extern omapi_object_type_t *omapi_type_message;
+extern omapi_object_type_t *omapi_type_auth_key;
+
+extern omapi_object_type_t *omapi_object_types;
+
+void omapi_type_relinquish (void);
+isc_result_t omapi_init (void);
+isc_result_t omapi_object_type_register (omapi_object_type_t **,
+ const char *,
+ isc_result_t (*)
+ (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *),
+ isc_result_t (*)
+ (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **),
+ isc_result_t (*) (omapi_object_t *,
+ const char *, int),
+ isc_result_t (*) (omapi_object_t *,
+ const char *,
+ va_list),
+ isc_result_t (*) (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*) (omapi_object_t **,
+ omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*) (omapi_object_t **,
+ omapi_object_t *),
+ isc_result_t (*) (omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*) (omapi_object_t *,
+ const char *, int),
+ isc_result_t (*) (omapi_object_t **,
+ const char *, int),
+ isc_result_t (*) (size_t), size_t,
+ isc_result_t (*) (omapi_object_t *,
+ const char *, int),
+ int);
+isc_result_t omapi_signal (omapi_object_t *, const char *, ...);
+isc_result_t omapi_signal_in (omapi_object_t *, const char *, ...);
+isc_result_t omapi_set_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *);
+isc_result_t omapi_set_value_str (omapi_object_t *, omapi_object_t *,
+ const char *, omapi_typed_data_t *);
+isc_result_t omapi_set_boolean_value (omapi_object_t *, omapi_object_t *,
+ const char *, int);
+isc_result_t omapi_set_int_value (omapi_object_t *, omapi_object_t *,
+ const char *, int);
+isc_result_t omapi_set_object_value (omapi_object_t *, omapi_object_t *,
+ const char *, omapi_object_t *);
+isc_result_t omapi_set_string_value (omapi_object_t *, omapi_object_t *,
+ const char *, const char *);
+isc_result_t omapi_get_value (omapi_object_t *, omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **);
+isc_result_t omapi_get_value_str (omapi_object_t *, omapi_object_t *,
+ const char *, omapi_value_t **);
+isc_result_t omapi_stuff_values (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *);
+isc_result_t omapi_object_create (omapi_object_t **, omapi_object_t *,
+ omapi_object_type_t *);
+isc_result_t omapi_object_update (omapi_object_t *, omapi_object_t *,
+ omapi_object_t *, omapi_handle_t);
+int omapi_data_string_cmp (omapi_data_string_t *, omapi_data_string_t *);
+int omapi_ds_strcmp (omapi_data_string_t *, const char *);
+int omapi_td_strcmp (omapi_typed_data_t *, const char *);
+int omapi_td_strcasecmp (omapi_typed_data_t *, const char *);
+isc_result_t omapi_make_value (omapi_value_t **, omapi_data_string_t *,
+ omapi_typed_data_t *, const char *, int);
+isc_result_t omapi_make_const_value (omapi_value_t **, omapi_data_string_t *,
+ const unsigned char *,
+ unsigned, const char *, int);
+isc_result_t omapi_make_int_value (omapi_value_t **, omapi_data_string_t *,
+ int, const char *, int);
+isc_result_t omapi_make_uint_value (omapi_value_t **, omapi_data_string_t *,
+ unsigned int, const char *, int);
+isc_result_t omapi_make_object_value (omapi_value_t **, omapi_data_string_t *,
+ omapi_object_t *, const char *, int);
+isc_result_t omapi_make_handle_value (omapi_value_t **, omapi_data_string_t *,
+ omapi_object_t *, const char *, int);
+isc_result_t omapi_make_string_value (omapi_value_t **, omapi_data_string_t *,
+ const char *, const char *, int);
+isc_result_t omapi_get_int_value (unsigned long *, omapi_typed_data_t *);
+
+isc_result_t omapi_object_handle (omapi_handle_t *, omapi_object_t *);
+isc_result_t omapi_handle_lookup (omapi_object_t **, omapi_handle_t);
+isc_result_t omapi_handle_td_lookup (omapi_object_t **, omapi_typed_data_t *);
+
+void * dmalloc (unsigned, const char *, int);
+void dfree (void *, const char *, int);
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void dmalloc_reuse (void *, const char *, int, int);
+void dmalloc_dump_outstanding (void);
+#else
+#define dmalloc_reuse(x,y,l,z)
+#endif
+#define MDL __FILE__, __LINE__
+#if defined (DEBUG_RC_HISTORY)
+void dump_rc_history (void *);
+void rc_history_next (int);
+#endif
+void omapi_print_dmalloc_usage_by_caller (void);
+isc_result_t omapi_object_allocate (omapi_object_t **,
+ omapi_object_type_t *,
+ size_t, const char *, int);
+isc_result_t omapi_object_initialize (omapi_object_t *,
+ omapi_object_type_t *,
+ size_t, size_t, const char *, int);
+isc_result_t omapi_object_reference (omapi_object_t **,
+ omapi_object_t *, const char *, int);
+isc_result_t omapi_object_dereference (omapi_object_t **, const char *, int);
+isc_result_t omapi_typed_data_new (const char *, int, omapi_typed_data_t **,
+ omapi_datatype_t, ...);
+isc_result_t omapi_typed_data_reference (omapi_typed_data_t **,
+ omapi_typed_data_t *,
+ const char *, int);
+isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **,
+ const char *, int);
+isc_result_t omapi_data_string_new (omapi_data_string_t **,
+ unsigned, const char *, int);
+isc_result_t omapi_data_string_reference (omapi_data_string_t **,
+ omapi_data_string_t *,
+ const char *, int);
+isc_result_t omapi_data_string_dereference (omapi_data_string_t **,
+ const char *, int);
+isc_result_t omapi_value_new (omapi_value_t **, const char *, int);
+isc_result_t omapi_value_reference (omapi_value_t **,
+ omapi_value_t *, const char *, int);
+isc_result_t omapi_value_dereference (omapi_value_t **, const char *, int);
+isc_result_t omapi_addr_list_new (omapi_addr_list_t **, unsigned,
+ const char *, int);
+isc_result_t omapi_addr_list_reference (omapi_addr_list_t **,
+ omapi_addr_list_t *,
+ const char *, int);
+isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **,
+ const char *, int);
+
+isc_result_t omapi_array_allocate (omapi_array_t **, omapi_array_ref_t,
+ omapi_array_deref_t, const char *, int);
+isc_result_t omapi_array_free (omapi_array_t **, const char *, int);
+isc_result_t omapi_array_extend (omapi_array_t *, char *, int *,
+ const char *, int);
+isc_result_t omapi_array_set (omapi_array_t *, void *, int, const char *, int);
+isc_result_t omapi_array_lookup (char **,
+ omapi_array_t *, int, const char *, int);
+OMAPI_ARRAY_TYPE_DECL(omapi_object, omapi_object_t);
+#endif /* _OMAPIP_H_ */
diff --git a/includes/omapip/omapip_p.h b/includes/omapip/omapip_p.h
new file mode 100644
index 0000000..1fab5a9
--- /dev/null
+++ b/includes/omapip/omapip_p.h
@@ -0,0 +1,306 @@
+/* omapip_p.h
+
+ Private master include file for the OMAPI library. */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef __OMAPIP_OMAPIP_P_H__
+#define __OMAPIP_OMAPIP_P_H__
+
+#ifndef __CYGWIN32__
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#else
+#define fd_set cygwin_fd_set
+#include <sys/types.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <time.h>
+
+/*
+ * XXX: I'm not sure why these were here.
+#include "cdefs.h"
+#include "osdep.h"
+ */
+
+#include <dst/dst.h>
+#include "result.h"
+
+#include <omapip/convert.h>
+#include <omapip/hash.h>
+#include <omapip/omapip.h>
+#include <omapip/trace.h>
+
+/* DST_API control flags */
+/* These are used in functions dst_sign_data and dst_verify_data */
+#define SIG_MODE_INIT 1 /* initalize digest */
+#define SIG_MODE_UPDATE 2 /* add data to digest */
+#define SIG_MODE_FINAL 4 /* generate/verify signature */
+#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL)
+
+/* OMAPI protocol header, version 1.00 */
+typedef struct {
+ u_int32_t authlen; /* Length of authenticator. */
+ u_int32_t authid; /* Authenticator object ID. */
+ u_int32_t op; /* Opcode. */
+ omapi_handle_t handle; /* Handle of object being operated on,
+ or zero. */
+ u_int32_t id; /* Transaction ID. */
+ u_int32_t rid; /* ID of transaction to which this is a response. */
+} omapi_protocol_header_t;
+
+#define OMAPI_PROTOCOL_VERSION 100
+
+#define OMAPI_OP_OPEN 1
+#define OMAPI_OP_REFRESH 2
+#define OMAPI_OP_UPDATE 3
+#define OMAPI_OP_NOTIFY 4
+#define OMAPI_OP_STATUS 5
+#define OMAPI_OP_DELETE 6
+
+typedef enum {
+ omapi_connection_unconnected,
+ omapi_connection_connecting,
+ omapi_connection_connected,
+ omapi_connection_disconnecting,
+ omapi_connection_closed
+} omapi_connection_state_t;
+
+typedef enum {
+ omapi_protocol_intro_wait,
+ omapi_protocol_header_wait,
+ omapi_protocol_signature_wait,
+ omapi_protocol_name_wait,
+ omapi_protocol_name_length_wait,
+ omapi_protocol_value_wait,
+ omapi_protocol_value_length_wait
+} omapi_protocol_state_t;
+
+typedef struct __omapi_message_object {
+ OMAPI_OBJECT_PREAMBLE;
+ struct __omapi_message_object *next, *prev;
+ omapi_object_t *object;
+ omapi_object_t *notify_object;
+ struct __omapi_protocol_object *protocol_object;
+ u_int32_t authlen;
+ omapi_typed_data_t *authenticator;
+ u_int32_t authid;
+ omapi_object_t *id_object;
+ u_int32_t op;
+ u_int32_t h;
+ u_int32_t id;
+ u_int32_t rid;
+} omapi_message_object_t;
+
+typedef struct __omapi_remote_auth {
+ struct __omapi_remote_auth *next;
+ omapi_handle_t remote_handle;
+ omapi_object_t *a;
+} omapi_remote_auth_t;
+
+typedef struct __omapi_protocol_object {
+ OMAPI_OBJECT_PREAMBLE;
+ u_int32_t header_size;
+ u_int32_t protocol_version;
+ u_int32_t next_xid;
+
+ omapi_protocol_state_t state; /* Input state. */
+ int reading_message_values; /* True if reading message-specific
+ values. */
+ omapi_message_object_t *message; /* Incoming message. */
+ omapi_data_string_t *name; /* Incoming name. */
+ omapi_typed_data_t *value; /* Incoming value. */
+ isc_result_t verify_result;
+ omapi_remote_auth_t *default_auth; /* Default authinfo to use. */
+ omapi_remote_auth_t *remote_auth_list; /* Authenticators active on
+ this connection. */
+
+ isc_boolean_t insecure; /* Set to allow unauthenticated
+ messages. */
+
+ isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *);
+} omapi_protocol_object_t;
+
+typedef struct {
+ OMAPI_OBJECT_PREAMBLE;
+
+ isc_boolean_t insecure; /* Set to allow unauthenticated
+ messages. */
+
+ isc_result_t (*verify_auth) (omapi_object_t *, omapi_auth_key_t *);
+} omapi_protocol_listener_object_t;
+
+#include <omapip/buffer.h>
+
+typedef struct __omapi_listener_object {
+ OMAPI_OBJECT_PREAMBLE;
+ int socket; /* Connection socket. */
+ int index;
+ struct sockaddr_in address;
+ isc_result_t (*verify_addr) (omapi_object_t *, omapi_addr_t *);
+} omapi_listener_object_t;
+
+typedef struct __omapi_connection_object {
+ OMAPI_OBJECT_PREAMBLE;
+ int socket; /* Connection socket. */
+ int32_t index;
+ omapi_connection_state_t state;
+ struct sockaddr_in remote_addr;
+ struct sockaddr_in local_addr;
+ omapi_addr_list_t *connect_list; /* List of addresses to which
+ to connect. */
+ int cptr; /* Current element we are connecting to. */
+ u_int32_t bytes_needed; /* Bytes of input needed before wakeup. */
+ u_int32_t in_bytes; /* Bytes of input already buffered. */
+ omapi_buffer_t *inbufs;
+ u_int32_t out_bytes; /* Bytes of output in buffers. */
+ omapi_buffer_t *outbufs;
+ omapi_listener_object_t *listener; /* Listener that accepted this
+ connection, if any. */
+ dst_key_t *in_key; /* Authenticator signing incoming
+ data. */
+ void *in_context; /* Input hash context. */
+ dst_key_t *out_key; /* Authenticator signing outgoing
+ data. */
+ void *out_context; /* Output hash context. */
+} omapi_connection_object_t;
+
+typedef struct __omapi_io_object {
+ OMAPI_OBJECT_PREAMBLE;
+ struct __omapi_io_object *next;
+ int (*readfd) (omapi_object_t *);
+ int (*writefd) (omapi_object_t *);
+ isc_result_t (*reader) (omapi_object_t *);
+ isc_result_t (*writer) (omapi_object_t *);
+ isc_result_t (*reaper) (omapi_object_t *);
+ isc_socket_t *fd;
+ isc_boolean_t closed; /* ISC_TRUE = closed, do not use */
+} omapi_io_object_t;
+
+typedef struct __omapi_generic_object {
+ OMAPI_OBJECT_PREAMBLE;
+ omapi_value_t **values;
+ u_int8_t *changed;
+ int nvalues, va_max;
+} omapi_generic_object_t;
+
+typedef struct __omapi_waiter_object {
+ OMAPI_OBJECT_PREAMBLE;
+ int ready;
+ isc_result_t waitstatus;
+ struct __omapi_waiter_object *next;
+} omapi_waiter_object_t;
+
+#define OMAPI_HANDLE_TABLE_SIZE 120
+
+typedef struct __omapi_handle_table {
+ omapi_handle_t first, limit;
+ omapi_handle_t next;
+ int leafp;
+ union {
+ omapi_object_t *object;
+ struct __omapi_handle_table *table;
+ } children [OMAPI_HANDLE_TABLE_SIZE];
+} omapi_handle_table_t;
+
+#include <omapip/alloc.h>
+
+OMAPI_OBJECT_ALLOC_DECL (omapi_protocol, omapi_protocol_object_t,
+ omapi_type_protocol)
+OMAPI_OBJECT_ALLOC_DECL (omapi_protocol_listener,
+ omapi_protocol_listener_object_t,
+ omapi_type_protocol_listener)
+OMAPI_OBJECT_ALLOC_DECL (omapi_connection,
+ omapi_connection_object_t, omapi_type_connection)
+OMAPI_OBJECT_ALLOC_DECL (omapi_listener,
+ omapi_listener_object_t, omapi_type_listener)
+OMAPI_OBJECT_ALLOC_DECL (omapi_io,
+ omapi_io_object_t, omapi_type_io_object)
+OMAPI_OBJECT_ALLOC_DECL (omapi_waiter,
+ omapi_waiter_object_t, omapi_type_waiter)
+OMAPI_OBJECT_ALLOC_DECL (omapi_generic,
+ omapi_generic_object_t, omapi_type_generic)
+OMAPI_OBJECT_ALLOC_DECL (omapi_message,
+ omapi_message_object_t, omapi_type_message)
+
+isc_result_t omapi_connection_sign_data (int mode,
+ dst_key_t *key,
+ void **context,
+ const unsigned char *data,
+ const unsigned len,
+ omapi_typed_data_t **result);
+isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
+ omapi_listener_object_t *listener,
+ int socket,
+ struct sockaddr_in *remote_addr);
+void omapi_listener_trace_setup (void);
+void omapi_connection_trace_setup (void);
+void omapi_buffer_trace_setup (void);
+void omapi_connection_register (omapi_connection_object_t *,
+ const char *, int);
+OMAPI_ARRAY_TYPE_DECL(omapi_listener, omapi_listener_object_t);
+OMAPI_ARRAY_TYPE_DECL(omapi_connection, omapi_connection_object_t);
+
+isc_result_t omapi_handle_clear(omapi_handle_t);
+
+extern int log_priority;
+extern int log_perror;
+extern void (*log_cleanup) (void);
+
+void log_fatal (const char *, ...)
+ __attribute__((__format__(__printf__,1,2)));
+int log_error (const char *, ...)
+ __attribute__((__format__(__printf__,1,2)));
+int log_info (const char *, ...)
+ __attribute__((__format__(__printf__,1,2)));
+int log_debug (const char *, ...)
+ __attribute__((__format__(__printf__,1,2)));
+void do_percentm (char *obuf, const char *ibuf);
+
+isc_result_t uerr2isc (int);
+isc_result_t ns_rcode_to_isc (int);
+
+extern omapi_message_object_t *omapi_registered_messages;
+
+#endif /* __OMAPIP_OMAPIP_P_H__ */
diff --git a/includes/omapip/result.h b/includes/omapip/result.h
new file mode 100644
index 0000000..85534d8
--- /dev/null
+++ b/includes/omapip/result.h
@@ -0,0 +1,120 @@
+/* result.h
+ */
+
+/*
+ * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#ifndef DHCP_RESULT_H
+#define DHCP_RESULT_H 1
+
+#include <isc/lang.h>
+#include <isc/resultclass.h>
+#include <isc/types.h>
+
+#include <isc/result.h>
+
+/*
+ * DHCP result codes
+ */
+
+/*
+ * In the previous code the results started at 36
+ * rather than ISC_RESULTCLASS_DHCP + 0
+ * ISC_R_NOTCONNECTED was + 4 (40), it has been superseded by the isc version
+ */
+
+#define DHCP_R_HOSTUNKNOWN (ISC_RESULTCLASS_DHCP + 0)
+#define DHCP_R_VERSIONMISMATCH (ISC_RESULTCLASS_DHCP + 1)
+#define DHCP_R_PROTOCOLERROR (ISC_RESULTCLASS_DHCP + 2)
+#define DHCP_R_INVALIDARG (ISC_RESULTCLASS_DHCP + 3)
+#define DHCP_R_NOTYET (ISC_RESULTCLASS_DHCP + 4)
+#define DHCP_R_UNCHANGED (ISC_RESULTCLASS_DHCP + 5)
+#define DHCP_R_MULTIPLE (ISC_RESULTCLASS_DHCP + 6)
+#define DHCP_R_KEYCONFLICT (ISC_RESULTCLASS_DHCP + 7)
+#define DHCP_R_BADPARSE (ISC_RESULTCLASS_DHCP + 8)
+#define DHCP_R_NOKEYS (ISC_RESULTCLASS_DHCP + 9)
+#define DHCP_R_KEY_UNKNOWN (ISC_RESULTCLASS_DHCP + 10)
+#define DHCP_R_INVALIDKEY (ISC_RESULTCLASS_DHCP + 11)
+#define DHCP_R_INCOMPLETE (ISC_RESULTCLASS_DHCP + 12)
+#define DHCP_R_FORMERR (ISC_RESULTCLASS_DHCP + 13)
+#define DHCP_R_SERVFAIL (ISC_RESULTCLASS_DHCP + 14)
+#define DHCP_R_NXDOMAIN (ISC_RESULTCLASS_DHCP + 15)
+#define DHCP_R_NOTIMPL (ISC_RESULTCLASS_DHCP + 16)
+#define DHCP_R_REFUSED (ISC_RESULTCLASS_DHCP + 17)
+#define DHCP_R_YXDOMAIN (ISC_RESULTCLASS_DHCP + 18)
+#define DHCP_R_YXRRSET (ISC_RESULTCLASS_DHCP + 19)
+#define DHCP_R_NXRRSET (ISC_RESULTCLASS_DHCP + 20)
+#define DHCP_R_NOTAUTH (ISC_RESULTCLASS_DHCP + 21)
+#define DHCP_R_NOTZONE (ISC_RESULTCLASS_DHCP + 22)
+#define DHCP_R_BADSIG (ISC_RESULTCLASS_DHCP + 23)
+#define DHCP_R_BADKEY (ISC_RESULTCLASS_DHCP + 24)
+#define DHCP_R_BADTIME (ISC_RESULTCLASS_DHCP + 25)
+#define DHCP_R_NOROOTZONE (ISC_RESULTCLASS_DHCP + 26)
+#define DHCP_R_DESTADDRREQ (ISC_RESULTCLASS_DHCP + 27)
+#define DHCP_R_CROSSZONE (ISC_RESULTCLASS_DHCP + 28)
+#define DHCP_R_NO_TSIG (ISC_RESULTCLASS_DHCP + 29)
+#define DHCP_R_NOT_EQUAL (ISC_RESULTCLASS_DHCP + 30)
+#define DHCP_R_CONNRESET (ISC_RESULTCLASS_DHCP + 31)
+#define DHCP_R_UNKNOWNATTRIBUTE (ISC_RESULTCLASS_DHCP + 32)
+
+#define DHCP_R_NRESULTS 33 /*%< Number of results */
+
+// Included for historical reasons, these should be removed as
+// soon as reasonable
+#define ISC_R_HOSTUNKNOWN DHCP_R_HOSTUNKNOWN
+#define ISC_R_VERSIONMISMATCH DHCP_R_VERSIONMISMATCH
+#define ISC_R_PROTOCOLERROR DHCP_R_PROTOCOLERROR
+#define ISC_R_INVALIDARG DHCP_R_INVALIDARG
+#define ISC_R_NOTYET DHCP_R_NOTYET
+#define ISC_R_UNCHANGED DHCP_R_UNCHANGED
+#define ISC_R_MULTIPLE DHCP_R_MULTIPLE
+#define ISC_R_KEYCONFLICT DHCP_R_KEYCONFLICT
+#define ISC_R_BADPARSE DHCP_R_BADPARSE
+#define ISC_R_NOKEYS DHCP_R_NOKEYS
+#define ISC_R_KEY_UNKNOWN DHCP_R_KEY_UNKNOWN
+#define ISC_R_INVALIDKEY DHCP_R_INVALIDKEY
+#define ISC_R_INCOMPLETE DHCP_R_INCOMPLETE
+#define ISC_R_FORMERR DHCP_R_FORMERR
+#define ISC_R_SERVFAIL DHCP_R_SERVFAIL
+#define ISC_R_NXDOMAIN DHCP_R_NXDOMAIN
+#define ISC_R_NOTIMPL DHCP_R_NOTIMPL
+#define ISC_R_REFUSED DHCP_R_REFUSED
+#define ISC_R_YXDOMAIN DHCP_R_YXDOMAIN
+#define ISC_R_YXRRSET DHCP_R_YXRRSET
+#define ISC_R_NXRRSET DHCP_R_NXRRSET
+#define ISC_R_NOTAUTH DHCP_R_NOTAUTH
+#define ISC_R_NOTZONE DHCP_R_NOTZONE
+#define ISC_R_BADSIG DHCP_R_BADSIG
+#define ISC_R_BADKEY DHCP_R_BADKEY
+#define ISC_R_BADTIME DHCP_R_BADTIME
+#define ISC_R_NOROOTZONE DHCP_R_NOROOTZONE
+#define ISC_R_DESTADDRREQ DHCP_R_DESTADDRREQ
+#define ISC_R_CROSSZONE DHCP_R_CROSSZONE
+#define ISC_R_NO_TSIG DHCP_R_NO_TSIG
+#define ISC_R_NOT_EQUAL DHCP_R_NOT_EQUAL
+#define ISC_R_CONNRESET DHCP_R_CONNRESET
+#define ISC_R_UNKNOWNATTRIBUTE DHCP_R_UNKNOWNATTRIBUTE
+
+isc_result_t
+dhcp_result_register(void);
+
+#endif /* DHCP_RESULT_H */
diff --git a/includes/omapip/trace.h b/includes/omapip/trace.h
new file mode 100644
index 0000000..fc5906c
--- /dev/null
+++ b/includes/omapip/trace.h
@@ -0,0 +1,115 @@
+/* trace.h
+
+ Definitions for omapi tracing facility... */
+
+/*
+ * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
+ * about Internet Systems Consortium, see https://www.isc.org/. To
+ * learn more about Nominum, Inc., see ``http://www.nominum.com''.
+ */
+
+#define TRACEFILE_MAGIC 0x64484370UL /* dHCp */
+#define TRACEFILE_VERSION 1
+
+/* The first thing in a trace file is the header, which basically just
+ defines the version of the file. */
+typedef struct {
+ u_int32_t magic; /* Magic number for trace file. */
+ u_int32_t version; /* Version of file. */
+ int32_t hlen; /* Length of this header. */
+ int32_t phlen; /* Length of packet headers. */
+} tracefile_header_t;
+
+/* The trace file is composed of a bunch of trace packets. Each such packet
+ has a type, followed by a length, followed by a timestamp, followed by
+ the actual contents of the packet. The type indexes are not fixed -
+ they are allocated either on readback or when writing a trace file.
+ One index type is reserved - type zero means that this record is a type
+ name to index mapping. */
+typedef struct {
+ u_int32_t type_index; /* Index to the type of handler that this
+ packet needs. */
+ u_int32_t length; /* Length of the packet. This includes
+ everything except the fixed header. */
+ u_int32_t when; /* When the packet was written. */
+ u_int32_t pad; /* Round this out to a quad boundary. */
+} tracepacket_t;
+
+#define TRACE_INDEX_MAPPING_SIZE 4 /* trace_index_mapping_t less name. */
+typedef struct {
+ u_int32_t index;
+ char name [1];
+} trace_index_mapping_t;
+
+struct trace_type; /* forward */
+typedef struct trace_type trace_type_t;
+
+struct trace_type {
+ trace_type_t *next;
+ int index;
+ char *name;
+ void *baggage;
+ void (*have_packet) (trace_type_t *, unsigned, char *);
+ void (*stop_tracing) (trace_type_t *);
+};
+
+typedef struct trace_iov {
+ const char *buf;
+ unsigned len;
+} trace_iov_t;
+
+typedef struct {
+ u_int16_t addrtype;
+ u_int16_t addrlen;
+ u_int8_t address [16];
+ u_int16_t port;
+} trace_addr_t;
+
+void trace_free_all (void);
+int trace_playback (void);
+int trace_record (void);
+isc_result_t trace_init(void (*set_time)(time_t), const char *, int);
+isc_result_t trace_begin (const char *, const char *, int);
+isc_result_t trace_write_packet (trace_type_t *, unsigned, const char *,
+ const char *, int);
+isc_result_t trace_write_packet_iov (trace_type_t *, int, trace_iov_t *,
+ const char *, int);
+void trace_type_stash (trace_type_t *);
+trace_type_t *trace_type_register (const char *, void *,
+ void (*) (trace_type_t *,
+ unsigned, char *),
+ void (*) (trace_type_t *),
+ const char *, int);
+void trace_stop (void);
+void trace_index_map_input (trace_type_t *, unsigned, char *);
+void trace_index_stop_tracing (trace_type_t *);
+void trace_replay_init (void);
+void trace_file_replay (const char *);
+isc_result_t trace_get_next_packet (trace_type_t **, tracepacket_t *,
+ char **, unsigned *, unsigned *);
+isc_result_t trace_get_file (trace_type_t *,
+ const char *, unsigned *, char **);
+isc_result_t trace_get_packet (trace_type_t **, unsigned *, char **);
+time_t trace_snoop_time (trace_type_t **);
diff --git a/includes/osdep.h b/includes/osdep.h
new file mode 100644
index 0000000..a8d8bb7
--- /dev/null
+++ b/includes/osdep.h
@@ -0,0 +1,293 @@
+/* osdep.h
+
+ Operating system dependencies... */
+
+/*
+ * Copyright (c) 2004-2005,2007-2010 by Internet Systems Consortium,
+ * Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#if !defined (__ISC_DHCP_OSDEP_H__)
+#define __ISC_DHCP_OSDEP_H__
+
+#include "site.h"
+
+#include "config.h"
+
+#include <inttypes.h>
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif /* LITTLE_ENDIAN */
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif /* BIG_ENDIAN */
+
+#ifndef BYTE_ORDER
+#define BYTE_ORDER DHCP_BYTE_ORDER
+#endif /* BYTE_ORDER */
+
+/* Porting::
+
+ If you add a new network API, you must add a check for it below: */
+
+#if !defined (USE_SOCKETS) && \
+ !defined (USE_SOCKET_SEND) && \
+ !defined (USE_SOCKET_RECEIVE) && \
+ !defined (USE_RAW_SOCKETS) && \
+ !defined (USE_RAW_SEND) && \
+ !defined (USE_SOCKET_RECEIVE) && \
+ !defined (USE_BPF) && \
+ !defined (USE_BPF_SEND) && \
+ !defined (USE_BPF_RECEIVE) && \
+ !defined (USE_LPF) && \
+ !defined (USE_LPF_SEND) && \
+ !defined (USE_LPF_RECEIVE) && \
+ !defined (USE_NIT) && \
+ !defined (USE_NIT_SEND) && \
+ !defined (USE_NIT_RECEIVE) && \
+ !defined (USE_DLPI_SEND) && \
+ !defined (USE_DLPI_RECEIVE)
+/* Determine default socket API to USE. */
+# if defined(HAVE_BPF)
+# define USE_BPF 1
+# elif defined(HAVE_LPF)
+# define USE_LPF 1
+# elif defined(HAVE_DLPI)
+# define USE_DLPI 1
+# endif
+#endif
+
+#if !defined (TIME_MAX)
+# define TIME_MAX 2147483647
+#endif
+
+/* snprintf/vsnprintf hacks. for systems with no libc versions only. */
+#ifdef NO_SNPRINTF
+ extern int isc_print_snprintf(char *, size_t, const char *, ...);
+ extern int isc_print_vsnprintf(char *, size_t, const char *, va_list ap);
+# define snprintf isc_print_snprintf
+# define vsnprintf isc_print_vsnprintf
+#endif
+
+/* Porting::
+
+ If you add a new network API, and have it set up so that it can be
+ used for sending or receiving, but doesn't have to be used for both,
+ then set up an ifdef like the ones below: */
+
+#ifdef USE_SOCKETS
+# define USE_SOCKET_SEND
+# define USE_SOCKET_RECEIVE
+# if defined(HAVE_DLPI)
+# define USE_DLPI_HWADDR
+# endif
+#endif
+
+#ifdef USE_RAW_SOCKETS
+# define USE_RAW_SEND
+# define USE_SOCKET_RECEIVE
+#endif
+
+#ifdef USE_BPF
+# define USE_BPF_SEND
+# define USE_BPF_RECEIVE
+#endif
+
+#ifdef USE_LPF
+# define USE_LPF_SEND
+# define USE_LPF_RECEIVE
+#endif
+
+#ifdef USE_NIT
+# define USE_NIT_SEND
+# define USE_NIT_RECEIVE
+#endif
+
+#ifdef USE_DLPI
+# define USE_DLPI_SEND
+# define USE_DLPI_RECEIVE
+#endif
+
+#ifdef USE_UPF
+# define USE_UPF_SEND
+# define USE_UPF_RECEIVE
+#endif
+
+/* Porting::
+
+ If you add support for sending packets directly out an interface,
+ and your support does not do ARP or routing, you must use a fallback
+ mechanism to deal with packets that need to be sent to routers.
+ Currently, all low-level packet interfaces use BSD sockets as a
+ fallback. */
+
+#if defined (USE_BPF_SEND) || defined (USE_NIT_SEND) || \
+ defined (USE_DLPI_SEND) || defined (USE_UPF_SEND) || \
+ defined (USE_LPF_SEND) || \
+ (defined (USE_SOCKET_SEND) && defined (HAVE_SO_BINDTODEVICE))
+# define USE_SOCKET_FALLBACK
+# define USE_FALLBACK
+#endif
+
+/* Porting::
+
+ If you add support for sending packets directly out an interface
+ and need to be able to assemble packets, add the USE_XXX_SEND
+ definition for your interface to the list tested below. */
+
+#if defined (USE_RAW_SEND) || defined (USE_BPF_SEND) || \
+ defined (USE_NIT_SEND) || defined (USE_UPF_SEND) || \
+ defined (USE_DLPI_SEND) || defined (USE_LPF_SEND)
+# define PACKET_ASSEMBLY
+#endif
+
+/* Porting::
+
+ If you add support for receiving packets directly from an interface
+ and need to be able to decode raw packets, add the USE_XXX_RECEIVE
+ definition for your interface to the list tested below. */
+
+#if defined (USE_RAW_RECEIVE) || defined (USE_BPF_SEND) || \
+ defined (USE_NIT_RECEIVE) || defined (USE_UPF_RECEIVE) || \
+ defined (USE_DLPI_RECEIVE) || defined (USE_LPF_RECEIVE)
+# define PACKET_DECODING
+#endif
+
+/* If we don't have a DLPI packet filter, we have to filter in userland.
+ Probably not worth doing, actually. */
+#if defined (USE_DLPI_RECEIVE) && !defined (USE_DLPI_PFMOD)
+# define USERLAND_FILTER
+#endif
+
+/* jmp_buf is assumed to be a struct unless otherwise defined in the
+ system header. */
+#ifndef jbp_decl
+# define jbp_decl(x) jmp_buf *x
+#endif
+#ifndef jref
+# define jref(x) (&(x))
+#endif
+#ifndef jdref
+# define jdref(x) (*(x))
+#endif
+#ifndef jrefproto
+# define jrefproto jmp_buf *
+#endif
+
+#ifndef BPF_FORMAT
+# define BPF_FORMAT "/dev/bpf%d"
+#endif
+
+#if defined (F_SETFD) && !defined (HAVE_SETFD)
+# define HAVE_SETFD
+#endif
+
+#if defined (IFF_POINTOPOINT) && !defined (HAVE_IFF_POINTOPOINT)
+# define HAVE_IFF_POINTOPOINT
+#endif
+
+#if defined (AF_LINK) && !defined (HAVE_AF_LINK)
+# define HAVE_AF_LINK
+#endif
+
+#if defined (ARPHRD_TUNNEL) && !defined (HAVE_ARPHRD_TUNNEL)
+# define HAVE_ARPHRD_TUNNEL
+#endif
+
+#if defined (ARPHRD_LOOPBACK) && !defined (HAVE_ARPHRD_LOOPBACK)
+# define HAVE_ARPHRD_LOOPBACK
+#endif
+
+#if defined (ARPHRD_ROSE) && !defined (HAVE_ARPHRD_ROSE)
+# define HAVE_ARPHRD_ROSE
+#endif
+
+#if defined (ARPHRD_IRDA) && !defined (HAVE_ARPHRD_IRDA)
+# define HAVE_ARPHRD_IRDA
+#endif
+
+#if defined (ARPHRD_SIT) && !defined (HAVE_ARPHRD_SIT)
+# define HAVE_ARPHRD_SIT
+#endif
+
+#if defined (ARPHRD_IEEE1394) & !defined (HAVE_ARPHRD_IEEE1394)
+# define HAVE_ARPHRD_IEEE1394
+#endif
+
+#if defined (ARPHRD_IEEE802) && !defined (HAVE_ARPHRD_IEEE802)
+# define HAVE_ARPHRD_IEEE802
+#endif
+
+#if defined (ARPHRD_IEEE802_TR) && !defined (HAVE_ARPHRD_IEEE802_TR)
+# define HAVE_ARPHRD_IEEE802_TR
+#endif
+
+#if defined (ARPHRD_FDDI) && !defined (HAVE_ARPHRD_FDDI)
+# define HAVE_ARPHRD_FDDI
+#endif
+
+#if defined (ARPHRD_AX25) && !defined (HAVE_ARPHRD_AX25)
+# define HAVE_ARPHRD_AX25
+#endif
+
+#if defined (ARPHRD_NETROM) && !defined (HAVE_ARPHRD_NETROM)
+# define HAVE_ARPHRD_NETROM
+#endif
+
+#if defined (ARPHRD_METRICOM) && !defined (HAVE_ARPHRD_METRICOM)
+# define HAVE_ARPHRD_METRICOM
+#endif
+
+#if defined (SO_BINDTODEVICE) && !defined (HAVE_SO_BINDTODEVICE)
+# define HAVE_SO_BINDTODEVICE
+#endif
+
+#if defined (AF_LINK) && !defined (HAVE_AF_LINK)
+# define HAVE_AF_LINK
+#endif
+
+/* Linux needs to define SHUT_* in /usr/include/sys/socket.h someday... */
+#if !defined (SHUT_RD)
+# define SHUT_RD 0
+#endif
+
+#if !defined (SOCKLEN_T)
+# define SOCKLEN_T socklen_t
+#elif defined(_AIX)
+#undef SOCKLEN_T
+#define SOCKLEN_T socklen_t
+#endif
+
+#if !defined (STDERR_FILENO)
+# define STDERR_FILENO 2
+#endif
+
+#endif /* __ISC_DHCP_OSDEP_H__ */
diff --git a/includes/site.h b/includes/site.h
new file mode 100644
index 0000000..b78b537
--- /dev/null
+++ b/includes/site.h
@@ -0,0 +1,229 @@
+/* Site-specific definitions.
+
+ For supported systems, you shouldn't need to make any changes here.
+ However, you may want to, in order to deal with site-specific
+ differences. */
+
+/* Add any site-specific definitions and inclusions here... */
+
+/* #include <site-foo-bar.h> */
+/* #define SITE_FOOBAR */
+
+/* Define this if you don't want dhcpd to run as a daemon and do want
+ to see all its output printed to stdout instead of being logged via
+ syslog(). This also makes dhcpd use the dhcpd.conf in its working
+ directory and write the dhcpd.leases file there. */
+
+/* #define DEBUG */
+
+/* Define this to see what the parser is parsing. You probably don't
+ want to see this. */
+
+/* #define DEBUG_TOKENS */
+
+/* Define this to see dumps of incoming and outgoing packets. This
+ slows things down quite a bit... */
+
+/* #define DEBUG_PACKET */
+
+/* Define this if you want to see dumps of expression evaluation. */
+
+/* #define DEBUG_EXPRESSIONS */
+
+/* Define this if you want to see dumps of find_lease() in action. */
+
+/* #define DEBUG_FIND_LEASE */
+
+/* Define this if you want to see dumps of parsed expressions. */
+
+/* #define DEBUG_EXPRESSION_PARSE */
+
+/* Define this if you want to watch the class matching process. */
+
+/* #define DEBUG_CLASS_MATCHING */
+
+/* Define this if you want to track memory usage for the purpose of
+ noticing memory leaks quickly. */
+
+/* #define DEBUG_MEMORY_LEAKAGE */
+/* #define DEBUG_MEMORY_LEAKAGE_ON_EXIT */
+
+/* Define this if you want exhaustive (and very slow) checking of the
+ malloc pool for corruption. */
+
+/* #define DEBUG_MALLOC_POOL */
+
+/* Define this if you want to see a message every time a lease's state
+ changes. */
+/* #define DEBUG_LEASE_STATE_TRANSITIONS */
+
+/* Define this if you want to maintain a history of the last N operations
+ that changed reference counts on objects. This can be used to debug
+ cases where an object is dereferenced too often, or not often enough. */
+
+/* #define DEBUG_RC_HISTORY */
+
+/* Define this if you want to see the history every cycle. */
+
+/* #define DEBUG_RC_HISTORY_EXHAUSTIVELY */
+
+/* This is the number of history entries to maintain - by default, 256. */
+
+/* #define RC_HISTORY_MAX 10240 */
+
+/* Define this if you want dhcpd to dump core when a non-fatal memory
+ allocation error is detected (i.e., something that would cause a
+ memory leak rather than a memory smash). */
+
+/* #define POINTER_DEBUG */
+
+/* Define this if you want debugging output for DHCP failover protocol
+ messages. */
+
+/* #define DEBUG_FAILOVER_MESSAGES */
+
+/* Define this to include contact messages in failover message debugging.
+ The contact messages are sent once per second, so this can generate a
+ lot of log entries. */
+
+/* #define DEBUG_FAILOVER_CONTACT_MESSAGES */
+
+/* Define this if you want debugging output for DHCP failover protocol
+ event timeout timing. */
+
+/* #define DEBUG_FAILOVER_TIMING */
+
+/* Define this if you want to include contact message timing, which is
+ performed once per second and can generate a lot of log entries. */
+
+/* #define DEBUG_FAILOVER_CONTACT_TIMING */
+
+/* Define this if you want all leases written to the lease file, even if
+ they are free leases that have never been used. */
+
+/* #define DEBUG_DUMP_ALL_LEASES */
+
+/* Define this if you want to see the requests and replies between the
+ DHCP code and the DNS library code. */
+
+/* #define DEBUG_DNS_UPDATES */
+
+/* Define this if you want DHCP failover protocol support in the DHCP
+ server. */
+
+/* #define FAILOVER_PROTOCOL */
+
+/* Define this if you want DNS update functionality to be available. */
+
+#define NSUPDATE
+
+/* Define this if you want the dhcpd.pid file to go somewhere other than
+ the default (which varies from system to system, but is usually either
+ /etc or /var/run. */
+
+/* #define _PATH_DHCPD_PID "/var/run/dhcpd.pid" */
+
+/* Define this if you want the dhcpd.leases file (the dynamic lease database)
+ to go somewhere other than the default location, which is normally
+ /etc/dhcpd.leases. */
+
+/* #define _PATH_DHCPD_DB "/etc/dhcpd.leases" */
+
+/* Define this if you want the dhcpd.conf file to go somewhere other than
+ the default location. By default, it goes in /etc/dhcpd.conf. */
+
+/* #define _PATH_DHCPD_CONF "/etc/dhcpd.conf" */
+
+/* Network API definitions. You do not need to choose one of these - if
+ you don't choose, one will be chosen for you in your system's config
+ header. DON'T MESS WITH THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!! */
+
+/* Define USE_SOCKETS to use the standard BSD socket API.
+
+ On many systems, the BSD socket API does not provide the ability to
+ send packets to the 255.255.255.255 broadcast address, which can
+ prevent some clients (e.g., Win95) from seeing replies. This is
+ not a problem on Solaris.
+
+ In addition, the BSD socket API will not work when more than one
+ network interface is configured on the server.
+
+ However, the BSD socket API is about as efficient as you can get, so if
+ the aforementioned problems do not matter to you, or if no other
+ API is supported for your system, you may want to go with it. */
+
+/* #define USE_SOCKETS */
+
+/* Define this to use the Sun Streams NIT API.
+
+ The Sun Streams NIT API is only supported on SunOS 4.x releases. */
+
+/* #define USE_NIT */
+
+/* Define this to use the Berkeley Packet Filter API.
+
+ The BPF API is available on all 4.4-BSD derivatives, including
+ NetBSD, FreeBSD and BSDI's BSD/OS. It's also available on
+ DEC Alpha OSF/1 in a compatibility mode supported by the Alpha OSF/1
+ packetfilter interface. */
+
+/* #define USE_BPF */
+
+/* Define this to use the raw socket API.
+
+ The raw socket API is provided on many BSD derivatives, and provides
+ a way to send out raw IP packets. It is only supported for sending
+ packets - packets must be received with the regular socket API.
+ This code is experimental - I've never gotten it to actually transmit
+ a packet to the 255.255.255.255 broadcast address - so use it at your
+ own risk. */
+
+/* #define USE_RAW_SOCKETS */
+
+/* Define this to change the logging facility used by dhcpd. */
+
+/* #define DHCPD_LOG_FACILITY LOG_DAEMON */
+
+
+/* Define this if you want to be able to execute external commands
+ during conditional evaluation. */
+
+/* #define ENABLE_EXECUTE */
+
+/* Define this if you aren't debugging and you want to save memory
+ (potentially a _lot_ of memory) by allocating leases in chunks rather
+ than one at a time. */
+
+#define COMPACT_LEASES
+
+/* Define this if you want to be able to save and playback server operational
+ traces. */
+
+/* #define TRACING */
+
+/* Define this if you want the server to use the previous behavior
+ when determining the DDNS TTL. If the user has specified a ddns-ttl
+ option that is used to detemine the ttl. (If the user specifies
+ an option that references the lease structure it is only usable
+ for v4. In that case v6 will use the default.) Otherwise when
+ defined the defaults are: v4 - 1/2 the lease time,
+ v6 - DEFAULT_DDNS_TTL. When undefined the defaults are 1/2 the
+ (preferred) lease time for both but with a cap on the maximum. */
+
+/* #define USE_OLD_DDNS_TTL */
+
+/* Define this if you want a DHCPv6 server to send replies to the
+ source port of the message it received. This is useful for testing
+ but is only included for backwards compatibility. */
+/* #define REPLY_TO_SOURCE_PORT */
+
+/* Define this if you want to enable strict checks in DNS Updates mechanism.
+ Do not enable this unless are DHCP developer. */
+/* #define DNS_UPDATES_MEMORY_CHECKS */
+
+/* Define this if you want to allow domain list in domain-name option.
+ RFC2132 does not allow that behavior, but it is somewhat used due
+ to historic reasons. Note that it may be removed some time in the
+ future. */
+
+#define ACCEPT_LIST_IN_DOMAIN_NAME
diff --git a/includes/statement.h b/includes/statement.h
new file mode 100644
index 0000000..2d30b89
--- /dev/null
+++ b/includes/statement.h
@@ -0,0 +1,110 @@
+/* statement.h
+
+ Definitions for executable statements... */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+struct executable_statement {
+ int refcnt;
+ struct executable_statement *next;
+ enum statement_op {
+ null_statement,
+ if_statement,
+ add_statement,
+ eval_statement,
+ break_statement,
+ default_option_statement,
+ supersede_option_statement,
+ append_option_statement,
+ prepend_option_statement,
+ send_option_statement,
+ statements_statement,
+ on_statement,
+ switch_statement,
+ case_statement,
+ default_statement,
+ set_statement,
+ unset_statement,
+ let_statement,
+ define_statement,
+ log_statement,
+ return_statement,
+ execute_statement
+ } op;
+ union {
+ struct {
+ struct executable_statement *tc, *fc;
+ struct expression *expr;
+ } ie;
+ struct expression *eval;
+ struct expression *retval;
+ struct class *add;
+ struct option_cache *option;
+ struct option_cache *supersede;
+ struct option_cache *prepend;
+ struct option_cache *append;
+ struct executable_statement *statements;
+ struct {
+ int evtypes;
+# define ON_COMMIT 1
+# define ON_EXPIRY 2
+# define ON_RELEASE 4
+# define ON_TRANSMISSION 8
+ struct executable_statement *statements;
+ } on;
+ struct {
+ struct expression *expr;
+ struct executable_statement *statements;
+ } s_switch;
+ struct expression *c_case;
+ struct {
+ char *name;
+ struct expression *expr;
+ struct executable_statement *statements;
+ } set, let;
+ char *unset;
+ struct {
+ enum {
+ log_priority_fatal,
+ log_priority_error,
+ log_priority_debug,
+ log_priority_info
+ } priority;
+ struct expression *expr;
+ } log;
+ struct {
+ char *command;
+ struct expression *arglist;
+ int argc;
+ } execute;
+ } data;
+};
+
diff --git a/includes/t_api.h b/includes/t_api.h
new file mode 100644
index 0000000..a8396ff
--- /dev/null
+++ b/includes/t_api.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2004-2007,2009 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: t_api.h,v 1.3.8.1 2009-11-20 01:49:01 sar Exp $ */
+
+#ifndef TESTS_T_API_H
+#define TESTS_T_API_H 1
+
+/*! \file tests/t_api.h */
+
+#include <stdio.h>
+
+#include <omapip/result.h>
+#include <isc/lang.h>
+#include <isc/formatcheck.h>
+
+/*
+ *
+ * Result codes.
+ *
+ */
+
+#define T_PASS 0x1
+#define T_FAIL 0x2
+#define T_UNRESOLVED 0x3
+#define T_UNSUPPORTED 0x4
+#define T_UNTESTED 0x5
+#define T_THREADONLY 0x6
+
+/*
+ *
+ * Assertion class codes.
+ *
+ */
+
+#define T_OPTIONAL 0x0
+#define T_REQUIRED 0x1
+
+/*
+ * Misc
+ */
+
+#define T_MAXTOKS 16
+#define T_ARG(n) (*(av + (n)))
+
+typedef void (*PFV)(void);
+
+typedef struct {
+ PFV pfv;
+ const char *func_name;
+} testspec_t;
+
+extern int T_debug;
+extern testspec_t T_testlist[];
+
+ISC_LANG_BEGINDECLS
+
+void
+t_assert(const char *component, int anum, int class, const char *what, ...)
+ ISC_FORMAT_PRINTF(4, 5);
+
+void
+t_info(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+t_result(int result);
+
+char *
+t_getenv(const char *name);
+
+char *
+t_fgetbs(FILE *fp);
+
+isc_result_t
+t_dns_result_fromtext(char *result);
+
+unsigned int
+t_dc_method_fromtext(char *dc_method);
+
+int
+t_bustline(char *line, char **toks);
+
+int
+t_eval(const char *filename, int (*func)(char **), int nargs);
+
+ISC_LANG_ENDDECLS
+
+#endif /* TESTS_T_API_H */
+
diff --git a/includes/tree.h b/includes/tree.h
new file mode 100644
index 0000000..291c0f6
--- /dev/null
+++ b/includes/tree.h
@@ -0,0 +1,355 @@
+/* tree.h
+
+ Definitions for address trees... */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* A pair of pointers, suitable for making a linked list. */
+typedef struct _pair {
+ caddr_t car;
+ struct _pair *cdr;
+} *pair;
+
+struct option_chain_head {
+ int refcnt;
+ pair first;
+};
+
+struct enumeration_value {
+ const char *name;
+ u_int8_t value;
+};
+
+struct enumeration {
+ struct enumeration *next;
+ const char *name;
+ unsigned width;
+ struct enumeration_value *values;
+};
+
+/* Tree node types... */
+#define TREE_CONCAT 1
+#define TREE_HOST_LOOKUP 2
+#define TREE_CONST 3
+#define TREE_LIMIT 4
+#define TREE_DATA_EXPR 5
+
+/* A data buffer with a reference count. */
+struct buffer {
+ int refcnt;
+ unsigned char data [1];
+};
+
+/* XXX The mechanism by which data strings are returned is currently
+ XXX broken: rather than returning an ephemeral pointer, we create
+ XXX a reference to the data in the caller's space, which the caller
+ XXX then has to dereference - instead, the reference should be
+ XXX ephemeral by default and be made a persistent reference explicitly. */
+/* XXX on the other hand, it seems to work pretty nicely, so maybe the
+ XXX above comment is meshuggenah. */
+/* XXX I think the above comment tries to say this:
+ XXX http://tinyurl.com/2tjqre */
+
+/* A string of data bytes, possibly accompanied by a larger buffer. */
+struct data_string {
+ struct buffer *buffer;
+ const unsigned char *data;
+ unsigned len; /* Does not include NUL terminator, if any. */
+ int terminated;
+};
+
+enum expression_context {
+ context_any, /* indefinite */
+ context_boolean,
+ context_data,
+ context_numeric,
+ context_dns,
+ context_data_or_numeric, /* indefinite */
+ context_function
+};
+
+struct fundef {
+ int refcnt;
+ struct string_list *args;
+ struct executable_statement *statements;
+};
+
+struct binding_value {
+ int refcnt;
+ enum {
+ binding_boolean,
+ binding_data,
+ binding_numeric,
+ binding_dns,
+ binding_function
+ } type;
+ union value {
+ struct data_string data;
+ unsigned long intval;
+ int boolean;
+#if defined (NSUPDATE_OLD)
+ ns_updrec *dns;
+#endif
+ struct fundef *fundef;
+ struct binding_value *bv;
+ } value;
+};
+
+struct binding {
+ struct binding *next;
+ char *name;
+ struct binding_value *value;
+};
+
+struct binding_scope {
+ int refcnt;
+ struct binding_scope *outer;
+ struct binding *bindings;
+};
+
+/* Expression tree structure. */
+
+enum expr_op {
+ expr_none,
+ expr_match,
+ expr_check,
+ expr_equal,
+ expr_substring,
+ expr_suffix,
+ expr_concat,
+ expr_host_lookup,
+ expr_and,
+ expr_or,
+ expr_not,
+ expr_option,
+ expr_hardware,
+ expr_packet,
+ expr_const_data,
+ expr_extract_int8,
+ expr_extract_int16,
+ expr_extract_int32,
+ expr_encode_int8,
+ expr_encode_int16,
+ expr_encode_int32,
+ expr_const_int,
+ expr_exists,
+ expr_encapsulate,
+ expr_known,
+ expr_reverse,
+ expr_leased_address,
+ expr_binary_to_ascii,
+ expr_config_option,
+ expr_host_decl_name,
+ expr_pick_first_value,
+ expr_lease_time,
+ expr_dns_transaction,
+ expr_static,
+ expr_ns_add,
+ expr_ns_delete,
+ expr_ns_exists,
+ expr_ns_not_exists,
+ expr_not_equal,
+ expr_null,
+ expr_variable_exists,
+ expr_variable_reference,
+ expr_filename,
+ expr_sname,
+ expr_arg,
+ expr_funcall,
+ expr_function,
+ expr_add,
+ expr_subtract,
+ expr_multiply,
+ expr_divide,
+ expr_remainder,
+ expr_binary_and,
+ expr_binary_or,
+ expr_binary_xor,
+ expr_client_state,
+ expr_ucase,
+ expr_lcase,
+ expr_regex_match,
+ expr_iregex_match,
+ expr_gethostname
+};
+
+struct expression {
+ int refcnt;
+ enum expr_op op;
+ union expr_union {
+ struct {
+ struct expression *expr;
+ struct expression *offset;
+ struct expression *len;
+ } substring;
+ struct expression *equal [2];
+ struct expression *and [2];
+ struct expression *or [2];
+ struct expression *not;
+ struct expression *add;
+ struct expression *subtract;
+ struct expression *multiply;
+ struct expression *divide;
+ struct expression *remainder;
+ struct collection *check;
+ struct {
+ struct expression *expr;
+ struct expression *len;
+ } suffix;
+ struct expression *lcase;
+ struct expression *ucase;
+ struct option *option;
+ struct option *config_option;
+ struct {
+ struct expression *offset;
+ struct expression *len;
+ } packet;
+ struct data_string const_data;
+ struct expression *extract_int;
+ struct expression *encode_int;
+ unsigned long const_int;
+ struct expression *concat [2];
+ struct dns_host_entry *host_lookup;
+ struct option *exists;
+ struct data_string encapsulate;
+ struct {
+ struct expression *base;
+ struct expression *width;
+ struct expression *separator;
+ struct expression *buffer;
+ } b2a;
+ struct {
+ struct expression *width;
+ struct expression *buffer;
+ } reverse;
+ struct {
+ struct expression *car;
+ struct expression *cdr;
+ } pick_first_value;
+ struct {
+ struct expression *car;
+ struct expression *cdr;
+ } dns_transaction;
+ struct {
+ unsigned rrclass;
+ unsigned rrtype;
+ struct expression *rrname;
+ struct expression *rrdata;
+ struct expression *ttl;
+ } ns_add;
+ struct {
+ unsigned rrclass;
+ unsigned rrtype;
+ struct expression *rrname;
+ struct expression *rrdata;
+ } ns_delete, ns_exists, ns_not_exists;
+ char *variable;
+ struct {
+ struct expression *val;
+ struct expression *next;
+ } arg;
+ struct {
+ char *name;
+ struct expression *arglist;
+ } funcall;
+ struct fundef *func;
+ } data;
+ int flags;
+# define EXPR_EPHEMERAL 1
+};
+
+/* DNS host entry structure... */
+struct dns_host_entry {
+ int refcnt;
+ TIME timeout;
+ struct data_string data;
+ char hostname [1];
+};
+
+struct option_cache; /* forward */
+struct packet; /* forward */
+struct option_state; /* forward */
+struct decoded_option_state; /* forward */
+struct lease; /* forward */
+struct client_state; /* forward */
+
+struct universe {
+ const char *name;
+ struct option_cache *(*lookup_func) (struct universe *,
+ struct option_state *,
+ unsigned);
+ void (*save_func) (struct universe *, struct option_state *,
+ struct option_cache *, isc_boolean_t);
+ void (*foreach) (struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **, struct universe *, void *,
+ void (*) (struct option_cache *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *));
+ void (*delete_func) (struct universe *universe,
+ struct option_state *, int);
+ int (*option_state_dereference) (struct universe *,
+ struct option_state *,
+ const char *, int);
+ int (*decode) (struct option_state *,
+ const unsigned char *, unsigned, struct universe *);
+ int (*encapsulate) (struct data_string *, struct packet *,
+ struct lease *, struct client_state *,
+ struct option_state *, struct option_state *,
+ struct binding_scope **,
+ struct universe *);
+ u_int32_t (*get_tag) (const unsigned char *);
+ void (*store_tag) (unsigned char *, u_int32_t);
+ u_int32_t (*get_length) (const unsigned char *);
+ void (*store_length) (unsigned char *, u_int32_t);
+ int tag_size, length_size;
+ unsigned site_code_min, end;
+ option_name_hash_t *name_hash;
+ option_code_hash_t *code_hash;
+ struct option *enc_opt;
+ int index;
+
+ /* Flags should probably become condensed. */
+ int concat_duplicates;
+};
+
+struct option {
+ const char *name;
+ const char *format;
+ struct universe *universe;
+ unsigned code;
+ int refcnt;
+};
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..4d4a951
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,323 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2005-05-14.22
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+chmodcmd="$chmodprog 0755"
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c (ignored)
+-d create directories instead of installing files.
+-g GROUP $chgrpprog installed files to GROUP.
+-m MODE $chmodprog installed files to MODE.
+-o USER $chownprog installed files to USER.
+-s $stripprog installed files.
+-t DIRECTORY install into DIRECTORY.
+-T report an error if DSTFILE is a directory.
+--help display this help and exit.
+--version display version info and exit.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test -n "$1"; do
+ case $1 in
+ -c) shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd=$stripprog
+ shift
+ continue;;
+
+ -t) dstarg=$2
+ shift
+ shift
+ continue;;
+
+ -T) no_target_directory=true
+ shift
+ continue;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ *) # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ test -n "$dir_arg$dstarg" && break
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dstarg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dstarg"
+ shift # fnord
+ fi
+ shift # arg
+ dstarg=$arg
+ done
+ break;;
+ esac
+done
+
+if test -z "$1"; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src ;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ src=
+
+ if test -d "$dst"; then
+ mkdircmd=:
+ chmodcmd=
+ else
+ mkdircmd=$mkdirprog
+ fi
+ else
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dstarg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dstarg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst ;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dstarg: Is a directory" >&2
+ exit 1
+ fi
+ dst=$dst/`basename "$src"`
+ fi
+ fi
+
+ # This sed command emulates the dirname command.
+ dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
+
+ # Make sure that the destination directory exists.
+
+ # Skip lots of stat calls in the usual case.
+ if test ! -d "$dstdir"; then
+ defaultIFS='
+ '
+ IFS="${IFS-$defaultIFS}"
+
+ oIFS=$IFS
+ # Some sh's can't handle IFS=/ for some reason.
+ IFS='%'
+ set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
+ shift
+ IFS=$oIFS
+
+ pathcomp=
+
+ while test $# -ne 0 ; do
+ pathcomp=$pathcomp$1
+ shift
+ if test ! -d "$pathcomp"; then
+ $mkdirprog "$pathcomp"
+ # mkdir can fail with a `File exist' error in case several
+ # install-sh are creating the directory concurrently. This
+ # is OK.
+ test -d "$pathcomp" || exit
+ fi
+ pathcomp=$pathcomp/
+ done
+ fi
+
+ if test -n "$dir_arg"; then
+ $doit $mkdircmd "$dst" \
+ && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
+ && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
+
+ else
+ dstfile=`basename "$dst"`
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Copy the file name to the temp name.
+ $doit $cpprog "$src" "$dsttmp" &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
+
+ # Now rename the file to the real destination.
+ { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
+ || {
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ if test -f "$dstdir/$dstfile"; then
+ $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
+ || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
+ || {
+ echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
+ (exit 1); exit 1
+ }
+ else
+ :
+ fi
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
+ }
+ }
+ fi || { (exit 1); exit 1; }
+done
+
+# The final little trick to "correctly" pass the exit status to the exit trap.
+{
+ (exit 0); exit 0
+}
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/missing b/missing
new file mode 100755
index 0000000..894e786
--- /dev/null
+++ b/missing
@@ -0,0 +1,360 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2005-06-08.21
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005
+# Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case "$1" in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# Now exit if we have it, but it failed. Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).
+case "$1" in
+ lex|yacc)
+ # Not GNU programs, they don't have --version.
+ ;;
+
+ tar)
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ exit 1
+ fi
+ ;;
+
+ *)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ # Could not run --version or --help. This is probably someone
+ # running `$TOOL --version' or `$TOOL --help' to check whether
+ # $TOOL exists and not knowing $TOOL uses missing.
+ exit 1
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case "$f" in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
+ test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison|yacc)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f y.tab.h ]; then
+ echo >y.tab.h
+ fi
+ if [ ! -f y.tab.c ]; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex|flex)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if [ $# -ne 1 ]; then
+ eval LASTARG="\${$#}"
+ case "$LASTARG" in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if [ -f "$SRCFILE" ]; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if [ ! -f lex.yy.c ]; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+ fi
+ if [ -f "$file" ]; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit 1
+ fi
+ ;;
+
+ makeinfo)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ # The file to touch is that specified with -o ...
+ file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+ if test -z "$file"; then
+ # ... or it is the one specified with @setfilename ...
+ infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile`
+ # ... or it is derived from the source name (dir/f.texi becomes f.info)
+ test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+ fi
+ # If the file does not exist, the user really needs makeinfo;
+ # let's fail without touching anything.
+ test -f $file || exit 1
+ touch $file
+ ;;
+
+ tar)
+ shift
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar "$@" && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar "$@" && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case "$firstarg" in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ case "$firstarg" in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/omapip/Makefile.am b/omapip/Makefile.am
new file mode 100644
index 0000000..595950a
--- /dev/null
+++ b/omapip/Makefile.am
@@ -0,0 +1,14 @@
+lib_LIBRARIES = libomapi.a
+noinst_PROGRAMS = svtest
+
+libomapi_a_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \
+ errwarn.c listener.c dispatch.c generic.c support.c \
+ handle.c message.c convert.c hash.c auth.c inet_addr.c \
+ array.c trace.c toisc.c iscprint.c isclib.c
+
+man_MANS = omapi.3
+EXTRA_DIST = $(man_MANS)
+
+svtest_SOURCES = test.c
+svtest_LDADD = libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a
+
diff --git a/omapip/Makefile.in b/omapip/Makefile.in
new file mode 100644
index 0000000..657ded4
--- /dev/null
+++ b/omapip/Makefile.in
@@ -0,0 +1,529 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+noinst_PROGRAMS = svtest$(EXEEXT)
+subdir = omapip
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)"
+libLIBRARIES_INSTALL = $(INSTALL_DATA)
+LIBRARIES = $(lib_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+libomapi_a_AR = $(AR) $(ARFLAGS)
+libomapi_a_LIBADD =
+am_libomapi_a_OBJECTS = protocol.$(OBJEXT) buffer.$(OBJEXT) \
+ alloc.$(OBJEXT) result.$(OBJEXT) connection.$(OBJEXT) \
+ errwarn.$(OBJEXT) listener.$(OBJEXT) dispatch.$(OBJEXT) \
+ generic.$(OBJEXT) support.$(OBJEXT) handle.$(OBJEXT) \
+ message.$(OBJEXT) convert.$(OBJEXT) hash.$(OBJEXT) \
+ auth.$(OBJEXT) inet_addr.$(OBJEXT) array.$(OBJEXT) \
+ trace.$(OBJEXT) toisc.$(OBJEXT) iscprint.$(OBJEXT) \
+ isclib.$(OBJEXT)
+libomapi_a_OBJECTS = $(am_libomapi_a_OBJECTS)
+PROGRAMS = $(noinst_PROGRAMS)
+am_svtest_OBJECTS = test.$(OBJEXT)
+svtest_OBJECTS = $(am_svtest_OBJECTS)
+svtest_DEPENDENCIES = libomapi.a ../bind/lib/libdns.a \
+ ../bind/lib/libisc.a
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libomapi_a_SOURCES) $(svtest_SOURCES)
+DIST_SOURCES = $(libomapi_a_SOURCES) $(svtest_SOURCES)
+man3dir = $(mandir)/man3
+NROFF = nroff
+MANS = $(man_MANS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LIBRARIES = libomapi.a
+libomapi_a_SOURCES = protocol.c buffer.c alloc.c result.c connection.c \
+ errwarn.c listener.c dispatch.c generic.c support.c \
+ handle.c message.c convert.c hash.c auth.c inet_addr.c \
+ array.c trace.c toisc.c iscprint.c isclib.c
+
+man_MANS = omapi.3
+EXTRA_DIST = $(man_MANS)
+svtest_SOURCES = test.c
+svtest_LDADD = libomapi.a ../bind/lib/libdns.a ../bind/lib/libisc.a
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign omapip/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign omapip/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLIBRARIES: $(lib_LIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(libLIBRARIES_INSTALL) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(libLIBRARIES_INSTALL) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+ @$(POST_INSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ p=$(am__strip_dir) \
+ echo " $(RANLIB) '$(DESTDIR)$(libdir)/$$p'"; \
+ $(RANLIB) "$(DESTDIR)$(libdir)/$$p"; \
+ else :; fi; \
+ done
+
+uninstall-libLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLIBRARIES:
+ -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
+libomapi.a: $(libomapi_a_OBJECTS) $(libomapi_a_DEPENDENCIES)
+ -rm -f libomapi.a
+ $(libomapi_a_AR) libomapi.a $(libomapi_a_OBJECTS) $(libomapi_a_LIBADD)
+ $(RANLIB) libomapi.a
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+svtest$(EXEEXT): $(svtest_OBJECTS) $(svtest_DEPENDENCIES)
+ @rm -f svtest$(EXEEXT)
+ $(LINK) $(svtest_OBJECTS) $(svtest_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/convert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispatch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errwarn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/inet_addr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isclib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iscprint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listener.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/result.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/toisc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man3: $(man3_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 3*) ;; \
+ *) ext='3' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst"; \
+ done
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 3*) ;; \
+ *) ext='3' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man3dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man3dir)/$$inst"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLIBRARIES clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-libLIBRARIES
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man3
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libLIBRARIES uninstall-man
+
+uninstall-man: uninstall-man3
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLIBRARIES clean-noinstPROGRAMS ctags distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libLIBRARIES install-man \
+ install-man3 install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags uninstall uninstall-am uninstall-libLIBRARIES \
+ uninstall-man uninstall-man3
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/omapip/alloc.c b/omapip/alloc.c
new file mode 100644
index 0000000..69172a5
--- /dev/null
+++ b/omapip/alloc.c
@@ -0,0 +1,1169 @@
+/* alloc.c
+
+ Functions supporting memory allocation for the object management
+ protocol... */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+struct dmalloc_preamble *dmalloc_list;
+unsigned long dmalloc_outstanding;
+unsigned long dmalloc_longterm;
+unsigned long dmalloc_generation;
+unsigned long dmalloc_cutoff_generation;
+#endif
+
+#if defined (DEBUG_RC_HISTORY)
+struct rc_history_entry rc_history [RC_HISTORY_MAX];
+int rc_history_index;
+int rc_history_count;
+#endif
+
+#if defined (DEBUG_RC_HISTORY)
+static void print_rc_hist_entry (int);
+#endif
+
+void *
+dmalloc(unsigned size, const char *file, int line) {
+ unsigned char *foo;
+ unsigned len;
+ void **bar;
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ int i;
+ struct dmalloc_preamble *dp;
+#endif
+
+ len = size + DMDSIZE;
+ if (len < size)
+ return NULL;
+
+ foo = malloc(len);
+
+ if (!foo)
+ return NULL;
+ bar = (void *)(foo + DMDOFFSET);
+ memset (bar, 0, size);
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ dp = (struct dmalloc_preamble *)foo;
+ dp -> prev = dmalloc_list;
+ if (dmalloc_list)
+ dmalloc_list -> next = dp;
+ dmalloc_list = dp;
+ dp -> next = (struct dmalloc_preamble *)0;
+ dp -> size = size;
+ dp -> file = file;
+ dp -> line = line;
+ dp -> generation = dmalloc_generation++;
+ dmalloc_outstanding += size;
+ for (i = 0; i < DMLFSIZE; i++)
+ dp -> low_fence [i] =
+ (((unsigned long)
+ (&dp -> low_fence [i])) % 143) + 113;
+ for (i = DMDOFFSET; i < DMDSIZE; i++)
+ foo [i + size] =
+ (((unsigned long)
+ (&foo [i + size])) % 143) + 113;
+#if defined (DEBUG_MALLOC_POOL_EXHAUSTIVELY)
+ /* Check _every_ entry in the pool! Very expensive. */
+ for (dp = dmalloc_list; dp; dp = dp -> prev) {
+ for (i = 0; i < DMLFSIZE; i++) {
+ if (dp -> low_fence [i] !=
+ (((unsigned long)
+ (&dp -> low_fence [i])) % 143) + 113)
+ {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+ foo = (unsigned char *)dp;
+ for (i = DMDOFFSET; i < DMDSIZE; i++) {
+ if (foo [i + dp -> size] !=
+ (((unsigned long)
+ (&foo [i + dp -> size])) % 143) + 113) {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+ }
+#endif
+#endif
+#ifdef DEBUG_REFCNT_DMALLOC_FREE
+ rc_register (file, line, 0, foo + DMDOFFSET, 1, 0, RC_MALLOC);
+#endif
+ return bar;
+}
+
+void
+dfree(void *ptr, const char *file, int line) {
+ if (!ptr) {
+ log_error ("dfree %s(%d): free on null pointer.", file, line);
+ return;
+ }
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ {
+ unsigned char *bar = ptr;
+ struct dmalloc_preamble *dp, *cur;
+ int i;
+ bar -= DMDOFFSET;
+ cur = (struct dmalloc_preamble *)bar;
+ for (dp = dmalloc_list; dp; dp = dp -> prev)
+ if (dp == cur)
+ break;
+ if (!dp) {
+ log_error ("%s(%d): freeing unknown memory: %lx",
+ file, line, (unsigned long)cur);
+ abort ();
+ }
+ if (dp -> prev)
+ dp -> prev -> next = dp -> next;
+ if (dp -> next)
+ dp -> next -> prev = dp -> prev;
+ if (dp == dmalloc_list)
+ dmalloc_list = dp -> prev;
+ if (dp -> generation >= dmalloc_cutoff_generation)
+ dmalloc_outstanding -= dp -> size;
+ else
+ dmalloc_longterm -= dp -> size;
+
+ for (i = 0; i < DMLFSIZE; i++) {
+ if (dp -> low_fence [i] !=
+ (((unsigned long)
+ (&dp -> low_fence [i])) % 143) + 113)
+ {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+ for (i = DMDOFFSET; i < DMDSIZE; i++) {
+ if (bar [i + dp -> size] !=
+ (((unsigned long)
+ (&bar [i + dp -> size])) % 143) + 113) {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+ ptr = bar;
+ }
+#endif
+#ifdef DEBUG_REFCNT_DMALLOC_FREE
+ rc_register (file, line,
+ 0, (unsigned char *)ptr + DMDOFFSET, 0, 1, RC_MALLOC);
+#endif
+ free (ptr);
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+/* For allocation functions that keep their own free lists, we want to
+ account for the reuse of the memory. */
+
+void
+dmalloc_reuse(void *foo, const char *file, int line, int justref) {
+ struct dmalloc_preamble *dp;
+
+ /* Get the pointer to the dmalloc header. */
+ dp = foo;
+ dp--;
+
+ /* If we just allocated this and are now referencing it, this
+ function would almost be a no-op, except that it would
+ increment the generation count needlessly. So just return
+ in this case. */
+ if (dp -> generation == dmalloc_generation)
+ return;
+
+ /* If this is longterm data, and we just made reference to it,
+ don't put it on the short-term list or change its name -
+ we don't need to know about this. */
+ if (dp -> generation < dmalloc_cutoff_generation && justref)
+ return;
+
+ /* Take it out of the place in the allocated list where it was. */
+ if (dp -> prev)
+ dp -> prev -> next = dp -> next;
+ if (dp -> next)
+ dp -> next -> prev = dp -> prev;
+ if (dp == dmalloc_list)
+ dmalloc_list = dp -> prev;
+
+ /* Account for its removal. */
+ if (dp -> generation >= dmalloc_cutoff_generation)
+ dmalloc_outstanding -= dp -> size;
+ else
+ dmalloc_longterm -= dp -> size;
+
+ /* Now put it at the head of the list. */
+ dp -> prev = dmalloc_list;
+ if (dmalloc_list)
+ dmalloc_list -> next = dp;
+ dmalloc_list = dp;
+ dp -> next = (struct dmalloc_preamble *)0;
+
+ /* Change the reference location information. */
+ dp -> file = file;
+ dp -> line = line;
+
+ /* Increment the generation. */
+ dp -> generation = dmalloc_generation++;
+
+ /* Account for it. */
+ dmalloc_outstanding += dp -> size;
+}
+
+void dmalloc_dump_outstanding ()
+{
+ static unsigned long dmalloc_cutoff_point;
+ struct dmalloc_preamble *dp;
+#if defined(DEBUG_MALLOC_POOL)
+ unsigned char *foo;
+ int i;
+#endif
+
+ if (!dmalloc_cutoff_point)
+ dmalloc_cutoff_point = dmalloc_cutoff_generation;
+ for (dp = dmalloc_list; dp; dp = dp -> prev) {
+ if (dp -> generation <= dmalloc_cutoff_point)
+ break;
+#if defined (DEBUG_MALLOC_POOL)
+ for (i = 0; i < DMLFSIZE; i++) {
+ if (dp -> low_fence [i] !=
+ (((unsigned long)
+ (&dp -> low_fence [i])) % 143) + 113)
+ {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+ foo = (unsigned char *)dp;
+ for (i = DMDOFFSET; i < DMDSIZE; i++) {
+ if (foo [i + dp -> size] !=
+ (((unsigned long)
+ (&foo [i + dp -> size])) % 143) + 113) {
+ log_error ("malloc fence modified: %s(%d)",
+ dp -> file, dp -> line);
+ abort ();
+ }
+ }
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ /* Don't count data that's actually on a free list
+ somewhere. */
+ if (dp -> file) {
+#if defined (DEBUG_RC_HISTORY)
+ int i, count, inhistory = 0, noted = 0;
+
+ /* If we have the info, see if this is actually
+ new garbage. */
+ if (rc_history_count < RC_HISTORY_MAX) {
+ count = rc_history_count;
+ } else
+ count = RC_HISTORY_MAX;
+ i = rc_history_index - 1;
+ if (i < 0)
+ i += RC_HISTORY_MAX;
+
+ do {
+ if (rc_history [i].addr == dp + 1) {
+ inhistory = 1;
+ if (!noted) {
+ log_info (" %s(%d): %ld", dp -> file,
+ dp -> line, dp -> size);
+ noted = 1;
+ }
+ print_rc_hist_entry (i);
+ if (!rc_history [i].refcnt)
+ break;
+ }
+ if (--i < 0)
+ i = RC_HISTORY_MAX - 1;
+ } while (count--);
+ if (!inhistory)
+#endif
+ log_info (" %s(%d): %ld",
+ dp -> file, dp -> line, dp -> size);
+ }
+#endif
+ }
+ if (dmalloc_list)
+ dmalloc_cutoff_point = dmalloc_list -> generation;
+}
+#endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */
+
+#if defined (DEBUG_RC_HISTORY)
+static void print_rc_hist_entry (int i)
+{
+ log_info (" referenced by %s(%d)[%lx]: addr = %lx refcnt = %x",
+ rc_history [i].file, rc_history [i].line,
+ (unsigned long)rc_history [i].reference,
+ (unsigned long)rc_history [i].addr,
+ rc_history [i].refcnt);
+}
+
+void dump_rc_history (void *addr)
+{
+ int i;
+
+ i = rc_history_index;
+ if (!rc_history [i].file)
+ i = 0;
+ else if (rc_history_count < RC_HISTORY_MAX) {
+ i -= rc_history_count;
+ if (i < 0)
+ i += RC_HISTORY_MAX;
+ }
+ rc_history_count = 0;
+
+ while (rc_history [i].file) {
+ if (!addr || addr == rc_history [i].addr)
+ print_rc_hist_entry (i);
+ ++i;
+ if (i == RC_HISTORY_MAX)
+ i = 0;
+ if (i == rc_history_index)
+ break;
+ }
+}
+void rc_history_next (int d)
+{
+#if defined (RC_HISTORY_COMPRESSION)
+ int i, j = 0, m, n = 0;
+ void *ap, *rp;
+
+ /* If we are decreasing the reference count, try to find the
+ entry where the reference was made and eliminate it; then
+ we can also eliminate this reference. */
+ if (d) {
+ m = rc_history_index - 1000;
+ if (m < -1)
+ m = -1;
+ ap = rc_history [rc_history_index].addr;
+ rp = rc_history [rc_history_index].reference;
+ for (i = rc_history_index - 1; i > m; i--) {
+ if (rc_history [i].addr == ap) {
+ if (rc_history [i].reference == rp) {
+ if (n > 10) {
+ for (n = i; n <= rc_history_index; n++)
+ print_rc_hist_entry (n);
+ n = 11;
+ }
+ memmove (&rc_history [i],
+ &rc_history [i + 1],
+ (unsigned)((rc_history_index - i) *
+ sizeof (struct rc_history_entry)));
+ --rc_history_count;
+ --rc_history_index;
+ for (j = i; j < rc_history_count; j++) {
+ if (rc_history [j].addr == ap)
+ --rc_history [j].refcnt;
+ }
+ if (n > 10) {
+ for (n = i; n <= rc_history_index; n++)
+ print_rc_hist_entry (n);
+ n = 11;
+ exit (0);
+ }
+ return;
+ }
+ }
+ }
+ }
+#endif
+ if (++rc_history_index == RC_HISTORY_MAX)
+ rc_history_index = 0;
+ ++rc_history_count;
+}
+#endif /* DEBUG_RC_HISTORY */
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+struct caller {
+ struct dmalloc_preamble *dp;
+ int count;
+};
+
+static int dmalloc_find_entry (struct dmalloc_preamble *dp,
+ struct caller *array,
+ int min, int max)
+{
+ int middle;
+
+ middle = (min + max) / 2;
+ if (middle == min)
+ return middle;
+ if (array [middle].dp -> file == dp -> file) {
+ if (array [middle].dp -> line == dp -> line)
+ return middle;
+ else if (array [middle].dp -> line < dp -> line)
+ return dmalloc_find_entry (dp, array, middle, max);
+ else
+ return dmalloc_find_entry (dp, array, 0, middle);
+ } else if (array [middle].dp -> file < dp -> file)
+ return dmalloc_find_entry (dp, array, middle, max);
+ else
+ return dmalloc_find_entry (dp, array, 0, middle);
+}
+
+void omapi_print_dmalloc_usage_by_caller ()
+{
+ struct dmalloc_preamble *dp;
+ int ccur, cmax, i;
+ struct caller cp [1024];
+
+ cmax = 1024;
+ ccur = 0;
+
+ memset (cp, 0, sizeof cp);
+ for (dp = dmalloc_list; dp; dp = dp -> prev) {
+ i = dmalloc_find_entry (dp, cp, 0, ccur);
+ if ((i == ccur ||
+ cp [i].dp -> file != dp -> file ||
+ cp [i].dp -> line != dp -> line) &&
+ ccur == cmax) {
+ log_error ("no space for memory usage summary.");
+ return;
+ }
+ if (i == ccur) {
+ cp [ccur++].dp = dp;
+ cp [i].count = 1;
+ } else if (cp [i].dp -> file < dp -> file ||
+ (cp [i].dp -> file == dp -> file &&
+ cp [i].dp -> line < dp -> line)) {
+ if (i + 1 != ccur)
+ memmove (cp + i + 2, cp + i + 1,
+ (ccur - i) * sizeof *cp);
+ cp [i + 1].dp = dp;
+ cp [i + 1].count = 1;
+ ccur++;
+ } else if (cp [i].dp -> file != dp -> file ||
+ cp [i].dp -> line != dp -> line) {
+ memmove (cp + i + 1,
+ cp + i, (ccur - i) * sizeof *cp);
+ cp [i].dp = dp;
+ cp [i].count = 1;
+ ccur++;
+ } else
+ cp [i].count++;
+#if 0
+ printf ("%d\t%s:%d\n", i, dp -> file, dp -> line);
+ dump_rc_history (dp + 1);
+#endif
+ }
+ for (i = 0; i < ccur; i++) {
+ printf ("%d\t%s:%d\t%d\n", i,
+ cp [i].dp -> file, cp [i].dp -> line, cp [i].count);
+#if defined(DUMP_RC_HISTORY)
+ dump_rc_history (cp [i].dp + 1);
+#endif
+ }
+}
+#endif /* DEBUG_MEMORY_LEAKAGE || DEBUG_MALLOC_POOL */
+
+isc_result_t omapi_object_allocate (omapi_object_t **o,
+ omapi_object_type_t *type,
+ size_t size,
+ const char *file, int line)
+{
+ size_t tsize;
+ omapi_object_t *foo;
+ isc_result_t status;
+
+ if (type -> allocator) {
+ foo = (omapi_object_t *)0;
+ status = (*type -> allocator) (&foo, file, line);
+ tsize = type -> size;
+ } else {
+ status = ISC_R_NOMEMORY;
+ tsize = 0;
+ }
+
+ if (status == ISC_R_NOMEMORY) {
+ if (type -> sizer)
+ tsize = (*type -> sizer) (size);
+ else
+ tsize = type -> size;
+
+ /* Sanity check. */
+ if (tsize < sizeof (omapi_object_t))
+ return DHCP_R_INVALIDARG;
+
+ foo = dmalloc (tsize, file, line);
+ if (!foo)
+ return ISC_R_NOMEMORY;
+ }
+
+ status = omapi_object_initialize (foo, type, size, tsize, file, line);
+ if (status != ISC_R_SUCCESS) {
+ if (type -> freer)
+ (*type -> freer) (foo, file, line);
+ else
+ dfree (foo, file, line);
+ return status;
+ }
+ return omapi_object_reference (o, foo, file, line);
+}
+
+isc_result_t omapi_object_initialize (omapi_object_t *o,
+ omapi_object_type_t *type,
+ size_t usize, size_t psize,
+ const char *file, int line)
+{
+ memset (o, 0, psize);
+ o -> type = type;
+ if (type -> initialize)
+ (*type -> initialize) (o, file, line);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_object_reference (omapi_object_t **r,
+ omapi_object_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!",
+ file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, h -> type -> rc_flag);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_object_dereference (omapi_object_t **h,
+ const char *file, int line)
+{
+ int outer_reference = 0;
+ int inner_reference = 0;
+ int handle_reference = 0;
+ int extra_references;
+ omapi_object_t *p, *hp;
+
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with refcnt of zero!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ /* See if this object's inner object refers to it, but don't
+ count this as a reference if we're being asked to free the
+ reference from the inner object. */
+ if ((*h) -> inner && (*h) -> inner -> outer &&
+ h != &((*h) -> inner -> outer))
+ inner_reference = 1;
+
+ /* Ditto for the outer object. */
+ if ((*h) -> outer && (*h) -> outer -> inner &&
+ h != &((*h) -> outer -> inner))
+ outer_reference = 1;
+
+ /* Ditto for the outer object. The code below assumes that
+ the only reason we'd get a dereference from the handle
+ table is if this function does it - otherwise we'd have to
+ traverse the handle table to find the address where the
+ reference is stored and compare against that, and we don't
+ want to do that if we can avoid it. */
+ if ((*h) -> handle)
+ handle_reference = 1;
+
+ /* If we are getting rid of the last reference other than
+ references to inner and outer objects, or from the handle
+ table, then we must examine all the objects in either
+ direction to see if they hold any non-inner, non-outer,
+ non-handle-table references. If not, we need to free the
+ entire chain of objects. */
+ if ((*h) -> refcnt ==
+ inner_reference + outer_reference + handle_reference + 1) {
+ if (inner_reference || outer_reference || handle_reference) {
+ /* XXX we could check for a reference from the
+ handle table here. */
+ extra_references = 0;
+ for (p = (*h) -> inner;
+ p && !extra_references; p = p -> inner) {
+ extra_references += p -> refcnt;
+ if (p -> inner && p -> inner -> outer == p)
+ --extra_references;
+ if (p -> outer)
+ --extra_references;
+ if (p -> handle)
+ --extra_references;
+ }
+ for (p = (*h) -> outer;
+ p && !extra_references; p = p -> outer) {
+ extra_references += p -> refcnt;
+ if (p -> outer && p -> outer -> inner == p)
+ --extra_references;
+ if (p -> inner)
+ --extra_references;
+ if (p -> handle)
+ --extra_references;
+ }
+ } else
+ extra_references = 0;
+
+ if (!extra_references) {
+ hp = *h;
+ *h = 0;
+ hp -> refcnt--;
+ if (inner_reference)
+ omapi_object_dereference
+ (&hp -> inner, file, line);
+ if (outer_reference)
+ omapi_object_dereference
+ (&hp -> outer, file, line);
+/* if (!hp -> type -> freer) */
+ rc_register (file, line, h, hp,
+ 0, 1, hp -> type -> rc_flag);
+ if (handle_reference) {
+ if (omapi_handle_clear(hp->handle) !=
+ ISC_R_SUCCESS) {
+ log_debug("Attempt to clear null "
+ "handle pointer");
+ }
+ }
+ if (hp -> type -> destroy)
+ (*(hp -> type -> destroy)) (hp, file, line);
+ if (hp -> type -> freer)
+ (hp -> type -> freer (hp, file, line));
+ else
+ dfree (hp, file, line);
+ } else {
+ (*h) -> refcnt--;
+/* if (!(*h) -> type -> freer) */
+ rc_register (file, line,
+ h, *h, (*h) -> refcnt, 1,
+ (*h) -> type -> rc_flag);
+ }
+ } else {
+ (*h) -> refcnt--;
+/* if (!(*h) -> type -> freer) */
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1,
+ (*h) -> type -> rc_flag);
+ }
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_buffer_new (omapi_buffer_t **h,
+ const char *file, int line)
+{
+ omapi_buffer_t *t;
+ isc_result_t status;
+
+ t = (omapi_buffer_t *)dmalloc (sizeof *t, file, line);
+ if (!t)
+ return ISC_R_NOMEMORY;
+ memset (t, 0, sizeof *t);
+ status = omapi_buffer_reference (h, t, file, line);
+ if (status != ISC_R_SUCCESS)
+ dfree (t, file, line);
+ (*h) -> head = sizeof ((*h) -> buf) - 1;
+ return status;
+}
+
+isc_result_t omapi_buffer_reference (omapi_buffer_t **r,
+ omapi_buffer_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!",
+ file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_buffer_dereference (omapi_buffer_t **h,
+ const char *file, int line)
+{
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with refcnt of zero!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ --(*h) -> refcnt;
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC);
+ if ((*h) -> refcnt == 0)
+ dfree (*h, file, line);
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_typed_data_new (const char *file, int line,
+ omapi_typed_data_t **t,
+ omapi_datatype_t type, ...)
+{
+ va_list l;
+ omapi_typed_data_t *new;
+ unsigned len;
+ unsigned val = 0;
+ int intval = 0;
+ char *s = NULL;
+ isc_result_t status;
+ omapi_object_t *obj = NULL;
+
+ va_start (l, type);
+
+ switch (type) {
+ case omapi_datatype_int:
+ len = OMAPI_TYPED_DATA_INT_LEN;
+ intval = va_arg (l, int);
+ break;
+ case omapi_datatype_string:
+ s = va_arg (l, char *);
+ val = strlen (s);
+ len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val;
+ if (len < val) {
+ va_end(l);
+ return DHCP_R_INVALIDARG;
+ }
+ break;
+ case omapi_datatype_data:
+ val = va_arg (l, unsigned);
+ len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val;
+ if (len < val) {
+ va_end(l);
+ return DHCP_R_INVALIDARG;
+ }
+ break;
+ case omapi_datatype_object:
+ len = OMAPI_TYPED_DATA_OBJECT_LEN;
+ obj = va_arg (l, omapi_object_t *);
+ break;
+ default:
+ va_end (l);
+ return DHCP_R_INVALIDARG;
+ }
+ va_end (l);
+
+ new = dmalloc (len, file, line);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, len);
+
+ switch (type) {
+ case omapi_datatype_int:
+ new -> u.integer = intval;
+ break;
+ case omapi_datatype_string:
+ memcpy (new -> u.buffer.value, s, val);
+ new -> u.buffer.len = val;
+ break;
+ case omapi_datatype_data:
+ new -> u.buffer.len = val;
+ break;
+ case omapi_datatype_object:
+ status = omapi_object_reference (&new -> u.object, obj,
+ file, line);
+ if (status != ISC_R_SUCCESS) {
+ dfree (new, file, line);
+ return status;
+ }
+ break;
+ }
+ new -> type = type;
+
+ return omapi_typed_data_reference (t, new, file, line);
+}
+
+isc_result_t omapi_typed_data_reference (omapi_typed_data_t **r,
+ omapi_typed_data_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **h,
+ const char *file, int line)
+{
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with refcnt of zero!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ --((*h) -> refcnt);
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC);
+ if ((*h) -> refcnt <= 0 ) {
+ switch ((*h) -> type) {
+ case omapi_datatype_int:
+ case omapi_datatype_string:
+ case omapi_datatype_data:
+ default:
+ break;
+ case omapi_datatype_object:
+ omapi_object_dereference (&(*h) -> u.object,
+ file, line);
+ break;
+ }
+ dfree (*h, file, line);
+ }
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_data_string_new (omapi_data_string_t **d, unsigned len,
+ const char *file, int line)
+{
+ omapi_data_string_t *new;
+ unsigned nlen;
+
+ nlen = OMAPI_DATA_STRING_EMPTY_SIZE + len;
+ if (nlen < len)
+ return DHCP_R_INVALIDARG;
+ new = dmalloc (nlen, file, line);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, OMAPI_DATA_STRING_EMPTY_SIZE);
+ new -> len = len;
+ return omapi_data_string_reference (d, new, file, line);
+}
+
+isc_result_t omapi_data_string_reference (omapi_data_string_t **r,
+ omapi_data_string_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_data_string_dereference (omapi_data_string_t **h,
+ const char *file, int line)
+{
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with refcnt of zero!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ --((*h) -> refcnt);
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC);
+ if ((*h) -> refcnt <= 0 ) {
+ dfree (*h, file, line);
+ }
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_value_new (omapi_value_t **d,
+ const char *file, int line)
+{
+ omapi_value_t *new;
+
+ new = dmalloc (sizeof *new, file, line);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, sizeof *new);
+ return omapi_value_reference (d, new, file, line);
+}
+
+isc_result_t omapi_value_reference (omapi_value_t **r,
+ omapi_value_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!",
+ file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_value_dereference (omapi_value_t **h,
+ const char *file, int line)
+{
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with refcnt of zero!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ --((*h) -> refcnt);
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC);
+ if ((*h) -> refcnt == 0) {
+ if ((*h) -> name)
+ omapi_data_string_dereference (&(*h) -> name,
+ file, line);
+ if ((*h) -> value)
+ omapi_typed_data_dereference (&(*h) -> value,
+ file, line);
+ dfree (*h, file, line);
+ }
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_addr_list_new (omapi_addr_list_t **d, unsigned count,
+ const char *file, int line)
+{
+ omapi_addr_list_t *new;
+
+ new = dmalloc ((count * sizeof (omapi_addr_t)) +
+ sizeof (omapi_addr_list_t), file, line);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, ((count * sizeof (omapi_addr_t)) +
+ sizeof (omapi_addr_list_t)));
+ new -> count = count;
+ new -> addresses = (omapi_addr_t *)(new + 1);
+ return omapi_addr_list_reference (d, new, file, line);
+}
+
+isc_result_t omapi_addr_list_reference (omapi_addr_list_t **r,
+ omapi_addr_list_t *h,
+ const char *file, int line)
+{
+ if (!h || !r)
+ return DHCP_R_INVALIDARG;
+
+ if (*r) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): reference store into non-null pointer!",
+ file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+ *r = h;
+ h -> refcnt++;
+ rc_register (file, line, r, h, h -> refcnt, 0, RC_MISC);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_addr_list_dereference (omapi_addr_list_t **h,
+ const char *file, int line)
+{
+ if (!h)
+ return DHCP_R_INVALIDARG;
+
+ if (!*h) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of null pointer!", file, line);
+ abort ();
+#else
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ if ((*h) -> refcnt <= 0) {
+#if defined (POINTER_DEBUG)
+ log_error ("%s(%d): dereference of pointer with zero refcnt!",
+ file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (*h);
+#endif
+ abort ();
+#else
+ *h = 0;
+ return DHCP_R_INVALIDARG;
+#endif
+ }
+
+ --((*h) -> refcnt);
+ rc_register (file, line, h, *h, (*h) -> refcnt, 1, RC_MISC);
+ if ((*h) -> refcnt <= 0 ) {
+ dfree (*h, file, line);
+ }
+ *h = 0;
+ return ISC_R_SUCCESS;
+}
+
diff --git a/omapip/array.c b/omapip/array.c
new file mode 100644
index 0000000..0a3ac61
--- /dev/null
+++ b/omapip/array.c
@@ -0,0 +1,162 @@
+/* listener.c
+
+ Subroutines that support the omapi extensible array type. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+/* Allocate a new extensible array. */
+
+isc_result_t omapi_array_allocate (omapi_array_t **array,
+ omapi_array_ref_t ref,
+ omapi_array_deref_t deref,
+ const char *file, int line)
+{
+ omapi_array_t *aptr;
+
+ if (!array || *array)
+ return DHCP_R_INVALIDARG;
+ aptr = dmalloc (sizeof (omapi_array_t),file, line);
+ if (!aptr)
+ return ISC_R_NOMEMORY;
+ *array = aptr;
+ aptr -> ref = ref;
+ aptr -> deref = deref;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_array_free (omapi_array_t **array,
+ const char *file, int line)
+{
+ omapi_array_t *aptr;
+ int i;
+
+ if (!array || !*array)
+ return DHCP_R_INVALIDARG;
+ aptr = *array;
+ for (i = 0; i < aptr -> count; i++)
+ if (aptr -> data [i] && aptr -> deref)
+ (*aptr -> deref) (&aptr -> data [i], file, line);
+ dfree (aptr -> data, MDL);
+ dfree (aptr, MDL);
+ *array = (omapi_array_t *)0;
+ return ISC_R_SUCCESS;
+}
+
+/* Extend the size of the array by one entry (we may allocate more than that)
+ and store the specified value in the new array element. */
+
+isc_result_t omapi_array_extend (omapi_array_t *array, char *ptr,
+ int *index, const char *file, int line)
+{
+ isc_result_t status;
+ int new = array -> count;
+ status = omapi_array_set (array, ptr, new, file, line);
+ if (index && status == ISC_R_SUCCESS)
+ *index = new;
+ return status;
+}
+
+/* Set a value in the specified array, extending it if necessary. */
+
+isc_result_t omapi_array_set (omapi_array_t *array, void *ptr, int index,
+ const char *file, int line)
+{
+ char **newbuf;
+ int delta;
+ isc_result_t status;
+
+ if (!array)
+ return DHCP_R_INVALIDARG;
+ if (!ptr)
+ return DHCP_R_INVALIDARG;
+ if (index < 0)
+ return DHCP_R_INVALIDARG;
+
+ /* If the proposed index is larger than the current available
+ space in the array, make more space in the array. */
+ if (array -> max <= index) {
+ delta = index - array -> max + 10;
+ newbuf = dmalloc ((array -> max + delta) * sizeof (char *),
+ file, line);
+ if (!newbuf)
+ return ISC_R_NOMEMORY;
+ /* Zero the new elements. */
+ memset (&newbuf [array -> max], 0, (sizeof (char *)) * delta);
+ array -> max += delta;
+ /* Copy the old array data into the new buffer. */
+ if (array -> data) {
+ memcpy (newbuf,
+ array -> data, array -> count * sizeof (char *));
+ dfree (array -> data, file, line);
+ }
+ array -> data = newbuf;
+ } else {
+ /* If there's already data there, and this is an array
+ of references, dereference what's there. */
+ if (array -> data [index]) {
+ status = ((*array -> deref) (&array -> data [index],
+ file, line));
+
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ }
+
+ /* Store the pointer using the referencer function. We have
+ either just memset this to zero or dereferenced what was
+ there previously, so there is no need to do anything if the
+ pointer we have been asked to store is null. */
+ if (ptr) {
+ status = (*array -> ref) (&array -> data [index], ptr,
+ file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ if (index >= array -> count)
+ array -> count = index + 1;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_array_lookup (char **ptr, omapi_array_t *array, int index,
+ const char *file, int line)
+{
+ if (!array || !ptr || *ptr || index < 0 || index >= array -> count)
+ return DHCP_R_INVALIDARG;
+ if (array -> data [index])
+ return (*array -> ref) (ptr,
+ array -> data [index], file, line);
+ return ISC_R_NOTFOUND;
+}
+
diff --git a/omapip/auth.c b/omapip/auth.c
new file mode 100644
index 0000000..1269afd
--- /dev/null
+++ b/omapip/auth.c
@@ -0,0 +1,284 @@
+/* auth.c
+
+ Subroutines having to do with authentication. */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+OMAPI_OBJECT_ALLOC (omapi_auth_key, omapi_auth_key_t, omapi_type_auth_key)
+typedef struct hash omapi_auth_hash_t;
+HASH_FUNCTIONS_DECL (omapi_auth_key, const char *,
+ omapi_auth_key_t, omapi_auth_hash_t)
+omapi_auth_hash_t *auth_key_hash;
+HASH_FUNCTIONS (omapi_auth_key, const char *, omapi_auth_key_t,
+ omapi_auth_hash_t,
+ omapi_auth_key_reference, omapi_auth_key_dereference,
+ do_case_hash)
+
+isc_result_t omapi_auth_key_new (omapi_auth_key_t **o, const char *file,
+ int line)
+{
+ return omapi_auth_key_allocate (o, file, line);
+}
+
+isc_result_t omapi_auth_key_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_auth_key_t *a;
+
+ if (h->type != omapi_type_auth_key)
+ return DHCP_R_INVALIDARG;
+ a = (omapi_auth_key_t *)h;
+
+ if (auth_key_hash != NULL)
+ omapi_auth_key_hash_delete(auth_key_hash, a->name, 0, MDL);
+
+ if (a->name != NULL)
+ dfree(a->name, MDL);
+ if (a->algorithm != NULL)
+ dfree(a->algorithm, MDL);
+ if (a->key != NULL)
+ omapi_data_string_dereference(&a->key, MDL);
+ if (a->tsec_key != NULL)
+ dns_tsec_destroy(&a->tsec_key);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_auth_key_enter (omapi_auth_key_t *a)
+{
+ omapi_auth_key_t *tk;
+ isc_result_t status;
+ dst_key_t *dstkey;
+
+ if (a -> type != omapi_type_auth_key)
+ return DHCP_R_INVALIDARG;
+
+ tk = (omapi_auth_key_t *)0;
+ if (auth_key_hash) {
+ omapi_auth_key_hash_lookup (&tk, auth_key_hash,
+ a -> name, 0, MDL);
+ if (tk == a) {
+ omapi_auth_key_dereference (&tk, MDL);
+ return ISC_R_SUCCESS;
+ }
+ if (tk) {
+ omapi_auth_key_hash_delete (auth_key_hash,
+ tk -> name, 0, MDL);
+ omapi_auth_key_dereference (&tk, MDL);
+ }
+ } else {
+ if (!omapi_auth_key_new_hash(&auth_key_hash,
+ KEY_HASH_SIZE, MDL))
+ return ISC_R_NOMEMORY;
+ }
+
+ /*
+ * If possible create a tsec structure for this key,
+ * if we can't create the structure we put out a warning
+ * and continue.
+ */
+ status = isclib_make_dst_key(a->name, a->algorithm,
+ a->key->value, a->key->len,
+ &dstkey);
+ if (status == ISC_R_SUCCESS) {
+ status = dns_tsec_create(dhcp_gbl_ctx.mctx, dns_tsectype_tsig,
+ dstkey, &a->tsec_key);
+ dst_key_free(&dstkey);
+ }
+ if (status != ISC_R_SUCCESS)
+ log_error("Unable to create tsec structure for %s", a->name);
+
+ omapi_auth_key_hash_add (auth_key_hash, a -> name, 0, a, MDL);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_auth_key_lookup_name (omapi_auth_key_t **a,
+ const char *name)
+{
+ if (!auth_key_hash)
+ return ISC_R_NOTFOUND;
+ if (!omapi_auth_key_hash_lookup (a, auth_key_hash, name, 0, MDL))
+ return ISC_R_NOTFOUND;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_auth_key_lookup (omapi_object_t **h,
+ omapi_object_t *id,
+ omapi_object_t *ref)
+{
+ isc_result_t status;
+ omapi_value_t *name = (omapi_value_t *)0;
+ omapi_value_t *algorithm = (omapi_value_t *)0;
+
+ if (!auth_key_hash)
+ return ISC_R_NOTFOUND;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ status = omapi_get_value_str (ref, id, "name", &name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if ((name -> value -> type != omapi_datatype_string) &&
+ (name -> value -> type != omapi_datatype_data)) {
+ omapi_value_dereference (&name, MDL);
+ return ISC_R_NOTFOUND;
+ }
+
+ status = omapi_get_value_str (ref, id, "algorithm", &algorithm);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (&name, MDL);
+ return status;
+ }
+
+ if ((algorithm -> value -> type != omapi_datatype_string) &&
+ (algorithm -> value -> type != omapi_datatype_data)) {
+ omapi_value_dereference (&name, MDL);
+ omapi_value_dereference (&algorithm, MDL);
+ return ISC_R_NOTFOUND;
+ }
+
+
+ if (!omapi_auth_key_hash_lookup ((omapi_auth_key_t **)h, auth_key_hash,
+ (const char *)
+ name -> value -> u.buffer.value,
+ name -> value -> u.buffer.len, MDL)) {
+ omapi_value_dereference (&name, MDL);
+ omapi_value_dereference (&algorithm, MDL);
+ return ISC_R_NOTFOUND;
+ }
+
+ if (omapi_td_strcasecmp (algorithm -> value,
+ ((omapi_auth_key_t *)*h) -> algorithm) != 0) {
+ omapi_value_dereference (&name, MDL);
+ omapi_value_dereference (&algorithm, MDL);
+ omapi_object_dereference (h, MDL);
+ return ISC_R_NOTFOUND;
+ }
+
+ omapi_value_dereference (&name, MDL);
+ omapi_value_dereference (&algorithm, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_auth_key_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ omapi_auth_key_t *a;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_auth_key)
+ return DHCP_R_INVALIDARG;
+ a = (omapi_auth_key_t *)h;
+
+ /* Write only the name and algorithm -- not the secret! */
+ if (a -> name) {
+ status = omapi_connection_put_name (c, "name");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_string (c, a -> name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ if (a -> algorithm) {
+ status = omapi_connection_put_name (c, "algorithm");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_string (c, a -> algorithm);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_auth_key_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ omapi_auth_key_t *a;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_auth_key)
+ return ISC_R_UNEXPECTED;
+ a = (omapi_auth_key_t *)h;
+
+ if (omapi_ds_strcmp (name, "name") == 0) {
+ if (a -> name)
+ return omapi_make_string_value
+ (value, name, a -> name, MDL);
+ else
+ return ISC_R_NOTFOUND;
+ } else if (omapi_ds_strcmp (name, "key") == 0) {
+ if (a -> key) {
+ status = omapi_value_new (value, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference
+ (&(*value) -> name, name, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (value, MDL);
+ return status;
+ }
+
+ status = omapi_typed_data_new (MDL, &(*value) -> value,
+ omapi_datatype_data,
+ a -> key -> len);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (value, MDL);
+ return status;
+ }
+
+ memcpy ((*value) -> value -> u.buffer.value,
+ a -> key -> value, a -> key -> len);
+ return ISC_R_SUCCESS;
+ } else
+ return ISC_R_NOTFOUND;
+ } else if (omapi_ds_strcmp (name, "algorithm") == 0) {
+ if (a -> algorithm)
+ return omapi_make_string_value
+ (value, name, a -> algorithm, MDL);
+ else
+ return ISC_R_NOTFOUND;
+ }
+
+ return ISC_R_SUCCESS;
+}
diff --git a/omapip/buffer.c b/omapip/buffer.c
new file mode 100644
index 0000000..4efff9f
--- /dev/null
+++ b/omapip/buffer.c
@@ -0,0 +1,723 @@
+/* buffer.c
+
+ Buffer access functions for the object management protocol... */
+
+/*
+ * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <errno.h>
+
+#if defined (TRACING)
+static void trace_connection_input_input (trace_type_t *, unsigned, char *);
+static void trace_connection_input_stop (trace_type_t *);
+static void trace_connection_output_input (trace_type_t *, unsigned, char *);
+static void trace_connection_output_stop (trace_type_t *);
+static trace_type_t *trace_connection_input;
+static trace_type_t *trace_connection_output;
+static isc_result_t omapi_connection_reader_trace (omapi_object_t *,
+ unsigned, char *,
+ unsigned *);
+extern omapi_array_t *omapi_connections;
+
+void omapi_buffer_trace_setup ()
+{
+ trace_connection_input =
+ trace_type_register ("connection-input",
+ (void *)0,
+ trace_connection_input_input,
+ trace_connection_input_stop, MDL);
+ trace_connection_output =
+ trace_type_register ("connection-output",
+ (void *)0,
+ trace_connection_output_input,
+ trace_connection_output_stop, MDL);
+}
+
+static void trace_connection_input_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ unsigned left, taken, cc = 0;
+ char *s;
+ int32_t connect_index;
+ isc_result_t status;
+ omapi_connection_object_t *c = (omapi_connection_object_t *)0;
+
+ memcpy (&connect_index, buf, sizeof connect_index);
+ connect_index = ntohl (connect_index);
+
+ omapi_array_foreach_begin (omapi_connections,
+ omapi_connection_object_t, lp) {
+ if (lp -> index == ntohl (connect_index)) {
+ omapi_connection_reference (&c, lp, MDL);
+ omapi_connection_dereference (&lp, MDL);
+ break;
+ }
+ } omapi_array_foreach_end (omapi_connections,
+ omapi_connection_object_t, lp);
+
+ if (!c) {
+ log_error ("trace connection input: no connection index %ld",
+ (long int)connect_index);
+ return;
+ }
+
+ s = buf + sizeof connect_index;
+ left = length - sizeof connect_index;
+
+ while (left) {
+ taken = 0;
+ status = omapi_connection_reader_trace ((omapi_object_t *)c,
+ left, s, &taken);
+ if (status != ISC_R_SUCCESS) {
+ log_error ("trace connection input: %s",
+ isc_result_totext (status));
+ break;
+ }
+ if (!taken) {
+ if (cc > 0) {
+ log_error ("trace connection_input: %s",
+ "input is not being consumed.");
+ break;
+ }
+ cc++;
+ } else {
+ cc = 0;
+ left -= taken;
+ }
+ }
+ omapi_connection_dereference (&c, MDL);
+}
+
+static void trace_connection_input_stop (trace_type_t *ttype) { }
+
+static void trace_connection_output_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ /* We *could* check to see if the output is correct, but for now
+ we aren't going to do that. */
+}
+
+static void trace_connection_output_stop (trace_type_t *ttype) { }
+
+#endif
+
+/* Make sure that at least len bytes are in the input buffer, and if not,
+ read enough bytes to make up the difference. */
+
+isc_result_t omapi_connection_reader (omapi_object_t *h)
+{
+#if defined (TRACING)
+ return omapi_connection_reader_trace (h, 0, (char *)0, (unsigned *)0);
+}
+
+static isc_result_t omapi_connection_reader_trace (omapi_object_t *h,
+ unsigned stuff_len,
+ char *stuff_buf,
+ unsigned *stuff_taken)
+{
+#endif
+ omapi_buffer_t *buffer;
+ isc_result_t status;
+ unsigned read_len;
+ int read_status;
+ omapi_connection_object_t *c;
+ unsigned bytes_to_read;
+
+ if (!h || h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ /* See if there are enough bytes. */
+ if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 &&
+ c -> in_bytes > c -> bytes_needed)
+ return ISC_R_SUCCESS;
+
+
+ if (c -> inbufs) {
+ for (buffer = c -> inbufs; buffer -> next;
+ buffer = buffer -> next)
+ ;
+ if (!BUFFER_BYTES_FREE (buffer)) {
+ status = omapi_buffer_new (&buffer -> next, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ buffer = buffer -> next;
+ }
+ } else {
+ status = omapi_buffer_new (&c -> inbufs, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ buffer = c -> inbufs;
+ }
+
+ bytes_to_read = BUFFER_BYTES_FREE (buffer);
+
+ while (bytes_to_read) {
+ if (buffer -> tail > buffer -> head)
+ read_len = sizeof (buffer -> buf) - buffer -> tail;
+ else
+ read_len = buffer -> head - buffer -> tail;
+
+#if defined (TRACING)
+ if (trace_playback()) {
+ if (stuff_len) {
+ if (read_len > stuff_len)
+ read_len = stuff_len;
+ if (stuff_taken)
+ *stuff_taken += read_len;
+ memcpy (&buffer -> buf [buffer -> tail],
+ stuff_buf, read_len);
+ stuff_len -= read_len;
+ stuff_buf += read_len;
+ read_status = read_len;
+ } else {
+ break;
+ }
+ } else
+#endif
+ {
+ read_status = read (c -> socket,
+ &buffer -> buf [buffer -> tail],
+ read_len);
+ }
+ if (read_status < 0) {
+ if (errno == EWOULDBLOCK)
+ break;
+ else if (errno == EIO)
+ return ISC_R_IOERROR;
+ else if (errno == EINVAL)
+ return DHCP_R_INVALIDARG;
+ else if (errno == ECONNRESET) {
+ omapi_disconnect (h, 1);
+ return ISC_R_SHUTTINGDOWN;
+ } else
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* If we got a zero-length read, as opposed to EWOULDBLOCK,
+ the remote end closed the connection. */
+ if (read_status == 0) {
+ omapi_disconnect (h, 0);
+ return ISC_R_SHUTTINGDOWN;
+ }
+#if defined (TRACING)
+ if (trace_record ()) {
+ trace_iov_t iov [2];
+ int32_t connect_index;
+
+ connect_index = htonl (c -> index);
+
+ iov [0].buf = (char *)&connect_index;
+ iov [0].len = sizeof connect_index;
+ iov [1].buf = &buffer -> buf [buffer -> tail];
+ iov [1].len = read_status;
+
+ status = (trace_write_packet_iov
+ (trace_connection_input, 2, iov, MDL));
+ if (status != ISC_R_SUCCESS) {
+ trace_stop ();
+ log_error ("trace connection input: %s",
+ isc_result_totext (status));
+ }
+ }
+#endif
+ buffer -> tail += read_status;
+ c -> in_bytes += read_status;
+ if (buffer -> tail == sizeof buffer -> buf)
+ buffer -> tail = 0;
+ if (read_status < read_len)
+ break;
+ bytes_to_read -= read_status;
+ }
+
+ if (c -> bytes_needed <= c -> in_bytes) {
+ omapi_signal (h, "ready", c);
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* Put some bytes into the output buffer for a connection. */
+
+isc_result_t omapi_connection_copyin (omapi_object_t *h,
+ const unsigned char *bufp,
+ unsigned len)
+{
+ omapi_buffer_t *buffer;
+ isc_result_t status;
+ int bytes_copied = 0;
+ unsigned copy_len;
+ int sig_flags = SIG_MODE_UPDATE;
+ omapi_connection_object_t *c;
+
+ /* Make sure len is valid. */
+ if (len < 0)
+ return DHCP_R_INVALIDARG;
+ if (!h || h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ /* If the connection is closed, return an error if the caller
+ tries to copy in. */
+ if (c -> state == omapi_connection_disconnecting ||
+ c -> state == omapi_connection_closed)
+ return ISC_R_NOTCONNECTED;
+
+ if (c -> outbufs) {
+ for (buffer = c -> outbufs;
+ buffer -> next; buffer = buffer -> next)
+ ;
+ } else {
+ status = omapi_buffer_new (&c -> outbufs, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto leave;
+ buffer = c -> outbufs;
+ }
+
+ while (bytes_copied < len) {
+ /* If there is no space available in this buffer,
+ allocate a new one. */
+ if (!BUFFER_BYTES_FREE (buffer)) {
+ status = (omapi_buffer_new (&buffer -> next, MDL));
+ if (status != ISC_R_SUCCESS)
+ goto leave;
+ buffer = buffer -> next;
+ }
+
+ if (buffer -> tail > buffer -> head)
+ copy_len = sizeof (buffer -> buf) - buffer -> tail;
+ else
+ copy_len = buffer -> head - buffer -> tail;
+
+ if (copy_len > (len - bytes_copied))
+ copy_len = len - bytes_copied;
+
+ if (c -> out_key) {
+ if (!c -> out_context)
+ sig_flags |= SIG_MODE_INIT;
+ status = omapi_connection_sign_data
+ (sig_flags, c -> out_key, &c -> out_context,
+ &bufp [bytes_copied], copy_len,
+ (omapi_typed_data_t **)0);
+ if (status != ISC_R_SUCCESS)
+ goto leave;
+ }
+
+ memcpy (&buffer -> buf [buffer -> tail],
+ &bufp [bytes_copied], copy_len);
+ buffer -> tail += copy_len;
+ c -> out_bytes += copy_len;
+ bytes_copied += copy_len;
+ if (buffer -> tail == sizeof buffer -> buf)
+ buffer -> tail = 0;
+ }
+
+ status = ISC_R_SUCCESS;
+
+ leave:
+ /*
+ * If we have any bytes to send and we have a proper io object
+ * inform the socket code that we would like to know when we
+ * can send more bytes.
+ */
+ if (c->out_bytes != 0) {
+ if ((c->outer != NULL) &&
+ (c->outer->type == omapi_type_io_object)) {
+ omapi_io_object_t *io = (omapi_io_object_t *)c->outer;
+ isc_socket_fdwatchpoke(io->fd,
+ ISC_SOCKFDWATCH_WRITE);
+ }
+ }
+
+ return (status);
+}
+
+/* Copy some bytes from the input buffer, and advance the input buffer
+ pointer beyond the bytes copied out. */
+
+isc_result_t omapi_connection_copyout (unsigned char *buf,
+ omapi_object_t *h,
+ unsigned size)
+{
+ unsigned bytes_remaining;
+ unsigned bytes_this_copy;
+ unsigned first_byte;
+ omapi_buffer_t *buffer;
+ unsigned char *bufp;
+ int sig_flags = SIG_MODE_UPDATE;
+ omapi_connection_object_t *c;
+ isc_result_t status;
+
+ if (!h || h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (size > c -> in_bytes)
+ return ISC_R_NOMORE;
+ bufp = buf;
+ bytes_remaining = size;
+ buffer = c -> inbufs;
+
+ while (bytes_remaining) {
+ if (!buffer)
+ return ISC_R_UNEXPECTED;
+ if (BYTES_IN_BUFFER (buffer)) {
+ if (buffer -> head == (sizeof buffer -> buf) - 1)
+ first_byte = 0;
+ else
+ first_byte = buffer -> head + 1;
+
+ if (first_byte > buffer -> tail) {
+ bytes_this_copy = (sizeof buffer -> buf -
+ first_byte);
+ } else {
+ bytes_this_copy =
+ buffer -> tail - first_byte;
+ }
+ if (bytes_this_copy > bytes_remaining)
+ bytes_this_copy = bytes_remaining;
+ if (bufp) {
+ if (c -> in_key) {
+ if (!c -> in_context)
+ sig_flags |= SIG_MODE_INIT;
+ status = omapi_connection_sign_data
+ (sig_flags,
+ c -> in_key,
+ &c -> in_context,
+ (unsigned char *)
+ &buffer -> buf [first_byte],
+ bytes_this_copy,
+ (omapi_typed_data_t **)0);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ memcpy (bufp, &buffer -> buf [first_byte],
+ bytes_this_copy);
+ bufp += bytes_this_copy;
+ }
+ bytes_remaining -= bytes_this_copy;
+ buffer -> head = first_byte + bytes_this_copy - 1;
+ c -> in_bytes -= bytes_this_copy;
+ }
+
+ if (!BYTES_IN_BUFFER (buffer))
+ buffer = buffer -> next;
+ }
+
+ /* Get rid of any input buffers that we emptied. */
+ buffer = (omapi_buffer_t *)0;
+ while (c -> inbufs &&
+ !BYTES_IN_BUFFER (c -> inbufs)) {
+ if (c -> inbufs -> next) {
+ omapi_buffer_reference (&buffer,
+ c -> inbufs -> next, MDL);
+ omapi_buffer_dereference (&c -> inbufs -> next, MDL);
+ }
+ omapi_buffer_dereference (&c -> inbufs, MDL);
+ if (buffer) {
+ omapi_buffer_reference
+ (&c -> inbufs, buffer, MDL);
+ omapi_buffer_dereference (&buffer, MDL);
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_writer (omapi_object_t *h)
+{
+ unsigned bytes_this_write;
+ int bytes_written;
+ unsigned first_byte;
+ omapi_buffer_t *buffer;
+ omapi_connection_object_t *c;
+
+ if (!h || h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ /* Already flushed... */
+ if (!c -> out_bytes)
+ return ISC_R_SUCCESS;
+
+ buffer = c -> outbufs;
+
+ while (c -> out_bytes) {
+ if (!buffer)
+ return ISC_R_UNEXPECTED;
+ if (BYTES_IN_BUFFER (buffer)) {
+ if (buffer -> head == (sizeof buffer -> buf) - 1)
+ first_byte = 0;
+ else
+ first_byte = buffer -> head + 1;
+
+ if (first_byte > buffer -> tail) {
+ bytes_this_write = (sizeof buffer -> buf -
+ first_byte);
+ } else {
+ bytes_this_write =
+ buffer -> tail - first_byte;
+ }
+ bytes_written = write (c -> socket,
+ &buffer -> buf [first_byte],
+ bytes_this_write);
+ /* If the write failed with EWOULDBLOCK or we wrote
+ zero bytes, a further write would block, so we have
+ flushed as much as we can for now. Other errors
+ are really errors. */
+ if (bytes_written < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return ISC_R_INPROGRESS;
+ else if (errno == EPIPE)
+ return ISC_R_NOCONN;
+#ifdef EDQUOT
+ else if (errno == EFBIG || errno == EDQUOT)
+#else
+ else if (errno == EFBIG)
+#endif
+ return ISC_R_NORESOURCES;
+ else if (errno == ENOSPC)
+ return ISC_R_NOSPACE;
+ else if (errno == EIO)
+ return ISC_R_IOERROR;
+ else if (errno == EINVAL)
+ return DHCP_R_INVALIDARG;
+ else if (errno == ECONNRESET)
+ return ISC_R_SHUTTINGDOWN;
+ else
+ return ISC_R_UNEXPECTED;
+ }
+ if (bytes_written == 0)
+ return ISC_R_INPROGRESS;
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ isc_result_t status;
+ trace_iov_t iov [2];
+ int32_t connect_index;
+
+ connect_index = htonl (c -> index);
+
+ iov [0].buf = (char *)&connect_index;
+ iov [0].len = sizeof connect_index;
+ iov [1].buf = &buffer -> buf [buffer -> tail];
+ iov [1].len = bytes_written;
+
+ status = (trace_write_packet_iov
+ (trace_connection_input, 2, iov,
+ MDL));
+ if (status != ISC_R_SUCCESS) {
+ trace_stop ();
+ log_error ("trace %s output: %s",
+ "connection",
+ isc_result_totext (status));
+ }
+ }
+#endif
+
+ buffer -> head = first_byte + bytes_written - 1;
+ c -> out_bytes -= bytes_written;
+
+ /* If we didn't finish out the write, we filled the
+ O.S. output buffer and a further write would block,
+ so stop trying to flush now. */
+ if (bytes_written != bytes_this_write)
+ return ISC_R_INPROGRESS;
+ }
+
+ if (!BYTES_IN_BUFFER (buffer))
+ buffer = buffer -> next;
+ }
+
+ /* Get rid of any output buffers we emptied. */
+ buffer = (omapi_buffer_t *)0;
+ while (c -> outbufs &&
+ !BYTES_IN_BUFFER (c -> outbufs)) {
+ if (c -> outbufs -> next) {
+ omapi_buffer_reference (&buffer,
+ c -> outbufs -> next, MDL);
+ omapi_buffer_dereference (&c -> outbufs -> next, MDL);
+ }
+ omapi_buffer_dereference (&c -> outbufs, MDL);
+ if (buffer) {
+ omapi_buffer_reference (&c -> outbufs, buffer, MDL);
+ omapi_buffer_dereference (&buffer, MDL);
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_get_uint32 (omapi_object_t *c,
+ u_int32_t *result)
+{
+ u_int32_t inbuf;
+ isc_result_t status;
+
+ status = omapi_connection_copyout ((unsigned char *)&inbuf,
+ c, sizeof inbuf);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ *result = ntohl (inbuf);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_put_uint32 (omapi_object_t *c,
+ u_int32_t value)
+{
+ u_int32_t inbuf;
+
+ inbuf = htonl (value);
+
+ return omapi_connection_copyin (c, (unsigned char *)&inbuf,
+ sizeof inbuf);
+}
+
+isc_result_t omapi_connection_get_uint16 (omapi_object_t *c,
+ u_int16_t *result)
+{
+ u_int16_t inbuf;
+ isc_result_t status;
+
+ status = omapi_connection_copyout ((unsigned char *)&inbuf,
+ c, sizeof inbuf);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ *result = ntohs (inbuf);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_put_uint16 (omapi_object_t *c,
+ u_int32_t value)
+{
+ u_int16_t inbuf;
+
+ inbuf = htons (value);
+
+ return omapi_connection_copyin (c, (unsigned char *)&inbuf,
+ sizeof inbuf);
+}
+
+isc_result_t omapi_connection_write_typed_data (omapi_object_t *c,
+ omapi_typed_data_t *data)
+{
+ isc_result_t status;
+ omapi_handle_t handle;
+
+ /* Null data is valid. */
+ if (!data)
+ return omapi_connection_put_uint32 (c, 0);
+
+ switch (data -> type) {
+ case omapi_datatype_int:
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ return omapi_connection_put_uint32 (c, ((u_int32_t)
+ (data -> u.integer)));
+
+ case omapi_datatype_string:
+ case omapi_datatype_data:
+ status = omapi_connection_put_uint32 (c, data -> u.buffer.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (data -> u.buffer.len)
+ return omapi_connection_copyin
+ (c, data -> u.buffer.value,
+ data -> u.buffer.len);
+ return ISC_R_SUCCESS;
+
+ case omapi_datatype_object:
+ if (data -> u.object) {
+ status = omapi_object_handle (&handle,
+ data -> u.object);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ } else
+ handle = 0;
+ status = omapi_connection_put_uint32 (c, sizeof handle);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ return omapi_connection_put_uint32 (c, handle);
+
+ }
+ return DHCP_R_INVALIDARG;
+}
+
+isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name)
+{
+ isc_result_t status;
+ unsigned len = strlen (name);
+
+ status = omapi_connection_put_uint16 (c, len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ return omapi_connection_copyin (c, (const unsigned char *)name, len);
+}
+
+isc_result_t omapi_connection_put_string (omapi_object_t *c,
+ const char *string)
+{
+ isc_result_t status;
+ unsigned len;
+
+ if (string)
+ len = strlen (string);
+ else
+ len = 0;
+
+ status = omapi_connection_put_uint32 (c, len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (len)
+ return omapi_connection_copyin
+ (c, (const unsigned char *)string, len);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h)
+{
+ isc_result_t status;
+ omapi_handle_t handle;
+
+ if (h) {
+ status = omapi_object_handle (&handle, h);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ } else
+ handle = 0; /* The null handle. */
+ status = omapi_connection_put_uint32 (c, sizeof handle);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ return omapi_connection_put_uint32 (c, handle);
+}
diff --git a/omapip/connection.c b/omapip/connection.c
new file mode 100644
index 0000000..a919968
--- /dev/null
+++ b/omapip/connection.c
@@ -0,0 +1,1109 @@
+/* connection.c
+
+ Subroutines for dealing with connections. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+
+#if defined (TRACING)
+static void trace_connect_input (trace_type_t *, unsigned, char *);
+static void trace_connect_stop (trace_type_t *);
+static void trace_disconnect_input (trace_type_t *, unsigned, char *);
+static void trace_disconnect_stop (trace_type_t *);
+trace_type_t *trace_connect;
+trace_type_t *trace_disconnect;
+extern omapi_array_t *trace_listeners;
+#endif
+static isc_result_t omapi_connection_connect_internal (omapi_object_t *);
+
+OMAPI_OBJECT_ALLOC (omapi_connection,
+ omapi_connection_object_t, omapi_type_connection)
+
+isc_result_t omapi_connect (omapi_object_t *c,
+ const char *server_name,
+ unsigned port)
+{
+ struct hostent *he;
+ unsigned i, hix;
+ omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
+ struct in_addr foo;
+ isc_result_t status;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connect(%s, port=%d)", server_name, port);
+#endif
+
+ if (!inet_aton (server_name, &foo)) {
+ /* If we didn't get a numeric address, try for a domain
+ name. It's okay for this call to block. */
+ he = gethostbyname (server_name);
+ if (!he)
+ return DHCP_R_HOSTUNKNOWN;
+ for (i = 0; he -> h_addr_list [i]; i++)
+ ;
+ if (i == 0)
+ return DHCP_R_HOSTUNKNOWN;
+ hix = i;
+
+ status = omapi_addr_list_new (&addrs, hix, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ for (i = 0; i < hix; i++) {
+ addrs -> addresses [i].addrtype = he -> h_addrtype;
+ addrs -> addresses [i].addrlen = he -> h_length;
+ memcpy (addrs -> addresses [i].address,
+ he -> h_addr_list [i],
+ (unsigned)he -> h_length);
+ addrs -> addresses [i].port = port;
+ }
+ } else {
+ status = omapi_addr_list_new (&addrs, 1, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ addrs -> addresses [0].addrtype = AF_INET;
+ addrs -> addresses [0].addrlen = sizeof foo;
+ memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
+ addrs -> addresses [0].port = port;
+ hix = 1;
+ }
+ status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
+ omapi_addr_list_dereference (&addrs, MDL);
+ return status;
+}
+
+isc_result_t omapi_connect_list (omapi_object_t *c,
+ omapi_addr_list_t *remote_addrs,
+ omapi_addr_t *local_addr)
+{
+ isc_result_t status;
+ omapi_connection_object_t *obj;
+ int flag;
+ struct sockaddr_in local_sin;
+
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_connection_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, c, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+ }
+
+ /* Store the address list on the object. */
+ omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
+ obj -> cptr = 0;
+ obj -> state = omapi_connection_unconnected;
+
+#if defined (TRACING)
+ /* If we're playing back, don't actually try to connect - just leave
+ the object available for a subsequent connect or disconnect. */
+ if (!trace_playback ()) {
+#endif
+ /* Create a socket on which to communicate. */
+ obj -> socket =
+ socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (obj -> socket < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ if (errno == EMFILE || errno == ENFILE
+ || errno == ENOBUFS)
+ return ISC_R_NORESOURCES;
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Set up the local address, if any. */
+ if (local_addr) {
+ /* Only do TCPv4 so far. */
+ if (local_addr -> addrtype != AF_INET) {
+ omapi_connection_dereference (&obj, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ local_sin.sin_port = htons (local_addr -> port);
+ memcpy (&local_sin.sin_addr,
+ local_addr -> address,
+ local_addr -> addrlen);
+#if defined (HAVE_SA_LEN)
+ local_sin.sin_len = sizeof local_addr;
+#endif
+ local_sin.sin_family = AF_INET;
+ memset (&local_sin.sin_zero, 0,
+ sizeof local_sin.sin_zero);
+
+ if (bind (obj -> socket, (struct sockaddr *)&local_sin,
+ sizeof local_sin) < 0) {
+ omapi_connection_object_t **objp = &obj;
+ omapi_object_t **o = (omapi_object_t **)objp;
+ omapi_object_dereference(o, MDL);
+ if (errno == EADDRINUSE)
+ return ISC_R_ADDRINUSE;
+ if (errno == EADDRNOTAVAIL)
+ return ISC_R_ADDRNOTAVAIL;
+ if (errno == EACCES)
+ return ISC_R_NOPERM;
+ return ISC_R_UNEXPECTED;
+ }
+ obj -> local_addr = local_sin;
+ }
+
+#if defined(F_SETFD)
+ if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
+ close (obj -> socket);
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+#endif
+
+ /* Set the SO_REUSEADDR flag (this should not fail). */
+ flag = 1;
+ if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&flag, sizeof flag) < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Set the file to nonblocking mode. */
+ if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+#ifdef SO_NOSIGPIPE
+ /*
+ * If available stop the OS from killing our
+ * program on a SIGPIPE failure
+ */
+ flag = 1;
+ if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE,
+ (char *)&flag, sizeof(flag)) < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+#endif
+
+ status = (omapi_register_io_object
+ ((omapi_object_t *)obj,
+ 0, omapi_connection_writefd,
+ 0, omapi_connection_connect,
+ omapi_connection_reaper));
+ if (status != ISC_R_SUCCESS)
+ goto out;
+ status = omapi_connection_connect_internal ((omapi_object_t *)
+ obj);
+ /*
+ * inprogress is the same as success but used
+ * to indicate to the dispatch code that we should
+ * mark the socket as requiring more attention.
+ * Routines calling this function should handle
+ * success properly.
+ */
+ if (status == ISC_R_INPROGRESS) {
+ status = ISC_R_SUCCESS;
+ }
+#if defined (TRACING)
+ }
+ omapi_connection_register (obj, MDL);
+#endif
+
+ out:
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+}
+
+#if defined (TRACING)
+omapi_array_t *omapi_connections;
+
+OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
+
+void omapi_connection_trace_setup (void) {
+ trace_connect = trace_type_register ("connect", (void *)0,
+ trace_connect_input,
+ trace_connect_stop, MDL);
+ trace_disconnect = trace_type_register ("disconnect", (void *)0,
+ trace_disconnect_input,
+ trace_disconnect_stop, MDL);
+}
+
+void omapi_connection_register (omapi_connection_object_t *obj,
+ const char *file, int line)
+{
+ isc_result_t status;
+ trace_iov_t iov [6];
+ int iov_count = 0;
+ int32_t connect_index, listener_index;
+ static int32_t index;
+
+ if (!omapi_connections) {
+ status = omapi_connection_array_allocate (&omapi_connections,
+ file, line);
+ if (status != ISC_R_SUCCESS)
+ return;
+ }
+
+ status = omapi_connection_array_extend (omapi_connections, obj,
+ (int *)0, file, line);
+ if (status != ISC_R_SUCCESS) {
+ obj -> index = -1;
+ return;
+ }
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ /* Connection registration packet:
+
+ int32_t index
+ int32_t listener_index [-1 means no listener]
+ u_int16_t remote_port
+ u_int16_t local_port
+ u_int32_t remote_addr
+ u_int32_t local_addr */
+
+ connect_index = htonl (index);
+ index++;
+ if (obj -> listener)
+ listener_index = htonl (obj -> listener -> index);
+ else
+ listener_index = htonl (-1);
+ iov [iov_count].buf = (char *)&connect_index;
+ iov [iov_count++].len = sizeof connect_index;
+ iov [iov_count].buf = (char *)&listener_index;
+ iov [iov_count++].len = sizeof listener_index;
+ iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port;
+ iov [iov_count++].len = sizeof obj -> remote_addr.sin_port;
+ iov [iov_count].buf = (char *)&obj -> local_addr.sin_port;
+ iov [iov_count++].len = sizeof obj -> local_addr.sin_port;
+ iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr;
+ iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr;
+ iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr;
+ iov [iov_count++].len = sizeof obj -> local_addr.sin_addr;
+
+ status = trace_write_packet_iov (trace_connect,
+ iov_count, iov, file, line);
+ }
+#endif
+}
+
+static void trace_connect_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ struct sockaddr_in remote, local;
+ int32_t connect_index, listener_index;
+ char *s = buf;
+ omapi_connection_object_t *obj;
+ isc_result_t status;
+ int i;
+
+ if (length != ((sizeof connect_index) +
+ (sizeof remote.sin_port) +
+ (sizeof remote.sin_addr)) * 2) {
+ log_error ("Trace connect: invalid length %d", length);
+ return;
+ }
+
+ memset (&remote, 0, sizeof remote);
+ memset (&local, 0, sizeof local);
+ memcpy (&connect_index, s, sizeof connect_index);
+ s += sizeof connect_index;
+ memcpy (&listener_index, s, sizeof listener_index);
+ s += sizeof listener_index;
+ memcpy (&remote.sin_port, s, sizeof remote.sin_port);
+ s += sizeof remote.sin_port;
+ memcpy (&local.sin_port, s, sizeof local.sin_port);
+ s += sizeof local.sin_port;
+ memcpy (&remote.sin_addr, s, sizeof remote.sin_addr);
+ s += sizeof remote.sin_addr;
+ memcpy (&local.sin_addr, s, sizeof local.sin_addr);
+ s += sizeof local.sin_addr;
+
+ connect_index = ntohl (connect_index);
+ listener_index = ntohl (listener_index);
+
+ /* If this was a connect to a listener, then we just slap together
+ a new connection. */
+ if (listener_index != -1) {
+ omapi_listener_object_t *listener;
+ listener = (omapi_listener_object_t *)0;
+ omapi_array_foreach_begin (trace_listeners,
+ omapi_listener_object_t, lp) {
+ if (lp -> address.sin_port == local.sin_port) {
+ omapi_listener_reference (&listener, lp, MDL);
+ omapi_listener_dereference (&lp, MDL);
+ break;
+ }
+ } omapi_array_foreach_end (trace_listeners,
+ omapi_listener_object_t, lp);
+ if (!listener) {
+ log_error ("%s%ld, addr %s, port %d",
+ "Spurious traced listener connect - index ",
+ (long int)listener_index,
+ inet_ntoa (local.sin_addr),
+ ntohs (local.sin_port));
+ return;
+ }
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj, listener, -1, &remote);
+ if (status != ISC_R_SUCCESS) {
+ log_error ("traced listener connect: %s",
+ isc_result_totext (status));
+ }
+ if (obj)
+ omapi_connection_dereference (&obj, MDL);
+ omapi_listener_dereference (&listener, MDL);
+ return;
+ }
+
+ /* Find the matching connect object, if there is one. */
+ omapi_array_foreach_begin (omapi_connections,
+ omapi_connection_object_t, lp) {
+ for (i = 0; (lp -> connect_list &&
+ i < lp -> connect_list -> count); i++) {
+ if (!memcmp (&remote.sin_addr,
+ &lp -> connect_list -> addresses [i].address,
+ sizeof remote.sin_addr) &&
+ (ntohs (remote.sin_port) ==
+ lp -> connect_list -> addresses [i].port))
+ lp -> state = omapi_connection_connected;
+ lp -> remote_addr = remote;
+ lp -> remote_addr.sin_family = AF_INET;
+ omapi_addr_list_dereference (&lp -> connect_list, MDL);
+ lp -> index = connect_index;
+ status = omapi_signal_in ((omapi_object_t *)lp,
+ "connect");
+ omapi_connection_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (omapi_connections,
+ omapi_connection_object_t, lp);
+
+ log_error ("Spurious traced connect - index %ld, addr %s, port %d",
+ (long int)connect_index, inet_ntoa (remote.sin_addr),
+ ntohs (remote.sin_port));
+ return;
+}
+
+static void trace_connect_stop (trace_type_t *ttype) { }
+
+static void trace_disconnect_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ int32_t *index;
+ if (length != sizeof *index) {
+ log_error ("trace disconnect: wrong length %d", length);
+ return;
+ }
+
+ index = (int32_t *)buf;
+
+ omapi_array_foreach_begin (omapi_connections,
+ omapi_connection_object_t, lp) {
+ if (lp -> index == ntohl (*index)) {
+ omapi_disconnect ((omapi_object_t *)lp, 1);
+ omapi_connection_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (omapi_connections,
+ omapi_connection_object_t, lp);
+
+ log_error ("trace disconnect: no connection matching index %ld",
+ (long int)ntohl (*index));
+}
+
+static void trace_disconnect_stop (trace_type_t *ttype) { }
+#endif
+
+/* Disconnect a connection object from the remote end. If force is nonzero,
+ close the connection immediately. Otherwise, shut down the receiving end
+ but allow any unsent data to be sent before actually closing the socket. */
+
+isc_result_t omapi_disconnect (omapi_object_t *h,
+ int force)
+{
+ omapi_connection_object_t *c;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_disconnect(%s)", force ? "force" : "");
+#endif
+
+ c = (omapi_connection_object_t *)h;
+ if (c -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ isc_result_t status;
+ int32_t index;
+
+ index = htonl (c -> index);
+ status = trace_write_packet (trace_disconnect,
+ sizeof index, (char *)&index,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ trace_stop ();
+ log_error ("trace_write_packet: %s",
+ isc_result_totext (status));
+ }
+ }
+ if (!trace_playback ()) {
+#endif
+ if (!force) {
+ /* If we're already disconnecting, we don't have to do
+ anything. */
+ if (c -> state == omapi_connection_disconnecting)
+ return ISC_R_SUCCESS;
+
+ /* Try to shut down the socket - this sends a FIN to
+ the remote end, so that it won't send us any more
+ data. If the shutdown succeeds, and we still
+ have bytes left to write, defer closing the socket
+ until that's done. */
+ if (!shutdown (c -> socket, SHUT_RD)) {
+ if (c -> out_bytes > 0) {
+ c -> state =
+ omapi_connection_disconnecting;
+ return ISC_R_SUCCESS;
+ }
+ }
+ }
+ close (c -> socket);
+#if defined (TRACING)
+ }
+#endif
+ c -> state = omapi_connection_closed;
+
+#if 0
+ /*
+ * Disconnecting from the I/O object seems incorrect as it doesn't
+ * cause the I/O object to be cleaned and released. Previous to
+ * using the isc socket library this wouldn't have caused a problem
+ * with the socket library we would have a reference to a closed
+ * socket. Instead we now do an unregister to properly free the
+ * I/O object.
+ */
+
+ /* Disconnect from I/O object, if any. */
+ if (h -> outer) {
+ if (h -> outer -> inner)
+ omapi_object_dereference (&h -> outer -> inner, MDL);
+ omapi_object_dereference (&h -> outer, MDL);
+ }
+#else
+ if (h->outer) {
+ omapi_unregister_io_object(h);
+ }
+#endif
+
+ /* If whatever created us registered a signal handler, send it
+ a disconnect signal. */
+ omapi_signal (h, "disconnect", h);
+
+ /* Disconnect from protocol object, if any. */
+ if (h->inner != NULL) {
+ if (h->inner->outer != NULL) {
+ omapi_object_dereference(&h->inner->outer, MDL);
+ }
+ omapi_object_dereference(&h->inner, MDL);
+ }
+
+ /* XXX: the code to free buffers should be in the dereference
+ function, but there is no special-purpose function to
+ dereference connections, so these just get leaked */
+ /* Free any buffers */
+ if (c->inbufs != NULL) {
+ omapi_buffer_dereference(&c->inbufs, MDL);
+ }
+ c->in_bytes = 0;
+ if (c->outbufs != NULL) {
+ omapi_buffer_dereference(&c->outbufs, MDL);
+ }
+ c->out_bytes = 0;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes)
+{
+ omapi_connection_object_t *c;
+
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ c -> bytes_needed = bytes;
+ if (c -> bytes_needed <= c -> in_bytes) {
+ return ISC_R_SUCCESS;
+ }
+ return DHCP_R_NOTYET;
+}
+
+/* Return the socket on which the dispatcher should wait for readiness
+ to read, for a connection object. */
+int omapi_connection_readfd (omapi_object_t *h)
+{
+ omapi_connection_object_t *c;
+ if (h -> type != omapi_type_connection)
+ return -1;
+ c = (omapi_connection_object_t *)h;
+ if (c -> state != omapi_connection_connected)
+ return -1;
+ return c -> socket;
+}
+
+/*
+ * Return the socket on which the dispatcher should wait for readiness
+ * to write, for a connection object. When bytes are buffered we should
+ * also poke the dispatcher to tell it to start or re-start watching the
+ * socket.
+ */
+int omapi_connection_writefd (omapi_object_t *h)
+{
+ omapi_connection_object_t *c;
+ if (h -> type != omapi_type_connection)
+ return -1;
+ c = (omapi_connection_object_t *)h;
+ return c->socket;
+}
+
+isc_result_t omapi_connection_connect (omapi_object_t *h)
+{
+ isc_result_t status;
+
+ /*
+ * We use the INPROGRESS status to indicate that
+ * we want more from the socket. In this case we
+ * have now connected and are trying to write to
+ * the socket for the first time. For the signaling
+ * code this is the same as a SUCCESS so we don't
+ * pass it on as a signal.
+ */
+ status = omapi_connection_connect_internal (h);
+ if (status == ISC_R_INPROGRESS)
+ return ISC_R_INPROGRESS;
+
+ if (status != ISC_R_SUCCESS)
+ omapi_signal (h, "status", status);
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t omapi_connection_connect_internal (omapi_object_t *h)
+{
+ int error;
+ omapi_connection_object_t *c;
+ socklen_t sl;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (c -> state == omapi_connection_connecting) {
+ sl = sizeof error;
+ if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
+ (char *)&error, &sl) < 0) {
+ omapi_disconnect (h, 1);
+ return ISC_R_SUCCESS;
+ }
+ if (!error)
+ c -> state = omapi_connection_connected;
+ }
+ if (c -> state == omapi_connection_connecting ||
+ c -> state == omapi_connection_unconnected) {
+ if (c -> cptr >= c -> connect_list -> count) {
+ switch (error) {
+ case ECONNREFUSED:
+ status = ISC_R_CONNREFUSED;
+ break;
+ case ENETUNREACH:
+ status = ISC_R_NETUNREACH;
+ break;
+ default:
+ status = uerr2isc (error);
+ break;
+ }
+ omapi_disconnect (h, 1);
+ return status;
+ }
+
+ if (c -> connect_list -> addresses [c -> cptr].addrtype !=
+ AF_INET) {
+ omapi_disconnect (h, 1);
+ return DHCP_R_INVALIDARG;
+ }
+
+ memcpy (&c -> remote_addr.sin_addr,
+ &c -> connect_list -> addresses [c -> cptr].address,
+ sizeof c -> remote_addr.sin_addr);
+ c -> remote_addr.sin_family = AF_INET;
+ c -> remote_addr.sin_port =
+ htons (c -> connect_list -> addresses [c -> cptr].port);
+#if defined (HAVE_SA_LEN)
+ c -> remote_addr.sin_len = sizeof c -> remote_addr;
+#endif
+ memset (&c -> remote_addr.sin_zero, 0,
+ sizeof c -> remote_addr.sin_zero);
+ ++c -> cptr;
+
+ error = connect (c -> socket,
+ (struct sockaddr *)&c -> remote_addr,
+ sizeof c -> remote_addr);
+ if (error < 0) {
+ error = errno;
+ if (error != EINPROGRESS) {
+ omapi_disconnect (h, 1);
+ switch (error) {
+ case ECONNREFUSED:
+ status = ISC_R_CONNREFUSED;
+ break;
+ case ENETUNREACH:
+ status = ISC_R_NETUNREACH;
+ break;
+ default:
+ status = uerr2isc (error);
+ break;
+ }
+ return status;
+ }
+ c -> state = omapi_connection_connecting;
+ return DHCP_R_INCOMPLETE;
+ }
+ c -> state = omapi_connection_connected;
+ }
+
+ /* I don't know why this would fail, so I'm tempted not to test
+ the return value. */
+ sl = sizeof (c -> local_addr);
+ if (getsockname (c -> socket,
+ (struct sockaddr *)&c -> local_addr, &sl) < 0) {
+ }
+
+ /* Reregister with the I/O object. If we don't already have an
+ I/O object this turns into a register call, otherwise we simply
+ modify the pointers in the I/O object. */
+
+ status = omapi_reregister_io_object (h,
+ omapi_connection_readfd,
+ omapi_connection_writefd,
+ omapi_connection_reader,
+ omapi_connection_writer,
+ omapi_connection_reaper);
+
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (h, 1);
+ return status;
+ }
+
+ omapi_signal_in (h, "connect");
+ omapi_addr_list_dereference (&c -> connect_list, MDL);
+ return ISC_R_INPROGRESS;
+}
+
+/* Reaper function for connection - if the connection is completely closed,
+ reap it. If it's in the disconnecting state, there were bytes left
+ to write when the user closed it, so if there are now no bytes left to
+ write, we can close it. */
+isc_result_t omapi_connection_reaper (omapi_object_t *h)
+{
+ omapi_connection_object_t *c;
+
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ c = (omapi_connection_object_t *)h;
+ if (c -> state == omapi_connection_disconnecting &&
+ c -> out_bytes == 0) {
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_reaper(): disconnect");
+#endif
+ omapi_disconnect (h, 1);
+ }
+ if (c -> state == omapi_connection_closed) {
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_reaper(): closed");
+#endif
+ return ISC_R_NOTCONNECTED;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) {
+ omapi_value_t *name = (omapi_value_t *)0;
+ omapi_value_t *algorithm = (omapi_value_t *)0;
+ omapi_value_t *key = (omapi_value_t *)0;
+ char *name_str = NULL;
+ isc_result_t status = ISC_R_SUCCESS;
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "name", &name);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "algorithm", &algorithm);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "key", &key);
+
+ if (status == ISC_R_SUCCESS) {
+ if ((algorithm->value->type != omapi_datatype_data &&
+ algorithm->value->type != omapi_datatype_string) ||
+ strncasecmp((char *)algorithm->value->u.buffer.value,
+ NS_TSIG_ALG_HMAC_MD5 ".",
+ algorithm->value->u.buffer.len) != 0) {
+ status = DHCP_R_INVALIDARG;
+ }
+ }
+
+ if (status == ISC_R_SUCCESS) {
+ name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL);
+ if (!name_str)
+ status = ISC_R_NOMEMORY;
+ }
+
+ if (status == ISC_R_SUCCESS) {
+ memcpy (name_str,
+ name -> value -> u.buffer.value,
+ name -> value -> u.buffer.len);
+ name_str [name -> value -> u.buffer.len] = 0;
+
+ status = isclib_make_dst_key(name_str,
+ DHCP_HMAC_MD5_NAME,
+ key->value->u.buffer.value,
+ key->value->u.buffer.len,
+ dst_key);
+
+ if (*dst_key == NULL)
+ status = ISC_R_NOMEMORY;
+ }
+
+ if (name_str)
+ dfree (name_str, MDL);
+ if (key)
+ omapi_value_dereference (&key, MDL);
+ if (algorithm)
+ omapi_value_dereference (&algorithm, MDL);
+ if (name)
+ omapi_value_dereference (&name, MDL);
+
+ return status;
+}
+
+isc_result_t omapi_connection_sign_data (int mode,
+ dst_key_t *key,
+ void **context,
+ const unsigned char *data,
+ const unsigned len,
+ omapi_typed_data_t **result)
+{
+ omapi_typed_data_t *td = (omapi_typed_data_t *)0;
+ isc_result_t status;
+ dst_context_t **dctx = (dst_context_t **)context;
+
+ /* Create the context for the dst module */
+ if (mode & SIG_MODE_INIT) {
+ status = dst_context_create(key, dhcp_gbl_ctx.mctx, dctx);
+ if (status != ISC_R_SUCCESS) {
+ return status;
+ }
+ }
+
+ /* If we have any data add it to the context */
+ if (len != 0) {
+ isc_region_t region;
+ region.base = (unsigned char *)data;
+ region.length = len;
+ dst_context_adddata(*dctx, &region);
+ }
+
+ /* Finish the signature and clean up the context */
+ if (mode & SIG_MODE_FINAL) {
+ unsigned int sigsize;
+ isc_buffer_t sigbuf;
+
+ status = dst_key_sigsize(key, &sigsize);
+ if (status != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = omapi_typed_data_new (MDL, &td,
+ omapi_datatype_data,
+ sigsize);
+ if (status != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len);
+ status = dst_context_sign(*dctx, &sigbuf);
+ if (status != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (result) {
+ omapi_typed_data_reference (result, td, MDL);
+ }
+
+ cleanup:
+ /* We are done with the context and the td. On success
+ * the td is now referenced from result, on failure we
+ * don't need it any more */
+ if (td) {
+ omapi_typed_data_dereference (&td, MDL);
+ }
+ dst_context_destroy(dctx);
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_output_auth_length (omapi_object_t *h,
+ unsigned *l)
+{
+ omapi_connection_object_t *c;
+
+ if (h->type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (c->out_key == NULL)
+ return ISC_R_NOTFOUND;
+
+ return(dst_key_sigsize(c->out_key, l));
+}
+
+isc_result_t omapi_connection_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ omapi_connection_object_t *c;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "input-authenticator") == 0) {
+ if (value && value -> type != omapi_datatype_object)
+ return DHCP_R_INVALIDARG;
+
+ if (c -> in_context) {
+ omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> in_key,
+ &c -> in_context,
+ 0, 0,
+ (omapi_typed_data_t **) 0);
+ }
+
+ if (c->in_key != NULL) {
+ dst_key_free(&c->in_key);
+ }
+
+ if (value) {
+ status = make_dst_key (&c -> in_key,
+ value -> u.object);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+ }
+ else if (omapi_ds_strcmp (name, "output-authenticator") == 0) {
+ if (value && value -> type != omapi_datatype_object)
+ return DHCP_R_INVALIDARG;
+
+ if (c -> out_context) {
+ omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> out_key,
+ &c -> out_context,
+ 0, 0,
+ (omapi_typed_data_t **) 0);
+ }
+
+ if (c->out_key != NULL) {
+ dst_key_free(&c->out_key);
+ }
+
+ if (value) {
+ status = make_dst_key (&c -> out_key,
+ value -> u.object);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+ }
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_connection_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ omapi_connection_object_t *c;
+ omapi_typed_data_t *td = (omapi_typed_data_t *)0;
+ isc_result_t status;
+ unsigned int sigsize;
+
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "input-signature") == 0) {
+ if (!c -> in_key || !c -> in_context)
+ return ISC_R_NOTFOUND;
+
+ status = omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> in_key,
+ &c -> in_context,
+ 0, 0, &td);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_make_value (value, name, td, MDL);
+ omapi_typed_data_dereference (&td, MDL);
+ return status;
+
+ } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) {
+ if (c->in_key == NULL)
+ return ISC_R_NOTFOUND;
+
+ status = dst_key_sigsize(c->in_key, &sigsize);
+ if (status != ISC_R_SUCCESS) {
+ return(status);
+ }
+
+ return omapi_make_int_value(value, name, sigsize, MDL);
+
+ } else if (omapi_ds_strcmp (name, "output-signature") == 0) {
+ if (!c -> out_key || !c -> out_context)
+ return ISC_R_NOTFOUND;
+
+ status = omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> out_key,
+ &c -> out_context,
+ 0, 0, &td);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_make_value (value, name, td, MDL);
+ omapi_typed_data_dereference (&td, MDL);
+ return status;
+
+ } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) {
+ if (c->out_key == NULL)
+ return ISC_R_NOTFOUND;
+
+
+ status = dst_key_sigsize(c->out_key, &sigsize);
+ if (status != ISC_R_SUCCESS) {
+ return(status);
+ }
+
+ return omapi_make_int_value(value, name, sigsize, MDL);
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_connection_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_connection_object_t *c;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_destroy()");
+#endif
+
+ if (h -> type != omapi_type_connection)
+ return ISC_R_UNEXPECTED;
+ c = (omapi_connection_object_t *)(h);
+ if (c -> state == omapi_connection_connected)
+ omapi_disconnect (h, 1);
+ if (c -> listener)
+ omapi_listener_dereference (&c -> listener, file, line);
+ if (c -> connect_list)
+ omapi_addr_list_dereference (&c -> connect_list, file, line);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_signal_handler(%s)", name);
+#endif
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_connection_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *m)
+{
+ if (m -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ if (m -> inner && m -> inner -> type -> stuff_values)
+ return (*(m -> inner -> type -> stuff_values)) (c, id,
+ m -> inner);
+ return ISC_R_SUCCESS;
+}
diff --git a/omapip/convert.c b/omapip/convert.c
new file mode 100644
index 0000000..10b4b45
--- /dev/null
+++ b/omapip/convert.c
@@ -0,0 +1,185 @@
+/* convert.c
+
+ Safe copying of option values into and out of the option buffer, which
+ can't be assumed to be aligned. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+u_int32_t getULong (buf)
+ const unsigned char *buf;
+{
+ u_int32_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (u_int32_t));
+ return ntohl (ibuf);
+}
+
+int32_t getLong (buf)
+ const unsigned char *buf;
+{
+ int32_t ibuf;
+
+ memcpy (&ibuf, buf, sizeof (int32_t));
+ return ntohl (ibuf);
+}
+
+u_int32_t getUShort (buf)
+ const unsigned char *buf;
+{
+ unsigned short ibuf;
+
+ memcpy (&ibuf, buf, sizeof (u_int16_t));
+ return ntohs (ibuf);
+}
+
+int32_t getShort (buf)
+ const unsigned char *buf;
+{
+ short ibuf;
+
+ memcpy (&ibuf, buf, sizeof (int16_t));
+ return ntohs (ibuf);
+}
+
+void putULong (obuf, val)
+ unsigned char *obuf;
+ u_int32_t val;
+{
+ u_int32_t tmp = htonl (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putLong (obuf, val)
+ unsigned char *obuf;
+ int32_t val;
+{
+ int32_t tmp = htonl (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putUShort (obuf, val)
+ unsigned char *obuf;
+ u_int32_t val;
+{
+ u_int16_t tmp = htons (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putShort (obuf, val)
+ unsigned char *obuf;
+ int32_t val;
+{
+ int16_t tmp = htons (val);
+ memcpy (obuf, &tmp, sizeof tmp);
+}
+
+void putUChar (obuf, val)
+ unsigned char *obuf;
+ u_int32_t val;
+{
+ *obuf = val;
+}
+
+u_int32_t getUChar (obuf)
+ const unsigned char *obuf;
+{
+ return obuf [0];
+}
+
+int converted_length (buf, base, width)
+ const unsigned char *buf;
+ unsigned int base;
+ unsigned int width;
+{
+ u_int32_t number;
+ u_int32_t column;
+ int power = 1;
+ u_int32_t newcolumn = base;
+
+ if (base > 16)
+ return 0;
+
+ if (width == 1)
+ number = getUChar (buf);
+ else if (width == 2)
+ number = getUShort (buf);
+ else if (width == 4)
+ number = getULong (buf);
+ else
+ return 0;
+
+ do {
+ column = newcolumn;
+
+ if (number < column)
+ return power;
+ power++;
+ newcolumn = column * base;
+ /* If we wrap around, it must be the next power of two up. */
+ } while (newcolumn > column);
+
+ return power;
+}
+
+int binary_to_ascii (outbuf, inbuf, base, width)
+ unsigned char *outbuf;
+ const unsigned char *inbuf;
+ unsigned int base;
+ unsigned int width;
+{
+ u_int32_t number;
+ static char h2a [] = "0123456789abcdef";
+ int power = converted_length (inbuf, base, width);
+ int i;
+
+ if (base > 16)
+ return 0;
+
+ if (width == 1)
+ number = getUChar (inbuf);
+ else if (width == 2)
+ number = getUShort (inbuf);
+ else if (width == 4)
+ number = getULong (inbuf);
+ else
+ return 0;
+
+ for (i = power - 1 ; i >= 0; i--) {
+ outbuf [i] = h2a [number % base];
+ number /= base;
+ }
+
+ return power;
+}
diff --git a/omapip/dispatch.c b/omapip/dispatch.c
new file mode 100644
index 0000000..4039659
--- /dev/null
+++ b/omapip/dispatch.c
@@ -0,0 +1,912 @@
+/* dispatch.c
+
+ I/O dispatcher. */
+
+/*
+ * Copyright (c) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <sys/time.h>
+
+static omapi_io_object_t omapi_io_states;
+struct timeval cur_tv;
+
+struct eventqueue *rw_queue_empty;
+
+OMAPI_OBJECT_ALLOC (omapi_io,
+ omapi_io_object_t, omapi_type_io_object)
+OMAPI_OBJECT_ALLOC (omapi_waiter,
+ omapi_waiter_object_t, omapi_type_waiter)
+
+void
+register_eventhandler(struct eventqueue **queue, void (*handler)(void *))
+{
+ struct eventqueue *t, *q;
+
+ /* traverse to end of list */
+ t = NULL;
+ for (q = *queue ; q ; q = q->next) {
+ if (q->handler == handler)
+ return; /* handler already registered */
+ t = q;
+ }
+
+ q = ((struct eventqueue *)dmalloc(sizeof(struct eventqueue), MDL));
+ if (!q)
+ log_fatal("register_eventhandler: no memory!");
+ memset(q, 0, sizeof *q);
+ if (t)
+ t->next = q;
+ else
+ *queue = q;
+ q->handler = handler;
+ return;
+}
+
+void
+unregister_eventhandler(struct eventqueue **queue, void (*handler)(void *))
+{
+ struct eventqueue *t, *q;
+
+ /* traverse to end of list */
+ t= NULL;
+ for (q = *queue ; q ; q = q->next) {
+ if (q->handler == handler) {
+ if (t)
+ t->next = q->next;
+ else
+ *queue = q->next;
+ dfree(q, MDL); /* Don't access q after this!*/
+ break;
+ }
+ t = q;
+ }
+ return;
+}
+
+void
+trigger_event(struct eventqueue **queue)
+{
+ struct eventqueue *q;
+
+ for (q=*queue ; q ; q=q->next) {
+ if (q->handler)
+ (*q->handler)(NULL);
+ }
+}
+
+/*
+ * Callback routine to connect the omapi I/O object and socket with
+ * the isc socket code. The isc socket code will call this routine
+ * which will then call the correct local routine to process the bytes.
+ *
+ * Currently we are always willing to read more data, this should be modified
+ * so that on connections we don't read more if we already have enough.
+ *
+ * If we have more bytes to write we ask the library to call us when
+ * we can write more. If we indicate we don't have more to write we need
+ * to poke the library via isc_socket_fdwatchpoke.
+ */
+
+/*
+ * sockdelete indicates if we are deleting the socket or leaving it in place
+ * 1 is delete, 0 is leave in place
+ */
+#define SOCKDELETE 1
+int
+omapi_iscsock_cb(isc_task_t *task,
+ isc_socket_t *socket,
+ void *cbarg,
+ int flags)
+{
+ omapi_io_object_t *obj;
+ isc_result_t status;
+
+ /* Get the current time... */
+ gettimeofday (&cur_tv, (struct timezone *)0);
+
+ /* isc socket stuff */
+#if SOCKDELETE
+ /*
+ * walk through the io states list, if our object is on there
+ * service it. if not ignore it.
+ */
+ for (obj = omapi_io_states.next;
+ (obj != NULL) && (obj->next != NULL);
+ obj = obj->next) {
+ if (obj == cbarg)
+ break;
+ }
+ if (obj == NULL) {
+ return(0);
+ }
+#else
+ /* Not much to be done if we have the wrong type of object. */
+ if (((omapi_object_t *)cbarg) -> type != omapi_type_io_object) {
+ log_fatal ("Incorrect object type, must be of type io_object");
+ }
+ obj = (omapi_io_object_t *)cbarg;
+
+ /*
+ * If the object is marked as closed don't try and process
+ * anything just indicate that we don't want any more.
+ *
+ * This should be a temporary fix until we arrange to properly
+ * close the socket.
+ */
+ if (obj->closed == ISC_TRUE) {
+ return(0);
+ }
+#endif
+
+ if ((flags == ISC_SOCKFDWATCH_READ) &&
+ (obj->reader != NULL) &&
+ (obj->inner != NULL)) {
+ obj->reader(obj->inner);
+ /* We always ask for more when reading */
+ return (1);
+ } else if ((flags == ISC_SOCKFDWATCH_WRITE) &&
+ (obj->writer != NULL) &&
+ (obj->inner != NULL)) {
+ status = obj->writer(obj->inner);
+ /* If the writer has more to write they should return
+ * ISC_R_INPROGRESS */
+ if (status == ISC_R_INPROGRESS) {
+ return (1);
+ }
+ }
+
+ /*
+ * We get here if we either had an error (inconsistent
+ * structures etc) or no more to write, tell the socket
+ * lib we don't have more to do right now.
+ */
+ return (0);
+}
+
+/* Register an I/O handle so that we can do asynchronous I/O on it. */
+
+isc_result_t omapi_register_io_object (omapi_object_t *h,
+ int (*readfd) (omapi_object_t *),
+ int (*writefd) (omapi_object_t *),
+ isc_result_t (*reader)
+ (omapi_object_t *),
+ isc_result_t (*writer)
+ (omapi_object_t *),
+ isc_result_t (*reaper)
+ (omapi_object_t *))
+{
+ isc_result_t status;
+ omapi_io_object_t *obj, *p;
+ int fd_flags = 0, fd = 0;
+
+ /* omapi_io_states is a static object. If its reference count
+ is zero, this is the first I/O handle to be registered, so
+ we need to initialize it. Because there is no inner or outer
+ pointer on this object, and we're setting its refcnt to 1, it
+ will never be freed. */
+ if (!omapi_io_states.refcnt) {
+ omapi_io_states.refcnt = 1;
+ omapi_io_states.type = omapi_type_io_object;
+ }
+
+ obj = (omapi_io_object_t *)0;
+ status = omapi_io_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ obj->closed = ISC_FALSE; /* mark as open */
+
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_io_dereference (&obj, MDL);
+ return status;
+ }
+
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_io_dereference (&obj, MDL);
+ return status;
+ }
+
+ /*
+ * Attach the I/O object to the isc socket library via the
+ * fdwatch function. This allows the socket library to watch
+ * over a socket that we built. If there are both a read and
+ * a write socket we asssume they are the same socket.
+ */
+
+ if (readfd) {
+ fd_flags |= ISC_SOCKFDWATCH_READ;
+ fd = readfd(h);
+ }
+
+ if (writefd) {
+ fd_flags |= ISC_SOCKFDWATCH_WRITE;
+ fd = writefd(h);
+ }
+
+ if (fd_flags != 0) {
+ status = isc_socket_fdwatchcreate(dhcp_gbl_ctx.socketmgr,
+ fd, fd_flags,
+ omapi_iscsock_cb,
+ obj,
+ dhcp_gbl_ctx.task,
+ &obj->fd);
+ if (status != ISC_R_SUCCESS) {
+ log_error("Unable to register fd with library %s",
+ isc_result_totext(status));
+
+ /*sar*/
+ /* is this the cleanup we need? */
+ omapi_object_dereference(&h->outer, MDL);
+ omapi_io_dereference (&obj, MDL);
+ return (status);
+ }
+ }
+
+
+ /* Find the last I/O state, if there are any. */
+ for (p = omapi_io_states.next;
+ p && p -> next; p = p -> next)
+ ;
+ if (p)
+ omapi_io_reference (&p -> next, obj, MDL);
+ else
+ omapi_io_reference (&omapi_io_states.next, obj, MDL);
+
+ obj -> readfd = readfd;
+ obj -> writefd = writefd;
+ obj -> reader = reader;
+ obj -> writer = writer;
+ obj -> reaper = reaper;
+
+ omapi_io_dereference(&obj, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * ReRegister an I/O handle so that we can do asynchronous I/O on it.
+ * If the handle doesn't exist we call the register routine to build it.
+ * If it does exist we change the functions associated with it, and
+ * repoke the fd code to make it happy. Neither the objects nor the
+ * fd are allowed to have changed.
+ */
+
+isc_result_t omapi_reregister_io_object (omapi_object_t *h,
+ int (*readfd) (omapi_object_t *),
+ int (*writefd) (omapi_object_t *),
+ isc_result_t (*reader)
+ (omapi_object_t *),
+ isc_result_t (*writer)
+ (omapi_object_t *),
+ isc_result_t (*reaper)
+ (omapi_object_t *))
+{
+ omapi_io_object_t *obj;
+ int fd_flags = 0;
+
+ if ((!h -> outer) || (h -> outer -> type != omapi_type_io_object)) {
+ /*
+ * If we don't have an object or if the type isn't what
+ * we expect do the normal registration (which will overwrite
+ * an incorrect type, that's what we did historically, may
+ * want to change that)
+ */
+ return (omapi_register_io_object (h, readfd, writefd,
+ reader, writer, reaper));
+ }
+
+ /* We have an io object of the correct type, try to update it */
+ /*sar*/
+ /* Should we validate that the fd matches the previous one?
+ * It's suppossed to, that's a requirement, don't bother yet */
+
+ obj = (omapi_io_object_t *)h->outer;
+
+ obj->readfd = readfd;
+ obj->writefd = writefd;
+ obj->reader = reader;
+ obj->writer = writer;
+ obj->reaper = reaper;
+
+ if (readfd) {
+ fd_flags |= ISC_SOCKFDWATCH_READ;
+ }
+
+ if (writefd) {
+ fd_flags |= ISC_SOCKFDWATCH_WRITE;
+ }
+
+ isc_socket_fdwatchpoke(obj->fd, fd_flags);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t omapi_unregister_io_object (omapi_object_t *h)
+{
+ omapi_io_object_t *obj, *ph;
+#if SOCKDELETE
+ omapi_io_object_t *p, *last;
+#endif
+
+ if (!h -> outer || h -> outer -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+ obj = (omapi_io_object_t *)h -> outer;
+ ph = (omapi_io_object_t *)0;
+ omapi_io_reference (&ph, obj, MDL);
+
+#if SOCKDELETE
+ /*
+ * For now we leave this out. We can't clean up the isc socket
+ * structure cleanly yet so we need to leave the io object in place.
+ * By leaving it on the io states list we avoid it being freed.
+ * We also mark it as closed to avoid using it.
+ */
+
+ /* remove from the list of I/O states */
+ last = &omapi_io_states;
+ for (p = omapi_io_states.next; p; p = p -> next) {
+ if (p == obj) {
+ omapi_io_dereference (&last -> next, MDL);
+ omapi_io_reference (&last -> next, p -> next, MDL);
+ break;
+ }
+ last = p;
+ }
+ if (obj -> next)
+ omapi_io_dereference (&obj -> next, MDL);
+#endif
+
+ if (obj -> outer) {
+ if (obj -> outer -> inner == (omapi_object_t *)obj)
+ omapi_object_dereference (&obj -> outer -> inner,
+ MDL);
+ omapi_object_dereference (&obj -> outer, MDL);
+ }
+ omapi_object_dereference (&obj -> inner, MDL);
+ omapi_object_dereference (&h -> outer, MDL);
+
+#if SOCKDELETE
+ /* remove isc socket associations */
+ if (obj->fd != NULL) {
+ isc_socket_cancel(obj->fd, dhcp_gbl_ctx.task,
+ ISC_SOCKCANCEL_ALL);
+ isc_socket_detach(&obj->fd);
+ }
+#else
+ obj->closed = ISC_TRUE;
+#endif
+
+ omapi_io_dereference (&ph, MDL);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_dispatch (struct timeval *t)
+{
+ return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states,
+ t);
+}
+
+isc_result_t omapi_wait_for_completion (omapi_object_t *object,
+ struct timeval *t)
+{
+ isc_result_t status;
+ omapi_waiter_object_t *waiter;
+ omapi_object_t *inner;
+
+ if (object) {
+ waiter = (omapi_waiter_object_t *)0;
+ status = omapi_waiter_allocate (&waiter, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Paste the waiter object onto the inner object we're
+ waiting on. */
+ for (inner = object; inner -> inner; inner = inner -> inner)
+ ;
+
+ status = omapi_object_reference (&waiter -> outer, inner, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_waiter_dereference (&waiter, MDL);
+ return status;
+ }
+
+ status = omapi_object_reference (&inner -> inner,
+ (omapi_object_t *)waiter,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_waiter_dereference (&waiter, MDL);
+ return status;
+ }
+ } else
+ waiter = (omapi_waiter_object_t *)0;
+
+ do {
+ status = omapi_one_dispatch ((omapi_object_t *)waiter, t);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ } while (!waiter || !waiter -> ready);
+
+ if (waiter -> outer) {
+ if (waiter -> outer -> inner) {
+ omapi_object_dereference (&waiter -> outer -> inner,
+ MDL);
+ if (waiter -> inner)
+ omapi_object_reference
+ (&waiter -> outer -> inner,
+ waiter -> inner, MDL);
+ }
+ omapi_object_dereference (&waiter -> outer, MDL);
+ }
+ if (waiter -> inner)
+ omapi_object_dereference (&waiter -> inner, MDL);
+
+ status = waiter -> waitstatus;
+ omapi_waiter_dereference (&waiter, MDL);
+ return status;
+}
+
+isc_result_t omapi_one_dispatch (omapi_object_t *wo,
+ struct timeval *t)
+{
+ fd_set r, w, x, rr, ww, xx;
+ int max = 0;
+ int count;
+ int desc;
+ struct timeval now, to;
+ omapi_io_object_t *io, *prev, *next;
+ omapi_waiter_object_t *waiter;
+ omapi_object_t *tmp = (omapi_object_t *)0;
+
+ if (!wo || wo -> type != omapi_type_waiter)
+ waiter = (omapi_waiter_object_t *)0;
+ else
+ waiter = (omapi_waiter_object_t *)wo;
+
+ FD_ZERO (&x);
+
+ /* First, see if the timeout has expired, and if so return. */
+ if (t) {
+ gettimeofday (&now, (struct timezone *)0);
+ cur_tv.tv_sec = now.tv_sec;
+ cur_tv.tv_usec = now.tv_usec;
+ if (now.tv_sec > t -> tv_sec ||
+ (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec))
+ return ISC_R_TIMEDOUT;
+
+ /* We didn't time out, so figure out how long until
+ we do. */
+ to.tv_sec = t -> tv_sec - now.tv_sec;
+ to.tv_usec = t -> tv_usec - now.tv_usec;
+ if (to.tv_usec < 0) {
+ to.tv_usec += 1000000;
+ to.tv_sec--;
+ }
+
+ /* It is possible for the timeout to get set larger than
+ the largest time select() is willing to accept.
+ Restricting the timeout to a maximum of one day should
+ work around this. -DPN. (Ref: Bug #416) */
+ if (to.tv_sec > (60 * 60 * 24))
+ to.tv_sec = 60 * 60 * 24;
+ }
+
+ /* If the object we're waiting on has reached completion,
+ return now. */
+ if (waiter && waiter -> ready)
+ return ISC_R_SUCCESS;
+
+ again:
+ /* If we have no I/O state, we can't proceed. */
+ if (!(io = omapi_io_states.next))
+ return ISC_R_NOMORE;
+
+ /* Set up the read and write masks. */
+ FD_ZERO (&r);
+ FD_ZERO (&w);
+
+ for (; io; io = io -> next) {
+ /* Check for a read socket. If we shouldn't be
+ trying to read for this I/O object, either there
+ won't be a readfd function, or it'll return -1. */
+ if (io -> readfd && io -> inner &&
+ (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
+ FD_SET (desc, &r);
+ if (desc > max)
+ max = desc;
+ }
+
+ /* Same deal for write fdets. */
+ if (io -> writefd && io -> inner &&
+ (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
+ FD_SET (desc, &w);
+ if (desc > max)
+ max = desc;
+ }
+ }
+
+ /* poll if all reader are dry */
+ now.tv_sec = 0;
+ now.tv_usec = 0;
+ rr=r;
+ ww=w;
+ xx=x;
+
+ /* poll once */
+ count = select(max + 1, &r, &w, &x, &now);
+ if (!count) {
+ /* We are dry now */
+ trigger_event(&rw_queue_empty);
+ /* Wait for a packet or a timeout... XXX */
+ r = rr;
+ w = ww;
+ x = xx;
+ count = select(max + 1, &r, &w, &x, t ? &to : NULL);
+ }
+
+ /* Get the current time... */
+ gettimeofday (&cur_tv, (struct timezone *)0);
+
+ /* We probably have a bad file descriptor. Figure out which one.
+ When we find it, call the reaper function on it, which will
+ maybe make it go away, and then try again. */
+ if (count < 0) {
+ struct timeval t0;
+ omapi_io_object_t *prev = (omapi_io_object_t *)0;
+ io = (omapi_io_object_t *)0;
+ if (omapi_io_states.next)
+ omapi_io_reference (&io, omapi_io_states.next, MDL);
+
+ while (io) {
+ omapi_object_t *obj;
+ FD_ZERO (&r);
+ FD_ZERO (&w);
+ t0.tv_sec = t0.tv_usec = 0;
+
+ if (io -> readfd && io -> inner &&
+ (desc = (*(io -> readfd)) (io -> inner)) >= 0) {
+ FD_SET (desc, &r);
+ count = select (desc + 1, &r, &w, &x, &t0);
+ bogon:
+ if (count < 0) {
+ log_error ("Bad descriptor %d.", desc);
+ for (obj = (omapi_object_t *)io;
+ obj -> outer;
+ obj = obj -> outer)
+ ;
+ for (; obj; obj = obj -> inner) {
+ omapi_value_t *ov;
+ int len;
+ const char *s;
+ ov = (omapi_value_t *)0;
+ omapi_get_value_str (obj,
+ (omapi_object_t *)0,
+ "name", &ov);
+ if (ov && ov -> value &&
+ (ov -> value -> type ==
+ omapi_datatype_string)) {
+ s = (char *)
+ ov -> value -> u.buffer.value;
+ len = ov -> value -> u.buffer.len;
+ } else {
+ s = "";
+ len = 0;
+ }
+ log_error ("Object %lx %s%s%.*s",
+ (unsigned long)obj,
+ obj -> type -> name,
+ len ? " " : "",
+ len, s);
+ if (len)
+ omapi_value_dereference (&ov, MDL);
+ }
+ (*(io -> reaper)) (io -> inner);
+ if (prev) {
+ omapi_io_dereference (&prev -> next, MDL);
+ if (io -> next)
+ omapi_io_reference (&prev -> next,
+ io -> next, MDL);
+ } else {
+ omapi_io_dereference
+ (&omapi_io_states.next, MDL);
+ if (io -> next)
+ omapi_io_reference
+ (&omapi_io_states.next,
+ io -> next, MDL);
+ }
+ omapi_io_dereference (&io, MDL);
+ goto again;
+ }
+ }
+
+ FD_ZERO (&r);
+ FD_ZERO (&w);
+ t0.tv_sec = t0.tv_usec = 0;
+
+ /* Same deal for write fdets. */
+ if (io -> writefd && io -> inner &&
+ (desc = (*(io -> writefd)) (io -> inner)) >= 0) {
+ FD_SET (desc, &w);
+ count = select (desc + 1, &r, &w, &x, &t0);
+ if (count < 0)
+ goto bogon;
+ }
+ if (prev)
+ omapi_io_dereference (&prev, MDL);
+ omapi_io_reference (&prev, io, MDL);
+ omapi_io_dereference (&io, MDL);
+ if (prev -> next)
+ omapi_io_reference (&io, prev -> next, MDL);
+ }
+ if (prev)
+ omapi_io_dereference (&prev, MDL);
+
+ }
+
+ for (io = omapi_io_states.next; io; io = io -> next) {
+ if (!io -> inner)
+ continue;
+ omapi_object_reference (&tmp, io -> inner, MDL);
+ /* Check for a read descriptor, and if there is one,
+ see if we got input on that socket. */
+ if (io -> readfd &&
+ (desc = (*(io -> readfd)) (tmp)) >= 0) {
+ if (FD_ISSET (desc, &r))
+ ((*(io -> reader)) (tmp));
+ }
+
+ /* Same deal for write descriptors. */
+ if (io -> writefd &&
+ (desc = (*(io -> writefd)) (tmp)) >= 0)
+ {
+ if (FD_ISSET (desc, &w))
+ ((*(io -> writer)) (tmp));
+ }
+ omapi_object_dereference (&tmp, MDL);
+ }
+
+ /* Now check for I/O handles that are no longer valid,
+ and remove them from the list. */
+ prev = NULL;
+ io = NULL;
+ if (omapi_io_states.next != NULL) {
+ omapi_io_reference(&io, omapi_io_states.next, MDL);
+ }
+ while (io != NULL) {
+ if ((io->inner == NULL) ||
+ ((io->reaper != NULL) &&
+ ((io->reaper)(io->inner) != ISC_R_SUCCESS)))
+ {
+
+ omapi_io_object_t *tmp = NULL;
+ /* Save a reference to the next
+ pointer, if there is one. */
+ if (io->next != NULL) {
+ omapi_io_reference(&tmp, io->next, MDL);
+ omapi_io_dereference(&io->next, MDL);
+ }
+ if (prev != NULL) {
+ omapi_io_dereference(&prev->next, MDL);
+ if (tmp != NULL)
+ omapi_io_reference(&prev->next,
+ tmp, MDL);
+ } else {
+ omapi_io_dereference(&omapi_io_states.next,
+ MDL);
+ if (tmp != NULL)
+ omapi_io_reference
+ (&omapi_io_states.next,
+ tmp, MDL);
+ else
+ omapi_signal_in(
+ (omapi_object_t *)
+ &omapi_io_states,
+ "ready");
+ }
+ if (tmp != NULL)
+ omapi_io_dereference(&tmp, MDL);
+
+ } else {
+
+ if (prev != NULL) {
+ omapi_io_dereference(&prev, MDL);
+ }
+ omapi_io_reference(&prev, io, MDL);
+ }
+
+ /*
+ * Equivalent to:
+ * io = io->next
+ * But using our reference counting voodoo.
+ */
+ next = NULL;
+ if (io->next != NULL) {
+ omapi_io_reference(&next, io->next, MDL);
+ }
+ omapi_io_dereference(&io, MDL);
+ if (next != NULL) {
+ omapi_io_reference(&io, next, MDL);
+ omapi_io_dereference(&next, MDL);
+ }
+ }
+ if (prev != NULL) {
+ omapi_io_dereference(&prev, MDL);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_io_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_io_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+/* omapi_io_destroy (object, MDL);
+ *
+ * Find the requested IO [object] and remove it from the list of io
+ * states, causing the cleanup functions to destroy it. Note that we must
+ * hold a reference on the object while moving its ->next reference and
+ * removing the reference in the chain to the target object...otherwise it
+ * may be cleaned up from under us.
+ */
+isc_result_t omapi_io_destroy (omapi_object_t *h, const char *file, int line)
+{
+ omapi_io_object_t *obj = NULL, *p, *last = NULL, **holder;
+
+ if (h -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+
+ /* remove from the list of I/O states */
+ for (p = omapi_io_states.next; p; p = p -> next) {
+ if (p == (omapi_io_object_t *)h) {
+ omapi_io_reference (&obj, p, MDL);
+
+ if (last)
+ holder = &last -> next;
+ else
+ holder = &omapi_io_states.next;
+
+ omapi_io_dereference (holder, MDL);
+
+ if (obj -> next) {
+ omapi_io_reference (holder, obj -> next, MDL);
+ omapi_io_dereference (&obj -> next, MDL);
+ }
+
+ return omapi_io_dereference (&obj, MDL);
+ }
+ last = p;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_io_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_io_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *i)
+{
+ if (i -> type != omapi_type_io_object)
+ return DHCP_R_INVALIDARG;
+
+ if (i -> inner && i -> inner -> type -> stuff_values)
+ return (*(i -> inner -> type -> stuff_values)) (c, id,
+ i -> inner);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_waiter_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ omapi_waiter_object_t *waiter;
+
+ if (h -> type != omapi_type_waiter)
+ return DHCP_R_INVALIDARG;
+
+ if (!strcmp (name, "ready")) {
+ waiter = (omapi_waiter_object_t *)h;
+ waiter -> ready = 1;
+ waiter -> waitstatus = ISC_R_SUCCESS;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!strcmp(name, "status")) {
+ waiter = (omapi_waiter_object_t *)h;
+ waiter->ready = 1;
+ waiter->waitstatus = va_arg(ap, isc_result_t);
+ return ISC_R_SUCCESS;
+ }
+
+ if (!strcmp (name, "disconnect")) {
+ waiter = (omapi_waiter_object_t *)h;
+ waiter -> ready = 1;
+ waiter -> waitstatus = DHCP_R_CONNRESET;
+ return ISC_R_SUCCESS;
+ }
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_io_state_foreach (isc_result_t (*func) (omapi_object_t *,
+ void *),
+ void *p)
+{
+ omapi_io_object_t *io;
+ isc_result_t status;
+
+ for (io = omapi_io_states.next; io; io = io -> next) {
+ if (io -> inner) {
+ status = (*func) (io -> inner, p);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
diff --git a/omapip/errwarn.c b/omapip/errwarn.c
new file mode 100644
index 0000000..722c3fc
--- /dev/null
+++ b/omapip/errwarn.c
@@ -0,0 +1,364 @@
+/* errwarn.c
+
+ Errors and warnings... */
+
+/*
+ * Copyright (c) 1995 RadioMail Corporation.
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software was written for RadioMail Corporation by Ted Lemon
+ * under a contract with Vixie Enterprises. Further modifications have
+ * been made for Internet Systems Consortium under a contract
+ * with Vixie Laboratories.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <errno.h>
+#include <syslog.h>
+
+#ifdef DEBUG
+int log_perror = -1;
+#else
+int log_perror = 1;
+#endif
+int log_priority;
+void (*log_cleanup) (void);
+
+#define CVT_BUF_MAX 1023
+static char mbuf [CVT_BUF_MAX + 1];
+static char fbuf [CVT_BUF_MAX + 1];
+
+/* Log an error message, then exit... */
+
+void log_fatal (const char * fmt, ... )
+{
+ va_list list;
+
+ do_percentm (fbuf, fmt);
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ 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) {
+ IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ }
+
+#if !defined (NOMINUM)
+ log_error ("%s", "");
+ log_error ("If you did not get this software from ftp.isc.org, please");
+ log_error ("get the latest from ftp.isc.org and install that before");
+ log_error ("requesting help.");
+ log_error ("%s", "");
+ log_error ("If you did get this software from ftp.isc.org and have not");
+ log_error ("yet read the README, please read it before requesting help.");
+ log_error ("If you intend to request help from the dhcp-server@isc.org");
+ log_error ("mailing list, please read the section on the README about");
+ log_error ("submitting bug reports and requests for help.");
+ log_error ("%s", "");
+ log_error ("Please do not under any circumstances send requests for");
+ log_error ("help directly to the authors of this software - please");
+ log_error ("send them to the appropriate mailing list as described in");
+ log_error ("the README file.");
+ log_error ("%s", "");
+ log_error ("exiting.");
+#endif
+ if (log_cleanup)
+ (*log_cleanup) ();
+ exit (1);
+}
+
+/* Log an error message... */
+
+int log_error (const char * fmt, ...)
+{
+ va_list list;
+
+ do_percentm (fbuf, fmt);
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ 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) {
+ IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ }
+
+ return 0;
+}
+
+/* Log a note... */
+
+int log_info (const char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm (fbuf, fmt);
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ 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) {
+ IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ }
+
+ return 0;
+}
+
+/* Log a debug message... */
+
+int log_debug (const char *fmt, ...)
+{
+ va_list list;
+
+ do_percentm (fbuf, fmt);
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ 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) {
+ IGNORE_RET (write (STDERR_FILENO, mbuf, strlen (mbuf)));
+ IGNORE_RET (write (STDERR_FILENO, "\n", 1));
+ }
+
+ return 0;
+}
+
+/* Find %m in the input string and substitute an error message string. */
+
+void do_percentm (obuf, ibuf)
+ char *obuf;
+ const char *ibuf;
+{
+ const char *s = ibuf;
+ char *p = obuf;
+ int infmt = 0;
+ const char *m;
+ int len = 0;
+
+ while (*s) {
+ if (infmt) {
+ if (*s == 'm') {
+#ifndef __CYGWIN32__
+ m = strerror (errno);
+#else
+ m = pWSAError ();
+#endif
+ if (!m)
+ m = "<unknown error>";
+ len += strlen (m);
+ if (len > CVT_BUF_MAX)
+ goto out;
+ strcpy (p - 1, m);
+ p += strlen (p);
+ ++s;
+ } else {
+ if (++len > CVT_BUF_MAX)
+ goto out;
+ *p++ = *s++;
+ }
+ infmt = 0;
+ } else {
+ if (*s == '%')
+ infmt = 1;
+ if (++len > CVT_BUF_MAX)
+ goto out;
+ *p++ = *s++;
+ }
+ }
+ out:
+ *p = 0;
+}
+
+#ifdef NO_STRERROR
+char *strerror (err)
+ int err;
+{
+ extern char *sys_errlist [];
+ extern int sys_nerr;
+ static char errbuf [128];
+
+ if (err < 0 || err >= sys_nerr) {
+ sprintf (errbuf, "Error %d", err);
+ return errbuf;
+ }
+ return sys_errlist [err];
+}
+#endif /* NO_STRERROR */
+
+#ifdef _WIN32
+char *pWSAError ()
+{
+ int err = WSAGetLastError ();
+
+ switch (err)
+ {
+ case WSAEACCES:
+ return "Permission denied";
+ case WSAEADDRINUSE:
+ return "Address already in use";
+ case WSAEADDRNOTAVAIL:
+ return "Cannot assign requested address";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family";
+ case WSAEALREADY:
+ return "Operation already in progress";
+ case WSAECONNABORTED:
+ return "Software caused connection abort";
+ case WSAECONNREFUSED:
+ return "Connection refused";
+ case WSAECONNRESET:
+ return "Connection reset by peer";
+ case WSAEDESTADDRREQ:
+ return "Destination address required";
+ case WSAEFAULT:
+ return "Bad address";
+ case WSAEHOSTDOWN:
+ return "Host is down";
+ case WSAEHOSTUNREACH:
+ return "No route to host";
+ case WSAEINPROGRESS:
+ return "Operation now in progress";
+ case WSAEINTR:
+ return "Interrupted function call";
+ case WSAEINVAL:
+ return "Invalid argument";
+ case WSAEISCONN:
+ return "Socket is already connected";
+ case WSAEMFILE:
+ return "Too many open files";
+ case WSAEMSGSIZE:
+ return "Message too long";
+ case WSAENETDOWN:
+ return "Network is down";
+ case WSAENETRESET:
+ return "Network dropped connection on reset";
+ case WSAENETUNREACH:
+ return "Network is unreachable";
+ case WSAENOBUFS:
+ return "No buffer space available";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option";
+ case WSAENOTCONN:
+ return "Socket is not connected";
+ case WSAENOTSOCK:
+ return "Socket operation on non-socket";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported";
+ case WSAEPROCLIM:
+ return "Too many processes";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket";
+ case WSAESHUTDOWN:
+ return "Cannot send after socket shutdown";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported";
+ case WSAETIMEDOUT:
+ return "Connection timed out";
+ case WSAEWOULDBLOCK:
+ return "Resource temporarily unavailable";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found";
+#if 0
+ case WSA_INVALID_HANDLE:
+ return "Specified event object handle is invalid";
+ case WSA_INVALID_PARAMETER:
+ return "One or more parameters are invalid";
+ case WSAINVALIDPROCTABLE:
+ return "Invalid procedure table from service provider";
+ case WSAINVALIDPROVIDER:
+ return "Invalid service provider version number";
+ case WSA_IO_PENDING:
+ return "Overlapped operations will complete later";
+ case WSA_IO_INCOMPLETE:
+ return "Overlapped I/O event object not in signaled state";
+ case WSA_NOT_ENOUGH_MEMORY:
+ return "Insufficient memory available";
+#endif
+ case WSANOTINITIALISED:
+ return "Successful WSAStartup not yet performer";
+ case WSANO_DATA:
+ return "Valid name, no data record of requested type";
+ case WSANO_RECOVERY:
+ return "This is a non-recoverable error";
+#if 0
+ case WSAPROVIDERFAILEDINIT:
+ return "Unable to initialize a service provider";
+ case WSASYSCALLFAILURE:
+ return "System call failure";
+#endif
+ case WSASYSNOTREADY:
+ return "Network subsystem is unavailable";
+ case WSATRY_AGAIN:
+ return "Non-authoritative host not found";
+ case WSAVERNOTSUPPORTED:
+ return "WINSOCK.DLL version out of range";
+ case WSAEDISCON:
+ return "Graceful shutdown in progress";
+#if 0
+ case WSA_OPERATION_ABORTED:
+ return "Overlapped operation aborted";
+#endif
+ }
+ return "Unknown WinSock error";
+}
+#endif /* _WIN32 */
diff --git a/omapip/generic.c b/omapip/generic.c
new file mode 100644
index 0000000..26e9361
--- /dev/null
+++ b/omapip/generic.c
@@ -0,0 +1,305 @@
+/* generic.c
+
+ Subroutines that support the generic object. */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+OMAPI_OBJECT_ALLOC (omapi_generic,
+ omapi_generic_object_t, omapi_type_generic)
+
+isc_result_t omapi_generic_new (omapi_object_t **gen,
+ const char *file, int line)
+{
+ /* Backwards compatibility. */
+ return omapi_generic_allocate ((omapi_generic_object_t **)gen,
+ file, line);
+}
+
+isc_result_t omapi_generic_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ omapi_generic_object_t *g;
+ omapi_value_t *new;
+ omapi_value_t **va;
+ u_int8_t *ca;
+ int vm_new;
+ int i, vfree = -1;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_generic)
+ return DHCP_R_INVALIDARG;
+ g = (omapi_generic_object_t *)h;
+
+ /* See if there's already a value with this name attached to
+ the generic object, and if so, replace the current value
+ with the new one. */
+ for (i = 0; i < g -> nvalues; i++) {
+ if (!omapi_data_string_cmp (name, g -> values [i] -> name)) {
+ /* There's an inconsistency here: the standard
+ behaviour of a set_values method when
+ passed a matching name and a null value is
+ to delete the value associated with that
+ name (where possible). In the generic
+ object, we remember the name/null pair,
+ because generic objects are generally used
+ to pass messages around, and this is the
+ way that remote entities delete values from
+ local objects. If the get_value method of
+ a generic object is called for a name that
+ maps to a name/null pair, ISC_R_NOTFOUND is
+ returned. */
+ new = (omapi_value_t *)0;
+ status = (omapi_value_new (&new, MDL));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ omapi_data_string_reference (&new -> name, name, MDL);
+ if (value)
+ omapi_typed_data_reference (&new -> value,
+ value, MDL);
+
+ omapi_value_dereference (&(g -> values [i]), MDL);
+ status = (omapi_value_reference
+ (&(g -> values [i]), new, MDL));
+ omapi_value_dereference (&new, MDL);
+ g -> changed [i] = 1;
+ return status;
+ }
+ /* Notice a free slot if we pass one. */
+ else if (vfree == -1 && !g -> values [i])
+ vfree = i;
+ }
+
+ /* If the name isn't already attached to this object, see if an
+ inner object has it. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status != ISC_R_NOTFOUND)
+ return status;
+ }
+
+ /* Okay, so it's a value that no inner object knows about, and
+ (implicitly, since the outer object set_value method would
+ have called this object's set_value method) it's an object that
+ no outer object knows about, it's this object's responsibility
+ to remember it - that's what generic objects do. */
+
+ /* Arrange for there to be space for the pointer to the new
+ name/value pair if necessary: */
+ if (vfree == -1) {
+ vfree = g -> nvalues;
+ if (vfree == g -> va_max) {
+ if (g -> va_max)
+ vm_new = 2 * g -> va_max;
+ else
+ vm_new = 10;
+ va = dmalloc (vm_new * sizeof *va, MDL);
+ if (!va)
+ return ISC_R_NOMEMORY;
+ ca = dmalloc (vm_new * sizeof *ca, MDL);
+ if (!ca) {
+ dfree (va, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ if (g -> va_max) {
+ memcpy (va, g -> values,
+ g -> va_max * sizeof *va);
+ memcpy (ca, g -> changed,
+ g -> va_max * sizeof *ca);
+ }
+ memset (va + g -> va_max, 0,
+ (vm_new - g -> va_max) * sizeof *va);
+ memset (ca + g -> va_max, 0,
+ (vm_new - g -> va_max) * sizeof *ca);
+ if (g -> values)
+ dfree (g -> values, MDL);
+ if (g -> changed)
+ dfree (g -> changed, MDL);
+ g -> values = va;
+ g -> changed = ca;
+ g -> va_max = vm_new;
+ }
+ }
+ status = omapi_value_new (&g -> values [vfree], MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ omapi_data_string_reference (&g -> values [vfree] -> name,
+ name, MDL);
+ if (value)
+ omapi_typed_data_reference
+ (&g -> values [vfree] -> value, value, MDL);
+ g -> changed [vfree] = 1;
+ if (vfree == g -> nvalues)
+ g -> nvalues++;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_generic_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ int i;
+ omapi_generic_object_t *g;
+
+ if (h -> type != omapi_type_generic)
+ return DHCP_R_INVALIDARG;
+ g = (omapi_generic_object_t *)h;
+
+ /* Look up the specified name in our list of objects. */
+ for (i = 0; i < g -> nvalues; i++) {
+ if (!g -> values[i])
+ continue;
+ if (!omapi_data_string_cmp (name, g -> values [i] -> name)) {
+ /* If this is a name/null value pair, this is the
+ same as if there were no value that matched
+ the specified name, so return ISC_R_NOTFOUND. */
+ if (!g -> values [i] -> value)
+ return ISC_R_NOTFOUND;
+ /* Otherwise, return the name/value pair. */
+ return omapi_value_reference (value,
+ g -> values [i], MDL);
+ }
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_generic_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_generic_object_t *g;
+ int i;
+
+ if (h -> type != omapi_type_generic)
+ return ISC_R_UNEXPECTED;
+ g = (omapi_generic_object_t *)h;
+
+ if (g -> values) {
+ for (i = 0; i < g -> nvalues; i++) {
+ if (g -> values [i])
+ omapi_value_dereference (&g -> values [i],
+ file, line);
+ }
+ dfree (g -> values, file, line);
+ dfree (g -> changed, file, line);
+ g -> values = (omapi_value_t **)0;
+ g -> changed = (u_int8_t *)0;
+ g -> va_max = 0;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_generic_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != omapi_type_generic)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_generic_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *g)
+{
+ omapi_generic_object_t *src;
+ int i;
+ isc_result_t status;
+
+ if (g -> type != omapi_type_generic)
+ return DHCP_R_INVALIDARG;
+ src = (omapi_generic_object_t *)g;
+
+ for (i = 0; i < src -> nvalues; i++) {
+ if (src -> values [i] && src -> values [i] -> name -> len &&
+ src -> changed [i]) {
+ status = (omapi_connection_put_uint16
+ (c, src -> values [i] -> name -> len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_copyin
+ (c, src -> values [i] -> name -> value,
+ src -> values [i] -> name -> len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = (omapi_connection_write_typed_data
+ (c, src -> values [i] -> value));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ }
+
+ if (g -> inner && g -> inner -> type -> stuff_values)
+ return (*(g -> inner -> type -> stuff_values)) (c, id,
+ g -> inner);
+ return ISC_R_SUCCESS;
+}
+
+/* Clear the changed flags on the object. This has the effect that if
+ generic_stuff is called, any attributes that still have a cleared changed
+ flag aren't sent to the peer. This also deletes any values that are
+ null, presuming that these have now been properly handled. */
+
+isc_result_t omapi_generic_clear_flags (omapi_object_t *o)
+{
+ int i;
+ omapi_generic_object_t *g;
+
+ if (o -> type != omapi_type_generic)
+ return DHCP_R_INVALIDARG;
+ g = (omapi_generic_object_t *)o;
+
+ for (i = 0; i < g -> nvalues; i++) {
+ g -> changed [i] = 0;
+ if (g -> values [i] &&
+ !g -> values [i] -> value)
+ omapi_value_dereference (&g -> values [i], MDL);
+ }
+ return ISC_R_SUCCESS;
+}
diff --git a/omapip/handle.c b/omapip/handle.c
new file mode 100644
index 0000000..b69ef12
--- /dev/null
+++ b/omapip/handle.c
@@ -0,0 +1,310 @@
+/* handle.c
+
+ Functions for maintaining handles on objects. */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+/* The handle table is a hierarchical tree designed for quick mapping
+ of handle identifiers to objects. Objects contain their own handle
+ identifiers if they have them, so the reverse mapping is also
+ quick. The hierarchy is made up of table objects, each of which
+ has 120 entries, a flag indicating whether the table is a leaf
+ table or an indirect table, the handle of the first object covered
+ by the table and the first object after that that's *not* covered
+ by the table, a count of how many objects of either type are
+ currently stored in the table, and an array of 120 entries pointing
+ either to objects or tables.
+
+ When we go to add an object to the table, we look to see if the
+ next object handle to be assigned is covered by the outermost
+ table. If it is, we find the place within that table where the
+ next handle should go, and if necessary create additional nodes in
+ the tree to contain the new handle. The pointer to the object is
+ then stored in the correct position.
+
+ Theoretically, we could have some code here to free up handle
+ tables as they go out of use, but by and large handle tables won't
+ go out of use, so this is being skipped for now. It shouldn't be
+ too hard to implement in the future if there's a different
+ application. */
+
+omapi_handle_table_t *omapi_handle_table;
+omapi_handle_t omapi_next_handle = 1; /* Next handle to be assigned. */
+
+#define FIND_HAND 0
+#define CLEAR_HAND 1
+
+static isc_result_t omapi_handle_lookup_in (omapi_object_t **,
+ omapi_handle_t,
+ omapi_handle_table_t *,
+ int);
+static isc_result_t omapi_object_handle_in_table (omapi_handle_t,
+ omapi_handle_table_t *,
+ omapi_object_t *);
+static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **);
+
+isc_result_t omapi_object_handle (omapi_handle_t *h, omapi_object_t *o)
+{
+ isc_result_t status;
+
+ if (o -> handle) {
+ *h = o -> handle;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_handle_table) {
+ omapi_handle_table = dmalloc (sizeof *omapi_handle_table, MDL);
+ if (!omapi_handle_table)
+ return ISC_R_NOMEMORY;
+ memset (omapi_handle_table, 0, sizeof *omapi_handle_table);
+ omapi_handle_table -> first = 0;
+ omapi_handle_table -> limit = OMAPI_HANDLE_TABLE_SIZE;
+ omapi_handle_table -> leafp = 1;
+ }
+
+ /* If this handle doesn't fit in the outer table, we need to
+ make a new outer table. This is a while loop in case for
+ some reason we decide to do disjoint handle allocation,
+ where the next level of indirection still isn't big enough
+ to enclose the next handle ID. */
+
+ while (omapi_next_handle >= omapi_handle_table -> limit) {
+ omapi_handle_table_t *new;
+
+ new = dmalloc (sizeof *new, MDL);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, sizeof *new);
+ new -> first = 0;
+ new -> limit = (omapi_handle_table -> limit *
+ OMAPI_HANDLE_TABLE_SIZE);
+ new -> leafp = 0;
+ new -> children [0].table = omapi_handle_table;
+ omapi_handle_table = new;
+ }
+
+ /* Try to cram this handle into the existing table. */
+ status = omapi_object_handle_in_table (omapi_next_handle,
+ omapi_handle_table, o);
+ /* If it worked, return the next handle and increment it. */
+ if (status == ISC_R_SUCCESS) {
+ *h = omapi_next_handle;
+ omapi_next_handle++;
+ return ISC_R_SUCCESS;
+ }
+ if (status != ISC_R_NOSPACE)
+ return status;
+
+ status = omapi_handle_table_enclose (&omapi_handle_table);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_handle_in_table (omapi_next_handle,
+ omapi_handle_table, o);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ *h = omapi_next_handle;
+ omapi_next_handle++;
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t omapi_object_handle_in_table (omapi_handle_t h,
+ omapi_handle_table_t *table,
+ omapi_object_t *o)
+{
+ omapi_handle_table_t *inner;
+ omapi_handle_t scale, index;
+ isc_result_t status;
+
+ if (table -> first > h || table -> limit <= h)
+ return ISC_R_NOSPACE;
+
+ /* If this is a leaf table, just stash the object in the
+ appropriate place. */
+ if (table -> leafp) {
+ status = (omapi_object_reference
+ (&table -> children [h - table -> first].object,
+ o, MDL));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ o -> handle = h;
+ return ISC_R_SUCCESS;
+ }
+
+ /* Scale is the number of handles represented by each child of this
+ table. For a leaf table, scale would be 1. For a first level
+ of indirection, 120. For a second, 120 * 120. Et cetera. */
+ scale = (table -> limit - table -> first) / OMAPI_HANDLE_TABLE_SIZE;
+
+ /* So the next most direct table from this one that contains the
+ handle must be the subtable of this table whose index into this
+ table's array of children is the handle divided by the scale. */
+ index = (h - table -> first) / scale;
+ inner = table -> children [index].table;
+
+ /* If there is no more direct table than this one in the slot
+ we came up with, make one. */
+ if (!inner) {
+ inner = dmalloc (sizeof *inner, MDL);
+ if (!inner)
+ return ISC_R_NOMEMORY;
+ memset (inner, 0, sizeof *inner);
+ inner -> first = index * scale + table -> first;
+ inner -> limit = inner -> first + scale;
+ if (scale == OMAPI_HANDLE_TABLE_SIZE)
+ inner -> leafp = 1;
+ table -> children [index].table = inner;
+ }
+
+ status = omapi_object_handle_in_table (h, inner, o);
+ if (status == ISC_R_NOSPACE) {
+ status = (omapi_handle_table_enclose
+ (&table -> children [index].table));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ return omapi_object_handle_in_table
+ (h, table -> children [index].table, o);
+ }
+ return status;
+}
+
+static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **table)
+{
+ omapi_handle_table_t *inner = *table;
+ omapi_handle_table_t *new;
+ int index, base, scale;
+
+ /* The scale of the table we're enclosing is going to be the
+ difference between its "first" and "limit" members. So the
+ scale of the table enclosing it is going to be that multiplied
+ by the table size. */
+ scale = (inner -> first - inner -> limit) * OMAPI_HANDLE_TABLE_SIZE;
+
+ /* The range that the enclosing table covers is going to be
+ the result of subtracting the remainder of dividing the
+ enclosed table's first entry number by the enclosing
+ table's scale. If handle IDs are being allocated
+ sequentially, the enclosing table's "first" value will be
+ the same as the enclosed table's "first" value. */
+ base = inner -> first - inner -> first % scale;
+
+ /* The index into the enclosing table at which the enclosed table
+ will be stored is going to be the difference between the "first"
+ value of the enclosing table and the enclosed table - zero, if
+ we are allocating sequentially. */
+ index = (base - inner -> first) / OMAPI_HANDLE_TABLE_SIZE;
+
+ new = dmalloc (sizeof *new, MDL);
+ if (!new)
+ return ISC_R_NOMEMORY;
+ memset (new, 0, sizeof *new);
+ new -> first = base;
+ new -> limit = base + scale;
+ if (scale == OMAPI_HANDLE_TABLE_SIZE)
+ new -> leafp = 0;
+ new -> children [index].table = inner;
+ *table = new;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_handle_lookup (omapi_object_t **o, omapi_handle_t h)
+{
+ return(omapi_handle_lookup_in(o, h, omapi_handle_table, FIND_HAND));
+}
+
+static isc_result_t omapi_handle_lookup_in (omapi_object_t **o,
+ omapi_handle_t h,
+ omapi_handle_table_t *table,
+ int op)
+{
+ omapi_handle_table_t *inner;
+ omapi_handle_t scale, index;
+
+ if (!table || table->first > h || table->limit <= h)
+ return(ISC_R_NOTFOUND);
+
+ /* If this is a leaf table, just grab the object. */
+ if (table->leafp) {
+ /* Not there? */
+ if (!table->children[h - table->first].object)
+ return(ISC_R_NOTFOUND);
+ if (op == CLEAR_HAND) {
+ table->children[h - table->first].object = NULL;
+ return(ISC_R_SUCCESS);
+ } else {
+ return(omapi_object_reference
+ (o, table->children[h - table->first].object,
+ MDL));
+ }
+ }
+
+ /* Scale is the number of handles represented by each child of this
+ table. For a leaf table, scale would be 1. For a first level
+ of indirection, 120. For a second, 120 * 120. Et cetera. */
+ scale = (table->limit - table->first) / OMAPI_HANDLE_TABLE_SIZE;
+
+ /* So the next most direct table from this one that contains the
+ handle must be the subtable of this table whose index into this
+ table's array of children is the handle divided by the scale. */
+ index = (h - table->first) / scale;
+ inner = table->children[index].table;
+
+ return(omapi_handle_lookup_in(o, h, table->children[index].table, op));
+}
+
+/* For looking up objects based on handles that have been sent on the wire. */
+isc_result_t omapi_handle_td_lookup (omapi_object_t **obj,
+ omapi_typed_data_t *handle)
+{
+ omapi_handle_t h;
+
+ if (handle->type == omapi_datatype_int)
+ h = handle->u.integer;
+ else if (handle->type == omapi_datatype_data &&
+ handle->u.buffer.len == sizeof h) {
+ memcpy(&h, handle->u.buffer.value, sizeof h);
+ h = ntohl(h);
+ } else
+ return(DHCP_R_INVALIDARG);
+ return(omapi_handle_lookup(obj, h));
+}
+
+isc_result_t omapi_handle_clear(omapi_handle_t h)
+{
+ return(omapi_handle_lookup_in(NULL, h, omapi_handle_table, CLEAR_HAND));
+}
diff --git a/omapip/hash.c b/omapip/hash.c
new file mode 100644
index 0000000..c3aa737
--- /dev/null
+++ b/omapip/hash.c
@@ -0,0 +1,566 @@
+/* hash.c
+
+ Routines for manipulating hash tables... */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <limits.h>
+#include <ctype.h>
+
+static unsigned
+find_length(const void *key,
+ unsigned (*do_hash)(const void *, unsigned, unsigned))
+{
+ if (do_hash == do_case_hash || do_hash == do_string_hash)
+ return strlen((const char *)key);
+ if (do_hash == do_number_hash)
+ return sizeof(unsigned);
+ if (do_hash == do_ip4_hash)
+ return 4;
+
+ log_debug("Unexpected hash function at %s:%d.", MDL);
+ /*
+ * If we get a hash function we don't specifically expect
+ * return a length of 0, this covers the case where a client
+ * id has a length of 0.
+ */
+ return 0;
+}
+
+int new_hash_table (tp, count, file, line)
+ struct hash_table **tp;
+ unsigned count;
+ const char *file;
+ int line;
+{
+ struct hash_table *rval;
+ unsigned extra;
+
+ if (!tp) {
+ log_error ("%s(%d): new_hash_table called with null pointer.",
+ file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#endif
+ return 0;
+ }
+ if (*tp) {
+ log_error ("%s(%d): non-null target for new_hash_table.",
+ file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#endif
+ }
+
+ /* There is one hash bucket in the structure. Allocate extra
+ * memory beyond the end of the structure to fulfill the requested
+ * count ("count - 1"). Do not let there be less than one.
+ */
+ if (count <= 1)
+ extra = 0;
+ else
+ extra = count - 1;
+
+ rval = dmalloc(sizeof(struct hash_table) +
+ (extra * sizeof(struct hash_bucket *)), file, line);
+ if (!rval)
+ return 0;
+ rval -> hash_count = count;
+ *tp = rval;
+ return 1;
+}
+
+void free_hash_table (tp, file, line)
+ struct hash_table **tp;
+ const char *file;
+ int line;
+{
+ struct hash_table *ptr = *tp;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ int i;
+ struct hash_bucket *hbc, *hbn = (struct hash_bucket *)0;
+
+ for (i = 0; i < ptr -> hash_count; i++) {
+ for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
+ hbn = hbc -> next;
+ if (ptr -> dereferencer && hbc -> value)
+ (*ptr -> dereferencer) (&hbc -> value, MDL);
+ }
+ for (hbc = ptr -> buckets [i]; hbc; hbc = hbn) {
+ hbn = hbc -> next;
+ free_hash_bucket (hbc, MDL);
+ }
+ ptr -> buckets [i] = (struct hash_bucket *)0;
+ }
+#endif
+
+ dfree((void *)ptr, MDL);
+ *tp = (struct hash_table *)0;
+}
+
+struct hash_bucket *free_hash_buckets;
+
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+struct hash_bucket *hash_bucket_hunks;
+
+void relinquish_hash_bucket_hunks ()
+{
+ struct hash_bucket *c, *n, **p;
+
+ /* Account for all the hash buckets on the free list. */
+ p = &free_hash_buckets;
+ for (c = free_hash_buckets; c; c = c -> next) {
+ for (n = hash_bucket_hunks; n; n = n -> next) {
+ if (c > n && c < n + 127) {
+ *p = c -> next;
+ n -> len++;
+ break;
+ }
+ }
+ /* If we didn't delete the hash bucket from the free list,
+ advance the pointer. */
+ if (!n)
+ p = &c -> next;
+ }
+
+ for (c = hash_bucket_hunks; c; c = n) {
+ n = c -> next;
+ if (c -> len != 126) {
+ log_info ("hashbucket %lx hash_buckets %d free %u",
+ (unsigned long)c, 127, c -> len);
+ }
+ dfree (c, MDL);
+ }
+}
+#endif
+
+struct hash_bucket *new_hash_bucket (file, line)
+ const char *file;
+ int line;
+{
+ struct hash_bucket *rval;
+ int i = 0;
+ if (!free_hash_buckets) {
+ rval = dmalloc (127 * sizeof (struct hash_bucket),
+ file, line);
+ if (!rval)
+ return rval;
+# if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ rval -> next = hash_bucket_hunks;
+ hash_bucket_hunks = rval;
+ hash_bucket_hunks -> len = 0;
+ i++;
+ rval++;
+#endif
+ for (; i < 127; i++) {
+ rval -> next = free_hash_buckets;
+ free_hash_buckets = rval;
+ rval++;
+ }
+ }
+ rval = free_hash_buckets;
+ free_hash_buckets = rval -> next;
+ return rval;
+}
+
+void free_hash_bucket (ptr, file, line)
+ struct hash_bucket *ptr;
+ const char *file;
+ int line;
+{
+#if defined (DEBUG_MALLOC_POOL)
+ struct hash_bucket *hp;
+
+ for (hp = free_hash_buckets; hp; hp = hp -> next) {
+ if (hp == ptr) {
+ log_error ("hash bucket freed twice!");
+ abort ();
+ }
+ }
+#endif
+ ptr -> next = free_hash_buckets;
+ free_hash_buckets = ptr;
+}
+
+int new_hash(struct hash_table **rp,
+ hash_reference referencer,
+ hash_dereference dereferencer,
+ unsigned hsize,
+ unsigned (*hasher)(const void *, unsigned, unsigned),
+ const char *file, int line)
+{
+ if (hsize == 0)
+ hsize = DEFAULT_HASH_SIZE;
+
+ if (!new_hash_table (rp, hsize, file, line))
+ return 0;
+
+ memset ((*rp)->buckets, 0, hsize * sizeof(struct hash_bucket *));
+
+ (*rp)->referencer = referencer;
+ (*rp)->dereferencer = dereferencer;
+ (*rp)->do_hash = hasher;
+
+ if (hasher == do_case_hash)
+ (*rp)->cmp = casecmp;
+ else
+ (*rp)->cmp = memcmp;
+
+ return 1;
+}
+
+unsigned
+do_case_hash(const void *name, unsigned len, unsigned size)
+{
+ register unsigned accum = 0;
+ register const unsigned char *s = name;
+ int i = len;
+ register unsigned c;
+
+ while (i--) {
+ /* Make the hash case-insensitive. */
+ c = *s++;
+ if (isascii(c))
+ c = tolower(c);
+
+ /* Add the character in... */
+ accum = (accum << 1) + c;
+
+ /* Add carry back in... */
+ while (accum > 65535) {
+ accum = (accum & 65535) + (accum >> 16);
+ }
+
+ }
+ return accum % size;
+}
+
+unsigned
+do_string_hash(const void *name, unsigned len, unsigned size)
+{
+ register unsigned accum = 0;
+ register const unsigned char *s = (const unsigned char *)name;
+ int i = len;
+
+ while (i--) {
+ /* Add the character in... */
+ accum = (accum << 1) + *s++;
+
+ /* Add carry back in... */
+ while (accum > 65535) {
+ accum = (accum & 65535) + (accum >> 16);
+ }
+ }
+ return accum % size;
+}
+
+/* Client identifiers are generally 32-bits of ordinary
+ * non-randomness followed by 24-bits of unordinary randomness.
+ * So, end-align in 24-bit chunks, and xor any preceding data
+ * just to mix it up a little.
+ */
+unsigned
+do_id_hash(const void *name, unsigned len, unsigned size)
+{
+ register unsigned accum = 0;
+ register const unsigned char *s = (const unsigned char *)name;
+ const unsigned char *end = s + len;
+
+ if (len == 0)
+ return 0;
+
+ /*
+ * The switch handles our starting conditions, then we hash the
+ * remaining bytes in groups of 3
+ */
+
+ switch (len % 3) {
+ case 0:
+ break;
+ case 2:
+ accum ^= *s++ << 8;
+ case 1:
+ accum ^= *s++;
+ break;
+ }
+
+ while (s < end) {
+ accum ^= *s++ << 16;
+ accum ^= *s++ << 8;
+ accum ^= *s++;
+ }
+
+ return accum % size;
+}
+
+unsigned
+do_number_hash(const void *key, unsigned len, unsigned size)
+{
+ register unsigned number = *((const unsigned *)key);
+
+ return number % size;
+}
+
+unsigned
+do_ip4_hash(const void *key, unsigned len, unsigned size)
+{
+ u_int32_t number;
+
+ memcpy(&number, key, 4);
+
+ number = ntohl(number);
+
+ return number % size;
+}
+
+unsigned char *
+hash_report(struct hash_table *table)
+{
+ static unsigned char retbuf[sizeof("Contents/Size (%): "
+ "2147483647/2147483647 "
+ "(2147483647%). "
+ "Min/max: 2147483647/2147483647")];
+ unsigned curlen, pct, contents=0, minlen=UINT_MAX, maxlen=0;
+ unsigned i;
+ struct hash_bucket *bp;
+
+ if (table == NULL)
+ return (unsigned char *) "No table.";
+
+ if (table->hash_count == 0)
+ return (unsigned char *) "Invalid hash table.";
+
+ for (i = 0 ; i < table->hash_count ; i++) {
+ curlen = 0;
+
+ bp = table->buckets[i];
+ while (bp != NULL) {
+ curlen++;
+ bp = bp->next;
+ }
+
+ if (curlen < minlen)
+ minlen = curlen;
+ if (curlen > maxlen)
+ maxlen = curlen;
+
+ contents += curlen;
+ }
+
+ if (contents >= (UINT_MAX / 100))
+ pct = contents / ((table->hash_count / 100) + 1);
+ else
+ pct = (contents * 100) / table->hash_count;
+
+ if (contents > 2147483647 ||
+ table->hash_count > 2147483647 ||
+ pct > 2147483647 ||
+ minlen > 2147483647 ||
+ maxlen > 2147483647)
+ return (unsigned char *) "Report out of range for display.";
+
+ sprintf((char *)retbuf,
+ "Contents/Size (%%): %u/%u (%u%%). Min/max: %u/%u",
+ contents, table->hash_count, pct, minlen, maxlen);
+
+ return retbuf;
+}
+
+void add_hash (table, key, len, pointer, file, line)
+ struct hash_table *table;
+ unsigned len;
+ const void *key;
+ hashed_object_t *pointer;
+ const char *file;
+ int line;
+{
+ int hashno;
+ struct hash_bucket *bp;
+ void *foo;
+
+ if (!table)
+ return;
+
+ if (!len)
+ len = find_length(key, table->do_hash);
+
+ hashno = (*table->do_hash)(key, len, table->hash_count);
+ bp = new_hash_bucket (file, line);
+
+ if (!bp) {
+ log_error ("Can't add entry to hash table: no memory.");
+ return;
+ }
+ bp -> name = key;
+ if (table -> referencer) {
+ foo = &bp -> value;
+ (*(table -> referencer)) (foo, pointer, file, line);
+ } else
+ bp -> value = pointer;
+ bp -> next = table -> buckets [hashno];
+ bp -> len = len;
+ table -> buckets [hashno] = bp;
+}
+
+void delete_hash_entry (table, key, len, file, line)
+ struct hash_table *table;
+ unsigned len;
+ const void *key;
+ const char *file;
+ int line;
+{
+ int hashno;
+ struct hash_bucket *bp, *pbp = (struct hash_bucket *)0;
+ void *foo;
+
+ if (!table)
+ return;
+
+ if (!len)
+ len = find_length(key, table->do_hash);
+
+ hashno = (*table->do_hash)(key, len, table->hash_count);
+
+ /* Go through the list looking for an entry that matches;
+ if we find it, delete it. */
+ for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
+ if ((!bp -> len &&
+ !strcmp ((const char *)bp->name, key)) ||
+ (bp -> len == len &&
+ !(table -> cmp)(bp->name, key, len))) {
+ if (pbp) {
+ pbp -> next = bp -> next;
+ } else {
+ table -> buckets [hashno] = bp -> next;
+ }
+ if (bp -> value && table -> dereferencer) {
+ foo = &bp -> value;
+ (*(table -> dereferencer)) (foo, file, line);
+ }
+ free_hash_bucket (bp, file, line);
+ break;
+ }
+ pbp = bp; /* jwg, 9/6/96 - nice catch! */
+ }
+}
+
+int hash_lookup (vp, table, key, len, file, line)
+ hashed_object_t **vp;
+ struct hash_table *table;
+ const void *key;
+ unsigned len;
+ const char *file;
+ int line;
+{
+ int hashno;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return 0;
+ if (!len)
+ len = find_length(key, table->do_hash);
+
+ if (*vp != NULL) {
+ log_fatal("Internal inconsistency: storage value has not been "
+ "initialized to zero (from %s:%d).", file, line);
+ }
+
+ hashno = (*table->do_hash)(key, len, table->hash_count);
+
+ for (bp = table -> buckets [hashno]; bp; bp = bp -> next) {
+ if (len == bp -> len
+ && !(*table->cmp)(bp->name, key, len)) {
+ if (table -> referencer)
+ (*table -> referencer) (vp, bp -> value,
+ file, line);
+ else
+ *vp = bp -> value;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int hash_foreach (struct hash_table *table, hash_foreach_func func)
+{
+ int i;
+ struct hash_bucket *bp, *next;
+ int count = 0;
+
+ if (!table)
+ return 0;
+
+ for (i = 0; i < table -> hash_count; i++) {
+ bp = table -> buckets [i];
+ while (bp) {
+ next = bp -> next;
+ if ((*func)(bp->name, bp->len, bp->value)
+ != ISC_R_SUCCESS)
+ return count;
+ bp = next;
+ count++;
+ }
+ }
+ return count;
+}
+
+int casecmp (const void *v1, const void *v2, size_t len)
+{
+ size_t i;
+ const unsigned char *s = v1;
+ const unsigned char *t = v2;
+
+ for (i = 0; i < len; i++)
+ {
+ int c1, c2;
+ if (isascii(s[i]))
+ c1 = tolower(s[i]);
+ else
+ c1 = s[i];
+
+ if (isascii(t[i]))
+ c2 = tolower(t[i]);
+ else
+ c2 = t[i];
+
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ }
+ return 0;
+}
diff --git a/omapip/inet_addr.c b/omapip/inet_addr.c
new file mode 100644
index 0000000..c5ccc79
--- /dev/null
+++ b/omapip/inet_addr.c
@@ -0,0 +1,147 @@
+/* $NetBSD: inet_addr.c,v 1.6 1996/02/02 15:22:23 mrg Exp $ */
+
+/*
+ * Copyright (c) 1983, 1990, 1993
+ * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93";
+#else
+static char rcsid[] = "$NetBSD: inet_addr.c,v 1.6 1996/02/02 15:22:23 mrg Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include "dhcpd.h"
+
+#include "omapip/omapip_p.h"
+
+#ifdef NEED_INET_ATON
+/*
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ */
+int
+inet_aton(cp, addr)
+ const char *cp;
+ struct in_addr *addr;
+{
+ register u_long val;
+ register int base, n;
+ register char c;
+ u_int parts[4];
+ register u_int *pp = parts;
+
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, other=decimal.
+ */
+ val = 0; base = 10;
+ if (*cp == '0') {
+ if (*++cp == 'x' || *cp == 'X')
+ base = 16, cp++;
+ else
+ base = 8;
+ }
+ while ((c = *cp) != '\0') {
+ if (isascii(c) && isdigit((int)c)) {
+ val = (val * base) + (c - '0');
+ cp++;
+ continue;
+ }
+ if (base == 16 && isascii(c) && isxdigit((int)c)) {
+ val = (val << 4) +
+ (c + 10 - (islower((int)c) ? 'a' : 'A'));
+ cp++;
+ continue;
+ }
+ break;
+ }
+ if (*cp == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16-bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3 || val > 0xff)
+ return (0);
+ *pp++ = val, cp++;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (*cp && (!isascii(*cp) || !isspace((int)*cp)))
+ return (0);
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ n = pp - parts + 1;
+ switch (n) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+ if (addr)
+ addr->s_addr = htonl(val);
+ return (1);
+}
+#endif
diff --git a/omapip/isclib.c b/omapip/isclib.c
new file mode 100644
index 0000000..1534dde
--- /dev/null
+++ b/omapip/isclib.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright(c) 2009-2010 by Internet Systems Consortium, Inc.("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * http://www.isc.org/
+ *
+ */
+
+/*Trying to figure out what we need to define to get things to work.
+ It looks like we want/need the export library but need the fdwatchcommand
+ which may be a problem */
+
+#include "dhcpd.h"
+
+#include <sys/time.h>
+
+dhcp_context_t dhcp_gbl_ctx;
+
+void
+isclib_cleanup(void)
+{
+#if defined (NSUPDATE)
+ if (dhcp_gbl_ctx.dnsclient != NULL)
+ dns_client_destroy((dns_client_t **)&dhcp_gbl_ctx.dnsclient);
+#endif
+
+ if (dhcp_gbl_ctx.task != NULL) {
+ isc_task_shutdown(dhcp_gbl_ctx.task);
+ isc_task_detach(&dhcp_gbl_ctx.task);
+ }
+
+ if (dhcp_gbl_ctx.timermgr != NULL)
+ isc_timermgr_destroy(&dhcp_gbl_ctx.timermgr);
+
+ if (dhcp_gbl_ctx.socketmgr != NULL)
+ isc_socketmgr_destroy(&dhcp_gbl_ctx.socketmgr);
+
+ if (dhcp_gbl_ctx.taskmgr != NULL)
+ isc_taskmgr_destroy(&dhcp_gbl_ctx.taskmgr);
+
+ if (dhcp_gbl_ctx.actx_started != ISC_FALSE) {
+ isc_app_ctxfinish(dhcp_gbl_ctx.actx);
+ dhcp_gbl_ctx.actx_started = ISC_FALSE;
+ }
+
+ if (dhcp_gbl_ctx.actx != NULL)
+ isc_appctx_destroy(&dhcp_gbl_ctx.actx);
+
+ if (dhcp_gbl_ctx.mctx != NULL)
+ isc_mem_detach(&dhcp_gbl_ctx.mctx);
+
+ return;
+}
+
+isc_result_t
+dhcp_context_create(void) {
+ isc_result_t result;
+
+ /*
+ * Set up the error messages, this isn't the right place
+ * for this call but it is convienent for now.
+ */
+ result = dhcp_result_register();
+ if (result != ISC_R_SUCCESS) {
+ log_fatal("register_table() %s: %u", "failed", result);
+ }
+
+ memset(&dhcp_gbl_ctx, 0, sizeof (dhcp_gbl_ctx));
+
+ isc_lib_register();
+
+ /* get the current time for use as the random seed */
+ gettimeofday(&cur_tv, (struct timezone *)0);
+ isc_random_seed(cur_tv.tv_sec);
+
+#if defined (NSUPDATE)
+ result = dns_lib_init();
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ result = isc_mem_create(0, 0, &dhcp_gbl_ctx.mctx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_appctx_create(dhcp_gbl_ctx.mctx, &dhcp_gbl_ctx.actx);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_app_ctxstart(dhcp_gbl_ctx.actx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dhcp_gbl_ctx.actx_started = ISC_TRUE;
+
+ result = isc_taskmgr_createinctx(dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.actx,
+ 1, 0,
+ &dhcp_gbl_ctx.taskmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_socketmgr_createinctx(dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.actx,
+ &dhcp_gbl_ctx.socketmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_timermgr_createinctx(dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.actx,
+ &dhcp_gbl_ctx.timermgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_task_create(dhcp_gbl_ctx.taskmgr, 0, &dhcp_gbl_ctx.task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+#if defined (NSUPDATE)
+ result = dns_client_createx(dhcp_gbl_ctx.mctx,
+ dhcp_gbl_ctx.actx,
+ dhcp_gbl_ctx.taskmgr,
+ dhcp_gbl_ctx.socketmgr,
+ dhcp_gbl_ctx.timermgr,
+ 0,
+ &dhcp_gbl_ctx.dnsclient);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#else
+ /* The dst library is inited as part of dns_lib_init, we don't
+ * need it if NSUPDATE is enabled */
+ result = dst_lib_init(dhcp_gbl_ctx.mctx, NULL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+#endif
+ return(ISC_R_SUCCESS);
+
+ cleanup:
+ /*
+ * Currently we don't try and cleanup, just return an error
+ * expecting that our caller will log the error and exit.
+ */
+
+ return(result);
+}
+
+/*
+ * Convert a string name into the proper structure for the isc routines
+ *
+ * Previously we allowed names without a trailing '.' however the current
+ * dns and dst code requires the names to end in a period. If the
+ * name doesn't have a trailing period add one as part of creating
+ * the dns name.
+ */
+
+isc_result_t
+dhcp_isc_name(unsigned char *namestr,
+ dns_fixedname_t *namefix,
+ dns_name_t **name)
+{
+ size_t namelen;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ namelen = strlen((char *)namestr);
+ isc_buffer_init(&b, namestr, namelen);
+ isc_buffer_add(&b, namelen);
+ dns_fixedname_init(namefix);
+ *name = dns_fixedname_name(namefix);
+ result = dns_name_fromtext(*name, &b, dns_rootname, 0, NULL);
+ isc_buffer_invalidate(&b);
+ return(result);
+}
+
+isc_result_t
+isclib_make_dst_key(char *inname,
+ char *algorithm,
+ unsigned char *secret,
+ int length,
+ dst_key_t **dstkey)
+{
+ isc_result_t result;
+ dns_name_t *name;
+ dns_fixedname_t name0;
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, secret, length);
+ isc_buffer_add(&b, length);
+
+ /* We only support HMAC_MD5 currently */
+ if (strcasecmp(algorithm, DHCP_HMAC_MD5_NAME) != 0) {
+ return(DHCP_R_INVALIDARG);
+ }
+
+ result = dhcp_isc_name((unsigned char *)inname, &name0, &name);
+ if (result != ISC_R_SUCCESS) {
+ return(result);
+ }
+
+ return(dst_key_frombuffer(name, DST_ALG_HMACMD5, DNS_KEYOWNER_ENTITY,
+ DNS_KEYPROTO_DNSSEC, dns_rdataclass_in,
+ &b, dhcp_gbl_ctx.mctx, dstkey));
+}
+
diff --git a/omapip/iscprint.c b/omapip/iscprint.c
new file mode 100644
index 0000000..bffb564
--- /dev/null
+++ b/omapip/iscprint.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001, 2003 Internet Software Consortium.
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: iscprint.c,v 1.2 2005-03-17 20:30:41 dhankins Exp $ */
+
+#include "dhcpd.h"
+
+#ifdef NO_SNPRINTF
+
+#ifndef LINT
+static char copyright[] =
+"$Id: iscprint.c,v 1.2 2005-03-17 20:30:41 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium, Inc. All rights reserved.";
+#endif
+
+#define INSIST(cond) REQUIRE(cond)
+#define REQUIRE(cond) if (!(cond)) { return 0; }
+
+/*
+ * Return length of string that would have been written if not truncated.
+ */
+
+int
+isc_print_snprintf(char *str, size_t size, const char *format, ...) {
+ va_list ap;
+ int ret;
+
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return (ret);
+}
+
+/*
+ * Return length of string that would have been written if not truncated.
+ */
+
+int
+isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
+ int h;
+ int l;
+ int q;
+ int alt;
+ int zero;
+ int left;
+ int plus;
+ int space;
+ int neg;
+ isc_int64_t tmpi;
+ isc_uint64_t tmpui;
+ unsigned long width;
+ unsigned long precision;
+ unsigned int length;
+ char buf[1024];
+ char c;
+ void *v;
+ char *save = str;
+ const char *cp;
+ const char *head;
+ int count = 0;
+ int pad;
+ int zeropad;
+ int dot;
+ double dbl;
+#ifdef HAVE_LONG_DOUBLE
+ long double ldbl;
+#endif
+ char fmt[32];
+
+ INSIST(str != NULL);
+ INSIST(format != NULL);
+
+ while (*format != '\0') {
+ if (*format != '%') {
+ if (size > 1) {
+ *str++ = *format;
+ size--;
+ }
+ count++;
+ format++;
+ continue;
+ }
+ format++;
+
+ /*
+ * Reset flags.
+ */
+ dot = neg = space = plus = left = zero = alt = h = l = q = 0;
+ width = precision = 0;
+ head = "";
+ length = pad = zeropad = 0;
+
+ do {
+ if (*format == '#') {
+ alt = 1;
+ format++;
+ } else if (*format == '-') {
+ left = 1;
+ zero = 0;
+ format++;
+ } else if (*format == ' ') {
+ if (!plus)
+ space = 1;
+ format++;
+ } else if (*format == '+') {
+ plus = 1;
+ space = 0;
+ format++;
+ } else if (*format == '0') {
+ if (!left)
+ zero = 1;
+ format++;
+ } else
+ break;
+ } while (1);
+
+ /*
+ * Width.
+ */
+ if (*format == '*') {
+ width = va_arg(ap, int);
+ format++;
+ } else if (isdigit((unsigned char)*format)) {
+ char *e;
+ width = strtoul(format, &e, 10);
+ format = e;
+ }
+
+ /*
+ * Precision.
+ */
+ if (*format == '.') {
+ format++;
+ dot = 1;
+ if (*format == '*') {
+ precision = va_arg(ap, int);
+ format++;
+ } else if (isdigit((unsigned char)*format)) {
+ char *e;
+ precision = strtoul(format, &e, 10);
+ format = e;
+ }
+ }
+
+ switch (*format) {
+ case '\0':
+ continue;
+ case '%':
+ if (size > 1) {
+ *str++ = *format;
+ size--;
+ }
+ count++;
+ break;
+ case 'q':
+ q = 1;
+ format++;
+ goto doint;
+ case 'h':
+ h = 1;
+ format++;
+ goto doint;
+ case 'l':
+ l = 1;
+ format++;
+ if (*format == 'l') {
+ q = 1;
+ format++;
+ }
+ goto doint;
+ case 'n':
+ case 'i':
+ case 'd':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ doint:
+ if (precision != 0)
+ zero = 0;
+ switch (*format) {
+ case 'n':
+ if (h) {
+ short int *p;
+ p = va_arg(ap, short *);
+ REQUIRE(p != NULL);
+ *p = str - save;
+ } else if (l) {
+ long int *p;
+ p = va_arg(ap, long *);
+ REQUIRE(p != NULL);
+ *p = str - save;
+ } else {
+ int *p;
+ p = va_arg(ap, int *);
+ REQUIRE(p != NULL);
+ *p = str - save;
+ }
+ break;
+ case 'i':
+ case 'd':
+ if (q)
+ tmpi = va_arg(ap, isc_int64_t);
+ else if (l)
+ tmpi = va_arg(ap, long int);
+ else
+ tmpi = va_arg(ap, int);
+ if (tmpi < 0) {
+ head = "-";
+ tmpui = -tmpi;
+ } else {
+ if (plus)
+ head = "+";
+ else if (space)
+ head = " ";
+ else
+ head = "";
+ tmpui = tmpi;
+ }
+ sprintf(buf, "%u", tmpui);
+ goto printint;
+ case 'o':
+ if (q)
+ tmpui = va_arg(ap, isc_uint64_t);
+ else if (l)
+ tmpui = va_arg(ap, long int);
+ else
+ tmpui = va_arg(ap, int);
+ sprintf(buf, alt ? "%#o"
+ : "%o", tmpui);
+ goto printint;
+ case 'u':
+ if (q)
+ tmpui = va_arg(ap, isc_uint64_t);
+ else if (l)
+ tmpui = va_arg(ap, unsigned long int);
+ else
+ tmpui = va_arg(ap, unsigned int);
+ sprintf(buf, "%u", tmpui);
+ goto printint;
+ case 'x':
+ if (q)
+ tmpui = va_arg(ap, isc_uint64_t);
+ else if (l)
+ tmpui = va_arg(ap, unsigned long int);
+ else
+ tmpui = va_arg(ap, unsigned int);
+ if (alt) {
+ head = "0x";
+ if (precision > 2)
+ precision -= 2;
+ }
+ sprintf(buf, "%x", tmpui);
+ goto printint;
+ case 'X':
+ if (q)
+ tmpui = va_arg(ap, isc_uint64_t);
+ else if (l)
+ tmpui = va_arg(ap, unsigned long int);
+ else
+ tmpui = va_arg(ap, unsigned int);
+ if (alt) {
+ head = "0X";
+ if (precision > 2)
+ precision -= 2;
+ }
+ sprintf(buf, "%X", tmpui);
+ goto printint;
+ printint:
+ if (precision != 0 || width != 0) {
+ length = strlen(buf);
+ if (length < precision)
+ zeropad = precision - length;
+ else if (length < width && zero)
+ zeropad = width - length;
+ if (width != 0) {
+ pad = width - length -
+ zeropad - strlen(head);
+ if (pad < 0)
+ pad = 0;
+ }
+ }
+ count += strlen(head) + strlen(buf) + pad +
+ zeropad;
+ if (!left) {
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ }
+ cp = head;
+ while (*cp != '\0' && size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (zeropad > 0 && size > 1) {
+ *str++ = '0';
+ size--;
+ zeropad--;
+ }
+ cp = buf;
+ while (*cp != '\0' && size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case 's':
+ cp = va_arg(ap, char *);
+ REQUIRE(cp != NULL);
+
+ if (precision != 0) {
+ /*
+ * cp need not be NULL terminated.
+ */
+ const char *tp;
+ unsigned long n;
+
+ n = precision;
+ tp = cp;
+ while (n != 0 && *tp != '\0')
+ n--, tp++;
+ length = precision - n;
+ } else {
+ length = strlen(cp);
+ }
+ if (width != 0) {
+ pad = width - length;
+ if (pad < 0)
+ pad = 0;
+ }
+ count += pad + length;
+ if (!left)
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ if (precision != 0)
+ while (precision > 0 && *cp != '\0' &&
+ size > 1) {
+ *str++ = *cp++;
+ size--;
+ precision--;
+ }
+ else
+ while (*cp != '\0' && size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ break;
+ case 'c':
+ c = va_arg(ap, int);
+ if (width > 0) {
+ count += width;
+ width--;
+ if (left) {
+ *str++ = c;
+ size--;
+ }
+ while (width-- > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ }
+ if (!left && size > 1) {
+ *str++ = c;
+ size--;
+ }
+ } else {
+ count++;
+ if (size > 1) {
+ *str++ = c;
+ size--;
+ }
+ }
+ break;
+ case 'p':
+ v = va_arg(ap, void *);
+ sprintf(buf, "%p", v);
+ length = strlen(buf);
+ if (precision > length)
+ zeropad = precision - length;
+ if (width > 0) {
+ pad = width - length - zeropad;
+ if (pad < 0)
+ pad = 0;
+ }
+ count += length + pad + zeropad;
+ if (!left)
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ cp = buf;
+ if (zeropad > 0 && buf[0] == '0' &&
+ (buf[1] == 'x' || buf[1] == 'X')) {
+ if (size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ if (size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (zeropad > 0 && size > 1) {
+ *str++ = '0';
+ size--;
+ zeropad--;
+ }
+ }
+ while (*cp != '\0' && size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ break;
+ case 'D': /*deprecated*/
+ INSIST("use %ld instead of %D" == NULL);
+ case 'O': /*deprecated*/
+ INSIST("use %lo instead of %O" == NULL);
+ case 'U': /*deprecated*/
+ INSIST("use %lu instead of %U" == NULL);
+
+ case 'L':
+#ifdef HAVE_LONG_DOUBLE
+ l = 1;
+#else
+ INSIST("long doubles are not supported" == NULL);
+#endif
+ /*FALLTHROUGH*/
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ if (!dot)
+ precision = 6;
+ /*
+ * IEEE floating point.
+ * MIN 2.2250738585072014E-308
+ * MAX 1.7976931348623157E+308
+ * VAX floating point has a smaller range than IEEE.
+ *
+ * precisions > 324 don't make much sense.
+ * if we cap the precision at 512 we will not
+ * overflow buf.
+ */
+ if (precision > 512)
+ precision = 512;
+ sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "",
+ plus ? "+" : space ? " " : "",
+ precision, l ? "L" : "", *format);
+ switch (*format) {
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+#ifdef HAVE_LONG_DOUBLE
+ if (l) {
+ ldbl = va_arg(ap, long double);
+ sprintf(buf, fmt, ldbl);
+ } else
+#endif
+ {
+ dbl = va_arg(ap, double);
+ sprintf(buf, fmt, dbl);
+ }
+ length = strlen(buf);
+ if (width > 0) {
+ pad = width - length;
+ if (pad < 0)
+ pad = 0;
+ }
+ count += length + pad;
+ if (!left)
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ cp = buf;
+ while (*cp != ' ' && size > 1) {
+ *str++ = *cp++;
+ size--;
+ }
+ while (pad > 0 && size > 1) {
+ *str++ = ' ';
+ size--;
+ pad--;
+ }
+ break;
+ default:
+ continue;
+ }
+ break;
+ default:
+ continue;
+ }
+ format++;
+ }
+ if (size > 0)
+ *str = '\0';
+ return (count);
+}
+
+#endif
diff --git a/omapip/listener.c b/omapip/listener.c
new file mode 100644
index 0000000..0c4dcb1
--- /dev/null
+++ b/omapip/listener.c
@@ -0,0 +1,478 @@
+/* listener.c
+
+ Subroutines that support the generic listener object. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <errno.h>
+
+#if defined (TRACING)
+omapi_array_t *trace_listeners;
+static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
+static void trace_listener_remember (omapi_listener_object_t *,
+ const char *, int);
+static void trace_listener_accept_stop (trace_type_t *);
+trace_type_t *trace_listener_accept;
+#endif
+
+OMAPI_OBJECT_ALLOC (omapi_listener,
+ omapi_listener_object_t, omapi_type_listener)
+
+isc_result_t omapi_listen (omapi_object_t *h,
+ unsigned port,
+ int max)
+{
+ omapi_addr_t addr;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_listen(port=%d, max=%d)", port, max);
+#endif
+
+ addr.addrtype = AF_INET;
+ addr.addrlen = sizeof (struct in_addr);
+ memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
+ addr.port = port;
+
+ return omapi_listen_addr (h, &addr, max);
+}
+
+isc_result_t omapi_listen_addr (omapi_object_t *h,
+ omapi_addr_t *addr,
+ int max)
+{
+ isc_result_t status;
+ omapi_listener_object_t *obj;
+ int i;
+
+ /* Currently only support IPv4 addresses. */
+ if (addr->addrtype != AF_INET)
+ return DHCP_R_INVALIDARG;
+
+ /* Get the handle. */
+ obj = (omapi_listener_object_t *)0;
+ status = omapi_listener_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ obj->socket = -1;
+
+ /* Connect this object to the inner object. */
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto error_exit;
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto error_exit;
+
+ /* Set up the address on which we will listen... */
+ obj -> address.sin_port = htons (addr -> port);
+ memcpy (&obj -> address.sin_addr,
+ addr -> address, sizeof obj -> address.sin_addr);
+#if defined (HAVE_SA_LEN)
+ obj -> address.sin_len =
+ sizeof (struct sockaddr_in);
+#endif
+ obj -> address.sin_family = AF_INET;
+ memset (&(obj -> address.sin_zero), 0,
+ sizeof obj -> address.sin_zero);
+
+#if defined (TRACING)
+ /* If we're playing back a trace file, we remember the object
+ on the trace listener queue. */
+ if (trace_playback ()) {
+ trace_listener_remember (obj, MDL);
+ } else {
+#endif
+ /* Create a socket on which to listen. */
+ obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (obj->socket == -1) {
+ if (errno == EMFILE
+ || errno == ENFILE || errno == ENOBUFS)
+ status = ISC_R_NORESOURCES;
+ else
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+#if defined (HAVE_SETFD)
+ if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+#endif
+
+ /* Set the REUSEADDR option so that we don't fail to start if
+ we're being restarted. */
+ i = 1;
+ if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&i, sizeof i) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ /* Try to bind to the wildcard address using the port number
+ we were given. */
+ i = bind (obj -> socket,
+ (struct sockaddr *)&obj -> address,
+ sizeof obj -> address);
+ if (i < 0) {
+ if (errno == EADDRINUSE)
+ status = ISC_R_ADDRNOTAVAIL;
+ else if (errno == EPERM)
+ status = ISC_R_NOPERM;
+ else
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ /* Now tell the kernel to listen for connections. */
+ if (listen (obj -> socket, max)) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ status = omapi_register_io_object ((omapi_object_t *)obj,
+ omapi_listener_readfd, 0,
+ omapi_accept, 0, 0);
+#if defined (TRACING)
+ }
+#endif
+
+ omapi_listener_dereference (&obj, MDL);
+ return status;
+
+error_exit:
+ if (obj != NULL) {
+ if (h->outer == (omapi_object_t *)obj) {
+ omapi_object_dereference((omapi_object_t **)&h->outer,
+ MDL);
+ }
+ if (obj->inner == h) {
+ omapi_object_dereference((omapi_object_t **)&obj->inner,
+ MDL);
+ }
+ if (obj->socket != -1) {
+ close(obj->socket);
+ }
+ omapi_listener_dereference(&obj, MDL);
+ }
+ return status;
+}
+
+/* Return the socket on which the dispatcher should wait for readiness
+ to read, for a listener object. */
+int omapi_listener_readfd (omapi_object_t *h)
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return -1;
+ l = (omapi_listener_object_t *)h;
+
+ return l -> socket;
+}
+
+/* Reader callback for a listener object. Accept an incoming connection. */
+isc_result_t omapi_accept (omapi_object_t *h)
+{
+ isc_result_t status;
+ socklen_t len;
+ omapi_connection_object_t *obj;
+ omapi_listener_object_t *listener;
+ struct sockaddr_in addr;
+ int socket;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ listener = (omapi_listener_object_t *)h;
+
+ /* Accept the connection. */
+ len = sizeof addr;
+ socket = accept (listener -> socket,
+ ((struct sockaddr *)&(addr)), &len);
+ if (socket < 0) {
+ if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
+ return ISC_R_NORESOURCES;
+ return ISC_R_UNEXPECTED;
+ }
+
+#if defined (TRACING)
+ /* If we're recording a trace, remember the connection. */
+ if (trace_record ()) {
+ trace_iov_t iov [3];
+ iov [0].buf = (char *)&addr.sin_port;
+ iov [0].len = sizeof addr.sin_port;
+ iov [1].buf = (char *)&addr.sin_addr;
+ iov [1].len = sizeof addr.sin_addr;
+ iov [2].buf = (char *)&listener -> address.sin_port;
+ iov [2].len = sizeof listener -> address.sin_port;
+ trace_write_packet_iov (trace_listener_accept,
+ 3, iov, MDL);
+ }
+#endif
+
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj, listener, socket, &addr);
+ if (status != ISC_R_SUCCESS) {
+ close (socket);
+ return status;
+ }
+
+ status = omapi_register_io_object ((omapi_object_t *)obj,
+ omapi_connection_readfd,
+ omapi_connection_writefd,
+ omapi_connection_reader,
+ omapi_connection_writer,
+ omapi_connection_reaper);
+
+ /* Lose our reference to the connection, so it'll be gc'd when it's
+ reaped. */
+ omapi_connection_dereference (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ omapi_disconnect ((omapi_object_t *)(obj), 1);
+ return status;
+}
+
+isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
+ omapi_listener_object_t *listener,
+ int socket,
+ struct sockaddr_in *remote_addr)
+{
+ isc_result_t status;
+ omapi_object_t *h = (omapi_object_t *)listener;
+ omapi_addr_t addr;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_accept()");
+#endif
+
+ /* Get the handle. */
+ status = omapi_connection_allocate (obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ (*obj) -> state = omapi_connection_connected;
+ (*obj) -> remote_addr = *remote_addr;
+ (*obj) -> socket = socket;
+
+ /* Verify that this host is allowed to connect. */
+ if (listener -> verify_addr) {
+ addr.addrtype = AF_INET;
+ addr.addrlen = sizeof (remote_addr -> sin_addr);
+ memcpy (addr.address, &remote_addr -> sin_addr,
+ sizeof (remote_addr -> sin_addr));
+ addr.port = ntohs(remote_addr -> sin_port);
+
+ status = (listener -> verify_addr) (h, &addr);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect ((omapi_object_t *)(*obj), 1);
+ omapi_connection_dereference (obj, MDL);
+ return status;
+ }
+ }
+
+ omapi_listener_reference (&(*obj) -> listener, listener, MDL);
+#if defined (TRACING)
+ omapi_connection_register (*obj, MDL);
+#endif
+ status = omapi_signal (h, "connect", (*obj));
+ return status;
+}
+
+#if defined (TRACING)
+OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
+
+void omapi_listener_trace_setup (void) {
+ trace_listener_accept =
+ trace_type_register ("listener-accept", (void *)0,
+ trace_listener_accept_input,
+ trace_listener_accept_stop, MDL);
+}
+
+static void trace_listener_remember (omapi_listener_object_t *obj,
+ const char *file, int line)
+{
+ isc_result_t status;
+ if (!trace_listeners) {
+ status = omapi_listener_array_allocate (&trace_listeners,
+ file, line);
+ if (status != ISC_R_SUCCESS) {
+ foo:
+ log_error ("trace_listener_remember: %s",
+ isc_result_totext (status));
+ return;
+ }
+ }
+ status = omapi_listener_array_extend (trace_listeners, obj,
+ &obj -> index, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto foo;
+}
+
+static void trace_listener_accept_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ struct in_addr *addr;
+ u_int16_t *remote_port;
+ u_int16_t *local_port;
+ omapi_connection_object_t *obj;
+ isc_result_t status;
+ struct sockaddr_in remote_addr;
+
+ addr = (struct in_addr *)buf;
+ remote_port = (u_int16_t *)(addr + 1);
+ local_port = remote_port + 1;
+
+ memset (&remote_addr, 0, sizeof remote_addr);
+ remote_addr.sin_addr = *addr;
+ remote_addr.sin_port = *remote_port;
+
+ omapi_array_foreach_begin (trace_listeners,
+ omapi_listener_object_t, lp) {
+ if (lp -> address.sin_port == *local_port) {
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj,
+ lp, 0, &remote_addr);
+ omapi_listener_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (trace_listeners,
+ omapi_listener_object_t, lp);
+ log_error ("trace_listener_accept: %s from %s/%d to port %d",
+ "unexpected connect",
+ inet_ntoa (*addr), *remote_port, *local_port);
+}
+
+static void trace_listener_accept_stop (trace_type_t *ttype) { }
+
+
+#endif
+
+isc_result_t omapi_listener_configure_security (omapi_object_t *h,
+ isc_result_t (*verify_addr)
+ (omapi_object_t *,
+ omapi_addr_t *))
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ l = (omapi_listener_object_t *)h;
+
+ l -> verify_addr = verify_addr;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_listener_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_listener_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_listener_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ l = (omapi_listener_object_t *)h;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_listener_destroy()");
+#endif
+
+ if (l -> socket != -1) {
+ close (l -> socket);
+ l -> socket = -1;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *l)
+{
+ if (l -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (l -> inner && l -> inner -> type -> stuff_values)
+ return (*(l -> inner -> type -> stuff_values)) (c, id,
+ l -> inner);
+ return ISC_R_SUCCESS;
+}
+
diff --git a/omapip/message.c b/omapip/message.c
new file mode 100644
index 0000000..3bd60ef
--- /dev/null
+++ b/omapip/message.c
@@ -0,0 +1,768 @@
+/* message.c
+
+ Subroutines for dealing with message objects. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+OMAPI_OBJECT_ALLOC (omapi_message,
+ omapi_message_object_t, omapi_type_message)
+
+omapi_message_object_t *omapi_registered_messages;
+
+isc_result_t omapi_message_new (omapi_object_t **o, const char *file, int line)
+{
+ omapi_message_object_t *m;
+ omapi_object_t *g;
+ isc_result_t status;
+
+ m = (omapi_message_object_t *)0;
+ status = omapi_message_allocate (&m, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ g = (omapi_object_t *)0;
+ status = omapi_generic_new (&g, file, line);
+ if (status != ISC_R_SUCCESS) {
+ dfree (m, file, line);
+ return status;
+ }
+ status = omapi_object_reference (&m -> inner, g, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference ((omapi_object_t **)&m, file, line);
+ omapi_object_dereference (&g, file, line);
+ return status;
+ }
+ status = omapi_object_reference (&g -> outer,
+ (omapi_object_t *)m, file, line);
+
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference ((omapi_object_t **)&m, file, line);
+ omapi_object_dereference (&g, file, line);
+ return status;
+ }
+
+ status = omapi_object_reference (o, (omapi_object_t *)m, file, line);
+ omapi_message_dereference (&m, file, line);
+ omapi_object_dereference (&g, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ return status;
+}
+
+isc_result_t omapi_message_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ omapi_message_object_t *m;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)h;
+
+ /* Can't set authlen. */
+
+ /* Can set authenticator, but the value must be typed data. */
+ if (!omapi_ds_strcmp (name, "authenticator")) {
+ if (m -> authenticator)
+ omapi_typed_data_dereference (&m -> authenticator,
+ MDL);
+ omapi_typed_data_reference (&m -> authenticator, value, MDL);
+ return ISC_R_SUCCESS;
+
+ } else if (!omapi_ds_strcmp (name, "object")) {
+ if (value -> type != omapi_datatype_object)
+ return DHCP_R_INVALIDARG;
+ if (m -> object)
+ omapi_object_dereference (&m -> object, MDL);
+ omapi_object_reference (&m -> object, value -> u.object, MDL);
+ return ISC_R_SUCCESS;
+
+ } else if (!omapi_ds_strcmp (name, "notify-object")) {
+ if (value -> type != omapi_datatype_object)
+ return DHCP_R_INVALIDARG;
+ if (m -> notify_object)
+ omapi_object_dereference (&m -> notify_object, MDL);
+ omapi_object_reference (&m -> notify_object,
+ value -> u.object, MDL);
+ return ISC_R_SUCCESS;
+
+ /* Can set authid, but it has to be an integer. */
+ } else if (!omapi_ds_strcmp (name, "authid")) {
+ if (value -> type != omapi_datatype_int)
+ return DHCP_R_INVALIDARG;
+ m -> authid = value -> u.integer;
+ return ISC_R_SUCCESS;
+
+ /* Can set op, but it has to be an integer. */
+ } else if (!omapi_ds_strcmp (name, "op")) {
+ if (value -> type != omapi_datatype_int)
+ return DHCP_R_INVALIDARG;
+ m -> op = value -> u.integer;
+ return ISC_R_SUCCESS;
+
+ /* Handle also has to be an integer. */
+ } else if (!omapi_ds_strcmp (name, "handle")) {
+ if (value -> type != omapi_datatype_int)
+ return DHCP_R_INVALIDARG;
+ m -> h = value -> u.integer;
+ return ISC_R_SUCCESS;
+
+ /* Transaction ID has to be an integer. */
+ } else if (!omapi_ds_strcmp (name, "id")) {
+ if (value -> type != omapi_datatype_int)
+ return DHCP_R_INVALIDARG;
+ m -> id = value -> u.integer;
+ return ISC_R_SUCCESS;
+
+ /* Remote transaction ID has to be an integer. */
+ } else if (!omapi_ds_strcmp (name, "rid")) {
+ if (value -> type != omapi_datatype_int)
+ return DHCP_R_INVALIDARG;
+ m -> rid = value -> u.integer;
+ return ISC_R_SUCCESS;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_message_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ omapi_message_object_t *m;
+ if (h -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)h;
+
+ /* Look for values that are in the message data structure. */
+ if (!omapi_ds_strcmp (name, "authlen"))
+ return omapi_make_int_value (value, name, (int)m -> authlen,
+ MDL);
+ else if (!omapi_ds_strcmp (name, "authenticator")) {
+ if (m -> authenticator)
+ return omapi_make_value (value, name,
+ m -> authenticator, MDL);
+ else
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "authid")) {
+ return omapi_make_int_value (value,
+ name, (int)m -> authid, MDL);
+ } else if (!omapi_ds_strcmp (name, "op")) {
+ return omapi_make_int_value (value, name, (int)m -> op, MDL);
+ } else if (!omapi_ds_strcmp (name, "handle")) {
+ return omapi_make_int_value (value, name, (int)m -> h, MDL);
+ } else if (!omapi_ds_strcmp (name, "id")) {
+ return omapi_make_int_value (value, name, (int)m -> id, MDL);
+ } else if (!omapi_ds_strcmp (name, "rid")) {
+ return omapi_make_int_value (value, name, (int)m -> rid, MDL);
+ }
+
+ /* See if there's an inner object that has the value. */
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_message_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_message_object_t *m;
+ if (h -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)h;
+ if (m -> authenticator) {
+ omapi_typed_data_dereference (&m -> authenticator, file, line);
+ }
+ if (!m -> prev && omapi_registered_messages != m)
+ omapi_message_unregister (h);
+ if (m -> id_object)
+ omapi_object_dereference (&m -> id_object, file, line);
+ if (m -> object)
+ omapi_object_dereference (&m -> object, file, line);
+ if (m -> notify_object)
+ omapi_object_dereference (&m -> notify_object, file, line);
+ if (m -> protocol_object)
+ omapi_protocol_dereference (&m -> protocol_object, file, line);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_message_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ omapi_message_object_t *m;
+ if (h -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)h;
+
+ if (!strcmp (name, "status")) {
+ if (m -> notify_object &&
+ m -> notify_object -> type -> signal_handler)
+ return ((m -> notify_object -> type -> signal_handler))
+ (m -> notify_object, name, ap);
+ else if (m -> object && m -> object -> type -> signal_handler)
+ return ((m -> object -> type -> signal_handler))
+ (m -> object, name, ap);
+ }
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_message_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *m)
+{
+ if (m -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+
+ if (m -> inner && m -> inner -> type -> stuff_values)
+ return (*(m -> inner -> type -> stuff_values)) (c, id,
+ m -> inner);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_message_register (omapi_object_t *mo)
+{
+ omapi_message_object_t *m;
+
+ if (mo -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)mo;
+
+ /* Already registered? */
+ if (m -> prev || m -> next || omapi_registered_messages == m)
+ return DHCP_R_INVALIDARG;
+
+ if (omapi_registered_messages) {
+ omapi_object_reference
+ ((omapi_object_t **)&m -> next,
+ (omapi_object_t *)omapi_registered_messages, MDL);
+ omapi_object_reference
+ ((omapi_object_t **)&omapi_registered_messages -> prev,
+ (omapi_object_t *)m, MDL);
+ omapi_object_dereference
+ ((omapi_object_t **)&omapi_registered_messages, MDL);
+ }
+ omapi_object_reference
+ ((omapi_object_t **)&omapi_registered_messages,
+ (omapi_object_t *)m, MDL);
+ return ISC_R_SUCCESS;;
+}
+
+isc_result_t omapi_message_unregister (omapi_object_t *mo)
+{
+ omapi_message_object_t *m;
+ omapi_message_object_t *n;
+
+ if (mo -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ m = (omapi_message_object_t *)mo;
+
+ /* Not registered? */
+ if (!m -> prev && omapi_registered_messages != m)
+ return DHCP_R_INVALIDARG;
+
+ n = (omapi_message_object_t *)0;
+ if (m -> next) {
+ omapi_object_reference ((omapi_object_t **)&n,
+ (omapi_object_t *)m -> next, MDL);
+ omapi_object_dereference ((omapi_object_t **)&m -> next, MDL);
+ omapi_object_dereference ((omapi_object_t **)&n -> prev, MDL);
+ }
+ if (m -> prev) {
+ omapi_message_object_t *tmp = (omapi_message_object_t *)0;
+ omapi_object_reference ((omapi_object_t **)&tmp,
+ (omapi_object_t *)m -> prev, MDL);
+ omapi_object_dereference ((omapi_object_t **)&m -> prev, MDL);
+ if (tmp -> next)
+ omapi_object_dereference
+ ((omapi_object_t **)&tmp -> next, MDL);
+ if (n)
+ omapi_object_reference
+ ((omapi_object_t **)&tmp -> next,
+ (omapi_object_t *)n, MDL);
+ omapi_object_dereference ((omapi_object_t **)&tmp, MDL);
+ } else {
+ omapi_object_dereference
+ ((omapi_object_t **)&omapi_registered_messages, MDL);
+ if (n)
+ omapi_object_reference
+ ((omapi_object_t **)&omapi_registered_messages,
+ (omapi_object_t *)n, MDL);
+ }
+ if (n)
+ omapi_object_dereference ((omapi_object_t **)&n, MDL);
+ return ISC_R_SUCCESS;
+}
+
+#ifdef DEBUG_PROTOCOL
+static const char *omapi_message_op_name(int op) {
+ switch (op) {
+ case OMAPI_OP_OPEN: return "OMAPI_OP_OPEN";
+ case OMAPI_OP_REFRESH: return "OMAPI_OP_REFRESH";
+ case OMAPI_OP_UPDATE: return "OMAPI_OP_UPDATE";
+ case OMAPI_OP_STATUS: return "OMAPI_OP_STATUS";
+ case OMAPI_OP_DELETE: return "OMAPI_OP_DELETE";
+ case OMAPI_OP_NOTIFY: return "OMAPI_OP_NOTIFY";
+ default: return "(unknown op)";
+ }
+}
+#endif
+
+static isc_result_t
+omapi_message_process_internal (omapi_object_t *, omapi_object_t *);
+
+isc_result_t omapi_message_process (omapi_object_t *mo, omapi_object_t *po)
+{
+ isc_result_t status;
+#if defined (DEBUG_MEMORY_LEAKAGE) && 0
+ unsigned long previous_outstanding = dmalloc_outstanding;
+#endif
+
+ status = omapi_message_process_internal (mo, po);
+
+#if defined (DEBUG_MEMORY_LEAKAGE) && 0
+ log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
+ dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm);
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE) && 0
+ dmalloc_dump_outstanding ();
+#endif
+#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY) && 0
+ dump_rc_history ();
+#endif
+
+ return status;
+}
+
+static isc_result_t
+omapi_message_process_internal (omapi_object_t *mo, omapi_object_t *po)
+{
+ omapi_message_object_t *message, *m;
+ omapi_object_t *object = (omapi_object_t *)0;
+ omapi_value_t *tv = (omapi_value_t *)0;
+ unsigned long create, update, exclusive;
+ unsigned long wsi;
+ isc_result_t status, waitstatus;
+ omapi_object_type_t *type;
+
+ if (mo -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ message = (omapi_message_object_t *)mo;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_message_process(): "
+ "op=%s handle=%#x id=%#x rid=%#x",
+ omapi_message_op_name (message -> op),
+ message -> h, message -> id, message -> rid);
+#endif
+
+ if (message -> rid) {
+ for (m = omapi_registered_messages; m; m = m -> next)
+ if (m -> id == message -> rid)
+ break;
+ /* If we don't have a real message corresponding to
+ the message ID to which this message claims it is a
+ response, something's fishy. */
+ if (!m)
+ return ISC_R_NOTFOUND;
+ /* The authenticator on responses must match the initial
+ message. */
+ if (message -> authid != m -> authid)
+ return ISC_R_NOTFOUND;
+ } else {
+ m = (omapi_message_object_t *)0;
+
+ /* All messages must have an authenticator, with the exception
+ of messages that are opening a new authenticator. */
+ if (omapi_protocol_authenticated(po) &&
+ !message->id_object &&
+ message->op != OMAPI_OP_OPEN) {
+ return omapi_protocol_send_status
+ (po, message->id_object, DHCP_R_NOKEYS,
+ message->id, "No authenticator on message");
+ }
+ }
+
+ switch (message -> op) {
+ case OMAPI_OP_OPEN:
+ if (m) {
+ return omapi_protocol_send_status
+ (po, message->id_object, DHCP_R_INVALIDARG,
+ message->id, "OPEN can't be a response");
+ }
+
+ /* Get the type of the requested object, if one was
+ specified. */
+ status = omapi_get_value_str (mo, message -> id_object,
+ "type", &tv);
+ if (status == ISC_R_SUCCESS &&
+ (tv -> value -> type == omapi_datatype_data ||
+ tv -> value -> type == omapi_datatype_string)) {
+ for (type = omapi_object_types;
+ type; type = type -> next)
+ if (!omapi_td_strcmp (tv -> value,
+ type -> name))
+ break;
+ } else
+ type = (omapi_object_type_t *)0;
+ if (tv)
+ omapi_value_dereference (&tv, MDL);
+
+ /* If this object had no authenticator, the requested object
+ must be an authenticator object. */
+ if (omapi_protocol_authenticated(po) &&
+ !message->id_object &&
+ type != omapi_type_auth_key) {
+ return omapi_protocol_send_status
+ (po, message->id_object, DHCP_R_NOKEYS,
+ message->id, "No authenticator on message");
+ }
+
+ /* Get the create flag. */
+ status = omapi_get_value_str (mo, message -> id_object,
+ "create", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_get_int_value (&create, tv -> value);
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "invalid create flag value");
+ }
+ } else
+ create = 0;
+
+ /* Get the update flag. */
+ status = omapi_get_value_str (mo, message -> id_object,
+ "update", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_get_int_value (&update, tv -> value);
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "invalid update flag value");
+ }
+ } else
+ update = 0;
+
+ /* Get the exclusive flag. */
+ status = omapi_get_value_str (mo, message -> id_object,
+ "exclusive", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_get_int_value (&exclusive, tv -> value);
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "invalid exclusive flag value");
+ }
+ } else
+ exclusive = 0;
+
+ /* If we weren't given a type, look the object up with
+ the handle. */
+ if (!type) {
+ if (create) {
+ return omapi_protocol_send_status
+ (po, message->id_object,
+ DHCP_R_INVALIDARG,
+ message->id,
+ "type required on create");
+ }
+ goto refresh;
+ }
+
+ /* If the type doesn't provide a lookup method, we can't
+ look up the object. */
+ if (!type -> lookup) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ ISC_R_NOTIMPLEMENTED, message -> id,
+ "unsearchable object type");
+ }
+
+ status = (*(type -> lookup)) (&object, message -> id_object,
+ message -> object);
+
+ if (status != ISC_R_SUCCESS &&
+ status != ISC_R_NOTFOUND &&
+ status != DHCP_R_NOKEYS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "object lookup failed");
+ }
+
+ /* If we didn't find the object and we aren't supposed to
+ create it, return an error. */
+ if (status == ISC_R_NOTFOUND && !create) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ ISC_R_NOTFOUND, message -> id,
+ "no object matches specification");
+ }
+
+ /* If we found an object, we're supposed to be creating an
+ object, and we're not supposed to have found an object,
+ return an error. */
+ if (status == ISC_R_SUCCESS && create && exclusive) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ ISC_R_EXISTS, message -> id,
+ "specified object already exists");
+ }
+
+ /* If we're creating the object, do it now. */
+ if (!object) {
+ status = omapi_object_create (&object,
+ message -> id_object,
+ type);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't create new object");
+ }
+ }
+
+ /* If we're updating it, do so now. */
+ if (create || update) {
+ /* This check does not belong here. */
+ if (object -> type == omapi_type_auth_key) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't update object");
+ }
+
+ status = omapi_object_update (object,
+ message -> id_object,
+ message -> object,
+ message -> h);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't update object");
+ }
+ }
+
+ /* If this is an authenticator object, add it to the active
+ set for the connection. */
+ if (object -> type == omapi_type_auth_key) {
+ omapi_handle_t handle;
+ status = omapi_object_handle (&handle, object);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't select authenticator");
+ }
+
+ status = omapi_protocol_add_auth (po, object, handle);
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't select authenticator");
+ }
+ }
+
+ /* Now send the new contents of the object back in
+ response. */
+ goto send;
+
+ case OMAPI_OP_REFRESH:
+ refresh:
+ status = omapi_handle_lookup (&object, message -> h);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "no matching handle");
+ }
+ send:
+ status = omapi_protocol_send_update (po, message -> id_object,
+ message -> id, object);
+ omapi_object_dereference (&object, MDL);
+ return status;
+
+ case OMAPI_OP_UPDATE:
+ if (m && m -> object) {
+ status = omapi_object_reference (&object, m -> object,
+ MDL);
+ } else {
+ status = omapi_handle_lookup (&object, message -> h);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "no matching handle");
+ }
+ }
+
+ if (object -> type == omapi_type_auth_key ||
+ (object -> inner &&
+ object -> inner -> type == omapi_type_auth_key)) {
+ if (!m) {
+ omapi_object_dereference (&object, MDL);
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "cannot update authenticator");
+ }
+
+ status = omapi_protocol_add_auth (po, object,
+ message -> h);
+ } else {
+ status = omapi_object_update (object,
+ message -> id_object,
+ message -> object,
+ message -> h);
+ }
+ if (status != ISC_R_SUCCESS) {
+ omapi_object_dereference (&object, MDL);
+ if (!message -> rid)
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "can't update object");
+ if (m)
+ omapi_signal ((omapi_object_t *)m,
+ "status", status,
+ (omapi_typed_data_t *)0);
+ return ISC_R_SUCCESS;
+ }
+ if (!message -> rid)
+ status = omapi_protocol_send_status
+ (po, message -> id_object, ISC_R_SUCCESS,
+ message -> id, (char *)0);
+ if (m) {
+ omapi_signal ((omapi_object_t *)m,
+ "status", ISC_R_SUCCESS,
+ (omapi_typed_data_t *)0);
+ omapi_message_unregister ((omapi_object_t *)m);
+ }
+
+ omapi_object_dereference (&object, MDL);
+
+ return status;
+
+ case OMAPI_OP_NOTIFY:
+ return omapi_protocol_send_status
+ (po, message -> id_object, ISC_R_NOTIMPLEMENTED,
+ message -> id, "notify not implemented yet");
+
+ case OMAPI_OP_STATUS:
+ /* The return status of a request. */
+ if (!m)
+ return ISC_R_UNEXPECTED;
+
+ /* Get the wait status. */
+ status = omapi_get_value_str (mo, message -> id_object,
+ "result", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_get_int_value (&wsi, tv -> value);
+ waitstatus = wsi;
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ waitstatus = ISC_R_UNEXPECTED;
+ } else
+ waitstatus = ISC_R_UNEXPECTED;
+
+ status = omapi_get_value_str (mo, message -> id_object,
+ "message", &tv);
+ omapi_signal ((omapi_object_t *)m, "status", waitstatus, tv);
+ if (status == ISC_R_SUCCESS)
+ omapi_value_dereference (&tv, MDL);
+
+ omapi_message_unregister((omapi_object_t *)m);
+
+ return ISC_R_SUCCESS;
+
+ case OMAPI_OP_DELETE:
+ status = omapi_handle_lookup (&object, message -> h);
+ if (status != ISC_R_SUCCESS) {
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ status, message -> id,
+ "no matching handle");
+ }
+
+ if (!object -> type -> remove)
+ return omapi_protocol_send_status
+ (po, message -> id_object,
+ ISC_R_NOTIMPLEMENTED, message -> id,
+ "no remove method for object");
+
+ status = (*(object -> type -> remove)) (object,
+ message -> id_object);
+ omapi_object_dereference (&object, MDL);
+
+ return omapi_protocol_send_status (po, message -> id_object,
+ status, message -> id,
+ (char *)0);
+ }
+ return ISC_R_NOTIMPLEMENTED;
+}
diff --git a/omapip/omapi.3 b/omapip/omapi.3
new file mode 100644
index 0000000..4868d7c
--- /dev/null
+++ b/omapip/omapi.3
@@ -0,0 +1,247 @@
+.\" omapi.3
+.\"
+.\" Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2000-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.TH omapi 3
+.SH NAME
+OMAPI - Object Management Application Programming Interface
+.SH DESCRIPTION
+.PP
+OMAPI is an programming layer designed for controlling remote
+applications, and for querying them for their state. It is currently
+used by the ISC DHCP server and this outline addresses the parts of
+OMAPI appropriate to the clients of DHCP server. It does this by also
+describing the use of a thin API layered on top of OMAPI called
+\'dhcpctl\'
+.PP
+OMAPI uses TCP/IP as the transport for server communication, and
+security can be imposed by having the client and server
+cryptographically sign messages using a shared secret.
+.PP
+dhcpctl works by presenting the client with handles to objects that
+act as surrogates for the real objects in the server. For example a
+client will create a handle for a lease object, and will request the
+server to fill the lease handle's state. The client application can
+then pull details such as the lease expiration time from the lease
+handle.
+.PP
+Modifications can be made to the server state by creating handles to
+new objects, or by modifying attributes of handles to existing
+objects, and then instructing the server to update itself according to
+the changes made.
+.SH USAGE
+.PP
+The client application must always call dhcpctl_initialize() before
+making calls to any other dhcpctl functions. This initializes
+various internal data structures.
+.PP
+To create the connection to the server the client must use
+dhcpctl_connect() function. As well as making the physical connection
+it will also set up the connection data structures to do
+authentication on each message, if that is required.
+.PP
+All the dhcpctl functions return an integer value of type
+isc_result_t. A successful call will yield a result of
+ISC_R_SUCCESS. If the call fails for a reason local to the client
+(e.g. insufficient local memory, or invalid arguments to the call)
+then the return value of the dhcpctl function will show that. If the
+call succeeds but the server couldn't process the request the error
+value from the server is returned through another way, shown below.
+.PP
+The easiest way to understand dhcpctl is to see it in action. The
+following program is fully functional, but almost all error checking
+has been removed to make is shorter and easier to understand. This
+program will query the server running on the localhost for the details
+of the lease for IP address 10.0.0.101. It will then print out the time
+the lease ends.
+.PP
+.nf
+ #include <stdarg.h>
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <stdio.h>
+ #include <netinet/in.h>
+
+ #include <isc/result.h>
+ #include <dhcpctl/dhcpctl.h>
+
+ int main (int argc, char **argv) {
+ dhcpctl_data_string ipaddrstring = NULL;
+ dhcpctl_data_string value = NULL;
+.fi
+.PP
+All modifications of handles and all accesses of handle data happen
+via dhcpctl_data_string objects.
+.PP
+.nf
+ dhcpctl_handle connection = NULL;
+ dhcpctl_handle lease = NULL;
+ isc_result_t waitstatus;
+ struct in_addr convaddr;
+ time_t thetime;
+
+ dhcpctl_initialize ();
+.fi
+.PP
+Required first step.
+.PP
+.nf
+ dhcpctl_connect (&connection, "127.0.0.1",
+ 7911, 0);
+.fi
+.PP
+Sets up the connection to the server. The server normally listens on
+port 7911 unless configured to do otherwise.
+.PP
+.nf
+ dhcpctl_new_object (&lease, connection,
+ "lease");
+.fi
+.PP
+Here we create a handle to a lease. This call just sets up local data
+structure. The server hasn't yet made any association between the
+client's data structure and any lease it has.
+.PP
+.nf
+ memset (&ipaddrstring, 0, sizeof
+ ipaddrstring);
+
+ inet_pton(AF_INET, "10.0.0.101",
+ &convaddr);
+
+ omapi_data_string_new (&ipaddrstring,
+ 4, MDL);
+.fi
+.PP
+Create a new data string to storing in the handle.
+.PP
+.nf
+ memcpy(ipaddrstring->value, &convaddr.s_addr, 4);
+
+ dhcpctl_set_value (lease, ipaddrstring,
+ "ip-address");
+.fi
+.PP
+We're setting the ip-address attribute of the lease handle to the
+given address. We've not set any other attributes so when the server
+makes the association the ip address will be all it uses to look up
+the lease in its tables.
+.PP
+.nf
+ dhcpctl_open_object (lease, connection, 0);
+.fi
+.PP
+Here we prime the connection with the request to look up the lease in
+the server and fill up the local handle with the attributes the server
+will send over in its answer.
+.PP
+.nf
+ dhcpctl_wait_for_completion (lease,
+ &waitstatus);
+.fi
+.PP
+This call causes the message to get sent to the server (the message to
+look up the lease and send back the attribute values in the
+answer). The value in the variable waitstatus when the function
+returns will be the result from the server. If the message could
+not be processed properly by the server then the error will be
+reflected here.
+.PP
+.nf
+ if (waitstatus != ISC_R_SUCCESS) {
+ /* server not authoritative */
+ exit (0);
+ }
+
+ dhcpctl_data_string_dereference(&ipaddrstring,
+ MDL);
+.fi
+.PP
+Clean-up memory we no longer need.
+.PP
+.nf
+ dhcpctl_get_value (&value, lease, "ends");
+.fi
+.PP
+Get the attribute named ``ends'' from the lease handle. This is a
+4-byte integer of the time (in unix epoch seconds) that the lease
+will expire.
+.PP
+.nf
+
+ memcpy(&thetime, value->value, value->len);
+ dhcpctl_data_string_dereference(&value, MDL);
+
+ fprintf (stdout, "ending time is %s",
+ ctime(&thetime));
+ }
+
+.fi
+.SH AUTHENTICATION
+If the server demands authenticated connections then before opening
+the connection the user must call dhcpctl_new_authenticator.
+.PP
+.nf
+ dhcpctl_handle authenticator = NULL;
+ const char *keyname = "a-key-name";
+ const char *algorithm = "hmac-md5";
+ const char *secret = "a-shared-secret";
+
+ dhcpctl_new_authenticator (&authenticator,
+ keyname,
+ algorithm,
+ secret,
+ strlen(secret) + 1);
+.fi
+.PP
+The keyname, algorithm and must all match what is specified in the server's
+dhcpd.conf file, excepting that the secret should appear in \'raw\' form, not
+in base64 as it would in dhcpd.conf:
+.PP
+.nf
+ key "a-key-name" {
+ algorithm hmac-md5;
+ secret "a-shared-secret";
+ };
+
+ # Set the omapi-key value to use
+ # authenticated connections
+ omapi-key a-key-name;
+.fi
+.PP
+The authenticator handle that is created by the call to
+dhcpctl_new_authenticator must be given as the last (the 4th) argument
+to the call to dhcpctl_connect(). All messages will then be signed
+with the given secret string using the specified algorithm.
+.SH SEE ALSO
+dhcpctl(3), omshell(1), dhcpd(8), dhclient(8), dhcpd.conf(5), dhclient.conf(5).
+.SH AUTHOR
+.B omapi
+was created by Ted Lemon of Nominum, Inc. This documentation was
+written by James Brister of Nominum, Inc.
diff --git a/omapip/protocol.c b/omapip/protocol.c
new file mode 100644
index 0000000..1a6d7e8
--- /dev/null
+++ b/omapip/protocol.c
@@ -0,0 +1,1314 @@
+/* protocol.c
+
+ Functions supporting the object management protocol... */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+OMAPI_OBJECT_ALLOC (omapi_protocol, omapi_protocol_object_t,
+ omapi_type_protocol)
+OMAPI_OBJECT_ALLOC (omapi_protocol_listener, omapi_protocol_listener_object_t,
+ omapi_type_protocol_listener)
+
+isc_result_t omapi_protocol_connect (omapi_object_t *h,
+ const char *server_name,
+ unsigned port,
+ omapi_object_t *a)
+{
+ isc_result_t rstatus, status;
+ omapi_protocol_object_t *obj;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_protocol_connect(%s port=%d)", server_name, port);
+#endif
+
+ obj = (omapi_protocol_object_t *)0;
+ status = omapi_protocol_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ rstatus = omapi_connect ((omapi_object_t *)obj, server_name, port);
+ if (rstatus != ISC_R_SUCCESS && rstatus != DHCP_R_INCOMPLETE) {
+ omapi_protocol_dereference (&obj, MDL);
+ return rstatus;
+ }
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_protocol_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_protocol_dereference (&obj, MDL);
+ return status;
+ }
+
+ /* If we were passed a default authenticator, store it now. We'll
+ open it once we're connected. */
+ if (a) {
+ obj -> default_auth =
+ dmalloc (sizeof(omapi_remote_auth_t), MDL);
+ if (!obj -> default_auth) {
+ omapi_protocol_dereference (&obj, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ obj -> default_auth -> next = (omapi_remote_auth_t *)0;
+ status = omapi_object_reference (&obj -> default_auth -> a,
+ a, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dfree (obj -> default_auth, MDL);
+ omapi_protocol_dereference (&obj, MDL);
+ return status;
+ }
+
+ obj -> insecure = 0;
+ rstatus = DHCP_R_INCOMPLETE;
+ } else {
+ obj -> insecure = 1;
+#if 0
+ status = ISC_R_SUCCESS;
+#endif
+ }
+
+ omapi_protocol_dereference (&obj, MDL);
+ return rstatus;
+}
+
+/* Send the protocol introduction message. */
+isc_result_t omapi_protocol_send_intro (omapi_object_t *h,
+ unsigned ver,
+ unsigned hsize)
+{
+ isc_result_t status;
+ omapi_protocol_object_t *p;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_protocol_send_intro()");
+#endif
+
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)h;
+
+ if (!h -> outer || h -> outer -> type != omapi_type_connection)
+ return ISC_R_NOTCONNECTED;
+
+ status = omapi_connection_put_uint32 (h -> outer, ver);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_uint32 (h -> outer, hsize);
+
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Require the other end to send an intro - this kicks off the
+ protocol input state machine. */
+ p -> state = omapi_protocol_intro_wait;
+ status = omapi_connection_require (h -> outer, 8);
+ if (status != ISC_R_SUCCESS && status != DHCP_R_NOTYET)
+ return status;
+
+ /* Make up an initial transaction ID for this connection. */
+ p -> next_xid = random ();
+ return ISC_R_SUCCESS;
+}
+
+#ifdef DEBUG_PROTOCOL
+extern const char *omapi_message_op_name(int);
+#endif /* DEBUG_PROTOCOL */
+
+isc_result_t omapi_protocol_send_message (omapi_object_t *po,
+ omapi_object_t *id,
+ omapi_object_t *mo,
+ omapi_object_t *omo)
+{
+ omapi_protocol_object_t *p;
+ omapi_object_t *c;
+ omapi_message_object_t *m, *om;
+ omapi_remote_auth_t *ra;
+ omapi_value_t *signature;
+ isc_result_t status;
+ unsigned auth_len;
+
+ if (po -> type != omapi_type_protocol ||
+ !po -> outer || po -> outer -> type != omapi_type_connection ||
+ mo -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ if (omo && omo -> type != omapi_type_message)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)po;
+ c = (omapi_object_t *)(po -> outer);
+ m = (omapi_message_object_t *)mo;
+ om = (omapi_message_object_t *)omo;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_protocol_send_message(): "
+ "op=%s handle=%#lx id=%#lx rid=%#lx",
+ omapi_message_op_name (m->op),
+ (long)(m -> object ? m -> object -> handle : m -> handle),
+ (long)p -> next_xid, (long)m -> rid);
+#endif
+
+ /* Find the authid to use for this message. */
+ if (id) {
+ for (ra = p -> remote_auth_list; ra; ra = ra -> next) {
+ if (ra -> a == id) {
+ break;
+ }
+ }
+
+ if (!ra)
+ return DHCP_R_KEY_UNKNOWN;
+ } else if (p -> remote_auth_list) {
+ ra = p -> default_auth;
+ } else {
+ ra = (omapi_remote_auth_t *)0;
+ }
+
+ if (ra) {
+ m -> authid = ra -> remote_handle;
+ status = omapi_object_reference (&m -> id_object,
+ ra -> a, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* Write the ID of the authentication key we're using. */
+ status = omapi_connection_put_uint32 (c, ra ? ra -> remote_handle : 0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Activate the authentication key on the connection. */
+ auth_len = 0;
+ if (ra) {
+ status = omapi_set_object_value (c, (omapi_object_t *)0,
+ "output-authenticator",
+ ra -> a);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ status = omapi_connection_output_auth_length (c, &auth_len);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+
+ /* Write the authenticator length */
+ status = omapi_connection_put_uint32 (c, auth_len);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Write the opcode. */
+ status = omapi_connection_put_uint32 (c, m -> op);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Write the handle. If we've been given an explicit handle, use
+ that. Otherwise, use the handle of the object we're sending.
+ The caller is responsible for arranging for one of these handles
+ to be set (or not). */
+ status = omapi_connection_put_uint32 (c, (m -> h
+ ? m -> h
+ : (m -> object
+ ? m -> object -> handle
+ : 0)));
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Set and write the transaction ID. */
+ m -> id = p -> next_xid++;
+ status = omapi_connection_put_uint32 (c, m -> id);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Write the transaction ID of the message to which this is a
+ response, if there is such a message. */
+ status = omapi_connection_put_uint32 (c, om ? om -> id : m -> rid);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Stuff out the name/value pairs specific to this message. */
+ status = omapi_stuff_values (c, id, (omapi_object_t *)m);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Write the zero-length name that terminates the list of name/value
+ pairs specific to the message. */
+ status = omapi_connection_put_uint16 (c, 0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Stuff out all the published name/value pairs in the object that's
+ being sent in the message, if there is one. */
+ if (m -> object) {
+ status = omapi_stuff_values (c, id, m -> object);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+
+ /* Write the zero-length name that terminates the list of name/value
+ pairs for the associated object. */
+ status = omapi_connection_put_uint16 (c, 0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ if (ra) {
+ /* Calculate the message signature. */
+ signature = (omapi_value_t *)0;
+ status = omapi_get_value_str (c, (omapi_object_t *)0,
+ "output-signature", &signature);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Write the authenticator... */
+ status = (omapi_connection_copyin
+ (c, signature -> value -> u.buffer.value,
+ signature -> value -> u.buffer.len));
+ omapi_value_dereference (&signature, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Dectivate the authentication key on the connection. */
+ status = omapi_set_value_str (c, (omapi_object_t *)0,
+ "output-authenticator",
+ (omapi_typed_data_t *)0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+
+ if (!omo) {
+ omapi_protocol_reference (&m -> protocol_object, p, MDL);
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+isc_result_t omapi_protocol_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ isc_result_t status;
+ omapi_protocol_object_t *p;
+ omapi_object_t *c;
+ omapi_message_object_t *m;
+ omapi_value_t *signature;
+ u_int16_t nlen;
+ u_int32_t vlen;
+ u_int32_t th;
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ unsigned long previous_outstanding = 0xDEADBEEF;
+ unsigned long connect_outstanding = 0xDEADBEEF;
+#endif
+
+ if (h -> type != omapi_type_protocol) {
+ /* XXX shouldn't happen. Put an assert here? */
+ return ISC_R_UNEXPECTED;
+ }
+ p = (omapi_protocol_object_t *)h;
+
+ if (!strcmp (name, "connect")) {
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ connect_outstanding = dmalloc_outstanding;
+#endif
+ /* Send the introductory message. */
+ status = omapi_protocol_send_intro
+ (h, OMAPI_PROTOCOL_VERSION,
+ sizeof (omapi_protocol_header_t));
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (p -> outer, 1);
+ return status;
+ }
+ return ISC_R_SUCCESS;
+ }
+
+ /* Should only receive these when opening the initial authenticator. */
+ if (!strcmp (name, "status")) {
+ status = va_arg (ap, isc_result_t);
+ if (status != ISC_R_SUCCESS) {
+ omapi_signal_in (h -> inner, "status", status,
+ (omapi_object_t *)0);
+ omapi_disconnect (p -> outer, 1);
+ return status;
+ } else {
+ return omapi_signal_in (h -> inner, "ready");
+ }
+ }
+
+ /* If we get a disconnect, dump memory usage. */
+ if (!strcmp (name, "disconnect")) {
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ if (connect_outstanding != 0xDEADBEEF) {
+ log_info ("generation %ld: %ld new, %ld outstanding, %ld%s",
+ dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm, " long-term");
+ }
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ dmalloc_dump_outstanding ();
+#endif
+#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
+ dump_rc_history (h);
+#endif
+ for (m = omapi_registered_messages; m; m = m -> next) {
+ if (m -> protocol_object == p) {
+ if (m -> object)
+ omapi_signal (m -> object, "disconnect");
+ }
+ }
+
+ /* XXX */
+ return ISC_R_SUCCESS;
+ }
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "ready")) {
+ if (p -> inner && p -> inner -> type -> signal_handler)
+ return (*(p -> inner -> type -> signal_handler)) (h,
+ name,
+ ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ if (!p -> outer || p -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = p -> outer;
+
+ /* We get here because we requested that we be woken up after
+ some number of bytes were read, and that number of bytes
+ has in fact been read. */
+ switch (p -> state) {
+ case omapi_protocol_intro_wait:
+ /* Get protocol version and header size in network
+ byte order. */
+ omapi_connection_get_uint32 (c, &p -> protocol_version);
+ omapi_connection_get_uint32 (c, &p -> header_size);
+
+ /* We currently only support the current protocol version. */
+ if (p -> protocol_version != OMAPI_PROTOCOL_VERSION) {
+ omapi_disconnect (c, 1);
+ return DHCP_R_VERSIONMISMATCH;
+ }
+
+ if (p -> header_size < sizeof (omapi_protocol_header_t)) {
+ omapi_disconnect (c, 1);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ if (p -> default_auth) {
+ status = omapi_protocol_send_open
+ (h, (omapi_object_t *)0, "authenticator",
+ p -> default_auth -> a,
+ OMAPI_NOTIFY_PROTOCOL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ } else {
+ status = omapi_signal_in (h -> inner, "ready");
+ }
+
+ to_header_wait:
+ /* The next thing we're expecting is a message header. */
+ p -> state = omapi_protocol_header_wait;
+
+ /* Register a need for the number of bytes in a
+ header, and if we already have that many, process
+ them immediately. */
+ if ((omapi_connection_require (c, p -> header_size)) !=
+ ISC_R_SUCCESS)
+ break;
+ /* If we already have the data, fall through. */
+
+ case omapi_protocol_header_wait:
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ if (previous_outstanding != 0xDEADBEEF) {
+ log_info ("%s %ld: %ld new, %ld outstanding, %ld%s",
+ "generation", dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm,
+ " long-term");
+#endif
+#if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL))
+ dmalloc_dump_outstanding ();
+#endif
+#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
+ dump_rc_history (h);
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ }
+ previous_outstanding = dmalloc_outstanding;
+#endif
+ status = omapi_message_new ((omapi_object_t **)&p -> message,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ p -> verify_result = ISC_R_SUCCESS;
+
+ /* Swap in the header... */
+ omapi_connection_get_uint32 (c, &p -> message -> authid);
+
+ /* Bind the authenticator to the message object. */
+ if (p -> message -> authid) {
+ status = (omapi_protocol_lookup_auth
+ (&p -> message -> id_object, h,
+ p -> message -> authid));
+ if (status != ISC_R_SUCCESS)
+ p -> verify_result = status;
+
+ /* Activate the authentication key. */
+ status = omapi_set_object_value
+ (c, (omapi_object_t *)0, "input-authenticator",
+ p -> message -> id_object);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+
+ omapi_connection_get_uint32 (c, &p -> message -> authlen);
+ omapi_connection_get_uint32 (c, &p -> message -> op);
+ omapi_connection_get_uint32 (c, &th);
+ p -> message -> h = th;
+ omapi_connection_get_uint32 (c, &p -> message -> id);
+ omapi_connection_get_uint32 (c, &p -> message -> rid);
+
+ /* If there was any extra header data, skip over it. */
+ if (p -> header_size > sizeof (omapi_protocol_header_t)) {
+ omapi_connection_copyout
+ (0, c, (p -> header_size -
+ sizeof (omapi_protocol_header_t)));
+ }
+
+ /* XXX must compute partial signature across the
+ XXX preceding bytes. Also, if authenticator
+ specifies encryption as well as signing, we may
+ have to decrypt the data on the way in. */
+
+ /* First we read in message-specific values, then object
+ values. */
+ p -> reading_message_values = 1;
+
+ need_name_length:
+ /* The next thing we're expecting is length of the
+ first name. */
+ p -> state = omapi_protocol_name_length_wait;
+
+ /* Wait for a 16-bit length. */
+ if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
+ break;
+ /* If it's already here, fall through. */
+
+ case omapi_protocol_name_length_wait:
+ omapi_connection_get_uint16 (c, &nlen);
+ /* A zero-length name means that we're done reading name+value
+ pairs. */
+ if (nlen == 0) {
+ /* If we've already read in the object, we are
+ done reading the message, but if we've just
+ finished reading in the values associated
+ with the message, we need to read the
+ object. */
+ if (p -> reading_message_values) {
+ p -> reading_message_values = 0;
+ goto need_name_length;
+ }
+
+ /* If the authenticator length is zero, there's no
+ signature to read in, so go straight to processing
+ the message. */
+ if (p -> message -> authlen == 0)
+ goto message_done;
+
+ /* The next thing we're expecting is the
+ message signature. */
+ p -> state = omapi_protocol_signature_wait;
+
+ /* Wait for the number of bytes specified for
+ the authenticator. If we already have it,
+ go read it in. */
+ if (omapi_connection_require
+ (c, p -> message -> authlen) == ISC_R_SUCCESS)
+ goto signature_wait;
+ break;
+ }
+
+ /* Allocate a buffer for the name. */
+ status = (omapi_data_string_new (&p -> name, nlen, MDL));
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return ISC_R_NOMEMORY;
+ }
+ p -> state = omapi_protocol_name_wait;
+ if (omapi_connection_require (c, nlen) != ISC_R_SUCCESS)
+ break;
+ /* If it's already here, fall through. */
+
+ case omapi_protocol_name_wait:
+ omapi_connection_copyout (p -> name -> value, c,
+ p -> name -> len);
+ /* Wait for a 32-bit length. */
+ p -> state = omapi_protocol_value_length_wait;
+ if ((omapi_connection_require (c, 4)) != ISC_R_SUCCESS)
+ break;
+ /* If it's already here, fall through. */
+
+ case omapi_protocol_value_length_wait:
+ omapi_connection_get_uint32 (c, &vlen);
+
+ /* Zero-length values are allowed - if we get one, we
+ don't have to read any data for the value - just
+ get the next one, if there is a next one. */
+ if (!vlen)
+ goto insert_new_value;
+
+ status = omapi_typed_data_new (MDL, &p -> value,
+ omapi_datatype_data,
+ vlen);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return ISC_R_NOMEMORY;
+ }
+
+ p -> state = omapi_protocol_value_wait;
+ if (omapi_connection_require (c, vlen) != ISC_R_SUCCESS)
+ break;
+ /* If it's already here, fall through. */
+
+ case omapi_protocol_value_wait:
+ omapi_connection_copyout (p -> value -> u.buffer.value, c,
+ p -> value -> u.buffer.len);
+
+ insert_new_value:
+ if (p -> reading_message_values) {
+ status = (omapi_set_value
+ ((omapi_object_t *)p -> message,
+ p -> message -> id_object,
+ p -> name, p -> value));
+ } else {
+ if (!p -> message -> object) {
+ /* We need a generic object to hang off of the
+ incoming message. */
+ status = (omapi_generic_new
+ (&p -> message -> object, MDL));
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+ status = (omapi_set_value
+ ((omapi_object_t *)p -> message -> object,
+ p -> message -> id_object,
+ p -> name, p -> value));
+ }
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ omapi_data_string_dereference (&p -> name, MDL);
+ if (p -> value)
+ omapi_typed_data_dereference (&p -> value, MDL);
+ goto need_name_length;
+
+ signature_wait:
+ case omapi_protocol_signature_wait:
+ if (p -> message -> id_object) {
+ /* Compute the signature of the message. */
+ signature = (omapi_value_t *)0;
+ status = omapi_get_value_str (c, (omapi_object_t *)0,
+ "input-signature",
+ &signature);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ /* Disable the authentication key on the connection. */
+ status = omapi_set_value_str (c, (omapi_object_t *)0,
+ "input-authenticator",
+ (omapi_typed_data_t *)0);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (&signature, MDL);
+ omapi_disconnect (c, 1);
+ return status;
+ }
+ }
+
+ /* Read the authenticator. */
+ status = omapi_typed_data_new (MDL,
+ &p -> message -> authenticator,
+ omapi_datatype_data,
+ p -> message -> authlen);
+
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (&signature, MDL);
+ omapi_disconnect (c, 1);
+ return ISC_R_NOMEMORY;
+ }
+ omapi_connection_copyout
+ (p -> message -> authenticator -> u.buffer.value, c,
+ p -> message -> authlen);
+
+ /* Verify the signature. */
+ if (p -> message -> id_object &&
+ ((signature -> value -> u.buffer.len !=
+ p -> message -> authlen) ||
+ (memcmp (signature -> value -> u.buffer.value,
+ p -> message -> authenticator -> u.buffer.value,
+ p -> message -> authlen) != 0))) {
+ /* Invalid signature. */
+ p->verify_result = DHCP_R_INVALIDKEY;
+ }
+
+ omapi_value_dereference (&signature, MDL);
+
+ /* Process the message. */
+ message_done:
+ if (p -> verify_result != ISC_R_SUCCESS) {
+ status = omapi_protocol_send_status
+ (h, (omapi_object_t *)0, p -> verify_result,
+ p -> message -> id, (char *)0);
+ } else {
+ status = omapi_message_process
+ ((omapi_object_t *)p -> message, h);
+ }
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (c, 1);
+ return ISC_R_NOMEMORY;
+ }
+
+ omapi_message_dereference (&p -> message, MDL);
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ log_info ("generation %ld: %ld new, %ld outstanding, %ld%s",
+ dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm, " long-term");
+#endif
+#if (defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL))
+ dmalloc_dump_outstanding ();
+#endif
+#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
+ dump_rc_history (h);
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ previous_outstanding = 0xDEADBEEF;
+#endif
+ /* Now wait for the next message. */
+ goto to_header_wait;
+
+ default:
+ /* XXX should never get here. Assertion? */
+ break;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_protocol_add_auth (omapi_object_t *po,
+ omapi_object_t *ao,
+ omapi_handle_t handle)
+{
+ omapi_protocol_object_t *p;
+ omapi_remote_auth_t *r;
+ isc_result_t status;
+
+ if (ao -> type != omapi_type_auth_key &&
+ (!ao -> inner || ao -> inner -> type != omapi_type_auth_key))
+ return DHCP_R_INVALIDARG;
+
+ if (po -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)po;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_protocol_add_auth(name=%s)",
+ ((omapi_auth_key_t *)ao) -> name);
+#endif
+
+ if (p -> verify_auth) {
+ status = (p -> verify_auth) (po, (omapi_auth_key_t *)ao);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* If omapi_protocol_connect() was called with a default
+ authenticator, p -> default_auth will already be set,
+ but p -> remote_auth_list will not yet be initialized. */
+ if (p -> default_auth && !p -> remote_auth_list) {
+ if (p -> default_auth -> a != ao) {
+ /* Something just went horribly wrong. */
+ omapi_disconnect (p -> outer, 1);
+ return ISC_R_UNEXPECTED;
+ }
+
+ p -> remote_auth_list = p -> default_auth;
+ p -> default_auth -> remote_handle = handle;
+
+ return omapi_signal_in (p -> inner, "ready");
+ }
+
+ r = dmalloc (sizeof(*r), MDL);
+ if (!r)
+ return ISC_R_NOMEMORY;
+
+ status = omapi_object_reference (&r -> a, ao, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dfree (r, MDL);
+ return status;
+ }
+
+ r -> remote_handle = handle;
+ r -> next = p -> remote_auth_list;
+ p -> remote_auth_list = r;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_protocol_lookup_auth (omapi_object_t **a,
+ omapi_object_t *po,
+ omapi_handle_t handle)
+{
+ omapi_protocol_object_t *p;
+ omapi_remote_auth_t *r;
+
+ if (po -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)po;
+
+ for (r = p -> remote_auth_list; r; r = r -> next)
+ if (r -> remote_handle == handle)
+ return omapi_object_reference (a, r -> a, MDL);
+
+ return DHCP_R_KEY_UNKNOWN;
+}
+
+isc_result_t omapi_protocol_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ omapi_protocol_object_t *p;
+ omapi_remote_auth_t *r;
+
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "default-authenticator") == 0) {
+ if (value -> type != omapi_datatype_object)
+ return DHCP_R_INVALIDARG;
+
+ if (!value || !value -> u.object) {
+ p -> default_auth = (omapi_remote_auth_t *)0;
+ } else {
+ for (r = p -> remote_auth_list; r; r = r -> next)
+ if (r -> a == value -> u.object)
+ break;
+
+ if (!r)
+ return DHCP_R_KEY_UNKNOWN;
+
+ p -> default_auth = r;
+ }
+
+ return ISC_R_SUCCESS;
+ }
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_protocol_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ omapi_protocol_object_t *p;
+
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "default-authenticator") == 0) {
+ if (!p -> default_auth)
+ return ISC_R_NOTFOUND;
+
+ return omapi_make_object_value (value, name,
+ p -> default_auth -> a, MDL);
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_protocol_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_protocol_object_t *p;
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_object_t *)h;
+ if (p -> message)
+ omapi_message_dereference (&p -> message, file, line);
+
+ /* This will happen if: 1) A default authenticator is supplied to
+ omapi_protocol_connect(), and 2) something goes wrong before
+ the authenticator can be opened. */
+ if (p -> default_auth && !p -> remote_auth_list)
+ dfree (p -> default_auth, file, line);
+
+ while (p -> remote_auth_list) {
+ omapi_remote_auth_t *r = p -> remote_auth_list -> next;
+ p -> remote_auth_list = r;
+ if (r) {
+ omapi_object_dereference (&r -> a, file, line);
+ dfree (r, file, line);
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_protocol_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *p)
+{
+ if (p -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+
+ if (p -> inner && p -> inner -> type -> stuff_values)
+ return (*(p -> inner -> type -> stuff_values)) (c, id,
+ p -> inner);
+ return ISC_R_SUCCESS;
+}
+
+/* Returns a boolean indicating whether this protocol requires that
+ messages be authenticated or not. */
+
+isc_boolean_t omapi_protocol_authenticated (omapi_object_t *h)
+{
+ if (h -> type != omapi_type_protocol)
+ return isc_boolean_false;
+ if (((omapi_protocol_object_t *)h) -> insecure)
+ return isc_boolean_false;
+ else
+ return isc_boolean_true;
+}
+
+/* Sets the address and authenticator verification callbacks. The handle
+ is to a listener object, not a protocol object. */
+
+isc_result_t omapi_protocol_configure_security (omapi_object_t *h,
+ isc_result_t (*verify_addr)
+ (omapi_object_t *,
+ omapi_addr_t *),
+ isc_result_t (*verify_auth)
+ (omapi_object_t *,
+ omapi_auth_key_t *))
+{
+ omapi_protocol_listener_object_t *l;
+
+ if (h -> outer && h -> outer -> type == omapi_type_protocol_listener)
+ h = h -> outer;
+
+ if (h -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+ l = (omapi_protocol_listener_object_t *)h;
+
+ l -> verify_auth = verify_auth;
+ l -> insecure = 0;
+
+ return omapi_listener_configure_security (h -> outer, verify_addr);
+}
+
+
+/* Set up a listener for the omapi protocol. The handle stored points to
+ a listener object, not a protocol object. */
+
+isc_result_t omapi_protocol_listen (omapi_object_t *h,
+ unsigned port,
+ int max)
+{
+ isc_result_t status;
+ omapi_protocol_listener_object_t *obj;
+
+ obj = (omapi_protocol_listener_object_t *)0;
+ status = omapi_protocol_listener_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_protocol_listener_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_protocol_listener_dereference (&obj, MDL);
+ return status;
+ }
+
+ /* What a terrible default. */
+ obj -> insecure = 1;
+
+ status = omapi_listen ((omapi_object_t *)obj, port, max);
+ omapi_protocol_listener_dereference (&obj, MDL);
+ return status;
+}
+
+/* Signal handler for protocol listener - if we get a connect signal,
+ create a new protocol connection, otherwise pass the signal down. */
+
+isc_result_t omapi_protocol_listener_signal (omapi_object_t *o,
+ const char *name, va_list ap)
+{
+ isc_result_t status;
+ omapi_object_t *c;
+ omapi_protocol_object_t *obj;
+ omapi_protocol_listener_object_t *p;
+
+ if (!o || o -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+ p = (omapi_protocol_listener_object_t *)o;
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "connect")) {
+ if (p -> inner && p -> inner -> type -> signal_handler)
+ return (*(p -> inner -> type -> signal_handler))
+ (p -> inner, name, ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ c = va_arg (ap, omapi_object_t *);
+ if (!c || c -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ obj = (omapi_protocol_object_t *)0;
+ status = omapi_protocol_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ obj -> verify_auth = p -> verify_auth;
+ obj -> insecure = p -> insecure;
+
+ status = omapi_object_reference (&obj -> outer, c, MDL);
+ if (status != ISC_R_SUCCESS) {
+ lose:
+ omapi_protocol_dereference (&obj, MDL);
+ omapi_disconnect (c, 1);
+ return status;
+ }
+
+ status = omapi_object_reference (&c -> inner,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto lose;
+
+ /* Send the introductory message. */
+ status = omapi_protocol_send_intro ((omapi_object_t *)obj,
+ OMAPI_PROTOCOL_VERSION,
+ sizeof (omapi_protocol_header_t));
+ if (status != ISC_R_SUCCESS)
+ goto lose;
+
+ omapi_protocol_dereference (&obj, MDL);
+ return status;
+}
+
+isc_result_t omapi_protocol_listener_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_protocol_listener_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_protocol_listener_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ if (h -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_protocol_listener_stuff (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *p)
+{
+ if (p -> type != omapi_type_protocol_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (p -> inner && p -> inner -> type -> stuff_values)
+ return (*(p -> inner -> type -> stuff_values)) (c, id,
+ p -> inner);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_protocol_send_status (omapi_object_t *po,
+ omapi_object_t *id,
+ isc_result_t waitstatus,
+ unsigned rid, const char *msg)
+{
+ isc_result_t status;
+ omapi_message_object_t *message = (omapi_message_object_t *)0;
+ omapi_object_t *mo;
+
+ if (po -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+
+ status = omapi_message_new ((omapi_object_t **)&message, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ mo = (omapi_object_t *)message;
+
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "op", OMAPI_OP_STATUS);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "rid", (int)rid);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "result", (int)waitstatus);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ /* If a message has been provided, send it. */
+ if (msg) {
+ status = omapi_set_string_value (mo, (omapi_object_t *)0,
+ "message", msg);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+ }
+
+ status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0);
+ omapi_message_dereference (&message, MDL);
+ return status;
+}
+
+/* The OMAPI_NOTIFY_PROTOCOL flag will cause the notify-object for the
+ message to be set to the protocol object. This is used when opening
+ the default authenticator. */
+
+isc_result_t omapi_protocol_send_open (omapi_object_t *po,
+ omapi_object_t *id,
+ const char *type,
+ omapi_object_t *object,
+ unsigned flags)
+{
+ isc_result_t status;
+ omapi_message_object_t *message = (omapi_message_object_t *)0;
+ omapi_object_t *mo;
+
+ if (po -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+
+ status = omapi_message_new ((omapi_object_t **)&message, MDL);
+ mo = (omapi_object_t *)message;
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "op", OMAPI_OP_OPEN);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_set_object_value (mo, (omapi_object_t *)0,
+ "object", object);
+
+ if ((flags & OMAPI_CREATE) && (status == ISC_R_SUCCESS))
+ status = omapi_set_boolean_value (mo, (omapi_object_t *)0,
+ "create", 1);
+
+ if ((flags & OMAPI_UPDATE) && (status == ISC_R_SUCCESS))
+ status = omapi_set_boolean_value (mo, (omapi_object_t *)0,
+ "update", 1);
+
+ if ((flags & OMAPI_EXCL) && (status == ISC_R_SUCCESS))
+ status = omapi_set_boolean_value (mo, (omapi_object_t *)0,
+ "exclusive", 1);
+
+ if ((flags & OMAPI_NOTIFY_PROTOCOL) && (status == ISC_R_SUCCESS))
+ status = omapi_set_object_value (mo, (omapi_object_t *)0,
+ "notify-object", po);
+
+ if (type && (status == ISC_R_SUCCESS))
+ status = omapi_set_string_value (mo, (omapi_object_t *)0,
+ "type", type);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_message_register (mo);
+
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_protocol_send_message (po, id, mo,
+ (omapi_object_t *)0);
+ if (status != ISC_R_SUCCESS)
+ omapi_message_unregister (mo);
+ }
+
+ if (message)
+ omapi_message_dereference (&message, MDL);
+
+ return status;
+}
+
+isc_result_t omapi_protocol_send_update (omapi_object_t *po,
+ omapi_object_t *id,
+ unsigned rid,
+ omapi_object_t *object)
+{
+ isc_result_t status;
+ omapi_message_object_t *message = (omapi_message_object_t *)0;
+ omapi_object_t *mo;
+
+ if (po -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+
+ status = omapi_message_new ((omapi_object_t **)&message, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ mo = (omapi_object_t *)message;
+
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "op", OMAPI_OP_UPDATE);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ if (rid) {
+ omapi_handle_t handle;
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "rid", (int)rid);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_object_handle (&handle, object);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+ status = omapi_set_int_value (mo, (omapi_object_t *)0,
+ "handle", (int)handle);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+ }
+
+ status = omapi_set_object_value (mo, (omapi_object_t *)0,
+ "object", object);
+ if (status != ISC_R_SUCCESS) {
+ omapi_message_dereference (&message, MDL);
+ return status;
+ }
+
+ status = omapi_protocol_send_message (po, id, mo, (omapi_object_t *)0);
+ omapi_message_dereference (&message, MDL);
+ return status;
+}
diff --git a/omapip/result.c b/omapip/result.c
new file mode 100644
index 0000000..383279f
--- /dev/null
+++ b/omapip/result.c
@@ -0,0 +1,86 @@
+/* result.c
+ */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "dhcpd.h"
+
+/*
+ * In the previous code the results started at 36
+ * rather than ISC_RESULTCLASS_DHCP + 0
+ * ISC_R_NOTCONNECTED was + 4 (40), it has been superseeded by the isc version
+ */
+
+static const char *text[DHCP_R_NRESULTS] = {
+ "host unknown", /* 0 */
+ "protocol version mismatch", /* 1 */
+ "protocol error", /* 2 */
+ "invalid argument", /* 3 */
+ "data not yet available", /* 4 */
+ "object unchanged", /* 5 */
+ "more than one object matches key", /* 6 */
+ "key conflict", /* 7 */
+ "parse error(s) occurred", /* 8 */
+ "no key specified", /* 9 */
+ "zone TSIG key not known", /* 10 */
+ "invalid TSIG key", /* 11 */
+ "operation in progress", /* 12 */
+ "DNS format error", /* 13 */
+ "DNS server failed", /* 14 */
+ "no such domain", /* 15 */
+ "not implemented", /* 16 */
+ "refused", /* 17 */
+ "domain already exists", /* 18 */
+ "RRset already exists", /* 19 */
+ "no such RRset", /* 20 */
+ "not authorized", /* 21 */
+ "not a zone", /* 22 */
+ "bad DNS signature", /* 23 */
+ "bad DNS key", /* 24 */
+ "clock skew too great", /* 25 */
+ "no root zone", /* 26 */
+ "destination address required", /* 27 */
+ "cross-zone update", /* 28 */
+ "no TSIG signature", /* 29 */
+ "not equal", /* 30 */
+ "connection reset by peer", /* 31 */
+ "unknown attribute" /* 32 */
+};
+
+#define DHCP_RESULT_RESULTSET 2
+#define DHCP_RESULT_UNAVAILABLESET 3
+
+// This is a placeholder as we don't allow for external message catalogs yet
+isc_msgcat_t * dhcp_msgcat = NULL;
+
+isc_result_t
+dhcp_result_register(void) {
+ isc_result_t result;
+
+ result = isc_result_register(ISC_RESULTCLASS_DHCP, DHCP_R_NRESULTS,
+ text, dhcp_msgcat, DHCP_RESULT_RESULTSET);
+
+ return(result);
+}
diff --git a/omapip/support.c b/omapip/support.c
new file mode 100644
index 0000000..4c8be8e
--- /dev/null
+++ b/omapip/support.c
@@ -0,0 +1,852 @@
+/* support.c
+
+ Subroutines providing general support for objects. */
+
+/*
+ * Copyright (c) 2004-2007,2009-2010
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+
+omapi_object_type_t *omapi_type_connection;
+omapi_object_type_t *omapi_type_listener;
+omapi_object_type_t *omapi_type_io_object;
+omapi_object_type_t *omapi_type_datagram;
+omapi_object_type_t *omapi_type_generic;
+omapi_object_type_t *omapi_type_protocol;
+omapi_object_type_t *omapi_type_protocol_listener;
+omapi_object_type_t *omapi_type_waiter;
+omapi_object_type_t *omapi_type_remote;
+omapi_object_type_t *omapi_type_message;
+omapi_object_type_t *omapi_type_auth_key;
+
+omapi_object_type_t *omapi_object_types;
+int omapi_object_type_count;
+
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void omapi_type_relinquish ()
+{
+ omapi_object_type_t *t, *n;
+
+ for (t = omapi_object_types; t; t = n) {
+ n = t -> next;
+ dfree (t, MDL);
+ }
+ omapi_object_types = (omapi_object_type_t *)0;
+}
+#endif
+
+isc_result_t omapi_init (void)
+{
+ isc_result_t status;
+
+ /* Register all the standard object types... */
+ status = omapi_object_type_register (&omapi_type_connection,
+ "connection",
+ omapi_connection_set_value,
+ omapi_connection_get_value,
+ omapi_connection_destroy,
+ omapi_connection_signal_handler,
+ omapi_connection_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof
+ (omapi_connection_object_t), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_listener,
+ "listener",
+ omapi_listener_set_value,
+ omapi_listener_get_value,
+ omapi_listener_destroy,
+ omapi_listener_signal_handler,
+ omapi_listener_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_listener_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_io_object,
+ "io",
+ omapi_io_set_value,
+ omapi_io_get_value,
+ omapi_io_destroy,
+ omapi_io_signal_handler,
+ omapi_io_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_io_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_generic,
+ "generic",
+ omapi_generic_set_value,
+ omapi_generic_get_value,
+ omapi_generic_destroy,
+ omapi_generic_signal_handler,
+ omapi_generic_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_generic_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_protocol,
+ "protocol",
+ omapi_protocol_set_value,
+ omapi_protocol_get_value,
+ omapi_protocol_destroy,
+ omapi_protocol_signal_handler,
+ omapi_protocol_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_protocol_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = (omapi_object_type_register
+ (&omapi_type_protocol_listener, "protocol-listener",
+ omapi_protocol_listener_set_value,
+ omapi_protocol_listener_get_value,
+ omapi_protocol_listener_destroy,
+ omapi_protocol_listener_signal,
+ omapi_protocol_listener_stuff,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_protocol_listener_object_t), 0, RC_MISC));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_message,
+ "message",
+ omapi_message_set_value,
+ omapi_message_get_value,
+ omapi_message_destroy,
+ omapi_message_signal_handler,
+ omapi_message_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_message_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_waiter,
+ "waiter",
+ 0,
+ 0,
+ 0,
+ omapi_waiter_signal_handler, 0,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (omapi_waiter_object_t),
+ 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_type_register (&omapi_type_auth_key,
+ "authenticator",
+ 0,
+ omapi_auth_key_get_value,
+ omapi_auth_key_destroy,
+ 0,
+ omapi_auth_key_stuff_values,
+ omapi_auth_key_lookup,
+ 0, 0, 0, 0, 0,
+ sizeof (omapi_auth_key_t), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+#if defined (TRACING)
+ omapi_listener_trace_setup ();
+ omapi_connection_trace_setup ();
+ omapi_buffer_trace_setup ();
+#endif
+
+ /* This seems silly, but leave it. */
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_object_type_register (omapi_object_type_t **type,
+ const char *name,
+ isc_result_t (*set_value)
+ (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_typed_data_t *),
+ isc_result_t (*get_value)
+ (omapi_object_t *,
+ omapi_object_t *,
+ omapi_data_string_t *,
+ omapi_value_t **),
+ isc_result_t (*destroy)
+ (omapi_object_t *,
+ const char *, int),
+ isc_result_t (*signal_handler)
+ (omapi_object_t *,
+ const char *, va_list),
+ isc_result_t (*stuff_values)
+ (omapi_object_t *,
+ omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*lookup)
+ (omapi_object_t **,
+ omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*create)
+ (omapi_object_t **,
+ omapi_object_t *),
+ isc_result_t (*remove)
+ (omapi_object_t *,
+ omapi_object_t *),
+ isc_result_t (*freer)
+ (omapi_object_t *,
+ const char *, int),
+ isc_result_t (*allocator)
+ (omapi_object_t **,
+ const char *, int),
+ isc_result_t (*sizer) (size_t),
+ size_t size,
+ isc_result_t (*initialize)
+ (omapi_object_t *,
+ const char *, int),
+ int rc_flag)
+{
+ omapi_object_type_t *t;
+
+ t = dmalloc (sizeof *t, MDL);
+ if (!t)
+ return ISC_R_NOMEMORY;
+ memset (t, 0, sizeof *t);
+
+ t -> name = name;
+ t -> set_value = set_value;
+ t -> get_value = get_value;
+ t -> destroy = destroy;
+ t -> signal_handler = signal_handler;
+ t -> stuff_values = stuff_values;
+ t -> lookup = lookup;
+ t -> create = create;
+ t -> remove = remove;
+ t -> next = omapi_object_types;
+ t -> sizer = sizer;
+ t -> size = size;
+ t -> freer = freer;
+ t -> allocator = allocator;
+ t -> initialize = initialize;
+ t -> rc_flag = rc_flag;
+ omapi_object_types = t;
+ if (type)
+ *type = t;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_signal (omapi_object_t *handle, const char *name, ...)
+{
+ va_list ap;
+ omapi_object_t *outer;
+ isc_result_t status;
+
+ va_start (ap, name);
+ for (outer = handle; outer -> outer; outer = outer -> outer)
+ ;
+ if (outer -> type -> signal_handler)
+ status = (*(outer -> type -> signal_handler)) (outer,
+ name, ap);
+ else
+ status = ISC_R_NOTFOUND;
+ va_end (ap);
+ return status;
+}
+
+isc_result_t omapi_signal_in (omapi_object_t *handle, const char *name, ...)
+{
+ va_list ap;
+ isc_result_t status;
+
+ if (!handle)
+ return ISC_R_NOTFOUND;
+ va_start (ap, name);
+
+ if (handle -> type -> signal_handler)
+ status = (*(handle -> type -> signal_handler)) (handle,
+ name, ap);
+ else
+ status = ISC_R_NOTFOUND;
+ va_end (ap);
+ return status;
+}
+
+isc_result_t omapi_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ omapi_object_t *outer;
+ isc_result_t status;
+
+#if defined (DEBUG)
+ if (!value) {
+ log_info ("omapi_set_value (%.*s, NULL)",
+ (int)name -> len, name -> value);
+ } else if (value -> type == omapi_datatype_int) {
+ log_info ("omapi_set_value (%.*s, %ld)",
+ (int)name -> len, name -> value,
+ (long)value -> u.integer);
+ } else if (value -> type == omapi_datatype_string) {
+ log_info ("omapi_set_value (%.*s, %.*s)",
+ (int)name -> len, name -> value,
+ (int)value -> u.buffer.len, value -> u.buffer.value);
+ } else if (value -> type == omapi_datatype_data) {
+ log_info ("omapi_set_value (%.*s, %ld %lx)",
+ (int)name -> len, name -> value,
+ (long)value -> u.buffer.len,
+ (unsigned long)value -> u.buffer.value);
+ } else if (value -> type == omapi_datatype_object) {
+ log_info ("omapi_set_value (%.*s, %s)",
+ (int)name -> len, name -> value,
+ value -> u.object
+ ? (value -> u.object -> type
+ ? value -> u.object -> type -> name
+ : "(unknown object)")
+ : "(unknown object)");
+ }
+#endif
+
+ for (outer = h; outer -> outer; outer = outer -> outer)
+ ;
+ if (outer -> type -> set_value)
+ status = (*(outer -> type -> set_value)) (outer,
+ id, name, value);
+ else
+ status = ISC_R_NOTFOUND;
+#if defined (DEBUG)
+ log_info (" ==> %s", isc_result_totext (status));
+#endif
+ return status;
+}
+
+isc_result_t omapi_set_value_str (omapi_object_t *h,
+ omapi_object_t *id,
+ const char *name,
+ omapi_typed_data_t *value)
+{
+ omapi_data_string_t *nds;
+ isc_result_t status;
+
+ nds = (omapi_data_string_t *)0;
+ status = omapi_data_string_new (&nds, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (nds -> value, name, strlen (name));
+
+ status = omapi_set_value (h, id, nds, value);
+ omapi_data_string_dereference (&nds, MDL);
+ return status;
+}
+
+isc_result_t omapi_set_boolean_value (omapi_object_t *h, omapi_object_t *id,
+ const char *name, int value)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *n = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&n, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (n -> value, name, strlen (name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&n, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, id, n, tv);
+ omapi_data_string_dereference (&n, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+isc_result_t omapi_set_int_value (omapi_object_t *h, omapi_object_t *id,
+ const char *name, int value)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *n = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&n, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (n -> value, name, strlen (name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_int, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&n, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, id, n, tv);
+ omapi_data_string_dereference (&n, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+isc_result_t omapi_set_object_value (omapi_object_t *h, omapi_object_t *id,
+ const char *name, omapi_object_t *value)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *n = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&n, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (n -> value, name, strlen (name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_object, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&n, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, id, n, tv);
+ omapi_data_string_dereference (&n, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+isc_result_t omapi_set_string_value (omapi_object_t *h, omapi_object_t *id,
+ const char *name, const char *value)
+{
+ isc_result_t status;
+ omapi_typed_data_t *tv = (omapi_typed_data_t *)0;
+ omapi_data_string_t *n = (omapi_data_string_t *)0;
+
+ status = omapi_data_string_new (&n, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (n -> value, name, strlen (name));
+
+ status = omapi_typed_data_new (MDL, &tv, omapi_datatype_string, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_data_string_dereference (&n, MDL);
+ return status;
+ }
+
+ status = omapi_set_value (h, id, n, tv);
+ omapi_data_string_dereference (&n, MDL);
+ omapi_typed_data_dereference (&tv, MDL);
+ return status;
+}
+
+isc_result_t omapi_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ omapi_object_t *outer;
+
+ for (outer = h; outer -> outer; outer = outer -> outer)
+ ;
+ if (outer -> type -> get_value)
+ return (*(outer -> type -> get_value)) (outer,
+ id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_get_value_str (omapi_object_t *h,
+ omapi_object_t *id,
+ const char *name,
+ omapi_value_t **value)
+{
+ omapi_object_t *outer;
+ omapi_data_string_t *nds;
+ isc_result_t status;
+
+ nds = (omapi_data_string_t *)0;
+ status = omapi_data_string_new (&nds, strlen (name), MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (nds -> value, name, strlen (name));
+
+ for (outer = h; outer -> outer; outer = outer -> outer)
+ ;
+ if (outer -> type -> get_value)
+ status = (*(outer -> type -> get_value)) (outer,
+ id, nds, value);
+ else
+ status = ISC_R_NOTFOUND;
+ omapi_data_string_dereference (&nds, MDL);
+ return status;
+}
+
+isc_result_t omapi_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *o)
+{
+ omapi_object_t *outer;
+
+ for (outer = o; outer -> outer; outer = outer -> outer)
+ ;
+ if (outer -> type -> stuff_values)
+ return (*(outer -> type -> stuff_values)) (c, id, outer);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_object_create (omapi_object_t **obj, omapi_object_t *id,
+ omapi_object_type_t *type)
+{
+ if (!type -> create)
+ return ISC_R_NOTIMPLEMENTED;
+ return (*(type -> create)) (obj, id);
+}
+
+isc_result_t omapi_object_update (omapi_object_t *obj, omapi_object_t *id,
+ omapi_object_t *src, omapi_handle_t handle)
+{
+ omapi_generic_object_t *gsrc;
+ isc_result_t status;
+ int i;
+
+ if (!src)
+ return DHCP_R_INVALIDARG;
+ if (src -> type != omapi_type_generic)
+ return ISC_R_NOTIMPLEMENTED;
+ gsrc = (omapi_generic_object_t *)src;
+ for (i = 0; i < gsrc -> nvalues; i++) {
+ status = omapi_set_value (obj, id,
+ gsrc -> values [i] -> name,
+ gsrc -> values [i] -> value);
+ if (status != ISC_R_SUCCESS && status != DHCP_R_UNCHANGED)
+ return status;
+ }
+ if (handle)
+ omapi_set_int_value (obj, id, "remote-handle", (int)handle);
+ status = omapi_signal (obj, "updated");
+ if (status != ISC_R_NOTFOUND)
+ return status;
+ return ISC_R_SUCCESS;
+}
+
+int omapi_data_string_cmp (omapi_data_string_t *s1, omapi_data_string_t *s2)
+{
+ unsigned len;
+ int rv;
+
+ if (s1 -> len > s2 -> len)
+ len = s2 -> len;
+ else
+ len = s1 -> len;
+ rv = memcmp (s1 -> value, s2 -> value, len);
+ if (rv)
+ return rv;
+ if (s1 -> len > s2 -> len)
+ return 1;
+ else if (s1 -> len < s2 -> len)
+ return -1;
+ return 0;
+}
+
+int omapi_ds_strcmp (omapi_data_string_t *s1, const char *s2)
+{
+ unsigned len, slen;
+ int rv;
+
+ slen = strlen (s2);
+ if (slen > s1 -> len)
+ len = s1 -> len;
+ else
+ len = slen;
+ rv = memcmp (s1 -> value, s2, len);
+ if (rv)
+ return rv;
+ if (s1 -> len > slen)
+ return 1;
+ else if (s1 -> len < slen)
+ return -1;
+ return 0;
+}
+
+int omapi_td_strcmp (omapi_typed_data_t *s1, const char *s2)
+{
+ unsigned len, slen;
+ int rv;
+
+ /* If the data type is not compatible, never equal. */
+ if (s1 -> type != omapi_datatype_data &&
+ s1 -> type != omapi_datatype_string)
+ return -1;
+
+ slen = strlen (s2);
+ if (slen > s1 -> u.buffer.len)
+ len = s1 -> u.buffer.len;
+ else
+ len = slen;
+ rv = memcmp (s1 -> u.buffer.value, s2, len);
+ if (rv)
+ return rv;
+ if (s1 -> u.buffer.len > slen)
+ return 1;
+ else if (s1 -> u.buffer.len < slen)
+ return -1;
+ return 0;
+}
+
+int omapi_td_strcasecmp (omapi_typed_data_t *s1, const char *s2)
+{
+ unsigned len, slen;
+ int rv;
+
+ /* If the data type is not compatible, never equal. */
+ if (s1 -> type != omapi_datatype_data &&
+ s1 -> type != omapi_datatype_string)
+ return -1;
+
+ slen = strlen (s2);
+ if (slen > s1 -> u.buffer.len)
+ len = s1 -> u.buffer.len;
+ else
+ len = slen;
+ rv = casecmp (s1 -> u.buffer.value, s2, len);
+ if (rv)
+ return rv;
+ if (s1 -> u.buffer.len > slen)
+ return 1;
+ else if (s1 -> u.buffer.len < slen)
+ return -1;
+ return 0;
+}
+
+isc_result_t omapi_make_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value,
+ const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ if (value) {
+ status = omapi_typed_data_reference (&(*vp) -> value,
+ value, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_make_const_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ const unsigned char *value,
+ unsigned len,
+ const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ if (value) {
+ status = omapi_typed_data_new (file, line, &(*vp) -> value,
+ omapi_datatype_data, len);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ memcpy ((*vp) -> value -> u.buffer.value, value, len);
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_make_int_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ int value, const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ status = omapi_typed_data_new (file, line, &(*vp) -> value,
+ omapi_datatype_int, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_make_uint_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ unsigned int value,
+ const char *file, int line)
+{
+ return omapi_make_int_value (vp, name, (int)value, file, line);
+}
+
+isc_result_t omapi_make_object_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ omapi_object_t *value,
+ const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+
+ if (value) {
+ status = omapi_typed_data_new (file, line, &(*vp) -> value,
+ omapi_datatype_object, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_make_handle_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ omapi_object_t *value,
+ const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ if (value) {
+ status = omapi_typed_data_new (file, line, &(*vp) -> value,
+ omapi_datatype_int);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ status = (omapi_object_handle
+ ((omapi_handle_t *)&(*vp) -> value -> u.integer,
+ value));
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_make_string_value (omapi_value_t **vp,
+ omapi_data_string_t *name,
+ const char *value,
+ const char *file, int line)
+{
+ isc_result_t status;
+
+ status = omapi_value_new (vp, file, line);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_data_string_reference (&(*vp) -> name,
+ name, file, line);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ if (value) {
+ status = omapi_typed_data_new (file, line, &(*vp) -> value,
+ omapi_datatype_string, value);
+ if (status != ISC_R_SUCCESS) {
+ omapi_value_dereference (vp, file, line);
+ return status;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_get_int_value (unsigned long *v, omapi_typed_data_t *t)
+{
+ u_int32_t rv;
+
+ if (t -> type == omapi_datatype_int) {
+ *v = t -> u.integer;
+ return ISC_R_SUCCESS;
+ } else if (t -> type == omapi_datatype_string ||
+ t -> type == omapi_datatype_data) {
+ if (t -> u.buffer.len != sizeof (rv))
+ return DHCP_R_INVALIDARG;
+ memcpy (&rv, t -> u.buffer.value, sizeof rv);
+ *v = ntohl (rv);
+ return ISC_R_SUCCESS;
+ }
+ return DHCP_R_INVALIDARG;
+}
diff --git a/omapip/test.c b/omapip/test.c
new file mode 100644
index 0000000..1171317
--- /dev/null
+++ b/omapip/test.c
@@ -0,0 +1,111 @@
+/* test.c
+
+ Test code for omapip... */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <omapip/result.h>
+#include <sys/time.h>
+#include <omapip/omapip.h>
+#include <omapip/isclib.h>
+
+int main (int argc, char **argv)
+{
+ omapi_object_t *listener = (omapi_object_t*)0;
+ omapi_object_t *connection = (omapi_object_t*)0;
+ isc_result_t status;
+
+ status = dhcp_context_create();
+ if (status != ISC_R_SUCCESS) {
+ fprintf(stderr, "Can't initialize context: %s\n",
+ isc_result_totext(status));
+ exit(1);
+ }
+
+ omapi_init ();
+
+ if (argc > 1 && !strcmp (argv [1], "listen")) {
+ if (argc < 3) {
+ fprintf (stderr, "Usage: test listen port\n");
+ exit (1);
+ }
+ status = omapi_generic_new (&listener, MDL);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "omapi_generic_new: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ status = omapi_protocol_listen (listener,
+ (unsigned)atoi (argv [2]), 1);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "omapi_listen: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ omapi_dispatch (0);
+ } else if (argc > 1 && !strcmp (argv [1], "connect")) {
+ if (argc < 4) {
+ fprintf (stderr, "Usage: test listen address port\n");
+ exit (1);
+ }
+ status = omapi_generic_new (&connection, MDL);
+ if (status != ISC_R_SUCCESS) {
+ fprintf (stderr, "omapi_generic_new: %s\n",
+ isc_result_totext (status));
+ exit (1);
+ }
+ status = omapi_protocol_connect (connection,
+ argv [2],
+ (unsigned)atoi (argv [3]), 0);
+ fprintf (stderr, "connect: %s\n", isc_result_totext (status));
+ if (status != ISC_R_SUCCESS)
+ exit (1);
+ status = omapi_wait_for_completion (connection, 0);
+ fprintf (stderr, "completion: %s\n",
+ isc_result_totext (status));
+ if (status != ISC_R_SUCCESS)
+ exit (1);
+ /* ... */
+ } else {
+ fprintf (stderr, "Usage: test [listen | connect] ...\n");
+ exit (1);
+ }
+
+ return 0;
+}
diff --git a/omapip/toisc.c b/omapip/toisc.c
new file mode 100644
index 0000000..11669a8
--- /dev/null
+++ b/omapip/toisc.c
@@ -0,0 +1,217 @@
+/* toisc.c
+
+ Convert non-ISC result codes to ISC result codes. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include "arpa/nameser.h"
+#include "minires.h"
+
+#include <errno.h>
+
+isc_result_t uerr2isc (int err)
+{
+ switch (err) {
+ case EPERM:
+ return ISC_R_NOPERM;
+
+ case ENOENT:
+ return ISC_R_NOTFOUND;
+
+ case ESRCH:
+ return ISC_R_NOTFOUND;
+
+ case EIO:
+ return ISC_R_IOERROR;
+
+ case ENXIO:
+ return ISC_R_NOTFOUND;
+
+ case E2BIG:
+ return ISC_R_NOSPACE;
+
+ case ENOEXEC:
+ return DHCP_R_FORMERR;
+
+ case ECHILD:
+ return ISC_R_NOTFOUND;
+
+ case ENOMEM:
+ return ISC_R_NOMEMORY;
+
+ case EACCES:
+ return ISC_R_NOPERM;
+
+ case EFAULT:
+ return DHCP_R_INVALIDARG;
+
+ case EEXIST:
+ return ISC_R_EXISTS;
+
+ case EINVAL:
+ return DHCP_R_INVALIDARG;
+
+ case ENOTTY:
+ return DHCP_R_INVALIDARG;
+
+ case EFBIG:
+ return ISC_R_NOSPACE;
+
+ case ENOSPC:
+ return ISC_R_NOSPACE;
+
+ case EROFS:
+ return ISC_R_NOPERM;
+
+ case EMLINK:
+ return ISC_R_NOSPACE;
+
+ case EPIPE:
+ return ISC_R_NOTCONNECTED;
+
+ case EINPROGRESS:
+ return ISC_R_ALREADYRUNNING;
+
+ case EALREADY:
+ return ISC_R_ALREADYRUNNING;
+
+ case ENOTSOCK:
+ return ISC_R_INVALIDFILE;
+
+ case EDESTADDRREQ:
+ return DHCP_R_DESTADDRREQ;
+
+ case EMSGSIZE:
+ return ISC_R_NOSPACE;
+
+ case EPROTOTYPE:
+ return DHCP_R_INVALIDARG;
+
+ case ENOPROTOOPT:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case EPROTONOSUPPORT:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case ESOCKTNOSUPPORT:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case EOPNOTSUPP:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case EPFNOSUPPORT:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case EAFNOSUPPORT:
+ return ISC_R_NOTIMPLEMENTED;
+
+ case EADDRINUSE:
+ return ISC_R_ADDRINUSE;
+
+ case EADDRNOTAVAIL:
+ return ISC_R_ADDRNOTAVAIL;
+
+ case ENETDOWN:
+ return ISC_R_NETDOWN;
+
+ case ENETUNREACH:
+ return ISC_R_NETUNREACH;
+
+ case ECONNABORTED:
+ return ISC_R_TIMEDOUT;
+
+ case ECONNRESET:
+ return DHCP_R_CONNRESET;
+
+ case ENOBUFS:
+ return ISC_R_NOSPACE;
+
+ case EISCONN:
+ return ISC_R_ALREADYRUNNING;
+
+ case ENOTCONN:
+ return ISC_R_NOTCONNECTED;
+
+ case ESHUTDOWN:
+ return ISC_R_SHUTTINGDOWN;
+
+ case ETIMEDOUT:
+ return ISC_R_TIMEDOUT;
+
+ case ECONNREFUSED:
+ return ISC_R_CONNREFUSED;
+
+ case EHOSTDOWN:
+ return ISC_R_HOSTDOWN;
+
+ case EHOSTUNREACH:
+ return ISC_R_HOSTUNREACH;
+
+#ifdef EDQUOT
+ case EDQUOT:
+ return ISC_R_QUOTA;
+#endif
+
+#ifdef EBADRPC
+ case EBADRPC:
+ return ISC_R_NOTIMPLEMENTED;
+#endif
+
+#ifdef ERPCMISMATCH
+ case ERPCMISMATCH:
+ return DHCP_R_VERSIONMISMATCH;
+#endif
+
+#ifdef EPROGMISMATCH
+ case EPROGMISMATCH:
+ return DHCP_R_VERSIONMISMATCH;
+#endif
+
+#ifdef EAUTH
+ case EAUTH:
+ return DHCP_R_NOTAUTH;
+#endif
+
+#ifdef ENEEDAUTH
+ case ENEEDAUTH:
+ return DHCP_R_NOTAUTH;
+#endif
+
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ return ISC_R_NOSPACE;
+#endif
+ }
+ return ISC_R_UNEXPECTED;
+}
diff --git a/omapip/trace.c b/omapip/trace.c
new file mode 100644
index 0000000..9fd3fb5
--- /dev/null
+++ b/omapip/trace.c
@@ -0,0 +1,716 @@
+/* trace.c
+
+ Subroutines that support tracing of OMAPI wire transactions and
+ provide a mechanism for programs using OMAPI to trace their own
+ transactions... */
+
+/*
+ * Copyright (c) 2009-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
+ * about Internet Systems Consortium, see https://www.isc.org/. To
+ * learn more about Nominum, Inc., see ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <errno.h>
+
+#if defined (TRACING)
+void (*trace_set_time_hook) (TIME);
+static int tracing_stopped;
+static int traceoutfile;
+static int traceindex;
+static trace_type_t **trace_types;
+static int trace_type_count;
+static int trace_type_max;
+static trace_type_t *new_trace_types;
+static FILE *traceinfile;
+static tracefile_header_t tracefile_header;
+static int trace_playback_flag;
+trace_type_t trace_time_marker;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+extern omapi_array_t *trace_listeners;
+extern omapi_array_t *omapi_connections;
+
+extern int errno;
+
+void trace_free_all ()
+{
+ trace_type_t *tp;
+ int i;
+ tp = new_trace_types;
+ while (tp) {
+ new_trace_types = tp -> next;
+ if (tp -> name) {
+ dfree (tp -> name, MDL);
+ tp -> name = (char *)0;
+ }
+ dfree (tp, MDL);
+ tp = new_trace_types;
+ }
+ for (i = 0; i < trace_type_count; i++) {
+ if (trace_types [i]) {
+ if (trace_types [i] -> name)
+ dfree (trace_types [i] -> name, MDL);
+ dfree (trace_types [i], MDL);
+ }
+ }
+ dfree (trace_types, MDL);
+ trace_types = (trace_type_t **)0;
+ trace_type_count = trace_type_max = 0;
+
+ omapi_array_free (&trace_listeners, MDL);
+ omapi_array_free (&omapi_connections, MDL);
+}
+#endif
+
+static isc_result_t trace_type_record (trace_type_t *,
+ unsigned, const char *, int);
+
+int trace_playback ()
+{
+ return trace_playback_flag;
+}
+
+int trace_record ()
+{
+ if (traceoutfile && !tracing_stopped)
+ return 1;
+ return 0;
+}
+
+isc_result_t trace_init (void (*set_time) (TIME),
+ const char *file, int line)
+{
+ trace_type_t *root_type;
+ static int root_setup = 0;
+
+ if (root_setup)
+ return ISC_R_SUCCESS;
+
+ trace_set_time_hook = set_time;
+
+ root_type = trace_type_register ("trace-index-mapping",
+ (void *)0, trace_index_map_input,
+ trace_index_stop_tracing, file, line);
+ if (!root_type)
+ return ISC_R_UNEXPECTED;
+ if (new_trace_types == root_type)
+ new_trace_types = new_trace_types -> next;
+ root_type -> index = 0;
+ trace_type_stash (root_type);
+
+ root_setup = 1;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t trace_begin (const char *filename,
+ const char *file, int line)
+{
+ tracefile_header_t tfh;
+ int status;
+ trace_type_t *tptr, *next;
+ isc_result_t result;
+
+ if (traceoutfile) {
+ log_error ("%s(%d): trace_begin called twice",
+ file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ traceoutfile = open (filename, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (traceoutfile < 0 && errno == EEXIST) {
+ log_error ("WARNING: Overwriting trace file \"%s\"", filename);
+ traceoutfile = open (filename, O_WRONLY | O_EXCL | O_TRUNC,
+ 0600);
+ }
+
+ if (traceoutfile < 0) {
+ log_error ("%s(%d): trace_begin: %s: %m",
+ file, line, filename);
+ return ISC_R_UNEXPECTED;
+ }
+#if defined (HAVE_SETFD)
+ if (fcntl (traceoutfile, F_SETFD, 1) < 0)
+ log_error ("Can't set close-on-exec on %s: %m", filename);
+#endif
+
+ tfh.magic = htonl (TRACEFILE_MAGIC);
+ tfh.version = htonl (TRACEFILE_VERSION);
+ tfh.hlen = htonl (sizeof (tracefile_header_t));
+ tfh.phlen = htonl (sizeof (tracepacket_t));
+
+ status = write (traceoutfile, &tfh, sizeof tfh);
+ if (status < 0) {
+ log_error ("%s(%d): trace_begin write failed: %m", file, line);
+ return ISC_R_UNEXPECTED;
+ } else if (status != sizeof tfh) {
+ log_error ("%s(%d): trace_begin: short write (%d:%ld)",
+ file, line, status, (long)(sizeof tfh));
+ trace_stop ();
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Stash all the types that have already been set up. */
+ if (new_trace_types) {
+ next = new_trace_types;
+ new_trace_types = (trace_type_t *)0;
+ for (tptr = next; tptr; tptr = next) {
+ next = tptr -> next;
+ if (tptr -> index != 0) {
+ result = (trace_type_record
+ (tptr,
+ strlen (tptr -> name), file, line));
+ if (result != ISC_R_SUCCESS)
+ return status;
+ }
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t trace_write_packet (trace_type_t *ttype, unsigned length,
+ const char *buf, const char *file, int line)
+{
+ trace_iov_t iov;
+
+ iov.buf = buf;
+ iov.len = length;
+ return trace_write_packet_iov (ttype, 1, &iov, file, line);
+}
+
+isc_result_t trace_write_packet_iov (trace_type_t *ttype,
+ int count, trace_iov_t *iov,
+ const char *file, int line)
+{
+ tracepacket_t tmp;
+ int status;
+ int i;
+ int length;
+
+ /* Really shouldn't get called here, but it may be hard to turn off
+ tracing midstream if the trace file write fails or something. */
+ if (tracing_stopped)
+ return 0;
+
+ if (!ttype) {
+ log_error ("%s(%d): trace_write_packet with null trace type",
+ file ? file : "<unknown file>", line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (!traceoutfile) {
+ log_error ("%s(%d): trace_write_packet with no tracefile.",
+ file ? file : "<unknown file>", line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ /* Compute the total length of the iov. */
+ length = 0;
+ for (i = 0; i < count; i++)
+ length += iov [i].len;
+
+ /* We have to swap out the data, because it may be read back on a
+ machine of different endianness. */
+ tmp.type_index = htonl (ttype -> index);
+ tmp.when = htonl (time ((time_t *)0)); /* XXX */
+ tmp.length = htonl (length);
+
+ status = write (traceoutfile, &tmp, sizeof tmp);
+ if (status < 0) {
+ log_error ("%s(%d): trace_write_packet write failed: %m",
+ file, line);
+ return ISC_R_UNEXPECTED;
+ } else if (status != sizeof tmp) {
+ log_error ("%s(%d): trace_write_packet: short write (%d:%ld)",
+ file, line, status, (long)(sizeof tmp));
+ trace_stop ();
+ }
+
+ for (i = 0; i < count; i++) {
+ status = write (traceoutfile, iov [i].buf, iov [i].len);
+ if (status < 0) {
+ log_error ("%s(%d): %s write failed: %m",
+ file, line, "trace_write_packet");
+ return ISC_R_UNEXPECTED;
+ } else if (status != iov [i].len) {
+ log_error ("%s(%d): %s: short write (%d:%d)",
+ file, line,
+ "trace_write_packet", status, length);
+ trace_stop ();
+ }
+ }
+
+ /* Write padding on the end of the packet to align the next
+ packet to an 8-byte boundary. This is in case we decide to
+ use mmap in some clever way later on. */
+ if (length % 8) {
+ static char zero [] = { 0, 0, 0, 0, 0, 0, 0 };
+ unsigned padl = 8 - (length % 8);
+
+ status = write (traceoutfile, zero, padl);
+ if (status < 0) {
+ log_error ("%s(%d): trace_write_packet write failed: %m",
+ file, line);
+ return ISC_R_UNEXPECTED;
+ } else if (status != padl) {
+ log_error ("%s(%d): trace_write_packet: short write (%d:%d)",
+ file, line, status, padl);
+ trace_stop ();
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void trace_type_stash (trace_type_t *tptr)
+{
+ trace_type_t **vec;
+ int delta;
+ if (trace_type_max <= tptr -> index) {
+ delta = tptr -> index - trace_type_max + 10;
+ vec = dmalloc (((trace_type_max + delta) *
+ sizeof (trace_type_t *)), MDL);
+ if (!vec)
+ return;
+ memset (&vec [trace_type_max], 0,
+ (sizeof (trace_type_t *)) * delta);
+ trace_type_max += delta;
+ if (trace_types) {
+ memcpy (vec, trace_types,
+ trace_type_count * sizeof (trace_type_t *));
+ dfree (trace_types, MDL);
+ }
+ trace_types = vec;
+ }
+ trace_types [tptr -> index] = tptr;
+ if (tptr -> index >= trace_type_count)
+ trace_type_count = tptr -> index + 1;
+}
+
+trace_type_t *trace_type_register (const char *name,
+ void *baggage,
+ void (*have_packet) (trace_type_t *,
+ unsigned, char *),
+ void (*stop_tracing) (trace_type_t *),
+ const char *file, int line)
+{
+ trace_type_t *ttmp;
+ unsigned slen = strlen (name);
+ isc_result_t status;
+
+ ttmp = dmalloc (sizeof *ttmp, file, line);
+ if (!ttmp)
+ return ttmp;
+ ttmp -> index = -1;
+ ttmp -> name = dmalloc (slen + 1, file, line);
+ if (!ttmp -> name) {
+ dfree (ttmp, file, line);
+ return (trace_type_t *)0;
+ }
+ strcpy (ttmp -> name, name);
+ ttmp -> have_packet = have_packet;
+ ttmp -> stop_tracing = stop_tracing;
+
+ if (traceoutfile) {
+ status = trace_type_record (ttmp, slen, file, line);
+ if (status != ISC_R_SUCCESS) {
+ dfree (ttmp -> name, file, line);
+ dfree (ttmp, file, line);
+ return (trace_type_t *)0;
+ }
+ } else {
+ ttmp -> next = new_trace_types;
+ new_trace_types = ttmp;
+ }
+
+ return ttmp;
+}
+
+static isc_result_t trace_type_record (trace_type_t *ttmp, unsigned slen,
+ const char *file, int line)
+{
+ trace_index_mapping_t *tim;
+ isc_result_t status;
+
+ tim = dmalloc (slen + TRACE_INDEX_MAPPING_SIZE, file, line);
+ if (!tim)
+ return ISC_R_NOMEMORY;
+ ttmp -> index = ++traceindex;
+ trace_type_stash (ttmp);
+ tim -> index = htonl (ttmp -> index);
+ memcpy (tim -> name, ttmp -> name, slen);
+ status = trace_write_packet (trace_types [0],
+ slen + TRACE_INDEX_MAPPING_SIZE,
+ (char *)tim, file, line);
+ dfree (tim, file, line);
+ return status;
+}
+
+/* Stop all registered trace types from trying to trace. */
+
+void trace_stop (void)
+{
+ int i;
+
+ for (i = 0; i < trace_type_count; i++)
+ if (trace_types [i] -> stop_tracing)
+ (*(trace_types [i] -> stop_tracing))
+ (trace_types [i]);
+ tracing_stopped = 1;
+}
+
+void trace_index_map_input (trace_type_t *ttype, unsigned length, char *buf)
+{
+ trace_index_mapping_t *tmap;
+ unsigned len;
+ trace_type_t *tptr, **prev;
+
+ if (length < TRACE_INDEX_MAPPING_SIZE) {
+ log_error ("short trace index mapping");
+ return;
+ }
+ tmap = (trace_index_mapping_t *)buf;
+
+ prev = &new_trace_types;
+ for (tptr = new_trace_types; tptr; tptr = tptr -> next) {
+ len = strlen (tptr -> name);
+ if (len == length - TRACE_INDEX_MAPPING_SIZE &&
+ !memcmp (tptr -> name, tmap -> name, len)) {
+ tptr -> index = ntohl (tmap -> index);
+ trace_type_stash (tptr);
+ *prev = tptr -> next;
+ return;
+ }
+ prev = &tptr -> next;
+ }
+
+ log_error ("No registered trace type for type name %.*s",
+ (int)length - TRACE_INDEX_MAPPING_SIZE, tmap -> name);
+ return;
+}
+
+void trace_index_stop_tracing (trace_type_t *ttype) { }
+
+void trace_replay_init (void)
+{
+ trace_playback_flag = 1;
+}
+
+void trace_file_replay (const char *filename)
+{
+ tracepacket_t *tpkt = NULL;
+ int status;
+ char *buf = NULL;
+ unsigned buflen;
+ unsigned bufmax = 0;
+ trace_type_t *ttype = NULL;
+ isc_result_t result;
+ int len;
+
+ traceinfile = fopen (filename, "r");
+ if (!traceinfile) {
+ log_error("Can't open tracefile %s: %m", filename);
+ return;
+ }
+#if defined (HAVE_SETFD)
+ if (fcntl (fileno(traceinfile), F_SETFD, 1) < 0)
+ log_error("Can't set close-on-exec on %s: %m", filename);
+#endif
+ status = fread(&tracefile_header, 1,
+ sizeof tracefile_header, traceinfile);
+ if (status < sizeof tracefile_header) {
+ if (ferror(traceinfile))
+ log_error("Error reading trace file header: %m");
+ else
+ log_error("Short read on trace file header: %d %ld.",
+ status, (long)(sizeof tracefile_header));
+ goto out;
+ }
+ tracefile_header.magic = ntohl(tracefile_header.magic);
+ tracefile_header.version = ntohl(tracefile_header.version);
+ tracefile_header.hlen = ntohl(tracefile_header.hlen);
+ tracefile_header.phlen = ntohl(tracefile_header.phlen);
+
+ if (tracefile_header.magic != TRACEFILE_MAGIC) {
+ log_error("%s: not a dhcp trace file.", filename);
+ goto out;
+ }
+ if (tracefile_header.version > TRACEFILE_VERSION) {
+ log_error ("tracefile version %ld > current %ld.",
+ (long int)tracefile_header.version,
+ (long int)TRACEFILE_VERSION);
+ goto out;
+ }
+ if (tracefile_header.phlen < sizeof *tpkt) {
+ log_error("tracefile packet size too small - %ld < %ld",
+ (long int)tracefile_header.phlen,
+ (long int)sizeof *tpkt);
+ goto out;
+ }
+ len = (sizeof tracefile_header) - tracefile_header.hlen;
+ if (len < 0) {
+ log_error("tracefile header size too small - %ld < %ld",
+ (long int)tracefile_header.hlen,
+ (long int)sizeof tracefile_header);
+ goto out;
+ }
+ if (len > 0) {
+ status = fseek(traceinfile, (long)len, SEEK_CUR);
+ if (status < 0) {
+ log_error("can't seek past header: %m");
+ goto out;
+ }
+ }
+
+ tpkt = dmalloc((unsigned)tracefile_header.phlen, MDL);
+ if (tpkt == NULL) {
+ log_error ("can't allocate trace packet header.");
+ goto out;
+ }
+
+ while ((result = trace_get_next_packet(&ttype, tpkt, &buf, &buflen,
+ &bufmax)) == ISC_R_SUCCESS) {
+ (*ttype->have_packet)(ttype, tpkt->length, buf);
+ ttype = NULL;
+ }
+ out:
+ fclose(traceinfile);
+ if (buf != NULL)
+ dfree(buf, MDL);
+ if (tpkt != NULL)
+ dfree(tpkt, MDL);
+}
+
+/* Get the next packet from the file. If ttp points to a nonzero pointer
+ to a trace type structure, check the next packet to see if it's of the
+ expected type, and back off if not. */
+
+isc_result_t trace_get_next_packet (trace_type_t **ttp,
+ tracepacket_t *tpkt,
+ char **buf, unsigned *buflen,
+ unsigned *bufmax)
+{
+ trace_type_t *ttype;
+ unsigned paylen;
+ int status, curposok = 0;
+ fpos_t curpos;
+
+ while(1) {
+ curposok = 0;
+ status = fgetpos(traceinfile, &curpos);
+ if (status < 0) {
+ log_error("Can't save tracefile position: %m");
+ } else {
+ curposok = 1;
+ }
+
+ status = fread(tpkt, 1, (size_t)tracefile_header.phlen,
+ traceinfile);
+ if (status < tracefile_header.phlen) {
+ if (ferror(traceinfile))
+ log_error("Error reading trace packet header: "
+ "%m");
+ else if (status == 0)
+ return ISC_R_EOF;
+ else
+ log_error ("Short read on trace packet header:"
+ " %ld %ld.",
+ (long int)status,
+ (long int)tracefile_header.phlen);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* Swap the packet. */
+ tpkt->type_index = ntohl(tpkt -> type_index);
+ tpkt->length = ntohl(tpkt -> length);
+ tpkt->when = ntohl(tpkt -> when);
+
+ /* See if there's a handler for this packet type. */
+ if (tpkt->type_index < trace_type_count &&
+ trace_types[tpkt->type_index])
+ ttype = trace_types[tpkt->type_index];
+ else {
+ log_error ("Trace packet with unknown index %ld",
+ (long int)tpkt->type_index);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /*
+ * Determine if we should try to expire any timer events.
+ * We do so if:
+ * we aren't looking for a specific type of packet
+ * we have a hook to use to update the timer
+ * the timestamp on the packet doesn't match the current time
+ * When we do so we rewind the file to the beginning of this
+ * packet and then try for a new packet. This allows
+ * any code triggered by a timeout to get the current packet
+ * while we get the next one.
+ */
+
+ if ((ttp != NULL) && (*ttp == NULL) &&
+ (tpkt->when != cur_tv.tv_sec) &&
+ (trace_set_time_hook != NULL)) {
+ if (curposok == 0) {
+ log_error("no curpos for fsetpos in "
+ "tracefile");
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ status = fsetpos(traceinfile, &curpos);
+ if (status < 0) {
+ log_error("fsetpos in tracefile failed: %m");
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ (*trace_set_time_hook) (tpkt->when);
+ continue;
+ }
+ break;
+ }
+
+ /* If we were supposed to get a particular kind of packet,
+ check to see that we got the right kind. */
+ if (ttp && *ttp && ttype != *ttp) {
+ log_error ("Read packet type %s when expecting %s",
+ ttype -> name, (*ttp) -> name);
+ status = fsetpos (traceinfile, &curpos);
+ if (status < 0) {
+ log_error ("fsetpos in tracefile failed: %m");
+ return DHCP_R_PROTOCOLERROR;
+ }
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
+
+ paylen = tpkt -> length;
+ if (paylen % 8)
+ paylen += 8 - (tpkt -> length % 8);
+ if (paylen > (*bufmax)) {
+ if ((*buf))
+ dfree ((*buf), MDL);
+ (*bufmax) = ((paylen + 1023) & ~1023U);
+ (*buf) = dmalloc ((*bufmax), MDL);
+ if (!(*buf)) {
+ log_error ("Can't allocate input buffer sized %d",
+ (*bufmax));
+ return ISC_R_NOMEMORY;
+ }
+ }
+
+ status = fread ((*buf), 1, paylen, traceinfile);
+ if (status < paylen) {
+ if (ferror (traceinfile))
+ log_error ("Error reading trace payload: %m");
+ else
+ log_error ("Short read on trace payload: %d %d.",
+ status, paylen);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* Store the actual length of the payload. */
+ *buflen = tpkt -> length;
+
+ if (ttp)
+ *ttp = ttype;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t trace_get_packet (trace_type_t **ttp,
+ unsigned *buflen, char **buf)
+{
+ tracepacket_t *tpkt;
+ unsigned bufmax = 0;
+ isc_result_t status;
+
+ if (!buf || *buf)
+ return DHCP_R_INVALIDARG;
+
+ tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
+ if (!tpkt) {
+ log_error ("can't allocate trace packet header.");
+ return ISC_R_NOMEMORY;
+ }
+
+ status = trace_get_next_packet (ttp, tpkt, buf, buflen, &bufmax);
+
+ dfree (tpkt, MDL);
+ return status;
+}
+
+/* Get a packet from the trace input file that contains a file with the
+ specified name. We don't hunt for the packet - it should be the next
+ packet in the tracefile. If it's not, or something else bad happens,
+ return an error code. */
+
+isc_result_t trace_get_file (trace_type_t *ttype,
+ const char *filename, unsigned *len, char **buf)
+{
+ fpos_t curpos;
+ unsigned max = 0;
+ tracepacket_t *tpkt;
+ int status;
+ isc_result_t result;
+
+ /* Disallow some obvious bogosities. */
+ if (!buf || !len || *buf)
+ return DHCP_R_INVALIDARG;
+
+ /* Save file position in case of filename mismatch. */
+ status = fgetpos (traceinfile, &curpos);
+ if (status < 0)
+ log_error ("Can't save tracefile position: %m");
+
+ tpkt = dmalloc ((unsigned)tracefile_header.phlen, MDL);
+ if (!tpkt) {
+ log_error ("can't allocate trace packet header.");
+ return ISC_R_NOMEMORY;
+ }
+
+ result = trace_get_next_packet (&ttype, tpkt, buf, len, &max);
+ if (result != ISC_R_SUCCESS) {
+ dfree (tpkt, MDL);
+ if (*buf)
+ dfree (*buf, MDL);
+ return result;
+ }
+
+ /* Make sure the filename is right. */
+ if (strcmp (filename, *buf)) {
+ log_error ("Read file %s when expecting %s", *buf, filename);
+ status = fsetpos (traceinfile, &curpos);
+ if (status < 0) {
+ log_error ("fsetpos in tracefile failed: %m");
+ dfree (tpkt, MDL);
+ dfree (*buf, MDL);
+ return DHCP_R_PROTOCOLERROR;
+ }
+ return ISC_R_UNEXPECTEDTOKEN;
+ }
+
+ dfree (tpkt, MDL);
+ return ISC_R_SUCCESS;
+}
+#endif /* TRACING */
diff --git a/relay/Makefile.am b/relay/Makefile.am
new file mode 100644
index 0000000..d8757ca
--- /dev/null
+++ b/relay/Makefile.am
@@ -0,0 +1,9 @@
+AM_CPPFLAGS = -DLOCALSTATEDIR='"@localstatedir@"'
+
+sbin_PROGRAMS = dhcrelay
+dhcrelay_SOURCES = dhcrelay.c
+dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+man_MANS = dhcrelay.8
+EXTRA_DIST = $(man_MANS)
+
diff --git a/relay/Makefile.in b/relay/Makefile.in
new file mode 100644
index 0000000..9336f0c
--- /dev/null
+++ b/relay/Makefile.in
@@ -0,0 +1,470 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+sbin_PROGRAMS = dhcrelay$(EXEEXT)
+subdir = relay
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(sbin_PROGRAMS)
+am_dhcrelay_OBJECTS = dhcrelay.$(OBJEXT)
+dhcrelay_OBJECTS = $(am_dhcrelay_OBJECTS)
+dhcrelay_DEPENDENCIES = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(dhcrelay_SOURCES)
+DIST_SOURCES = $(dhcrelay_SOURCES)
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -DLOCALSTATEDIR='"@localstatedir@"'
+dhcrelay_SOURCES = dhcrelay.c
+dhcrelay_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../bind/lib/libdns.a ../bind/lib/libisc.a
+
+man_MANS = dhcrelay.8
+EXTRA_DIST = $(man_MANS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign relay/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign relay/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+dhcrelay$(EXEEXT): $(dhcrelay_OBJECTS) $(dhcrelay_DEPENDENCIES)
+ @rm -f dhcrelay$(EXEEXT)
+ $(LINK) $(dhcrelay_OBJECTS) $(dhcrelay_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcrelay.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/relay/dhcrelay.8 b/relay/dhcrelay.8
new file mode 100644
index 0000000..a087d14
--- /dev/null
+++ b/relay/dhcrelay.8
@@ -0,0 +1,258 @@
+.\" dhcrelay.8
+.\"
+.\" Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1997-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.\" $Id: dhcrelay.8,v 1.16.24.3 2011-04-15 22:12:50 sar Exp $
+.\"
+.TH dhcrelay 8
+.SH NAME
+dhcrelay - Dynamic Host Configuration Protocol Relay Agent
+.SH SYNOPSIS
+.B dhcrelay
+[
+.B -4
+]
+[
+.B -dqaD
+]
+[
+.B -p
+.I port
+]
+[
+.B -c
+.I count
+]
+[
+.B -A
+.I length
+]
+[
+.B -pf
+.I pid-file
+]
+[
+.B --no-pid
+]
+[
+.B -m
+.I append
+|
+.I replace
+|
+.I forward
+|
+.I discard
+]
+[
+.B -i
+.I interface0
+[
+.B ...
+.B -i
+.I interfaceN
+]
+]
+.I server0
+[
+.I ...serverN
+]
+.PP
+.B dhcrelay -6
+[
+.B -dqI
+]
+[
+.B -p
+.I port
+]
+[
+.B -c
+.I count
+]
+[
+.B -pf
+.I pid-file
+]
+[
+.B --no-pid
+]
+.B -l
+.I lower0
+[
+.B ...
+.B -l
+.I lowerN
+]
+.B -u
+.I upper0
+[
+.B ...
+.B -u
+.I upperN
+]
+.SH DESCRIPTION
+The Internet Systems Consortium DHCP Relay Agent, dhcrelay, provides a
+means for relaying DHCP and BOOTP requests from a subnet to which
+no DHCP server is directly connected to one or more DHCP servers on
+other subnets. It supports both DHCPv4/BOOTP and DHCPv6 protocols.
+.SH OPERATION
+.PP
+The DHCP Relay Agent listens for DHCPv4 or DHCPv6 queries from clients or
+other relay agents on one or more interfaces, passing them along to
+``upstream'' servers or relay agents as specified on the command line.
+When a reply is received from upstream, it is multicast or unicast back
+downstream to the source of the original request.
+.SH COMMAND LINE
+.PP
+\fIProtocol selection options:\fR
+.TP
+-6
+Run dhcrelay as a DHCPv6 relay agent. Incompatible with the \fB-4\fR
+option.
+.TP
+-4
+Run dhcrelay as a DHCPv4/BOOTP relay agent. This is the default mode of
+operation, so the argument is not necessary, but may be specified for
+clarity. Incompatible with \fB-6\fR.
+.PP
+\fISpecifying DHCPv4/BOOTP servers\fR
+.PP
+In DHCPv4 mode, a list of one or more server addresses must be specified on
+the command line, to which DHCP/BOOTP queries should be relayed.
+.PP
+\fIOptions available for both DHCPv4 and DHCPv6:\fR
+.TP
+-c COUNT
+Maximum hop count. When forwarding packets, dhcrelay discards packets
+which have reached a hop count of COUNT. Default is 10. Maximum is 255.
+.TP
+-d
+Force dhcrelay to run as a foreground process. Useful when running
+dhcrelay under a debugger, or running out of inittab on System V systems.
+.TP
+-p PORT
+Listen and transmit on port PORT. This is mostly useful for debugging
+purposes. Default is port 67 for DHCPv4/BOOTP, or port 547 for DHCPv6.
+.TP
+-q
+Quiet mode. Prevents dhcrelay6 from printing its network configuration
+on startup.
+.TP
+-pf pid-file
+Path to alternate pid file.
+.TP
+--no-pid
+Option to disable writing pid files. By default the program
+will write a pid file.
+.PP
+\fIOptions available in DHCPv4 mode only:\fR
+.TP
+-a
+Append an agent option field to each request before forwarding it to
+the server. Agent option fields in responses sent from servers to
+clients will be stripped before forwarding such responses back to the
+client. The agent option field will contain two agent options: the Circuit
+ID suboption and the Remote ID suboption. Currently, the Circuit ID will
+be the printable name of the interface on which the client request was
+received. The client supports inclusion of a Remote ID suboption as well,
+but this is not used by default.
+.TP
+-A LENGTH
+Specify the maximum packet size to send to a DHCPv4/BOOTP server. This
+might be done to allow sufficient space for addition of relay agent
+options while still fitting into the Ethernet MTU size.
+.TP
+-D
+Drop packets from upstream servers if they contain Relay Agent
+Information options that indicate they were generated in response to
+a query that came via a different relay agent. If this option is not
+specified, such packets will be relayed anyway.
+.TP
+-i \fIifname\fR
+Listen for DHCPv4/BOOTP queries on interface \fIifname\fR. Multiple
+interfaces may be specified by using more than one \fB-i\fR option. If
+no interfaces are specified on the command line, dhcrelay will identify
+all network interfaces, eliminating non-broadcast interfaces if possible,
+and attempt to listen on all of them.
+.TP
+-m \fIappend\fR|\fIreplace\fR|\fIforward\fR|\fIdiscard\fR
+Control the handling of incoming DHCPv4 packets which already contain
+relay agent options. If such a packet does not have \fIgiaddr\fR set in
+its header, the DHCP standard requires that the packet be discarded.
+However, if \fIgiaddr\fR is set, the relay agent may handle the situation
+in four ways: It may \fIappend\fR its own set of relay options to the
+packet, leaving the supplied option field intact; it may \fIreplace\fR the
+existing agent option field; it may \fIforward\fR the packet unchanged; or,
+it may \fIdiscard\fR it.
+.PP
+\fIOptions available in DHCPv6 mode only:\fR
+.TP
+-I
+Force use of the DHCPv6 Interface-ID option. This option is
+automatically sent when there are two or more downstream interfaces
+in use, to disambiguate between them. The \fB-I\fR option causes
+dhcrelay to send the option even if there is only one downstream
+interface.
+.TP
+-l [\fIaddress%\fR]\fIifname\fR[\fI#index\fR]
+Specifies the ``lower'' network interface for DHCPv6 relay mode: the
+interface on which queries will be received from clients or from other
+relay agents. At least one \fB-l\fR option must be included in the command
+line when running in DHCPv6 mode. The interface name \fIifname\fR is a
+mandatory parameter. The link address can be specified by \fIaddress%\fR;
+if it isn't, dhcrelay will use the first non-link-local address configured
+on the interface. The optional \fI#index\fR parameter specifies the
+interface index.
+.TP
+-u [\fIaddress%\fR]\fIifname\fR
+Specifies the ``upper'' network interface for DHCPv6 relay mode: the
+interface to which queries from clients and other relay agents should be
+forwarded. At least one \fB-u\fR option must be included in the command
+line when running in DHCPv6 mode. The interface name \fIifname\fR is a
+mandatory parameter. The destination unicast or multicast address can be
+specified by \fIaddress%\fR; if not specified, the relay agent will forward
+to the DHCPv6 \fIAll_DHCP_Relay_Agents_and_Servers\fR multicast address.
+.PP
+It is possible to specify the same interface with different addresses
+more than once, and even, when the system supports it, to use the same
+interface as both upper and lower interfaces.
+.SH SEE ALSO
+dhclient(8), dhcpd(8), RFC3315, RFC2132, RFC2131.
+.SH BUGS
+.PP
+Using the same interface on both upper and lower sides may cause
+loops, so when running this way, the maximum hop count should be set
+to a low value.
+.PP
+The loopback interface is not (yet) recognized as a valid interface.
+.SH AUTHOR
+.B dhcrelay(8)
+To learn more about Internet Systems Consortium, see
+.B https://www.isc.org
diff --git a/relay/dhcrelay.c b/relay/dhcrelay.c
new file mode 100644
index 0000000..f21f16f
--- /dev/null
+++ b/relay/dhcrelay.c
@@ -0,0 +1,1679 @@
+/* dhcrelay.c
+
+ DHCP/BOOTP Relay Agent. */
+
+/*
+ * Copyright(c) 2004-2011 by Internet Systems Consortium, Inc.("ISC")
+ * Copyright(c) 1997-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <syslog.h>
+#include <sys/time.h>
+
+TIME default_lease_time = 43200; /* 12 hours... */
+TIME max_lease_time = 86400; /* 24 hours... */
+struct tree_cache *global_options[256];
+
+struct option *requested_opts[2];
+
+/* Needed to prevent linking against conflex.c. */
+int lexline;
+int lexchar;
+char *token_line;
+char *tlname;
+
+const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
+isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
+/* False (default) => we write and use a pid file */
+isc_boolean_t no_pid_file = ISC_FALSE;
+
+int bogus_agent_drops = 0; /* Packets dropped because agent option
+ field was specified and we're not relaying
+ packets that already have an agent option
+ specified. */
+int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
+ client, but with a bogus giaddr. */
+int client_packets_relayed = 0; /* Packets relayed from client to server. */
+int server_packet_errors = 0; /* Errors sending packets to servers. */
+int server_packets_relayed = 0; /* Packets relayed from server to client. */
+int client_packet_errors = 0; /* Errors sending packets to clients. */
+
+int add_agent_options = 0; /* If nonzero, add relay agent options. */
+
+int agent_option_errors = 0; /* Number of packets forwarded without
+ agent options because there was no room. */
+int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
+ don't have matching circuit-id's. */
+int corrupt_agent_options = 0; /* Number of packets dropped because
+ relay agent information option was bad. */
+int missing_agent_option = 0; /* Number of packets dropped because no
+ RAI option matching our ID was found. */
+int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
+ did not match any known circuit ID. */
+int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
+ was missing. */
+int max_hop_count = 10; /* Maximum hop count */
+
+#ifdef DHCPv6
+ /* Force use of DHCPv6 interface-id option. */
+isc_boolean_t use_if_id = ISC_FALSE;
+#endif
+
+ /* Maximum size of a packet with agent options added. */
+int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
+
+ /* What to do about packets we're asked to relay that
+ already have a relay option: */
+enum { forward_and_append, /* Forward and append our own relay option. */
+ forward_and_replace, /* Forward, but replace theirs with ours. */
+ forward_untouched, /* Forward without changes. */
+ discard } agent_relay_mode = forward_and_replace;
+
+u_int16_t local_port;
+u_int16_t remote_port;
+
+/* Relay agent server list. */
+struct server_list {
+ struct server_list *next;
+ struct sockaddr_in to;
+} *servers;
+
+#ifdef DHCPv6
+struct stream_list {
+ struct stream_list *next;
+ struct interface_info *ifp;
+ struct sockaddr_in6 link;
+ int id;
+} *downstreams, *upstreams;
+
+static struct stream_list *parse_downstream(char *);
+static struct stream_list *parse_upstream(char *);
+static void setup_streams(void);
+#endif
+
+static void do_relay4(struct interface_info *, struct dhcp_packet *,
+ unsigned int, unsigned int, struct iaddr,
+ struct hardware *);
+static int add_relay_agent_options(struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ struct in_addr);
+static int find_interface_by_agent_option(struct dhcp_packet *,
+ struct interface_info **, u_int8_t *, int);
+static int strip_relay_agent_options(struct interface_info *,
+ struct interface_info **,
+ struct dhcp_packet *, unsigned);
+
+static const char copyright[] =
+"Copyright 2004-2011 Internet Systems Consortium.";
+static const char arr[] = "All rights reserved.";
+static const char message[] =
+"Internet Systems Consortium DHCP Relay Agent";
+static const char url[] =
+"For info, please visit https://www.isc.org/software/dhcp/";
+
+#ifdef DHCPv6
+#define DHCRELAY_USAGE \
+"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
+" [-A <length>] [-c <hops>] [-p <port>]\n" \
+" [-pf <pid-file>] [--no-pid]\n"\
+" [-m append|replace|forward|discard]\n" \
+" [-i interface0 [ ... -i interfaceN]\n" \
+" server0 [ ... serverN]\n\n" \
+" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
+" [-pf <pid-file>] [--no-pid]\n"\
+" -l lower0 [ ... -l lowerN]\n" \
+" -u upper0 [ ... -u upperN]\n" \
+" lower (client link): [address%%]interface[#index]\n" \
+" upper (server link): [address%%]interface"
+#else
+#define DHCRELAY_USAGE \
+"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
+" [-pf <pid-file>] [--no-pid]\n"\
+" [-m append|replace|forward|discard]\n" \
+" [-i interface0 [ ... -i interfaceN]\n" \
+" server0 [ ... serverN]\n\n"
+#endif
+
+static void usage() {
+ log_fatal(DHCRELAY_USAGE);
+}
+
+int
+main(int argc, char **argv) {
+ isc_result_t status;
+ struct servent *ent;
+ struct server_list *sp = NULL;
+ struct interface_info *tmp = NULL;
+ char *service_local = NULL, *service_remote = NULL;
+ u_int16_t port_local = 0, port_remote = 0;
+ int no_daemon = 0, quiet = 0;
+ int fd;
+ int i;
+#ifdef DHCPv6
+ struct stream_list *sl = NULL;
+ int local_family_set = 0;
+#endif
+
+ /* Make sure that file descriptors 0(stdin), 1,(stdout), and
+ 2(stderr) are open. To do this, we assume that when we
+ open a file the lowest available file descriptor is used. */
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 0)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 1)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 2)
+ log_perror = 0; /* No sense logging to /dev/null. */
+ else if (fd != -1)
+ close(fd);
+
+ openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
+
+#if !defined(DEBUG)
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+
+ /* Set up the isc and dns library managers */
+ status = dhcp_context_create();
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't initialize context: %s",
+ isc_result_totext(status));
+
+ /* Set up the OMAPI. */
+ status = omapi_init();
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't initialize OMAPI: %s",
+ isc_result_totext(status));
+
+ /* Set up the OMAPI wrappers for the interface object. */
+ interface_setup();
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-4")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+ } else if (!strcmp(argv[i], "-6")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+#endif
+ } else if (!strcmp(argv[i], "-d")) {
+ no_daemon = 1;
+ } else if (!strcmp(argv[i], "-q")) {
+ quiet = 1;
+ quiet_interface_discovery = 1;
+ } else if (!strcmp(argv[i], "-p")) {
+ if (++i == argc)
+ usage();
+ local_port = validate_port(argv[i]);
+ log_debug("binding to user-specified port %d",
+ ntohs(local_port));
+ } else if (!strcmp(argv[i], "-c")) {
+ int hcount;
+ if (++i == argc)
+ usage();
+ hcount = atoi(argv[i]);
+ if (hcount <= 255)
+ max_hop_count= hcount;
+ else
+ usage();
+ } else if (!strcmp(argv[i], "-i")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ status = interface_allocate(&tmp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ argv[i],
+ isc_result_totext(status));
+ if (++i == argc) {
+ usage();
+ }
+ strcpy(tmp->name, argv[i]);
+ interface_snorf(tmp, INTERFACE_REQUESTED);
+ interface_dereference(&tmp, MDL);
+ } else if (!strcmp(argv[i], "-a")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ add_agent_options = 1;
+ } else if (!strcmp(argv[i], "-A")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ if (++i == argc)
+ usage();
+
+ dhcp_max_agent_option_packet_length = atoi(argv[i]);
+
+ if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
+ log_fatal("%s: packet length exceeds "
+ "longest possible MTU\n",
+ argv[i]);
+ } else if (!strcmp(argv[i], "-m")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ if (++i == argc)
+ usage();
+ if (!strcasecmp(argv[i], "append")) {
+ agent_relay_mode = forward_and_append;
+ } else if (!strcasecmp(argv[i], "replace")) {
+ agent_relay_mode = forward_and_replace;
+ } else if (!strcasecmp(argv[i], "forward")) {
+ agent_relay_mode = forward_untouched;
+ } else if (!strcasecmp(argv[i], "discard")) {
+ agent_relay_mode = discard;
+ } else
+ usage();
+ } else if (!strcmp(argv[i], "-D")) {
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ drop_agent_mismatches = 1;
+#ifdef DHCPv6
+ } else if (!strcmp(argv[i], "-I")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ use_if_id = ISC_TRUE;
+ } else if (!strcmp(argv[i], "-l")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (downstreams != NULL)
+ use_if_id = ISC_TRUE;
+ if (++i == argc)
+ usage();
+ sl = parse_downstream(argv[i]);
+ sl->next = downstreams;
+ downstreams = sl;
+ } else if (!strcmp(argv[i], "-u")) {
+ if (local_family_set && (local_family == AF_INET)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET6;
+ if (++i == argc)
+ usage();
+ sl = parse_upstream(argv[i]);
+ sl->next = upstreams;
+ upstreams = sl;
+#endif
+ } else if (!strcmp(argv[i], "-pf")) {
+ if (++i == argc)
+ usage();
+ path_dhcrelay_pid = argv[i];
+ no_dhcrelay_pid = ISC_TRUE;
+ } else if (!strcmp(argv[i], "--no-pid")) {
+ no_pid_file = ISC_TRUE;
+ } else if (!strcmp(argv[i], "--version")) {
+ log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
+ exit(0);
+ } else if (!strcmp(argv[i], "--help") ||
+ !strcmp(argv[i], "-h")) {
+ log_info(DHCRELAY_USAGE);
+ exit(0);
+ } else if (argv[i][0] == '-') {
+ usage();
+ } else {
+ struct hostent *he;
+ struct in_addr ia, *iap = NULL;
+
+#ifdef DHCPv6
+ if (local_family_set && (local_family == AF_INET6)) {
+ usage();
+ }
+ local_family_set = 1;
+ local_family = AF_INET;
+#endif
+ if (inet_aton(argv[i], &ia)) {
+ iap = &ia;
+ } else {
+ he = gethostbyname(argv[i]);
+ if (!he) {
+ log_error("%s: host unknown", argv[i]);
+ } else {
+ iap = ((struct in_addr *)
+ he->h_addr_list[0]);
+ }
+ }
+
+ if (iap) {
+ sp = ((struct server_list *)
+ dmalloc(sizeof *sp, MDL));
+ if (!sp)
+ log_fatal("no memory for server.\n");
+ sp->next = servers;
+ servers = sp;
+ memcpy(&sp->to.sin_addr, iap, sizeof *iap);
+ }
+ }
+ }
+
+ /*
+ * If the user didn't specify a pid file directly
+ * find one from environment variables or defaults
+ */
+ if (no_dhcrelay_pid == ISC_FALSE) {
+ if (local_family == AF_INET) {
+ path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
+ if (path_dhcrelay_pid == NULL)
+ path_dhcrelay_pid = _PATH_DHCRELAY_PID;
+ }
+#ifdef DHCPv6
+ else {
+ path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
+ if (path_dhcrelay_pid == NULL)
+ path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
+ }
+#endif
+ }
+
+ if (!quiet) {
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info(copyright);
+ log_info(arr);
+ log_info(url);
+ } else {
+ quiet = 0;
+ log_perror = 0;
+ }
+
+ /* Set default port */
+ if (local_family == AF_INET) {
+ service_local = "bootps";
+ service_remote = "bootpc";
+ port_local = htons(67);
+ port_remote = htons(68);
+ }
+#ifdef DHCPv6
+ else {
+ service_local = "dhcpv6-server";
+ service_remote = "dhcpv6-client";
+ port_local = htons(547);
+ port_remote = htons(546);
+ }
+#endif
+
+ if (!local_port) {
+ ent = getservbyname(service_local, "udp");
+ if (ent)
+ local_port = ent->s_port;
+ else
+ local_port = port_local;
+
+ ent = getservbyname(service_remote, "udp");
+ if (ent)
+ remote_port = ent->s_port;
+ else
+ remote_port = port_remote;
+
+ endservent();
+ }
+
+ if (local_family == AF_INET) {
+ /* We need at least one server */
+ if (servers == NULL) {
+ log_fatal("No servers specified.");
+ }
+
+
+ /* Set up the server sockaddrs. */
+ for (sp = servers; sp; sp = sp->next) {
+ sp->to.sin_port = local_port;
+ sp->to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ sp->to.sin_len = sizeof sp->to;
+#endif
+ }
+ }
+#ifdef DHCPv6
+ else {
+ unsigned code;
+
+ /* We need at least one upstream and one downstream interface */
+ if (upstreams == NULL || downstreams == NULL) {
+ log_info("Must specify at least one lower "
+ "and one upper interface.\n");
+ usage();
+ }
+
+ /* Set up the initial dhcp option universe. */
+ initialize_common_option_spaces();
+
+ /* Check requested options. */
+ code = D6O_RELAY_MSG;
+ if (!option_code_hash_lookup(&requested_opts[0],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the RELAY_MSG "
+ "option definition.");
+ code = D6O_INTERFACE_ID;
+ if (!option_code_hash_lookup(&requested_opts[1],
+ dhcpv6_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find the INTERFACE_ID "
+ "option definition.");
+ }
+#endif
+
+ /* Get the current time... */
+ gettimeofday(&cur_tv, NULL);
+
+ /* Discover all the network interfaces. */
+ discover_interfaces(DISCOVER_RELAY);
+
+#ifdef DHCPv6
+ if (local_family == AF_INET6)
+ setup_streams();
+#endif
+
+ /* Become a daemon... */
+ if (!no_daemon) {
+ int pid;
+ FILE *pf;
+ int pfdesc;
+
+ log_perror = 0;
+
+ if ((pid = fork()) < 0)
+ log_fatal("Can't fork daemon: %m");
+ else if (pid)
+ exit(0);
+
+ if (no_pid_file == ISC_FALSE) {
+ pfdesc = open(path_dhcrelay_pid,
+ O_CREAT | O_TRUNC | O_WRONLY, 0644);
+
+ if (pfdesc < 0) {
+ log_error("Can't create %s: %m",
+ path_dhcrelay_pid);
+ } else {
+ pf = fdopen(pfdesc, "w");
+ if (!pf)
+ log_error("Can't fdopen %s: %m",
+ path_dhcrelay_pid);
+ else {
+ fprintf(pf, "%ld\n",(long)getpid());
+ fclose(pf);
+ }
+ }
+ }
+
+ close(0);
+ close(1);
+ close(2);
+ pid = setsid();
+
+ IGNORE_RET (chdir("/"));
+ }
+
+ /* Set up the packet handler... */
+ if (local_family == AF_INET)
+ bootp_packet_handler = do_relay4;
+#ifdef DHCPv6
+ else
+ dhcpv6_packet_handler = do_packet6;
+#endif
+
+ /* Start dispatching packets and timeouts... */
+ dispatch();
+
+ /* Not reached */
+ return (0);
+}
+
+static void
+do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
+ unsigned int length, unsigned int from_port, struct iaddr from,
+ struct hardware *hfrom) {
+ struct server_list *sp;
+ struct sockaddr_in to;
+ struct interface_info *out;
+ struct hardware hto, *htop;
+
+ if (packet->hlen > sizeof packet->chaddr) {
+ log_info("Discarding packet with invalid hlen.");
+ return;
+ }
+
+ if (ip->address_count < 1 || ip->addresses == NULL) {
+ log_info("Discarding packet received on %s interface that "
+ "has no IPv4 address assigned.", ip->name);
+ return;
+ }
+
+ /* Find the interface that corresponds to the giaddr
+ in the packet. */
+ if (packet->giaddr.s_addr) {
+ for (out = interfaces; out; out = out->next) {
+ int i;
+
+ for (i = 0 ; i < out->address_count ; i++ ) {
+ if (out->addresses[i].s_addr ==
+ packet->giaddr.s_addr)
+ i = -1;
+ break;
+ }
+
+ if (i == -1)
+ break;
+ }
+ } else {
+ out = NULL;
+ }
+
+ /* If it's a bootreply, forward it to the client. */
+ if (packet->op == BOOTREPLY) {
+ if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
+ can_unicast_without_arp(out)) {
+ to.sin_addr = packet->yiaddr;
+ to.sin_port = remote_port;
+
+ /* and hardware address is not broadcast */
+ htop = &hto;
+ } else {
+ to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ to.sin_port = remote_port;
+
+ /* hardware address is broadcast */
+ htop = NULL;
+ }
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+
+ memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
+ hto.hbuf[0] = packet->htype;
+ hto.hlen = packet->hlen + 1;
+
+ /* Wipe out the agent relay options and, if possible, figure
+ out which interface to use based on the contents of the
+ option that we put on the request to which the server is
+ replying. */
+ if (!(length =
+ strip_relay_agent_options(ip, &out, packet, length)))
+ return;
+
+ if (!out) {
+ log_error("Packet to bogus giaddr %s.\n",
+ inet_ntoa(packet->giaddr));
+ ++bogus_giaddr_drops;
+ return;
+ }
+
+ if (send_packet(out, NULL, packet, length, out->addresses[0],
+ &to, htop) < 0) {
+ ++server_packet_errors;
+ } else {
+ log_debug("Forwarded BOOTREPLY for %s to %s",
+ print_hw_addr(packet->htype, packet->hlen,
+ packet->chaddr),
+ inet_ntoa(to.sin_addr));
+
+ ++server_packets_relayed;
+ }
+ return;
+ }
+
+ /* If giaddr matches one of our addresses, ignore the packet -
+ we just sent it. */
+ if (out)
+ return;
+
+ /* Add relay agent options if indicated. If something goes wrong,
+ drop the packet. */
+ if (!(length = add_relay_agent_options(ip, packet, length,
+ ip->addresses[0])))
+ return;
+
+ /* If giaddr is not already set, Set it so the server can
+ figure out what net it's from and so that we can later
+ forward the response to the correct net. If it's already
+ set, the response will be sent directly to the relay agent
+ that set giaddr, so we won't see it. */
+ if (!packet->giaddr.s_addr)
+ packet->giaddr = ip->addresses[0];
+ if (packet->hops < max_hop_count)
+ packet->hops = packet->hops + 1;
+ else
+ return;
+
+ /* Otherwise, it's a BOOTREQUEST, so forward it to all the
+ servers. */
+ for (sp = servers; sp; sp = sp->next) {
+ if (send_packet((fallback_interface
+ ? fallback_interface : interfaces),
+ NULL, packet, length, ip->addresses[0],
+ &sp->to, NULL) < 0) {
+ ++client_packet_errors;
+ } else {
+ log_debug("Forwarded BOOTREQUEST for %s to %s",
+ print_hw_addr(packet->htype, packet->hlen,
+ packet->chaddr),
+ inet_ntoa(sp->to.sin_addr));
+ ++client_packets_relayed;
+ }
+ }
+
+}
+
+/* Strip any Relay Agent Information options from the DHCP packet
+ option buffer. If there is a circuit ID suboption, look up the
+ outgoing interface based upon it. */
+
+static int
+strip_relay_agent_options(struct interface_info *in,
+ struct interface_info **out,
+ struct dhcp_packet *packet,
+ unsigned length) {
+ int is_dhcp = 0;
+ u_int8_t *op, *nextop, *sp, *max;
+ int good_agent_option = 0;
+ int status;
+
+ /* If we're not adding agent options to packets, we're not taking
+ them out either. */
+ if (!add_agent_options)
+ return (length);
+
+ /* If there's no cookie, it's a bootp packet, so we should just
+ forward it unchanged. */
+ if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
+ return (length);
+
+ max = ((u_int8_t *)packet) + length;
+ sp = op = &packet->options[4];
+
+ while (op < max) {
+ switch(*op) {
+ /* Skip padding... */
+ case DHO_PAD:
+ if (sp != op)
+ *sp = *op;
+ ++op;
+ ++sp;
+ continue;
+
+ /* If we see a message type, it's a DHCP packet. */
+ case DHO_DHCP_MESSAGE_TYPE:
+ is_dhcp = 1;
+ goto skip;
+ break;
+
+ /* Quit immediately if we hit an End option. */
+ case DHO_END:
+ if (sp != op)
+ *sp++ = *op++;
+ goto out;
+
+ case DHO_DHCP_AGENT_OPTIONS:
+ /* We shouldn't see a relay agent option in a
+ packet before we've seen the DHCP packet type,
+ but if we do, we have to leave it alone. */
+ if (!is_dhcp)
+ goto skip;
+
+ /* Do not process an agent option if it exceeds the
+ * buffer. Fail this packet.
+ */
+ nextop = op + op[1] + 2;
+ if (nextop > max)
+ return (0);
+
+ status = find_interface_by_agent_option(packet,
+ out, op + 2,
+ op[1]);
+ if (status == -1 && drop_agent_mismatches)
+ return (0);
+ if (status)
+ good_agent_option = 1;
+ op = nextop;
+ break;
+
+ skip:
+ /* Skip over other options. */
+ default:
+ /* Fail if processing this option will exceed the
+ * buffer(op[1] is malformed).
+ */
+ nextop = op + op[1] + 2;
+ if (nextop > max)
+ return (0);
+
+ if (sp != op) {
+ memmove(sp, op, op[1] + 2);
+ sp += op[1] + 2;
+ op = nextop;
+ } else
+ op = sp = nextop;
+
+ break;
+ }
+ }
+ out:
+
+ /* If it's not a DHCP packet, we're not supposed to touch it. */
+ if (!is_dhcp)
+ return (length);
+
+ /* If none of the agent options we found matched, or if we didn't
+ find any agent options, count this packet as not having any
+ matching agent options, and if we're relying on agent options
+ to determine the outgoing interface, drop the packet. */
+
+ if (!good_agent_option) {
+ ++missing_agent_option;
+ if (drop_agent_mismatches)
+ return (0);
+ }
+
+ /* Adjust the length... */
+ if (sp != op) {
+ length = sp -((u_int8_t *)packet);
+
+ /* Make sure the packet isn't short(this is unlikely,
+ but WTH) */
+ if (length < BOOTP_MIN_LEN) {
+ memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
+ length = BOOTP_MIN_LEN;
+ }
+ }
+ return (length);
+}
+
+
+/* Find an interface that matches the circuit ID specified in the
+ Relay Agent Information option. If one is found, store it through
+ the pointer given; otherwise, leave the existing pointer alone.
+
+ We actually deviate somewhat from the current specification here:
+ if the option buffer is corrupt, we suggest that the caller not
+ respond to this packet. If the circuit ID doesn't match any known
+ interface, we suggest that the caller to drop the packet. Only if
+ we find a circuit ID that matches an existing interface do we tell
+ the caller to go ahead and process the packet. */
+
+static int
+find_interface_by_agent_option(struct dhcp_packet *packet,
+ struct interface_info **out,
+ u_int8_t *buf, int len) {
+ int i = 0;
+ u_int8_t *circuit_id = 0;
+ unsigned circuit_id_len = 0;
+ struct interface_info *ip;
+
+ while (i < len) {
+ /* If the next agent option overflows the end of the
+ packet, the agent option buffer is corrupt. */
+ if (i + 1 == len ||
+ i + buf[i + 1] + 2 > len) {
+ ++corrupt_agent_options;
+ return (-1);
+ }
+ switch(buf[i]) {
+ /* Remember where the circuit ID is... */
+ case RAI_CIRCUIT_ID:
+ circuit_id = &buf[i + 2];
+ circuit_id_len = buf[i + 1];
+ i += circuit_id_len + 2;
+ continue;
+
+ default:
+ i += buf[i + 1] + 2;
+ break;
+ }
+ }
+
+ /* If there's no circuit ID, it's not really ours, tell the caller
+ it's no good. */
+ if (!circuit_id) {
+ ++missing_circuit_id;
+ return (-1);
+ }
+
+ /* Scan the interface list looking for an interface whose
+ name matches the one specified in circuit_id. */
+
+ for (ip = interfaces; ip; ip = ip->next) {
+ if (ip->circuit_id &&
+ ip->circuit_id_len == circuit_id_len &&
+ !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
+ break;
+ }
+
+ /* If we got a match, use it. */
+ if (ip) {
+ *out = ip;
+ return (1);
+ }
+
+ /* If we didn't get a match, the circuit ID was bogus. */
+ ++bad_circuit_id;
+ return (-1);
+}
+
+/*
+ * Examine a packet to see if it's a candidate to have a Relay
+ * Agent Information option tacked onto its tail. If it is, tack
+ * the option on.
+ */
+static int
+add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
+ unsigned length, struct in_addr giaddr) {
+ int is_dhcp = 0, mms;
+ unsigned optlen;
+ u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
+
+ /* If we're not adding agent options to packets, we can skip
+ this. */
+ if (!add_agent_options)
+ return (length);
+
+ /* If there's no cookie, it's a bootp packet, so we should just
+ forward it unchanged. */
+ if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
+ return (length);
+
+ max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
+
+ /* Commence processing after the cookie. */
+ sp = op = &packet->options[4];
+
+ while (op < max) {
+ switch(*op) {
+ /* Skip padding... */
+ case DHO_PAD:
+ /* Remember the first pad byte so we can commandeer
+ * padded space.
+ *
+ * XXX: Is this really a good idea? Sure, we can
+ * seemingly reduce the packet while we're looking,
+ * but if the packet was signed by the client then
+ * this padding is part of the checksum(RFC3118),
+ * and its nonpresence would break authentication.
+ */
+ if (end_pad == NULL)
+ end_pad = sp;
+
+ if (sp != op)
+ *sp++ = *op++;
+ else
+ sp = ++op;
+
+ continue;
+
+ /* If we see a message type, it's a DHCP packet. */
+ case DHO_DHCP_MESSAGE_TYPE:
+ is_dhcp = 1;
+ goto skip;
+
+ /*
+ * If there's a maximum message size option, we
+ * should pay attention to it
+ */
+ case DHO_DHCP_MAX_MESSAGE_SIZE:
+ mms = ntohs(*(op + 2));
+ if (mms < dhcp_max_agent_option_packet_length &&
+ mms >= DHCP_MTU_MIN)
+ max = ((u_int8_t *)packet) + mms;
+ goto skip;
+
+ /* Quit immediately if we hit an End option. */
+ case DHO_END:
+ goto out;
+
+ case DHO_DHCP_AGENT_OPTIONS:
+ /* We shouldn't see a relay agent option in a
+ packet before we've seen the DHCP packet type,
+ but if we do, we have to leave it alone. */
+ if (!is_dhcp)
+ goto skip;
+
+ end_pad = NULL;
+
+ /* There's already a Relay Agent Information option
+ in this packet. How embarrassing. Decide what
+ to do based on the mode the user specified. */
+
+ switch(agent_relay_mode) {
+ case forward_and_append:
+ goto skip;
+ case forward_untouched:
+ return (length);
+ case discard:
+ return (0);
+ case forward_and_replace:
+ default:
+ break;
+ }
+
+ /* Skip over the agent option and start copying
+ if we aren't copying already. */
+ op += op[1] + 2;
+ break;
+
+ skip:
+ /* Skip over other options. */
+ default:
+ /* Fail if processing this option will exceed the
+ * buffer(op[1] is malformed).
+ */
+ nextop = op + op[1] + 2;
+ if (nextop > max)
+ return (0);
+
+ end_pad = NULL;
+
+ if (sp != op) {
+ memmove(sp, op, op[1] + 2);
+ sp += op[1] + 2;
+ op = nextop;
+ } else
+ op = sp = nextop;
+
+ break;
+ }
+ }
+ out:
+
+ /* If it's not a DHCP packet, we're not supposed to touch it. */
+ if (!is_dhcp)
+ return (length);
+
+ /* If the packet was padded out, we can store the agent option
+ at the beginning of the padding. */
+
+ if (end_pad != NULL)
+ sp = end_pad;
+
+ /* Remember where the end of the packet was after parsing
+ it. */
+ op = sp;
+
+ /* Sanity check. Had better not ever happen. */
+ if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
+ log_fatal("Circuit ID length %d out of range [1-255] on "
+ "%s\n", ip->circuit_id_len, ip->name);
+ optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
+
+ if (ip->remote_id) {
+ if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
+ log_fatal("Remote ID length %d out of range [1-255] "
+ "on %s\n", ip->circuit_id_len, ip->name);
+ optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
+ }
+
+ /* We do not support relay option fragmenting(multiple options to
+ * support an option data exceeding 255 bytes).
+ */
+ if ((optlen < 3) ||(optlen > 255))
+ log_fatal("Total agent option length(%u) out of range "
+ "[3 - 255] on %s\n", optlen, ip->name);
+
+ /*
+ * Is there room for the option, its code+len, and DHO_END?
+ * If not, forward without adding the option.
+ */
+ if (max - sp >= optlen + 3) {
+ log_debug("Adding %d-byte relay agent option", optlen + 3);
+
+ /* Okay, cons up *our* Relay Agent Information option. */
+ *sp++ = DHO_DHCP_AGENT_OPTIONS;
+ *sp++ = optlen;
+
+ /* Copy in the circuit id... */
+ *sp++ = RAI_CIRCUIT_ID;
+ *sp++ = ip->circuit_id_len;
+ memcpy(sp, ip->circuit_id, ip->circuit_id_len);
+ sp += ip->circuit_id_len;
+
+ /* Copy in remote ID... */
+ if (ip->remote_id) {
+ *sp++ = RAI_REMOTE_ID;
+ *sp++ = ip->remote_id_len;
+ memcpy(sp, ip->remote_id, ip->remote_id_len);
+ sp += ip->remote_id_len;
+ }
+ } else {
+ ++agent_option_errors;
+ log_error("No room in packet (used %d of %d) "
+ "for %d-byte relay agent option: omitted",
+ (int) (sp - ((u_int8_t *) packet)),
+ (int) (max - ((u_int8_t *) packet)),
+ optlen + 3);
+ }
+
+ /*
+ * Deposit an END option unless the packet is full (shouldn't
+ * be possible).
+ */
+ if (sp < max)
+ *sp++ = DHO_END;
+
+ /* Recalculate total packet length. */
+ length = sp -((u_int8_t *)packet);
+
+ /* Make sure the packet isn't short(this is unlikely, but WTH) */
+ if (length < BOOTP_MIN_LEN) {
+ memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
+ return (BOOTP_MIN_LEN);
+ }
+
+ return (length);
+}
+
+#ifdef DHCPv6
+/*
+ * Parse a downstream argument: [address%]interface[#index].
+ */
+static struct stream_list *
+parse_downstream(char *arg) {
+ struct stream_list *dp, *up;
+ struct interface_info *ifp = NULL;
+ char *ifname, *addr, *iid;
+ isc_result_t status;
+
+ if (!supports_multiple_interfaces(ifp) &&
+ (downstreams != NULL))
+ log_fatal("No support for multiple interfaces.");
+
+ /* Decode the argument. */
+ ifname = strchr(arg, '%');
+ if (ifname == NULL) {
+ ifname = arg;
+ addr = NULL;
+ } else {
+ *ifname++ = '\0';
+ addr = arg;
+ }
+ iid = strchr(ifname, '#');
+ if (iid != NULL) {
+ *iid++ = '\0';
+ }
+ if (strlen(ifname) >= sizeof(ifp->name)) {
+ log_error("Interface name '%s' too long", ifname);
+ usage();
+ }
+
+ /* Don't declare twice. */
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (strcmp(ifname, dp->ifp->name) == 0)
+ log_fatal("Down interface '%s' declared twice.",
+ ifname);
+ }
+
+ /* Share with up side? */
+ for (up = upstreams; up; up = up->next) {
+ if (strcmp(ifname, up->ifp->name) == 0) {
+ log_info("Interface '%s' is both down and up.",
+ ifname);
+ ifp = up->ifp;
+ break;
+ }
+ }
+
+ /* New interface. */
+ if (ifp == NULL) {
+ status = interface_allocate(&ifp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ arg, isc_result_totext(status));
+ strcpy(ifp->name, ifname);
+ if (interfaces) {
+ interface_reference(&ifp->next, interfaces, MDL);
+ interface_dereference(&interfaces, MDL);
+ }
+ interface_reference(&interfaces, ifp, MDL);
+ ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
+ }
+
+ /* New downstream. */
+ dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
+ if (!dp)
+ log_fatal("No memory for downstream.");
+ dp->ifp = ifp;
+ if (iid != NULL) {
+ dp->id = atoi(iid);
+ } else {
+ dp->id = -1;
+ }
+ /* !addr case handled by setup. */
+ if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
+ log_fatal("Bad link address '%s'", addr);
+
+ return dp;
+}
+
+/*
+ * Parse an upstream argument: [address]%interface.
+ */
+static struct stream_list *
+parse_upstream(char *arg) {
+ struct stream_list *up, *dp;
+ struct interface_info *ifp = NULL;
+ char *ifname, *addr;
+ isc_result_t status;
+
+ /* Decode the argument. */
+ ifname = strchr(arg, '%');
+ if (ifname == NULL) {
+ ifname = arg;
+ addr = All_DHCP_Servers;
+ } else {
+ *ifname++ = '\0';
+ addr = arg;
+ }
+ if (strlen(ifname) >= sizeof(ifp->name)) {
+ log_fatal("Interface name '%s' too long", ifname);
+ }
+
+ /* Shared up interface? */
+ for (up = upstreams; up; up = up->next) {
+ if (strcmp(ifname, up->ifp->name) == 0) {
+ ifp = up->ifp;
+ break;
+ }
+ }
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (strcmp(ifname, dp->ifp->name) == 0) {
+ ifp = dp->ifp;
+ break;
+ }
+ }
+
+ /* New interface. */
+ if (ifp == NULL) {
+ status = interface_allocate(&ifp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal("%s: interface_allocate: %s",
+ arg, isc_result_totext(status));
+ strcpy(ifp->name, ifname);
+ if (interfaces) {
+ interface_reference(&ifp->next, interfaces, MDL);
+ interface_dereference(&interfaces, MDL);
+ }
+ interface_reference(&interfaces, ifp, MDL);
+ ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
+ }
+
+ /* New upstream. */
+ up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
+ if (up == NULL)
+ log_fatal("No memory for upstream.");
+
+ up->ifp = ifp;
+
+ if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
+ log_fatal("Bad address %s", addr);
+
+ return up;
+}
+
+/*
+ * Setup downstream interfaces.
+ */
+static void
+setup_streams(void) {
+ struct stream_list *dp, *up;
+ int i;
+ isc_boolean_t link_is_set;
+
+ for (dp = downstreams; dp; dp = dp->next) {
+ /* Check interface */
+ if (dp->ifp->v6address_count == 0)
+ log_fatal("Interface '%s' has no IPv6 addresses.",
+ dp->ifp->name);
+
+ /* Check/set link. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
+ link_is_set = ISC_FALSE;
+ else
+ link_is_set = ISC_TRUE;
+ for (i = 0; i < dp->ifp->v6address_count; i++) {
+ if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
+ continue;
+ if (!link_is_set)
+ break;
+ if (!memcmp(&dp->ifp->v6addresses[i],
+ &dp->link.sin6_addr,
+ sizeof(dp->link.sin6_addr)))
+ break;
+ }
+ if (i == dp->ifp->v6address_count)
+ log_fatal("Can't find link address for interface '%s'.",
+ dp->ifp->name);
+ if (!link_is_set)
+ memcpy(&dp->link.sin6_addr,
+ &dp->ifp->v6addresses[i],
+ sizeof(dp->link.sin6_addr));
+
+ /* Set interface-id. */
+ if (dp->id == -1)
+ dp->id = dp->ifp->index;
+ }
+
+ for (up = upstreams; up; up = up->next) {
+ up->link.sin6_port = local_port;
+ up->link.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ up->link.sin6_len = sizeof(up->link);
+#endif
+
+ if (up->ifp->v6address_count == 0)
+ log_fatal("Interface '%s' has no IPv6 addresses.",
+ up->ifp->name);
+ }
+}
+
+/*
+ * Add DHCPv6 agent options here.
+ */
+static const int required_forw_opts[] = {
+ D6O_INTERFACE_ID,
+ D6O_RELAY_MSG,
+ 0
+};
+
+/*
+ * Process a packet upwards, i.e., from client to server.
+ */
+static void
+process_up6(struct packet *packet, struct stream_list *dp) {
+ char forw_data[65535];
+ unsigned cursor;
+ struct dhcpv6_relay_packet *relay;
+ struct option_state *opts;
+ struct stream_list *up;
+
+ /* Check if the message should be relayed to the server. */
+ switch (packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ case DHCPV6_REQUEST:
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ case DHCPV6_RELEASE:
+ case DHCPV6_DECLINE:
+ case DHCPV6_INFORMATION_REQUEST:
+ case DHCPV6_RELAY_FORW:
+ case DHCPV6_LEASEQUERY:
+ log_info("Relaying %s from %s port %d going up.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ break;
+
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_REPLY:
+ case DHCPV6_RECONFIGURE:
+ case DHCPV6_RELAY_REPL:
+ case DHCPV6_LEASEQUERY_REPLY:
+ log_info("Discarding %s from %s port %d going up.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+
+ default:
+ log_info("Unknown %d type from %s port %d going up.",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+ }
+
+ /* Build the relay-forward header. */
+ relay = (struct dhcpv6_relay_packet *) forw_data;
+ cursor = offsetof(struct dhcpv6_relay_packet, options);
+ relay->msg_type = DHCPV6_RELAY_FORW;
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
+ if (packet->dhcpv6_hop_count >= max_hop_count) {
+ log_info("Hop count exceeded,");
+ return;
+ }
+ relay->hop_count = packet->dhcpv6_hop_count + 1;
+ if (dp) {
+ memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
+ } else {
+ /* On smart relay add: && !global. */
+ if (!use_if_id && downstreams->next) {
+ log_info("Shan't get back the interface.");
+ return;
+ }
+ memset(&relay->link_address, 0, 16);
+ }
+ } else {
+ relay->hop_count = 0;
+ if (!dp)
+ return;
+ memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
+ }
+ memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
+
+ /* Get an option state. */
+ opts = NULL;
+ if (!option_state_allocate(&opts, MDL)) {
+ log_fatal("No memory for upwards options.");
+ }
+
+ /* Add an interface-id (if used). */
+ if (use_if_id) {
+ int if_id;
+
+ if (dp) {
+ if_id = dp->id;
+ } else if (!downstreams->next) {
+ if_id = downstreams->id;
+ } else {
+ log_info("Don't know the interface.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+
+ if (!save_option_buffer(&dhcpv6_universe, opts,
+ NULL, (unsigned char *) &if_id,
+ sizeof(int),
+ D6O_INTERFACE_ID, 0)) {
+ log_error("Can't save interface-id.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+ }
+
+ /* Add the relay-msg carrying the packet. */
+ if (!save_option_buffer(&dhcpv6_universe, opts,
+ NULL, (unsigned char *) packet->raw,
+ packet->packet_length,
+ D6O_RELAY_MSG, 0)) {
+ log_error("Can't save relay-msg.");
+ option_state_dereference(&opts, MDL);
+ return;
+ }
+
+ /* Finish the relay-forward message. */
+ cursor += store_options6(forw_data + cursor,
+ sizeof(forw_data) - cursor,
+ opts, packet,
+ required_forw_opts, NULL);
+ option_state_dereference(&opts, MDL);
+
+ /* Send it to all upstreams. */
+ for (up = upstreams; up; up = up->next) {
+ send_packet6(up->ifp, (unsigned char *) forw_data,
+ (size_t) cursor, &up->link);
+ }
+}
+
+/*
+ * Process a packet downwards, i.e., from server to client.
+ */
+static void
+process_down6(struct packet *packet) {
+ struct stream_list *dp;
+ struct option_cache *oc;
+ struct data_string relay_msg;
+ const struct dhcpv6_packet *msg;
+ struct data_string if_id;
+ struct sockaddr_in6 to;
+ struct iaddr peer;
+
+ /* The packet must be a relay-reply message. */
+ if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
+ if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
+ log_info("Discarding %s from %s port %d going down.",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ else
+ log_info("Unknown %d type from %s port %d going down.",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ return;
+ }
+
+ /* Inits. */
+ memset(&relay_msg, 0, sizeof(relay_msg));
+ memset(&if_id, 0, sizeof(if_id));
+ memset(&to, 0, sizeof(to));
+ to.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ to.sin6_len = sizeof(to);
+#endif
+ to.sin6_port = remote_port;
+ peer.len = 16;
+
+ /* Get the relay-msg option (carrying the message to relay). */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
+ if (oc == NULL) {
+ log_info("No relay-msg.");
+ return;
+ }
+ if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
+ log_error("Can't evaluate relay-msg.");
+ return;
+ }
+ msg = (const struct dhcpv6_packet *) relay_msg.data;
+
+ /* Get the interface-id (if exists) and the downstream. */
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_INTERFACE_ID);
+ if (oc != NULL) {
+ int if_index;
+
+ if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (if_id.len != sizeof(int))) {
+ log_info("Can't evaluate interface-id.");
+ goto cleanup;
+ }
+ memcpy(&if_index, if_id.data, sizeof(int));
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (dp->id == if_index)
+ break;
+ }
+ } else {
+ if (use_if_id) {
+ /* Require an interface-id. */
+ log_info("No interface-id.");
+ goto cleanup;
+ }
+ for (dp = downstreams; dp; dp = dp->next) {
+ /* Get the first matching one. */
+ if (!memcmp(&dp->link.sin6_addr,
+ &packet->dhcpv6_link_address,
+ sizeof(struct in6_addr)))
+ break;
+ }
+ }
+ /* Why bother when there is no choice. */
+ if (!dp && !downstreams->next)
+ dp = downstreams;
+ if (!dp) {
+ log_info("Can't find the down interface.");
+ goto cleanup;
+ }
+ memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
+ to.sin6_addr = packet->dhcpv6_peer_address;
+
+ /* Check if we should relay the carried message. */
+ switch (msg->msg_type) {
+ /* Relay-Reply of for another relay, not a client. */
+ case DHCPV6_RELAY_REPL:
+ to.sin6_port = local_port;
+ /* Fall into: */
+
+ case DHCPV6_ADVERTISE:
+ case DHCPV6_REPLY:
+ case DHCPV6_RECONFIGURE:
+ case DHCPV6_RELAY_FORW:
+ case DHCPV6_LEASEQUERY_REPLY:
+ log_info("Relaying %s to %s port %d down.",
+ dhcpv6_type_names[msg->msg_type],
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ break;
+
+ case DHCPV6_SOLICIT:
+ case DHCPV6_REQUEST:
+ case DHCPV6_CONFIRM:
+ case DHCPV6_RENEW:
+ case DHCPV6_REBIND:
+ case DHCPV6_RELEASE:
+ case DHCPV6_DECLINE:
+ case DHCPV6_INFORMATION_REQUEST:
+ case DHCPV6_LEASEQUERY:
+ log_info("Discarding %s to %s port %d down.",
+ dhcpv6_type_names[msg->msg_type],
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ goto cleanup;
+
+ default:
+ log_info("Unknown %d type to %s port %d down.",
+ msg->msg_type,
+ piaddr(peer),
+ ntohs(to.sin6_port));
+ goto cleanup;
+ }
+
+ /* Send the message to the downstream. */
+ send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
+ (size_t) relay_msg.len, &to);
+
+ cleanup:
+ if (relay_msg.data != NULL)
+ data_string_forget(&relay_msg, MDL);
+ if (if_id.data != NULL)
+ data_string_forget(&if_id, MDL);
+}
+
+/*
+ * Called by the dispatch packet handler with a decoded packet.
+ */
+void
+dhcpv6(struct packet *packet) {
+ struct stream_list *dp;
+
+ /* Try all relay-replies downwards. */
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
+ process_down6(packet);
+ return;
+ }
+ /* Others are candidates to go up if they come from down. */
+ for (dp = downstreams; dp; dp = dp->next) {
+ if (packet->interface != dp->ifp)
+ continue;
+ process_up6(packet, dp);
+ return;
+ }
+ /* Relay-forward could work from an unknown interface. */
+ if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
+ process_up6(packet, NULL);
+ return;
+ }
+
+ log_info("Can't process packet from interface '%s'.",
+ packet->interface->name);
+}
+#endif
+
+/* Stub routines needed for linking with DHCP libraries. */
+void
+bootp(struct packet *packet) {
+ return;
+}
+
+void
+dhcp(struct packet *packet) {
+ return;
+}
+
+void
+classify(struct packet *p, struct class *c) {
+ return;
+}
+
+int
+check_collection(struct packet *p, struct lease *l, struct collection *c) {
+ return 0;
+}
+
+isc_result_t
+find_class(struct class **class, const char *c1, const char *c2, int i) {
+ return ISC_R_NOTFOUND;
+}
+
+int
+parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
+ return 0;
+}
+
+isc_result_t
+dhcp_set_control_state(control_object_state_t oldstate,
+ control_object_state_t newstate) {
+ return ISC_R_SUCCESS;
+}
diff --git a/server/Makefile.am b/server/Makefile.am
new file mode 100644
index 0000000..cdfaf47
--- /dev/null
+++ b/server/Makefile.am
@@ -0,0 +1,16 @@
+AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"'
+
+dist_sysconf_DATA = dhcpd.conf
+sbin_PROGRAMS = dhcpd
+dhcpd_SOURCES = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \
+ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c \
+ dhcpv6.c mdb6.c ldap.c ldap_casa.c
+
+dhcpd_CFLAGS = $(LDAP_CFLAGS)
+dhcpd_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../dhcpctl/libdhcpctl.a ../bind/lib/libdns.a \
+ ../bind/lib/libisc.a
+
+man_MANS = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5
+EXTRA_DIST = $(man_MANS)
+
diff --git a/server/Makefile.in b/server/Makefile.in
new file mode 100644
index 0000000..9c808c0
--- /dev/null
+++ b/server/Makefile.in
@@ -0,0 +1,817 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+sbin_PROGRAMS = dhcpd$(EXEEXT)
+subdir = server
+DIST_COMMON = $(dist_sysconf_DATA) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(sysconfdir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(sbin_PROGRAMS)
+am_dhcpd_OBJECTS = dhcpd-dhcpd.$(OBJEXT) dhcpd-dhcp.$(OBJEXT) \
+ dhcpd-bootp.$(OBJEXT) dhcpd-confpars.$(OBJEXT) \
+ dhcpd-db.$(OBJEXT) dhcpd-class.$(OBJEXT) \
+ dhcpd-failover.$(OBJEXT) dhcpd-omapi.$(OBJEXT) \
+ dhcpd-mdb.$(OBJEXT) dhcpd-stables.$(OBJEXT) \
+ dhcpd-salloc.$(OBJEXT) dhcpd-ddns.$(OBJEXT) \
+ dhcpd-dhcpleasequery.$(OBJEXT) dhcpd-dhcpv6.$(OBJEXT) \
+ dhcpd-mdb6.$(OBJEXT) dhcpd-ldap.$(OBJEXT) \
+ dhcpd-ldap_casa.$(OBJEXT)
+dhcpd_OBJECTS = $(am_dhcpd_OBJECTS)
+dhcpd_DEPENDENCIES = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../dhcpctl/libdhcpctl.a ../bind/lib/libdns.a \
+ ../bind/lib/libisc.a
+dhcpd_LINK = $(CCLD) $(dhcpd_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(dhcpd_SOURCES)
+DIST_SOURCES = $(dhcpd_SOURCES)
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+dist_sysconfDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(dist_sysconf_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"'
+dist_sysconf_DATA = dhcpd.conf
+dhcpd_SOURCES = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \
+ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c \
+ dhcpv6.c mdb6.c ldap.c ldap_casa.c
+
+dhcpd_CFLAGS = $(LDAP_CFLAGS)
+dhcpd_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \
+ ../dhcpctl/libdhcpctl.a ../bind/lib/libdns.a \
+ ../bind/lib/libisc.a
+
+man_MANS = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5
+EXTRA_DIST = $(man_MANS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign server/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign server/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+dhcpd$(EXEEXT): $(dhcpd_OBJECTS) $(dhcpd_DEPENDENCIES)
+ @rm -f dhcpd$(EXEEXT)
+ $(dhcpd_LINK) $(dhcpd_OBJECTS) $(dhcpd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-bootp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-class.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-confpars.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-db.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ddns.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpleasequery.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-dhcpv6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-failover.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_casa.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-omapi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-salloc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-stables.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+dhcpd-dhcpd.o: dhcpd.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpd.o -MD -MP -MF $(DEPDIR)/dhcpd-dhcpd.Tpo -c -o dhcpd-dhcpd.o `test -f 'dhcpd.c' || echo '$(srcdir)/'`dhcpd.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpd.Tpo $(DEPDIR)/dhcpd-dhcpd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpd.c' object='dhcpd-dhcpd.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpd.o `test -f 'dhcpd.c' || echo '$(srcdir)/'`dhcpd.c
+
+dhcpd-dhcpd.obj: dhcpd.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpd.obj -MD -MP -MF $(DEPDIR)/dhcpd-dhcpd.Tpo -c -o dhcpd-dhcpd.obj `if test -f 'dhcpd.c'; then $(CYGPATH_W) 'dhcpd.c'; else $(CYGPATH_W) '$(srcdir)/dhcpd.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpd.Tpo $(DEPDIR)/dhcpd-dhcpd.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpd.c' object='dhcpd-dhcpd.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpd.obj `if test -f 'dhcpd.c'; then $(CYGPATH_W) 'dhcpd.c'; else $(CYGPATH_W) '$(srcdir)/dhcpd.c'; fi`
+
+dhcpd-dhcp.o: dhcp.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcp.o -MD -MP -MF $(DEPDIR)/dhcpd-dhcp.Tpo -c -o dhcpd-dhcp.o `test -f 'dhcp.c' || echo '$(srcdir)/'`dhcp.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcp.Tpo $(DEPDIR)/dhcpd-dhcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcp.c' object='dhcpd-dhcp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcp.o `test -f 'dhcp.c' || echo '$(srcdir)/'`dhcp.c
+
+dhcpd-dhcp.obj: dhcp.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcp.obj -MD -MP -MF $(DEPDIR)/dhcpd-dhcp.Tpo -c -o dhcpd-dhcp.obj `if test -f 'dhcp.c'; then $(CYGPATH_W) 'dhcp.c'; else $(CYGPATH_W) '$(srcdir)/dhcp.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcp.Tpo $(DEPDIR)/dhcpd-dhcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcp.c' object='dhcpd-dhcp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcp.obj `if test -f 'dhcp.c'; then $(CYGPATH_W) 'dhcp.c'; else $(CYGPATH_W) '$(srcdir)/dhcp.c'; fi`
+
+dhcpd-bootp.o: bootp.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-bootp.o -MD -MP -MF $(DEPDIR)/dhcpd-bootp.Tpo -c -o dhcpd-bootp.o `test -f 'bootp.c' || echo '$(srcdir)/'`bootp.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-bootp.Tpo $(DEPDIR)/dhcpd-bootp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bootp.c' object='dhcpd-bootp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-bootp.o `test -f 'bootp.c' || echo '$(srcdir)/'`bootp.c
+
+dhcpd-bootp.obj: bootp.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-bootp.obj -MD -MP -MF $(DEPDIR)/dhcpd-bootp.Tpo -c -o dhcpd-bootp.obj `if test -f 'bootp.c'; then $(CYGPATH_W) 'bootp.c'; else $(CYGPATH_W) '$(srcdir)/bootp.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-bootp.Tpo $(DEPDIR)/dhcpd-bootp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bootp.c' object='dhcpd-bootp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-bootp.obj `if test -f 'bootp.c'; then $(CYGPATH_W) 'bootp.c'; else $(CYGPATH_W) '$(srcdir)/bootp.c'; fi`
+
+dhcpd-confpars.o: confpars.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-confpars.o -MD -MP -MF $(DEPDIR)/dhcpd-confpars.Tpo -c -o dhcpd-confpars.o `test -f 'confpars.c' || echo '$(srcdir)/'`confpars.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-confpars.Tpo $(DEPDIR)/dhcpd-confpars.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='confpars.c' object='dhcpd-confpars.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-confpars.o `test -f 'confpars.c' || echo '$(srcdir)/'`confpars.c
+
+dhcpd-confpars.obj: confpars.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-confpars.obj -MD -MP -MF $(DEPDIR)/dhcpd-confpars.Tpo -c -o dhcpd-confpars.obj `if test -f 'confpars.c'; then $(CYGPATH_W) 'confpars.c'; else $(CYGPATH_W) '$(srcdir)/confpars.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-confpars.Tpo $(DEPDIR)/dhcpd-confpars.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='confpars.c' object='dhcpd-confpars.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-confpars.obj `if test -f 'confpars.c'; then $(CYGPATH_W) 'confpars.c'; else $(CYGPATH_W) '$(srcdir)/confpars.c'; fi`
+
+dhcpd-db.o: db.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-db.o -MD -MP -MF $(DEPDIR)/dhcpd-db.Tpo -c -o dhcpd-db.o `test -f 'db.c' || echo '$(srcdir)/'`db.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-db.Tpo $(DEPDIR)/dhcpd-db.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='db.c' object='dhcpd-db.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-db.o `test -f 'db.c' || echo '$(srcdir)/'`db.c
+
+dhcpd-db.obj: db.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-db.obj -MD -MP -MF $(DEPDIR)/dhcpd-db.Tpo -c -o dhcpd-db.obj `if test -f 'db.c'; then $(CYGPATH_W) 'db.c'; else $(CYGPATH_W) '$(srcdir)/db.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-db.Tpo $(DEPDIR)/dhcpd-db.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='db.c' object='dhcpd-db.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-db.obj `if test -f 'db.c'; then $(CYGPATH_W) 'db.c'; else $(CYGPATH_W) '$(srcdir)/db.c'; fi`
+
+dhcpd-class.o: class.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-class.o -MD -MP -MF $(DEPDIR)/dhcpd-class.Tpo -c -o dhcpd-class.o `test -f 'class.c' || echo '$(srcdir)/'`class.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-class.Tpo $(DEPDIR)/dhcpd-class.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='class.c' object='dhcpd-class.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-class.o `test -f 'class.c' || echo '$(srcdir)/'`class.c
+
+dhcpd-class.obj: class.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-class.obj -MD -MP -MF $(DEPDIR)/dhcpd-class.Tpo -c -o dhcpd-class.obj `if test -f 'class.c'; then $(CYGPATH_W) 'class.c'; else $(CYGPATH_W) '$(srcdir)/class.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-class.Tpo $(DEPDIR)/dhcpd-class.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='class.c' object='dhcpd-class.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-class.obj `if test -f 'class.c'; then $(CYGPATH_W) 'class.c'; else $(CYGPATH_W) '$(srcdir)/class.c'; fi`
+
+dhcpd-failover.o: failover.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-failover.o -MD -MP -MF $(DEPDIR)/dhcpd-failover.Tpo -c -o dhcpd-failover.o `test -f 'failover.c' || echo '$(srcdir)/'`failover.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-failover.Tpo $(DEPDIR)/dhcpd-failover.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='failover.c' object='dhcpd-failover.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-failover.o `test -f 'failover.c' || echo '$(srcdir)/'`failover.c
+
+dhcpd-failover.obj: failover.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-failover.obj -MD -MP -MF $(DEPDIR)/dhcpd-failover.Tpo -c -o dhcpd-failover.obj `if test -f 'failover.c'; then $(CYGPATH_W) 'failover.c'; else $(CYGPATH_W) '$(srcdir)/failover.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-failover.Tpo $(DEPDIR)/dhcpd-failover.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='failover.c' object='dhcpd-failover.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-failover.obj `if test -f 'failover.c'; then $(CYGPATH_W) 'failover.c'; else $(CYGPATH_W) '$(srcdir)/failover.c'; fi`
+
+dhcpd-omapi.o: omapi.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-omapi.o -MD -MP -MF $(DEPDIR)/dhcpd-omapi.Tpo -c -o dhcpd-omapi.o `test -f 'omapi.c' || echo '$(srcdir)/'`omapi.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-omapi.Tpo $(DEPDIR)/dhcpd-omapi.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='omapi.c' object='dhcpd-omapi.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-omapi.o `test -f 'omapi.c' || echo '$(srcdir)/'`omapi.c
+
+dhcpd-omapi.obj: omapi.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-omapi.obj -MD -MP -MF $(DEPDIR)/dhcpd-omapi.Tpo -c -o dhcpd-omapi.obj `if test -f 'omapi.c'; then $(CYGPATH_W) 'omapi.c'; else $(CYGPATH_W) '$(srcdir)/omapi.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-omapi.Tpo $(DEPDIR)/dhcpd-omapi.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='omapi.c' object='dhcpd-omapi.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-omapi.obj `if test -f 'omapi.c'; then $(CYGPATH_W) 'omapi.c'; else $(CYGPATH_W) '$(srcdir)/omapi.c'; fi`
+
+dhcpd-mdb.o: mdb.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-mdb.o -MD -MP -MF $(DEPDIR)/dhcpd-mdb.Tpo -c -o dhcpd-mdb.o `test -f 'mdb.c' || echo '$(srcdir)/'`mdb.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-mdb.Tpo $(DEPDIR)/dhcpd-mdb.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdb.c' object='dhcpd-mdb.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-mdb.o `test -f 'mdb.c' || echo '$(srcdir)/'`mdb.c
+
+dhcpd-mdb.obj: mdb.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-mdb.obj -MD -MP -MF $(DEPDIR)/dhcpd-mdb.Tpo -c -o dhcpd-mdb.obj `if test -f 'mdb.c'; then $(CYGPATH_W) 'mdb.c'; else $(CYGPATH_W) '$(srcdir)/mdb.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-mdb.Tpo $(DEPDIR)/dhcpd-mdb.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdb.c' object='dhcpd-mdb.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-mdb.obj `if test -f 'mdb.c'; then $(CYGPATH_W) 'mdb.c'; else $(CYGPATH_W) '$(srcdir)/mdb.c'; fi`
+
+dhcpd-stables.o: stables.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-stables.o -MD -MP -MF $(DEPDIR)/dhcpd-stables.Tpo -c -o dhcpd-stables.o `test -f 'stables.c' || echo '$(srcdir)/'`stables.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-stables.Tpo $(DEPDIR)/dhcpd-stables.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stables.c' object='dhcpd-stables.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-stables.o `test -f 'stables.c' || echo '$(srcdir)/'`stables.c
+
+dhcpd-stables.obj: stables.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-stables.obj -MD -MP -MF $(DEPDIR)/dhcpd-stables.Tpo -c -o dhcpd-stables.obj `if test -f 'stables.c'; then $(CYGPATH_W) 'stables.c'; else $(CYGPATH_W) '$(srcdir)/stables.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-stables.Tpo $(DEPDIR)/dhcpd-stables.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='stables.c' object='dhcpd-stables.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-stables.obj `if test -f 'stables.c'; then $(CYGPATH_W) 'stables.c'; else $(CYGPATH_W) '$(srcdir)/stables.c'; fi`
+
+dhcpd-salloc.o: salloc.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-salloc.o -MD -MP -MF $(DEPDIR)/dhcpd-salloc.Tpo -c -o dhcpd-salloc.o `test -f 'salloc.c' || echo '$(srcdir)/'`salloc.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-salloc.Tpo $(DEPDIR)/dhcpd-salloc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salloc.c' object='dhcpd-salloc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-salloc.o `test -f 'salloc.c' || echo '$(srcdir)/'`salloc.c
+
+dhcpd-salloc.obj: salloc.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-salloc.obj -MD -MP -MF $(DEPDIR)/dhcpd-salloc.Tpo -c -o dhcpd-salloc.obj `if test -f 'salloc.c'; then $(CYGPATH_W) 'salloc.c'; else $(CYGPATH_W) '$(srcdir)/salloc.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-salloc.Tpo $(DEPDIR)/dhcpd-salloc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salloc.c' object='dhcpd-salloc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-salloc.obj `if test -f 'salloc.c'; then $(CYGPATH_W) 'salloc.c'; else $(CYGPATH_W) '$(srcdir)/salloc.c'; fi`
+
+dhcpd-ddns.o: ddns.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ddns.o -MD -MP -MF $(DEPDIR)/dhcpd-ddns.Tpo -c -o dhcpd-ddns.o `test -f 'ddns.c' || echo '$(srcdir)/'`ddns.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ddns.Tpo $(DEPDIR)/dhcpd-ddns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ddns.c' object='dhcpd-ddns.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ddns.o `test -f 'ddns.c' || echo '$(srcdir)/'`ddns.c
+
+dhcpd-ddns.obj: ddns.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ddns.obj -MD -MP -MF $(DEPDIR)/dhcpd-ddns.Tpo -c -o dhcpd-ddns.obj `if test -f 'ddns.c'; then $(CYGPATH_W) 'ddns.c'; else $(CYGPATH_W) '$(srcdir)/ddns.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ddns.Tpo $(DEPDIR)/dhcpd-ddns.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ddns.c' object='dhcpd-ddns.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ddns.obj `if test -f 'ddns.c'; then $(CYGPATH_W) 'ddns.c'; else $(CYGPATH_W) '$(srcdir)/ddns.c'; fi`
+
+dhcpd-dhcpleasequery.o: dhcpleasequery.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpleasequery.o -MD -MP -MF $(DEPDIR)/dhcpd-dhcpleasequery.Tpo -c -o dhcpd-dhcpleasequery.o `test -f 'dhcpleasequery.c' || echo '$(srcdir)/'`dhcpleasequery.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpleasequery.Tpo $(DEPDIR)/dhcpd-dhcpleasequery.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpleasequery.c' object='dhcpd-dhcpleasequery.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpleasequery.o `test -f 'dhcpleasequery.c' || echo '$(srcdir)/'`dhcpleasequery.c
+
+dhcpd-dhcpleasequery.obj: dhcpleasequery.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpleasequery.obj -MD -MP -MF $(DEPDIR)/dhcpd-dhcpleasequery.Tpo -c -o dhcpd-dhcpleasequery.obj `if test -f 'dhcpleasequery.c'; then $(CYGPATH_W) 'dhcpleasequery.c'; else $(CYGPATH_W) '$(srcdir)/dhcpleasequery.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpleasequery.Tpo $(DEPDIR)/dhcpd-dhcpleasequery.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpleasequery.c' object='dhcpd-dhcpleasequery.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpleasequery.obj `if test -f 'dhcpleasequery.c'; then $(CYGPATH_W) 'dhcpleasequery.c'; else $(CYGPATH_W) '$(srcdir)/dhcpleasequery.c'; fi`
+
+dhcpd-dhcpv6.o: dhcpv6.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpv6.o -MD -MP -MF $(DEPDIR)/dhcpd-dhcpv6.Tpo -c -o dhcpd-dhcpv6.o `test -f 'dhcpv6.c' || echo '$(srcdir)/'`dhcpv6.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpv6.Tpo $(DEPDIR)/dhcpd-dhcpv6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpv6.c' object='dhcpd-dhcpv6.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpv6.o `test -f 'dhcpv6.c' || echo '$(srcdir)/'`dhcpv6.c
+
+dhcpd-dhcpv6.obj: dhcpv6.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-dhcpv6.obj -MD -MP -MF $(DEPDIR)/dhcpd-dhcpv6.Tpo -c -o dhcpd-dhcpv6.obj `if test -f 'dhcpv6.c'; then $(CYGPATH_W) 'dhcpv6.c'; else $(CYGPATH_W) '$(srcdir)/dhcpv6.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-dhcpv6.Tpo $(DEPDIR)/dhcpd-dhcpv6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dhcpv6.c' object='dhcpd-dhcpv6.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-dhcpv6.obj `if test -f 'dhcpv6.c'; then $(CYGPATH_W) 'dhcpv6.c'; else $(CYGPATH_W) '$(srcdir)/dhcpv6.c'; fi`
+
+dhcpd-mdb6.o: mdb6.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-mdb6.o -MD -MP -MF $(DEPDIR)/dhcpd-mdb6.Tpo -c -o dhcpd-mdb6.o `test -f 'mdb6.c' || echo '$(srcdir)/'`mdb6.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-mdb6.Tpo $(DEPDIR)/dhcpd-mdb6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdb6.c' object='dhcpd-mdb6.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-mdb6.o `test -f 'mdb6.c' || echo '$(srcdir)/'`mdb6.c
+
+dhcpd-mdb6.obj: mdb6.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-mdb6.obj -MD -MP -MF $(DEPDIR)/dhcpd-mdb6.Tpo -c -o dhcpd-mdb6.obj `if test -f 'mdb6.c'; then $(CYGPATH_W) 'mdb6.c'; else $(CYGPATH_W) '$(srcdir)/mdb6.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-mdb6.Tpo $(DEPDIR)/dhcpd-mdb6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mdb6.c' object='dhcpd-mdb6.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-mdb6.obj `if test -f 'mdb6.c'; then $(CYGPATH_W) 'mdb6.c'; else $(CYGPATH_W) '$(srcdir)/mdb6.c'; fi`
+
+dhcpd-ldap.o: ldap.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap.o -MD -MP -MF $(DEPDIR)/dhcpd-ldap.Tpo -c -o dhcpd-ldap.o `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ldap.Tpo $(DEPDIR)/dhcpd-ldap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap.c' object='dhcpd-ldap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap.o `test -f 'ldap.c' || echo '$(srcdir)/'`ldap.c
+
+dhcpd-ldap.obj: ldap.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap.obj -MD -MP -MF $(DEPDIR)/dhcpd-ldap.Tpo -c -o dhcpd-ldap.obj `if test -f 'ldap.c'; then $(CYGPATH_W) 'ldap.c'; else $(CYGPATH_W) '$(srcdir)/ldap.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ldap.Tpo $(DEPDIR)/dhcpd-ldap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap.c' object='dhcpd-ldap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap.obj `if test -f 'ldap.c'; then $(CYGPATH_W) 'ldap.c'; else $(CYGPATH_W) '$(srcdir)/ldap.c'; fi`
+
+dhcpd-ldap_casa.o: ldap_casa.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap_casa.o -MD -MP -MF $(DEPDIR)/dhcpd-ldap_casa.Tpo -c -o dhcpd-ldap_casa.o `test -f 'ldap_casa.c' || echo '$(srcdir)/'`ldap_casa.c
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ldap_casa.Tpo $(DEPDIR)/dhcpd-ldap_casa.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap_casa.c' object='dhcpd-ldap_casa.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap_casa.o `test -f 'ldap_casa.c' || echo '$(srcdir)/'`ldap_casa.c
+
+dhcpd-ldap_casa.obj: ldap_casa.c
+@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap_casa.obj -MD -MP -MF $(DEPDIR)/dhcpd-ldap_casa.Tpo -c -o dhcpd-ldap_casa.obj `if test -f 'ldap_casa.c'; then $(CYGPATH_W) 'ldap_casa.c'; else $(CYGPATH_W) '$(srcdir)/ldap_casa.c'; fi`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/dhcpd-ldap_casa.Tpo $(DEPDIR)/dhcpd-ldap_casa.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap_casa.c' object='dhcpd-ldap_casa.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap_casa.obj `if test -f 'ldap_casa.c'; then $(CYGPATH_W) 'ldap_casa.c'; else $(CYGPATH_W) '$(srcdir)/ldap_casa.c'; fi`
+install-man5: $(man5_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man5dir)/$$inst"; \
+ done
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+install-dist_sysconfDATA: $(dist_sysconf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(sysconfdir)" || $(MKDIR_P) "$(DESTDIR)$(sysconfdir)"
+ @list='$(dist_sysconf_DATA)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(dist_sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
+ $(dist_sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
+ done
+
+uninstall-dist_sysconfDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sysconf_DATA)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(sysconfdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-dist_sysconfDATA install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man5 install-man8
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_sysconfDATA uninstall-man \
+ uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man5 uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dist_sysconfDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man5 install-man8 install-pdf install-pdf-am \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-dist_sysconfDATA uninstall-man \
+ uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/server/bootp.c b/server/bootp.c
new file mode 100644
index 0000000..c88fab8
--- /dev/null
+++ b/server/bootp.c
@@ -0,0 +1,422 @@
+/* bootp.c
+
+ BOOTP Protocol support. */
+
+/*
+ * Copyright (c) 2004,2005,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <errno.h>
+
+#if defined (TRACING)
+# define send_packet trace_packet_send
+#endif
+
+void bootp (packet)
+ struct packet *packet;
+{
+ int result;
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *host = (struct host_decl *)0;
+ struct packet outgoing;
+ struct dhcp_packet raw;
+ struct sockaddr_in to;
+ struct in_addr from;
+ struct hardware hto;
+ struct option_state *options = (struct option_state *)0;
+ struct lease *lease = (struct lease *)0;
+ unsigned i;
+ struct data_string d1;
+ struct option_cache *oc;
+ char msgbuf [1024];
+ int ignorep;
+ int peer_has_leases = 0;
+
+ if (packet -> raw -> op != BOOTREQUEST)
+ return;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf, "BOOTREQUEST from %s via %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ if (!locate_network (packet)) {
+ log_info ("%s: network unknown", msgbuf);
+ return;
+ }
+
+ find_lease (&lease, packet, packet -> shared_network,
+ 0, 0, (struct lease *)0, MDL);
+
+ if (lease && lease->host)
+ host_reference(&hp, lease->host, MDL);
+
+ if (!lease || ((lease->flags & STATIC_LEASE) == 0)) {
+ struct host_decl *h;
+
+ /* We didn't find an applicable fixed-address host
+ declaration. Just in case we may be able to dynamically
+ assign an address, see if there's a host declaration
+ that doesn't have an ip address associated with it. */
+
+ if (!hp)
+ find_hosts_by_haddr(&hp, packet->raw->htype,
+ packet->raw->chaddr,
+ packet->raw->hlen, MDL);
+
+ for (h = hp; h; h = h -> n_ipaddr) {
+ if (!h -> fixed_addr) {
+ host_reference(&host, h, MDL);
+ break;
+ }
+ }
+
+ if (hp)
+ host_dereference(&hp, MDL);
+
+ if (host) {
+ host_reference(&hp, host, MDL);
+ host_dereference(&host, MDL);
+ }
+
+ /* Allocate a lease if we have not yet found one. */
+ if (!lease)
+ allocate_lease (&lease, packet,
+ packet -> shared_network -> pools,
+ &peer_has_leases);
+
+ if (lease == NULL) {
+ log_info("%s: BOOTP from dynamic client and no "
+ "dynamic leases", msgbuf);
+ goto out;
+ }
+
+#if defined(FAILOVER_PROTOCOL)
+ if ((lease->pool != NULL) &&
+ (lease->pool->failover_peer != NULL)) {
+ dhcp_failover_state_t *peer;
+
+ peer = lease->pool->failover_peer;
+
+ /* If we are in a failover state that bars us from
+ * answering, do not do so.
+ * If we are in a cooperative state, load balance
+ * (all) responses.
+ */
+ if ((peer->service_state == not_responding) ||
+ (peer->service_state == service_startup)) {
+ log_info("%s: not responding%s",
+ msgbuf, peer->nrr);
+ goto out;
+ } else if((peer->service_state == cooperating) &&
+ !load_balance_mine(packet, peer)) {
+ log_info("%s: load balance to peer %s",
+ msgbuf, peer->name);
+ goto out;
+ }
+ }
+#endif
+
+ ack_lease (packet, lease, 0, 0, msgbuf, 0, hp);
+ goto out;
+ }
+
+ /* Run the executable statements to compute the client and server
+ options. */
+ option_state_allocate (&options, MDL);
+
+ /* Execute the subnet statements. */
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, lease -> subnet -> group,
+ (struct group *)0);
+
+ /* Execute statements from class scopes. */
+ for (i = packet -> class_count; i > 0; i--) {
+ execute_statements_in_scope
+ ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, packet -> classes [i - 1] -> group,
+ lease -> subnet -> group);
+ }
+
+ /* Execute the host statements. */
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope,
+ hp -> group, lease -> subnet -> group);
+
+ /* Drop the request if it's not allowed for this client. */
+ if ((oc = lookup_option (&server_universe, options, SV_ALLOW_BOOTP)) &&
+ !evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: bootp disallowed", msgbuf);
+ goto out;
+ }
+
+ if ((oc = lookup_option (&server_universe,
+ options, SV_ALLOW_BOOTING)) &&
+ !evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: booting disallowed", msgbuf);
+ goto out;
+ }
+
+ /* Set up the outgoing packet... */
+ memset (&outgoing, 0, sizeof outgoing);
+ memset (&raw, 0, sizeof raw);
+ outgoing.raw = &raw;
+
+ /* If we didn't get a known vendor magic number on the way in,
+ just copy the input options to the output. */
+ if (!packet -> options_valid &&
+ !(evaluate_boolean_option_cache
+ (&ignorep, packet, lease, (struct client_state *)0,
+ packet -> options, options, &lease -> scope,
+ lookup_option (&server_universe, options,
+ SV_ALWAYS_REPLY_RFC1048), MDL))) {
+ memcpy (outgoing.raw -> options,
+ packet -> raw -> options, DHCP_MAX_OPTION_LEN);
+ outgoing.packet_length = BOOTP_MIN_LEN;
+ } else {
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+
+ oc = (struct option_cache *)0;
+ i = DHO_SUBNET_MASK;
+ if (!lookup_option (&dhcp_universe, options, i)) {
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data
+ (&oc -> expression,
+ lease -> subnet -> netmask.iabuf,
+ lease -> subnet -> netmask.len,
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+
+ /* Pack the options into the buffer. Unlike DHCP, we
+ can't pack options into the filename and server
+ name buffers. */
+
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw, lease,
+ (struct client_state *)0, 0,
+ packet -> options, options,
+ &lease -> scope,
+ 0, 0, 1, (struct data_string *)0,
+ (const char *)0);
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+ }
+
+ /* Take the fields that we care about... */
+ raw.op = BOOTREPLY;
+ raw.htype = packet -> raw -> htype;
+ raw.hlen = packet -> raw -> hlen;
+ memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
+ raw.hops = packet -> raw -> hops;
+ raw.xid = packet -> raw -> xid;
+ raw.secs = packet -> raw -> secs;
+ raw.flags = packet -> raw -> flags;
+ raw.ciaddr = packet -> raw -> ciaddr;
+
+ /* yiaddr is an ipv4 address, it must be 4 octets. */
+ memcpy (&raw.yiaddr, lease->ip_addr.iabuf, 4);
+
+ /* If we're always supposed to broadcast to this client, set
+ the broadcast bit in the bootp flags field. */
+ if ((oc = lookup_option (&server_universe,
+ options, SV_ALWAYS_BROADCAST)) &&
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL))
+ raw.flags |= htons (BOOTP_BROADCAST);
+
+ /* Figure out the address of the next server. */
+ memset (&d1, 0, sizeof d1);
+ oc = lookup_option (&server_universe, options, SV_NEXT_SERVER);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ /* If there was more than one answer, take the first. */
+ if (d1.len >= 4 && d1.data)
+ memcpy (&raw.siaddr, d1.data, 4);
+ data_string_forget (&d1, MDL);
+ } else {
+ if ((lease->subnet->shared_network->interface != NULL) &&
+ lease->subnet->shared_network->interface->address_count)
+ raw.siaddr =
+ lease->subnet->shared_network->interface->addresses[0];
+ else if (packet->interface->address_count)
+ raw.siaddr = packet->interface->addresses[0];
+ }
+
+ raw.giaddr = packet -> raw -> giaddr;
+
+ /* Figure out the filename. */
+ oc = lookup_option (&server_universe, options, SV_FILENAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ memcpy (raw.file, d1.data,
+ d1.len > sizeof raw.file ? sizeof raw.file : d1.len);
+ if (sizeof raw.file > d1.len)
+ memset (&raw.file [d1.len],
+ 0, (sizeof raw.file) - d1.len);
+ data_string_forget (&d1, MDL);
+ } else
+ memcpy (raw.file, packet -> raw -> file, sizeof raw.file);
+
+ /* Choose a server name as above. */
+ oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ memcpy (raw.sname, d1.data,
+ d1.len > sizeof raw.sname ? sizeof raw.sname : d1.len);
+ if (sizeof raw.sname > d1.len)
+ memset (&raw.sname [d1.len],
+ 0, (sizeof raw.sname) - d1.len);
+ data_string_forget (&d1, MDL);
+ }
+
+ /* Execute the commit statements, if there are any. */
+ execute_statements ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options,
+ options, &lease -> scope, lease -> on_commit);
+
+ /* We're done with the option state. */
+ option_state_dereference (&options, MDL);
+
+ /* Set up the hardware destination address... */
+ hto.hbuf [0] = packet -> raw -> htype;
+ hto.hlen = packet -> raw -> hlen + 1;
+ memcpy (&hto.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
+
+ if (packet->interface->address_count) {
+ from = packet->interface->addresses[0];
+ } else {
+ log_error("%s: Interface %s appears to have no IPv4 "
+ "addresses, and so dhcpd cannot select a source "
+ "address.", msgbuf, packet->interface->name);
+ goto out;
+ }
+
+ /* Report what we're doing... */
+ log_info ("%s", msgbuf);
+ log_info ("BOOTREPLY for %s to %s (%s) via %s",
+ piaddr (lease->ip_addr), hp -> name,
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* Set up the parts of the address that are in common. */
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+ memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+ /* If this was gatewayed, send it back to the gateway... */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ to.sin_port = local_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, outgoing.packet_length,
+ from, &to, &hto);
+ goto out;
+ }
+
+ /* If it comes from a client that already knows its address
+ and is not requesting a broadcast response, and we can
+ unicast to a client without using the ARP protocol, sent it
+ directly to that client. */
+ } else if (!(raw.flags & htons (BOOTP_BROADCAST)) &&
+ can_unicast_without_arp (packet -> interface)) {
+ to.sin_addr = raw.yiaddr;
+ to.sin_port = remote_port;
+
+ /* Otherwise, broadcast it on the local network. */
+ } else {
+ to.sin_addr = limited_broadcast;
+ to.sin_port = remote_port; /* XXX */
+ }
+
+ errno = 0;
+ result = send_packet (packet -> interface,
+ packet, &raw, outgoing.packet_length,
+ from, &to, &hto);
+ out:
+ if (options)
+ option_state_dereference (&options, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+ if (hp)
+ host_dereference (&hp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+}
diff --git a/server/class.c b/server/class.c
new file mode 100644
index 0000000..2f92255
--- /dev/null
+++ b/server/class.c
@@ -0,0 +1,308 @@
+/* class.c
+
+ Handling for client classes. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+struct collection default_collection = {
+ (struct collection *)0,
+ "default",
+ (struct class *)0,
+};
+
+struct collection *collections = &default_collection;
+struct executable_statement *default_classification_rules;
+
+int have_billing_classes;
+
+/* Build the default classification rule tree. */
+
+void classification_setup ()
+{
+ /* eval ... */
+ default_classification_rules = (struct executable_statement *)0;
+ if (!executable_statement_allocate (&default_classification_rules,
+ MDL))
+ log_fatal ("Can't allocate check of default collection");
+ default_classification_rules -> op = eval_statement;
+
+ /* check-collection "default" */
+ if (!expression_allocate (&default_classification_rules -> data.eval,
+ MDL))
+ log_fatal ("Can't allocate default check expression");
+ default_classification_rules -> data.eval -> op = expr_check;
+ default_classification_rules -> data.eval -> data.check =
+ &default_collection;
+}
+
+void classify_client (packet)
+ struct packet *packet;
+{
+ execute_statements ((struct binding_value **)0, packet,
+ (struct lease *)0, (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, default_classification_rules);
+}
+
+int check_collection (packet, lease, collection)
+ struct packet *packet;
+ struct lease *lease;
+ struct collection *collection;
+{
+ struct class *class, *nc;
+ struct data_string data;
+ int matched = 0;
+ int status;
+ int ignorep;
+ int classfound;
+
+ for (class = collection -> classes; class; class = class -> nic) {
+#if defined (DEBUG_CLASS_MATCHING)
+ log_info ("checking against class %s...", class -> name);
+#endif
+ memset (&data, 0, sizeof data);
+
+ /* If there is a "match if" expression, check it. If
+ we get a match, and there's no subclass expression,
+ it's a match. If we get a match and there is a subclass
+ expression, then we check the submatch. If it's not a
+ match, that's final - we don't check the submatch. */
+
+ if (class -> expr) {
+ status = (evaluate_boolean_expression_result
+ (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ lease ? &lease -> scope : &global_scope,
+ class -> expr));
+ if (status) {
+ if (!class -> submatch) {
+ matched = 1;
+#if defined (DEBUG_CLASS_MATCHING)
+ log_info ("matches class.");
+#endif
+ classify (packet, class);
+ continue;
+ }
+ } else
+ continue;
+ }
+
+ /* Check to see if the client matches an existing subclass.
+ If it doesn't, and this is a spawning class, spawn a new
+ subclass and put the client in it. */
+ if (class -> submatch) {
+ status = (evaluate_data_expression
+ (&data, packet, lease,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ lease ? &lease -> scope : &global_scope,
+ class -> submatch, MDL));
+ if (status && data.len) {
+ nc = (struct class *)0;
+ classfound = class_hash_lookup (&nc, class -> hash,
+ (const char *)data.data, data.len, MDL);
+
+#ifdef LDAP_CONFIGURATION
+ if (!classfound && find_subclass_in_ldap (class, &nc, &data))
+ classfound = 1;
+#endif
+
+ if (classfound) {
+#if defined (DEBUG_CLASS_MATCHING)
+ log_info ("matches subclass %s.",
+ print_hex_1 (data.len,
+ data.data, 60));
+#endif
+ data_string_forget (&data, MDL);
+ classify (packet, nc);
+ matched = 1;
+ class_dereference (&nc, MDL);
+ continue;
+ }
+ if (!class -> spawning) {
+ data_string_forget (&data, MDL);
+ continue;
+ }
+ /* XXX Write out the spawned class? */
+#if defined (DEBUG_CLASS_MATCHING)
+ log_info ("spawning subclass %s.",
+ print_hex_1 (data.len, data.data, 60));
+#endif
+ status = class_allocate (&nc, MDL);
+ group_reference (&nc -> group,
+ class -> group, MDL);
+ class_reference (&nc -> superclass,
+ class, MDL);
+ nc -> lease_limit = class -> lease_limit;
+ nc -> dirty = 1;
+ if (nc -> lease_limit) {
+ nc -> billed_leases =
+ (dmalloc
+ (nc -> lease_limit *
+ sizeof (struct lease *),
+ MDL));
+ if (!nc -> billed_leases) {
+ log_error ("no memory for%s",
+ " billing");
+ data_string_forget
+ (&nc -> hash_string,
+ MDL);
+ class_dereference (&nc, MDL);
+ data_string_forget (&data,
+ MDL);
+ continue;
+ }
+ memset (nc -> billed_leases, 0,
+ (nc -> lease_limit *
+ sizeof nc -> billed_leases));
+ }
+ data_string_copy (&nc -> hash_string, &data,
+ MDL);
+ data_string_forget (&data, MDL);
+ if (!class -> hash)
+ class_new_hash(&class->hash,
+ SCLASS_HASH_SIZE, MDL);
+ class_hash_add (class -> hash,
+ (const char *)
+ nc -> hash_string.data,
+ nc -> hash_string.len,
+ nc, MDL);
+ classify (packet, nc);
+ class_dereference (&nc, MDL);
+ }
+ }
+ }
+ return matched;
+}
+
+void classify (packet, class)
+ struct packet *packet;
+ struct class *class;
+{
+ if (packet -> class_count < PACKET_MAX_CLASSES)
+ class_reference (&packet -> classes [packet -> class_count++],
+ class, MDL);
+ else
+ log_error ("too many classes match %s",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+}
+
+
+isc_result_t unlink_class(struct class **class) {
+ struct collection *lp;
+ struct class *cp, *pp;
+
+ for (lp = collections; lp; lp = lp -> next) {
+ for (pp = 0, cp = lp -> classes; cp; pp = cp, cp = cp -> nic)
+ if (cp == *class) {
+ if (pp == 0) {
+ lp->classes = cp->nic;
+ } else {
+ pp->nic = cp->nic;
+ }
+ cp->nic = 0;
+ class_dereference(class, MDL);
+
+ return ISC_R_SUCCESS;
+ }
+ }
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t find_class (struct class **class, const char *name,
+ const char *file, int line)
+{
+ struct collection *lp;
+ struct class *cp;
+
+ for (lp = collections; lp; lp = lp -> next) {
+ for (cp = lp -> classes; cp; cp = cp -> nic)
+ if (cp -> name && !strcmp (name, cp -> name)) {
+ return class_reference (class, cp, file, line);
+ }
+ }
+ return ISC_R_NOTFOUND;
+}
+
+int unbill_class (lease, class)
+ struct lease *lease;
+ struct class *class;
+{
+ int i;
+
+ for (i = 0; i < class -> lease_limit; i++)
+ if (class -> billed_leases [i] == lease)
+ break;
+ if (i == class -> lease_limit) {
+ log_error ("lease %s unbilled with no billing arrangement.",
+ piaddr (lease -> ip_addr));
+ return 0;
+ }
+ class_dereference (&lease -> billing_class, MDL);
+ lease_dereference (&class -> billed_leases [i], MDL);
+ class -> leases_consumed--;
+ return 1;
+}
+
+int bill_class (lease, class)
+ struct lease *lease;
+ struct class *class;
+{
+ int i;
+
+ if (lease -> billing_class) {
+ log_error ("lease billed with existing billing arrangement.");
+ unbill_class (lease, lease -> billing_class);
+ }
+
+ if (class -> leases_consumed == class -> lease_limit)
+ return 0;
+
+ for (i = 0; i < class -> lease_limit; i++)
+ if (!class -> billed_leases [i])
+ break;
+
+ if (i == class -> lease_limit) {
+ log_error ("class billing consumption disagrees with leases.");
+ return 0;
+ }
+
+ lease_reference (&class -> billed_leases [i], lease, MDL);
+ class_reference (&lease -> billing_class, class, MDL);
+ class -> leases_consumed++;
+ return 1;
+}
diff --git a/server/confpars.c b/server/confpars.c
new file mode 100644
index 0000000..c0742d4
--- /dev/null
+++ b/server/confpars.c
@@ -0,0 +1,5504 @@
+/* confpars.c
+
+ Parser for dhcpd config file... */
+
+/*
+ * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+static unsigned char global_host_once = 1;
+static unsigned char dhcpv6_class_once = 1;
+
+static int parse_binding_value(struct parse *cfile,
+ struct binding_value *value);
+
+#if defined (TRACING)
+trace_type_t *trace_readconf_type;
+trace_type_t *trace_readleases_type;
+
+void parse_trace_setup ()
+{
+ trace_readconf_type = trace_type_register ("readconf", (void *)0,
+ trace_conf_input,
+ trace_conf_stop, MDL);
+ trace_readleases_type = trace_type_register ("readleases", (void *)0,
+ trace_conf_input,
+ trace_conf_stop, MDL);
+}
+#endif
+
+/* conf-file :== parameters declarations END_OF_FILE
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration */
+
+isc_result_t readconf ()
+{
+ isc_result_t res;
+
+ res = read_conf_file (path_dhcpd_conf, root_group, ROOT_GROUP, 0);
+#if defined(LDAP_CONFIGURATION)
+ if (res != ISC_R_SUCCESS)
+ return (res);
+
+ return ldap_read_config ();
+#else
+ return (res);
+#endif
+}
+
+isc_result_t read_conf_file (const char *filename, struct group *group,
+ int group_type, int leasep)
+{
+ int file;
+ struct parse *cfile;
+ isc_result_t status;
+#if defined (TRACING)
+ char *fbuf, *dbuf;
+ off_t flen;
+ int result;
+ unsigned tflen, ulen;
+ trace_type_t *ttype;
+
+ if (leasep)
+ ttype = trace_readleases_type;
+ else
+ ttype = trace_readconf_type;
+
+ /* If we're in playback, we need to snarf the contents of the
+ named file out of the playback file rather than trying to
+ open and read it. */
+ if (trace_playback ()) {
+ dbuf = (char *)0;
+ tflen = 0;
+ status = trace_get_file (ttype, filename, &tflen, &dbuf);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ ulen = tflen;
+
+ /* What we get back is filename\0contents, where contents is
+ terminated just by the length. So we figure out the length
+ of the filename, and subtract that and the NUL from the
+ total length to get the length of the contents of the file.
+ We make fbuf a pointer to the contents of the file, and
+ leave dbuf as it is so we can free it later. */
+ tflen = strlen (dbuf);
+ ulen = ulen - tflen - 1;
+ fbuf = dbuf + tflen + 1;
+ goto memfile;
+ }
+#endif
+
+ if ((file = open (filename, O_RDONLY)) < 0) {
+ if (leasep) {
+ log_error ("Can't open lease database %s: %m --",
+ path_dhcpd_db);
+ log_error (" check for failed database %s!",
+ "rewrite attempt");
+ log_error ("Please read the dhcpd.leases manual%s",
+ " page if you");
+ log_fatal ("don't know what to do about this.");
+ } else {
+ log_fatal ("Can't open %s: %m", filename);
+ }
+ }
+
+ cfile = (struct parse *)0;
+#if defined (TRACING)
+ flen = lseek (file, (off_t)0, SEEK_END);
+ if (flen < 0) {
+ boom:
+ log_fatal ("Can't lseek on %s: %m", filename);
+ }
+ if (lseek (file, (off_t)0, SEEK_SET) < 0)
+ goto boom;
+ /* Can't handle files greater than 2^31-1. */
+ if (flen > 0x7FFFFFFFUL)
+ log_fatal ("%s: file is too long to buffer.", filename);
+ ulen = flen;
+
+ /* Allocate a buffer that will be what's written to the tracefile,
+ and also will be what we parse from. */
+ tflen = strlen (filename);
+ dbuf = dmalloc (ulen + tflen + 1, MDL);
+ if (!dbuf)
+ log_fatal ("No memory for %s (%d bytes)",
+ filename, ulen);
+
+ /* Copy the name into the beginning, nul-terminated. */
+ strcpy (dbuf, filename);
+
+ /* Load the file in after the NUL. */
+ fbuf = dbuf + tflen + 1;
+ result = read (file, fbuf, ulen);
+ if (result < 0)
+ log_fatal ("Can't read in %s: %m", filename);
+ if (result != ulen)
+ log_fatal ("%s: short read of %d bytes instead of %d.",
+ filename, ulen, result);
+ close (file);
+ memfile:
+ /* If we're recording, write out the filename and file contents. */
+ if (trace_record ())
+ trace_write_packet (ttype, ulen + tflen + 1, dbuf, MDL);
+ status = new_parse(&cfile, -1, fbuf, ulen, filename, 0); /* XXX */
+#else
+ status = new_parse(&cfile, file, NULL, 0, filename, 0);
+#endif
+ if (status != ISC_R_SUCCESS || cfile == NULL)
+ return status;
+
+ if (leasep)
+ status = lease_file_subparse (cfile);
+ else
+ status = conf_file_subparse (cfile, group, group_type);
+ end_parse (&cfile);
+#if defined (TRACING)
+ dfree (dbuf, MDL);
+#endif
+ return status;
+}
+
+#if defined (TRACING)
+void trace_conf_input (trace_type_t *ttype, unsigned len, char *data)
+{
+ char *fbuf;
+ unsigned flen;
+ unsigned tflen;
+ struct parse *cfile = (struct parse *)0;
+ static int postconf_initialized;
+ static int leaseconf_initialized;
+ isc_result_t status;
+
+ /* Do what's done above, except that we don't have to read in the
+ data, because it's already been read for us. */
+ tflen = strlen (data);
+ flen = len - tflen - 1;
+ fbuf = data + tflen + 1;
+
+ /* If we're recording, write out the filename and file contents. */
+ if (trace_record ())
+ trace_write_packet (ttype, len, data, MDL);
+
+ status = new_parse(&cfile, -1, fbuf, flen, data, 0);
+ if (status == ISC_R_SUCCESS || cfile != NULL) {
+ if (ttype == trace_readleases_type)
+ lease_file_subparse (cfile);
+ else
+ conf_file_subparse (cfile, root_group, ROOT_GROUP);
+ end_parse (&cfile);
+ }
+
+ /* Postconfiguration needs to be done after the config file
+ has been loaded. */
+ if (!postconf_initialized && ttype == trace_readconf_type) {
+ postconf_initialization (0);
+ postconf_initialized = 1;
+ }
+
+ if (!leaseconf_initialized && ttype == trace_readleases_type) {
+ db_startup (0);
+ leaseconf_initialized = 1;
+ postdb_startup ();
+ }
+}
+
+void trace_conf_stop (trace_type_t *ttype) { }
+#endif
+
+/* conf-file :== parameters declarations END_OF_FILE
+ parameters :== <nil> | parameter | parameters parameter
+ declarations :== <nil> | declaration | declarations declaration */
+
+isc_result_t conf_file_subparse (struct parse *cfile, struct group *group,
+ int group_type)
+{
+ const char *val;
+ enum dhcp_token token;
+ int declaration = 0;
+ int status;
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE)
+ break;
+ declaration = parse_statement (cfile, group, group_type,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ status = cfile->warnings_occurred ? DHCP_R_BADPARSE : ISC_R_SUCCESS;
+ return status;
+}
+
+/* lease-file :== lease-declarations END_OF_FILE
+ lease-statements :== <nil>
+ | lease-declaration
+ | lease-declarations lease-declaration */
+
+isc_result_t lease_file_subparse (struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ isc_result_t status;
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == END_OF_FILE)
+ break;
+ if (token == LEASE) {
+ struct lease *lease = (struct lease *)0;
+ if (parse_lease_declaration (&lease, cfile)) {
+ enter_lease (lease);
+ lease_dereference (&lease, MDL);
+ } else
+ parse_warn (cfile,
+ "possibly corrupt lease file");
+ } else if (token == IA_NA) {
+ parse_ia_na_declaration(cfile);
+ } else if (token == IA_TA) {
+ parse_ia_ta_declaration(cfile);
+ } else if (token == IA_PD) {
+ parse_ia_pd_declaration(cfile);
+ } else if (token == CLASS) {
+ parse_class_declaration(0, cfile, root_group,
+ CLASS_TYPE_CLASS);
+ } else if (token == SUBCLASS) {
+ parse_class_declaration(0, cfile, root_group,
+ CLASS_TYPE_SUBCLASS);
+ } else if (token == HOST) {
+ parse_host_declaration (cfile, root_group);
+ } else if (token == GROUP) {
+ parse_group_declaration (cfile, root_group);
+#if defined (FAILOVER_PROTOCOL)
+ } else if (token == FAILOVER) {
+ parse_failover_state_declaration
+ (cfile, (dhcp_failover_state_t *)0);
+#endif
+#ifdef DHCPv6
+ } else if (token == SERVER_DUID) {
+ parse_server_duid(cfile);
+#endif /* DHCPv6 */
+ } else {
+ log_error ("Corrupt lease file - possible data loss!");
+ skip_to_semi (cfile);
+ }
+
+ } while (1);
+
+ status = cfile->warnings_occurred ? DHCP_R_BADPARSE : ISC_R_SUCCESS;
+ return status;
+}
+
+/* statement :== parameter | declaration
+
+ parameter :== DEFAULT_LEASE_TIME lease_time
+ | MAX_LEASE_TIME lease_time
+ | DYNAMIC_BOOTP_LEASE_CUTOFF date
+ | DYNAMIC_BOOTP_LEASE_LENGTH lease_time
+ | BOOT_UNKNOWN_CLIENTS boolean
+ | ONE_LEASE_PER_CLIENT boolean
+ | GET_LEASE_HOSTNAMES boolean
+ | USE_HOST_DECL_NAME boolean
+ | NEXT_SERVER ip-addr-or-hostname SEMI
+ | option_parameter
+ | SERVER-IDENTIFIER ip-addr-or-hostname SEMI
+ | FILENAME string-parameter
+ | SERVER_NAME string-parameter
+ | hardware-parameter
+ | fixed-address-parameter
+ | ALLOW allow-deny-keyword
+ | DENY allow-deny-keyword
+ | USE_LEASE_ADDR_FOR_DEFAULT_ROUTE boolean
+ | AUTHORITATIVE
+ | NOT AUTHORITATIVE
+
+ declaration :== host-declaration
+ | group-declaration
+ | shared-network-declaration
+ | subnet-declaration
+ | VENDOR_CLASS class-declaration
+ | USER_CLASS class-declaration
+ | RANGE address-range-declaration */
+
+int parse_statement (cfile, group, type, host_decl, declaration)
+ struct parse *cfile;
+ struct group *group;
+ int type;
+ struct host_decl *host_decl;
+ int declaration;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct shared_network *share;
+ char *n;
+ struct hardware hardware;
+ struct executable_statement *et, *ep;
+ struct option *option = NULL;
+ struct option_cache *cache;
+ int lose;
+ int known;
+ isc_result_t status;
+ unsigned code;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+
+ switch (token) {
+ case INCLUDE:
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "filename string expected.");
+ skip_to_semi (cfile);
+ } else {
+ status = read_conf_file (val, group, type, 0);
+ if (status != ISC_R_SUCCESS)
+ parse_warn (cfile, "%s: bad parse.", val);
+ parse_semi (cfile);
+ }
+ return 1;
+
+ case HOST:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type != HOST_DECL && type != CLASS_DECL) {
+ if (global_host_once &&
+ (type == SUBNET_DECL || type == SHARED_NET_DECL)) {
+ global_host_once = 0;
+ log_error("WARNING: Host declarations are "
+ "global. They are not limited to "
+ "the scope you declared them in.");
+ }
+
+ parse_host_declaration (cfile, group);
+ } else {
+ parse_warn (cfile,
+ "host declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case GROUP:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type != HOST_DECL && type != CLASS_DECL)
+ parse_group_declaration (cfile, group);
+ else {
+ parse_warn (cfile,
+ "group declarations not allowed here.");
+ skip_to_semi (cfile);
+ }
+ return 1;
+
+ case SHARED_NETWORK:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == SHARED_NET_DECL ||
+ type == HOST_DECL ||
+ type == SUBNET_DECL ||
+ type == CLASS_DECL) {
+ parse_warn (cfile, "shared-network parameters not %s.",
+ "allowed here");
+ skip_to_semi (cfile);
+ break;
+ }
+
+ parse_shared_net_declaration (cfile, group);
+ return 1;
+
+ case SUBNET:
+ case SUBNET6:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == HOST_DECL || type == SUBNET_DECL ||
+ type == CLASS_DECL) {
+ parse_warn (cfile,
+ "subnet declarations not allowed here.");
+ skip_to_semi (cfile);
+ return 1;
+ }
+
+ /* If we're in a subnet declaration, just do the parse. */
+ if (group->shared_network != NULL) {
+ if (token == SUBNET) {
+ parse_subnet_declaration(cfile,
+ group->shared_network);
+ } else {
+ parse_subnet6_declaration(cfile,
+ group->shared_network);
+ }
+ break;
+ }
+
+ /*
+ * Otherwise, cons up a fake shared network structure
+ * and populate it with the lone subnet...because the
+ * intention most likely is to refer to the entire link
+ * by shorthand, any configuration inside the subnet is
+ * actually placed in the shared-network's group.
+ */
+
+ share = NULL;
+ status = shared_network_allocate (&share, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't allocate shared subnet: %s",
+ isc_result_totext (status));
+ if (!clone_group (&share -> group, group, MDL))
+ log_fatal ("Can't allocate group for shared net");
+ shared_network_reference (&share -> group -> shared_network,
+ share, MDL);
+
+ /*
+ * This is an implicit shared network, not explicit in
+ * the config.
+ */
+ share->flags |= SHARED_IMPLICIT;
+
+ if (token == SUBNET) {
+ parse_subnet_declaration(cfile, share);
+ } else {
+ parse_subnet6_declaration(cfile, share);
+ }
+
+ /* share -> subnets is the subnet we just parsed. */
+ if (share->subnets) {
+ interface_reference(&share->interface,
+ share->subnets->interface,
+ MDL);
+
+ /* Make the shared network name from network number. */
+ if (token == SUBNET) {
+ n = piaddrmask(&share->subnets->net,
+ &share->subnets->netmask);
+ } else {
+ n = piaddrcidr(&share->subnets->net,
+ share->subnets->prefix_len);
+ }
+
+ share->name = strdup(n);
+
+ if (share->name == NULL)
+ log_fatal("Out of memory allocating default "
+ "shared network name (\"%s\").", n);
+
+ /* Copy the authoritative parameter from the subnet,
+ since there is no opportunity to declare it here. */
+ share->group->authoritative =
+ share->subnets->group->authoritative;
+ enter_shared_network(share);
+ }
+ shared_network_dereference(&share, MDL);
+ return 1;
+
+ case VENDOR_CLASS:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == CLASS_DECL) {
+ parse_warn (cfile,
+ "class declarations not allowed here.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_class_declaration(NULL, cfile, group, CLASS_TYPE_VENDOR);
+ return 1;
+
+ case USER_CLASS:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == CLASS_DECL) {
+ parse_warn (cfile,
+ "class declarations not allowed here.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_class_declaration(NULL, cfile, group, CLASS_TYPE_USER);
+ return 1;
+
+ case CLASS:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == CLASS_DECL) {
+ parse_warn (cfile,
+ "class declarations not allowed here.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_class_declaration(NULL, cfile, group, CLASS_TYPE_CLASS);
+ return 1;
+
+ case SUBCLASS:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == CLASS_DECL) {
+ parse_warn (cfile,
+ "class declarations not allowed here.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_class_declaration(NULL, cfile, group,
+ CLASS_TYPE_SUBCLASS);
+ return 1;
+
+ case HARDWARE:
+ next_token (&val, (unsigned *)0, cfile);
+ memset (&hardware, 0, sizeof hardware);
+ if (host_decl && memcmp(&hardware, &(host_decl->interface),
+ sizeof(hardware)) != 0) {
+ parse_warn(cfile, "Host %s hardware address already "
+ "configured.", host_decl->name);
+ break;
+ }
+
+ parse_hardware_param (cfile, &hardware);
+ if (host_decl)
+ host_decl -> interface = hardware;
+ else
+ parse_warn (cfile, "hardware address parameter %s",
+ "not allowed here.");
+ break;
+
+ case FIXED_ADDR:
+ case FIXED_ADDR6:
+ next_token(&val, NULL, cfile);
+ cache = NULL;
+ if (parse_fixed_addr_param(&cache, cfile, token)) {
+ if (host_decl) {
+ if (host_decl->fixed_addr) {
+ option_cache_dereference(&cache, MDL);
+ parse_warn(cfile,
+ "Only one fixed address "
+ "declaration per host.");
+ } else {
+ host_decl->fixed_addr = cache;
+ }
+ } else {
+ parse_warn(cfile,
+ "fixed-address parameter not "
+ "allowed here.");
+ option_cache_dereference(&cache, MDL);
+ }
+ }
+ break;
+
+ case POOL:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type == POOL_DECL) {
+ parse_warn (cfile, "pool declared within pool.");
+ skip_to_semi(cfile);
+ } else if (type != SUBNET_DECL && type != SHARED_NET_DECL) {
+ parse_warn (cfile, "pool declared outside of network");
+ skip_to_semi(cfile);
+ } else
+ parse_pool_statement (cfile, group, type);
+
+ return declaration;
+
+ case RANGE:
+ next_token (&val, (unsigned *)0, cfile);
+ if (type != SUBNET_DECL || !group -> subnet) {
+ parse_warn (cfile,
+ "range declaration not allowed here.");
+ skip_to_semi (cfile);
+ return declaration;
+ }
+ parse_address_range (cfile, group, type, (struct pool *)0,
+ (struct lease **)0);
+ return declaration;
+
+#ifdef DHCPv6
+ case RANGE6:
+ next_token(NULL, NULL, cfile);
+ if ((type != SUBNET_DECL) || (group->subnet == NULL)) {
+ parse_warn (cfile,
+ "range6 declaration not allowed here.");
+ skip_to_semi(cfile);
+ return declaration;
+ }
+ parse_address_range6(cfile, group);
+ return declaration;
+
+ case PREFIX6:
+ next_token(NULL, NULL, cfile);
+ if ((type != SUBNET_DECL) || (group->subnet == NULL)) {
+ parse_warn (cfile,
+ "prefix6 declaration not allowed here.");
+ skip_to_semi(cfile);
+ return declaration;
+ }
+ parse_prefix6(cfile, group);
+ return declaration;
+
+ case FIXED_PREFIX6:
+ next_token(&val, NULL, cfile);
+ if (!host_decl) {
+ parse_warn (cfile,
+ "fixed-prefix6 declaration not "
+ "allowed here.");
+ skip_to_semi(cfile);
+ break;
+ }
+ parse_fixed_prefix6(cfile, host_decl);
+ break;
+
+#endif /* DHCPv6 */
+
+ case TOKEN_NOT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case AUTHORITATIVE:
+ group -> authoritative = 0;
+ goto authoritative;
+ default:
+ parse_warn (cfile, "expecting assertion");
+ skip_to_semi (cfile);
+ break;
+ }
+ break;
+ case AUTHORITATIVE:
+ token = next_token (&val, (unsigned *)0, cfile);
+ group -> authoritative = 1;
+ authoritative:
+ if (type == HOST_DECL)
+ parse_warn (cfile, "authority makes no sense here.");
+ parse_semi (cfile);
+ break;
+
+ /* "server-identifier" is a special hack, equivalent to
+ "option dhcp-server-identifier". */
+ case SERVER_IDENTIFIER:
+ code = DHO_DHCP_SERVER_IDENTIFIER;
+ if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Server identifier not in hash (%s:%d).",
+ MDL);
+ token = next_token (&val, (unsigned *)0, cfile);
+ goto finish_option;
+
+ case OPTION:
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SPACE) {
+ if (type != ROOT_GROUP) {
+ parse_warn (cfile,
+ "option space definitions %s",
+ "may not be scoped.");
+ skip_to_semi (cfile);
+ break;
+ }
+ parse_option_space_decl (cfile);
+ return declaration;
+ }
+
+ known = 0;
+ status = parse_option_name(cfile, 1, &known, &option);
+ if (status == ISC_R_SUCCESS) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == CODE) {
+ if (type != ROOT_GROUP) {
+ parse_warn (cfile,
+ "option definitions%s",
+ " may not be scoped.");
+ skip_to_semi (cfile);
+ option_dereference(&option, MDL);
+ break;
+ }
+ next_token (&val, (unsigned *)0, cfile);
+
+ /*
+ * If the option was known, remove it from the
+ * code and name hashes before redefining it.
+ */
+ if (known) {
+ option_name_hash_delete(
+ option->universe->name_hash,
+ option->name, 0, MDL);
+ option_code_hash_delete(
+ option->universe->code_hash,
+ &option->code, 0, MDL);
+ }
+
+ parse_option_code_definition(cfile, option);
+ option_dereference(&option, MDL);
+ return declaration;
+ }
+
+ /* If this wasn't an option code definition, don't
+ allow an unknown option. */
+ if (!known) {
+ parse_warn (cfile, "unknown option %s.%s",
+ option -> universe -> name,
+ option -> name);
+ skip_to_semi (cfile);
+ option_dereference(&option, MDL);
+ return declaration;
+ }
+
+ finish_option:
+ et = (struct executable_statement *)0;
+ if (!parse_option_statement
+ (&et, cfile, 1, option,
+ supersede_option_statement))
+ return declaration;
+ option_dereference(&option, MDL);
+ goto insert_statement;
+ } else
+ return declaration;
+
+ break;
+
+ case FAILOVER:
+ if (type != ROOT_GROUP && type != SHARED_NET_DECL) {
+ parse_warn (cfile, "failover peers may only be %s",
+ "defined in shared-network");
+ log_error ("declarations and the outer scope.");
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+#if defined (FAILOVER_PROTOCOL)
+ parse_failover_peer (cfile, group, type);
+#else
+ parse_warn (cfile, "No failover support.");
+ skip_to_semi (cfile);
+#endif
+ break;
+
+#ifdef DHCPv6
+ case SERVER_DUID:
+ parse_server_duid_conf(cfile);
+ break;
+#endif /* DHCPv6 */
+
+ default:
+ et = (struct executable_statement *)0;
+ lose = 0;
+ if (!parse_executable_statement (&et, cfile, &lose,
+ context_any)) {
+ if (!lose) {
+ if (declaration)
+ parse_warn (cfile,
+ "expecting a declaration");
+ else
+ parse_warn (cfile,
+ "expecting a parameter %s",
+ "or declaration");
+ skip_to_semi (cfile);
+ }
+ return declaration;
+ }
+ if (!et)
+ return declaration;
+ insert_statement:
+ if (group -> statements) {
+ int multi = 0;
+
+ /* If this set of statements is only referenced
+ by this group, just add the current statement
+ to the end of the chain. */
+ for (ep = group -> statements; ep -> next;
+ ep = ep -> next)
+ if (ep -> refcnt > 1) /* XXX */
+ multi = 1;
+ if (!multi) {
+ executable_statement_reference (&ep -> next,
+ et, MDL);
+ executable_statement_dereference (&et, MDL);
+ return declaration;
+ }
+
+ /* Otherwise, make a parent chain, and put the
+ current group statements first and the new
+ statement in the next pointer. */
+ ep = (struct executable_statement *)0;
+ if (!executable_statement_allocate (&ep, MDL))
+ log_fatal ("No memory for statements.");
+ ep -> op = statements_statement;
+ executable_statement_reference (&ep -> data.statements,
+ group -> statements,
+ MDL);
+ executable_statement_reference (&ep -> next, et, MDL);
+ executable_statement_dereference (&group -> statements,
+ MDL);
+ executable_statement_reference (&group -> statements,
+ ep, MDL);
+ executable_statement_dereference (&ep, MDL);
+ } else {
+ executable_statement_reference (&group -> statements,
+ et, MDL);
+ }
+ executable_statement_dereference (&et, MDL);
+ return declaration;
+ }
+
+ return 0;
+}
+
+#if defined (FAILOVER_PROTOCOL)
+void parse_failover_peer (cfile, group, type)
+ struct parse *cfile;
+ struct group *group;
+ int type;
+{
+ enum dhcp_token token;
+ const char *val;
+ dhcp_failover_state_t *peer;
+ u_int32_t *tp;
+ char *name;
+ u_int32_t split;
+ u_int8_t hba [32];
+ unsigned hba_len = sizeof hba;
+ int i;
+ struct expression *expr;
+ isc_result_t status;
+ dhcp_failover_config_t *cp;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != PEER) {
+ parse_warn (cfile, "expecting \"peer\"");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token) || token == STRING) {
+ name = dmalloc (strlen (val) + 1, MDL);
+ if (!name)
+ log_fatal ("no memory for peer name %s", name);
+ strcpy (name, val);
+ } else {
+ parse_warn (cfile, "expecting failover peer name.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* See if there's a peer declaration by this name. */
+ peer = (dhcp_failover_state_t *)0;
+ find_failover_peer (&peer, name, MDL);
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ dfree (name, MDL);
+ if (type != SHARED_NET_DECL)
+ parse_warn (cfile, "failover peer reference not %s",
+ "in shared-network declaration");
+ else {
+ if (!peer) {
+ parse_warn (cfile, "reference to unknown%s%s",
+ " failover peer ", name);
+ return;
+ }
+ dhcp_failover_state_reference
+ (&group -> shared_network -> failover_peer,
+ peer, MDL);
+ }
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ } else if (token == STATE) {
+ if (!peer) {
+ parse_warn (cfile, "state declaration for unknown%s%s",
+ " failover peer ", name);
+ return;
+ }
+ parse_failover_state_declaration (cfile, peer);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ } else if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ skip_to_semi (cfile);
+ }
+
+ /* Make sure this isn't a redeclaration. */
+ if (peer) {
+ parse_warn (cfile, "redeclaration of failover peer %s", name);
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+
+ status = dhcp_failover_state_allocate (&peer, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't allocate failover peer %s: %s",
+ name, isc_result_totext (status));
+
+ /* Save the name. */
+ peer -> name = name;
+
+ do {
+ cp = &peer -> me;
+ peer:
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case RBRACE:
+ break;
+
+ case PRIMARY:
+ peer -> i_am = primary;
+ break;
+
+ case SECONDARY:
+ peer -> i_am = secondary;
+ if (peer -> hba)
+ parse_warn (cfile,
+ "secondary may not define %s",
+ "load balance settings.");
+ break;
+
+ case PEER:
+ cp = &peer -> partner;
+ goto peer;
+
+ case ADDRESS:
+ expr = (struct expression *)0;
+ if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) {
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ option_cache (&cp -> address,
+ (struct data_string *)0, expr,
+ (struct option *)0, MDL);
+ expression_dereference (&expr, MDL);
+ break;
+
+ case PORT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting number");
+ skip_to_rbrace (cfile, 1);
+ }
+ cp -> port = atoi (val);
+ break;
+
+ case MAX_LEASE_MISBALANCE:
+ tp = &peer->max_lease_misbalance;
+ goto parse_idle;
+
+ case MAX_LEASE_OWNERSHIP:
+ tp = &peer->max_lease_ownership;
+ goto parse_idle;
+
+ case MAX_BALANCE:
+ tp = &peer->max_balance;
+ goto parse_idle;
+
+ case MIN_BALANCE:
+ tp = &peer->min_balance;
+ goto parse_idle;
+
+ case AUTO_PARTNER_DOWN:
+ tp = &peer->auto_partner_down;
+ goto parse_idle;
+
+ case MAX_RESPONSE_DELAY:
+ tp = &cp -> max_response_delay;
+ parse_idle:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting number.");
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ *tp = atoi (val);
+ break;
+
+ case MAX_UNACKED_UPDATES:
+ tp = &cp -> max_flying_updates;
+ goto parse_idle;
+
+ case MCLT:
+ tp = &peer -> mclt;
+ goto parse_idle;
+
+ case HBA:
+ hba_len = 32;
+ if (peer -> i_am == secondary)
+ parse_warn (cfile,
+ "secondary may not define %s",
+ "load balance settings.");
+ if (!parse_numeric_aggregate (cfile, hba, &hba_len,
+ COLON, 16, 8)) {
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ if (hba_len != 32) {
+ parse_warn (cfile,
+ "HBA must be exactly 32 bytes.");
+ dfree (hba, MDL);
+ break;
+ }
+ make_hba:
+ peer -> hba = dmalloc (32, MDL);
+ if (!peer -> hba) {
+ dfree (peer -> name, MDL);
+ dfree (peer, MDL);
+ }
+ memcpy (peer -> hba, hba, 32);
+ break;
+
+ case SPLIT:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (peer -> i_am == secondary)
+ parse_warn (cfile,
+ "secondary may not define %s",
+ "load balance settings.");
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting number");
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ split = atoi (val);
+ if (split > 255) {
+ parse_warn (cfile, "split must be < 256");
+ } else {
+ memset (hba, 0, sizeof hba);
+ for (i = 0; i < split; i++) {
+ if (i < split)
+ hba [i / 8] |= (1 << (i & 7));
+ }
+ goto make_hba;
+ }
+ break;
+
+ case LOAD:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != BALANCE) {
+ parse_warn (cfile, "expecting 'balance'");
+ badload:
+ skip_to_rbrace (cfile, 1);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != TOKEN_MAX) {
+ parse_warn (cfile, "expecting 'max'");
+ goto badload;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SECONDS) {
+ parse_warn (cfile, "expecting 'secs'");
+ goto badload;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting number");
+ goto badload;
+ }
+ peer -> load_balance_max_secs = atoi (val);
+ break;
+
+ default:
+ parse_warn (cfile,
+ "invalid statement in peer declaration");
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ if (token != RBRACE && !parse_semi (cfile)) {
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&peer, MDL);
+ return;
+ }
+ } while (token != RBRACE);
+
+ /* me.address can be null; the failover link initiate code tries to
+ * derive a reasonable address to use.
+ */
+ if (!peer -> partner.address)
+ parse_warn (cfile, "peer address may not be omitted");
+
+ if (!peer->me.port)
+ peer->me.port = DEFAULT_FAILOVER_PORT;
+ if (!peer->partner.port)
+ peer->partner.port = DEFAULT_FAILOVER_PORT;
+
+ if (peer -> i_am == primary) {
+ if (!peer -> hba) {
+ parse_warn (cfile,
+ "primary failover server must have hba or split.");
+ } else if (!peer -> mclt) {
+ parse_warn (cfile,
+ "primary failover server must have mclt.");
+ }
+ }
+
+ if (!peer->max_lease_misbalance)
+ peer->max_lease_misbalance = DEFAULT_MAX_LEASE_MISBALANCE;
+ if (!peer->max_lease_ownership)
+ peer->max_lease_ownership = DEFAULT_MAX_LEASE_OWNERSHIP;
+ if (!peer->max_balance)
+ peer->max_balance = DEFAULT_MAX_BALANCE_TIME;
+ if (!peer->min_balance)
+ peer->min_balance = DEFAULT_MIN_BALANCE_TIME;
+ if (!peer->me.max_flying_updates)
+ peer->me.max_flying_updates = DEFAULT_MAX_FLYING_UPDATES;
+ if (!peer->me.max_response_delay)
+ peer->me.max_response_delay = DEFAULT_MAX_RESPONSE_DELAY;
+
+ if (type == SHARED_NET_DECL)
+ group->shared_network->failover_peer = peer;
+
+ /* Set the initial state. */
+ if (peer -> i_am == primary) {
+ peer -> me.state = recover;
+ peer -> me.stos = cur_time;
+ peer -> partner.state = unknown_state;
+ peer -> partner.stos = cur_time;
+ } else {
+ peer -> me.state = recover;
+ peer -> me.stos = cur_time;
+ peer -> partner.state = unknown_state;
+ peer -> partner.stos = cur_time;
+ }
+
+ status = enter_failover_peer (peer);
+ if (status != ISC_R_SUCCESS)
+ parse_warn (cfile, "failover peer %s: %s",
+ peer -> name, isc_result_totext (status));
+ dhcp_failover_state_dereference (&peer, MDL);
+}
+
+void parse_failover_state_declaration (struct parse *cfile,
+ dhcp_failover_state_t *peer)
+{
+ enum dhcp_token token;
+ const char *val;
+ char *name;
+ dhcp_failover_state_t *state;
+ dhcp_failover_config_t *cp;
+
+ if (!peer) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != PEER) {
+ parse_warn (cfile, "expecting \"peer\"");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token) || token == STRING) {
+ name = dmalloc (strlen (val) + 1, MDL);
+ if (!name)
+ log_fatal ("failover peer name %s: no memory",
+ name);
+ strcpy (name, val);
+ } else {
+ parse_warn (cfile, "expecting failover peer name.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* See if there's a peer declaration by this name. */
+ state = (dhcp_failover_state_t *)0;
+ find_failover_peer (&state, name, MDL);
+ if (!state) {
+ parse_warn (cfile, "unknown failover peer: %s", name);
+ skip_to_semi (cfile);
+ return;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STATE) {
+ parse_warn (cfile, "expecting 'state'");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return;
+ }
+ } else {
+ state = (dhcp_failover_state_t *)0;
+ dhcp_failover_state_reference (&state, peer, MDL);
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ dhcp_failover_state_dereference (&state, MDL);
+ return;
+ }
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case RBRACE:
+ break;
+ case MY:
+ cp = &state -> me;
+ do_state:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STATE) {
+ parse_warn (cfile, "expecting 'state'");
+ goto bogus;
+ }
+ parse_failover_state (cfile,
+ &cp -> state, &cp -> stos);
+ break;
+
+ case PARTNER:
+ cp = &state -> partner;
+ goto do_state;
+
+ case MCLT:
+ if (state -> i_am == primary) {
+ parse_warn (cfile,
+ "mclt not valid for primary");
+ goto bogus;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting a number.");
+ goto bogus;
+ }
+ state -> mclt = atoi (val);
+ parse_semi (cfile);
+ break;
+
+ default:
+ parse_warn (cfile, "expecting state setting.");
+ bogus:
+ skip_to_rbrace (cfile, 1);
+ dhcp_failover_state_dereference (&state, MDL);
+ return;
+ }
+ } while (token != RBRACE);
+ dhcp_failover_state_dereference (&state, MDL);
+}
+
+void parse_failover_state (cfile, state, stos)
+ struct parse *cfile;
+ enum failover_state *state;
+ TIME *stos;
+{
+ enum dhcp_token token;
+ const char *val;
+ enum failover_state state_in;
+ TIME stos_in;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case UNKNOWN_STATE:
+ state_in = unknown_state;
+ break;
+
+ case PARTNER_DOWN:
+ state_in = partner_down;
+ break;
+
+ case NORMAL:
+ state_in = normal;
+ break;
+
+ case COMMUNICATIONS_INTERRUPTED:
+ state_in = communications_interrupted;
+ break;
+
+ case CONFLICT_DONE:
+ state_in = conflict_done;
+ break;
+
+ case RESOLUTION_INTERRUPTED:
+ state_in = resolution_interrupted;
+ break;
+
+ case POTENTIAL_CONFLICT:
+ state_in = potential_conflict;
+ break;
+
+ case RECOVER:
+ state_in = recover;
+ break;
+
+ case RECOVER_WAIT:
+ state_in = recover_wait;
+ break;
+
+ case RECOVER_DONE:
+ state_in = recover_done;
+ break;
+
+ case SHUTDOWN:
+ state_in = shut_down;
+ break;
+
+ case PAUSED:
+ state_in = paused;
+ break;
+
+ case STARTUP:
+ state_in = startup;
+ break;
+
+ default:
+ parse_warn (cfile, "unknown failover state");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ stos_in = cur_time;
+ } else {
+ if (token != AT) {
+ parse_warn (cfile, "expecting \"at\"");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ stos_in = parse_date (cfile);
+ if (!stos_in)
+ return;
+ }
+
+ /* Now that we've apparently gotten a clean parse, we
+ can trust that this is a state that was fully committed to
+ disk, so we can install it. */
+ *stos = stos_in;
+ *state = state_in;
+}
+#endif /* defined (FAILOVER_PROTOCOL) */
+
+/* Permit_list_match returns 1 if every element of the permit list in lhs
+ also appears in rhs. Note that this doesn't by itself mean that the
+ two lists are equal - to check for equality, permit_list_match has to
+ return 1 with (list1, list2) and with (list2, list1). */
+
+int permit_list_match (struct permit *lhs, struct permit *rhs)
+{
+ struct permit *plp, *prp;
+ int matched;
+
+ if (!lhs)
+ return 1;
+ if (!rhs)
+ return 0;
+ for (plp = lhs; plp; plp = plp -> next) {
+ matched = 0;
+ for (prp = rhs; prp; prp = prp -> next) {
+ if (prp -> type == plp -> type &&
+ (prp -> type != permit_class ||
+ prp -> class == plp -> class)) {
+ matched = 1;
+ break;
+ }
+ }
+ if (!matched)
+ return 0;
+ }
+ return 1;
+}
+
+void parse_pool_statement (cfile, group, type)
+ struct parse *cfile;
+ struct group *group;
+ int type;
+{
+ enum dhcp_token token;
+ const char *val;
+ int done = 0;
+ struct pool *pool, **p, *pp;
+ struct permit *permit;
+ struct permit **permit_head;
+ int declaration = 0;
+ isc_result_t status;
+ struct lease *lpchain = (struct lease *)0, *lp;
+ TIME t;
+ int is_allow = 0;
+
+ pool = (struct pool *)0;
+ status = pool_allocate (&pool, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("no memory for pool: %s",
+ isc_result_totext (status));
+
+ if (type == SUBNET_DECL)
+ shared_network_reference (&pool -> shared_network,
+ group -> subnet -> shared_network,
+ MDL);
+ else if (type == SHARED_NET_DECL)
+ shared_network_reference (&pool -> shared_network,
+ group -> shared_network, MDL);
+ else {
+ parse_warn(cfile, "Dynamic pools are only valid inside "
+ "subnet or shared-network statements.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (pool->shared_network == NULL ||
+ !clone_group(&pool->group, pool->shared_network->group, MDL))
+ log_fatal("can't clone pool group.");
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Inherit the failover peer from the shared network. */
+ if (pool -> shared_network -> failover_peer)
+ dhcp_failover_state_reference
+ (&pool -> failover_peer,
+ pool -> shared_network -> failover_peer, MDL);
+#endif
+
+ if (!parse_lbrace (cfile)) {
+ pool_dereference (&pool, MDL);
+ return;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case TOKEN_NO:
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != FAILOVER ||
+ (token = next_token (&val, (unsigned *)0,
+ cfile)) != PEER) {
+ parse_warn (cfile,
+ "expecting \"failover peer\".");
+ skip_to_semi (cfile);
+ continue;
+ }
+#if defined (FAILOVER_PROTOCOL)
+ if (pool -> failover_peer)
+ dhcp_failover_state_dereference
+ (&pool -> failover_peer, MDL);
+#endif
+ break;
+
+#if defined (FAILOVER_PROTOCOL)
+ case FAILOVER:
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != PEER) {
+ parse_warn (cfile, "expecting 'peer'.");
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting string.");
+ skip_to_semi (cfile);
+ break;
+ }
+ if (pool -> failover_peer)
+ dhcp_failover_state_dereference
+ (&pool -> failover_peer, MDL);
+ status = find_failover_peer (&pool -> failover_peer,
+ val, MDL);
+ if (status != ISC_R_SUCCESS)
+ parse_warn (cfile,
+ "failover peer %s: %s", val,
+ isc_result_totext (status));
+ else
+ pool -> failover_peer -> pool_count++;
+ parse_semi (cfile);
+ break;
+#endif
+
+ case RANGE:
+ next_token (&val, (unsigned *)0, cfile);
+ parse_address_range (cfile, group, type,
+ pool, &lpchain);
+ break;
+ case ALLOW:
+ permit_head = &pool -> permit_list;
+ /* remember the clause which leads to get_permit */
+ is_allow = 1;
+ get_permit:
+ permit = new_permit (MDL);
+ if (!permit)
+ log_fatal ("no memory for permit");
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case UNKNOWN:
+ permit -> type = permit_unknown_clients;
+ get_clients:
+ if (next_token (&val, (unsigned *)0,
+ cfile) != CLIENTS) {
+ parse_warn (cfile,
+ "expecting \"clients\"");
+ skip_to_semi (cfile);
+ free_permit (permit, MDL);
+ continue;
+ }
+ break;
+
+ case KNOWN_CLIENTS:
+ permit -> type = permit_known_clients;
+ break;
+
+ case UNKNOWN_CLIENTS:
+ permit -> type = permit_unknown_clients;
+ break;
+
+ case KNOWN:
+ permit -> type = permit_known_clients;
+ goto get_clients;
+
+ case AUTHENTICATED:
+ permit -> type = permit_authenticated_clients;
+ goto get_clients;
+
+ case UNAUTHENTICATED:
+ permit -> type =
+ permit_unauthenticated_clients;
+ goto get_clients;
+
+ case ALL:
+ permit -> type = permit_all_clients;
+ goto get_clients;
+ break;
+
+ case DYNAMIC:
+ permit -> type = permit_dynamic_bootp_clients;
+ if (next_token (&val, (unsigned *)0,
+ cfile) != TOKEN_BOOTP) {
+ parse_warn (cfile,
+ "expecting \"bootp\"");
+ skip_to_semi (cfile);
+ free_permit (permit, MDL);
+ continue;
+ }
+ goto get_clients;
+
+ case MEMBERS:
+ if (next_token (&val, (unsigned *)0,
+ cfile) != OF) {
+ parse_warn (cfile, "expecting \"of\"");
+ skip_to_semi (cfile);
+ free_permit (permit, MDL);
+ continue;
+ }
+ if (next_token (&val, (unsigned *)0,
+ cfile) != STRING) {
+ parse_warn (cfile,
+ "expecting class name.");
+ skip_to_semi (cfile);
+ free_permit (permit, MDL);
+ continue;
+ }
+ permit -> type = permit_class;
+ permit -> class = (struct class *)0;
+ find_class (&permit -> class, val, MDL);
+ if (!permit -> class)
+ parse_warn (cfile,
+ "no such class: %s", val);
+ break;
+
+ case AFTER:
+ if (pool->valid_from || pool->valid_until) {
+ parse_warn(cfile,
+ "duplicate \"after\" clause.");
+ skip_to_semi(cfile);
+ free_permit(permit, MDL);
+ continue;
+ }
+ t = parse_date_core(cfile);
+ permit->type = permit_after;
+ permit->after = t;
+ if (is_allow) {
+ pool->valid_from = t;
+ } else {
+ pool->valid_until = t;
+ }
+ break;
+
+ default:
+ parse_warn (cfile, "expecting permit type.");
+ skip_to_semi (cfile);
+ break;
+ }
+ while (*permit_head)
+ permit_head = &((*permit_head) -> next);
+ *permit_head = permit;
+ parse_semi (cfile);
+ break;
+
+ case DENY:
+ permit_head = &pool -> prohibit_list;
+ /* remember the clause which leads to get_permit */
+ is_allow = 0;
+ goto get_permit;
+
+ case RBRACE:
+ next_token (&val, (unsigned *)0, cfile);
+ done = 1;
+ break;
+
+ case END_OF_FILE:
+ /*
+ * We can get to END_OF_FILE if, for instance,
+ * the parse_statement() reads all available tokens
+ * and leaves us at the end.
+ */
+ parse_warn(cfile, "unexpected end of file");
+ goto cleanup;
+
+ default:
+ declaration = parse_statement (cfile, pool -> group,
+ POOL_DECL,
+ (struct host_decl *)0,
+ declaration);
+ break;
+ }
+ } while (!done);
+
+ /* See if there's already a pool into which we can merge this one. */
+ for (pp = pool -> shared_network -> pools; pp; pp = pp -> next) {
+ if (pp -> group -> statements != pool -> group -> statements)
+ continue;
+#if defined (FAILOVER_PROTOCOL)
+ if (pool -> failover_peer != pp -> failover_peer)
+ continue;
+#endif
+ if (!permit_list_match (pp -> permit_list,
+ pool -> permit_list) ||
+ !permit_list_match (pool -> permit_list,
+ pp -> permit_list) ||
+ !permit_list_match (pp -> prohibit_list,
+ pool -> prohibit_list) ||
+ !permit_list_match (pool -> prohibit_list,
+ pp -> prohibit_list))
+ continue;
+
+ /* Okay, we can merge these two pools. All we have to
+ do is fix up the leases, which all point to their pool. */
+ for (lp = lpchain; lp; lp = lp -> next) {
+ pool_dereference (&lp -> pool, MDL);
+ pool_reference (&lp -> pool, pp, MDL);
+ }
+ break;
+ }
+
+ /* If we didn't succeed in merging this pool into another, put
+ it on the list. */
+ if (!pp) {
+ p = &pool -> shared_network -> pools;
+ for (; *p; p = &((*p) -> next))
+ ;
+ pool_reference (p, pool, MDL);
+ }
+
+ /* Don't allow a pool declaration with no addresses, since it is
+ probably a configuration error. */
+ if (!lpchain) {
+ parse_warn (cfile, "Pool declaration with no address range.");
+ log_error ("Pool declarations must always contain at least");
+ log_error ("one range statement.");
+ }
+
+cleanup:
+ /* Dereference the lease chain. */
+ lp = (struct lease *)0;
+ while (lpchain) {
+ lease_reference (&lp, lpchain, MDL);
+ lease_dereference (&lpchain, MDL);
+ if (lp -> next) {
+ lease_reference (&lpchain, lp -> next, MDL);
+ lease_dereference (&lp -> next, MDL);
+ lease_dereference (&lp, MDL);
+ }
+ }
+ pool_dereference (&pool, MDL);
+}
+
+/* Expect a left brace; if there isn't one, skip over the rest of the
+ statement and return zero; otherwise, return 1. */
+
+int parse_lbrace (cfile)
+ struct parse *cfile;
+{
+ enum dhcp_token token;
+ const char *val;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LBRACE) {
+ parse_warn (cfile, "expecting left brace.");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* host-declaration :== hostname RBRACE parameters declarations LBRACE */
+
+void parse_host_declaration (cfile, group)
+ struct parse *cfile;
+ struct group *group;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct host_decl *host;
+ char *name;
+ int declaration = 0;
+ int dynamicp = 0;
+ int deleted = 0;
+ isc_result_t status;
+ int known;
+ struct option *option;
+ struct expression *expr = NULL;
+
+ name = parse_host_name (cfile);
+ if (!name) {
+ parse_warn (cfile, "expecting a name for host declaration.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ host = (struct host_decl *)0;
+ status = host_allocate (&host, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("can't allocate host decl struct %s: %s",
+ name, isc_result_totext (status));
+ host -> name = name;
+ if (!clone_group (&host -> group, group, MDL)) {
+ log_fatal ("can't clone group for host %s", name);
+ boom:
+ host_dereference (&host, MDL);
+ return;
+ }
+
+ if (!parse_lbrace (cfile))
+ goto boom;
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ break;
+ }
+ if (token == END_OF_FILE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ }
+ /* If the host declaration was created by the server,
+ remember to save it. */
+ if (token == DYNAMIC) {
+ dynamicp = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ }
+ /* If the host declaration was created by the server,
+ remember to save it. */
+ if (token == TOKEN_DELETED) {
+ deleted = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ }
+
+ if (token == GROUP) {
+ struct group_object *go;
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING && !is_identifier (token)) {
+ parse_warn (cfile,
+ "expecting string or identifier.");
+ skip_to_rbrace (cfile, 1);
+ break;
+ }
+ go = (struct group_object *)0;
+ if (!group_hash_lookup (&go, group_name_hash,
+ val, strlen (val), MDL)) {
+ parse_warn (cfile, "unknown group %s in host %s",
+ val, host -> name);
+ } else {
+ if (host -> named_group)
+ group_object_dereference
+ (&host -> named_group, MDL);
+ group_object_reference (&host -> named_group,
+ go, MDL);
+ group_object_dereference (&go, MDL);
+ }
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ }
+
+ if (token == UID) {
+ const char *s;
+ unsigned char *t = 0;
+ unsigned len;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ data_string_forget (&host -> client_identifier, MDL);
+
+ if (host->client_identifier.len != 0) {
+ parse_warn(cfile, "Host %s already has a "
+ "client identifier.",
+ host->name);
+ break;
+ }
+
+ /* See if it's a string or a cshl. */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, &len, cfile);
+ s = val;
+ host -> client_identifier.terminated = 1;
+ } else {
+ len = 0;
+ t = parse_numeric_aggregate
+ (cfile,
+ (unsigned char *)0, &len, ':', 16, 8);
+ if (!t) {
+ parse_warn (cfile,
+ "expecting hex list.");
+ skip_to_semi (cfile);
+ }
+ s = (const char *)t;
+ }
+ if (!buffer_allocate
+ (&host -> client_identifier.buffer,
+ len + host -> client_identifier.terminated, MDL))
+ log_fatal ("no memory for uid for host %s.",
+ host -> name);
+ host -> client_identifier.data =
+ host -> client_identifier.buffer -> data;
+ host -> client_identifier.len = len;
+ memcpy (host -> client_identifier.buffer -> data, s,
+ len + host -> client_identifier.terminated);
+ if (t)
+ dfree (t, MDL);
+
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ }
+
+ if (token == HOST_IDENTIFIER) {
+ if (host->host_id_option != NULL) {
+ parse_warn(cfile,
+ "only one host-identifier allowed "
+ "per host");
+ skip_to_rbrace(cfile, 1);
+ break;
+ }
+ next_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != OPTION) {
+ parse_warn(cfile,
+ "host-identifier must be an option");
+ skip_to_rbrace(cfile, 1);
+ break;
+ }
+ known = 0;
+ option = NULL;
+ status = parse_option_name(cfile, 1, &known, &option);
+ if ((status != ISC_R_SUCCESS) || (option == NULL)) {
+ break;
+ }
+ if (!known) {
+ parse_warn(cfile, "unknown option %s.%s",
+ option->universe->name,
+ option->name);
+ skip_to_rbrace(cfile, 1);
+ break;
+ }
+
+ if (! parse_option_data(&expr, cfile, 1, option)) {
+ skip_to_rbrace(cfile, 1);
+ option_dereference(&option, MDL);
+ break;
+ }
+
+ if (!parse_semi(cfile)) {
+ skip_to_rbrace(cfile, 1);
+ expression_dereference(&expr, MDL);
+ option_dereference(&option, MDL);
+ break;
+ }
+
+ option_reference(&host->host_id_option, option, MDL);
+ option_dereference(&option, MDL);
+ data_string_copy(&host->host_id,
+ &expr->data.const_data, MDL);
+ expression_dereference(&expr, MDL);
+ continue;
+ }
+
+ declaration = parse_statement(cfile, host->group, HOST_DECL,
+ host, declaration);
+ } while (1);
+
+ if (deleted) {
+ struct host_decl *hp = (struct host_decl *)0;
+ if (host_hash_lookup (&hp, host_name_hash,
+ (unsigned char *)host -> name,
+ strlen (host -> name), MDL)) {
+ delete_host (hp, 0);
+ host_dereference (&hp, MDL);
+ }
+ } else {
+ if (host -> named_group && host -> named_group -> group) {
+ if (host -> group -> statements ||
+ (host -> group -> authoritative !=
+ host -> named_group -> group -> authoritative)) {
+ if (host -> group -> next)
+ group_dereference (&host -> group -> next,
+ MDL);
+ group_reference (&host -> group -> next,
+ host -> named_group -> group,
+ MDL);
+ } else {
+ group_dereference (&host -> group, MDL);
+ group_reference (&host -> group,
+ host -> named_group -> group,
+ MDL);
+ }
+ }
+
+ if (dynamicp)
+ host -> flags |= HOST_DECL_DYNAMIC;
+ else
+ host -> flags |= HOST_DECL_STATIC;
+
+ status = enter_host (host, dynamicp, 0);
+ if (status != ISC_R_SUCCESS)
+ parse_warn (cfile, "host %s: %s", host -> name,
+ isc_result_totext (status));
+ }
+ host_dereference (&host, MDL);
+}
+
+/* class-declaration :== STRING LBRACE parameters declarations RBRACE
+*/
+
+int parse_class_declaration (cp, cfile, group, type)
+ struct class **cp;
+ struct parse *cfile;
+ struct group *group;
+ int type;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct class *class = (struct class *)0, *pc = (struct class *)0;
+ int declaration = 0;
+ int lose = 0;
+ struct data_string data;
+ char *name;
+ const char *tname;
+ struct executable_statement *stmt = (struct executable_statement *)0;
+ int new = 1;
+ isc_result_t status = ISC_R_FAILURE;
+ int matchedonce = 0;
+ int submatchedonce = 0;
+ unsigned code;
+
+ if (dhcpv6_class_once && local_family == AF_INET6) {
+ dhcpv6_class_once = 0;
+ log_error("WARNING: class declarations are not supported "
+ "for DHCPv6.");
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "Expecting class name");
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ /* See if there's already a class with the specified name. */
+ find_class (&pc, val, MDL);
+
+ /* If it is a class, we're updating it. If it's any of the other
+ * types (subclass, vendor or user class), the named class is a
+ * reference to the parent class so its mandatory.
+ */
+ if (pc && (type == CLASS_TYPE_CLASS)) {
+ class_reference(&class, pc, MDL);
+ new = 0;
+ class_dereference(&pc, MDL);
+ } else if (!pc && (type != CLASS_TYPE_CLASS)) {
+ parse_warn(cfile, "no class named %s", val);
+ skip_to_semi(cfile);
+ return 0;
+ }
+
+ /* The old vendor-class and user-class declarations had an implicit
+ match. We don't do the implicit match anymore. Instead, for
+ backward compatibility, we have an implicit-vendor-class and an
+ implicit-user-class. vendor-class and user-class declarations
+ are turned into subclasses of the implicit classes, and the
+ submatch expression of the implicit classes extracts the contents of
+ the vendor class or user class. */
+ if ((type == CLASS_TYPE_VENDOR) || (type == CLASS_TYPE_USER)) {
+ data.len = strlen (val);
+ data.buffer = (struct buffer *)0;
+ if (!buffer_allocate (&data.buffer, data.len + 1, MDL))
+ log_fatal ("no memory for class name.");
+ data.data = &data.buffer -> data [0];
+ data.terminated = 1;
+
+ tname = type ? "implicit-vendor-class" : "implicit-user-class";
+ } else if (type == CLASS_TYPE_CLASS) {
+ tname = val;
+ } else {
+ tname = (const char *)0;
+ }
+
+ if (tname) {
+ name = dmalloc (strlen (tname) + 1, MDL);
+ if (!name)
+ log_fatal ("No memory for class name %s.", tname);
+ strcpy (name, val);
+ } else
+ name = (char *)0;
+
+ /* If this is a straight subclass, parse the hash string. */
+ if (type == CLASS_TYPE_SUBCLASS) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, &data.len, cfile);
+ data.buffer = (struct buffer *)0;
+ if (!buffer_allocate (&data.buffer,
+ data.len + 1, MDL)) {
+ if (pc)
+ class_dereference (&pc, MDL);
+
+ return 0;
+ }
+ data.terminated = 1;
+ data.data = &data.buffer -> data [0];
+ memcpy ((char *)data.buffer -> data, val,
+ data.len + 1);
+ } else if (token == NUMBER_OR_NAME || token == NUMBER) {
+ memset (&data, 0, sizeof data);
+ if (!parse_cshl (&data, cfile)) {
+ if (pc)
+ class_dereference (&pc, MDL);
+ return 0;
+ }
+ } else {
+ parse_warn (cfile, "Expecting string or hex list.");
+ if (pc)
+ class_dereference (&pc, MDL);
+ return 0;
+ }
+ }
+
+ /* See if there's already a class in the hash table matching the
+ hash data. */
+ if (type != CLASS_TYPE_CLASS)
+ class_hash_lookup (&class, pc -> hash,
+ (const char *)data.data, data.len, MDL);
+
+ /* If we didn't find an existing class, allocate a new one. */
+ if (!class) {
+ /* Allocate the class structure... */
+ status = class_allocate (&class, MDL);
+ if (pc) {
+ group_reference (&class -> group, pc -> group, MDL);
+ class_reference (&class -> superclass, pc, MDL);
+ class -> lease_limit = pc -> lease_limit;
+ if (class -> lease_limit) {
+ class -> billed_leases =
+ dmalloc (class -> lease_limit *
+ sizeof (struct lease *), MDL);
+ if (!class -> billed_leases)
+ log_fatal ("no memory for billing");
+ memset (class -> billed_leases, 0,
+ (class -> lease_limit *
+ sizeof class -> billed_leases));
+ }
+ data_string_copy (&class -> hash_string, &data, MDL);
+ if (!pc -> hash &&
+ !class_new_hash (&pc->hash, SCLASS_HASH_SIZE, MDL))
+ log_fatal ("No memory for subclass hash.");
+ class_hash_add (pc -> hash,
+ (const char *)class -> hash_string.data,
+ class -> hash_string.len,
+ (void *)class, MDL);
+ } else {
+ if (class->group)
+ group_dereference(&class->group, MDL);
+ if (!clone_group (&class -> group, group, MDL))
+ log_fatal ("no memory to clone class group.");
+ }
+
+ /* If this is an implicit vendor or user class, add a
+ statement that causes the vendor or user class ID to
+ be sent back in the reply. */
+ if (type == CLASS_TYPE_VENDOR || type == CLASS_TYPE_USER) {
+ stmt = (struct executable_statement *)0;
+ if (!executable_statement_allocate (&stmt, MDL))
+ log_fatal ("no memory for class statement.");
+ stmt -> op = supersede_option_statement;
+ if (option_cache_allocate (&stmt -> data.option,
+ MDL)) {
+ stmt -> data.option -> data = data;
+ code = (type == CLASS_TYPE_VENDOR)
+ ? DHO_VENDOR_CLASS_IDENTIFIER
+ : DHO_USER_CLASS;
+ option_code_hash_lookup(
+ &stmt->data.option->option,
+ dhcp_universe.code_hash,
+ &code, 0, MDL);
+ }
+ class -> statements = stmt;
+ }
+
+ /* Save the name, if there is one. */
+ if (class->name != NULL)
+ dfree(class->name, MDL);
+ class->name = name;
+ }
+
+ if (type != CLASS_TYPE_CLASS)
+ data_string_forget(&data, MDL);
+
+ /* Spawned classes don't have to have their own settings. */
+ if (class -> superclass) {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI) {
+ next_token (&val, (unsigned *)0, cfile);
+ if (cp)
+ status = class_reference (cp, class, MDL);
+ class_dereference (&class, MDL);
+ if (pc)
+ class_dereference (&pc, MDL);
+ return cp ? (status == ISC_R_SUCCESS) : 1;
+ }
+ /* Give the subclass its own group. */
+ if (!clone_group (&class -> group, class -> group, MDL))
+ log_fatal ("can't clone class group.");
+
+ }
+
+ if (!parse_lbrace (cfile)) {
+ class_dereference (&class, MDL);
+ if (pc)
+ class_dereference (&pc, MDL);
+ return 0;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ } else if (token == DYNAMIC) {
+ class->flags |= CLASS_DECL_DYNAMIC;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ } else if (token == TOKEN_DELETED) {
+ class->flags |= CLASS_DECL_DELETED;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ } else if (token == MATCH) {
+ if (pc) {
+ parse_warn (cfile,
+ "invalid match in subclass.");
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token != IF)
+ goto submatch;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (matchedonce) {
+ parse_warn(cfile, "A class may only have "
+ "one 'match if' clause.");
+ skip_to_semi(cfile);
+ break;
+ }
+ matchedonce = 1;
+ if (class->expr)
+ expression_dereference(&class->expr, MDL);
+ if (!parse_boolean_expression (&class->expr, cfile,
+ &lose)) {
+ if (!lose) {
+ parse_warn (cfile,
+ "expecting boolean expr.");
+ skip_to_semi (cfile);
+ }
+ } else {
+#if defined (DEBUG_EXPRESSION_PARSE)
+ print_expression ("class match",
+ class -> expr);
+#endif
+ parse_semi (cfile);
+ }
+ } else if (token == SPAWN) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (pc) {
+ parse_warn (cfile,
+ "invalid spawn in subclass.");
+ skip_to_semi (cfile);
+ break;
+ }
+ class -> spawning = 1;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != WITH) {
+ parse_warn (cfile,
+ "expecting with after spawn");
+ skip_to_semi (cfile);
+ break;
+ }
+ submatch:
+ if (submatchedonce) {
+ parse_warn (cfile,
+ "can't override existing %s.",
+ "submatch/spawn");
+ skip_to_semi (cfile);
+ break;
+ }
+ submatchedonce = 1;
+ if (class->submatch)
+ expression_dereference(&class->submatch, MDL);
+ if (!parse_data_expression (&class -> submatch,
+ cfile, &lose)) {
+ if (!lose) {
+ parse_warn (cfile,
+ "expecting data expr.");
+ skip_to_semi (cfile);
+ }
+ } else {
+#if defined (DEBUG_EXPRESSION_PARSE)
+ print_expression ("class submatch",
+ class -> submatch);
+#endif
+ parse_semi (cfile);
+ }
+ } else if (token == LEASE) {
+ next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != LIMIT) {
+ parse_warn (cfile, "expecting \"limit\"");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NUMBER) {
+ parse_warn (cfile, "expecting a number");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ break;
+ }
+ class -> lease_limit = atoi (val);
+ if (class->billed_leases)
+ dfree(class->billed_leases, MDL);
+ class -> billed_leases =
+ dmalloc (class -> lease_limit *
+ sizeof (struct lease *), MDL);
+ if (!class -> billed_leases)
+ log_fatal ("no memory for billed leases.");
+ memset (class -> billed_leases, 0,
+ (class -> lease_limit *
+ sizeof class -> billed_leases));
+ have_billing_classes = 1;
+ parse_semi (cfile);
+ } else {
+ declaration = parse_statement (cfile, class -> group,
+ CLASS_DECL,
+ (struct host_decl *)0,
+ declaration);
+ }
+ } while (1);
+
+ if (class->flags & CLASS_DECL_DELETED) {
+ if (type == CLASS_TYPE_CLASS) {
+ struct class *theclass = NULL;
+
+ status = find_class(&theclass, class->name, MDL);
+ if (status == ISC_R_SUCCESS) {
+ delete_class(theclass, 0);
+ class_dereference(&theclass, MDL);
+ }
+ } else {
+ class_hash_delete(pc->hash,
+ (char *)class->hash_string.data,
+ class->hash_string.len, MDL);
+ }
+ } else if (type == CLASS_TYPE_CLASS && new) {
+ if (!collections -> classes)
+ class_reference (&collections -> classes, class, MDL);
+ else {
+ struct class *c;
+ for (c = collections -> classes;
+ c -> nic; c = c -> nic)
+ ;
+ class_reference (&c -> nic, class, MDL);
+ }
+ }
+
+ if (cp) /* should always be 0??? */
+ status = class_reference (cp, class, MDL);
+ class_dereference (&class, MDL);
+ if (pc)
+ class_dereference (&pc, MDL);
+ return cp ? (status == ISC_R_SUCCESS) : 1;
+}
+
+/* shared-network-declaration :==
+ hostname LBRACE declarations parameters RBRACE */
+
+void parse_shared_net_declaration (cfile, group)
+ struct parse *cfile;
+ struct group *group;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct shared_network *share;
+ char *name;
+ int declaration = 0;
+ isc_result_t status;
+
+ share = (struct shared_network *)0;
+ status = shared_network_allocate (&share, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't allocate shared subnet: %s",
+ isc_result_totext (status));
+ clone_group (&share -> group, group, MDL);
+ shared_network_reference (&share -> group -> shared_network,
+ share, MDL);
+
+ /* Get the name of the shared network... */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ token = next_token (&val, (unsigned *)0, cfile);
+
+ if (val [0] == 0) {
+ parse_warn (cfile, "zero-length shared network name");
+ val = "<no-name-given>";
+ }
+ name = dmalloc (strlen (val) + 1, MDL);
+ if (!name)
+ log_fatal ("no memory for shared network name");
+ strcpy (name, val);
+ } else {
+ name = parse_host_name (cfile);
+ if (!name) {
+ parse_warn (cfile,
+ "expecting a name for shared-network");
+ skip_to_semi (cfile);
+ shared_network_dereference (&share, MDL);
+ return;
+ }
+ }
+ share -> name = name;
+
+ if (!parse_lbrace (cfile)) {
+ shared_network_dereference (&share, MDL);
+ return;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!share -> subnets)
+ parse_warn (cfile,
+ "empty shared-network decl");
+ else
+ enter_shared_network (share);
+ shared_network_dereference (&share, MDL);
+ return;
+ } else if (token == END_OF_FILE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ } else if (token == INTERFACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
+ new_shared_network_interface (cfile, share, val);
+ if (!parse_semi (cfile))
+ break;
+ continue;
+ }
+
+ declaration = parse_statement (cfile, share -> group,
+ SHARED_NET_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+ shared_network_dereference (&share, MDL);
+}
+
+
+static int
+common_subnet_parsing(struct parse *cfile,
+ struct shared_network *share,
+ struct subnet *subnet) {
+ enum dhcp_token token;
+ struct subnet *t, *u;
+ const char *val;
+ int declaration = 0;
+
+ enter_subnet(subnet);
+
+ if (!parse_lbrace(cfile)) {
+ subnet_dereference(&subnet, MDL);
+ return 0;
+ }
+
+ do {
+ token = peek_token(&val, NULL, cfile);
+ if (token == RBRACE) {
+ token = next_token(&val, NULL, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ token = next_token(&val, NULL, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ } else if (token == INTERFACE) {
+ token = next_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ new_shared_network_interface(cfile, share, val);
+ if (!parse_semi(cfile))
+ break;
+ continue;
+ }
+ declaration = parse_statement(cfile, subnet->group,
+ SUBNET_DECL,
+ NULL,
+ declaration);
+ } while (1);
+
+ /* Add the subnet to the list of subnets in this shared net. */
+ if (share->subnets == NULL) {
+ subnet_reference(&share->subnets, subnet, MDL);
+ } else {
+ u = NULL;
+ for (t = share->subnets; t->next_sibling; t = t->next_sibling) {
+ if (subnet_inner_than(subnet, t, 0)) {
+ subnet_reference(&subnet->next_sibling, t, MDL);
+ if (u) {
+ subnet_dereference(&u->next_sibling,
+ MDL);
+ subnet_reference(&u->next_sibling,
+ subnet, MDL);
+ } else {
+ subnet_dereference(&share->subnets,
+ MDL);
+ subnet_reference(&share->subnets,
+ subnet, MDL);
+ }
+ subnet_dereference(&subnet, MDL);
+ return 1;
+ }
+ u = t;
+ }
+ subnet_reference(&t->next_sibling, subnet, MDL);
+ }
+ subnet_dereference(&subnet, MDL);
+ return 1;
+}
+
+/* subnet-declaration :==
+ net NETMASK netmask RBRACE parameters declarations LBRACE */
+
+void parse_subnet_declaration (cfile, share)
+ struct parse *cfile;
+ struct shared_network *share;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct subnet *subnet;
+ struct iaddr iaddr;
+ unsigned char addr [4];
+ unsigned len = sizeof addr;
+ isc_result_t status;
+
+ subnet = (struct subnet *)0;
+ status = subnet_allocate (&subnet, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Allocation of new subnet failed: %s",
+ isc_result_totext (status));
+ shared_network_reference (&subnet -> shared_network, share, MDL);
+
+ /*
+ * If our parent shared network was implicitly created by the software,
+ * and not explicitly configured by the user, then we actually put all
+ * configuration scope in the parent (the shared network and subnet
+ * share the same {}-level scope).
+ *
+ * Otherwise, we clone the parent group and continue as normal.
+ */
+ if (share->flags & SHARED_IMPLICIT) {
+ group_reference(&subnet->group, share->group, MDL);
+ } else {
+ if (!clone_group(&subnet->group, share->group, MDL)) {
+ log_fatal("Allocation of group for new subnet failed.");
+ }
+ }
+ subnet_reference (&subnet -> group -> subnet, subnet, MDL);
+
+ /* Get the network number... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) {
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> net = iaddr;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NETMASK) {
+ parse_warn (cfile, "Expecting netmask");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ /* Get the netmask... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) {
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+ memcpy (iaddr.iabuf, addr, len);
+ iaddr.len = len;
+ subnet -> netmask = iaddr;
+
+ /* Validate the network number/netmask pair. */
+ if (host_addr (subnet -> net, subnet -> netmask)) {
+ char *maskstr;
+
+ maskstr = strdup (piaddr (subnet -> netmask));
+ parse_warn (cfile,
+ "subnet %s netmask %s: bad subnet number/mask combination.",
+ piaddr (subnet -> net), maskstr);
+ free(maskstr);
+ subnet_dereference (&subnet, MDL);
+ skip_to_semi (cfile);
+ return;
+ }
+
+ common_subnet_parsing(cfile, share, subnet);
+}
+
+/* subnet6-declaration :==
+ net / bits RBRACE parameters declarations LBRACE */
+
+void
+parse_subnet6_declaration(struct parse *cfile, struct shared_network *share) {
+#if !defined(DHCPv6)
+ parse_warn(cfile, "No DHCPv6 support.");
+ skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+ struct subnet *subnet;
+ isc_result_t status;
+ enum dhcp_token token;
+ const char *val;
+ char *endp;
+ int ofs;
+ const static int mask[] = { 0x00, 0x80, 0xC0, 0xE0,
+ 0xF0, 0xF8, 0xFC, 0xFE };
+ struct iaddr iaddr;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "subnet6 statement is only supported "
+ "in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ subnet = NULL;
+ status = subnet_allocate(&subnet, MDL);
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("Allocation of new subnet failed: %s",
+ isc_result_totext(status));
+ }
+ shared_network_reference(&subnet->shared_network, share, MDL);
+
+ /*
+ * If our parent shared network was implicitly created by the software,
+ * and not explicitly configured by the user, then we actually put all
+ * configuration scope in the parent (the shared network and subnet
+ * share the same {}-level scope).
+ *
+ * Otherwise, we clone the parent group and continue as normal.
+ */
+ if (share->flags & SHARED_IMPLICIT) {
+ group_reference(&subnet->group, share->group, MDL);
+ } else {
+ if (!clone_group(&subnet->group, share->group, MDL)) {
+ log_fatal("Allocation of group for new subnet failed.");
+ }
+ }
+ subnet_reference(&subnet->group->subnet, subnet, MDL);
+
+ if (!parse_ip6_addr(cfile, &subnet->net)) {
+ subnet_dereference(&subnet, MDL);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SLASH) {
+ parse_warn(cfile, "Expecting a '/'.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "Expecting a number.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ subnet->prefix_len = strtol(val, &endp, 10);
+ if ((subnet->prefix_len < 0) ||
+ (subnet->prefix_len > 128) ||
+ (*endp != '\0')) {
+ parse_warn(cfile, "Expecting a number between 0 and 128.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!is_cidr_mask_valid(&subnet->net, subnet->prefix_len)) {
+ parse_warn(cfile, "New subnet mask too short.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Create a netmask.
+ */
+ subnet->netmask.len = 16;
+ ofs = subnet->prefix_len / 8;
+ if (ofs < subnet->netmask.len) {
+ subnet->netmask.iabuf[ofs] = mask[subnet->prefix_len % 8];
+ }
+ while (--ofs >= 0) {
+ subnet->netmask.iabuf[ofs] = 0xFF;
+ }
+
+ /* Validate the network number/netmask pair. */
+ iaddr = subnet_number(subnet->net, subnet->netmask);
+ if (memcmp(&iaddr, &subnet->net, 16) != 0) {
+ parse_warn(cfile,
+ "subnet %s/%d: prefix not long enough for address.",
+ piaddr(subnet->net), subnet->prefix_len);
+ subnet_dereference(&subnet, MDL);
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!common_subnet_parsing(cfile, share, subnet)) {
+ return;
+ }
+#endif /* defined(DHCPv6) */
+}
+
+/* group-declaration :== RBRACE parameters declarations LBRACE */
+
+void parse_group_declaration (cfile, group)
+ struct parse *cfile;
+ struct group *group;
+{
+ const char *val;
+ enum dhcp_token token;
+ struct group *g;
+ int declaration = 0;
+ struct group_object *t;
+ isc_result_t status;
+ char *name = NULL;
+ int deletedp = 0;
+ int dynamicp = 0;
+ int staticp = 0;
+
+ g = (struct group *)0;
+ if (!clone_group (&g, group, MDL))
+ log_fatal ("no memory for explicit group.");
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token) || token == STRING) {
+ next_token (&val, (unsigned *)0, cfile);
+
+ name = dmalloc (strlen (val) + 1, MDL);
+ if (!name)
+ log_fatal ("no memory for group decl name %s", val);
+ strcpy (name, val);
+ }
+
+ if (!parse_lbrace (cfile)) {
+ group_dereference (&g, MDL);
+ return;
+ }
+
+ do {
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ break;
+ } else if (token == END_OF_FILE) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ } else if (token == TOKEN_DELETED) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_semi (cfile);
+ deletedp = 1;
+ } else if (token == DYNAMIC) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_semi (cfile);
+ dynamicp = 1;
+ } else if (token == STATIC) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ parse_semi (cfile);
+ staticp = 1;
+ }
+ declaration = parse_statement (cfile, g, GROUP_DECL,
+ (struct host_decl *)0,
+ declaration);
+ } while (1);
+
+ if (name) {
+ if (deletedp) {
+ if (group_name_hash) {
+ t = (struct group_object *)0;
+ if (group_hash_lookup (&t, group_name_hash,
+ name,
+ strlen (name), MDL)) {
+ delete_group (t, 0);
+ }
+ }
+ } else {
+ t = (struct group_object *)0;
+ status = group_object_allocate (&t, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("no memory for group decl %s: %s",
+ val, isc_result_totext (status));
+ group_reference (&t -> group, g, MDL);
+ t -> name = name;
+ t -> flags = ((staticp ? GROUP_OBJECT_STATIC : 0) |
+ (dynamicp ? GROUP_OBJECT_DYNAMIC : 0) |
+ (deletedp ? GROUP_OBJECT_DELETED : 0));
+ supersede_group (t, 0);
+ }
+ if (t)
+ group_object_dereference (&t, MDL);
+ }
+}
+
+/* fixed-addr-parameter :== ip-addrs-or-hostnames SEMI
+ ip-addrs-or-hostnames :== ip-addr-or-hostname
+ | ip-addrs-or-hostnames ip-addr-or-hostname */
+
+int
+parse_fixed_addr_param(struct option_cache **oc,
+ struct parse *cfile,
+ enum dhcp_token type) {
+ int parse_ok;
+ const char *val;
+ enum dhcp_token token;
+ struct expression *expr = NULL;
+ struct expression *tmp, *new;
+ int status;
+
+ do {
+ tmp = NULL;
+ if (type == FIXED_ADDR) {
+ parse_ok = parse_ip_addr_or_hostname(&tmp, cfile, 1);
+ } else {
+ /* INSIST(type == FIXED_ADDR6); */
+ parse_ok = parse_ip6_addr_expr(&tmp, cfile);
+ }
+ if (parse_ok) {
+ if (expr != NULL) {
+ new = NULL;
+ status = make_concat(&new, expr, tmp);
+ expression_dereference(&expr, MDL);
+ expression_dereference(&tmp, MDL);
+ if (!status) {
+ return 0;
+ }
+ expr = new;
+ } else {
+ expr = tmp;
+ }
+ } else {
+ if (expr != NULL) {
+ expression_dereference (&expr, MDL);
+ }
+ return 0;
+ }
+ token = peek_token(&val, NULL, cfile);
+ if (token == COMMA) {
+ token = next_token(&val, NULL, cfile);
+ }
+ } while (token == COMMA);
+
+ if (!parse_semi(cfile)) {
+ if (expr) {
+ expression_dereference (&expr, MDL);
+ }
+ return 0;
+ }
+
+ status = option_cache(oc, NULL, expr, NULL, MDL);
+ expression_dereference(&expr, MDL);
+ return status;
+}
+
+/* lease_declaration :== LEASE ip_address LBRACE lease_parameters RBRACE
+
+ lease_parameters :== <nil>
+ | lease_parameter
+ | lease_parameters lease_parameter
+
+ lease_parameter :== STARTS date
+ | ENDS date
+ | TIMESTAMP date
+ | HARDWARE hardware-parameter
+ | UID hex_numbers SEMI
+ | HOSTNAME hostname SEMI
+ | CLIENT_HOSTNAME hostname SEMI
+ | CLASS identifier SEMI
+ | DYNAMIC_BOOTP SEMI */
+
+int parse_lease_declaration (struct lease **lp, struct parse *cfile)
+{
+ const char *val;
+ enum dhcp_token token;
+ unsigned char addr [4];
+ unsigned len = sizeof addr;
+ int seenmask = 0;
+ int seenbit;
+ char tbuf [32];
+ struct lease *lease;
+ struct executable_statement *on;
+ int lose;
+ TIME t;
+ int noequal, newbinding;
+ struct binding *binding;
+ struct binding_value *nv;
+ isc_result_t status;
+ struct option_cache *oc;
+ pair *p;
+ binding_state_t new_state;
+ unsigned buflen = 0;
+ struct class *class;
+
+ lease = (struct lease *)0;
+ status = lease_allocate (&lease, MDL);
+ if (status != ISC_R_SUCCESS)
+ return 0;
+
+ /* Get the address for which the lease has been issued. */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8)) {
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ memcpy (lease -> ip_addr.iabuf, addr, len);
+ lease -> ip_addr.len = len;
+
+ if (!parse_lbrace (cfile)) {
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+
+ do {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == RBRACE)
+ break;
+ else if (token == END_OF_FILE) {
+ parse_warn (cfile, "unexpected end of file");
+ break;
+ }
+ strncpy (tbuf, val, sizeof tbuf);
+ tbuf [(sizeof tbuf) - 1] = 0;
+
+ /* Parse any of the times associated with the lease. */
+ switch (token) {
+ case STARTS:
+ case ENDS:
+ case TIMESTAMP:
+ case TSTP:
+ case TSFP:
+ case ATSFP:
+ case CLTT:
+ t = parse_date (cfile);
+ switch (token) {
+ case STARTS:
+ seenbit = 1;
+ lease -> starts = t;
+ break;
+
+ case ENDS:
+ seenbit = 2;
+ lease -> ends = t;
+ break;
+
+ case TSTP:
+ seenbit = 65536;
+ lease -> tstp = t;
+ break;
+
+ case TSFP:
+ seenbit = 131072;
+ lease -> tsfp = t;
+ break;
+
+ case ATSFP:
+ seenbit = 262144;
+ lease->atsfp = t;
+ break;
+
+ case CLTT:
+ seenbit = 524288;
+ lease -> cltt = t;
+ break;
+
+ default: /* for gcc, we'll never get here. */
+ log_fatal ("Impossible error at %s:%d.", MDL);
+ return 0;
+ }
+ break;
+
+ /* Colon-separated hexadecimal octets... */
+ case UID:
+ seenbit = 8;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ unsigned char *tuid;
+ token = next_token (&val, &buflen, cfile);
+ if (buflen < sizeof lease -> uid_buf) {
+ tuid = lease -> uid_buf;
+ lease -> uid_max =
+ sizeof lease -> uid_buf;
+ } else {
+ tuid = ((unsigned char *)
+ dmalloc (buflen, MDL));
+ if (!tuid) {
+ log_error ("no space for uid");
+ lease_dereference (&lease,
+ MDL);
+ return 0;
+ }
+ lease -> uid_max = buflen;
+ }
+ lease -> uid_len = buflen;
+ memcpy (tuid, val, lease -> uid_len);
+ lease -> uid = tuid;
+ } else {
+ buflen = 0;
+ lease -> uid = (parse_numeric_aggregate
+ (cfile, (unsigned char *)0,
+ &buflen, ':', 16, 8));
+ if (!lease -> uid) {
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ lease -> uid_len = buflen;
+ lease -> uid_max = buflen;
+ if (lease -> uid_len == 0) {
+ lease -> uid = (unsigned char *)0;
+ parse_warn (cfile, "zero-length uid");
+ seenbit = 0;
+ parse_semi (cfile);
+ break;
+ }
+ }
+ parse_semi (cfile);
+ if (!lease -> uid) {
+ log_fatal ("No memory for lease uid");
+ }
+ break;
+
+ case CLASS:
+ seenbit = 32;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (!is_identifier (token)) {
+ if (token != SEMI)
+ skip_to_rbrace (cfile, 1);
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ parse_semi (cfile);
+ /* for now, we aren't using this. */
+ break;
+
+ case HARDWARE:
+ seenbit = 64;
+ parse_hardware_param (cfile,
+ &lease -> hardware_addr);
+ break;
+
+ case TOKEN_RESERVED:
+ seenbit = 0;
+ lease->flags |= RESERVED_LEASE;
+ parse_semi(cfile);
+ break;
+
+ case DYNAMIC_BOOTP:
+ seenbit = 0;
+ lease -> flags |= BOOTP_LEASE;
+ parse_semi (cfile);
+ break;
+
+ /* XXX: Reverse compatibility? */
+ case TOKEN_ABANDONED:
+ seenbit = 256;
+ lease -> binding_state = FTS_ABANDONED;
+ lease -> next_binding_state = FTS_ABANDONED;
+ parse_semi (cfile);
+ break;
+
+ case TOKEN_NEXT:
+ seenbit = 128;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != BINDING) {
+ parse_warn (cfile, "expecting 'binding'");
+ skip_to_semi (cfile);
+ break;
+ }
+ goto do_binding_state;
+
+ case REWIND:
+ seenbit = 512;
+ token = next_token(&val, NULL, cfile);
+ if (token != BINDING) {
+ parse_warn(cfile, "expecting 'binding'");
+ skip_to_semi(cfile);
+ break;
+ }
+ goto do_binding_state;
+
+ case BINDING:
+ seenbit = 256;
+
+ do_binding_state:
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != STATE) {
+ parse_warn (cfile, "expecting 'state'");
+ skip_to_semi (cfile);
+ break;
+ }
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case TOKEN_ABANDONED:
+ new_state = FTS_ABANDONED;
+ break;
+ case TOKEN_FREE:
+ new_state = FTS_FREE;
+ break;
+ case TOKEN_ACTIVE:
+ new_state = FTS_ACTIVE;
+ break;
+ case TOKEN_EXPIRED:
+ new_state = FTS_EXPIRED;
+ break;
+ case TOKEN_RELEASED:
+ new_state = FTS_RELEASED;
+ break;
+ case TOKEN_RESET:
+ new_state = FTS_RESET;
+ break;
+ case TOKEN_BACKUP:
+ new_state = FTS_BACKUP;
+ break;
+
+ /* RESERVED and BOOTP states preserved for
+ * compatibleness with older versions.
+ */
+ case TOKEN_RESERVED:
+ new_state = FTS_ACTIVE;
+ lease->flags |= RESERVED_LEASE;
+ break;
+ case TOKEN_BOOTP:
+ new_state = FTS_ACTIVE;
+ lease->flags |= BOOTP_LEASE;
+ break;
+
+ default:
+ parse_warn (cfile,
+ "%s: expecting a binding state.",
+ val);
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ if (seenbit == 256) {
+ lease -> binding_state = new_state;
+
+ /*
+ * Apply default/conservative next/rewind
+ * binding states if they haven't been set
+ * yet. These defaults will be over-ridden if
+ * they are set later in parsing.
+ */
+ if (!(seenmask & 128))
+ lease->next_binding_state = new_state;
+
+ /* The most conservative rewind state. */
+ if (!(seenmask & 512))
+ lease->rewind_binding_state = new_state;
+ } else if (seenbit == 128)
+ lease -> next_binding_state = new_state;
+ else if (seenbit == 512)
+ lease->rewind_binding_state = new_state;
+ else
+ log_fatal("Impossible condition at %s:%d.",
+ MDL);
+
+ parse_semi (cfile);
+ break;
+
+ case CLIENT_HOSTNAME:
+ seenbit = 1024;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == STRING) {
+ if (!parse_string (cfile,
+ &lease -> client_hostname,
+ (unsigned *)0)) {
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ } else {
+ lease -> client_hostname =
+ parse_host_name (cfile);
+ if (lease -> client_hostname)
+ parse_semi (cfile);
+ else {
+ parse_warn (cfile,
+ "expecting a hostname.");
+ skip_to_semi (cfile);
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ }
+ break;
+
+ case BILLING:
+ seenbit = 2048;
+ class = (struct class *)0;
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == CLASS) {
+ token = next_token (&val,
+ (unsigned *)0, cfile);
+ if (token != STRING) {
+ parse_warn (cfile, "expecting string");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ token = BILLING;
+ break;
+ }
+ if (lease -> billing_class)
+ class_dereference (&lease -> billing_class,
+ MDL);
+ find_class (&class, val, MDL);
+ if (!class)
+ parse_warn (cfile,
+ "unknown class %s", val);
+ parse_semi (cfile);
+ } else if (token == SUBCLASS) {
+ if (lease -> billing_class)
+ class_dereference (&lease -> billing_class,
+ MDL);
+ parse_class_declaration(&class, cfile, NULL,
+ CLASS_TYPE_SUBCLASS);
+ } else {
+ parse_warn (cfile, "expecting \"class\"");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ }
+ if (class) {
+ class_reference (&lease -> billing_class,
+ class, MDL);
+ class_dereference (&class, MDL);
+ }
+ break;
+
+ case ON:
+ on = (struct executable_statement *)0;
+ lose = 0;
+ if (!parse_on_statement (&on, cfile, &lose)) {
+ skip_to_rbrace (cfile, 1);
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ seenbit = 0;
+ if ((on -> data.on.evtypes & ON_EXPIRY) &&
+ on -> data.on.statements) {
+ seenbit |= 16384;
+ executable_statement_reference
+ (&lease -> on_expiry,
+ on -> data.on.statements, MDL);
+ }
+ if ((on -> data.on.evtypes & ON_RELEASE) &&
+ on -> data.on.statements) {
+ seenbit |= 32768;
+ executable_statement_reference
+ (&lease -> on_release,
+ on -> data.on.statements, MDL);
+ }
+ executable_statement_dereference (&on, MDL);
+ break;
+
+ case OPTION:
+ case SUPERSEDE:
+ noequal = 0;
+ seenbit = 0;
+ oc = (struct option_cache *)0;
+ if (parse_option_decl (&oc, cfile)) {
+ if (oc -> option -> universe !=
+ &agent_universe) {
+ parse_warn (cfile,
+ "agent option expected.");
+ option_cache_dereference (&oc, MDL);
+ break;
+ }
+ if (!lease -> agent_options &&
+ !(option_chain_head_allocate
+ (&lease -> agent_options, MDL))) {
+ log_error ("no memory to stash agent option");
+ break;
+ }
+ for (p = &lease -> agent_options -> first;
+ *p; p = &((*p) -> cdr))
+ ;
+ *p = cons (0, 0);
+ option_cache_reference (((struct option_cache **)
+ &((*p) -> car)), oc, MDL);
+ option_cache_dereference (&oc, MDL);
+ }
+ break;
+
+ case TOKEN_SET:
+ noequal = 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != NAME && token != NUMBER_OR_NAME) {
+ parse_warn (cfile,
+ "%s can't be a variable name",
+ val);
+ badset:
+ skip_to_semi (cfile);
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+
+ seenbit = 0;
+ special_set:
+ if (lease -> scope)
+ binding = find_binding (lease -> scope, val);
+ else
+ binding = (struct binding *)0;
+
+ if (!binding) {
+ if (!lease -> scope)
+ if (!(binding_scope_allocate
+ (&lease -> scope, MDL)))
+ log_fatal ("no memory for scope");
+ binding = dmalloc (sizeof *binding, MDL);
+ if (!binding)
+ log_fatal ("No memory for lease %s.",
+ "binding");
+ memset (binding, 0, sizeof *binding);
+ binding -> name =
+ dmalloc (strlen (val) + 1, MDL);
+ if (!binding -> name)
+ log_fatal ("No memory for binding %s.",
+ "name");
+ strcpy (binding -> name, val);
+ newbinding = 1;
+ } else {
+ newbinding = 0;
+ }
+
+ nv = NULL;
+ if (!binding_value_allocate(&nv, MDL))
+ log_fatal("no memory for binding value.");
+
+ if (!noequal) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != EQUAL) {
+ parse_warn (cfile,
+ "expecting '=' in set statement.");
+ goto badset;
+ }
+ }
+
+ if (!parse_binding_value(cfile, nv)) {
+ binding_value_dereference(&nv, MDL);
+ lease_dereference(&lease, MDL);
+ return 0;
+ }
+
+ if (newbinding) {
+ binding_value_reference(&binding->value,
+ nv, MDL);
+ binding->next = lease->scope->bindings;
+ lease->scope->bindings = binding;
+ } else {
+ binding_value_dereference(&binding->value, MDL);
+ binding_value_reference(&binding->value,
+ nv, MDL);
+ }
+
+ binding_value_dereference(&nv, MDL);
+ parse_semi(cfile);
+ break;
+
+ /* case NAME: */
+ default:
+ if (!strcasecmp (val, "ddns-fwd-name")) {
+ seenbit = 4096;
+ noequal = 1;
+ goto special_set;
+ } else if (!strcasecmp (val, "ddns-rev-name")) {
+ seenbit = 8192;
+ noequal = 1;
+ goto special_set;
+ } else
+ parse_warn(cfile, "Unexpected configuration "
+ "directive.");
+ skip_to_semi (cfile);
+ seenbit = 0;
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+
+ if (seenmask & seenbit) {
+ parse_warn (cfile,
+ "Too many %s parameters in lease %s\n",
+ tbuf, piaddr (lease -> ip_addr));
+ } else
+ seenmask |= seenbit;
+
+ } while (1);
+
+ /* If no binding state is specified, make one up. */
+ if (!(seenmask & 256)) {
+ if (lease -> ends > cur_time ||
+ lease -> on_expiry || lease -> on_release)
+ lease -> binding_state = FTS_ACTIVE;
+#if defined (FAILOVER_PROTOCOL)
+ else if (lease -> pool && lease -> pool -> failover_peer)
+ lease -> binding_state = FTS_EXPIRED;
+#endif
+ else
+ lease -> binding_state = FTS_FREE;
+ if (lease -> binding_state == FTS_ACTIVE) {
+#if defined (FAILOVER_PROTOCOL)
+ if (lease -> pool && lease -> pool -> failover_peer)
+ lease -> next_binding_state = FTS_EXPIRED;
+ else
+#endif
+ lease -> next_binding_state = FTS_FREE;
+ } else
+ lease -> next_binding_state = lease -> binding_state;
+
+ /* The most conservative rewind state implies no rewind. */
+ lease->rewind_binding_state = lease->binding_state;
+ }
+
+ if (!(seenmask & 65536))
+ lease -> tstp = lease -> ends;
+
+ lease_reference (lp, lease, MDL);
+ lease_dereference (&lease, MDL);
+ return 1;
+}
+
+/* Parse the right side of a 'binding value'.
+ *
+ * set foo = "bar"; is a string
+ * set foo = false; is a boolean
+ * set foo = %31; is a numeric value.
+ */
+static int
+parse_binding_value(struct parse *cfile, struct binding_value *value)
+{
+ struct data_string *data;
+ unsigned char *s;
+ const char *val;
+ unsigned buflen;
+ int token;
+
+ if ((cfile == NULL) || (value == NULL))
+ log_fatal("Invalid arguments at %s:%d.", MDL);
+
+ token = peek_token(&val, NULL, cfile);
+ if (token == STRING) {
+ token = next_token(&val, &buflen, cfile);
+
+ value->type = binding_data;
+ value->value.data.len = buflen;
+
+ data = &value->value.data;
+
+ if (!buffer_allocate(&data->buffer, buflen + 1, MDL))
+ log_fatal ("No memory for binding.");
+
+ memcpy(data->buffer->data, val, buflen + 1);
+
+ data->data = data->buffer->data;
+ data->terminated = 1;
+ } else if (token == NUMBER_OR_NAME) {
+ value->type = binding_data;
+
+ data = &value->value.data;
+ s = parse_numeric_aggregate(cfile, NULL, &data->len,
+ ':', 16, 8);
+ if (s == NULL) {
+ skip_to_semi(cfile);
+ return 0;
+ }
+
+ if (data->len) {
+ if (!buffer_allocate(&data->buffer, data->len + 1,
+ MDL))
+ log_fatal("No memory for binding.");
+
+ memcpy(data->buffer->data, s, data->len);
+ data->data = data->buffer->data;
+
+ dfree (s, MDL);
+ }
+ } else if (token == PERCENT) {
+ token = next_token(&val, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting decimal number.");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return 0;
+ }
+ value->type = binding_numeric;
+ value->value.intval = atol(val);
+ } else if (token == NAME) {
+ token = next_token(&val, NULL, cfile);
+ value->type = binding_boolean;
+ if (!strcasecmp(val, "true"))
+ value->value.boolean = 1;
+ else if (!strcasecmp(val, "false"))
+ value->value.boolean = 0;
+ else {
+ parse_warn(cfile, "expecting true or false");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return 0;
+ }
+ } else {
+ parse_warn (cfile, "expecting a constant value.");
+ if (token != SEMI)
+ skip_to_semi (cfile);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* address-range-declaration :== ip-address ip-address SEMI
+ | DYNAMIC_BOOTP ip-address ip-address SEMI */
+
+void parse_address_range (cfile, group, type, inpool, lpchain)
+ struct parse *cfile;
+ struct group *group;
+ int type;
+ struct pool *inpool;
+ struct lease **lpchain;
+{
+ struct iaddr low, high, net;
+ unsigned char addr [4];
+ unsigned len = sizeof addr;
+ enum dhcp_token token;
+ const char *val;
+ int dynamic = 0;
+ struct subnet *subnet;
+ struct shared_network *share;
+ struct pool *pool;
+ isc_result_t status;
+
+ if ((token = peek_token (&val,
+ (unsigned *)0, cfile)) == DYNAMIC_BOOTP) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ dynamic = 1;
+ }
+
+ /* Get the bottom address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (low.iabuf, addr, len);
+ low.len = len;
+
+ /* Only one address? */
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == SEMI)
+ high = low;
+ else {
+ /* Get the top address in the range... */
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return;
+ memcpy (high.iabuf, addr, len);
+ high.len = len;
+ }
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token != SEMI) {
+ parse_warn (cfile, "semicolon expected.");
+ skip_to_semi (cfile);
+ return;
+ }
+
+ if (type == SUBNET_DECL) {
+ subnet = group -> subnet;
+ share = subnet -> shared_network;
+ } else {
+ share = group -> shared_network;
+ for (subnet = share -> subnets;
+ subnet; subnet = subnet -> next_sibling) {
+ net = subnet_number (low, subnet -> netmask);
+ if (addr_eq (net, subnet -> net))
+ break;
+ }
+ if (!subnet) {
+ parse_warn (cfile, "address range not on network %s",
+ group -> shared_network -> name);
+ log_error ("Be sure to place pool statement after %s",
+ "related subnet declarations.");
+ return;
+ }
+ }
+
+ if (!inpool) {
+ struct pool *last = (struct pool *)0;
+
+ /* If we're permitting dynamic bootp for this range,
+ then look for a pool with an empty prohibit list and
+ a permit list with one entry that permits all clients. */
+ for (pool = share -> pools; pool; pool = pool -> next) {
+ if ((!dynamic && !pool -> permit_list &&
+ pool -> prohibit_list &&
+ !pool -> prohibit_list -> next &&
+ (pool -> prohibit_list -> type ==
+ permit_dynamic_bootp_clients)) ||
+ (dynamic && !pool -> prohibit_list &&
+ pool -> permit_list &&
+ !pool -> permit_list -> next &&
+ (pool -> permit_list -> type ==
+ permit_all_clients))) {
+ break;
+ }
+ last = pool;
+ }
+
+ /* If we didn't get a pool, make one. */
+ if (!pool) {
+ struct permit *p;
+ status = pool_allocate (&pool, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("no memory for ad-hoc pool: %s",
+ isc_result_totext (status));
+ p = new_permit (MDL);
+ if (!p)
+ log_fatal ("no memory for ad-hoc permit.");
+
+ /* Dynamic pools permit all clients. Otherwise
+ we prohibit BOOTP clients. */
+ if (dynamic) {
+ p -> type = permit_all_clients;
+ pool -> permit_list = p;
+ } else {
+ p -> type = permit_dynamic_bootp_clients;
+ pool -> prohibit_list = p;
+ }
+
+ if (share -> pools)
+ pool_reference (&last -> next, pool, MDL);
+ else
+ pool_reference (&share -> pools, pool, MDL);
+ shared_network_reference (&pool -> shared_network,
+ share, MDL);
+ if (!clone_group (&pool -> group, share -> group, MDL))
+ log_fatal ("no memory for anon pool group.");
+ } else {
+ pool = (struct pool *)0;
+ if (last)
+ pool_reference (&pool, last, MDL);
+ else
+ pool_reference (&pool, share -> pools, MDL);
+ }
+ } else {
+ pool = (struct pool *)0;
+ pool_reference (&pool, inpool, MDL);
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ if (pool -> failover_peer && dynamic) {
+ /* Doctor, do you think I'm overly sensitive
+ about getting bug reports I can't fix? */
+ parse_warn (cfile, "dynamic-bootp flag is %s",
+ "not permitted for address");
+ log_error ("range declarations where there is a failover");
+ log_error ("peer in scope. If you wish to declare an");
+ log_error ("address range from which dynamic bootp leases");
+ log_error ("can be allocated, please declare it within a");
+ log_error ("pool declaration that also contains the \"no");
+ log_error ("failover\" statement. The failover protocol");
+ log_error ("itself does not permit dynamic bootp - this");
+ log_error ("is not a limitation specific to the ISC DHCP");
+ log_error ("server. Please don't ask me to defend this");
+ log_error ("until you have read and really tried %s",
+ "to understand");
+ log_error ("the failover protocol specification.");
+
+ /* We don't actually bomb at this point - instead,
+ we let parse_lease_file notice the error and
+ bomb at that point - it's easier. */
+ }
+#endif /* FAILOVER_PROTOCOL */
+
+ /* Create the new address range... */
+ new_address_range (cfile, low, high, subnet, pool, lpchain);
+ pool_dereference (&pool, MDL);
+}
+
+#ifdef DHCPv6
+static void
+add_ipv6_pool_to_subnet(struct subnet *subnet, u_int16_t type,
+ struct iaddr *lo_addr, int bits, int units) {
+ struct ipv6_pool *pool;
+ struct shared_network *share;
+ struct in6_addr tmp_in6_addr;
+ int num_pools;
+ struct ipv6_pool **tmp;
+
+ share = subnet->shared_network;
+
+ /*
+ * Create our pool.
+ */
+ if (lo_addr->len != sizeof(tmp_in6_addr)) {
+ log_fatal("Internal error: Attempt to add non-IPv6 address "
+ "to IPv6 shared network.");
+ }
+ memcpy(&tmp_in6_addr, lo_addr->iabuf, sizeof(tmp_in6_addr));
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, type, &tmp_in6_addr,
+ bits, units, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory");
+ }
+
+ /*
+ * Add to our global IPv6 pool set.
+ */
+ if (add_ipv6_pool(pool) != ISC_R_SUCCESS) {
+ log_fatal ("Out of memory");
+ }
+
+ /*
+ * Link the pool to its network.
+ */
+ pool->subnet = NULL;
+ subnet_reference(&pool->subnet, subnet, MDL);
+ pool->shared_network = NULL;
+ shared_network_reference(&pool->shared_network, share, MDL);
+
+ /*
+ * Increase our array size for ipv6_pools in the shared_network.
+ */
+ if (share->ipv6_pools == NULL) {
+ num_pools = 0;
+ } else {
+ num_pools = 0;
+ while (share->ipv6_pools[num_pools] != NULL) {
+ num_pools++;
+ }
+ }
+ tmp = dmalloc(sizeof(struct ipv6_pool *) * (num_pools + 2), MDL);
+ if (tmp == NULL) {
+ log_fatal("Out of memory");
+ }
+ if (num_pools > 0) {
+ memcpy(tmp, share->ipv6_pools,
+ sizeof(struct ipv6_pool *) * num_pools);
+ }
+ if (share->ipv6_pools != NULL) {
+ dfree(share->ipv6_pools, MDL);
+ }
+ share->ipv6_pools = tmp;
+
+ /*
+ * Record this pool in our array of pools for this shared network.
+ */
+ ipv6_pool_reference(&share->ipv6_pools[num_pools], pool, MDL);
+ share->ipv6_pools[num_pools+1] = NULL;
+}
+
+/* address-range6-declaration :== ip-address6 ip-address6 SEMI
+ | ip-address6 SLASH number SEMI
+ | ip-address6 [SLASH number] TEMPORARY SEMI */
+
+void
+parse_address_range6(struct parse *cfile, struct group *group) {
+ struct iaddr lo, hi;
+ int bits;
+ enum dhcp_token token;
+ const char *val;
+ struct iaddrcidrnetlist *nets;
+ struct iaddrcidrnetlist *p;
+ u_int16_t type = D6O_IA_NA;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "range6 statement is only supported "
+ "in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /* This is enforced by the caller, this is just a sanity check. */
+ if (group->subnet == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /*
+ * Read starting address.
+ */
+ if (!parse_ip6_addr(cfile, &lo)) {
+ return;
+ }
+
+ /*
+ * See if we we're using range or CIDR notation or TEMPORARY
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SLASH) {
+ /*
+ * '/' means CIDR notation, so read the bits we want.
+ */
+ next_token(NULL, NULL, cfile);
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting number");
+ skip_to_semi(cfile);
+ return;
+ }
+ bits = atoi(val);
+ if ((bits < 0) || (bits > 128)) {
+ parse_warn(cfile, "networks have 0 to 128 bits");
+ skip_to_semi(cfile);
+ return;
+ }
+ if (!is_cidr_mask_valid(&lo, bits)) {
+ parse_warn(cfile, "network mask too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * can be temporary (RFC 4941 like)
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == TEMPORARY) {
+ if (bits < 64)
+ parse_warn(cfile, "temporary mask too short");
+ if (bits == 128)
+ parse_warn(cfile, "temporary singleton?");
+ token = next_token(NULL, NULL, cfile);
+ type = D6O_IA_TA;
+ }
+
+ add_ipv6_pool_to_subnet(group->subnet, type, &lo,
+ bits, 128);
+
+ } else if (token == TEMPORARY) {
+ /*
+ * temporary (RFC 4941)
+ */
+ type = D6O_IA_TA;
+ next_token(NULL, NULL, cfile);
+ bits = 64;
+ if (!is_cidr_mask_valid(&lo, bits)) {
+ parse_warn(cfile, "network mask too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ add_ipv6_pool_to_subnet(group->subnet, type, &lo,
+ bits, 128);
+ } else {
+ /*
+ * No '/', so we are looking for the end address of
+ * the IPv6 pool.
+ */
+ if (!parse_ip6_addr(cfile, &hi)) {
+ return;
+ }
+
+ /*
+ * Convert our range to a set of CIDR networks.
+ */
+ nets = NULL;
+ if (range2cidr(&nets, &lo, &hi) != ISC_R_SUCCESS) {
+ log_fatal("Error converting range to CIDR networks");
+ }
+
+ for (p=nets; p != NULL; p=p->next) {
+ add_ipv6_pool_to_subnet(group->subnet, type,
+ &p->cidrnet.lo_addr,
+ p->cidrnet.bits, 128);
+ }
+
+ free_iaddrcidrnetlist(&nets);
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "semicolon expected.");
+ skip_to_semi(cfile);
+ return;
+ }
+}
+
+/* prefix6-declaration :== ip-address6 ip-address6 SLASH number SEMI */
+
+void
+parse_prefix6(struct parse *cfile, struct group *group) {
+ struct iaddr lo, hi;
+ int bits;
+ enum dhcp_token token;
+ const char *val;
+ struct iaddrcidrnetlist *nets;
+ struct iaddrcidrnetlist *p;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "prefix6 statement is only supported "
+ "in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /* This is enforced by the caller, so it's just a sanity check. */
+ if (group->subnet == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /*
+ * Read starting and ending address.
+ */
+ if (!parse_ip6_addr(cfile, &lo)) {
+ return;
+ }
+ if (!parse_ip6_addr(cfile, &hi)) {
+ return;
+ }
+
+ /*
+ * Next is '/' number ';'.
+ */
+ token = next_token(NULL, NULL, cfile);
+ if (token != SLASH) {
+ parse_warn(cfile, "expecting '/'");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "expecting number");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return;
+ }
+ bits = atoi(val);
+ if ((bits <= 0) || (bits >= 128)) {
+ parse_warn(cfile, "networks have 0 to 128 bits (exclusive)");
+ return;
+ }
+ if (!is_cidr_mask_valid(&lo, bits) ||
+ !is_cidr_mask_valid(&hi, bits)) {
+ parse_warn(cfile, "network mask too short");
+ return;
+ }
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "semicolon expected.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Convert our range to a set of CIDR networks.
+ */
+ nets = NULL;
+ if (range2cidr(&nets, &lo, &hi) != ISC_R_SUCCESS) {
+ log_fatal("Error converting prefix to CIDR");
+ }
+
+ for (p = nets; p != NULL; p = p->next) {
+ /* Normalize and check. */
+ if (p->cidrnet.bits == 128) {
+ p->cidrnet.bits = bits;
+ }
+ if (p->cidrnet.bits > bits) {
+ parse_warn(cfile, "impossible mask length");
+ continue;
+ }
+ add_ipv6_pool_to_subnet(group->subnet, D6O_IA_PD,
+ &p->cidrnet.lo_addr,
+ p->cidrnet.bits, bits);
+ }
+
+ free_iaddrcidrnetlist(&nets);
+}
+
+/* fixed-prefix6 :== ip6-address SLASH number SEMI */
+
+void
+parse_fixed_prefix6(struct parse *cfile, struct host_decl *host_decl) {
+ struct iaddrcidrnetlist *ia, **h;
+ enum dhcp_token token;
+ const char *val;
+
+ /*
+ * Get the head of the fixed-prefix list.
+ */
+ h = &host_decl->fixed_prefix;
+
+ /*
+ * Walk to the end.
+ */
+ while (*h != NULL) {
+ h = &((*h)->next);
+ }
+
+ /*
+ * Allocate a new iaddrcidrnetlist structure.
+ */
+ ia = dmalloc(sizeof(*ia), MDL);
+ if (!ia) {
+ log_fatal("Out of memory");
+ }
+
+ /*
+ * Parse it.
+ */
+ if (!parse_ip6_addr(cfile, &ia->cidrnet.lo_addr)) {
+ dfree(ia, MDL);
+ return;
+ }
+ token = next_token(NULL, NULL, cfile);
+ if (token != SLASH) {
+ dfree(ia, MDL);
+ parse_warn(cfile, "expecting '/'");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ dfree(ia, MDL);
+ parse_warn(cfile, "expecting number");
+ if (token != SEMI)
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI) {
+ dfree(ia, MDL);
+ parse_warn(cfile, "semicolon expected.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Fill it.
+ */
+ ia->cidrnet.bits = atoi(val);
+ if ((ia->cidrnet.bits < 0) || (ia->cidrnet.bits > 128)) {
+ dfree(ia, MDL);
+ parse_warn(cfile, "networks have 0 to 128 bits");
+ return;
+ }
+ if (!is_cidr_mask_valid(&ia->cidrnet.lo_addr, ia->cidrnet.bits)) {
+ dfree(ia, MDL);
+ parse_warn(cfile, "network mask too short");
+ return;
+ }
+
+ /*
+ * Store it.
+ */
+ *h = ia;
+ return;
+}
+#endif /* DHCPv6 */
+
+/* allow-deny-keyword :== BOOTP
+ | BOOTING
+ | DYNAMIC_BOOTP
+ | UNKNOWN_CLIENTS */
+
+int parse_allow_deny (oc, cfile, flag)
+ struct option_cache **oc;
+ struct parse *cfile;
+ int flag;
+{
+ enum dhcp_token token;
+ const char *val;
+ unsigned char rf = flag;
+ unsigned code;
+ struct option *option = NULL;
+ struct expression *data = (struct expression *)0;
+ int status;
+
+ if (!make_const_data (&data, &rf, 1, 0, 1, MDL))
+ return 0;
+
+ token = next_token (&val, (unsigned *)0, cfile);
+ switch (token) {
+ case TOKEN_BOOTP:
+ code = SV_ALLOW_BOOTP;
+ break;
+
+ case BOOTING:
+ code = SV_ALLOW_BOOTING;
+ break;
+
+ case DYNAMIC_BOOTP:
+ code = SV_DYNAMIC_BOOTP;
+ break;
+
+ case UNKNOWN_CLIENTS:
+ code = SV_BOOT_UNKNOWN_CLIENTS;
+ break;
+
+ case DUPLICATES:
+ code = SV_DUPLICATES;
+ break;
+
+ case DECLINES:
+ code= SV_DECLINES;
+ break;
+
+ case CLIENT_UPDATES:
+ code = SV_CLIENT_UPDATES;
+ break;
+
+ case LEASEQUERY:
+ code = SV_LEASEQUERY;
+ break;
+
+ default:
+ parse_warn (cfile, "expecting allow/deny key");
+ skip_to_semi (cfile);
+ return 0;
+ }
+ /* Reference on option is passed to option cache. */
+ if (!option_code_hash_lookup(&option, server_universe.code_hash,
+ &code, 0, MDL))
+ log_fatal("Unable to find server option %u (%s:%d).",
+ code, MDL);
+ status = option_cache(oc, NULL, data, option, MDL);
+ expression_dereference (&data, MDL);
+ parse_semi (cfile);
+ return status;
+}
+
+void
+parse_ia_na_declaration(struct parse *cfile) {
+#if !defined(DHCPv6)
+ parse_warn(cfile, "No DHCPv6 support.");
+ skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+ enum dhcp_token token;
+ struct ia_xx *ia;
+ const char *val;
+ struct ia_xx *old_ia;
+ unsigned int len;
+ u_int32_t iaid;
+ struct iaddr iaddr;
+ binding_state_t state;
+ u_int32_t prefer;
+ u_int32_t valid;
+ TIME end_time;
+ struct iasubopt *iaaddr;
+ struct ipv6_pool *pool;
+ char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ isc_boolean_t newbinding;
+ struct binding_scope *scope=NULL;
+ struct binding *bnd;
+ struct binding_value *nv=NULL;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "IA_NA is only supported in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting an iaid+ia_na string");
+ skip_to_semi(cfile);
+ return;
+ }
+ if (len < 5) {
+ parse_warn(cfile, "corrupt lease file; "
+ "iaid+ia_na string too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memcpy(&iaid, val, 4);
+ ia = NULL;
+ if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ ia->ia_type = D6O_IA_NA;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ if (token == CLTT) {
+ ia->cltt = parse_date (cfile);
+ continue;
+ }
+
+ if (token != IAADDR) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting IAADDR or right brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!parse_ip6_addr(cfile, &iaddr)) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting IPv6 address");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ state = FTS_LAST+1;
+ prefer = valid = 0;
+ end_time = -1;
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ switch(token) {
+ /* Lease binding state. */
+ case BINDING:
+ token = next_token(&val, NULL, cfile);
+ if (token != STATE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting state");
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case TOKEN_ABANDONED:
+ state = FTS_ABANDONED;
+ break;
+ case TOKEN_FREE:
+ state = FTS_FREE;
+ break;
+ case TOKEN_ACTIVE:
+ state = FTS_ACTIVE;
+ break;
+ case TOKEN_EXPIRED:
+ state = FTS_EXPIRED;
+ break;
+ case TOKEN_RELEASED:
+ state = FTS_RELEASED;
+ break;
+ default:
+ parse_warn(cfile,
+ "corrupt lease "
+ "file; "
+ "expecting a "
+ "binding state.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting "
+ "semicolon.");
+ }
+ break;
+
+ /* Lease preferred lifetime. */
+ case PREFERRED_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "preferred time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ prefer = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Lease valid lifetime. */
+ case MAX_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "max time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ valid = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Lease expiration time. */
+ case ENDS:
+ end_time = parse_date(cfile);
+ break;
+
+ /* Lease binding scopes. */
+ case TOKEN_SET:
+ token = next_token(&val, NULL, cfile);
+ if ((token != NAME) &&
+ (token != NUMBER_OR_NAME)) {
+ parse_warn(cfile, "%s is not a valid "
+ "variable name",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+
+ if (scope != NULL)
+ bnd = find_binding(scope, val);
+ else {
+ if (!binding_scope_allocate(&scope,
+ MDL)) {
+ log_fatal("Out of memory for "
+ "lease binding "
+ "scope.");
+ }
+
+ bnd = NULL;
+ }
+
+ if (bnd == NULL) {
+ bnd = dmalloc(sizeof(*bnd),
+ MDL);
+ if (bnd == NULL) {
+ log_fatal("No memory for "
+ "lease binding.");
+ }
+
+ bnd->name = dmalloc(strlen(val) + 1,
+ MDL);
+ if (bnd->name == NULL) {
+ log_fatal("No memory for "
+ "binding name.");
+ }
+ strcpy(bnd->name, val);
+
+ newbinding = ISC_TRUE;
+ } else {
+ newbinding = ISC_FALSE;
+ }
+
+ if (!binding_value_allocate(&nv, MDL)) {
+ log_fatal("no memory for binding "
+ "value.");
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != EQUAL) {
+ parse_warn(cfile, "expecting '=' in "
+ "set statement.");
+ goto binding_err;
+ }
+
+ if (!parse_binding_value(cfile, nv)) {
+ binding_err:
+ binding_value_dereference(&nv, MDL);
+ binding_scope_dereference(&scope, MDL);
+ return;
+ }
+
+ if (newbinding) {
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ bnd->next = scope->bindings;
+ scope->bindings = bnd;
+ } else {
+ binding_value_dereference(&bnd->value,
+ MDL);
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ }
+
+ binding_value_dereference(&nv, MDL);
+ parse_semi(cfile);
+ break;
+
+ default:
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting ia_na contents, "
+ "got '%s'", val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ }
+
+ if (state == FTS_LAST+1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing state in iaaddr");
+ return;
+ }
+ if (end_time == -1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing end time in iaaddr");
+ return;
+ }
+
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
+ iaaddr->plen = 0;
+ iaaddr->state = state;
+ iaaddr->prefer = prefer;
+ iaaddr->valid = valid;
+ if (iaaddr->state == FTS_RELEASED)
+ iaaddr->hard_lifetime_end_time = end_time;
+
+ if (scope != NULL) {
+ binding_scope_reference(&iaaddr->scope, scope, MDL);
+ binding_scope_dereference(&scope, MDL);
+ }
+
+ /* add to our various structures */
+ ia_add_iasubopt(ia, iaaddr, MDL);
+ ia_reference(&iaaddr->ia, ia, MDL);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, D6O_IA_NA,
+ &iaaddr->addr) != ISC_R_SUCCESS) {
+ inet_ntop(AF_INET6, &iaaddr->addr,
+ addr_buf, sizeof(addr_buf));
+ parse_warn(cfile, "no pool found for address %s",
+ addr_buf);
+ return;
+ }
+ add_lease6(pool, iaaddr, end_time);
+ ipv6_pool_dereference(&pool, MDL);
+ iasubopt_dereference(&iaaddr, MDL);
+ }
+
+ /*
+ * If we have an existing record for this IA_NA, remove it.
+ */
+ old_ia = NULL;
+ if (ia_hash_lookup(&old_ia, ia_na_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL)) {
+ ia_hash_delete(ia_na_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL);
+ ia_dereference(&old_ia, MDL);
+ }
+
+ /*
+ * If we have addresses, add this, otherwise don't bother.
+ */
+ if (ia->num_iasubopt > 0) {
+ ia_hash_add(ia_na_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, ia, MDL);
+ }
+ ia_dereference(&ia, MDL);
+#endif /* defined(DHCPv6) */
+}
+
+void
+parse_ia_ta_declaration(struct parse *cfile) {
+#if !defined(DHCPv6)
+ parse_warn(cfile, "No DHCPv6 support.");
+ skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+ enum dhcp_token token;
+ struct ia_xx *ia;
+ const char *val;
+ struct ia_xx *old_ia;
+ unsigned int len;
+ u_int32_t iaid;
+ struct iaddr iaddr;
+ binding_state_t state;
+ u_int32_t prefer;
+ u_int32_t valid;
+ TIME end_time;
+ struct iasubopt *iaaddr;
+ struct ipv6_pool *pool;
+ char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ isc_boolean_t newbinding;
+ struct binding_scope *scope=NULL;
+ struct binding *bnd;
+ struct binding_value *nv=NULL;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "IA_TA is only supported in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting an iaid+ia_ta string");
+ skip_to_semi(cfile);
+ return;
+ }
+ if (len < 5) {
+ parse_warn(cfile, "corrupt lease file; "
+ "iaid+ia_ta string too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memcpy(&iaid, val, 4);
+ ia = NULL;
+ if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ ia->ia_type = D6O_IA_TA;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ if (token == CLTT) {
+ ia->cltt = parse_date (cfile);
+ continue;
+ }
+
+ if (token != IAADDR) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting IAADDR or right brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!parse_ip6_addr(cfile, &iaddr)) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting IPv6 address");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ state = FTS_LAST+1;
+ prefer = valid = 0;
+ end_time = -1;
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ switch(token) {
+ /* Lease binding state. */
+ case BINDING:
+ token = next_token(&val, NULL, cfile);
+ if (token != STATE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting state");
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case TOKEN_ABANDONED:
+ state = FTS_ABANDONED;
+ break;
+ case TOKEN_FREE:
+ state = FTS_FREE;
+ break;
+ case TOKEN_ACTIVE:
+ state = FTS_ACTIVE;
+ break;
+ case TOKEN_EXPIRED:
+ state = FTS_EXPIRED;
+ break;
+ case TOKEN_RELEASED:
+ state = FTS_RELEASED;
+ break;
+ default:
+ parse_warn(cfile,
+ "corrupt lease "
+ "file; "
+ "expecting a "
+ "binding state.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting "
+ "semicolon.");
+ }
+ break;
+
+ /* Lease preferred lifetime. */
+ case PREFERRED_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "preferred time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ prefer = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Lease valid lifetime. */
+ case MAX_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "max time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ valid = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Lease expiration time. */
+ case ENDS:
+ end_time = parse_date(cfile);
+ break;
+
+ /* Lease binding scopes. */
+ case TOKEN_SET:
+ token = next_token(&val, NULL, cfile);
+ if ((token != NAME) &&
+ (token != NUMBER_OR_NAME)) {
+ parse_warn(cfile, "%s is not a valid "
+ "variable name",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+
+ if (scope != NULL)
+ bnd = find_binding(scope, val);
+ else {
+ if (!binding_scope_allocate(&scope,
+ MDL)) {
+ log_fatal("Out of memory for "
+ "lease binding "
+ "scope.");
+ }
+
+ bnd = NULL;
+ }
+
+ if (bnd == NULL) {
+ bnd = dmalloc(sizeof(*bnd),
+ MDL);
+ if (bnd == NULL) {
+ log_fatal("No memory for "
+ "lease binding.");
+ }
+
+ bnd->name = dmalloc(strlen(val) + 1,
+ MDL);
+ if (bnd->name == NULL) {
+ log_fatal("No memory for "
+ "binding name.");
+ }
+ strcpy(bnd->name, val);
+
+ newbinding = ISC_TRUE;
+ } else {
+ newbinding = ISC_FALSE;
+ }
+
+ if (!binding_value_allocate(&nv, MDL)) {
+ log_fatal("no memory for binding "
+ "value.");
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != EQUAL) {
+ parse_warn(cfile, "expecting '=' in "
+ "set statement.");
+ goto binding_err;
+ }
+
+ if (!parse_binding_value(cfile, nv)) {
+ binding_err:
+ binding_value_dereference(&nv, MDL);
+ binding_scope_dereference(&scope, MDL);
+ return;
+ }
+
+ if (newbinding) {
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ bnd->next = scope->bindings;
+ scope->bindings = bnd;
+ } else {
+ binding_value_dereference(&bnd->value,
+ MDL);
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ }
+
+ binding_value_dereference(&nv, MDL);
+ parse_semi(cfile);
+ break;
+
+ default:
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting ia_ta contents, "
+ "got '%s'", val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ }
+
+ if (state == FTS_LAST+1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing state in iaaddr");
+ return;
+ }
+ if (end_time == -1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing end time in iaaddr");
+ return;
+ }
+
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ memcpy(&iaaddr->addr, iaddr.iabuf, sizeof(iaaddr->addr));
+ iaaddr->plen = 0;
+ iaaddr->state = state;
+ iaaddr->prefer = prefer;
+ iaaddr->valid = valid;
+ if (iaaddr->state == FTS_RELEASED)
+ iaaddr->hard_lifetime_end_time = end_time;
+
+ if (scope != NULL) {
+ binding_scope_reference(&iaaddr->scope, scope, MDL);
+ binding_scope_dereference(&scope, MDL);
+ }
+
+ /* add to our various structures */
+ ia_add_iasubopt(ia, iaaddr, MDL);
+ ia_reference(&iaaddr->ia, ia, MDL);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, D6O_IA_TA,
+ &iaaddr->addr) != ISC_R_SUCCESS) {
+ inet_ntop(AF_INET6, &iaaddr->addr,
+ addr_buf, sizeof(addr_buf));
+ parse_warn(cfile, "no pool found for address %s",
+ addr_buf);
+ return;
+ }
+ add_lease6(pool, iaaddr, end_time);
+ ipv6_pool_dereference(&pool, MDL);
+ iasubopt_dereference(&iaaddr, MDL);
+ }
+
+ /*
+ * If we have an existing record for this IA_TA, remove it.
+ */
+ old_ia = NULL;
+ if (ia_hash_lookup(&old_ia, ia_ta_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL)) {
+ ia_hash_delete(ia_ta_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL);
+ ia_dereference(&old_ia, MDL);
+ }
+
+ /*
+ * If we have addresses, add this, otherwise don't bother.
+ */
+ if (ia->num_iasubopt > 0) {
+ ia_hash_add(ia_ta_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, ia, MDL);
+ }
+ ia_dereference(&ia, MDL);
+#endif /* defined(DHCPv6) */
+}
+
+void
+parse_ia_pd_declaration(struct parse *cfile) {
+#if !defined(DHCPv6)
+ parse_warn(cfile, "No DHCPv6 support.");
+ skip_to_semi(cfile);
+#else /* defined(DHCPv6) */
+ enum dhcp_token token;
+ struct ia_xx *ia;
+ const char *val;
+ struct ia_xx *old_ia;
+ unsigned int len;
+ u_int32_t iaid;
+ struct iaddr iaddr;
+ u_int8_t plen;
+ binding_state_t state;
+ u_int32_t prefer;
+ u_int32_t valid;
+ TIME end_time;
+ struct iasubopt *iapref;
+ struct ipv6_pool *pool;
+ char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ isc_boolean_t newbinding;
+ struct binding_scope *scope=NULL;
+ struct binding *bnd;
+ struct binding_value *nv=NULL;
+
+ if (local_family != AF_INET6) {
+ parse_warn(cfile, "IA_PD is only supported in DHCPv6 mode.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting an iaid+ia_pd string");
+ skip_to_semi(cfile);
+ return;
+ }
+ if (len < 5) {
+ parse_warn(cfile, "corrupt lease file; "
+ "iaid+ia_pd string too short");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memcpy(&iaid, val, 4);
+ ia = NULL;
+ if (ia_allocate(&ia, iaid, val+4, len-4, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ ia->ia_type = D6O_IA_PD;
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ if (token == CLTT) {
+ ia->cltt = parse_date (cfile);
+ continue;
+ }
+
+ if (token != IAPREFIX) {
+ parse_warn(cfile, "corrupt lease file; expecting "
+ "IAPREFIX or right brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ if (!parse_ip6_prefix(cfile, &iaddr, &plen)) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting IPv6 prefix");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != LBRACE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting left brace");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ state = FTS_LAST+1;
+ prefer = valid = 0;
+ end_time = -1;
+ for (;;) {
+ token = next_token(&val, NULL, cfile);
+ if (token == RBRACE) break;
+
+ switch(token) {
+ /* Prefix binding state. */
+ case BINDING:
+ token = next_token(&val, NULL, cfile);
+ if (token != STATE) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting state");
+ skip_to_semi(cfile);
+ return;
+ }
+ token = next_token(&val, NULL, cfile);
+ switch (token) {
+ case TOKEN_ABANDONED:
+ state = FTS_ABANDONED;
+ break;
+ case TOKEN_FREE:
+ state = FTS_FREE;
+ break;
+ case TOKEN_ACTIVE:
+ state = FTS_ACTIVE;
+ break;
+ case TOKEN_EXPIRED:
+ state = FTS_EXPIRED;
+ break;
+ case TOKEN_RELEASED:
+ state = FTS_RELEASED;
+ break;
+ default:
+ parse_warn(cfile,
+ "corrupt lease "
+ "file; "
+ "expecting a "
+ "binding state.");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting "
+ "semicolon.");
+ }
+ break;
+
+ /* Lease preferred lifetime. */
+ case PREFERRED_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "preferred time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ prefer = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Lease valid lifetime. */
+ case MAX_LIFE:
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "%s is not a valid "
+ "max time",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ valid = atoi (val);
+
+ /*
+ * Currently we peek for the semi-colon to
+ * allow processing of older lease files that
+ * don't have the semi-colon. Eventually we
+ * should remove the peeking code.
+ */
+ token = peek_token(&val, NULL, cfile);
+ if (token == SEMI) {
+ token = next_token(&val, NULL, cfile);
+ } else {
+ parse_warn(cfile,
+ "corrupt lease file; "
+ "expecting semicolon.");
+ }
+ break;
+
+ /* Prefix expiration time. */
+ case ENDS:
+ end_time = parse_date(cfile);
+ break;
+
+ /* Prefix binding scopes. */
+ case TOKEN_SET:
+ token = next_token(&val, NULL, cfile);
+ if ((token != NAME) &&
+ (token != NUMBER_OR_NAME)) {
+ parse_warn(cfile, "%s is not a valid "
+ "variable name",
+ val);
+ skip_to_semi(cfile);
+ continue;
+ }
+
+ if (scope != NULL)
+ bnd = find_binding(scope, val);
+ else {
+ if (!binding_scope_allocate(&scope,
+ MDL)) {
+ log_fatal("Out of memory for "
+ "lease binding "
+ "scope.");
+ }
+
+ bnd = NULL;
+ }
+
+ if (bnd == NULL) {
+ bnd = dmalloc(sizeof(*bnd),
+ MDL);
+ if (bnd == NULL) {
+ log_fatal("No memory for "
+ "prefix binding.");
+ }
+
+ bnd->name = dmalloc(strlen(val) + 1,
+ MDL);
+ if (bnd->name == NULL) {
+ log_fatal("No memory for "
+ "binding name.");
+ }
+ strcpy(bnd->name, val);
+
+ newbinding = ISC_TRUE;
+ } else {
+ newbinding = ISC_FALSE;
+ }
+
+ if (!binding_value_allocate(&nv, MDL)) {
+ log_fatal("no memory for binding "
+ "value.");
+ }
+
+ token = next_token(NULL, NULL, cfile);
+ if (token != EQUAL) {
+ parse_warn(cfile, "expecting '=' in "
+ "set statement.");
+ goto binding_err;
+ }
+
+ if (!parse_binding_value(cfile, nv)) {
+ binding_err:
+ binding_value_dereference(&nv, MDL);
+ binding_scope_dereference(&scope, MDL);
+ return;
+ }
+
+ if (newbinding) {
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ bnd->next = scope->bindings;
+ scope->bindings = bnd;
+ } else {
+ binding_value_dereference(&bnd->value,
+ MDL);
+ binding_value_reference(&bnd->value,
+ nv, MDL);
+ }
+
+ binding_value_dereference(&nv, MDL);
+ parse_semi(cfile);
+ break;
+
+ default:
+ parse_warn(cfile, "corrupt lease file; "
+ "expecting ia_pd contents, "
+ "got '%s'", val);
+ skip_to_semi(cfile);
+ continue;
+ }
+ }
+
+ if (state == FTS_LAST+1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing state in iaprefix");
+ return;
+ }
+ if (end_time == -1) {
+ parse_warn(cfile, "corrupt lease file; "
+ "missing end time in iaprefix");
+ return;
+ }
+
+ iapref = NULL;
+ if (iasubopt_allocate(&iapref, MDL) != ISC_R_SUCCESS) {
+ log_fatal("Out of memory.");
+ }
+ memcpy(&iapref->addr, iaddr.iabuf, sizeof(iapref->addr));
+ iapref->plen = plen;
+ iapref->state = state;
+ iapref->prefer = prefer;
+ iapref->valid = valid;
+ if (iapref->state == FTS_RELEASED)
+ iapref->hard_lifetime_end_time = end_time;
+
+ if (scope != NULL) {
+ binding_scope_reference(&iapref->scope, scope, MDL);
+ binding_scope_dereference(&scope, MDL);
+ }
+
+ /* add to our various structures */
+ ia_add_iasubopt(ia, iapref, MDL);
+ ia_reference(&iapref->ia, ia, MDL);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, D6O_IA_PD,
+ &iapref->addr) != ISC_R_SUCCESS) {
+ inet_ntop(AF_INET6, &iapref->addr,
+ addr_buf, sizeof(addr_buf));
+ parse_warn(cfile, "no pool found for address %s",
+ addr_buf);
+ return;
+ }
+ add_lease6(pool, iapref, end_time);
+ ipv6_pool_dereference(&pool, MDL);
+ iasubopt_dereference(&iapref, MDL);
+ }
+
+ /*
+ * If we have an existing record for this IA_PD, remove it.
+ */
+ old_ia = NULL;
+ if (ia_hash_lookup(&old_ia, ia_pd_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL)) {
+ ia_hash_delete(ia_pd_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, MDL);
+ ia_dereference(&old_ia, MDL);
+ }
+
+ /*
+ * If we have prefixes, add this, otherwise don't bother.
+ */
+ if (ia->num_iasubopt > 0) {
+ ia_hash_add(ia_pd_active,
+ (unsigned char *)ia->iaid_duid.data,
+ ia->iaid_duid.len, ia, MDL);
+ }
+ ia_dereference(&ia, MDL);
+#endif /* defined(DHCPv6) */
+}
+
+#ifdef DHCPv6
+/*
+ * When we parse a server-duid statement in a lease file, we are
+ * looking at the saved server DUID from a previous run. In this case
+ * we expect it to be followed by the binary representation of the
+ * DUID stored in a string:
+ *
+ * server-duid "\000\001\000\001\015\221\034JRT\000\0224Y";
+ */
+void
+parse_server_duid(struct parse *cfile) {
+ enum dhcp_token token;
+ const char *val;
+ unsigned int len;
+ struct data_string duid;
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "corrupt lease file; expecting a DUID");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ memset(&duid, 0, sizeof(duid));
+ duid.len = len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (unsigned char *)duid.buffer->data;
+ memcpy(duid.buffer->data, val, len);
+
+ set_server_duid(&duid);
+
+ data_string_forget(&duid, MDL);
+
+ token = next_token(&val, &len, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "corrupt lease file; expecting a semicolon");
+ skip_to_semi(cfile);
+ return;
+ }
+}
+
+/*
+ * When we parse a server-duid statement in a config file, we will
+ * have the type of the server DUID to generate, and possibly the
+ * actual value defined.
+ *
+ * server-duid llt;
+ * server-duid llt ethernet|ieee802|fddi 213982198 00:16:6F:49:7D:9B;
+ * server-duid ll;
+ * server-duid ll ethernet|ieee802|fddi 00:16:6F:49:7D:9B;
+ * server-duid en 2495 "enterprise-specific-identifier-1234";
+ */
+void
+parse_server_duid_conf(struct parse *cfile) {
+ enum dhcp_token token;
+ const char *val;
+ unsigned int len;
+ u_int32_t enterprise_number;
+ int ll_type;
+ struct data_string ll_addr;
+ u_int32_t llt_time;
+ struct data_string duid;
+ int duid_type_num;
+
+ /*
+ * Consume the SERVER_DUID token.
+ */
+ token = next_token(NULL, NULL, cfile);
+
+ /*
+ * Obtain the DUID type.
+ */
+ token = next_token(&val, NULL, cfile);
+
+ /*
+ * Enterprise is the easiest - enterprise number and raw data
+ * are required.
+ */
+ if (token == EN) {
+ /*
+ * Get enterprise number and identifier.
+ */
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "enterprise number expected");
+ skip_to_semi(cfile);
+ return;
+ }
+ enterprise_number = atoi(val);
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "identifier expected");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Save the DUID.
+ */
+ memset(&duid, 0, sizeof(duid));
+ duid.len = 2 + 4 + len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (unsigned char *)duid.buffer->data;
+ putUShort(duid.buffer->data, DUID_EN);
+ putULong(duid.buffer->data + 2, enterprise_number);
+ memcpy(duid.buffer->data + 6, val, len);
+
+ set_server_duid(&duid);
+ data_string_forget(&duid, MDL);
+ }
+
+ /*
+ * Next easiest is the link-layer DUID. It consists only of
+ * the LL directive, or optionally the specific value to use.
+ *
+ * If we have LL only, then we set the type. If we have the
+ * value, then we set the actual DUID.
+ */
+ else if (token == LL) {
+ if (peek_token(NULL, NULL, cfile) == SEMI) {
+ set_server_duid_type(DUID_LL);
+ } else {
+ /*
+ * Get our hardware type and address.
+ */
+ token = next_token(NULL, NULL, cfile);
+ switch (token) {
+ case ETHERNET:
+ ll_type = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ ll_type = HTYPE_IEEE802;
+ break;
+ case TOKEN_FDDI:
+ ll_type = HTYPE_FDDI;
+ break;
+ default:
+ parse_warn(cfile, "hardware type expected");
+ skip_to_semi(cfile);
+ return;
+ }
+ memset(&ll_addr, 0, sizeof(ll_addr));
+ if (!parse_cshl(&ll_addr, cfile)) {
+ return;
+ }
+
+ /*
+ * Save the DUID.
+ */
+ memset(&duid, 0, sizeof(duid));
+ duid.len = 2 + 2 + ll_addr.len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (unsigned char *)duid.buffer->data;
+ putUShort(duid.buffer->data, DUID_LL);
+ putULong(duid.buffer->data + 2, ll_type);
+ memcpy(duid.buffer->data + 4,
+ ll_addr.data, ll_addr.len);
+
+ set_server_duid(&duid);
+ data_string_forget(&duid, MDL);
+ data_string_forget(&ll_addr, MDL);
+ }
+ }
+
+ /*
+ * Finally the link-layer DUID plus time. It consists only of
+ * the LLT directive, or optionally the specific value to use.
+ *
+ * If we have LLT only, then we set the type. If we have the
+ * value, then we set the actual DUID.
+ */
+ else if (token == LLT) {
+ if (peek_token(NULL, NULL, cfile) == SEMI) {
+ set_server_duid_type(DUID_LLT);
+ } else {
+ /*
+ * Get our hardware type, timestamp, and address.
+ */
+ token = next_token(NULL, NULL, cfile);
+ switch (token) {
+ case ETHERNET:
+ ll_type = HTYPE_ETHER;
+ break;
+ case TOKEN_RING:
+ ll_type = HTYPE_IEEE802;
+ break;
+ case TOKEN_FDDI:
+ ll_type = HTYPE_FDDI;
+ break;
+ default:
+ parse_warn(cfile, "hardware type expected");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ token = next_token(&val, NULL, cfile);
+ if (token != NUMBER) {
+ parse_warn(cfile, "timestamp expected");
+ skip_to_semi(cfile);
+ return;
+ }
+ llt_time = atoi(val);
+
+ memset(&ll_addr, 0, sizeof(ll_addr));
+ if (!parse_cshl(&ll_addr, cfile)) {
+ return;
+ }
+
+ /*
+ * Save the DUID.
+ */
+ memset(&duid, 0, sizeof(duid));
+ duid.len = 2 + 2 + 4 + ll_addr.len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (unsigned char *)duid.buffer->data;
+ putUShort(duid.buffer->data, DUID_LLT);
+ putULong(duid.buffer->data + 2, ll_type);
+ putULong(duid.buffer->data + 4, llt_time);
+ memcpy(duid.buffer->data + 8,
+ ll_addr.data, ll_addr.len);
+
+ set_server_duid(&duid);
+ data_string_forget(&duid, MDL);
+ data_string_forget(&ll_addr, MDL);
+ }
+ }
+
+ /*
+ * If users want they can use a number for DUID types.
+ * This is useful for supporting future, not-yet-defined
+ * DUID types.
+ *
+ * In this case, they have to put in the complete value.
+ *
+ * This also works for existing DUID types of course.
+ */
+ else if (token == NUMBER) {
+ duid_type_num = atoi(val);
+
+ token = next_token(&val, &len, cfile);
+ if (token != STRING) {
+ parse_warn(cfile, "identifier expected");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Save the DUID.
+ */
+ memset(&duid, 0, sizeof(duid));
+ duid.len = 2 + len;
+ if (!buffer_allocate(&duid.buffer, duid.len, MDL)) {
+ log_fatal("Out of memory storing DUID");
+ }
+ duid.data = (unsigned char *)duid.buffer->data;
+ putUShort(duid.buffer->data, duid_type_num);
+ memcpy(duid.buffer->data + 2, val, len);
+
+ set_server_duid(&duid);
+ data_string_forget(&duid, MDL);
+ }
+
+ /*
+ * Anything else is an error.
+ */
+ else {
+ parse_warn(cfile, "DUID type of LLT, EN, or LL expected");
+ skip_to_semi(cfile);
+ return;
+ }
+
+ /*
+ * Finally consume our trailing semicolon.
+ */
+ token = next_token(NULL, NULL, cfile);
+ if (token != SEMI) {
+ parse_warn(cfile, "semicolon expected");
+ skip_to_semi(cfile);
+ }
+}
+
+#endif /* DHCPv6 */
+
diff --git a/server/db.c b/server/db.c
new file mode 100644
index 0000000..107d0b6
--- /dev/null
+++ b/server/db.c
@@ -0,0 +1,1180 @@
+/* db.c
+
+ Persistent database management routines for DHCPD... */
+
+/*
+ * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <ctype.h>
+#include <errno.h>
+
+static isc_result_t write_binding_scope(FILE *db_file, struct binding *bnd,
+ char *prepend);
+
+FILE *db_file;
+
+static int counting = 0;
+static int count = 0;
+TIME write_time;
+int lease_file_is_corrupt = 0;
+
+/* Write a single binding scope value in parsable format.
+ */
+
+static isc_result_t
+write_binding_scope(FILE *db_file, struct binding *bnd, char *prepend) {
+ char *s;
+
+ if ((db_file == NULL) || (bnd == NULL) || (prepend == NULL))
+ return DHCP_R_INVALIDARG;
+
+ if (bnd->value->type == binding_data) {
+ if (bnd->value->value.data.data != NULL) {
+ s = quotify_buf(bnd->value->value.data.data,
+ bnd->value->value.data.len, MDL);
+ if (s != NULL) {
+ errno = 0;
+ fprintf(db_file, "%sset %s = \"%s\";",
+ prepend, bnd->name, s);
+ if (errno)
+ return ISC_R_FAILURE;
+
+ dfree(s, MDL);
+ } else {
+ return ISC_R_FAILURE;
+ }
+ }
+ } else if (bnd->value->type == binding_numeric) {
+ errno = 0;
+ fprintf(db_file, "%sset %s = %%%ld;", prepend,
+ bnd->name, bnd->value->value.intval);
+ if (errno)
+ return ISC_R_FAILURE;
+ } else if (bnd->value->type == binding_boolean) {
+ errno = 0;
+ fprintf(db_file, "%sset %s = %s;", prepend, bnd->name,
+ bnd->value->value.intval ? "true" : "false");
+ if (errno)
+ return ISC_R_FAILURE;
+ } else if (bnd->value->type == binding_dns) {
+ log_error("%s: persistent dns values not supported.",
+ bnd->name);
+ } else if (bnd->value->type == binding_function) {
+ log_error("%s: persistent functions not supported.",
+ bnd->name);
+ } else {
+ log_fatal("%s: unknown binding type %d", bnd->name,
+ bnd->value->type);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/* Write the specified lease to the current lease database file. */
+
+int write_lease (lease)
+ struct lease *lease;
+{
+ int errors = 0;
+ struct binding *b;
+ char *s;
+ const char *tval;
+
+ /* If the lease file is corrupt, don't try to write any more leases
+ until we've written a good lease file. */
+ if (lease_file_is_corrupt)
+ if (!new_lease_file ())
+ return 0;
+
+ if (counting)
+ ++count;
+ errno = 0;
+ fprintf (db_file, "lease %s {", piaddr (lease -> ip_addr));
+ if (errno) {
+ ++errors;
+ }
+
+ if (lease->starts &&
+ ((tval = print_time(lease->starts)) == NULL ||
+ fprintf(db_file, "\n starts %s", tval) < 0))
+ ++errors;
+
+ if (lease->ends &&
+ ((tval = print_time(lease->ends)) == NULL ||
+ fprintf(db_file, "\n ends %s", tval) < 0))
+ ++errors;
+
+ if (lease->tstp &&
+ ((tval = print_time(lease->tstp)) == NULL ||
+ fprintf(db_file, "\n tstp %s", tval) < 0))
+ ++errors;
+
+ if (lease->tsfp &&
+ ((tval = print_time(lease->tsfp)) == NULL ||
+ fprintf(db_file, "\n tsfp %s", tval) < 0))
+ ++errors;
+
+ if (lease->atsfp &&
+ ((tval = print_time(lease->atsfp)) == NULL ||
+ fprintf(db_file, "\n atsfp %s", tval) < 0))
+ ++errors;
+
+ if (lease->cltt &&
+ ((tval = print_time(lease->cltt)) == NULL ||
+ fprintf(db_file, "\n cltt %s", tval) < 0))
+ ++errors;
+
+ if (fprintf (db_file, "\n binding state %s;",
+ ((lease -> binding_state > 0 &&
+ lease -> binding_state <= FTS_LAST)
+ ? binding_state_names [lease -> binding_state - 1]
+ : "abandoned")) < 0)
+ ++errors;
+
+ if (lease -> binding_state != lease -> next_binding_state)
+ if (fprintf (db_file, "\n next binding state %s;",
+ ((lease -> next_binding_state > 0 &&
+ lease -> next_binding_state <= FTS_LAST)
+ ? (binding_state_names
+ [lease -> next_binding_state - 1])
+ : "abandoned")) < 0)
+ ++errors;
+
+ /*
+ * In this case, if the rewind state is not present in the lease file,
+ * the reader will use the current binding state as the most
+ * conservative (safest) state. So if the in-memory rewind state is
+ * for some reason invalid, the best thing to do is not to write a
+ * state and let the reader take on a safe state.
+ */
+ if ((lease->binding_state != lease->rewind_binding_state) &&
+ (lease->rewind_binding_state > 0) &&
+ (lease->rewind_binding_state <= FTS_LAST) &&
+ (fprintf(db_file, "\n rewind binding state %s;",
+ binding_state_names[lease->rewind_binding_state-1])) < 0)
+ ++errors;
+
+ if (lease->flags & RESERVED_LEASE)
+ if (fprintf(db_file, "\n reserved;") < 0)
+ ++errors;
+
+ if (lease->flags & BOOTP_LEASE)
+ if (fprintf(db_file, "\n dynamic-bootp;") < 0)
+ ++errors;
+
+ /* If this lease is billed to a class and is still valid,
+ write it out. */
+ if (lease -> billing_class && lease -> ends > cur_time) {
+ if (!write_billing_class (lease -> billing_class)) {
+ log_error ("unable to write class %s",
+ lease -> billing_class -> name);
+ ++errors;
+ }
+ }
+
+ if (lease -> hardware_addr.hlen) {
+ errno = 0;
+ fprintf (db_file, "\n hardware %s %s;",
+ hardware_types [lease -> hardware_addr.hbuf [0]],
+ print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1]));
+ if (errno)
+ ++errors;
+ }
+ if (lease -> uid_len) {
+ s = quotify_buf (lease -> uid, lease -> uid_len, MDL);
+ if (s) {
+ errno = 0;
+ fprintf (db_file, "\n uid \"%s\";", s);
+ if (errno)
+ ++errors;
+ dfree (s, MDL);
+ } else
+ ++errors;
+ }
+
+ if (lease->scope != NULL) {
+ for (b = lease->scope->bindings; b; b = b->next) {
+ if (!b->value)
+ continue;
+
+ if (write_binding_scope(db_file, b, "\n ") != ISC_R_SUCCESS)
+ ++errors;
+ }
+ }
+
+ if (lease -> agent_options) {
+ struct option_cache *oc;
+ struct data_string ds;
+ pair p;
+
+ memset (&ds, 0, sizeof ds);
+ for (p = lease -> agent_options -> first; p; p = p -> cdr) {
+ oc = (struct option_cache *)p -> car;
+ if (oc -> data.len) {
+ errno = 0;
+ fprintf (db_file, "\n option agent.%s %s;",
+ oc -> option -> name,
+ pretty_print_option (oc -> option, oc -> data.data,
+ oc -> data.len, 1, 1));
+ if (errno)
+ ++errors;
+ }
+ }
+ }
+ if (lease -> client_hostname &&
+ db_printable((unsigned char *)lease->client_hostname)) {
+ s = quotify_string (lease -> client_hostname, MDL);
+ if (s) {
+ errno = 0;
+ fprintf (db_file, "\n client-hostname \"%s\";", s);
+ if (errno)
+ ++errors;
+ dfree (s, MDL);
+ } else
+ ++errors;
+ }
+ if (lease -> on_expiry) {
+ errno = 0;
+ fprintf (db_file, "\n on expiry%s {",
+ lease -> on_expiry == lease -> on_release
+ ? " or release" : "");
+ write_statements (db_file, lease -> on_expiry, 4);
+ /* XXX */
+ fprintf (db_file, "\n }");
+ if (errno)
+ ++errors;
+ }
+ if (lease -> on_release && lease -> on_release != lease -> on_expiry) {
+ errno = 0;
+ fprintf (db_file, "\n on release {");
+ write_statements (db_file, lease -> on_release, 4);
+ /* XXX */
+ fprintf (db_file, "\n }");
+ if (errno)
+ ++errors;
+ }
+
+ errno = 0;
+ fputs ("\n}\n", db_file);
+ if (errno)
+ ++errors;
+
+ if (errors) {
+ log_info ("write_lease: unable to write lease %s",
+ piaddr (lease -> ip_addr));
+ lease_file_is_corrupt = 1;
+ }
+
+ return !errors;
+}
+
+int write_host (host)
+ struct host_decl *host;
+{
+ int errors = 0;
+ int i;
+ struct data_string ip_addrs;
+
+ /* If the lease file is corrupt, don't try to write any more leases
+ until we've written a good lease file. */
+ if (lease_file_is_corrupt)
+ if (!new_lease_file ())
+ return 0;
+
+ if (!db_printable((unsigned char *)host->name))
+ return 0;
+
+ if (counting)
+ ++count;
+
+ errno = 0;
+ fprintf (db_file, "host %s {", host -> name);
+ if (errno)
+ ++errors;
+
+ if (host -> flags & HOST_DECL_DYNAMIC) {
+ errno = 0;
+ fprintf (db_file, "\n dynamic;");
+ if (errno)
+ ++errors;
+ }
+
+ if (host -> flags & HOST_DECL_DELETED) {
+ errno = 0;
+ fprintf (db_file, "\n deleted;");
+ if (errno)
+ ++errors;
+ } else {
+ if (host -> interface.hlen) {
+ errno = 0;
+ fprintf (db_file, "\n hardware %s %s;",
+ hardware_types [host -> interface.hbuf [0]],
+ print_hw_addr (host -> interface.hbuf [0],
+ host -> interface.hlen - 1,
+ &host -> interface.hbuf [1]));
+ if (errno)
+ ++errors;
+ }
+ if (host -> client_identifier.len) {
+ int i;
+ errno = 0;
+ if (db_printable_len (host -> client_identifier.data,
+ host -> client_identifier.len)) {
+ fprintf (db_file, "\n uid \"%.*s\";",
+ (int)host -> client_identifier.len,
+ host -> client_identifier.data);
+ if (errno)
+ ++errors;
+ } else {
+ fprintf (db_file,
+ "\n uid %2.2x",
+ host -> client_identifier.data [0]);
+ if (errno)
+ ++errors;
+ for (i = 1;
+ i < host -> client_identifier.len; i++) {
+ errno = 0;
+ fprintf (db_file, ":%2.2x",
+ host ->
+ client_identifier.data [i]);
+ if (errno)
+ ++errors;
+ }
+
+ errno = 0;
+ fputc (';', db_file);
+ if (errno)
+ ++errors;
+ }
+ }
+
+ memset (&ip_addrs, 0, sizeof ip_addrs);
+ if (host -> fixed_addr &&
+ evaluate_option_cache (&ip_addrs, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ host -> fixed_addr, MDL)) {
+
+ errno = 0;
+ fprintf (db_file, "\n fixed-address ");
+ if (errno)
+ ++errors;
+ for (i = 0; i < ip_addrs.len - 3; i += 4) {
+
+ errno = 0;
+ fprintf (db_file, "%u.%u.%u.%u%s",
+ ip_addrs.data [i] & 0xff,
+ ip_addrs.data [i + 1] & 0xff,
+ ip_addrs.data [i + 2] & 0xff,
+ ip_addrs.data [i + 3] & 0xff,
+ i + 7 < ip_addrs.len ? "," : "");
+ if (errno)
+ ++errors;
+ }
+
+ errno = 0;
+ fputc (';', db_file);
+ if (errno)
+ ++errors;
+ }
+
+ if (host -> named_group) {
+ errno = 0;
+ fprintf (db_file, "\n group \"%s\";",
+ host -> named_group -> name);
+ if (errno)
+ ++errors;
+ }
+
+ if (host -> group &&
+ (!host -> named_group ||
+ host -> group != host -> named_group -> group) &&
+ host -> group != root_group) {
+ errno = 0;
+ write_statements (db_file,
+ host -> group -> statements, 8);
+ if (errno)
+ ++errors;
+ }
+ }
+
+ errno = 0;
+ fputs ("\n}\n", db_file);
+ if (errno)
+ ++errors;
+
+ if (errors) {
+ log_info ("write_host: unable to write host %s",
+ host -> name);
+ lease_file_is_corrupt = 1;
+ }
+
+ return !errors;
+}
+
+int write_group (group)
+ struct group_object *group;
+{
+ int errors = 0;
+
+ /* If the lease file is corrupt, don't try to write any more leases
+ until we've written a good lease file. */
+ if (lease_file_is_corrupt)
+ if (!new_lease_file ())
+ return 0;
+
+ if (!db_printable((unsigned char *)group->name))
+ return 0;
+
+ if (counting)
+ ++count;
+
+ errno = 0;
+ fprintf (db_file, "group %s {", group -> name);
+ if (errno)
+ ++errors;
+
+ if (group -> flags & GROUP_OBJECT_DYNAMIC) {
+ errno = 0;
+ fprintf (db_file, "\n dynamic;");
+ if (errno)
+ ++errors;
+ }
+
+ if (group -> flags & GROUP_OBJECT_STATIC) {
+ errno = 0;
+ fprintf (db_file, "\n static;");
+ if (errno)
+ ++errors;
+ }
+
+ if (group -> flags & GROUP_OBJECT_DELETED) {
+ errno = 0;
+ fprintf (db_file, "\n deleted;");
+ if (errno)
+ ++errors;
+ } else {
+ if (group -> group) {
+ errno = 0;
+ write_statements (db_file,
+ group -> group -> statements, 8);
+ if (errno)
+ ++errors;
+ }
+ }
+
+ errno = 0;
+ fputs ("\n}\n", db_file);
+ if (errno)
+ ++errors;
+
+ if (errors) {
+ log_info ("write_group: unable to write group %s",
+ group -> name);
+ lease_file_is_corrupt = 1;
+ }
+
+ return !errors;
+}
+
+/*
+ * Write an IA and the options it has.
+ */
+int
+write_ia(const struct ia_xx *ia) {
+ struct iasubopt *iasubopt;
+ struct binding *bnd;
+ int i;
+ char addr_buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff.255.255.255.255")];
+ const char *binding_state;
+ const char *tval;
+ char *s;
+ int fprintf_ret;
+
+ /*
+ * If the lease file is corrupt, don't try to write any more
+ * leases until we've written a good lease file.
+ */
+ if (lease_file_is_corrupt) {
+ if (!new_lease_file()) {
+ return 0;
+ }
+ }
+
+ if (counting) {
+ ++count;
+ }
+
+
+ s = quotify_buf(ia->iaid_duid.data, ia->iaid_duid.len, MDL);
+ if (s == NULL) {
+ goto error_exit;
+ }
+ switch (ia->ia_type) {
+ case D6O_IA_NA:
+ fprintf_ret = fprintf(db_file, "ia-na \"%s\" {\n", s);
+ break;
+ case D6O_IA_TA:
+ fprintf_ret = fprintf(db_file, "ia-ta \"%s\" {\n", s);
+ break;
+ case D6O_IA_PD:
+ fprintf_ret = fprintf(db_file, "ia-pd \"%s\" {\n", s);
+ break;
+ default:
+ log_error("Unknown ia type %u for \"%s\" at %s:%d",
+ (unsigned)ia->ia_type, s, MDL);
+ fprintf_ret = -1;
+ }
+ dfree(s, MDL);
+ if (fprintf_ret < 0) {
+ goto error_exit;
+ }
+ if (ia->cltt != MIN_TIME) {
+ tval = print_time(ia->cltt);
+ if (tval == NULL) {
+ goto error_exit;
+ }
+ if (fprintf(db_file, " cltt %s\n", tval) < 0) {
+ goto error_exit;
+ }
+ }
+ for (i=0; i<ia->num_iasubopt; i++) {
+ iasubopt = ia->iasubopt[i];
+
+ inet_ntop(AF_INET6, &iasubopt->addr,
+ addr_buf, sizeof(addr_buf));
+ if ((ia->ia_type != D6O_IA_PD) &&
+ (fprintf(db_file, " iaaddr %s {\n", addr_buf) < 0)) {
+ goto error_exit;
+ }
+ if ((ia->ia_type == D6O_IA_PD) &&
+ (fprintf(db_file, " iaprefix %s/%d {\n",
+ addr_buf, (int)iasubopt->plen) < 0)) {
+ goto error_exit;
+ }
+ if ((iasubopt->state <= 0) || (iasubopt->state > FTS_LAST)) {
+ log_fatal("Unknown iasubopt state %d at %s:%d",
+ iasubopt->state, MDL);
+ }
+ binding_state = binding_state_names[iasubopt->state-1];
+ if (fprintf(db_file, " binding state %s;\n",
+ binding_state) < 0) {
+ goto error_exit;
+ }
+ if (fprintf(db_file, " preferred-life %u;\n",
+ (unsigned)iasubopt->prefer) < 0) {
+ goto error_exit;
+ }
+ if (fprintf(db_file, " max-life %u;\n",
+ (unsigned)iasubopt->valid) < 0) {
+ goto error_exit;
+ }
+
+ /* Note that from here on out, the \n is prepended to the
+ * next write, rather than appended to the current write.
+ */
+ if ((iasubopt->state == FTS_ACTIVE) ||
+ (iasubopt->state == FTS_ABANDONED) ||
+ (iasubopt->hard_lifetime_end_time != 0)) {
+ tval = print_time(iasubopt->hard_lifetime_end_time);
+ } else {
+ tval = print_time(iasubopt->soft_lifetime_end_time);
+ }
+ if (tval == NULL) {
+ goto error_exit;
+ }
+ if (fprintf(db_file, " ends %s", tval) < 0) {
+ goto error_exit;
+ }
+
+ /* Write out any binding scopes: note that 'ends' above does
+ * not have \n on the end! We want that.
+ */
+ if (iasubopt->scope != NULL)
+ bnd = iasubopt->scope->bindings;
+ else
+ bnd = NULL;
+
+ for (; bnd != NULL ; bnd = bnd->next) {
+ if (bnd->value == NULL)
+ continue;
+
+ /* We don't do a regular error_exit because the
+ * lease db is not corrupt in this case.
+ */
+ if (write_binding_scope(db_file, bnd,
+ "\n ") != ISC_R_SUCCESS)
+ goto error_exit;
+
+ }
+
+ if (fprintf(db_file, "\n }\n") < 0)
+ goto error_exit;
+ }
+ if (fprintf(db_file, "}\n\n") < 0)
+ goto error_exit;
+
+ fflush(db_file);
+ return 1;
+
+error_exit:
+ log_info("write_ia: unable to write ia");
+ lease_file_is_corrupt = 1;
+ return 0;
+}
+
+#ifdef DHCPv6
+/*
+ * Put a copy of the server DUID in the leases file.
+ */
+int
+write_server_duid(void) {
+ struct data_string server_duid;
+ char *s;
+ int fprintf_ret;
+
+ /*
+ * Only write the DUID if it's been set.
+ */
+ if (!server_duid_isset()) {
+ return 1;
+ }
+
+ /*
+ * If the lease file is corrupt, don't try to write any more
+ * leases until we've written a good lease file.
+ */
+ if (lease_file_is_corrupt) {
+ if (!new_lease_file()) {
+ return 0;
+ }
+ }
+
+ /*
+ * Get a copy of our server DUID and convert to a quoted string.
+ */
+ memset(&server_duid, 0, sizeof(server_duid));
+ copy_server_duid(&server_duid, MDL);
+ s = quotify_buf(server_duid.data, server_duid.len, MDL);
+ data_string_forget(&server_duid, MDL);
+ if (s == NULL) {
+ goto error_exit;
+ }
+
+ /*
+ * Write to the leases file.
+ */
+ fprintf_ret = fprintf(db_file, "server-duid \"%s\";\n\n", s);
+ dfree(s, MDL);
+ if (fprintf_ret < 0) {
+ goto error_exit;
+ }
+
+ /*
+ * Check if we actually managed to write.
+ */
+ fflush(db_file);
+ return 1;
+
+error_exit:
+ log_info("write_server_duid: unable to write server-duid");
+ lease_file_is_corrupt = 1;
+ return 0;
+}
+#endif /* DHCPv6 */
+
+#if defined (FAILOVER_PROTOCOL)
+int write_failover_state (dhcp_failover_state_t *state)
+{
+ int errors = 0;
+ const char *tval;
+
+ if (lease_file_is_corrupt)
+ if (!new_lease_file ())
+ return 0;
+
+ errno = 0;
+ fprintf (db_file, "\nfailover peer \"%s\" state {", state -> name);
+ if (errno)
+ ++errors;
+
+ tval = print_time(state->me.stos);
+ if (tval == NULL ||
+ fprintf(db_file, "\n my state %s at %s",
+ (state->me.state == startup) ?
+ dhcp_failover_state_name_print(state->saved_state) :
+ dhcp_failover_state_name_print(state->me.state),
+ tval) < 0)
+ ++errors;
+
+ tval = print_time(state->partner.stos);
+ if (tval == NULL ||
+ fprintf(db_file, "\n partner state %s at %s",
+ dhcp_failover_state_name_print(state->partner.state),
+ tval) < 0)
+ ++errors;
+
+ if (state -> i_am == secondary) {
+ errno = 0;
+ fprintf (db_file, "\n mclt %ld;",
+ (unsigned long)state -> mclt);
+ if (errno)
+ ++errors;
+ }
+
+ errno = 0;
+ fprintf (db_file, "\n}\n");
+ if (errno)
+ ++errors;
+
+ if (errors) {
+ log_info ("write_failover_state: unable to write state %s",
+ state -> name);
+ lease_file_is_corrupt = 1;
+ return 0;
+ }
+
+ return 1;
+
+}
+#endif
+
+int db_printable (s)
+ const unsigned char *s;
+{
+ int i;
+ for (i = 0; s [i]; i++)
+ if (!isascii (s [i]) || !isprint (s [i])
+ || s [i] == '"' || s [i] == '\\')
+ return 0;
+ return 1;
+}
+
+int db_printable_len (s, len)
+ const unsigned char *s;
+ unsigned len;
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (!isascii (s [i]) || !isprint (s [i]) ||
+ s [i] == '"' || s [i] == '\\')
+ return 0;
+ return 1;
+}
+
+static int print_hash_string(FILE *fp, struct class *class)
+{
+ int i;
+
+ for (i = 0 ; i < class->hash_string.len ; i++)
+ if (!isascii(class->hash_string.data[i]) ||
+ !isprint(class->hash_string.data[i]))
+ break;
+
+ if (i == class->hash_string.len) {
+ if (fprintf(fp, " \"%.*s\"", (int)class->hash_string.len,
+ class->hash_string.data) <= 0) {
+ log_error("Failure writing hash string: %m");
+ return 0;
+ }
+ } else {
+ if (fprintf(fp, " %2.2x", class->hash_string.data[0]) <= 0) {
+ log_error("Failure writing hash string: %m");
+ return 0;
+ }
+ for (i = 1 ; i < class->hash_string.len ; i++) {
+ if (fprintf(fp, ":%2.2x",
+ class->hash_string.data[i]) <= 0) {
+ log_error("Failure writing hash string: %m");
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+isc_result_t
+write_named_billing_class(const void *key, unsigned len, void *object)
+{
+ const unsigned char *name = key;
+ struct class *class = object;
+
+ if (class->flags & CLASS_DECL_DYNAMIC) {
+ numclasseswritten++;
+ if (class->superclass == 0) {
+ if (fprintf(db_file, "class \"%s\" {\n", name) <= 0)
+ return ISC_R_IOERROR;
+ } else {
+ if (fprintf(db_file, "subclass \"%s\"",
+ class->superclass->name) <= 0)
+ return ISC_R_IOERROR;
+ if (!print_hash_string(db_file, class))
+ return ISC_R_IOERROR;
+ if (fprintf(db_file, " {\n") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if ((class->flags & CLASS_DECL_DELETED) != 0) {
+ if (fprintf(db_file, " deleted;\n") <= 0)
+ return ISC_R_IOERROR;
+ } else {
+ if (fprintf(db_file, " dynamic;\n") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (class->lease_limit > 0) {
+ if (fprintf(db_file, " lease limit %d;\n",
+ class->lease_limit) <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (class->expr != 0) {
+ if (fprintf(db_file, " match if ") <= 0)
+ return ISC_R_IOERROR;
+
+ errno = 0;
+ write_expression(db_file, class->expr, 5, 5, 0);
+ if (errno)
+ return ISC_R_IOERROR;
+
+ if (fprintf(db_file, ";\n") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (class->submatch != 0) {
+ if (class->spawning) {
+ if (fprintf(db_file, " spawn ") <= 0)
+ return ISC_R_IOERROR;
+ } else {
+ if (fprintf(db_file, " match ") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ errno = 0;
+ write_expression(db_file, class->submatch, 5, 5, 0);
+ if (errno)
+ return ISC_R_IOERROR;
+
+ if (fprintf(db_file, ";\n") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (class->statements != 0) {
+ errno = 0;
+ write_statements(db_file, class->statements, 8);
+ if (errno)
+ return ISC_R_IOERROR;
+ }
+
+ /* XXXJAB this isn't right, but classes read in off the
+ leases file don't get the root group assigned to them
+ (due to clone_group() call). */
+ if (class->group != 0 && class->group->authoritative != 0) {
+ errno = 0;
+ write_statements(db_file, class->group->statements, 8);
+ if (errno)
+ return ISC_R_IOERROR;
+ }
+
+ if (fprintf(db_file, "}\n\n") <= 0)
+ return ISC_R_IOERROR;
+ }
+
+ if (class->hash != NULL) { /* yep. recursive. god help us. */
+ /* XXX - cannot check error status of this...
+ * foo_hash_foreach returns a count of operations completed.
+ */
+ class_hash_foreach(class->hash, write_named_billing_class);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void write_billing_classes ()
+{
+ struct collection *lp;
+ struct class *cp;
+
+ for (lp = collections; lp; lp = lp -> next) {
+ for (cp = lp -> classes; cp; cp = cp -> nic) {
+ if (cp -> spawning && cp -> hash) {
+ class_hash_foreach (cp -> hash, write_named_billing_class);
+ }
+ }
+ }
+}
+
+/* Write a spawned class to the database file. */
+
+int write_billing_class (class)
+ struct class *class;
+{
+ int errors = 0;
+
+ if (lease_file_is_corrupt)
+ if (!new_lease_file ())
+ return 0;
+
+ if (!class -> superclass) {
+ errno = 0;
+ fprintf (db_file, "\n billing class \"%s\";", class -> name);
+ return !errno;
+ }
+
+ if (fprintf(db_file, "\n billing subclass \"%s\"",
+ class -> superclass -> name) < 0)
+ ++errors;
+
+ if (!print_hash_string(db_file, class))
+ ++errors;
+
+ if (fprintf(db_file, ";") < 0)
+ ++errors;
+
+ class -> dirty = !errors;
+ if (errors)
+ lease_file_is_corrupt = 1;
+
+ return !errors;
+}
+
+/* Commit leases after a timeout. */
+void commit_leases_timeout (void *foo)
+{
+ commit_leases ();
+}
+
+/* Commit any leases that have been written out... */
+
+int commit_leases ()
+{
+ /* Commit any outstanding writes to the lease database file.
+ We need to do this even if we're rewriting the file below,
+ just in case the rewrite fails. */
+ if (fflush (db_file) == EOF) {
+ log_info ("commit_leases: unable to commit: %m");
+ return 0;
+ }
+ if (fsync (fileno (db_file)) < 0) {
+ log_info ("commit_leases: unable to commit: %m");
+ return 0;
+ }
+
+ /* send out all deferred ACKs now */
+ flush_ackqueue(NULL);
+
+ /* If we haven't rewritten the lease database in over an
+ hour, rewrite it now. (The length of time should probably
+ be configurable. */
+ if (count && cur_time - write_time > 3600) {
+ count = 0;
+ write_time = cur_time;
+ new_lease_file ();
+ }
+ return 1;
+}
+
+void db_startup (testp)
+ int testp;
+{
+ isc_result_t status;
+
+#if defined (TRACING)
+ if (!trace_playback ()) {
+#endif
+ /* Read in the existing lease file... */
+ status = read_conf_file (path_dhcpd_db,
+ (struct group *)0, 0, 1);
+ /* XXX ignore status? */
+#if defined (TRACING)
+ }
+#endif
+
+#if defined (TRACING)
+ /* If we're playing back, there is no lease file, so we can't
+ append it, so we create one immediately (maybe this isn't
+ the best solution... */
+ if (trace_playback ()) {
+ new_lease_file ();
+ }
+#endif
+ if (!testp) {
+ db_file = fopen (path_dhcpd_db, "a");
+ if (!db_file)
+ log_fatal ("Can't open %s for append.", path_dhcpd_db);
+ expire_all_pools ();
+#if defined (TRACING)
+ if (trace_playback ())
+ write_time = cur_time;
+ else
+#endif
+ time(&write_time);
+ new_lease_file ();
+ }
+
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("Host HW hash: %s", host_hash_report(host_hw_addr_hash));
+ log_info("Host UID hash: %s", host_hash_report(host_uid_hash));
+ log_info("Lease IP hash: %s",
+ lease_ip_hash_report(lease_ip_addr_hash));
+ log_info("Lease UID hash: %s", lease_id_hash_report(lease_uid_hash));
+ log_info("Lease HW hash: %s",
+ lease_id_hash_report(lease_hw_addr_hash));
+#endif
+}
+
+int new_lease_file ()
+{
+ char newfname [512];
+ char backfname [512];
+ TIME t;
+ int db_fd;
+ int db_validity;
+ FILE *new_db_file;
+
+ /* Make a temporary lease file... */
+ time(&t);
+
+ db_validity = lease_file_is_corrupt;
+
+ /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
+ * This should never happen since the path is a configuration
+ * variable from build-time or command-line. But if it should,
+ * either by malice or ignorance, we panic, since the potential
+ * for havoc is high.
+ */
+ if (snprintf (newfname, sizeof newfname, "%s.%d",
+ path_dhcpd_db, (int)t) >= sizeof newfname)
+ log_fatal("new_lease_file: lease file path too long");
+
+ db_fd = open (newfname, O_WRONLY | O_TRUNC | O_CREAT, 0664);
+ if (db_fd < 0) {
+ log_error ("Can't create new lease file: %m");
+ return 0;
+ }
+ if ((new_db_file = fdopen(db_fd, "w")) == NULL) {
+ log_error("Can't fdopen new lease file: %m");
+ close(db_fd);
+ goto fdfail;
+ }
+
+ /* Close previous database, if any. */
+ if (db_file)
+ fclose(db_file);
+ db_file = new_db_file;
+
+ errno = 0;
+ fprintf (db_file, "# The format of this file is documented in the %s",
+ "dhcpd.leases(5) manual page.\n");
+ if (errno)
+ goto fail;
+
+ fprintf (db_file, "# This lease file was written by isc-dhcp-%s\n\n",
+ PACKAGE_VERSION);
+ if (errno)
+ goto fail;
+
+ /* At this point we have a new lease file that, so far, could not
+ * be described as either corrupt nor valid.
+ */
+ lease_file_is_corrupt = 0;
+
+ /* Write out all the leases that we know of... */
+ counting = 0;
+ if (!write_leases ())
+ goto fail;
+
+#if defined (TRACING)
+ if (!trace_playback ()) {
+#endif
+ /* %Audit% Truncated filename causes panic. %2004.06.17,Safe%
+ * This should never happen since the path is a configuration
+ * variable from build-time or command-line. But if it should,
+ * either by malice or ignorance, we panic, since the potential
+ * for havoc is too high.
+ */
+ if (snprintf (backfname, sizeof backfname, "%s~", path_dhcpd_db)
+ >= sizeof backfname)
+ log_fatal("new_lease_file: backup lease file path too long");
+
+ /* Get the old database out of the way... */
+ if (unlink (backfname) < 0 && errno != ENOENT) {
+ log_error ("Can't remove old lease database backup %s: %m",
+ backfname);
+ goto fail;
+ }
+ if (link(path_dhcpd_db, backfname) < 0) {
+ if (errno == ENOENT) {
+ log_error("%s is missing - no lease db to backup.",
+ path_dhcpd_db);
+ } else {
+ log_error("Can't backup lease database %s to %s: %m",
+ path_dhcpd_db, backfname);
+ goto fail;
+ }
+ }
+#if defined (TRACING)
+ }
+#endif
+
+ /* Move in the new file... */
+ if (rename (newfname, path_dhcpd_db) < 0) {
+ log_error ("Can't install new lease database %s to %s: %m",
+ newfname, path_dhcpd_db);
+ goto fail;
+ }
+
+ counting = 1;
+ return 1;
+
+ fail:
+ lease_file_is_corrupt = db_validity;
+ fdfail:
+ unlink (newfname);
+ return 0;
+}
+
+int group_writer (struct group_object *group)
+{
+ if (!write_group (group))
+ return 0;
+ if (!commit_leases ())
+ return 0;
+ return 1;
+}
diff --git a/server/ddns.c b/server/ddns.c
new file mode 100644
index 0000000..b1683c3
--- /dev/null
+++ b/server/ddns.c
@@ -0,0 +1,1764 @@
+/* ddns.c
+
+ Dynamic DNS updates. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2000-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been donated to Internet Systems Consortium
+ * by Damien Neil of Nominum, Inc.
+ *
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include "dst/md5.h"
+#include <dns/result.h>
+
+#ifdef NSUPDATE
+
+static void ddns_fwd_srv_connector(struct lease *lease,
+ struct iasubopt *lease6,
+ struct binding_scope **inscope,
+ dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult);
+
+/* DN: No way of checking that there is enough space in a data_string's
+ buffer. Be certain to allocate enough!
+ TL: This is why the expression evaluation code allocates a *new*
+ data_string. :') */
+static void data_string_append (struct data_string *ds1,
+ struct data_string *ds2)
+{
+ memcpy (ds1 -> buffer -> data + ds1 -> len,
+ ds2 -> data,
+ ds2 -> len);
+ ds1 -> len += ds2 -> len;
+}
+
+
+/* Determine what, if any, forward and reverse updates need to be
+ * performed, and carry them through.
+ */
+int
+ddns_updates(struct packet *packet, struct lease *lease, struct lease *old,
+ struct iasubopt *lease6, struct iasubopt *old6,
+ struct option_state *options)
+{
+ unsigned long ddns_ttl = DEFAULT_DDNS_TTL;
+ struct data_string ddns_hostname;
+ struct data_string ddns_domainname;
+ struct data_string old_ddns_fwd_name;
+ struct data_string ddns_fwd_name;
+ //struct data_string ddns_rev_name;
+ struct data_string ddns_dhcid;
+ struct binding_scope **scope = NULL;
+ //struct iaddr addr;
+ struct data_string d1;
+ struct option_cache *oc;
+ int s1, s2;
+ int result = 0;
+ isc_result_t rcode1 = ISC_R_SUCCESS;
+ int server_updates_a = 1;
+ //int server_updates_ptr = 1;
+ struct buffer *bp = (struct buffer *)0;
+ int ignorep = 0, client_ignorep = 0;
+ int rev_name_len;
+ int i;
+
+ dhcp_ddns_cb_t *ddns_cb;
+ int do_remove = 0;
+
+ if (ddns_update_style != 2)
+ return 0;
+
+ /*
+ * sigh, I want to cancel any previous udpates before we do anything
+ * else but this means we need to deal with the lease vs lease6
+ * question twice.
+ * If there is a ddns request already outstanding cancel it.
+ */
+
+ if (lease != NULL) {
+ if ((old != NULL) && (old->ddns_cb != NULL)) {
+ ddns_cancel(old->ddns_cb);
+ old->ddns_cb = NULL;
+ }
+ } else if (lease6 != NULL) {
+ if ((old6 != NULL) && (old6->ddns_cb != NULL)) {
+ ddns_cancel(old6->ddns_cb);
+ old6->ddns_cb = NULL;
+ }
+ } else {
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ /* Silence compiler warnings. */
+ result = 0;
+ return(0);
+ }
+
+ /* allocate our control block */
+ ddns_cb = ddns_cb_alloc(MDL);
+ if (ddns_cb == NULL) {
+ return(0);
+ }
+ /* assume that we shall update both the A and ptr records */
+ ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
+
+ if (lease != NULL) {
+ scope = &(lease->scope);
+ ddns_cb->address = lease->ip_addr;
+ } else if (lease6 != NULL) {
+ scope = &(lease6->scope);
+ memcpy(ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
+ ddns_cb->address.len = 16;
+ }
+
+ memset (&d1, 0, sizeof(d1));
+ memset (&ddns_hostname, 0, sizeof (ddns_hostname));
+ memset (&ddns_domainname, 0, sizeof (ddns_domainname));
+ memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name));
+ memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name));
+ //memset (&ddns_rev_name, 0, sizeof (ddns_rev_name));
+ memset (&ddns_dhcid, 0, sizeof (ddns_dhcid));
+
+ /* If we are allowed to accept the client's update of its own A
+ record, see if the client wants to update its own A record. */
+ if (!(oc = lookup_option(&server_universe, options,
+ SV_CLIENT_UPDATES)) ||
+ evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL,
+ packet->options, options, scope,
+ oc, MDL)) {
+ /* If there's no fqdn.no-client-update or if it's
+ nonzero, don't try to use the client-supplied
+ XXX */
+ if (!(oc = lookup_option (&fqdn_universe, packet -> options,
+ FQDN_SERVER_UPDATE)) ||
+ evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
+ goto noclient;
+ /* Win98 and Win2k will happily claim to be willing to
+ update an unqualified domain name. */
+ if (!(oc = lookup_option (&fqdn_universe, packet -> options,
+ FQDN_DOMAINNAME)))
+ goto noclient;
+ if (!(oc = lookup_option (&fqdn_universe, packet -> options,
+ FQDN_FQDN)) ||
+ !evaluate_option_cache(&ddns_fwd_name, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
+ goto noclient;
+ ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
+ server_updates_a = 0;
+ goto client_updates;
+ }
+ noclient:
+ /* If do-forward-updates is disabled, this basically means don't
+ do an update unless the client is participating, so if we get
+ here and do-forward-updates is disabled, we can stop. */
+ if ((oc = lookup_option (&server_universe, options,
+ SV_DO_FORWARD_UPDATES)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL)) {
+ goto out;
+ }
+
+ /* If it's a static lease, then don't do the DNS update unless we're
+ specifically configured to do so. If the client asked to do its
+ own update and we allowed that, we don't do this test. */
+ /* XXX: note that we cannot detect static DHCPv6 leases. */
+ if ((lease != NULL) && (lease->flags & STATIC_LEASE)) {
+ if (!(oc = lookup_option(&server_universe, options,
+ SV_UPDATE_STATIC_LEASES)) ||
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
+ goto out;
+ }
+
+ /*
+ * Compute the name for the A record.
+ */
+ oc = lookup_option(&server_universe, options, SV_DDNS_HOST_NAME);
+ if (oc)
+ s1 = evaluate_option_cache(&ddns_hostname, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL);
+ else
+ s1 = 0;
+
+ oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME);
+ if (oc)
+ s2 = evaluate_option_cache(&ddns_domainname, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL);
+ else
+ s2 = 0;
+
+ if (s1 && s2) {
+ if (ddns_hostname.len + ddns_domainname.len > 253) {
+ log_error ("ddns_update: host.domain name too long");
+
+ goto out;
+ }
+
+ buffer_allocate (&ddns_fwd_name.buffer,
+ ddns_hostname.len + ddns_domainname.len + 2,
+ MDL);
+ if (ddns_fwd_name.buffer) {
+ ddns_fwd_name.data = ddns_fwd_name.buffer->data;
+ data_string_append (&ddns_fwd_name, &ddns_hostname);
+ ddns_fwd_name.buffer->data[ddns_fwd_name.len] = '.';
+ ddns_fwd_name.len++;
+ data_string_append (&ddns_fwd_name, &ddns_domainname);
+ ddns_fwd_name.buffer->data[ddns_fwd_name.len] ='\0';
+ ddns_fwd_name.terminated = 1;
+ }
+ }
+ client_updates:
+
+ /* See if there's a name already stored on the lease. */
+ if (find_bound_string(&old_ddns_fwd_name, *scope, "ddns-fwd-name")) {
+ /* If there is, see if it's different. */
+ if (old_ddns_fwd_name.len != ddns_fwd_name.len ||
+ memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
+ old_ddns_fwd_name.len)) {
+ /*
+ * If the name is different, mark the old record
+ * for deletion and continue getting the new info.
+ */
+ do_remove = 1;
+ goto in;
+ }
+
+ /* See if there's a DHCID on the lease, and if not
+ * then potentially look for 'on events' for ad-hoc ddns.
+ */
+ if (!find_bound_string(&ddns_dhcid, *scope, "ddns-txt") &&
+ (old != NULL)) {
+ /* If there's no DHCID, the update was probably
+ done with the old-style ad-hoc DDNS updates.
+ So if the expiry and release events look like
+ they're the same, run them. This should delete
+ the old DDNS data. */
+ if (old -> on_expiry == old -> on_release) {
+ execute_statements(NULL, NULL, lease, NULL,
+ NULL, NULL, scope,
+ old->on_expiry);
+ if (old -> on_expiry)
+ executable_statement_dereference
+ (&old -> on_expiry, MDL);
+ if (old -> on_release)
+ executable_statement_dereference
+ (&old -> on_release, MDL);
+ /* Now, install the DDNS data the new way. */
+ goto in;
+ }
+ } else
+ data_string_forget(&ddns_dhcid, MDL);
+
+ /* See if the administrator wants to do updates even
+ in cases where the update already appears to have been
+ done. */
+ if (!(oc = lookup_option(&server_universe, options,
+ SV_UPDATE_OPTIMIZATION)) ||
+ evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL)) {
+ result = 1;
+ goto noerror;
+ }
+ /* If there's no "ddns-fwd-name" on the lease record, see if
+ * there's a ddns-client-fqdn indicating a previous client
+ * update (if it changes, we need to adjust the PTR).
+ */
+ } else if (find_bound_string(&old_ddns_fwd_name, *scope,
+ "ddns-client-fqdn")) {
+ /* If the name is not different, no need to update
+ the PTR record. */
+ if (old_ddns_fwd_name.len == ddns_fwd_name.len &&
+ !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data,
+ old_ddns_fwd_name.len) &&
+ (!(oc = lookup_option(&server_universe, options,
+ SV_UPDATE_OPTIMIZATION)) ||
+ evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))) {
+ goto noerror;
+ }
+ }
+ in:
+
+ /* If we don't have a name that the client has been assigned, we
+ can just skip all this. */
+
+ if ((!ddns_fwd_name.len) || (ddns_fwd_name.len > 255)) {
+ if (ddns_fwd_name.len > 255) {
+ log_error ("client provided fqdn: too long");
+ }
+
+ /* If desired do the removals */
+ if (do_remove != 0) {
+ (void) ddns_removals(lease, lease6, NULL);
+ }
+ goto out;
+ }
+
+ /*
+ * Compute the RR TTL.
+ *
+ * We have two ways of computing the TTL.
+ * The old behavior was to allow for the customer to set up
+ * the option or to default things. For v4 this was 1/2
+ * of the lease time, for v6 this was DEFAULT_DDNS_TTL.
+ * The new behavior continues to allow the customer to set
+ * up an option but the defaults are a little different.
+ * We now use 1/2 of the (preferred) lease time for both
+ * v4 and v6 and cap them at a maximum value.
+ * If the customer chooses to use an experession that references
+ * part of the lease the v6 value will be the default as there
+ * isn't a lease available for v6.
+ */
+
+ ddns_ttl = DEFAULT_DDNS_TTL;
+ if (lease != NULL) {
+ if (lease->ends <= cur_time) {
+ ddns_ttl = 0;
+ } else {
+ ddns_ttl = (lease->ends - cur_time)/2;
+ }
+ }
+#ifndef USE_OLD_DDNS_TTL
+ else if (lease6 != NULL) {
+ ddns_ttl = lease6->prefer/2;
+ }
+
+ if (ddns_ttl > MAX_DEFAULT_DDNS_TTL) {
+ ddns_ttl = MAX_DEFAULT_DDNS_TTL;
+ }
+#endif
+
+ if ((oc = lookup_option(&server_universe, options, SV_DDNS_TTL))) {
+ if (evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ ddns_ttl = getULong (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ ddns_cb->ttl = ddns_ttl;
+
+ /*
+ * Compute the reverse IP name, starting with the domain name.
+ */
+ oc = lookup_option(&server_universe, options, SV_DDNS_REV_DOMAIN_NAME);
+ if (oc)
+ s1 = evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL);
+ else
+ s1 = 0;
+
+ /*
+ * Figure out the length of the part of the name that depends
+ * on the address.
+ */
+ if (ddns_cb->address.len == 4) {
+ char buf[17];
+ /* XXX: WOW this is gross. */
+ rev_name_len = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.",
+ ddns_cb->address.iabuf[3] & 0xff,
+ ddns_cb->address.iabuf[2] & 0xff,
+ ddns_cb->address.iabuf[1] & 0xff,
+ ddns_cb->address.iabuf[0] & 0xff) + 1;
+
+ if (s1) {
+ rev_name_len += d1.len;
+
+ if (rev_name_len > 255) {
+ log_error("ddns_update: Calculated rev domain "
+ "name too long.");
+ s1 = 0;
+ data_string_forget(&d1, MDL);
+ }
+ }
+ } else if (ddns_cb->address.len == 16) {
+ /*
+ * IPv6 reverse names are always the same length, with
+ * 32 hex characters separated by dots.
+ */
+ rev_name_len = sizeof("0.1.2.3.4.5.6.7."
+ "8.9.a.b.c.d.e.f."
+ "0.1.2.3.4.5.6.7."
+ "8.9.a.b.c.d.e.f."
+ "ip6.arpa.");
+
+ /* Set s1 to make sure we gate into updates. */
+ s1 = 1;
+ } else {
+ log_fatal("invalid address length %d", ddns_cb->address.len);
+ /* Silence compiler warnings. */
+ return 0;
+ }
+
+ /* See if we are configured NOT to do reverse ptr updates */
+ if ((oc = lookup_option(&server_universe, options,
+ SV_DO_REVERSE_UPDATES)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL)) {
+ ddns_cb->flags &= ~DDNS_UPDATE_PTR;
+ }
+
+ if (s1) {
+ buffer_allocate(&ddns_cb->rev_name.buffer, rev_name_len, MDL);
+ if (ddns_cb->rev_name.buffer != NULL) {
+ struct data_string *rname = &ddns_cb->rev_name;
+ rname->data = rname->buffer->data;
+
+ if (ddns_cb->address.len == 4) {
+ rname->len =
+ sprintf((char *)rname->buffer->data,
+ "%u.%u.%u.%u.",
+ ddns_cb->address.iabuf[3] & 0xff,
+ ddns_cb->address.iabuf[2] & 0xff,
+ ddns_cb->address.iabuf[1] & 0xff,
+ ddns_cb->address.iabuf[0] & 0xff);
+
+ /*
+ * d1.data may be opaque, garbage bytes, from
+ * user (mis)configuration.
+ */
+ data_string_append(rname, &d1);
+ rname->buffer->data[rname->len] = '\0';
+ } else if (ddns_cb->address.len == 16) {
+ char *p = (char *)&rname->buffer->data;
+ unsigned char *a = ddns_cb->address.iabuf + 15;
+ for (i=0; i<16; i++) {
+ sprintf(p, "%x.%x.",
+ (*a & 0xF), ((*a >> 4) & 0xF));
+ p += 4;
+ a -= 1;
+ }
+ strcat(p, "ip6.arpa.");
+ rname->len = strlen((const char *)rname->data);
+ }
+
+ rname->terminated = 1;
+ }
+
+ if (d1.data != NULL)
+ data_string_forget(&d1, MDL);
+ }
+
+ /*
+ * If we are updating the A record, compute the DHCID value.
+ */
+ if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
+ if (lease6 != NULL)
+ result = get_dhcid(&ddns_cb->dhcid, 2,
+ lease6->ia->iaid_duid.data,
+ lease6->ia->iaid_duid.len);
+ else if ((lease != NULL) && (lease->uid != NULL) &&
+ (lease->uid_len != 0))
+ result = get_dhcid (&ddns_cb->dhcid,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ lease -> uid, lease -> uid_len);
+ else if (lease != NULL)
+ result = get_dhcid (&ddns_cb->dhcid, 0,
+ lease -> hardware_addr.hbuf,
+ lease -> hardware_addr.hlen);
+ else
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (!result)
+ goto badfqdn;
+ }
+
+ /*
+ * Perform updates.
+ */
+
+ data_string_copy(&ddns_cb->fwd_name, &ddns_fwd_name, MDL);
+
+ if (ddns_cb->flags && DDNS_UPDATE_ADDR) {
+ oc = lookup_option(&server_universe, options,
+ SV_DDNS_CONFLICT_DETECT);
+ if (oc &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
+ ddns_cb->flags |= DDNS_CONFLICT_OVERRIDE;
+
+ }
+
+ /*
+ * Previously if we failed during the removal operations
+ * we skipped the fqdn option processing. I'm not sure
+ * if we want to continue with that if we fail before sending
+ * the ddns messages. Currently we don't.
+ */
+ if (do_remove) {
+ rcode1 = ddns_removals(lease, lease6, ddns_cb);
+ }
+ else {
+ ddns_fwd_srv_connector(lease, lease6, scope, ddns_cb,
+ ISC_R_SUCCESS);
+ }
+ ddns_cb = NULL;
+
+ noerror:
+ /*
+ * If fqdn-reply option is disabled in dhcpd.conf, then don't
+ * send the client an FQDN option at all, even if one was requested.
+ * (WinXP clients allegedly misbehave if the option is present,
+ * refusing to handle PTR updates themselves).
+ */
+ if ((oc = lookup_option (&server_universe, options, SV_FQDN_REPLY)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL)) {
+ goto badfqdn;
+
+ /* If we're ignoring client updates, then we tell a sort of 'white
+ * lie'. We've already updated the name the server wants (per the
+ * config written by the server admin). Now let the client do as
+ * it pleases with the name they supplied (if any).
+ *
+ * We only form an FQDN option this way if the client supplied an
+ * FQDN option that had FQDN_SERVER_UPDATE set false.
+ */
+ } else if (client_ignorep &&
+ (oc = lookup_option(&fqdn_universe, packet->options,
+ FQDN_SERVER_UPDATE)) &&
+ !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL)) {
+ oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN);
+ if (oc && evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, options,
+ scope, oc, MDL)) {
+ if (d1.len == 0 ||
+ !buffer_allocate(&bp, d1.len + 5, MDL))
+ goto badfqdn;
+
+ /* Server pretends it is not updating. */
+ bp->data[0] = 0;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[0], 1,
+ FQDN_SERVER_UPDATE, 0))
+ goto badfqdn;
+
+ /* Client is encouraged to update. */
+ bp->data[1] = 0;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[1], 1,
+ FQDN_NO_CLIENT_UPDATE, 0))
+ goto badfqdn;
+
+ /* Use the encoding of client's FQDN option. */
+ oc = lookup_option(&fqdn_universe, packet->options,
+ FQDN_ENCODED);
+ if (oc &&
+ evaluate_boolean_option_cache(&ignorep, packet,
+ lease, NULL,
+ packet->options,
+ options, scope,
+ oc, MDL))
+ bp->data[2] = 1; /* FQDN is encoded. */
+ else
+ bp->data[2] = 0; /* FQDN is not encoded. */
+
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[2], 1,
+ FQDN_ENCODED, 0))
+ goto badfqdn;
+
+ /* Current FQDN drafts indicate 255 is mandatory. */
+ bp->data[3] = 255;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[3], 1,
+ FQDN_RCODE1, 0))
+ goto badfqdn;
+
+ bp->data[4] = 255;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[4], 1,
+ FQDN_RCODE2, 0))
+ goto badfqdn;
+
+ /* Copy in the FQDN supplied by the client. Note well
+ * that the format of this option in the cache is going
+ * to be in text format. If the fqdn supplied by the
+ * client is encoded, it is decoded into the option
+ * cache when parsed out of the packet. It will be
+ * re-encoded when the option is assembled to be
+ * transmitted if the client elects that encoding.
+ */
+ memcpy(&bp->data[5], d1.data, d1.len);
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data[5], d1.len,
+ FQDN_FQDN, 0))
+ goto badfqdn;
+
+ data_string_forget(&d1, MDL);
+ }
+ /* Set up the outgoing FQDN option if there was an incoming
+ * FQDN option. If there's a valid FQDN option, there MUST
+ * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed
+ * length head of the option contents, so we test the latter
+ * to detect the presence of the former.
+ */
+ } else if ((oc = lookup_option(&fqdn_universe, packet->options,
+ FQDN_ENCODED)) &&
+ buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) {
+ bp -> data [0] = server_updates_a;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [0], 1,
+ FQDN_SERVER_UPDATE, 0))
+ goto badfqdn;
+ bp -> data [1] = server_updates_a;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [1], 1,
+ FQDN_NO_CLIENT_UPDATE, 0))
+ goto badfqdn;
+
+ /* Do the same encoding the client did. */
+ if (evaluate_boolean_option_cache(&ignorep, packet, lease,
+ NULL, packet->options,
+ options, scope, oc, MDL))
+ bp -> data [2] = 1;
+ else
+ bp -> data [2] = 0;
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [2], 1,
+ FQDN_ENCODED, 0))
+ goto badfqdn;
+ bp -> data [3] = 255;//isc_rcode_to_ns (rcode1);
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [3], 1,
+ FQDN_RCODE1, 0))
+ goto badfqdn;
+ bp -> data [4] = 255;//isc_rcode_to_ns (rcode2);
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [4], 1,
+ FQDN_RCODE2, 0))
+ goto badfqdn;
+ if (ddns_fwd_name.len) {
+ memcpy (&bp -> data [5],
+ ddns_fwd_name.data, ddns_fwd_name.len);
+ if (!save_option_buffer(&fqdn_universe, options,
+ bp, &bp->data [5],
+ ddns_fwd_name.len,
+ FQDN_FQDN, 0))
+ goto badfqdn;
+ }
+ }
+
+ badfqdn:
+ out:
+ /*
+ * Final cleanup.
+ */
+ if (ddns_cb != NULL) {
+ ddns_cb_free(ddns_cb, MDL);
+ }
+
+ data_string_forget(&d1, MDL);
+ data_string_forget(&ddns_hostname, MDL);
+ data_string_forget(&ddns_domainname, MDL);
+ data_string_forget(&old_ddns_fwd_name, MDL);
+ data_string_forget(&ddns_fwd_name, MDL);
+ //data_string_forget(&ddns_rev_name, MDL);
+ //data_string_forget(&ddns_dhcid, MDL);
+ if (bp)
+ buffer_dereference(&bp, MDL);
+
+ return result;
+}
+
+/*
+ * Utility function to update text strings within a lease.
+ *
+ * The first issue is to find the proper scope. Sometimes we shall be
+ * called with a pointer to the scope in other cases we need to find
+ * the proper lease and then get the scope. Once we have the scope we update
+ * the proper strings, as indicated by the state value in the control block.
+ * Lastly, if we needed to find the scope we write it out, if we used a
+ * scope that was passed as an argument we don't write it, assuming that
+ * our caller (or his ...) will do the write.
+ */
+
+isc_result_t
+ddns_update_lease_text(dhcp_ddns_cb_t *ddns_cb,
+ struct binding_scope **inscope)
+{
+ struct binding_scope **scope = NULL;
+ struct lease *lease = NULL;
+ struct iasubopt *lease6 = NULL;
+ struct ipv6_pool *pool = NULL;
+ struct in6_addr addr;
+ struct data_string lease_dhcid;
+
+ if (inscope != NULL) {
+ scope = inscope;
+ } else if (ddns_cb->address.len == 4) {
+ if (find_lease_by_ip_addr(&lease, ddns_cb->address, MDL) != 0){
+ scope = &(lease->scope);
+ }
+ } else if (ddns_cb->address.len == 16) {
+ memcpy(&addr, &ddns_cb->address.iabuf, 16);
+ if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) ==
+ ISC_R_SUCCESS) ||
+ (find_ipv6_pool(&pool, D6O_IA_NA, &addr) ==
+ ISC_R_SUCCESS)) {
+ if (iasubopt_hash_lookup(&lease6, pool->leases,
+ &addr, 16, MDL)) {
+ scope = &(lease6->scope);
+ }
+ ipv6_pool_dereference(&pool, MDL);
+ }
+ } else {
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ if (scope == NULL) {
+ /* If necessary get rid of the lease */
+ if (lease) {
+ lease_dereference(&lease, MDL);
+ }
+ else if (lease6) {
+ iasubopt_dereference(&lease6, MDL);
+ }
+
+ return(ISC_R_FAILURE);
+ }
+
+ /* We now have a scope and can proceed to update it */
+ switch(ddns_cb->state) {
+ case DDNS_STATE_REM_PTR:
+ unset(*scope, "ddns-rev-name");
+ if ((ddns_cb->flags & DDNS_CLIENT_DID_UPDATE) != 0) {
+ unset(*scope, "ddns-client-fqdn");
+ }
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ case DDNS_STATE_CLEANUP:
+ bind_ds_value(scope, "ddns-rev-name", &ddns_cb->rev_name);
+ if ((ddns_cb->flags & DDNS_UPDATE_ADDR) == 0) {
+ bind_ds_value(scope, "ddns-client-fqdn",
+ &ddns_cb->fwd_name);
+ }
+ break;
+
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ bind_ds_value(scope, "ddns-fwd-name", &ddns_cb->fwd_name);
+
+ /* convert from dns version to lease version of dhcid */
+ memset(&lease_dhcid, 0, sizeof(lease_dhcid));
+ dhcid_tolease(&ddns_cb->dhcid, &lease_dhcid);
+ bind_ds_value(scope, "ddns-txt", &lease_dhcid);
+ data_string_forget(&lease_dhcid, MDL);
+
+ break;
+
+ case DDNS_STATE_REM_FW_NXRR:
+ case DDNS_STATE_REM_FW_YXDHCID:
+ unset(*scope, "ddns-fwd-name");
+ unset(*scope, "ddns-txt");
+ break;
+ }
+
+ /* If necessary write it out and get rid of the lease */
+ if (lease) {
+ write_lease(lease);
+ lease_dereference(&lease, MDL);
+ } else if (lease6) {
+ write_ia(lease6->ia);
+ iasubopt_dereference(&lease6, MDL);
+ }
+
+ return(ISC_R_SUCCESS);
+}
+
+/*
+ * This function should be called when update_lease_ptr function fails.
+ * It does inform user about the condition, provides some hints how to
+ * resolve this and dies gracefully. This can happend in at least three
+ * cases (all are configuration mistakes):
+ * a) IPv4: user have duplicate fixed-address entries (the same
+ * address is defined twice). We may have found wrong lease.
+ * b) IPv6: user have overlapping pools (we tried to find
+ * a lease in a wrong pool)
+ * c) IPv6: user have duplicate fixed-address6 entires (the same
+ * address is defined twice). We may have found wrong lease.
+ *
+ * Comment: while it would be possible to recover from both cases
+ * by forcibly searching for leases in *all* following pools, that would
+ * only hide the real problem - a misconfiguration. Proper solution
+ * is to log the problem, die and let the user fix his config file.
+ */
+void
+update_lease_failed(struct lease *lease,
+ struct iasubopt *lease6,
+ dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_cb_t *ddns_cb_set,
+ const char * file, int line)
+{
+ char lease_address[MAX_ADDRESS_STRING_LEN + 64];
+ char reason[128]; /* likely reason */
+
+ sprintf(reason, "unknown");
+ sprintf(lease_address, "unknown");
+
+ /* let's pretend that everything is ok, so we can continue for
+ for information gathering purposes */
+
+ if (ddns_cb != NULL) {
+ strncpy(lease_address, piaddr(ddns_cb->address),
+ MAX_ADDRESS_STRING_LEN);
+
+ if (ddns_cb->address.len == 4) {
+ sprintf(reason, "duplicate IPv4 fixed-address entry");
+ } else if (ddns_cb->address.len == 16) {
+ sprintf(reason, "duplicate IPv6 fixed-address6 entry "
+ "or overlapping pools");
+ } else {
+ /*
+ * Should not happen. We have non-IPv4, non-IPv6
+ * address. Something is very wrong here.
+ */
+ sprintf(reason, "corrupted ddns_cb structure (address "
+ "length is %d)", ddns_cb->address.len);
+ }
+ }
+
+ log_error("Failed to properly update internal lease structure with "
+ "DDNS");
+ log_error("control block structures. Tried to update lease for"
+ "%s address, ddns_cb=%p.", lease_address, ddns_cb);
+
+ log_error("%s", "");
+ log_error("This condition can occur, if DHCP server configuration is "
+ "inconsistent.");
+ log_error("In particular, please do check that your configuration:");
+ log_error("a) does not have overlapping pools (especially containing");
+ log_error(" %s address).", lease_address);
+ log_error("b) there are no duplicate fixed-address or fixed-address6");
+ log_error("entries for the %s address.", lease_address);
+ log_error("%s", "");
+ log_error("Possible reason for this failure: %s", reason);
+
+ log_fatal("%s(%d): Failed to update lease database with DDNS info for "
+ "address %s. Lease database inconsistent. Unable to recover."
+ " Terminating.", file, line, lease_address);
+}
+
+/*
+ * utility function to update found lease. It does extra checks
+ * that we are indeed updating the right lease. It may happen
+ * that user have duplicate fixed-address entries, so we attempt
+ * to update wrong lease. See also safe_lease6_update.
+ */
+
+void
+safe_lease_update(struct lease *lease,
+ struct iasubopt *lease6,
+ dhcp_ddns_cb_t *oldcb,
+ dhcp_ddns_cb_t *newcb,
+ const char *file, int line)
+{
+ char addrbuf[MAX_ADDRESS_STRING_LEN];
+
+ if (lease != NULL) {
+ if ( (lease->ddns_cb == NULL) && (newcb == NULL) ) {
+ /*
+ * Trying to clean up pointer that is already null. We
+ * are most likely trying to update wrong lease here.
+ */
+
+ /*
+ * this error message pops out during every DNS Update
+ * for fixed leases. It is enabled only for debugging
+ * DNS Updates. Investigation pending.
+ */
+#if defined (DEBUG_DNS_UPDATES)
+ log_error("%s(%d): Invalid lease update. Tried to "
+ "clear already NULL DDNS control block "
+ "pointer for lease %s.",
+ file, line, piaddr(lease->ip_addr) );
+#endif
+
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(lease, NULL, oldcb, newcb, file,
+ line);
+#endif
+ /*
+ * May not reach this: update_lease_failed calls
+ * log_fatal.
+ */
+ return;
+ }
+
+ if ( (lease->ddns_cb != NULL) && (lease->ddns_cb != oldcb) ) {
+ /*
+ * There is existing cb structure, but it differs from
+ * what we expected to see there. Most likely we are
+ * trying to update wrong lease.
+ */
+ log_error("%s(%d): Failed to update internal lease "
+ "structure with DDNS control block. Existing"
+ " ddns_cb structure does not match "
+ "expectations.IPv4=%s, old ddns_cb=%p, tried"
+ "to update to new ddns_cb=%p", file, line,
+ piaddr(lease->ip_addr), oldcb, newcb);
+
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(lease, NULL, oldcb, newcb, file,
+ line);
+#endif
+ /*
+ * May not reach this: update_lease_failed calls
+ * log_fatal.
+ */
+ return;
+ }
+ /* additional IPv4 specific checks may be added here */
+
+ /* update the lease */
+ lease->ddns_cb = newcb;
+ } else if (lease6 != NULL) {
+ if ( (lease6->ddns_cb == NULL) && (newcb == NULL) ) {
+ inet_ntop(AF_INET6, &lease6->addr, addrbuf,
+ MAX_ADDRESS_STRING_LEN);
+ /*
+ * Trying to clean up pointer that is already null. We
+ * are most likely trying to update wrong lease here.
+ */
+#if defined (DEBUG_DNS_UPDATES)
+ log_error("%s(%d): Failed to update internal lease "
+ "structure. Tried to clear already NULL "
+ "DDNS control block pointer for lease %s.",
+ file, line, addrbuf);
+#endif
+
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(NULL, lease6, oldcb, newcb, file,
+ line);
+#endif
+
+ /*
+ * May not reach this: update_lease_failed calls
+ * log_fatal.
+ */
+ return;
+ }
+
+ if ( (lease6->ddns_cb != NULL) && (lease6->ddns_cb != oldcb) ) {
+ /*
+ * there is existing cb structure, but it differs from
+ * what we expected to see there. Most likely we are
+ * trying to update wrong lease.
+ */
+ inet_ntop(AF_INET6, &lease6->addr, addrbuf,
+ MAX_ADDRESS_STRING_LEN);
+
+ log_error("%s(%d): Failed to update internal lease "
+ "structure with DDNS control block. Existing"
+ " ddns_cb structure does not match "
+ "expectations.IPv6=%s, old ddns_cb=%p, tried"
+ "to update to new ddns_cb=%p", file, line,
+ addrbuf, oldcb, newcb);
+
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(NULL, lease6, oldcb, newcb, file,
+ line);
+#endif
+ /*
+ * May not reach this: update_lease_failed calls
+ * log_fatal.
+ */
+ return;
+ }
+ /* additional IPv6 specific checks may be added here */
+
+ /* update the lease */
+ lease6->ddns_cb = newcb;
+
+ } else {
+ /* should never get here */
+ log_fatal("Impossible condition at %s:%d (called from %s:%d).",
+ MDL, file, line);
+ }
+}
+
+/*
+ * Utility function to update the pointer to the DDNS control block
+ * in a lease.
+ * SUCCESS - able to update the pointer
+ * FAILURE - lease didn't exist or sanity checks failed
+ * lease and lease6 may be empty in which case we attempt to find
+ * the lease from the ddns_cb information.
+ * ddns_cb is the control block to use if a lookup is necessary
+ * ddns_cb_set is the pointer to insert into the lease and may be NULL
+ * The last two arguments may look odd as they will be the same much of the
+ * time, but I need an argument to tell me if I'm setting or clearing in
+ * addition to the address information from the cb to look up the lease.
+ * using the same value twice allows me more flexibility.
+ */
+
+isc_result_t
+ddns_update_lease_ptr(struct lease *lease,
+ struct iasubopt *lease6,
+ dhcp_ddns_cb_t *ddns_cb,
+ dhcp_ddns_cb_t *ddns_cb_set,
+ const char * file, int line)
+{
+ char ddns_address[MAX_ADDRESS_STRING_LEN];
+ sprintf(ddns_address, "uknown");
+ if (ddns_cb) {
+ strncpy(ddns_address, piaddr(ddns_cb->address),
+ MAX_ADDRESS_STRING_LEN);
+ }
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("%s(%d): Updating lease_ptr for ddns_cp=%p (addr=%s)",
+ file, line, ddns_cb, ddns_address );
+#endif
+
+ if (lease != NULL) {
+ safe_lease_update(lease, NULL, ddns_cb, ddns_cb_set,
+ file, line);
+ /* lease->ddns_cb = ddns_cb_set; */
+ } else if (lease6 != NULL) {
+ safe_lease_update(NULL, lease6, ddns_cb, ddns_cb_set,
+ file, line);
+ /* lease6->ddns_cb = ddns_cb_set; */
+ } else if (ddns_cb->address.len == 4) {
+ struct lease *find_lease = NULL;
+ if (find_lease_by_ip_addr(&find_lease,
+ ddns_cb->address, MDL) != 0) {
+#if defined (DEBUG_DNS_UPDATES)
+ log_info("%s(%d): find_lease_by_ip_addr(%s) successful:"
+ "lease=%p", file, line, ddns_address,
+ find_lease);
+#endif
+
+ /* find_lease->ddns_cb = ddns_cb_set; */
+ safe_lease_update(find_lease, NULL, ddns_cb,
+ ddns_cb_set, file, line);
+ lease_dereference(&find_lease, MDL);
+ }
+ else {
+#if defined (DEBUG_DNS_UPDATES)
+ log_error("%s(%d): ddns_update_lease_ptr failed. "
+ "Lease for %s not found. (Is it static lease?)",
+ file, line, piaddr(ddns_cb->address));
+#endif
+
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
+ file, line);
+#endif
+ /*
+ * may not reach this. update_lease_failed
+ * calls log_fatal.
+ */
+ return(ISC_R_FAILURE);
+
+ }
+ } else if (ddns_cb->address.len == 16) {
+ struct iasubopt *find_lease6 = NULL;
+ struct ipv6_pool *pool = NULL;
+ struct in6_addr addr;
+ char addrbuf[MAX_ADDRESS_STRING_LEN];
+
+ memcpy(&addr, &ddns_cb->address.iabuf, 16);
+ if ((find_ipv6_pool(&pool, D6O_IA_TA, &addr) !=
+ ISC_R_SUCCESS) &&
+ (find_ipv6_pool(&pool, D6O_IA_NA, &addr) !=
+ ISC_R_SUCCESS)) {
+ inet_ntop(AF_INET6, &lease6->addr, addrbuf,
+ MAX_ADDRESS_STRING_LEN);
+ log_error("%s(%d): Pool for lease %s not found.",
+ file, line, addrbuf);
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
+ file, line);
+#endif
+ /*
+ * never reached. update_lease_failed
+ * calls log_fatal.
+ */
+ return(ISC_R_FAILURE);
+ }
+
+ if (iasubopt_hash_lookup(&find_lease6, pool->leases,
+ &addr, 16, MDL)) {
+ find_lease6->ddns_cb = ddns_cb_set;
+ iasubopt_dereference(&find_lease6, MDL);
+ } else {
+ inet_ntop(AF_INET6, &lease6->addr, addrbuf,
+ MAX_ADDRESS_STRING_LEN);
+ log_error("%s(%d): Lease %s not found within pool.",
+ file, line, addrbuf);
+#if defined (DNS_UPDATES_MEMORY_CHECKS)
+ update_lease_failed(NULL, NULL, ddns_cb, ddns_cb_set,
+ file, line);
+#endif
+ /*
+ * never reached. update_lease_failed
+ * calls log_fatal.
+ */
+ return(ISC_R_FAILURE);
+ }
+ ipv6_pool_dereference(&pool, MDL);
+ } else {
+ /* shouldn't get here */
+ log_fatal("Impossible condition at %s:%d, called from %s:%d.",
+ MDL, file, line);
+ }
+
+ return(ISC_R_SUCCESS);
+}
+
+void
+ddns_ptr_add(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ if (eresult == ISC_R_SUCCESS) {
+ log_info("Added reverse map from %.*s to %.*s",
+ (int)ddns_cb->rev_name.len,
+ (const char *)ddns_cb->rev_name.data,
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data);
+
+ ddns_update_lease_text(ddns_cb, NULL);
+ } else {
+ log_error("Unable to add reverse map from %.*s to %.*s: %s",
+ (int)ddns_cb->rev_name.len,
+ (const char *)ddns_cb->rev_name.data,
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data,
+ isc_result_totext (eresult));
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_cb_free(ddns_cb, MDL);
+ /*
+ * A single DDNS operation may require several calls depending on
+ * the current state as the prerequisites for the first message
+ * may not succeed requiring a second operation and potentially
+ * a ptr operation after that. The commit_leases operation is
+ * invoked at the end of this set of operations in order to require
+ * a single write for all of the changes. We call commit_leases
+ * here rather than immediately after the call to update the lease
+ * text in order to save any previously written data.
+ */
+ commit_leases();
+ return;
+}
+
+/*
+ * action routine when trying to remove a pointer
+ * this will be called after the ddns queries have completed
+ * if we succeeded in removing the pointer we go to the next step (if any)
+ * if not we cleanup and leave.
+ */
+
+void
+ddns_ptr_remove(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result = eresult;
+
+ switch(eresult) {
+ case ISC_R_SUCCESS:
+ log_info("Removed reverse map on %.*s",
+ (int)ddns_cb->rev_name.len,
+ (const char *)ddns_cb->rev_name.data);
+ /* fall through */
+ case DNS_R_NXRRSET:
+ case DNS_R_NXDOMAIN:
+ /* No entry is the same as success.
+ * Remove the information from the lease and
+ * continue with any next step */
+ ddns_update_lease_text(ddns_cb, NULL);
+
+ /* trigger any add operation */
+ result = ISC_R_SUCCESS;
+ break;
+
+ default:
+ log_error("Can't remove reverse map on %.*s: %s",
+ (int)ddns_cb->rev_name.len,
+ (const char *)ddns_cb->rev_name.data,
+ isc_result_totext (eresult));
+ break;
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, result);
+ ddns_cb_free(ddns_cb, MDL);
+ return;
+}
+
+
+/*
+ * If the first query succeeds, the updater can conclude that it
+ * has added a new name whose only RRs are the A and DHCID RR records.
+ * The A RR update is now complete (and a client updater is finished,
+ * while a server might proceed to perform a PTR RR update).
+ * -- "Interaction between DHCP and DNS"
+ *
+ * If the second query succeeds, the updater can conclude that the current
+ * client was the last client associated with the domain name, and that
+ * the name now contains the updated A RR. The A RR update is now
+ * complete (and a client updater is finished, while a server would
+ * then proceed to perform a PTR RR update).
+ * -- "Interaction between DHCP and DNS"
+ *
+ * If the second query fails with NXRRSET, the updater must conclude
+ * that the client's desired name is in use by another host. At this
+ * juncture, the updater can decide (based on some administrative
+ * configuration outside of the scope of this document) whether to let
+ * the existing owner of the name keep that name, and to (possibly)
+ * perform some name disambiguation operation on behalf of the current
+ * client, or to replace the RRs on the name with RRs that represent
+ * the current client. If the configured policy allows replacement of
+ * existing records, the updater submits a query that deletes the
+ * existing A RR and the existing DHCID RR, adding A and DHCID RRs that
+ * represent the IP address and client-identity of the new client.
+ * -- "Interaction between DHCP and DNS"
+ */
+
+void
+ddns_fwd_srv_add2(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result;
+ const char *logstr = NULL;
+ char ddns_address[MAX_ADDRESS_STRING_LEN];
+
+ /* Construct a printable form of the address for logging */
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+
+ switch(eresult) {
+ case ISC_R_SUCCESS:
+ log_info("Added new forward map from %.*s to %s",
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data,
+ ddns_address);
+
+ ddns_update_lease_text(ddns_cb, NULL);
+
+ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
+ /* if we have zone information get rid of it */
+ if (ddns_cb->zone != NULL) {
+ ddns_cb_forget_zone(ddns_cb);
+ }
+
+ ddns_cb->state = DDNS_STATE_ADD_PTR;
+ ddns_cb->cur_func = ddns_ptr_add;
+
+ result = ddns_modify_ptr(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+ break;
+
+ case DNS_R_YXRRSET:
+ case DNS_R_YXDOMAIN:
+ logstr = "DHCID mismatch, belongs to another client.";
+ break;
+
+ case DNS_R_NXRRSET:
+ case DNS_R_NXDOMAIN:
+ logstr = "Has an address record but no DHCID, not mine.";
+ break;
+
+ default:
+ logstr = isc_result_totext(eresult);
+ break;
+ }
+
+ if (logstr != NULL) {
+ log_error("Forward map from %.*s to %s FAILED: %s",
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data,
+ ddns_address, logstr);
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_cb_free(ddns_cb, MDL);
+ /*
+ * A single DDNS operation may require several calls depending on
+ * the current state as the prerequisites for the first message
+ * may not succeed requiring a second operation and potentially
+ * a ptr operation after that. The commit_leases operation is
+ * invoked at the end of this set of operations in order to require
+ * a single write for all of the changes. We call commit_leases
+ * here rather than immediately after the call to update the lease
+ * text in order to save any previously written data.
+ */
+ commit_leases();
+ return;
+}
+
+void
+ddns_fwd_srv_add1(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result;
+ char ddns_address[MAX_ADDRESS_STRING_LEN];
+
+ /* Construct a printable form of the address for logging */
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+
+ switch(eresult) {
+ case ISC_R_SUCCESS:
+ log_info ("Added new forward map from %.*s to %s",
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data,
+ ddns_address);
+
+ ddns_update_lease_text(ddns_cb, NULL);
+
+ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
+ /* if we have zone information get rid of it */
+ if (ddns_cb->zone != NULL) {
+ ddns_cb_forget_zone(ddns_cb);
+ }
+
+ ddns_cb->state = DDNS_STATE_ADD_PTR;
+ ddns_cb->cur_func = ddns_ptr_add;
+
+ result = ddns_modify_ptr(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+
+ break;
+
+ case DNS_R_YXDOMAIN:
+ /* we can reuse the zone information */
+ ddns_cb->state = DDNS_STATE_ADD_FW_YXDHCID;
+ ddns_cb->cur_func = ddns_fwd_srv_add2;
+
+ result = ddns_modify_fwd(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+
+ break;
+
+ default:
+ log_error ("Unable to add forward map from %.*s to %s: %s",
+ (int)ddns_cb->fwd_name.len,
+ (const char *)ddns_cb->fwd_name.data,
+ ddns_address,
+ isc_result_totext (eresult));
+ break;
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_cb_free(ddns_cb, MDL);
+ /*
+ * A single DDNS operation may require several calls depending on
+ * the current state as the prerequisites for the first message
+ * may not succeed requiring a second operation and potentially
+ * a ptr operation after that. The commit_leases operation is
+ * invoked at the end of this set of operations in order to require
+ * a single write for all of the changes. We call commit_leases
+ * here rather than immediately after the call to update the lease
+ * text in order to save any previously written data.
+ */
+ commit_leases();
+ return;
+}
+
+static void
+ddns_fwd_srv_connector(struct lease *lease,
+ struct iasubopt *lease6,
+ struct binding_scope **inscope,
+ dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result = ISC_R_FAILURE;
+
+ if (ddns_cb == NULL) {
+ /* nothing to do */
+ return;
+ }
+
+ if (eresult == ISC_R_SUCCESS) {
+ /*
+ * If we have updates dispatch as appropriate,
+ * if not do FQDN binding if desired.
+ */
+
+ if (ddns_cb->flags & DDNS_UPDATE_ADDR) {
+ ddns_cb->state = DDNS_STATE_ADD_FW_NXDOMAIN;
+ ddns_cb->cur_func = ddns_fwd_srv_add1;
+ result = ddns_modify_fwd(ddns_cb);
+ } else if ((ddns_cb->flags & DDNS_UPDATE_PTR) &&
+ (ddns_cb->rev_name.len != 0)) {
+ ddns_cb->state = DDNS_STATE_ADD_PTR;
+ ddns_cb->cur_func = ddns_ptr_add;
+ result = ddns_modify_ptr(ddns_cb);
+ } else {
+ ddns_update_lease_text(ddns_cb, inscope);
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb, MDL);
+ } else {
+ ddns_cb_free(ddns_cb, MDL);
+ }
+
+ return;
+}
+
+/*
+ * If the first query fails, the updater MUST NOT delete the DNS name. It
+ * may be that the host whose lease on the server has expired has moved
+ * to another network and obtained a lease from a different server,
+ * which has caused the client's A RR to be replaced. It may also be
+ * that some other client has been configured with a name that matches
+ * the name of the DHCP client, and the policy was that the last client
+ * to specify the name would get the name. In this case, the DHCID RR
+ * will no longer match the updater's notion of the client-identity of
+ * the host pointed to by the DNS name.
+ * -- "Interaction between DHCP and DNS"
+ */
+
+void
+ddns_fwd_srv_rem2(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ if (eresult == ISC_R_SUCCESS) {
+ ddns_update_lease_text(ddns_cb, NULL);
+
+ /* Do the next operation */
+ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
+ /* if we have zone information get rid of it */
+ if (ddns_cb->zone != NULL) {
+ ddns_cb_forget_zone(ddns_cb);
+ }
+
+ ddns_cb->state = DDNS_STATE_REM_PTR;
+ ddns_cb->cur_func = ddns_ptr_remove;
+
+ eresult = ddns_modify_ptr(ddns_cb);
+ if (eresult == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
+ ddns_cb_free(ddns_cb, MDL);
+ return;
+}
+
+
+/*
+ * First action routine when trying to remove a fwd
+ * this will be called after the ddns queries have completed
+ * if we succeeded in removing the fwd we go to the next step (if any)
+ * if not we cleanup and leave.
+ */
+
+void
+ddns_fwd_srv_rem1(dhcp_ddns_cb_t *ddns_cb,
+ isc_result_t eresult)
+{
+ isc_result_t result = eresult;
+ char ddns_address[MAX_ADDRESS_STRING_LEN];
+
+ switch(eresult) {
+ case ISC_R_SUCCESS:
+ /* Construct a printable form of the address for logging */
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+ log_info("Removed forward map from %.*s to %s",
+ (int)ddns_cb->fwd_name.len,
+ (const char*)ddns_cb->fwd_name.data,
+ ddns_address);
+
+ /* Do the second step of the FWD removal */
+ ddns_cb->state = DDNS_STATE_REM_FW_NXRR;
+ ddns_cb->cur_func = ddns_fwd_srv_rem2;
+ result = ddns_modify_fwd(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ break;
+
+ case DNS_R_NXRRSET:
+ case DNS_R_NXDOMAIN:
+ ddns_update_lease_text(ddns_cb, NULL);
+
+ /* Do the next operation */
+ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
+ /* if we have zone information get rid of it */
+ if (ddns_cb->zone != NULL) {
+ ddns_cb_forget_zone(ddns_cb);
+ }
+
+ ddns_cb->state = DDNS_STATE_REM_PTR;
+ ddns_cb->cur_func = ddns_ptr_remove;
+
+ result = ddns_modify_ptr(ddns_cb);
+ if (result == ISC_R_SUCCESS) {
+ return;
+ }
+ }
+ else {
+ /* Trigger the add operation */
+ eresult = ISC_R_SUCCESS;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ddns_update_lease_ptr(NULL, NULL, ddns_cb, NULL, MDL);
+ ddns_fwd_srv_connector(NULL, NULL, NULL, ddns_cb->next_op, eresult);
+ ddns_cb_free(ddns_cb, MDL);
+}
+
+
+/*
+ * Remove relevant entries from DNS.
+ *
+ * Return values:
+ * 0 - badness occurred and we weren't able to do what was wanted
+ * 1 - we were able to do stuff but it's in progress
+ * in both cases any additional block has been passed on to it's handler
+ */
+
+int
+ddns_removals(struct lease *lease,
+ struct iasubopt *lease6,
+ dhcp_ddns_cb_t *add_ddns_cb)
+{
+ isc_result_t rcode, execute_add = ISC_R_FAILURE;
+ struct binding_scope **scope = NULL;
+ int result = 0;
+ dhcp_ddns_cb_t *ddns_cb = NULL;
+ struct data_string leaseid;
+
+ /*
+ * Cancel any outstanding requests. When called
+ * from within the DNS code we probably will have
+ * already done the cancel but if called from outside
+ * - for example as part of a lease expiry - we won't.
+ */
+ if ((lease != NULL) && (lease->ddns_cb != NULL)) {
+ ddns_cancel(lease->ddns_cb);
+ lease->ddns_cb = NULL;
+ } else if ((lease6 != NULL) && (lease6->ddns_cb != NULL)) {
+ ddns_cancel(lease6->ddns_cb);
+ lease6->ddns_cb = NULL;
+ } else
+ goto cleanup;
+
+ /* allocate our control block */
+ ddns_cb = ddns_cb_alloc(MDL);
+ if (ddns_cb == NULL) {
+ goto cleanup;
+ }
+
+ if (lease != NULL) {
+ scope = &(lease->scope);
+ ddns_cb->address = lease->ip_addr;
+ } else if (lease6 != NULL) {
+ scope = &(lease6->scope);
+ memcpy(&ddns_cb->address.iabuf, lease6->addr.s6_addr, 16);
+ ddns_cb->address.len = 16;
+ } else
+ goto cleanup;
+
+ /* No scope implies that DDNS has not been performed for this lease. */
+ if (*scope == NULL)
+ goto cleanup;
+
+ if (ddns_update_style != 2)
+ goto cleanup;
+
+ /* Assume that we are removing both records */
+ ddns_cb->flags = DDNS_UPDATE_ADDR | DDNS_UPDATE_PTR;
+
+ /* and that we want to do the add call */
+ execute_add = ISC_R_SUCCESS;
+
+ /*
+ * Look up stored names.
+ */
+
+ /*
+ * Find the fwd name and copy it to the control block. If we don't
+ * have it we can't delete the fwd record but we can still try to
+ * remove the ptr record and cleanup the lease information if the
+ * client did the fwd update.
+ */
+ if (!find_bound_string(&ddns_cb->fwd_name, *scope, "ddns-fwd-name")) {
+ /* don't try and delete the A, or do the add */
+ ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
+ execute_add = ISC_R_FAILURE;
+
+ /* Check if client did update */
+ if (find_bound_string(&ddns_cb->fwd_name, *scope,
+ "ddns-client-fqdn")) {
+ ddns_cb->flags |= DDNS_CLIENT_DID_UPDATE;
+ }
+ }
+
+ /*
+ * Find the ptr name and copy it to the control block. If we don't
+ * have it this isn't an interim or rfc3??? record so we can't delete
+ * the A record using this mechanism but we can delete the ptr record.
+ * In this case we will attempt to do any requested next step.
+ */
+ memset(&leaseid, 0, sizeof(leaseid));
+ if (!find_bound_string (&leaseid, *scope, "ddns-txt")) {
+ ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
+ } else {
+ if (dhcid_fromlease(&ddns_cb->dhcid, &leaseid) !=
+ ISC_R_SUCCESS) {
+ /* We couldn't convert the dhcid from the lease
+ * version to the dns version. We can't delete
+ * the A record but can continue to the ptr
+ */
+ ddns_cb->flags &= ~DDNS_UPDATE_ADDR;
+ }
+ data_string_forget(&leaseid, MDL);
+ }
+
+ /*
+ * Find the rev name and copy it to the control block. If we don't
+ * have it we can't get rid of it but we can try to remove the fwd
+ * pointer if desired.
+ */
+ if (!find_bound_string(&ddns_cb->rev_name, *scope, "ddns-rev-name")) {
+ ddns_cb->flags &= ~DDNS_UPDATE_PTR;
+ }
+
+ /*
+ * If we have a second control block for doing an add
+ * after the remove finished attach it to our control block.
+ */
+ ddns_cb->next_op = add_ddns_cb;
+
+ /*
+ * Now that we've collected the information we can try to process it.
+ * If necessary we call an appropriate routine to send a message and
+ * provide it with an action routine to run on the control block given
+ * the results of the message. We have three entry points from here,
+ * one for removing the A record, the next for removing the PTR and
+ * the third for doing any requested add.
+ */
+ if ((ddns_cb->flags & DDNS_UPDATE_ADDR) != 0) {
+ if (ddns_cb->fwd_name.len != 0) {
+ ddns_cb->state = DDNS_STATE_REM_FW_YXDHCID;
+ ddns_cb->cur_func = ddns_fwd_srv_rem1;
+
+ rcode = ddns_modify_fwd(ddns_cb);
+ if (rcode == ISC_R_SUCCESS) {
+ ddns_update_lease_ptr(lease, lease6, ddns_cb,
+ ddns_cb, MDL);
+ return(1);
+ }
+
+ /*
+ * We weren't able to process the request tag the
+ * add so we won't execute it.
+ */
+ execute_add = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ else {
+ /*remove info from scope */
+ unset(*scope, "ddns-fwd-name");
+ unset(*scope, "ddns-txt");
+ }
+ }
+
+ if ((ddns_cb->flags & DDNS_UPDATE_PTR) != 0) {
+ ddns_cb->state = DDNS_STATE_REM_PTR;
+ ddns_cb->cur_func = ddns_ptr_remove;
+
+ /*
+ * if execute add isn't success remove the control block so
+ * it won't be processed when the remove completes. We
+ * also arrange to clean it up and get rid of it.
+ */
+ if (execute_add != ISC_R_SUCCESS) {
+ ddns_cb->next_op = NULL;
+ ddns_fwd_srv_connector(lease, lease6, scope,
+ add_ddns_cb, execute_add);
+ add_ddns_cb = NULL;
+ }
+ else {
+ result = 1;
+ }
+
+ rcode = ddns_modify_ptr(ddns_cb);
+ if (rcode == ISC_R_SUCCESS) {
+ ddns_update_lease_ptr(lease, lease6, ddns_cb, ddns_cb,
+ MDL);
+ return(result);
+ }
+
+ /* We weren't able to process the request tag the
+ * add so we won't execute it */
+ execute_add = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ cleanup:
+ /*
+ * We've gotten here because we didn't need to send a message or
+ * we failed when trying to do so. We send the additional cb
+ * off to handle sending and/or cleanup and cleanup anything
+ * we allocated here.
+ */
+ ddns_fwd_srv_connector(lease, lease6, scope, add_ddns_cb, execute_add);
+ if (ddns_cb != NULL)
+ ddns_cb_free(ddns_cb, MDL);
+
+ return(result);
+}
+
+#endif /* NSUPDATE */
diff --git a/server/dhcp.c b/server/dhcp.c
new file mode 100644
index 0000000..da4585f
--- /dev/null
+++ b/server/dhcp.c
@@ -0,0 +1,4474 @@
+/* dhcp.c
+
+ DHCP Protocol engine. */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <errno.h>
+#include <limits.h>
+#include <sys/time.h>
+
+static void commit_leases_ackout(void *foo);
+static void maybe_return_agent_options(struct packet *packet,
+ struct option_state *options);
+
+int outstanding_pings;
+
+struct leasequeue *ackqueue_head, *ackqueue_tail;
+static struct leasequeue *free_ackqueue;
+static struct timeval max_fsync;
+
+int outstanding_acks;
+int max_outstanding_acks = DEFAULT_DELAYED_ACK;
+int max_ack_delay_secs = DEFAULT_ACK_DELAY_SECS;
+int max_ack_delay_usecs = DEFAULT_ACK_DELAY_USECS;
+int min_ack_delay_usecs = DEFAULT_MIN_ACK_DELAY_USECS;
+
+static char dhcp_message [256];
+static int site_code_min;
+
+static int find_min_site_code(struct universe *);
+static isc_result_t lowest_site_code(const void *, unsigned, void *);
+
+static const char *dhcp_type_names [] = {
+ "DHCPDISCOVER",
+ "DHCPOFFER",
+ "DHCPREQUEST",
+ "DHCPDECLINE",
+ "DHCPACK",
+ "DHCPNAK",
+ "DHCPRELEASE",
+ "DHCPINFORM",
+ "type 9",
+ "DHCPLEASEQUERY",
+ "DHCPLEASEUNASSIGNED",
+ "DHCPLEASEUNKNOWN",
+ "DHCPLEASEACTIVE"
+};
+const int dhcp_type_name_max = ((sizeof dhcp_type_names) / sizeof (char *));
+
+#if defined (TRACING)
+# define send_packet trace_packet_send
+#endif
+
+void
+dhcp (struct packet *packet) {
+ int ms_nulltp = 0;
+ struct option_cache *oc;
+ struct lease *lease = NULL;
+ const char *errmsg;
+ struct data_string data;
+
+ if (!locate_network(packet) &&
+ packet->packet_type != DHCPREQUEST &&
+ packet->packet_type != DHCPINFORM &&
+ packet->packet_type != DHCPLEASEQUERY) {
+ const char *s;
+ char typebuf[32];
+ errmsg = "unknown network segment";
+ bad_packet:
+
+ if (packet->packet_type > 0 &&
+ packet->packet_type <= dhcp_type_name_max) {
+ s = dhcp_type_names[packet->packet_type - 1];
+ } else {
+ /* %Audit% Cannot exceed 28 bytes. %2004.06.17,Safe% */
+ sprintf(typebuf, "type %d", packet->packet_type);
+ s = typebuf;
+ }
+
+ log_info("%s from %s via %s: %s", s,
+ (packet->raw->htype
+ ? print_hw_addr(packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr)
+ : "<no identifier>"),
+ packet->raw->giaddr.s_addr
+ ? inet_ntoa(packet->raw->giaddr)
+ : packet->interface->name, errmsg);
+ goto out;
+ }
+
+ /* There is a problem with the relay agent information option,
+ * which is that in order for a normal relay agent to append
+ * this option, the relay agent has to have been involved in
+ * getting the packet from the client to the server. Note
+ * that this is the software entity known as the relay agent,
+ * _not_ the hardware entity known as a router in which the
+ * relay agent may be running, so the fact that a router has
+ * forwarded a packet does not mean that the relay agent in
+ * the router was involved.
+ *
+ * So when the client broadcasts (DHCPDISCOVER, or giaddr is set),
+ * we can be sure that there are either agent options in the
+ * packet, or there aren't supposed to be. When the giaddr is not
+ * set, it's still possible that the client is on a directly
+ * attached subnet, and agent options are being appended by an l2
+ * device that has no address, and so sets no giaddr.
+ *
+ * But in either case it's possible that the packets we receive
+ * from the client in RENEW state may not include the agent options,
+ * so if they are not in the packet we must "pretend" the last values
+ * we observed were provided.
+ */
+ if (packet->packet_type == DHCPREQUEST &&
+ packet->raw->ciaddr.s_addr && !packet->raw->giaddr.s_addr &&
+ (packet->options->universe_count <= agent_universe.index ||
+ packet->options->universes[agent_universe.index] == NULL))
+ {
+ struct iaddr cip;
+
+ cip.len = sizeof packet -> raw -> ciaddr;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr,
+ sizeof packet -> raw -> ciaddr);
+ if (!find_lease_by_ip_addr (&lease, cip, MDL))
+ goto nolease;
+
+ /* If there are no agent options on the lease, it's not
+ interesting. */
+ if (!lease -> agent_options)
+ goto nolease;
+
+ /* The client should not be unicasting a renewal if its lease
+ has expired, so make it go through the process of getting
+ its agent options legally. */
+ if (lease -> ends < cur_time)
+ goto nolease;
+
+ if (lease -> uid_len) {
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ if (!oc)
+ goto nolease;
+
+ memset (&data, 0, sizeof data);
+ if (!evaluate_option_cache (&data,
+ packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL))
+ goto nolease;
+ if (lease -> uid_len != data.len ||
+ memcmp (lease -> uid, data.data, data.len)) {
+ data_string_forget (&data, MDL);
+ goto nolease;
+ }
+ data_string_forget (&data, MDL);
+ } else
+ if ((lease -> hardware_addr.hbuf [0] !=
+ packet -> raw -> htype) ||
+ (lease -> hardware_addr.hlen - 1 !=
+ packet -> raw -> hlen) ||
+ memcmp (&lease -> hardware_addr.hbuf [1],
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen))
+ goto nolease;
+
+ /* Okay, so we found a lease that matches the client. */
+ option_chain_head_reference ((struct option_chain_head **)
+ &(packet -> options -> universes
+ [agent_universe.index]),
+ lease -> agent_options, MDL);
+
+ if (packet->options->universe_count <= agent_universe.index)
+ packet->options->universe_count =
+ agent_universe.index + 1;
+
+ packet->agent_options_stashed = ISC_TRUE;
+ }
+ nolease:
+
+ /* If a client null terminates options it sends, it probably
+ * expects the server to reciprocate.
+ */
+ if ((oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_HOST_NAME))) {
+ if (!oc -> expression)
+ ms_nulltp = oc->flags & OPTION_HAD_NULLS;
+ }
+
+ /* Classify the client. */
+ classify_client (packet);
+
+ switch (packet -> packet_type) {
+ case DHCPDISCOVER:
+ dhcpdiscover (packet, ms_nulltp);
+ break;
+
+ case DHCPREQUEST:
+ dhcprequest (packet, ms_nulltp, lease);
+ break;
+
+ case DHCPRELEASE:
+ dhcprelease (packet, ms_nulltp);
+ break;
+
+ case DHCPDECLINE:
+ dhcpdecline (packet, ms_nulltp);
+ break;
+
+ case DHCPINFORM:
+ dhcpinform (packet, ms_nulltp);
+ break;
+
+ case DHCPLEASEQUERY:
+ dhcpleasequery(packet, ms_nulltp);
+ break;
+
+ case DHCPACK:
+ case DHCPOFFER:
+ case DHCPNAK:
+ case DHCPLEASEUNASSIGNED:
+ case DHCPLEASEUNKNOWN:
+ case DHCPLEASEACTIVE:
+ break;
+
+ default:
+ errmsg = "unknown packet type";
+ goto bad_packet;
+ }
+ out:
+ if (lease)
+ lease_dereference (&lease, MDL);
+}
+
+void dhcpdiscover (packet, ms_nulltp)
+ struct packet *packet;
+ int ms_nulltp;
+{
+ struct lease *lease = (struct lease *)0;
+ char msgbuf [1024]; /* XXX */
+ TIME when;
+ const char *s;
+ int peer_has_leases = 0;
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *peer;
+#endif
+
+ find_lease (&lease, packet, packet -> shared_network,
+ 0, &peer_has_leases, (struct lease *)0, MDL);
+
+ if (lease && lease -> client_hostname) {
+ if ((strlen (lease -> client_hostname) <= 64) &&
+ db_printable((unsigned char *)lease->client_hostname))
+ s = lease -> client_hostname;
+ else
+ s = "Hostname Unsuitable for Printing";
+ } else
+ s = (char *)0;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf, "DHCPDISCOVER from %s %s%s%svia %s",
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ /* Sourceless packets don't make sense here. */
+ if (!packet -> shared_network) {
+ log_info ("Packet from unknown subnet: %s",
+ inet_ntoa (packet -> raw -> giaddr));
+ goto out;
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ if (lease && lease -> pool && lease -> pool -> failover_peer) {
+ peer = lease -> pool -> failover_peer;
+
+ /*
+ * If the lease is ours to (re)allocate, then allocate it.
+ *
+ * If the lease is active, it belongs to the client. This
+ * is the right lease, if we are to offer one. We decide
+ * whether or not to offer later on.
+ *
+ * If the lease was last active, and we've reached this
+ * point, then it was last active with the same client. We
+ * can safely re-activate the lease with this client.
+ */
+ if (lease->binding_state == FTS_ACTIVE ||
+ lease->rewind_binding_state == FTS_ACTIVE ||
+ lease_mine_to_reallocate(lease)) {
+ ; /* This space intentionally left blank. */
+
+ /* Otherwise, we can't let the client have this lease. */
+ } else {
+#if defined (DEBUG_FIND_LEASE)
+ log_debug ("discarding %s - %s",
+ piaddr (lease -> ip_addr),
+ binding_state_print (lease -> binding_state));
+#endif
+ lease_dereference (&lease, MDL);
+ }
+ }
+#endif
+
+ /* If we didn't find a lease, try to allocate one... */
+ if (!lease) {
+ if (!allocate_lease (&lease, packet,
+ packet -> shared_network -> pools,
+ &peer_has_leases)) {
+ if (peer_has_leases)
+ log_error ("%s: peer holds all free leases",
+ msgbuf);
+ else
+ log_error ("%s: network %s: no free leases",
+ msgbuf,
+ packet -> shared_network -> name);
+ return;
+ }
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ if (lease && lease -> pool && lease -> pool -> failover_peer) {
+ peer = lease -> pool -> failover_peer;
+ if (peer -> service_state == not_responding ||
+ peer -> service_state == service_startup) {
+ log_info ("%s: not responding%s",
+ msgbuf, peer -> nrr);
+ goto out;
+ }
+ } else
+ peer = (dhcp_failover_state_t *)0;
+
+ /* Do load balancing if configured. */
+ if (peer && (peer -> service_state == cooperating) &&
+ !load_balance_mine (packet, peer)) {
+ if (peer_has_leases) {
+ log_debug ("%s: load balance to peer %s",
+ msgbuf, peer -> name);
+ goto out;
+ } else {
+ log_debug ("%s: cancel load balance to peer %s - %s",
+ msgbuf, peer -> name, "no free leases");
+ }
+ }
+#endif
+
+ /* If it's an expired lease, get rid of any bindings. */
+ if (lease -> ends < cur_time && lease -> scope)
+ binding_scope_dereference (&lease -> scope, MDL);
+
+ /* Set the lease to really expire in 2 minutes, unless it has
+ not yet expired, in which case leave its expiry time alone. */
+ when = cur_time + 120;
+ if (when < lease -> ends)
+ when = lease -> ends;
+
+ ack_lease (packet, lease, DHCPOFFER, when, msgbuf, ms_nulltp,
+ (struct host_decl *)0);
+ out:
+ if (lease)
+ lease_dereference (&lease, MDL);
+}
+
+void dhcprequest (packet, ms_nulltp, ip_lease)
+ struct packet *packet;
+ int ms_nulltp;
+ struct lease *ip_lease;
+{
+ struct lease *lease;
+ struct iaddr cip;
+ struct iaddr sip;
+ struct subnet *subnet;
+ int ours = 0;
+ struct option_cache *oc;
+ struct data_string data;
+ char msgbuf [1024]; /* XXX */
+ const char *s;
+ char smbuf [19];
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *peer;
+#endif
+ int have_server_identifier = 0;
+ int have_requested_addr = 0;
+
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_REQUESTED_ADDRESS);
+ memset (&data, 0, sizeof data);
+ if (oc &&
+ evaluate_option_cache (&data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ cip.len = 4;
+ memcpy (cip.iabuf, data.data, 4);
+ data_string_forget (&data, MDL);
+ have_requested_addr = 1;
+ } else {
+ oc = (struct option_cache *)0;
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr.s_addr, 4);
+ }
+
+ /* Find the lease that matches the address requested by the
+ client. */
+
+ subnet = (struct subnet *)0;
+ lease = (struct lease *)0;
+ if (find_subnet (&subnet, cip, MDL))
+ find_lease (&lease, packet,
+ subnet -> shared_network, &ours, 0, ip_lease, MDL);
+
+ if (lease && lease -> client_hostname) {
+ if ((strlen (lease -> client_hostname) <= 64) &&
+ db_printable((unsigned char *)lease->client_hostname))
+ s = lease -> client_hostname;
+ else
+ s = "Hostname Unsuitable for Printing";
+ } else
+ s = (char *)0;
+
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_SERVER_IDENTIFIER);
+ memset (&data, 0, sizeof data);
+ if (oc &&
+ evaluate_option_cache (&data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ sip.len = 4;
+ memcpy (sip.iabuf, data.data, 4);
+ data_string_forget (&data, MDL);
+ /* piaddr() should not return more than a 15 byte string.
+ * safe.
+ */
+ sprintf (smbuf, " (%s)", piaddr (sip));
+ have_server_identifier = 1;
+ } else
+ smbuf [0] = 0;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCPREQUEST for %s%s from %s %s%s%svia %s",
+ piaddr (cip), smbuf,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+#if defined (FAILOVER_PROTOCOL)
+ if (lease && lease -> pool && lease -> pool -> failover_peer) {
+ peer = lease -> pool -> failover_peer;
+ if (peer -> service_state == not_responding ||
+ peer -> service_state == service_startup) {
+ log_info ("%s: not responding%s",
+ msgbuf, peer -> nrr);
+ goto out;
+ }
+
+ /* "load balance to peer" - is not done at all for request.
+ *
+ * If it's RENEWING, we are the only server to hear it, so
+ * we have to serve it. If it's REBINDING, it's out of
+ * communication with the other server, so there's no point
+ * in waiting to serve it. However, if the lease we're
+ * offering is not a free lease, then we may be the only
+ * server that can offer it, so we can't load balance if
+ * the lease isn't in the free or backup state. If it is
+ * in the free or backup state, then that state is what
+ * mandates one server or the other should perform the
+ * allocation, not the LBA...we know the peer cannot
+ * allocate a request for an address in our free state.
+ *
+ * So our only compass is lease_mine_to_reallocate(). This
+ * effects both load balancing, and a sanity-check that we
+ * are not going to try to allocate a lease that isn't ours.
+ */
+ if ((lease -> binding_state == FTS_FREE ||
+ lease -> binding_state == FTS_BACKUP) &&
+ !lease_mine_to_reallocate (lease)) {
+ log_debug ("%s: lease owned by peer", msgbuf);
+ goto out;
+ }
+
+ /*
+ * If the lease is in a transitional state, we can't
+ * renew it unless we can rewind it to a non-transitional
+ * state (active, free, or backup). lease_mine_to_reallocate()
+ * checks for free/backup, so we only need to check for active.
+ */
+ if ((lease->binding_state == FTS_RELEASED ||
+ lease->binding_state == FTS_EXPIRED) &&
+ lease->rewind_binding_state != FTS_ACTIVE &&
+ !lease_mine_to_reallocate(lease)) {
+ log_debug("%s: lease in transition state %s", msgbuf,
+ (lease->binding_state == FTS_RELEASED)
+ ? "released" : "expired");
+ goto out;
+ }
+
+ /* It's actually very unlikely that we'll ever get here,
+ but if we do, tell the client to stop using the lease,
+ because the administrator reset it. */
+ if (lease -> binding_state == FTS_RESET &&
+ !lease_mine_to_reallocate (lease)) {
+ log_debug ("%s: lease reset by administrator", msgbuf);
+ nak_lease (packet, &cip);
+ goto out;
+ }
+
+ /* At this point it's possible that we will get a broadcast
+ DHCPREQUEST for a lease that we didn't offer, because
+ both we and the peer are in a position to offer it.
+ In that case, we probably shouldn't answer. In order
+ to not answer, we would have to compare the server
+ identifier sent by the client with the list of possible
+ server identifiers we can send, and if the client's
+ identifier isn't on the list, drop the DHCPREQUEST.
+ We aren't currently doing that for two reasons - first,
+ it's not clear that all clients do the right thing
+ with respect to sending the client identifier, which
+ could mean that we might simply not respond to a client
+ that is depending on us to respond. Secondly, we allow
+ the user to specify the server identifier to send, and
+ we don't enforce that the server identifier should be
+ one of our IP addresses. This is probably not a big
+ deal, but it's theoretically an issue.
+
+ The reason we care about this is that if both servers
+ send a DHCPACK to the DHCPREQUEST, they are then going
+ to send dueling BNDUPD messages, which could cause
+ trouble. I think it causes no harm, but it seems
+ wrong. */
+ } else
+ peer = (dhcp_failover_state_t *)0;
+#endif
+
+ /* If a client on a given network REQUESTs a lease on an
+ address on a different network, NAK it. If the Requested
+ Address option was used, the protocol says that it must
+ have been broadcast, so we can trust the source network
+ information.
+
+ If ciaddr was specified and Requested Address was not, then
+ we really only know for sure what network a packet came from
+ if it came through a BOOTP gateway - if it came through an
+ IP router, we'll just have to assume that it's cool.
+
+ If we don't think we know where the packet came from, it
+ came through a gateway from an unknown network, so it's not
+ from a RENEWING client. If we recognize the network it
+ *thinks* it's on, we can NAK it even though we don't
+ recognize the network it's *actually* on; otherwise we just
+ have to ignore it.
+
+ We don't currently try to take advantage of access to the
+ raw packet, because it's not available on all platforms.
+ So a packet that was unicast to us through a router from a
+ RENEWING client is going to look exactly like a packet that
+ was broadcast to us from an INIT-REBOOT client.
+
+ Since we can't tell the difference between these two kinds
+ of packets, if the packet appears to have come in off the
+ local wire, we have to treat it as if it's a RENEWING
+ client. This means that we can't NAK a RENEWING client on
+ the local wire that has a bogus address. The good news is
+ that we won't ACK it either, so it should revert to INIT
+ state and send us a DHCPDISCOVER, which we *can* work with.
+
+ Because we can't detect that a RENEWING client is on the
+ wrong wire, it's going to sit there trying to renew until
+ it gets to the REBIND state, when we *can* NAK it because
+ the packet will get to us through a BOOTP gateway. We
+ shouldn't actually see DHCPREQUEST packets from RENEWING
+ clients on the wrong wire anyway, since their idea of their
+ local router will be wrong. In any case, the protocol
+ doesn't really allow us to NAK a DHCPREQUEST from a
+ RENEWING client, so we can punt on this issue. */
+
+ if (!packet -> shared_network ||
+ (packet -> raw -> ciaddr.s_addr &&
+ packet -> raw -> giaddr.s_addr) ||
+ (have_requested_addr && !packet -> raw -> ciaddr.s_addr)) {
+
+ /* If we don't know where it came from but we do know
+ where it claims to have come from, it didn't come
+ from there. */
+ if (!packet -> shared_network) {
+ if (subnet && subnet -> group -> authoritative) {
+ log_info ("%s: wrong network.", msgbuf);
+ nak_lease (packet, &cip);
+ goto out;
+ }
+ /* Otherwise, ignore it. */
+ log_info ("%s: ignored (%s).", msgbuf,
+ (subnet
+ ? "not authoritative" : "unknown subnet"));
+ goto out;
+ }
+
+ /* If we do know where it came from and it asked for an
+ address that is not on that shared network, nak it. */
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+ if (!find_grouped_subnet (&subnet, packet -> shared_network,
+ cip, MDL)) {
+ if (packet -> shared_network -> group -> authoritative)
+ {
+ log_info ("%s: wrong network.", msgbuf);
+ nak_lease (packet, &cip);
+ goto out;
+ }
+ log_info ("%s: ignored (not authoritative).", msgbuf);
+ return;
+ }
+ }
+
+ /* If the address the client asked for is ours, but it wasn't
+ available for the client, NAK it. */
+ if (!lease && ours) {
+ log_info ("%s: lease %s unavailable.", msgbuf, piaddr (cip));
+ nak_lease (packet, &cip);
+ goto out;
+ }
+
+ /* Otherwise, send the lease to the client if we found one. */
+ if (lease) {
+ ack_lease (packet, lease, DHCPACK, 0, msgbuf, ms_nulltp,
+ (struct host_decl *)0);
+ } else
+ log_info ("%s: unknown lease %s.", msgbuf, piaddr (cip));
+
+ out:
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+ return;
+}
+
+void dhcprelease (packet, ms_nulltp)
+ struct packet *packet;
+ int ms_nulltp;
+{
+ struct lease *lease = (struct lease *)0, *next = (struct lease *)0;
+ struct iaddr cip;
+ struct option_cache *oc;
+ struct data_string data;
+ const char *s;
+ char msgbuf [1024], cstr[16]; /* XXX */
+
+
+ /* DHCPRELEASE must not specify address in requested-address
+ option, but old protocol specs weren't explicit about this,
+ so let it go. */
+ if ((oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_REQUESTED_ADDRESS))) {
+ log_info ("DHCPRELEASE from %s specified requested-address.",
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr));
+ }
+
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ memset (&data, 0, sizeof data);
+ if (oc &&
+ evaluate_option_cache (&data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ find_lease_by_uid (&lease, data.data, data.len, MDL);
+ data_string_forget (&data, MDL);
+
+ /* See if we can find a lease that matches the IP address
+ the client is claiming. */
+ while (lease) {
+ if (lease -> n_uid)
+ lease_reference (&next, lease -> n_uid, MDL);
+ if (!memcmp (&packet -> raw -> ciaddr,
+ lease -> ip_addr.iabuf, 4)) {
+ break;
+ }
+ lease_dereference (&lease, MDL);
+ if (next) {
+ lease_reference (&lease, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ }
+ if (next)
+ lease_dereference (&next, MDL);
+ }
+
+ /* The client is supposed to pass a valid client-identifier,
+ but the spec on this has changed historically, so try the
+ IP address in ciaddr if the client-identifier fails. */
+ if (!lease) {
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
+ find_lease_by_ip_addr (&lease, cip, MDL);
+ }
+
+
+ /* If the hardware address doesn't match, don't do the release. */
+ if (lease &&
+ (lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 ||
+ lease -> hardware_addr.hbuf [0] != packet -> raw -> htype ||
+ memcmp (&lease -> hardware_addr.hbuf [1],
+ packet -> raw -> chaddr, packet -> raw -> hlen)))
+ lease_dereference (&lease, MDL);
+
+ if (lease && lease -> client_hostname) {
+ if ((strlen (lease -> client_hostname) <= 64) &&
+ db_printable((unsigned char *)lease->client_hostname))
+ s = lease -> client_hostname;
+ else
+ s = "Hostname Unsuitable for Printing";
+ } else
+ s = (char *)0;
+
+ /* %Audit% Cannot exceed 16 bytes. %2004.06.17,Safe%
+ * We copy this out to stack because we actually want to log two
+ * inet_ntoa()'s in this message.
+ */
+ strncpy(cstr, inet_ntoa (packet -> raw -> ciaddr), 15);
+ cstr[15] = '\0';
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCPRELEASE of %s from %s %s%s%svia %s (%sfound)",
+ cstr,
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name,
+ lease ? "" : "not ");
+
+#if defined (FAILOVER_PROTOCOL)
+ if (lease && lease -> pool && lease -> pool -> failover_peer) {
+ dhcp_failover_state_t *peer = lease -> pool -> failover_peer;
+ if (peer -> service_state == not_responding ||
+ peer -> service_state == service_startup) {
+ log_info ("%s: ignored%s",
+ peer -> name, peer -> nrr);
+ goto out;
+ }
+
+ /* DHCPRELEASE messages are unicast, so if the client
+ sent the DHCPRELEASE to us, it's not going to send it
+ to the peer. Not sure why this would happen, and
+ if it does happen I think we still have to change the
+ lease state, so that's what we're doing.
+ XXX See what it says in the draft about this. */
+ }
+#endif
+
+ /* If we found a lease, release it. */
+ if (lease && lease -> ends > cur_time) {
+ release_lease (lease, packet);
+ }
+ log_info ("%s", msgbuf);
+#if defined(FAILOVER_PROTOCOL)
+ out:
+#endif
+ if (lease)
+ lease_dereference (&lease, MDL);
+}
+
+void dhcpdecline (packet, ms_nulltp)
+ struct packet *packet;
+ int ms_nulltp;
+{
+ struct lease *lease = (struct lease *)0;
+ struct option_state *options = (struct option_state *)0;
+ int ignorep = 0;
+ int i;
+ const char *status;
+ const char *s;
+ char msgbuf [1024]; /* XXX */
+ struct iaddr cip;
+ struct option_cache *oc;
+ struct data_string data;
+
+ /* DHCPDECLINE must specify address. */
+ if (!(oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_REQUESTED_ADDRESS)))
+ return;
+ memset (&data, 0, sizeof data);
+ if (!evaluate_option_cache (&data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL))
+ return;
+
+ cip.len = 4;
+ memcpy (cip.iabuf, data.data, 4);
+ data_string_forget (&data, MDL);
+ find_lease_by_ip_addr (&lease, cip, MDL);
+
+ if (lease && lease -> client_hostname) {
+ if ((strlen (lease -> client_hostname) <= 64) &&
+ db_printable((unsigned char *)lease->client_hostname))
+ s = lease -> client_hostname;
+ else
+ s = "Hostname Unsuitable for Printing";
+ } else
+ s = (char *)0;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf,
+ "DHCPDECLINE of %s from %s %s%s%svia %s",
+ piaddr (cip),
+ (packet -> raw -> htype
+ ? print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)
+ : (lease
+ ? print_hex_1(lease->uid_len, lease->uid, 60)
+ : "<no identifier>")),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+ option_state_allocate (&options, MDL);
+
+ /* Execute statements in scope starting with the subnet scope. */
+ if (lease)
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, options,
+ &global_scope,
+ lease -> subnet -> group,
+ (struct group *)0);
+
+ /* Execute statements in the class scopes. */
+ for (i = packet -> class_count; i > 0; i--) {
+ execute_statements_in_scope
+ ((struct binding_value **)0, packet, (struct lease *)0,
+ (struct client_state *)0, packet -> options, options,
+ &global_scope, packet -> classes [i - 1] -> group,
+ lease ? lease -> subnet -> group : (struct group *)0);
+ }
+
+ /* Drop the request if dhcpdeclines are being ignored. */
+ oc = lookup_option (&server_universe, options, SV_DECLINES);
+ if (!oc ||
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, options,
+ &lease -> scope, oc, MDL)) {
+ /* If we found a lease, mark it as unusable and complain. */
+ if (lease) {
+#if defined (FAILOVER_PROTOCOL)
+ if (lease -> pool && lease -> pool -> failover_peer) {
+ dhcp_failover_state_t *peer =
+ lease -> pool -> failover_peer;
+ if (peer -> service_state == not_responding ||
+ peer -> service_state == service_startup) {
+ if (!ignorep)
+ log_info ("%s: ignored%s",
+ peer -> name, peer -> nrr);
+ goto out;
+ }
+
+ /* DHCPDECLINE messages are broadcast, so we can safely
+ ignore the DHCPDECLINE if the peer has the lease.
+ XXX Of course, at this point that information has been
+ lost. */
+ }
+#endif
+
+ abandon_lease (lease, "declined.");
+ status = "abandoned";
+ } else {
+ status = "not found";
+ }
+ } else
+ status = "ignored";
+
+ if (!ignorep)
+ log_info ("%s: %s", msgbuf, status);
+
+#if defined(FAILOVER_PROTOCOL)
+ out:
+#endif
+ if (options)
+ option_state_dereference (&options, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+}
+
+void dhcpinform (packet, ms_nulltp)
+ struct packet *packet;
+ int ms_nulltp;
+{
+ char msgbuf [1024];
+ struct data_string d1, prl;
+ struct option_cache *oc;
+ struct option_state *options = (struct option_state *)0;
+ struct dhcp_packet raw;
+ struct packet outgoing;
+ unsigned char dhcpack = DHCPACK;
+ struct subnet *subnet = NULL;
+ struct iaddr cip, gip;
+ unsigned i;
+ int nulltp;
+ struct sockaddr_in to;
+ struct in_addr from;
+ isc_boolean_t zeroed_ciaddr;
+
+ /* The client should set ciaddr to its IP address, but apparently
+ it's common for clients not to do this, so we'll use their IP
+ source address if they didn't set ciaddr. */
+ if (!packet -> raw -> ciaddr.s_addr) {
+ zeroed_ciaddr = ISC_TRUE;
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> client_addr.iabuf, 4);
+ } else {
+ zeroed_ciaddr = ISC_FALSE;
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
+ }
+
+ if (packet->raw->giaddr.s_addr) {
+ gip.len = 4;
+ memcpy(gip.iabuf, &packet->raw->giaddr, 4);
+ } else
+ gip.len = 0;
+
+ /* %Audit% This is log output. %2004.06.17,Safe%
+ * If we truncate we hope the user can get a hint from the log.
+ */
+ snprintf (msgbuf, sizeof msgbuf, "DHCPINFORM from %s via %s",
+ piaddr (cip), packet->raw->giaddr.s_addr ?
+ inet_ntoa(packet->raw->giaddr) :
+ packet -> interface -> name);
+
+ /* If the IP source address is zero, don't respond. */
+ if (!memcmp (cip.iabuf, "\0\0\0", 4)) {
+ log_info ("%s: ignored (null source address).", msgbuf);
+ return;
+ }
+
+ /* Find the subnet that the client is on. */
+ if (zeroed_ciaddr && (gip.len != 0)) {
+ /* XXX - do subnet selection relay agent suboption here */
+ find_subnet(&subnet, gip, MDL);
+
+ if (subnet == NULL) {
+ log_info("%s: unknown subnet for relay address %s",
+ msgbuf, piaddr(gip));
+ return;
+ }
+ } else {
+ /* XXX - do subnet selection (not relay agent) option here */
+ find_subnet(&subnet, cip, MDL);
+
+ if (subnet == NULL) {
+ log_info("%s: unknown subnet for %s address %s",
+ msgbuf, zeroed_ciaddr ? "source" : "client",
+ piaddr(cip));
+ return;
+ }
+ }
+
+ /* We don't respond to DHCPINFORM packets if we're not authoritative.
+ It would be nice if a per-host value could override this, but
+ there's overhead involved in checking this, so let's see how people
+ react first. */
+ if (subnet && !subnet -> group -> authoritative) {
+ static int eso = 0;
+ log_info ("%s: not authoritative for subnet %s",
+ msgbuf, piaddr (subnet -> net));
+ if (!eso) {
+ log_info ("If this DHCP server is authoritative for%s",
+ " that subnet,");
+ log_info ("please write an `authoritative;' directi%s",
+ "ve either in the");
+ log_info ("subnet declaration or in some scope that%s",
+ " encloses the");
+ log_info ("subnet declaration - for example, write %s",
+ "it at the top");
+ log_info ("of the dhcpd.conf file.");
+ }
+ if (eso++ == 100)
+ eso = 0;
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+
+ option_state_allocate (&options, MDL);
+ memset (&outgoing, 0, sizeof outgoing);
+ memset (&raw, 0, sizeof raw);
+ outgoing.raw = &raw;
+
+ maybe_return_agent_options(packet, options);
+
+ /* Execute statements in scope starting with the subnet scope. */
+ if (subnet)
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, options,
+ &global_scope, subnet -> group,
+ (struct group *)0);
+
+ /* Execute statements in the class scopes. */
+ for (i = packet -> class_count; i > 0; i--) {
+ execute_statements_in_scope
+ ((struct binding_value **)0, packet, (struct lease *)0,
+ (struct client_state *)0, packet -> options, options,
+ &global_scope, packet -> classes [i - 1] -> group,
+ subnet ? subnet -> group : (struct group *)0);
+ }
+
+ /* Figure out the filename. */
+ memset (&d1, 0, sizeof d1);
+ oc = lookup_option (&server_universe, options, SV_FILENAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ i = d1.len;
+ if (i >= sizeof(raw.file)) {
+ log_info("file name longer than packet field "
+ "truncated - field: %lu name: %d %.*s",
+ (unsigned long)sizeof(raw.file), i, i,
+ d1.data);
+ i = sizeof(raw.file);
+ } else
+ raw.file[i] = 0;
+ memcpy (raw.file, d1.data, i);
+ data_string_forget (&d1, MDL);
+ }
+
+ /* Choose a server name as above. */
+ oc = lookup_option (&server_universe, options, SV_SERVER_NAME);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ i = d1.len;
+ if (i >= sizeof(raw.sname)) {
+ log_info("server name longer than packet field "
+ "truncated - field: %lu name: %d %.*s",
+ (unsigned long)sizeof(raw.sname), i, i,
+ d1.data);
+ i = sizeof(raw.sname);
+ } else
+ raw.sname[i] = 0;
+ memcpy (raw.sname, d1.data, i);
+ data_string_forget (&d1, MDL);
+ }
+
+ /* Set a flag if this client is a lame Microsoft client that NUL
+ terminates string options and expects us to do likewise. */
+ nulltp = 0;
+ if ((oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_HOST_NAME))) {
+ if (!oc->expression)
+ nulltp = oc->flags & OPTION_HAD_NULLS;
+ }
+
+ /* Put in DHCP-specific options. */
+ i = DHO_DHCP_MESSAGE_TYPE;
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ &dhcpack, 1, 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe, options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+
+ get_server_source_address(&from, options, packet);
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ i = DHO_SUBNET_MASK;
+ if (subnet && !lookup_option (&dhcp_universe, options, i)) {
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ subnet -> netmask.iabuf,
+ subnet -> netmask.len,
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe, options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+
+ /* If a site option space has been specified, use that for
+ site option codes. */
+ i = SV_SITE_OPTION_SPACE;
+ if ((oc = lookup_option (&server_universe, options, i)) &&
+ evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, options,
+ &global_scope, oc, MDL)) {
+ struct universe *u = (struct universe *)0;
+
+ if (!universe_hash_lookup (&u, universe_hash,
+ (const char *)d1.data, d1.len,
+ MDL)) {
+ log_error ("unknown option space %s.", d1.data);
+ option_state_dereference (&options, MDL);
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+ return;
+ }
+
+ options -> site_universe = u -> index;
+ options->site_code_min = find_min_site_code(u);
+ data_string_forget (&d1, MDL);
+ } else {
+ options -> site_universe = dhcp_universe.index;
+ options -> site_code_min = 0; /* Trust me, it works. */
+ }
+
+ memset (&prl, 0, sizeof prl);
+
+ /* Use the parameter list from the scope if there is one. */
+ oc = lookup_option (&dhcp_universe, options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+
+ /* Otherwise, 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 (!oc)
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+
+ if (oc)
+ evaluate_option_cache (&prl, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, options,
+ &global_scope, oc, MDL);
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+ dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
+#endif
+
+ log_info ("%s", msgbuf);
+
+ /* Figure out the address of the boot file server. */
+ if ((oc =
+ lookup_option (&server_universe, options, SV_NEXT_SERVER))) {
+ if (evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, options,
+ &global_scope, oc, MDL)) {
+ /* If there was more than one answer,
+ take the first. */
+ if (d1.len >= 4 && d1.data)
+ memcpy (&raw.siaddr, d1.data, 4);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /*
+ * Remove any time options, per section 3.4 RFC 2131
+ */
+ delete_option(&dhcp_universe, options, DHO_DHCP_LEASE_TIME);
+ delete_option(&dhcp_universe, options, DHO_DHCP_RENEWAL_TIME);
+ delete_option(&dhcp_universe, options, DHO_DHCP_REBINDING_TIME);
+
+ /* Set up the option buffer... */
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw, (struct lease *)0,
+ (struct client_state *)0,
+ 0, packet -> options, options, &global_scope,
+ 0, nulltp, 0,
+ prl.len ? &prl : (struct data_string *)0,
+ (char *)0);
+ option_state_dereference (&options, MDL);
+ data_string_forget (&prl, MDL);
+
+ /* Make sure that the packet is at least as big as a BOOTP packet. */
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+
+ raw.giaddr = packet -> raw -> giaddr;
+ raw.ciaddr = packet -> raw -> ciaddr;
+ memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
+ raw.hlen = packet -> raw -> hlen;
+ raw.htype = packet -> raw -> htype;
+
+ raw.xid = packet -> raw -> xid;
+ raw.secs = packet -> raw -> secs;
+ raw.flags = packet -> raw -> flags;
+ raw.hops = packet -> raw -> hops;
+ raw.op = BOOTREPLY;
+
+#ifdef DEBUG_PACKET
+ dump_packet (&outgoing);
+ dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+#endif
+
+ /* Set up the common stuff... */
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+ memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+ /* RFC2131 states the server SHOULD unciast to ciaddr.
+ * There are two wrinkles - relays, and when ciaddr is zero.
+ * There's actually no mention of relays at all in rfc2131 in
+ * regard to DHCPINFORM, except to say we might get packets from
+ * clients via them. Note: relays unicast to clients to the
+ * "yiaddr" address, which servers are forbidden to set when
+ * answering an inform.
+ *
+ * The solution: If ciaddr is zero, and giaddr is set, go via the
+ * relay with the broadcast flag set to help the relay (with no
+ * yiaddr and very likely no chaddr, it will have no idea where to
+ * send the packet).
+ *
+ * If the ciaddr is zero and giaddr is not set, go via the source
+ * IP address (but you are permitted to barf on their shoes).
+ *
+ * If ciaddr is not zero, send the packet there always.
+ */
+ if (!raw.ciaddr.s_addr && gip.len) {
+ memcpy(&to.sin_addr, gip.iabuf, 4);
+ to.sin_port = local_port;
+ raw.flags |= htons(BOOTP_BROADCAST);
+ } else {
+ gip.len = 0;
+ memcpy(&to.sin_addr, cip.iabuf, 4);
+ to.sin_port = remote_port;
+ }
+
+ /* Report what we're sending. */
+ snprintf(msgbuf, sizeof msgbuf, "DHCPACK to %s (%s) via", piaddr(cip),
+ (packet->raw->htype && packet->raw->hlen) ?
+ print_hw_addr(packet->raw->htype, packet->raw->hlen,
+ packet->raw->chaddr) :
+ "<no client hardware address>");
+ log_info("%s %s", msgbuf, gip.len ? piaddr(gip) :
+ packet->interface->name);
+
+ errno = 0;
+ send_packet ((fallback_interface
+ ? fallback_interface : packet -> interface),
+ &outgoing, &raw, outgoing.packet_length,
+ from, &to, (struct hardware *)0);
+ if (subnet)
+ subnet_dereference (&subnet, MDL);
+}
+
+void nak_lease (packet, cip)
+ struct packet *packet;
+ struct iaddr *cip;
+{
+ struct sockaddr_in to;
+ struct in_addr from;
+ int result;
+ struct dhcp_packet raw;
+ unsigned char nak = DHCPNAK;
+ struct packet outgoing;
+ unsigned i;
+ struct option_state *options = (struct option_state *)0;
+ struct option_cache *oc = (struct option_cache *)0;
+
+ option_state_allocate (&options, MDL);
+ memset (&outgoing, 0, sizeof outgoing);
+ memset (&raw, 0, sizeof raw);
+ outgoing.raw = &raw;
+
+ /* Set DHCP_MESSAGE_TYPE to DHCPNAK */
+ if (!option_cache_allocate (&oc, MDL)) {
+ log_error ("No memory for DHCPNAK message type.");
+ option_state_dereference (&options, MDL);
+ return;
+ }
+ if (!make_const_data (&oc -> expression, &nak, sizeof nak,
+ 0, 0, MDL)) {
+ log_error ("No memory for expr_const expression.");
+ option_cache_dereference (&oc, MDL);
+ option_state_dereference (&options, MDL);
+ return;
+ }
+ i = DHO_DHCP_MESSAGE_TYPE;
+ option_code_hash_lookup(&oc->option, dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe, options, oc);
+ option_cache_dereference (&oc, MDL);
+
+ /* Set DHCP_MESSAGE to whatever the message is */
+ if (!option_cache_allocate (&oc, MDL)) {
+ log_error ("No memory for DHCPNAK message type.");
+ option_state_dereference (&options, MDL);
+ return;
+ }
+ if (!make_const_data (&oc -> expression,
+ (unsigned char *)dhcp_message,
+ strlen (dhcp_message), 1, 0, MDL)) {
+ log_error ("No memory for expr_const expression.");
+ option_cache_dereference (&oc, MDL);
+ option_state_dereference (&options, MDL);
+ return;
+ }
+ i = DHO_DHCP_MESSAGE;
+ option_code_hash_lookup(&oc->option, dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe, options, oc);
+ option_cache_dereference (&oc, MDL);
+
+ get_server_source_address(&from, options, packet);
+
+ /* If there were agent options in the incoming packet, return
+ * them. We do not check giaddr to detect the presence of a
+ * relay, as this excludes "l2" relay agents which have no
+ * giaddr to set.
+ */
+ if (packet->options->universe_count > agent_universe.index &&
+ packet->options->universes [agent_universe.index]) {
+ option_chain_head_reference
+ ((struct option_chain_head **)
+ &(options -> universes [agent_universe.index]),
+ (struct option_chain_head *)
+ packet -> options -> universes [agent_universe.index],
+ MDL);
+ }
+
+ /* Do not use the client's requested parameter list. */
+ delete_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+
+ /* Set up the option buffer... */
+ outgoing.packet_length =
+ cons_options (packet, outgoing.raw, (struct lease *)0,
+ (struct client_state *)0,
+ 0, packet -> options, options, &global_scope,
+ 0, 0, 0, (struct data_string *)0, (char *)0);
+ option_state_dereference (&options, MDL);
+
+/* memset (&raw.ciaddr, 0, sizeof raw.ciaddr);*/
+ if (packet->interface->address_count)
+ raw.siaddr = packet->interface->addresses[0];
+ raw.giaddr = packet -> raw -> giaddr;
+ memcpy (raw.chaddr, packet -> raw -> chaddr, sizeof raw.chaddr);
+ raw.hlen = packet -> raw -> hlen;
+ raw.htype = packet -> raw -> htype;
+
+ raw.xid = packet -> raw -> xid;
+ raw.secs = packet -> raw -> secs;
+ raw.flags = packet -> raw -> flags | htons (BOOTP_BROADCAST);
+ raw.hops = packet -> raw -> hops;
+ raw.op = BOOTREPLY;
+
+ /* Report what we're sending... */
+ log_info ("DHCPNAK on %s to %s via %s",
+ piaddr (*cip),
+ print_hw_addr (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr),
+ packet -> raw -> giaddr.s_addr
+ ? inet_ntoa (packet -> raw -> giaddr)
+ : packet -> interface -> name);
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+ dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
+ dump_packet (&outgoing);
+ dump_raw ((unsigned char *)&raw, outgoing.packet_length);
+#endif
+
+ /* Set up the common stuff... */
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+ memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+ /* Make sure that the packet is at least as big as a BOOTP packet. */
+ if (outgoing.packet_length < BOOTP_MIN_LEN)
+ outgoing.packet_length = BOOTP_MIN_LEN;
+
+ /* If this was gatewayed, send it back to the gateway.
+ Otherwise, broadcast it on the local network. */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK))
+ to.sin_port = local_port;
+ else
+ to.sin_port = remote_port; /* for testing. */
+
+ if (fallback_interface) {
+ result = send_packet(fallback_interface, packet, &raw,
+ outgoing.packet_length, from, &to,
+ NULL);
+ return;
+ }
+ } else {
+ to.sin_addr = limited_broadcast;
+ to.sin_port = remote_port;
+ }
+
+ errno = 0;
+ result = send_packet(packet->interface, packet, &raw,
+ outgoing.packet_length, from, &to, NULL);
+}
+
+void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp)
+ struct packet *packet;
+ struct lease *lease;
+ unsigned int offer;
+ TIME when;
+ char *msg;
+ int ms_nulltp;
+ struct host_decl *hp;
+{
+ struct lease *lt;
+ struct lease_state *state;
+ struct lease *next;
+ struct host_decl *host = (struct host_decl *)0;
+ TIME lease_time;
+ TIME offered_lease_time;
+ struct data_string d1;
+ TIME min_lease_time;
+ TIME max_lease_time;
+ TIME default_lease_time;
+ struct option_cache *oc;
+ isc_result_t result;
+ TIME ping_timeout;
+ TIME lease_cltt;
+ struct in_addr from;
+ TIME remaining_time;
+ struct iaddr cip;
+#if defined(DELAYED_ACK)
+ isc_boolean_t enqueue = ISC_TRUE;
+#endif
+
+ unsigned i, j;
+ int s1;
+ int ignorep;
+ struct timeval tv;
+
+ /* If we're already acking this lease, don't do it again. */
+ if (lease -> state)
+ return;
+
+ /* Save original cltt for comparison later. */
+ lease_cltt = lease->cltt;
+
+ /* If the lease carries a host record, remember it. */
+ if (hp)
+ host_reference (&host, hp, MDL);
+ else if (lease -> host)
+ host_reference (&host, lease -> host, MDL);
+
+ /* Allocate a lease state structure... */
+ state = new_lease_state (MDL);
+ if (!state)
+ log_fatal ("unable to allocate lease state!");
+ state -> got_requested_address = packet -> got_requested_address;
+ shared_network_reference (&state -> shared_network,
+ packet -> interface -> shared_network, MDL);
+
+ /* See if we got a server identifier option. */
+ if (lookup_option (&dhcp_universe,
+ packet -> options, DHO_DHCP_SERVER_IDENTIFIER))
+ state -> got_server_identifier = 1;
+
+ maybe_return_agent_options(packet, state->options);
+
+ /* If we are offering a lease that is still currently valid, preserve
+ the events. We need to do this because if the client does not
+ REQUEST our offer, it will expire in 2 minutes, overriding the
+ expire time in the currently in force lease. We want the expire
+ events to be executed at that point. */
+ if (lease -> ends <= cur_time && offer != DHCPOFFER) {
+ /* Get rid of any old expiry or release statements - by
+ executing the statements below, we will be inserting new
+ ones if there are any to insert. */
+ if (lease -> on_expiry)
+ executable_statement_dereference (&lease -> on_expiry,
+ MDL);
+ if (lease -> on_commit)
+ executable_statement_dereference (&lease -> on_commit,
+ MDL);
+ if (lease -> on_release)
+ executable_statement_dereference (&lease -> on_release,
+ MDL);
+ }
+
+ /* Execute statements in scope starting with the subnet scope. */
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options,
+ state -> options, &lease -> scope,
+ lease -> subnet -> group,
+ (struct group *)0);
+
+ /* If the lease is from a pool, run the pool scope. */
+ if (lease -> pool)
+ (execute_statements_in_scope
+ ((struct binding_value **)0, packet, lease,
+ (struct client_state *)0, packet -> options,
+ state -> options, &lease -> scope, lease -> pool -> group,
+ lease -> pool -> shared_network -> group));
+
+ /* Execute statements from class scopes. */
+ for (i = packet -> class_count; i > 0; i--) {
+ execute_statements_in_scope
+ ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, packet -> classes [i - 1] -> group,
+ (lease -> pool
+ ? lease -> pool -> group
+ : lease -> subnet -> group));
+ }
+
+ /* See if the client is only supposed to have one lease at a time,
+ and if so, find its other leases and release them. We can only
+ do this on DHCPREQUEST. It's a little weird to do this before
+ looking at permissions, because the client might not actually
+ _get_ a lease after we've done the permission check, but the
+ assumption for this option is that the client has exactly one
+ network interface, and will only ever remember one lease. So
+ if it sends a DHCPREQUEST, and doesn't get the lease, it's already
+ forgotten about its old lease, so we can too. */
+ if (packet -> packet_type == DHCPREQUEST &&
+ (oc = lookup_option (&server_universe, state -> options,
+ SV_ONE_LEASE_PER_CLIENT)) &&
+ evaluate_boolean_option_cache (&ignorep,
+ packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options, &lease -> scope,
+ oc, MDL)) {
+ struct lease *seek;
+ if (lease -> uid_len) {
+ do {
+ seek = (struct lease *)0;
+ find_lease_by_uid (&seek, lease -> uid,
+ lease -> uid_len, MDL);
+ if (!seek)
+ break;
+ if (seek == lease && !seek -> n_uid) {
+ lease_dereference (&seek, MDL);
+ break;
+ }
+ next = (struct lease *)0;
+
+ /* Don't release expired leases, and don't
+ release the lease we're going to assign. */
+ next = (struct lease *)0;
+ while (seek) {
+ if (seek -> n_uid)
+ lease_reference (&next, seek -> n_uid, MDL);
+ if (seek != lease &&
+ seek -> binding_state != FTS_RELEASED &&
+ seek -> binding_state != FTS_EXPIRED &&
+ seek -> binding_state != FTS_RESET &&
+ seek -> binding_state != FTS_FREE &&
+ seek -> binding_state != FTS_BACKUP)
+ break;
+ lease_dereference (&seek, MDL);
+ if (next) {
+ lease_reference (&seek, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ }
+ if (next)
+ lease_dereference (&next, MDL);
+ if (seek) {
+ release_lease (seek, packet);
+ lease_dereference (&seek, MDL);
+ } else
+ break;
+ } while (1);
+ }
+ if (!lease -> uid_len ||
+ (host &&
+ !host -> client_identifier.len &&
+ (oc = lookup_option (&server_universe, state -> options,
+ SV_DUPLICATES)) &&
+ !evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope,
+ oc, MDL))) {
+ do {
+ seek = (struct lease *)0;
+ find_lease_by_hw_addr
+ (&seek, lease -> hardware_addr.hbuf,
+ lease -> hardware_addr.hlen, MDL);
+ if (!seek)
+ break;
+ if (seek == lease && !seek -> n_hw) {
+ lease_dereference (&seek, MDL);
+ break;
+ }
+ next = (struct lease *)0;
+ while (seek) {
+ if (seek -> n_hw)
+ lease_reference (&next, seek -> n_hw, MDL);
+ if (seek != lease &&
+ seek -> binding_state != FTS_RELEASED &&
+ seek -> binding_state != FTS_EXPIRED &&
+ seek -> binding_state != FTS_RESET &&
+ seek -> binding_state != FTS_FREE &&
+ seek -> binding_state != FTS_BACKUP)
+ break;
+ lease_dereference (&seek, MDL);
+ if (next) {
+ lease_reference (&seek, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ }
+ if (next)
+ lease_dereference (&next, MDL);
+ if (seek) {
+ release_lease (seek, packet);
+ lease_dereference (&seek, MDL);
+ } else
+ break;
+ } while (1);
+ }
+ }
+
+
+ /* Make sure this packet satisfies the configured minimum
+ number of seconds. */
+ memset (&d1, 0, sizeof d1);
+ if (offer == DHCPOFFER &&
+ (oc = lookup_option (&server_universe, state -> options,
+ SV_MIN_SECS))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len &&
+ ntohs (packet -> raw -> secs) < d1.data [0]) {
+ log_info("%s: configured min-secs value (%d) "
+ "is greater than secs field (%d). "
+ "message dropped.", msg, d1.data[0],
+ ntohs(packet->raw->secs));
+ data_string_forget (&d1, MDL);
+ free_lease_state (state, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /* Try to find a matching host declaration for this lease.
+ */
+ if (!host) {
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *h;
+
+ /* Try to find a host_decl that matches the client
+ identifier or hardware address on the packet, and
+ has no fixed IP address. If there is one, hang
+ it off the lease so that its option definitions
+ can be used. */
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ find_hosts_by_uid (&hp, d1.data, d1.len, MDL);
+ data_string_forget (&d1, MDL);
+ for (h = hp; h; h = h -> n_ipaddr) {
+ if (!h -> fixed_addr)
+ break;
+ }
+ if (h)
+ host_reference (&host, h, MDL);
+ if (hp != NULL)
+ host_dereference(&hp, MDL);
+ }
+ if (!host) {
+ find_hosts_by_haddr (&hp,
+ packet -> raw -> htype,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen,
+ MDL);
+ for (h = hp; h; h = h -> n_ipaddr) {
+ if (!h -> fixed_addr)
+ break;
+ }
+ if (h)
+ host_reference (&host, h, MDL);
+ if (hp != NULL)
+ host_dereference(&hp, MDL);
+ }
+ if (!host) {
+ find_hosts_by_option(&hp, packet, packet->options, MDL);
+ for (h = hp; h; h = h -> n_ipaddr) {
+ if (!h -> fixed_addr)
+ break;
+ }
+ if (h)
+ host_reference (&host, h, MDL);
+ if (hp != NULL)
+ host_dereference(&hp, MDL);
+ }
+ }
+
+ /* If we have a host_decl structure, run the options associated
+ with its group. Whether the host decl struct is old or not. */
+ if (host)
+ execute_statements_in_scope ((struct binding_value **)0,
+ packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options, &lease -> scope,
+ host -> group,
+ (lease -> pool
+ ? lease -> pool -> group
+ : lease -> subnet -> group));
+
+ /* Drop the request if it's not allowed for this client. By
+ default, unknown clients are allowed. */
+ if (!host &&
+ (oc = lookup_option (&server_universe, state -> options,
+ SV_BOOT_UNKNOWN_CLIENTS)) &&
+ !evaluate_boolean_option_cache (&ignorep,
+ packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: unknown client", msg);
+ free_lease_state (state, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+
+ /* Drop the request if it's not allowed for this client. */
+ if (!offer &&
+ (oc = lookup_option (&server_universe, state -> options,
+ SV_ALLOW_BOOTP)) &&
+ !evaluate_boolean_option_cache (&ignorep,
+ packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: bootp disallowed", msg);
+ free_lease_state (state, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+
+ /* Drop the request if booting is specifically denied. */
+ oc = lookup_option (&server_universe, state -> options,
+ SV_ALLOW_BOOTING);
+ if (oc &&
+ !evaluate_boolean_option_cache (&ignorep,
+ packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (!ignorep)
+ log_info ("%s: booting disallowed", msg);
+ free_lease_state (state, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+
+ /* If we are configured to do per-class billing, do it. */
+ if (have_billing_classes && !(lease -> flags & STATIC_LEASE)) {
+ /* See if the lease is currently being billed to a
+ class, and if so, whether or not it can continue to
+ be billed to that class. */
+ if (lease -> billing_class) {
+ for (i = 0; i < packet -> class_count; i++)
+ if (packet -> classes [i] ==
+ lease -> billing_class)
+ break;
+ if (i == packet -> class_count)
+ unbill_class (lease, lease -> billing_class);
+ }
+
+ /* If we don't have an active billing, see if we need
+ one, and if we do, try to do so. */
+ if (lease->billing_class == NULL) {
+ int bill = 0;
+ for (i = 0; i < packet->class_count; i++) {
+ if (packet->classes[i]->lease_limit) {
+ bill++;
+ if (bill_class(lease,
+ packet->classes[i]))
+ break;
+ }
+ }
+ if (bill != 0 && i == packet->class_count) {
+ log_info("%s: no available billing: lease "
+ "limit reached in all matching "
+ "classes", msg);
+ free_lease_state(state, MDL);
+ if (host)
+ host_dereference(&host, MDL);
+ return;
+ }
+
+ /* If this is an offer, undo the billing. We go
+ * through all the steps above to bill a class so
+ * we can hit the 'no available billing' mark and
+ * abort without offering. But it just doesn't make
+ * sense to permanently bill a class for a non-active
+ * lease. This means on REQUEST, we will bill this
+ * lease again (if there is a REQUEST).
+ */
+ if (offer == DHCPOFFER &&
+ lease->billing_class != NULL &&
+ lease->binding_state != FTS_ACTIVE)
+ unbill_class(lease, lease->billing_class);
+ }
+ }
+
+ /* Figure out the filename. */
+ oc = lookup_option (&server_universe, state -> options, SV_FILENAME);
+ if (oc)
+ evaluate_option_cache (&state -> filename, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL);
+
+ /* Choose a server name as above. */
+ oc = lookup_option (&server_universe, state -> options,
+ SV_SERVER_NAME);
+ if (oc)
+ evaluate_option_cache (&state -> server_name, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL);
+
+ /* At this point, we have a lease that we can offer the client.
+ Now we construct a lease structure that contains what we want,
+ and call supersede_lease to do the right thing with it. */
+ lt = (struct lease *)0;
+ result = lease_allocate (&lt, MDL);
+ if (result != ISC_R_SUCCESS) {
+ log_info ("%s: can't allocate temporary lease structure: %s",
+ msg, isc_result_totext (result));
+ free_lease_state (state, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+
+ /* Use the ip address of the lease that we finally found in
+ the database. */
+ lt -> ip_addr = lease -> ip_addr;
+
+ /* Start now. */
+ lt -> starts = cur_time;
+
+ /* Figure out how long a lease to assign. If this is a
+ dynamic BOOTP lease, its duration must be infinite. */
+ if (offer) {
+ lt->flags &= ~BOOTP_LEASE;
+
+ default_lease_time = DEFAULT_DEFAULT_LEASE_TIME;
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_DEFAULT_LEASE_TIME))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ default_lease_time =
+ getULong (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ if ((oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_LEASE_TIME)))
+ s1 = evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL);
+ else
+ s1 = 0;
+
+ if (s1 && (d1.len == 4)) {
+ u_int32_t ones = 0xffffffff;
+
+ /* One potential use of reserved leases is to allow
+ * clients to signal reservation of their lease. They
+ * can kinda sorta do this, if you squint hard enough,
+ * by supplying an 'infinite' requested-lease-time
+ * option. This is generally bad practice...you want
+ * clients to return to the server on at least some
+ * period (days, months, years) to get up-to-date
+ * config state. So;
+ *
+ * 1) A client requests 0xffffffff lease-time.
+ * 2) The server reserves the lease, and assigns a
+ * <= max_lease_time lease-time to the client, which
+ * we presume is much smaller than 0xffffffff.
+ * 3) The client ultimately fails to renew its lease
+ * (all clients go offline at some point).
+ * 4) The server retains the reservation, although
+ * the lease expires and passes through those states
+ * as normal, it's placed in the 'reserved' queue,
+ * and is under no circumstances allocated to any
+ * clients.
+ *
+ * Whether the client knows its reserving its lease or
+ * not, this can be a handy tool for a sysadmin.
+ */
+ if ((memcmp(d1.data, &ones, 4) == 0) &&
+ (oc = lookup_option(&server_universe,
+ state->options,
+ SV_RESERVE_INFINITE)) &&
+ evaluate_boolean_option_cache(&ignorep, packet,
+ lease, NULL, packet->options,
+ state->options, &lease->scope,
+ oc, MDL)) {
+ lt->flags |= RESERVED_LEASE;
+ if (!ignorep)
+ log_info("Infinite-leasetime "
+ "reservation made on %s.",
+ piaddr(lt->ip_addr));
+ }
+
+ lease_time = getULong (d1.data);
+ } else
+ lease_time = default_lease_time;
+
+ if (s1)
+ data_string_forget(&d1, MDL);
+
+ /* See if there's a maximum lease time. */
+ max_lease_time = DEFAULT_MAX_LEASE_TIME;
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_MAX_LEASE_TIME))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ max_lease_time =
+ getULong (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /* Enforce the maximum lease length. */
+ if (lease_time < 0 /* XXX */
+ || lease_time > max_lease_time)
+ lease_time = max_lease_time;
+
+ min_lease_time = DEFAULT_MIN_LEASE_TIME;
+ if (min_lease_time > max_lease_time)
+ min_lease_time = max_lease_time;
+
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_MIN_LEASE_TIME))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ min_lease_time = getULong (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /* CC: If there are less than
+ adaptive-lease-time-threshold % free leases,
+ hand out only short term leases */
+
+ memset(&d1, 0, sizeof(d1));
+ if (lease->pool &&
+ (oc = lookup_option(&server_universe, state->options,
+ SV_ADAPTIVE_LEASE_TIME_THRESHOLD)) &&
+ evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, state->options,
+ &lease->scope, oc, MDL)) {
+ if (d1.len == 1 && d1.data[0] > 0 &&
+ d1.data[0] < 100) {
+ TIME adaptive_time;
+ int poolfilled, total, count;
+
+ if (min_lease_time)
+ adaptive_time = min_lease_time;
+ else
+ adaptive_time = DEFAULT_MIN_LEASE_TIME;
+
+ /* Allow the client to keep its lease. */
+ if (lease->ends - cur_time > adaptive_time)
+ adaptive_time = lease->ends - cur_time;
+
+ count = lease->pool->lease_count;
+ total = count - (lease->pool->free_leases +
+ lease->pool->backup_leases);
+
+ poolfilled = (total > (INT_MAX / 100)) ?
+ total / (count / 100) :
+ (total * 100) / count;
+
+ log_debug("Adap-lease: Total: %d, Free: %d, "
+ "Ends: %d, Adaptive: %d, Fill: %d, "
+ "Threshold: %d",
+ lease->pool->lease_count,
+ lease->pool->free_leases,
+ (int)(lease->ends - cur_time),
+ (int)adaptive_time, poolfilled,
+ d1.data[0]);
+
+ if (poolfilled >= d1.data[0] &&
+ lease_time > adaptive_time) {
+ log_info("Pool over threshold, time "
+ "for %s reduced from %d to "
+ "%d.", piaddr(lease->ip_addr),
+ (int)lease_time,
+ (int)adaptive_time);
+
+ lease_time = adaptive_time;
+ }
+ }
+ data_string_forget(&d1, MDL);
+ }
+
+ /* a client requests an address which is not yet active*/
+ if (lease->pool && lease->pool->valid_from &&
+ cur_time < lease->pool->valid_from) {
+ /* NAK leases before pool activation date */
+ cip.len = 4;
+ memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
+ nak_lease(packet, &cip);
+ free_lease_state (state, MDL);
+ lease_dereference (&lt, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+
+ }
+
+ /* CC:
+ a) NAK current lease if past the expiration date
+ b) extend lease only up to the expiration date, but not
+ below min-lease-time
+ Setting min-lease-time is essential for this to work!
+ The value of min-lease-time determines the lenght
+ of the transition window:
+ A client renewing a second before the deadline will
+ get a min-lease-time lease. Since the current ip might not
+ be routable after the deadline, the client will
+ be offline until it DISCOVERS again. Otherwise it will
+ receive a NAK at T/2.
+ A min-lease-time of 6 seconds effectively switches over
+ all clients in this pool very quickly.
+ */
+
+ if (lease->pool && lease->pool->valid_until) {
+ if (cur_time >= lease->pool->valid_until) {
+ /* NAK leases after pool expiration date */
+ cip.len = 4;
+ memcpy (cip.iabuf, &lt->ip_addr.iabuf, 4);
+ nak_lease(packet, &cip);
+ free_lease_state (state, MDL);
+ lease_dereference (&lt, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return;
+ }
+ remaining_time = lease->pool->valid_until - cur_time;
+ if (lease_time > remaining_time)
+ lease_time = remaining_time;
+ }
+
+ if (lease_time < min_lease_time) {
+ if (min_lease_time)
+ lease_time = min_lease_time;
+ else
+ lease_time = default_lease_time;
+ }
+
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Okay, we know the lease duration. Now check the
+ failover state, if any. */
+ if (lease -> pool && lease -> pool -> failover_peer) {
+ TIME new_lease_time = lease_time;
+ dhcp_failover_state_t *peer =
+ lease -> pool -> failover_peer;
+
+ /* Copy previous lease failover ack-state. */
+ lt->tsfp = lease->tsfp;
+ lt->atsfp = lease->atsfp;
+
+ /* cltt set below */
+
+ /* Lease times less than MCLT are not a concern. */
+ if (lease_time > peer->mclt) {
+ /* Each server can only offer a lease time
+ * that is either equal to MCLT (at least),
+ * or up to TSFP+MCLT. Only if the desired
+ * lease time falls within TSFP+MCLT, can
+ * the server allow it.
+ */
+ if (lt->tsfp <= cur_time)
+ new_lease_time = peer->mclt;
+ else if ((cur_time + lease_time) >
+ (lt->tsfp + peer->mclt))
+ new_lease_time = (lt->tsfp - cur_time)
+ + peer->mclt;
+ }
+
+ /* Update potential expiry. Allow for the desired
+ * lease time plus one half the actual (whether
+ * modified downward or not) lease time, which is
+ * actually an estimate of when the client will
+ * renew. This way, the client will be able to get
+ * the desired lease time upon renewal.
+ */
+ if (offer == DHCPACK) {
+ lt->tstp = cur_time + lease_time +
+ (new_lease_time / 2);
+
+ /* If we reduced the potential expiry time,
+ * make sure we don't offer an old-expiry-time
+ * lease for this lease before the change is
+ * ack'd.
+ */
+ if (lt->tstp < lt->tsfp)
+ lt->tsfp = lt->tstp;
+ } else
+ lt->tstp = lease->tstp;
+
+ /* Use failover-modified lease time. */
+ lease_time = new_lease_time;
+ }
+#endif /* FAILOVER_PROTOCOL */
+
+ /* If the lease duration causes the time value to wrap,
+ use the maximum expiry time. */
+ if (cur_time + lease_time < cur_time)
+ state -> offered_expiry = MAX_TIME - 1;
+ else
+ state -> offered_expiry = cur_time + lease_time;
+ if (when)
+ lt -> ends = when;
+ else
+ lt -> ends = state -> offered_expiry;
+
+ /* Don't make lease active until we actually get a
+ DHCPREQUEST. */
+ if (offer == DHCPACK)
+ lt -> next_binding_state = FTS_ACTIVE;
+ else
+ lt -> next_binding_state = lease -> binding_state;
+ } else {
+ lt->flags |= BOOTP_LEASE;
+
+ lease_time = MAX_TIME - cur_time;
+
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_BOOTP_LEASE_LENGTH))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ lease_time = getULong (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_BOOTP_LEASE_CUTOFF))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ lease_time = (getULong (d1.data) -
+ cur_time);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ lt -> ends = state -> offered_expiry = cur_time + lease_time;
+ lt -> next_binding_state = FTS_ACTIVE;
+ }
+
+ /* Update Client Last Transaction Time. */
+ lt->cltt = cur_time;
+
+ /* Record the uid, if given... */
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len <= sizeof lt -> uid_buf) {
+ memcpy (lt -> uid_buf, d1.data, d1.len);
+ lt -> uid = lt -> uid_buf;
+ lt -> uid_max = sizeof lt -> uid_buf;
+ lt -> uid_len = d1.len;
+ } else {
+ unsigned char *tuid;
+ lt -> uid_max = d1.len;
+ lt -> uid_len = d1.len;
+ tuid = (unsigned char *)dmalloc (lt -> uid_max, MDL);
+ /* XXX inelegant */
+ if (!tuid)
+ log_fatal ("no memory for large uid.");
+ memcpy (tuid, d1.data, lt -> uid_len);
+ lt -> uid = tuid;
+ }
+ data_string_forget (&d1, MDL);
+ }
+
+ if (host) {
+ host_reference (&lt -> host, host, MDL);
+ host_dereference (&host, MDL);
+ }
+ if (lease -> subnet)
+ subnet_reference (&lt -> subnet, lease -> subnet, MDL);
+ if (lease -> billing_class)
+ class_reference (&lt -> billing_class,
+ lease -> billing_class, MDL);
+
+ /* Set a flag if this client is a broken client that NUL
+ terminates string options and expects us to do likewise. */
+ if (ms_nulltp)
+ lease -> flags |= MS_NULL_TERMINATION;
+ else
+ lease -> flags &= ~MS_NULL_TERMINATION;
+
+ /* Save any bindings. */
+ if (lease -> scope) {
+ binding_scope_reference (&lt -> scope, lease -> scope, MDL);
+ binding_scope_dereference (&lease -> scope, MDL);
+ }
+ if (lease -> agent_options)
+ option_chain_head_reference (&lt -> agent_options,
+ lease -> agent_options, MDL);
+
+ /* Save the vendor-class-identifier for DHCPLEASEQUERY. */
+ oc = lookup_option(&dhcp_universe, packet->options,
+ DHO_VENDOR_CLASS_IDENTIFIER);
+ if (oc != NULL &&
+ evaluate_option_cache(&d1, packet, NULL, NULL, packet->options,
+ NULL, &lease->scope, oc, MDL)) {
+ if (d1.len != 0) {
+ bind_ds_value(&lease->scope, "vendor-class-identifier",
+ &d1);
+ }
+
+ data_string_forget(&d1, MDL);
+ }
+
+ /* If we got relay agent information options from the packet, then
+ * cache them for renewal in case the relay agent can't supply them
+ * when the client unicasts. The options may be from an addressed
+ * "l3" relay, or from an unaddressed "l2" relay which does not set
+ * giaddr.
+ */
+ if (!packet->agent_options_stashed &&
+ (packet->options != NULL) &&
+ packet->options->universe_count > agent_universe.index &&
+ packet->options->universes[agent_universe.index] != NULL) {
+ oc = lookup_option (&server_universe, state -> options,
+ SV_STASH_AGENT_OPTIONS);
+ if (!oc ||
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (lt -> agent_options)
+ option_chain_head_dereference (&lt -> agent_options, MDL);
+ option_chain_head_reference
+ (&lt -> agent_options,
+ (struct option_chain_head *)
+ packet -> options -> universes [agent_universe.index],
+ MDL);
+ }
+ }
+
+ /* Replace the old lease hostname with the new one, if it's changed. */
+ oc = lookup_option (&dhcp_universe, packet -> options, DHO_HOST_NAME);
+ if (oc)
+ s1 = evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL);
+ else
+ s1 = 0;
+
+ if (oc && s1 &&
+ lease -> client_hostname &&
+ strlen (lease -> client_hostname) == d1.len &&
+ !memcmp (lease -> client_hostname, d1.data, d1.len)) {
+ /* Hasn't changed. */
+ data_string_forget (&d1, MDL);
+ lt -> client_hostname = lease -> client_hostname;
+ lease -> client_hostname = (char *)0;
+ } else if (oc && s1) {
+ lt -> client_hostname = dmalloc (d1.len + 1, MDL);
+ if (!lt -> client_hostname)
+ log_error ("no memory for client hostname.");
+ else {
+ memcpy (lt -> client_hostname, d1.data, d1.len);
+ lt -> client_hostname [d1.len] = 0;
+ }
+ data_string_forget (&d1, MDL);
+ }
+
+ /* Record the hardware address, if given... */
+ lt -> hardware_addr.hlen = packet -> raw -> hlen + 1;
+ lt -> hardware_addr.hbuf [0] = packet -> raw -> htype;
+ memcpy (&lt -> hardware_addr.hbuf [1], packet -> raw -> chaddr,
+ sizeof packet -> raw -> chaddr);
+
+ lt -> flags = lease -> flags & ~PERSISTENT_FLAGS;
+
+ /* If there are statements to execute when the lease is
+ committed, execute them. */
+ if (lease -> on_commit && (!offer || offer == DHCPACK)) {
+ execute_statements ((struct binding_value **)0,
+ packet, lt, (struct client_state *)0,
+ packet -> options,
+ state -> options, &lt -> scope,
+ lease -> on_commit);
+ if (lease -> on_commit)
+ executable_statement_dereference (&lease -> on_commit,
+ MDL);
+ }
+
+#ifdef NSUPDATE
+ /* Perform DDNS updates, if configured to. */
+ if ((!offer || offer == DHCPACK) &&
+ (!(oc = lookup_option (&server_universe, state -> options,
+ SV_DDNS_UPDATES)) ||
+ evaluate_boolean_option_cache (&ignorep, packet, lt,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lt -> scope, oc, MDL))) {
+ ddns_updates(packet, lt, lease, NULL, NULL, state->options);
+ }
+#endif /* NSUPDATE */
+
+ /* Don't call supersede_lease on a mocked-up lease. */
+ if (lease -> flags & STATIC_LEASE) {
+ /* Copy the hardware address into the static lease
+ structure. */
+ lease -> hardware_addr.hlen = packet -> raw -> hlen + 1;
+ lease -> hardware_addr.hbuf [0] = packet -> raw -> htype;
+ memcpy (&lease -> hardware_addr.hbuf [1],
+ packet -> raw -> chaddr,
+ sizeof packet -> raw -> chaddr); /* XXX */
+ } else {
+
+#if !defined(DELAYED_ACK)
+ /* Install the new information on 'lt' onto the lease at
+ * 'lease'. If this is a DHCPOFFER, it is a 'soft' promise,
+ * if it is a DHCPACK, it is a 'hard' binding, so it needs
+ * to be recorded and propogated immediately. If the update
+ * fails, don't ACK it (or BOOTREPLY) either; we may give
+ * the same lease to another client later, and that would be
+ * a conflict.
+ */
+ if (!supersede_lease(lease, lt, !offer || (offer == DHCPACK),
+ offer == DHCPACK, offer == DHCPACK)) {
+#else /* defined(DELAYED_ACK) */
+ /* Install the new information on 'lt' onto the lease at
+ * 'lease'.  We will not 'commit' this information to disk
+ * yet (fsync()), we will 'propogate' the information if
+ * this is BOOTP or a DHCPACK, but we will not 'pimmediate'ly
+ * transmit failover binding updates (this is delayed until
+ * after the fsync()). If the update fails, don't ACK it (or
+ * BOOTREPLY either); we may give the same lease out to a
+ * different client, and that would be a conflict.
+ */
+ if (!supersede_lease(lease, lt, 0, !offer || offer == DHCPACK,
+ 0)) {
+#endif
+ log_info ("%s: database update failed", msg);
+ free_lease_state (state, MDL);
+ lease_dereference (&lt, MDL);
+ return;
+ }
+ }
+ lease_dereference (&lt, MDL);
+
+ /* Remember the interface on which the packet arrived. */
+ state -> ip = packet -> interface;
+
+ /* Remember the giaddr, xid, secs, flags and hops. */
+ state -> giaddr = packet -> raw -> giaddr;
+ state -> ciaddr = packet -> raw -> ciaddr;
+ state -> xid = packet -> raw -> xid;
+ state -> secs = packet -> raw -> secs;
+ state -> bootp_flags = packet -> raw -> flags;
+ state -> hops = packet -> raw -> hops;
+ state -> offer = offer;
+
+ /* If we're always supposed to broadcast to this client, set
+ the broadcast bit in the bootp flags field. */
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_ALWAYS_BROADCAST)) &&
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL))
+ state -> bootp_flags |= htons (BOOTP_BROADCAST);
+
+ /* Get the Maximum Message Size option from the packet, if one
+ was sent. */
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_MAX_MESSAGE_SIZE);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int16_t))
+ state -> max_message_size = getUShort (d1.data);
+ data_string_forget (&d1, MDL);
+ } else {
+ oc = lookup_option (&dhcp_universe, state -> options,
+ DHO_DHCP_MAX_MESSAGE_SIZE);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int16_t))
+ state -> max_message_size =
+ getUShort (d1.data);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /* Get the Subnet Selection option from the packet, if one
+ was sent. */
+ if ((oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_SUBNET_SELECTION))) {
+
+ /* Make a copy of the data. */
+ struct option_cache *noc = (struct option_cache *)0;
+ if (option_cache_allocate (&noc, MDL)) {
+ if (oc -> data.len)
+ data_string_copy (&noc -> data,
+ &oc -> data, MDL);
+ if (oc -> expression)
+ expression_reference (&noc -> expression,
+ oc -> expression, MDL);
+ if (oc -> option)
+ option_reference(&(noc->option), oc->option,
+ MDL);
+ }
+
+ save_option (&dhcp_universe, state -> options, noc);
+ option_cache_dereference (&noc, MDL);
+ }
+
+ /* Now, if appropriate, put in DHCP-specific options that
+ override those. */
+ if (state -> offer) {
+ i = DHO_DHCP_MESSAGE_TYPE;
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ &state -> offer, 1, 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+
+ get_server_source_address(&from, state->options, packet);
+ memcpy(state->from.iabuf, &from, sizeof(from));
+ state->from.len = sizeof(from);
+
+ offered_lease_time =
+ state -> offered_expiry - cur_time;
+
+ putULong(state->expiry, (u_int32_t)offered_lease_time);
+ i = DHO_DHCP_LEASE_TIME;
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data(&oc->expression, state->expiry,
+ 4, 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+
+ /*
+ * Validate any configured renew or rebinding times against
+ * the determined lease time. Do rebinding first so that
+ * the renew time can be validated against the rebind time.
+ */
+ if ((oc = lookup_option(&dhcp_universe, state->options,
+ DHO_DHCP_REBINDING_TIME)) != NULL &&
+ evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, state->options,
+ &lease->scope, oc, MDL)) {
+ TIME rebind_time = getULong(d1.data);
+
+ /* Drop the configured (invalid) rebinding time. */
+ if (rebind_time >= offered_lease_time)
+ delete_option(&dhcp_universe, state->options,
+ DHO_DHCP_REBINDING_TIME);
+ else /* XXX: variable is reused. */
+ offered_lease_time = rebind_time;
+
+ data_string_forget(&d1, MDL);
+ }
+
+ if ((oc = lookup_option(&dhcp_universe, state->options,
+ DHO_DHCP_RENEWAL_TIME)) != NULL &&
+ evaluate_option_cache(&d1, packet, lease, NULL,
+ packet->options, state->options,
+ &lease->scope, oc, MDL)) {
+ if (getULong(d1.data) >= offered_lease_time)
+ delete_option(&dhcp_universe, state->options,
+ DHO_DHCP_RENEWAL_TIME);
+
+ data_string_forget(&d1, MDL);
+ }
+ } else {
+ /* XXXSK: should we use get_server_source_address() here? */
+ if (state -> ip -> address_count) {
+ state -> from.len =
+ sizeof state -> ip -> addresses [0];
+ memcpy (state -> from.iabuf,
+ &state -> ip -> addresses [0],
+ state -> from.len);
+ }
+ }
+
+ /* Figure out the address of the boot file server. */
+ memset (&state -> siaddr, 0, sizeof state -> siaddr);
+ if ((oc =
+ lookup_option (&server_universe,
+ state -> options, SV_NEXT_SERVER))) {
+ if (evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ /* If there was more than one answer,
+ take the first. */
+ if (d1.len >= 4 && d1.data)
+ memcpy (&state -> siaddr, d1.data, 4);
+ data_string_forget (&d1, MDL);
+ }
+ }
+
+ /* Use the subnet mask from the subnet declaration if no other
+ mask has been provided. */
+ i = DHO_SUBNET_MASK;
+ if (!lookup_option (&dhcp_universe, state -> options, i)) {
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ lease -> subnet -> netmask.iabuf,
+ lease -> subnet -> netmask.len,
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+
+ /* Use the hostname from the host declaration if there is one
+ and no hostname has otherwise been provided, and if the
+ use-host-decl-name flag is set. */
+ i = DHO_HOST_NAME;
+ j = SV_USE_HOST_DECL_NAMES;
+ if (!lookup_option (&dhcp_universe, state -> options, i) &&
+ lease -> host && lease -> host -> name &&
+ (evaluate_boolean_option_cache
+ (&ignorep, packet, lease, (struct client_state *)0,
+ packet -> options, state -> options, &lease -> scope,
+ lookup_option (&server_universe, state -> options, j), MDL))) {
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ ((unsigned char *)
+ lease -> host -> name),
+ strlen (lease -> host -> name),
+ 1, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+
+ /* If we don't have a hostname yet, and we've been asked to do
+ a reverse lookup to find the hostname, do it. */
+ i = DHO_HOST_NAME;
+ j = SV_GET_LEASE_HOSTNAMES;
+ if (!lookup_option(&dhcp_universe, state->options, i) &&
+ evaluate_boolean_option_cache
+ (&ignorep, packet, lease, NULL,
+ packet->options, state->options, &lease->scope,
+ lookup_option (&server_universe, state->options, j), MDL)) {
+ struct in_addr ia;
+ struct hostent *h;
+
+ memcpy (&ia, lease -> ip_addr.iabuf, 4);
+
+ h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET);
+ if (!h)
+ log_error ("No hostname for %s", inet_ntoa (ia));
+ else {
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ ((unsigned char *)
+ h -> h_name),
+ strlen (h -> h_name) + 1,
+ 1, 1, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+ }
+
+ /* If so directed, use the leased IP address as the router address.
+ This supposedly makes Win95 machines ARP for all IP addresses,
+ so if the local router does proxy arp, you win. */
+
+ if (evaluate_boolean_option_cache
+ (&ignorep, packet, lease, (struct client_state *)0,
+ packet -> options, state -> options, &lease -> scope,
+ lookup_option (&server_universe, state -> options,
+ SV_USE_LEASE_ADDR_FOR_DEFAULT_ROUTE), MDL)) {
+ i = DHO_ROUTERS;
+ oc = lookup_option (&dhcp_universe, state -> options, i);
+ if (!oc) {
+ oc = (struct option_cache *)0;
+ if (option_cache_allocate (&oc, MDL)) {
+ if (make_const_data (&oc -> expression,
+ lease -> ip_addr.iabuf,
+ lease -> ip_addr.len,
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &i, 0, MDL);
+ save_option (&dhcp_universe,
+ state -> options, oc);
+ }
+ option_cache_dereference (&oc, MDL);
+ }
+ }
+ }
+
+ /* If a site option space has been specified, use that for
+ site option codes. */
+ i = SV_SITE_OPTION_SPACE;
+ if ((oc = lookup_option (&server_universe, state -> options, i)) &&
+ evaluate_option_cache (&d1, packet, lease,
+ (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL)) {
+ struct universe *u = (struct universe *)0;
+
+ if (!universe_hash_lookup (&u, universe_hash,
+ (const char *)d1.data, d1.len,
+ MDL)) {
+ log_error ("unknown option space %s.", d1.data);
+ return;
+ }
+
+ state -> options -> site_universe = u -> index;
+ state->options->site_code_min = find_min_site_code(u);
+ data_string_forget (&d1, MDL);
+ } else {
+ state -> options -> site_code_min = 0;
+ state -> options -> site_universe = dhcp_universe.index;
+ }
+
+ /* If the client has provided a list of options that it wishes
+ returned, use it to prioritize. If there's a parameter
+ request list in scope, use that in preference. Otherwise
+ use the default priority list. */
+
+ oc = lookup_option (&dhcp_universe, state -> options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+
+ if (!oc)
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+ if (oc)
+ evaluate_option_cache (&state -> parameter_request_list,
+ packet, lease, (struct client_state *)0,
+ packet -> options, state -> options,
+ &lease -> scope, oc, MDL);
+
+#ifdef DEBUG_PACKET
+ dump_packet (packet);
+ dump_raw ((unsigned char *)packet -> raw, packet -> packet_length);
+#endif
+
+ lease -> state = state;
+
+ log_info ("%s", msg);
+
+ /* Hang the packet off the lease state. */
+ packet_reference (&lease -> state -> packet, packet, MDL);
+
+ /* If this is a DHCPOFFER, ping the lease address before actually
+ sending the offer. */
+ if (offer == DHCPOFFER && !(lease -> flags & STATIC_LEASE) &&
+ ((cur_time - lease_cltt) > 60) &&
+ (!(oc = lookup_option (&server_universe, state -> options,
+ SV_PING_CHECKS)) ||
+ evaluate_boolean_option_cache (&ignorep, packet, lease,
+ (struct client_state *)0,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL))) {
+ icmp_echorequest (&lease -> ip_addr);
+
+ /* Determine whether to use configured or default ping timeout.
+ */
+ if ((oc = lookup_option (&server_universe, state -> options,
+ SV_PING_TIMEOUT)) &&
+ evaluate_option_cache (&d1, packet, lease, NULL,
+ packet -> options,
+ state -> options,
+ &lease -> scope, oc, MDL)) {
+ if (d1.len == sizeof (u_int32_t))
+ ping_timeout = getULong (d1.data);
+ else
+ ping_timeout = DEFAULT_PING_TIMEOUT;
+
+ data_string_forget (&d1, MDL);
+ } else
+ ping_timeout = DEFAULT_PING_TIMEOUT;
+
+#ifdef DEBUG
+ log_debug ("Ping timeout: %ld", (long)ping_timeout);
+#endif
+
+ /*
+ * Set a timeout for 'ping-timeout' seconds from NOW, including
+ * current microseconds. As ping-timeout defaults to 1, the
+ * exclusion of current microseconds causes a value somewhere
+ * /between/ zero and one.
+ */
+ tv.tv_sec = cur_tv.tv_sec + ping_timeout;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout (&tv, lease_ping_timeout, lease,
+ (tvref_t)lease_reference,
+ (tvunref_t)lease_dereference);
+ ++outstanding_pings;
+ } else {
+ lease->cltt = cur_time;
+#if defined(DELAYED_ACK)
+ if (!(lease->flags & STATIC_LEASE) &&
+ (!offer || (offer == DHCPACK)))
+ delayed_ack_enqueue(lease);
+ else
+#endif
+ dhcp_reply(lease);
+ }
+}
+
+/*
+ * CC: queue single ACK:
+ * - write the lease (but do not fsync it yet)
+ * - add to double linked list
+ * - commit if more than xx ACKs pending
+ * - if necessary set the max timer and bump the next timer
+ * but only up to the max timer value.
+ */
+
+void
+delayed_ack_enqueue(struct lease *lease)
+{
+ struct leasequeue *q;
+
+ if (!write_lease(lease))
+ return;
+ if (free_ackqueue) {
+ q = free_ackqueue;
+ free_ackqueue = q->next;
+ } else {
+ q = ((struct leasequeue *)
+ dmalloc(sizeof(struct leasequeue), MDL));
+ if (!q)
+ log_fatal("delayed_ack_enqueue: no memory!");
+ }
+ memset(q, 0, sizeof *q);
+ /* prepend to ackqueue*/
+ lease_reference(&q->lease, lease, MDL);
+ q->next = ackqueue_head;
+ ackqueue_head = q;
+ if (!ackqueue_tail)
+ ackqueue_tail = q;
+ else
+ q->next->prev = q;
+
+ outstanding_acks++;
+ if (outstanding_acks > max_outstanding_acks) {
+ commit_leases();
+
+ /* Reset max_fsync and cancel any pending timeout. */
+ memset(&max_fsync, 0, sizeof(max_fsync));
+ cancel_timeout(commit_leases_ackout, NULL);
+ } else {
+ struct timeval next_fsync;
+
+ if (max_fsync.tv_sec == 0 && max_fsync.tv_usec == 0) {
+ /* set the maximum time we'll wait */
+ max_fsync.tv_sec = cur_tv.tv_sec + max_ack_delay_secs;
+ max_fsync.tv_usec = cur_tv.tv_usec +
+ max_ack_delay_usecs;
+
+ if (max_fsync.tv_usec >= 1000000) {
+ max_fsync.tv_sec++;
+ max_fsync.tv_usec -= 1000000;
+ }
+ }
+
+ /* Set the timeout */
+ next_fsync.tv_sec = cur_tv.tv_sec;
+ next_fsync.tv_usec = cur_tv.tv_usec + min_ack_delay_usecs;
+ if (next_fsync.tv_usec >= 1000000) {
+ next_fsync.tv_sec++;
+ next_fsync.tv_usec -= 1000000;
+ }
+ /* but not more than the max */
+ if ((next_fsync.tv_sec > max_fsync.tv_sec) ||
+ ((next_fsync.tv_sec == max_fsync.tv_sec) &&
+ (next_fsync.tv_usec > max_fsync.tv_usec))) {
+ next_fsync.tv_sec = max_fsync.tv_sec;
+ next_fsync.tv_usec = max_fsync.tv_usec;
+ }
+
+ add_timeout(&next_fsync, commit_leases_ackout, NULL,
+ (tvref_t) NULL, (tvunref_t) NULL);
+ }
+}
+
+static void
+commit_leases_ackout(void *foo)
+{
+ if (outstanding_acks) {
+ commit_leases();
+
+ memset(&max_fsync, 0, sizeof(max_fsync));
+ }
+}
+
+/* CC: process the delayed ACK responses:
+ - send out the ACK packets
+ - move the queue slots to the free list
+ */
+void
+flush_ackqueue(void *foo)
+{
+ struct leasequeue *ack, *p;
+ /* process from bottom to retain packet order */
+ for (ack = ackqueue_tail ; ack ; ack = p) {
+ p = ack->prev;
+
+ /* dhcp_reply() requires that the reply state still be valid */
+ if (ack->lease->state == NULL)
+ log_error("delayed ack for %s has gone stale",
+ piaddr(ack->lease->ip_addr));
+ else
+ dhcp_reply(ack->lease);
+
+ lease_dereference(&ack->lease, MDL);
+ ack->next = free_ackqueue;
+ free_ackqueue = ack;
+ }
+ ackqueue_head = NULL;
+ ackqueue_tail = NULL;
+ outstanding_acks = 0;
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void
+relinquish_ackqueue(void)
+{
+ struct leasequeue *q, *n;
+
+ for (q = ackqueue_head ; q ; q = n) {
+ n = q->next;
+ dfree(q, MDL);
+ }
+ for (q = free_ackqueue ; q ; q = n) {
+ n = q->next;
+ dfree(q, MDL);
+ }
+}
+#endif
+
+void dhcp_reply (lease)
+ struct lease *lease;
+{
+ int bufs = 0;
+ unsigned packet_length;
+ struct dhcp_packet raw;
+ struct sockaddr_in to;
+ struct in_addr from;
+ struct hardware hto;
+ int result;
+ struct lease_state *state = lease -> state;
+ int nulltp, bootpp, unicastp = 1;
+ struct data_string d1;
+ const char *s;
+
+ if (!state)
+ log_fatal ("dhcp_reply was supplied lease with no state!");
+
+ /* Compose a response for the client... */
+ memset (&raw, 0, sizeof raw);
+ memset (&d1, 0, sizeof d1);
+
+ /* Copy in the filename if given; otherwise, flag the filename
+ buffer as available for options. */
+ if (state -> filename.len && state -> filename.data) {
+ memcpy (raw.file,
+ state -> filename.data,
+ state -> filename.len > sizeof raw.file
+ ? sizeof raw.file : state -> filename.len);
+ if (sizeof raw.file > state -> filename.len)
+ memset (&raw.file [state -> filename.len], 0,
+ (sizeof raw.file) - state -> filename.len);
+ else
+ log_info("file name longer than packet field "
+ "truncated - field: %lu name: %d %.*s",
+ (unsigned long)sizeof(raw.file),
+ state->filename.len, state->filename.len,
+ state->filename.data);
+ } else
+ bufs |= 1;
+
+ /* Copy in the server name if given; otherwise, flag the
+ server_name buffer as available for options. */
+ if (state -> server_name.len && state -> server_name.data) {
+ memcpy (raw.sname,
+ state -> server_name.data,
+ state -> server_name.len > sizeof raw.sname
+ ? sizeof raw.sname : state -> server_name.len);
+ if (sizeof raw.sname > state -> server_name.len)
+ memset (&raw.sname [state -> server_name.len], 0,
+ (sizeof raw.sname) - state -> server_name.len);
+ else
+ log_info("server name longer than packet field "
+ "truncated - field: %lu name: %d %.*s",
+ (unsigned long)sizeof(raw.sname),
+ state->server_name.len,
+ state->server_name.len,
+ state->server_name.data);
+ } else
+ bufs |= 2; /* XXX */
+
+ memcpy (raw.chaddr,
+ &lease -> hardware_addr.hbuf [1], sizeof raw.chaddr);
+ raw.hlen = lease -> hardware_addr.hlen - 1;
+ raw.htype = lease -> hardware_addr.hbuf [0];
+
+ /* See if this is a Microsoft client that NUL-terminates its
+ strings and expects us to do likewise... */
+ if (lease -> flags & MS_NULL_TERMINATION)
+ nulltp = 1;
+ else
+ nulltp = 0;
+
+ /* See if this is a bootp client... */
+ if (state -> offer)
+ bootpp = 0;
+ else
+ bootpp = 1;
+
+ /* Insert such options as will fit into the buffer. */
+ packet_length = cons_options (state -> packet, &raw, lease,
+ (struct client_state *)0,
+ state -> max_message_size,
+ state -> packet -> options,
+ state -> options, &global_scope,
+ bufs, nulltp, bootpp,
+ &state -> parameter_request_list,
+ (char *)0);
+
+ memcpy (&raw.ciaddr, &state -> ciaddr, sizeof raw.ciaddr);
+ memcpy (&raw.yiaddr, lease -> ip_addr.iabuf, 4);
+ raw.siaddr = state -> siaddr;
+ raw.giaddr = state -> giaddr;
+
+ raw.xid = state -> xid;
+ raw.secs = state -> secs;
+ raw.flags = state -> bootp_flags;
+ raw.hops = state -> hops;
+ raw.op = BOOTREPLY;
+
+ if (lease -> client_hostname) {
+ if ((strlen (lease -> client_hostname) <= 64) &&
+ db_printable((unsigned char *)lease->client_hostname))
+ s = lease -> client_hostname;
+ else
+ s = "Hostname Unsuitable for Printing";
+ } else
+ s = (char *)0;
+
+ /* Say what we're doing... */
+ log_info ("%s on %s to %s %s%s%svia %s",
+ (state -> offer
+ ? (state -> offer == DHCPACK ? "DHCPACK" : "DHCPOFFER")
+ : "BOOTREPLY"),
+ piaddr (lease -> ip_addr),
+ (lease -> hardware_addr.hlen
+ ? print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1])
+ : print_hex_1(lease->uid_len, lease->uid, 60)),
+ s ? "(" : "", s ? s : "", s ? ") " : "",
+ (state -> giaddr.s_addr
+ ? inet_ntoa (state -> giaddr)
+ : state -> ip -> name));
+
+ /* Set up the hardware address... */
+ hto.hlen = lease -> hardware_addr.hlen;
+ memcpy (hto.hbuf, lease -> hardware_addr.hbuf, hto.hlen);
+
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof to;
+#endif
+ memset (to.sin_zero, 0, sizeof to.sin_zero);
+
+#ifdef DEBUG_PACKET
+ dump_raw ((unsigned char *)&raw, packet_length);
+#endif
+
+ /* Make sure outgoing packets are at least as big
+ as a BOOTP packet. */
+ if (packet_length < BOOTP_MIN_LEN)
+ packet_length = BOOTP_MIN_LEN;
+
+ /* If this was gatewayed, send it back to the gateway... */
+ if (raw.giaddr.s_addr) {
+ to.sin_addr = raw.giaddr;
+ if (raw.giaddr.s_addr != htonl (INADDR_LOOPBACK))
+ to.sin_port = local_port;
+ else
+ to.sin_port = remote_port; /* For debugging. */
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, packet_length,
+ raw.siaddr, &to,
+ (struct hardware *)0);
+
+ free_lease_state (state, MDL);
+ lease -> state = (struct lease_state *)0;
+ return;
+ }
+
+ /* If the client is RENEWING, unicast to the client using the
+ regular IP stack. Some clients, particularly those that
+ follow RFC1541, are buggy, and send both ciaddr and server
+ identifier. We deal with this situation by assuming that
+ if we got both dhcp-server-identifier and ciaddr, and
+ giaddr was not set, then the client is on the local
+ network, and we can therefore unicast or broadcast to it
+ successfully. A client in REQUESTING state on another
+ network that's making this mistake will have set giaddr,
+ and will therefore get a relayed response from the above
+ code. */
+ } else if (raw.ciaddr.s_addr &&
+ !((state -> got_server_identifier ||
+ (raw.flags & htons (BOOTP_BROADCAST))) &&
+ /* XXX This won't work if giaddr isn't zero, but it is: */
+ (state -> shared_network ==
+ lease -> subnet -> shared_network)) &&
+ state -> offer == DHCPACK) {
+ to.sin_addr = raw.ciaddr;
+ to.sin_port = remote_port;
+
+ if (fallback_interface) {
+ result = send_packet (fallback_interface,
+ (struct packet *)0,
+ &raw, packet_length,
+ raw.siaddr, &to,
+ (struct hardware *)0);
+ free_lease_state (state, MDL);
+ lease -> state = (struct lease_state *)0;
+ return;
+ }
+
+ /* If it comes from a client that already knows its address
+ and is not requesting a broadcast response, and we can
+ unicast to a client without using the ARP protocol, sent it
+ directly to that client. */
+ } else if (!(raw.flags & htons (BOOTP_BROADCAST)) &&
+ can_unicast_without_arp (state -> ip)) {
+ to.sin_addr = raw.yiaddr;
+ to.sin_port = remote_port;
+
+ /* Otherwise, broadcast it on the local network. */
+ } else {
+ to.sin_addr = limited_broadcast;
+ to.sin_port = remote_port;
+ if (!(lease -> flags & UNICAST_BROADCAST_HACK))
+ unicastp = 0;
+ }
+
+ memcpy (&from, state -> from.iabuf, sizeof from);
+
+ result = send_packet (state -> ip,
+ (struct packet *)0, &raw, packet_length,
+ from, &to,
+ unicastp ? &hto : (struct hardware *)0);
+
+ /* Free all of the entries in the option_state structure
+ now that we're done with them. */
+
+ free_lease_state (state, MDL);
+ lease -> state = (struct lease_state *)0;
+}
+
+int find_lease (struct lease **lp,
+ struct packet *packet, struct shared_network *share, int *ours,
+ int *peer_has_leases, struct lease *ip_lease_in,
+ const char *file, int line)
+{
+ struct lease *uid_lease = (struct lease *)0;
+ struct lease *ip_lease = (struct lease *)0;
+ struct lease *hw_lease = (struct lease *)0;
+ struct lease *lease = (struct lease *)0;
+ struct iaddr cip;
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *host = (struct host_decl *)0;
+ struct lease *fixed_lease = (struct lease *)0;
+ struct lease *next = (struct lease *)0;
+ struct option_cache *oc;
+ struct data_string d1;
+ int have_client_identifier = 0;
+ struct data_string client_identifier;
+ struct hardware h;
+
+#if defined(FAILOVER_PROTOCOL)
+ /* Quick check to see if the peer has leases. */
+ if (peer_has_leases) {
+ struct pool *pool;
+
+ for (pool = share->pools ; pool ; pool = pool->next) {
+ dhcp_failover_state_t *peer = pool->failover_peer;
+
+ if (peer &&
+ ((peer->i_am == primary && pool->backup_leases) ||
+ (peer->i_am == secondary && pool->free_leases))) {
+ *peer_has_leases = 1;
+ break;
+ }
+ }
+ }
+#endif /* FAILOVER_PROTOCOL */
+
+ if (packet -> raw -> ciaddr.s_addr) {
+ cip.len = 4;
+ memcpy (cip.iabuf, &packet -> raw -> ciaddr, 4);
+ } else {
+ /* Look up the requested address. */
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_REQUESTED_ADDRESS);
+ memset (&d1, 0, sizeof d1);
+ if (oc &&
+ evaluate_option_cache (&d1, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ packet -> got_requested_address = 1;
+ cip.len = 4;
+ memcpy (cip.iabuf, d1.data, cip.len);
+ data_string_forget (&d1, MDL);
+ } else
+ cip.len = 0;
+ }
+
+ /* Try to find a host or lease that's been assigned to the
+ specified unique client identifier. */
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ memset (&client_identifier, 0, sizeof client_identifier);
+ if (oc &&
+ evaluate_option_cache (&client_identifier,
+ packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ /* Remember this for later. */
+ have_client_identifier = 1;
+
+ /* First, try to find a fixed host entry for the specified
+ client identifier... */
+ if (find_hosts_by_uid (&hp, client_identifier.data,
+ client_identifier.len, MDL)) {
+ /* Remember if we know of this client. */
+ packet -> known = 1;
+ mockup_lease (&fixed_lease, packet, share, hp);
+ }
+
+#if defined (DEBUG_FIND_LEASE)
+ if (fixed_lease) {
+ log_info ("Found host for client identifier: %s.",
+ piaddr (fixed_lease -> ip_addr));
+ }
+#endif
+ if (hp) {
+ if (!fixed_lease) /* Save the host if we found one. */
+ host_reference (&host, hp, MDL);
+ host_dereference (&hp, MDL);
+ }
+
+ find_lease_by_uid (&uid_lease, client_identifier.data,
+ client_identifier.len, MDL);
+ }
+
+ /* If we didn't find a fixed lease using the uid, try doing
+ it with the hardware address... */
+ if (!fixed_lease && !host) {
+ if (find_hosts_by_haddr (&hp, packet -> raw -> htype,
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen, MDL)) {
+ /* Remember if we know of this client. */
+ packet -> known = 1;
+ if (host)
+ host_dereference (&host, MDL);
+ host_reference (&host, hp, MDL);
+ host_dereference (&hp, MDL);
+ mockup_lease (&fixed_lease, packet, share, host);
+#if defined (DEBUG_FIND_LEASE)
+ if (fixed_lease) {
+ log_info ("Found host for link address: %s.",
+ piaddr (fixed_lease -> ip_addr));
+ }
+#endif
+ }
+ }
+
+ /* Finally, if we haven't found anything yet try again with the
+ * host-identifier option ... */
+ if (!fixed_lease && !host) {
+ if (find_hosts_by_option(&hp, packet,
+ packet->options, MDL) == 1) {
+ packet->known = 1;
+ if (host)
+ host_dereference(&host, MDL);
+ host_reference(&host, hp, MDL);
+ host_dereference(&hp, MDL);
+ mockup_lease (&fixed_lease, packet, share, host);
+#if defined (DEBUG_FIND_LEASE)
+ if (fixed_lease) {
+ log_info ("Found host via host-identifier");
+ }
+#endif
+ }
+ }
+
+ /* If fixed_lease is present but does not match the requested
+ IP address, and this is a DHCPREQUEST, then we can't return
+ any other lease, so we might as well return now. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease &&
+ (fixed_lease -> ip_addr.len != cip.len ||
+ memcmp (fixed_lease -> ip_addr.iabuf,
+ cip.iabuf, cip.len))) {
+ if (ours)
+ *ours = 1;
+ strcpy (dhcp_message, "requested address is incorrect");
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("Client's fixed-address %s doesn't match %s%s",
+ piaddr (fixed_lease -> ip_addr), "request ",
+ print_dotted_quads (cip.len, cip.iabuf));
+#endif
+ goto out;
+ }
+
+ /*
+ * If we found leases matching the client identifier, loop through
+ * the n_uid pointer looking for one that's actually valid. We
+ * can't do this until we get here because we depend on
+ * packet -> known, which may be set by either the uid host
+ * lookup or the haddr host lookup.
+ *
+ * Note that the n_uid lease chain is sorted in order of
+ * preference, so the first one is the best one.
+ */
+ while (uid_lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("trying next lease matching client id: %s",
+ piaddr (uid_lease -> ip_addr));
+#endif
+
+#if defined (FAILOVER_PROTOCOL)
+ /*
+ * When we lookup a lease by uid, we know the client identifier
+ * matches the lease's record. If it is active, or was last
+ * active with the same client, we can trivially extend it.
+ * If is not or was not active, we can allocate it to this
+ * client if it matches the usual free/backup criteria (which
+ * is contained in lease_mine_to_reallocate()).
+ */
+ if (uid_lease->binding_state != FTS_ACTIVE &&
+ uid_lease->rewind_binding_state != FTS_ACTIVE &&
+ !lease_mine_to_reallocate(uid_lease)) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info("not active or not mine to allocate: %s",
+ piaddr(uid_lease->ip_addr));
+#endif
+ goto n_uid;
+ }
+#endif
+
+ if (uid_lease -> subnet -> shared_network != share) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("wrong network segment: %s",
+ piaddr (uid_lease -> ip_addr));
+#endif
+ goto n_uid;
+ }
+
+ if ((uid_lease -> pool -> prohibit_list &&
+ permitted (packet, uid_lease -> pool -> prohibit_list)) ||
+ (uid_lease -> pool -> permit_list &&
+ !permitted (packet, uid_lease -> pool -> permit_list))) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not permitted: %s",
+ piaddr (uid_lease -> ip_addr));
+#endif
+ n_uid:
+ if (uid_lease -> n_uid)
+ lease_reference (&next,
+ uid_lease -> n_uid, MDL);
+ if (!packet -> raw -> ciaddr.s_addr)
+ release_lease (uid_lease, packet);
+ lease_dereference (&uid_lease, MDL);
+ if (next) {
+ lease_reference (&uid_lease, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ continue;
+ }
+ break;
+ }
+#if defined (DEBUG_FIND_LEASE)
+ if (uid_lease)
+ log_info ("Found lease for client id: %s.",
+ piaddr (uid_lease -> ip_addr));
+#endif
+
+ /* Find a lease whose hardware address matches, whose client
+ * identifier matches (or equally doesn't have one), that's
+ * permitted, and that's on the correct subnet.
+ *
+ * Note that the n_hw chain is sorted in order of preference, so
+ * the first one found is the best one.
+ */
+ h.hlen = packet -> raw -> hlen + 1;
+ h.hbuf [0] = packet -> raw -> htype;
+ memcpy (&h.hbuf [1], packet -> raw -> chaddr, packet -> raw -> hlen);
+ find_lease_by_hw_addr (&hw_lease, h.hbuf, h.hlen, MDL);
+ while (hw_lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("trying next lease matching hw addr: %s",
+ piaddr (hw_lease -> ip_addr));
+#endif
+#if defined (FAILOVER_PROTOCOL)
+ /*
+ * When we lookup a lease by chaddr, we know the MAC address
+ * matches the lease record (we will check if the lease has a
+ * client-id the client does not next). If the lease is
+ * currently active or was last active with this client, we can
+ * trivially extend it. Otherwise, there are a set of rules
+ * that govern if we can reallocate this lease to any client
+ * ("lease_mine_to_reallocate()") including this one.
+ */
+ if (hw_lease->binding_state != FTS_ACTIVE &&
+ hw_lease->rewind_binding_state != FTS_ACTIVE &&
+ !lease_mine_to_reallocate(hw_lease)) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info("not active or not mine to allocate: %s",
+ piaddr(hw_lease->ip_addr));
+#endif
+ goto n_hw;
+ }
+#endif
+
+ /*
+ * This conditional skips "potentially active" leases (leases
+ * we think are expired may be extended by the peer, etc) that
+ * may be assigned to a differently /client-identified/ client
+ * with the same MAC address.
+ */
+ if (hw_lease -> binding_state != FTS_FREE &&
+ hw_lease -> binding_state != FTS_BACKUP &&
+ hw_lease -> uid &&
+ (!have_client_identifier ||
+ hw_lease -> uid_len != client_identifier.len ||
+ memcmp (hw_lease -> uid, client_identifier.data,
+ hw_lease -> uid_len))) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("wrong client identifier: %s",
+ piaddr (hw_lease -> ip_addr));
+#endif
+ goto n_hw;
+ }
+ if (hw_lease -> subnet -> shared_network != share) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("wrong network segment: %s",
+ piaddr (hw_lease -> ip_addr));
+#endif
+ goto n_hw;
+ }
+ if ((hw_lease -> pool -> prohibit_list &&
+ permitted (packet, hw_lease -> pool -> prohibit_list)) ||
+ (hw_lease -> pool -> permit_list &&
+ !permitted (packet, hw_lease -> pool -> permit_list))) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not permitted: %s",
+ piaddr (hw_lease -> ip_addr));
+#endif
+ if (!packet -> raw -> ciaddr.s_addr)
+ release_lease (hw_lease, packet);
+ n_hw:
+ if (hw_lease -> n_hw)
+ lease_reference (&next, hw_lease -> n_hw, MDL);
+ lease_dereference (&hw_lease, MDL);
+ if (next) {
+ lease_reference (&hw_lease, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ continue;
+ }
+ break;
+ }
+#if defined (DEBUG_FIND_LEASE)
+ if (hw_lease)
+ log_info ("Found lease for hardware address: %s.",
+ piaddr (hw_lease -> ip_addr));
+#endif
+
+ /* Try to find a lease that's been allocated to the client's
+ IP address. */
+ if (ip_lease_in)
+ lease_reference (&ip_lease, ip_lease_in, MDL);
+ else if (cip.len)
+ find_lease_by_ip_addr (&ip_lease, cip, MDL);
+
+#if defined (DEBUG_FIND_LEASE)
+ if (ip_lease)
+ log_info ("Found lease for requested address: %s.",
+ piaddr (ip_lease -> ip_addr));
+#endif
+
+ /* If ip_lease is valid at this point, set ours to one, so that
+ even if we choose a different lease, we know that the address
+ the client was requesting was ours, and thus we can NAK it. */
+ if (ip_lease && ours)
+ *ours = 1;
+
+ /* If the requested IP address isn't on the network the packet
+ came from, don't use it. Allow abandoned leases to be matched
+ here - if the client is requesting it, there's a decent chance
+ that it's because the lease database got trashed and a client
+ that thought it had this lease answered an ARP or PING, causing the
+ lease to be abandoned. If so, this request probably came from
+ that client. */
+ if (ip_lease && (ip_lease -> subnet -> shared_network != share)) {
+ if (ours)
+ *ours = 1;
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("...but it was on the wrong shared network.");
+#endif
+ strcpy (dhcp_message, "requested address on bad subnet");
+ lease_dereference (&ip_lease, MDL);
+ }
+
+ /*
+ * If the requested address is in use (or potentially in use) by
+ * a different client, it can't be granted.
+ *
+ * This first conditional only detects if the lease is currently
+ * identified to a different client (client-id and/or chaddr
+ * mismatch). In this case we may not want to give the client the
+ * lease, if doing so may potentially be an addressing conflict.
+ */
+ if (ip_lease &&
+ (ip_lease -> uid ?
+ (!have_client_identifier ||
+ ip_lease -> uid_len != client_identifier.len ||
+ memcmp (ip_lease -> uid, client_identifier.data,
+ ip_lease -> uid_len)) :
+ (ip_lease -> hardware_addr.hbuf [0] != packet -> raw -> htype ||
+ ip_lease -> hardware_addr.hlen != packet -> raw -> hlen + 1 ||
+ memcmp (&ip_lease -> hardware_addr.hbuf [1],
+ packet -> raw -> chaddr,
+ (unsigned)(ip_lease -> hardware_addr.hlen - 1))))) {
+ /*
+ * A lease is unavailable for allocation to a new client if
+ * it is not in the FREE or BACKUP state. There may be
+ * leases that are in the expired state with a rewinding
+ * state that is free or backup, but these will be processed
+ * into the free or backup states by expiration processes, so
+ * checking for them here is superfluous.
+ */
+ if (ip_lease -> binding_state != FTS_FREE &&
+ ip_lease -> binding_state != FTS_BACKUP) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("rejecting lease for requested address.");
+#endif
+ /* If we're rejecting it because the peer has
+ it, don't set "ours", because we shouldn't NAK. */
+ if (ours && ip_lease -> binding_state != FTS_ACTIVE)
+ *ours = 0;
+ lease_dereference (&ip_lease, MDL);
+ }
+ }
+
+ /*
+ * If we got an ip_lease and a uid_lease or hw_lease, and ip_lease
+ * is/was not active, and is not ours to reallocate, forget about it.
+ */
+ if (ip_lease && (uid_lease || hw_lease) &&
+ ip_lease->binding_state != FTS_ACTIVE &&
+ ip_lease->rewind_binding_state != FTS_ACTIVE &&
+#if defined(FAILOVER_PROTOCOL)
+ !lease_mine_to_reallocate(ip_lease) &&
+#endif
+ packet->packet_type == DHCPDISCOVER) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info("ip lease not active or not ours to offer.");
+#endif
+ lease_dereference(&ip_lease, MDL);
+ }
+
+ /* If for some reason the client has more than one lease
+ on the subnet that matches its uid, pick the one that
+ it asked for and (if we can) free the other. */
+ if (ip_lease && ip_lease->binding_state == FTS_ACTIVE &&
+ ip_lease->uid && ip_lease != uid_lease) {
+ if (have_client_identifier &&
+ (ip_lease -> uid_len == client_identifier.len) &&
+ !memcmp (client_identifier.data,
+ ip_lease -> uid, ip_lease -> uid_len)) {
+ if (uid_lease) {
+ if (uid_lease->binding_state == FTS_ACTIVE) {
+ log_error ("client %s has duplicate%s on %s",
+ (print_hw_addr
+ (packet -> raw -> htype,
+ packet -> raw -> hlen,
+ packet -> raw -> chaddr)),
+ " leases",
+ (ip_lease -> subnet ->
+ shared_network -> name));
+
+ /* If the client is REQUESTing the lease,
+ it shouldn't still be using the old
+ one, so we can free it for allocation. */
+ if (uid_lease &&
+ uid_lease->binding_state == FTS_ACTIVE &&
+ !packet -> raw -> ciaddr.s_addr &&
+ (share ==
+ uid_lease -> subnet -> shared_network) &&
+ packet -> packet_type == DHCPREQUEST)
+ release_lease (uid_lease, packet);
+ }
+ lease_dereference (&uid_lease, MDL);
+ lease_reference (&uid_lease, ip_lease, MDL);
+ }
+ }
+
+ /* If we get to here and fixed_lease is not null, that means
+ that there are both a dynamic lease and a fixed-address
+ declaration for the same IP address. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease) {
+ lease_dereference (&fixed_lease, MDL);
+ db_conflict:
+ log_error ("Dynamic and static leases present for %s.",
+ piaddr (cip));
+ log_error ("Remove host declaration %s or remove %s",
+ (fixed_lease && fixed_lease -> host
+ ? (fixed_lease -> host -> name
+ ? fixed_lease -> host -> name
+ : piaddr (cip))
+ : piaddr (cip)),
+ piaddr (cip));
+ log_error ("from the dynamic address pool for %s",
+ ip_lease -> subnet -> shared_network -> name
+ );
+ if (fixed_lease)
+ lease_dereference (&ip_lease, MDL);
+ strcpy (dhcp_message,
+ "database conflict - call for help!");
+ }
+
+ if (ip_lease && ip_lease != uid_lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("requested address not available.");
+#endif
+ lease_dereference (&ip_lease, MDL);
+ }
+ }
+
+ /* If we get to here with both fixed_lease and ip_lease not
+ null, then we have a configuration file bug. */
+ if (packet -> packet_type == DHCPREQUEST && fixed_lease && ip_lease)
+ goto db_conflict;
+
+ /* Toss extra pointers to the same lease... */
+ if (hw_lease && hw_lease == uid_lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("hardware lease and uid lease are identical.");
+#endif
+ lease_dereference (&hw_lease, MDL);
+ }
+ if (ip_lease && ip_lease == hw_lease) {
+ lease_dereference (&hw_lease, MDL);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("hardware lease and ip lease are identical.");
+#endif
+ }
+ if (ip_lease && ip_lease == uid_lease) {
+ lease_dereference (&uid_lease, MDL);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("uid lease and ip lease are identical.");
+#endif
+ }
+
+ /* Make sure the client is permitted to use the requested lease. */
+ if (ip_lease &&
+ ((ip_lease -> pool -> prohibit_list &&
+ permitted (packet, ip_lease -> pool -> prohibit_list)) ||
+ (ip_lease -> pool -> permit_list &&
+ !permitted (packet, ip_lease -> pool -> permit_list)))) {
+ if (!packet->raw->ciaddr.s_addr &&
+ (ip_lease->binding_state == FTS_ACTIVE))
+ release_lease (ip_lease, packet);
+
+ lease_dereference (&ip_lease, MDL);
+ }
+
+ if (uid_lease &&
+ ((uid_lease -> pool -> prohibit_list &&
+ permitted (packet, uid_lease -> pool -> prohibit_list)) ||
+ (uid_lease -> pool -> permit_list &&
+ !permitted (packet, uid_lease -> pool -> permit_list)))) {
+ if (!packet -> raw -> ciaddr.s_addr)
+ release_lease (uid_lease, packet);
+ lease_dereference (&uid_lease, MDL);
+ }
+
+ if (hw_lease &&
+ ((hw_lease -> pool -> prohibit_list &&
+ permitted (packet, hw_lease -> pool -> prohibit_list)) ||
+ (hw_lease -> pool -> permit_list &&
+ !permitted (packet, hw_lease -> pool -> permit_list)))) {
+ if (!packet -> raw -> ciaddr.s_addr)
+ release_lease (hw_lease, packet);
+ lease_dereference (&hw_lease, MDL);
+ }
+
+ /* If we've already eliminated the lease, it wasn't there to
+ begin with. If we have come up with a matching lease,
+ set the message to bad network in case we have to throw it out. */
+ if (!ip_lease) {
+ strcpy (dhcp_message, "requested address not available");
+ }
+
+ /* If this is a DHCPREQUEST, make sure the lease we're going to return
+ matches the requested IP address. If it doesn't, don't return a
+ lease at all. */
+ if (packet -> packet_type == DHCPREQUEST &&
+ !ip_lease && !fixed_lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("no applicable lease found for DHCPREQUEST.");
+#endif
+ goto out;
+ }
+
+ /* At this point, if fixed_lease is nonzero, we can assign it to
+ this client. */
+ if (fixed_lease) {
+ lease_reference (&lease, fixed_lease, MDL);
+ lease_dereference (&fixed_lease, MDL);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("choosing fixed address.");
+#endif
+ }
+
+ /* If we got a lease that matched the ip address and don't have
+ a better offer, use that; otherwise, release it. */
+ if (ip_lease) {
+ if (lease) {
+ if (!packet -> raw -> ciaddr.s_addr)
+ release_lease (ip_lease, packet);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not choosing requested address (!).");
+#endif
+ } else {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("choosing lease on requested address.");
+#endif
+ lease_reference (&lease, ip_lease, MDL);
+ if (lease -> host)
+ host_dereference (&lease -> host, MDL);
+ }
+ lease_dereference (&ip_lease, MDL);
+ }
+
+ /* If we got a lease that matched the client identifier, we may want
+ to use it, but if we already have a lease we like, we must free
+ the lease that matched the client identifier. */
+ if (uid_lease) {
+ if (lease) {
+ log_error("uid lease %s for client %s is duplicate "
+ "on %s",
+ piaddr(uid_lease->ip_addr),
+ print_hw_addr(packet->raw->htype,
+ packet->raw->hlen,
+ packet->raw->chaddr),
+ uid_lease->subnet->shared_network->name);
+
+ if (!packet -> raw -> ciaddr.s_addr &&
+ packet -> packet_type == DHCPREQUEST &&
+ uid_lease -> binding_state == FTS_ACTIVE)
+ release_lease(uid_lease, packet);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not choosing uid lease.");
+#endif
+ } else {
+ lease_reference (&lease, uid_lease, MDL);
+ if (lease -> host)
+ host_dereference (&lease -> host, MDL);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("choosing uid lease.");
+#endif
+ }
+ lease_dereference (&uid_lease, MDL);
+ }
+
+ /* The lease that matched the hardware address is treated likewise. */
+ if (hw_lease) {
+ if (lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not choosing hardware lease.");
+#endif
+ } else {
+ /* We're a little lax here - if the client didn't
+ send a client identifier and it's a bootp client,
+ but the lease has a client identifier, we still
+ let the client have a lease. */
+ if (!hw_lease -> uid_len ||
+ (have_client_identifier
+ ? (hw_lease -> uid_len ==
+ client_identifier.len &&
+ !memcmp (hw_lease -> uid,
+ client_identifier.data,
+ client_identifier.len))
+ : packet -> packet_type == 0)) {
+ lease_reference (&lease, hw_lease, MDL);
+ if (lease -> host)
+ host_dereference (&lease -> host, MDL);
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("choosing hardware lease.");
+#endif
+ } else {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("not choosing hardware lease: %s.",
+ "uid mismatch");
+#endif
+ }
+ }
+ lease_dereference (&hw_lease, MDL);
+ }
+
+ /*
+ * If we found a host_decl but no matching address, try to
+ * find a host_decl that has no address, and if there is one,
+ * hang it off the lease so that we can use the supplied
+ * options.
+ */
+ if (lease && host && !lease->host) {
+ struct host_decl *p = NULL;
+ struct host_decl *n = NULL;
+
+ host_reference(&p, host, MDL);
+ while (p != NULL) {
+ if (!p->fixed_addr) {
+ /*
+ * If the lease is currently active, then it
+ * must be allocated to the present client.
+ * We store a reference to the host record on
+ * the lease to save a lookup later (in
+ * ack_lease()). We mustn't refer to the host
+ * record on non-active leases because the
+ * client may be denied later.
+ *
+ * XXX: Not having this reference (such as in
+ * DHCPDISCOVER/INIT) means ack_lease will have
+ * to perform this lookup a second time. This
+ * hopefully isn't a problem as DHCPREQUEST is
+ * more common than DHCPDISCOVER.
+ */
+ if (lease->binding_state == FTS_ACTIVE)
+ host_reference(&lease->host, p, MDL);
+
+ host_dereference(&p, MDL);
+ break;
+ }
+ if (p->n_ipaddr != NULL)
+ host_reference(&n, p->n_ipaddr, MDL);
+ host_dereference(&p, MDL);
+ if (n != NULL) {
+ host_reference(&p, n, MDL);
+ host_dereference(&n, MDL);
+ }
+ }
+ }
+
+ /* If we find an abandoned lease, but it's the one the client
+ requested, we assume that previous bugginess on the part
+ of the client, or a server database loss, caused the lease to
+ be abandoned, so we reclaim it and let the client have it. */
+ if (lease &&
+ (lease -> binding_state == FTS_ABANDONED) &&
+ lease == ip_lease &&
+ packet -> packet_type == DHCPREQUEST) {
+ log_error ("Reclaiming REQUESTed abandoned IP address %s.",
+ piaddr (lease -> ip_addr));
+ } else if (lease && (lease -> binding_state == FTS_ABANDONED)) {
+ /* Otherwise, if it's not the one the client requested, we do not
+ return it - instead, we claim it's ours, causing a DHCPNAK to be
+ sent if this lookup is for a DHCPREQUEST, and force the client
+ to go back through the allocation process. */
+ if (ours)
+ *ours = 1;
+ lease_dereference (&lease, MDL);
+ }
+
+ out:
+ if (have_client_identifier)
+ data_string_forget (&client_identifier, MDL);
+
+ if (fixed_lease)
+ lease_dereference (&fixed_lease, MDL);
+ if (hw_lease)
+ lease_dereference (&hw_lease, MDL);
+ if (uid_lease)
+ lease_dereference (&uid_lease, MDL);
+ if (ip_lease)
+ lease_dereference (&ip_lease, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+
+ if (lease) {
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("Returning lease: %s.",
+ piaddr (lease -> ip_addr));
+#endif
+ lease_reference (lp, lease, file, line);
+ lease_dereference (&lease, MDL);
+ return 1;
+ }
+#if defined (DEBUG_FIND_LEASE)
+ log_info ("Not returning a lease.");
+#endif
+ return 0;
+}
+
+/* Search the provided host_decl structure list for an address that's on
+ the specified shared network. If one is found, mock up and return a
+ lease structure for it; otherwise return the null pointer. */
+
+int mockup_lease (struct lease **lp, struct packet *packet,
+ struct shared_network *share, struct host_decl *hp)
+{
+ struct lease *lease = (struct lease *)0;
+ struct host_decl *rhp = (struct host_decl *)0;
+
+ if (lease_allocate (&lease, MDL) != ISC_R_SUCCESS)
+ return 0;
+ if (host_reference (&rhp, hp, MDL) != ISC_R_SUCCESS) {
+ lease_dereference (&lease, MDL);
+ return 0;
+ }
+ if (!find_host_for_network (&lease -> subnet,
+ &rhp, &lease -> ip_addr, share)) {
+ lease_dereference (&lease, MDL);
+ host_dereference (&rhp, MDL);
+ return 0;
+ }
+ host_reference (&lease -> host, rhp, MDL);
+ if (rhp -> client_identifier.len > sizeof lease -> uid_buf)
+ lease -> uid = dmalloc (rhp -> client_identifier.len, MDL);
+ else
+ lease -> uid = lease -> uid_buf;
+ if (!lease -> uid) {
+ lease_dereference (&lease, MDL);
+ host_dereference (&rhp, MDL);
+ return 0;
+ }
+ memcpy (lease -> uid, rhp -> client_identifier.data,
+ rhp -> client_identifier.len);
+ lease -> uid_len = rhp -> client_identifier.len;
+ lease -> hardware_addr = rhp -> interface;
+ lease -> starts = lease -> cltt = lease -> ends = MIN_TIME;
+ lease -> flags = STATIC_LEASE;
+ lease -> binding_state = FTS_FREE;
+
+ lease_reference (lp, lease, MDL);
+
+ lease_dereference (&lease, MDL);
+ host_dereference (&rhp, MDL);
+ return 1;
+}
+
+/* Look through all the pools in a list starting with the specified pool
+ for a free lease. We try to find a virgin lease if we can. If we
+ don't find a virgin lease, we try to find a non-virgin lease that's
+ free. If we can't find one of those, we try to reclaim an abandoned
+ lease. If all of these possibilities fail to pan out, we don't return
+ a lease at all. */
+
+int allocate_lease (struct lease **lp, struct packet *packet,
+ struct pool *pool, int *peer_has_leases)
+{
+ struct lease *lease = (struct lease *)0;
+ struct lease *candl = (struct lease *)0;
+
+ for (; pool ; pool = pool -> next) {
+ if ((pool -> prohibit_list &&
+ permitted (packet, pool -> prohibit_list)) ||
+ (pool -> permit_list &&
+ !permitted (packet, pool -> permit_list)))
+ continue;
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Peer_has_leases just says that we found at least one
+ free lease. If no free lease is returned, the caller
+ can deduce that this means the peer is hogging all the
+ free leases, so we can print a better error message. */
+ /* XXX Do we need code here to ignore PEER_IS_OWNER and
+ * XXX just check tstp if we're in, e.g., PARTNER_DOWN?
+ * XXX Where do we deal with CONFLICT_DETECTED, et al? */
+ /* XXX This should be handled by the lease binding "state
+ * XXX machine" - that is, when we get here, if a lease
+ * XXX could be allocated, it will have the correct
+ * XXX binding state so that the following code will
+ * XXX result in its being allocated. */
+ /* Skip to the most expired lease in the pool that is not
+ * owned by a failover peer. */
+ if (pool->failover_peer != NULL) {
+ if (pool->failover_peer->i_am == primary) {
+ candl = pool->free;
+
+ /*
+ * In normal operation, we never want to touch
+ * the peer's leases. In partner-down
+ * operation, we need to be able to pick up
+ * the peer's leases after STOS+MCLT.
+ */
+ if (pool->backup != NULL) {
+ if (((candl == NULL) ||
+ (candl->ends >
+ pool->backup->ends)) &&
+ lease_mine_to_reallocate(
+ pool->backup)) {
+ candl = pool->backup;
+ } else {
+ *peer_has_leases = 1;
+ }
+ }
+ } else {
+ candl = pool->backup;
+
+ if (pool->free != NULL) {
+ if (((candl == NULL) ||
+ (candl->ends >
+ pool->free->ends)) &&
+ lease_mine_to_reallocate(
+ pool->free)) {
+ candl = pool->free;
+ } else {
+ *peer_has_leases = 1;
+ }
+ }
+ }
+
+ /* Try abandoned leases as a last resort. */
+ if ((candl == NULL) &&
+ (pool->abandoned != NULL) &&
+ lease_mine_to_reallocate(pool->abandoned))
+ candl = pool->abandoned;
+ } else
+#endif
+ {
+ if (pool -> free)
+ candl = pool -> free;
+ else
+ candl = pool -> abandoned;
+ }
+
+ /*
+ * XXX: This may not match with documented expectation.
+ * It's expected that when we OFFER a lease, we set its
+ * ends time forward 2 minutes so that it gets sorted to
+ * the end of its free list (avoiding a similar allocation
+ * to another client). It is not expected that we issue a
+ * "no free leases" error when the last lease has been
+ * offered, but it's not exactly broken either.
+ */
+ if (!candl || (candl -> ends > cur_time))
+ continue;
+
+ if (!lease) {
+ lease = candl;
+ continue;
+ }
+
+ /*
+ * There are tiers of lease state preference, listed here in
+ * reverse order (least to most preferential):
+ *
+ * ABANDONED
+ * FREE/BACKUP
+ *
+ * If the selected lease and candidate are both of the same
+ * state, select the oldest (longest ago) expiration time
+ * between the two. If the candidate lease is of a higher
+ * preferred grade over the selected lease, use it.
+ */
+ if ((lease -> binding_state == FTS_ABANDONED) &&
+ ((candl -> binding_state != FTS_ABANDONED) ||
+ (candl -> ends < lease -> ends))) {
+ lease = candl;
+ continue;
+ } else if (candl -> binding_state == FTS_ABANDONED)
+ continue;
+
+ if ((lease -> uid_len || lease -> hardware_addr.hlen) &&
+ ((!candl -> uid_len && !candl -> hardware_addr.hlen) ||
+ (candl -> ends < lease -> ends))) {
+ lease = candl;
+ continue;
+ } else if (candl -> uid_len || candl -> hardware_addr.hlen)
+ continue;
+
+ if (candl -> ends < lease -> ends)
+ lease = candl;
+ }
+
+ if (lease != NULL) {
+ if (lease->binding_state == FTS_ABANDONED)
+ log_error("Reclaiming abandoned lease %s.",
+ piaddr(lease->ip_addr));
+
+ /*
+ * XXX: For reliability, we go ahead and remove the host
+ * record and try to move on. For correctness, if there
+ * are any other stale host vectors, we want to find them.
+ */
+ if (lease->host != NULL) {
+ log_debug("soft impossible condition (%s:%d): stale "
+ "host \"%s\" found on lease %s", MDL,
+ lease->host->name,
+ piaddr(lease->ip_addr));
+ host_dereference(&lease->host, MDL);
+ }
+
+ lease_reference (lp, lease, MDL);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Determine whether or not a permit exists on a particular permit list
+ that matches the specified packet, returning nonzero if so, zero if
+ not. */
+
+int permitted (packet, permit_list)
+ struct packet *packet;
+ struct permit *permit_list;
+{
+ struct permit *p;
+ int i;
+
+ for (p = permit_list; p; p = p -> next) {
+ switch (p -> type) {
+ case permit_unknown_clients:
+ if (!packet -> known)
+ return 1;
+ break;
+
+ case permit_known_clients:
+ if (packet -> known)
+ return 1;
+ break;
+
+ case permit_authenticated_clients:
+ if (packet -> authenticated)
+ return 1;
+ break;
+
+ case permit_unauthenticated_clients:
+ if (!packet -> authenticated)
+ return 1;
+ break;
+
+ case permit_all_clients:
+ return 1;
+
+ case permit_dynamic_bootp_clients:
+ if (!packet -> options_valid ||
+ !packet -> packet_type)
+ return 1;
+ break;
+
+ case permit_class:
+ for (i = 0; i < packet -> class_count; i++) {
+ if (p -> class == packet -> classes [i])
+ return 1;
+ if (packet -> classes [i] &&
+ packet -> classes [i] -> superclass &&
+ (packet -> classes [i] -> superclass ==
+ p -> class))
+ return 1;
+ }
+ break;
+
+ case permit_after:
+ if (cur_time > p->after)
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+int locate_network (packet)
+ struct packet *packet;
+{
+ struct iaddr ia;
+ struct data_string data;
+ struct subnet *subnet = (struct subnet *)0;
+ struct option_cache *oc;
+
+ /* See if there's a Relay Agent Link Selection Option, or a
+ * Subnet Selection Option. The Link-Select and Subnet-Select
+ * are formatted and used precisely the same, but we must prefer
+ * the link-select over the subnet-select.
+ */
+ if ((oc = lookup_option(&agent_universe, packet->options,
+ RAI_LINK_SELECT)) == NULL)
+ oc = lookup_option(&dhcp_universe, packet->options,
+ DHO_SUBNET_SELECTION);
+
+ /* If there's no SSO and no giaddr, then use the shared_network
+ from the interface, if there is one. If not, fail. */
+ if (!oc && !packet -> raw -> giaddr.s_addr) {
+ if (packet -> interface -> shared_network) {
+ shared_network_reference
+ (&packet -> shared_network,
+ packet -> interface -> shared_network, MDL);
+ return 1;
+ }
+ return 0;
+ }
+
+ /* If there's an option indicating link connection, and it's valid,
+ * use it to figure out the subnet. If it's not valid, fail.
+ */
+ if (oc) {
+ memset (&data, 0, sizeof data);
+ if (!evaluate_option_cache (&data, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ return 0;
+ }
+ if (data.len != 4) {
+ return 0;
+ }
+ ia.len = 4;
+ memcpy (ia.iabuf, data.data, 4);
+ data_string_forget (&data, MDL);
+ } else {
+ ia.len = 4;
+ memcpy (ia.iabuf, &packet -> raw -> giaddr, 4);
+ }
+
+ /* If we know the subnet on which the IP address lives, use it. */
+ if (find_subnet (&subnet, ia, MDL)) {
+ shared_network_reference (&packet -> shared_network,
+ subnet -> shared_network, MDL);
+ subnet_dereference (&subnet, MDL);
+ return 1;
+ }
+
+ /* Otherwise, fail. */
+ return 0;
+}
+
+/*
+ * Try to figure out the source address to send packets from.
+ *
+ * If the packet we received specified the server address, then we
+ * will use that.
+ *
+ * Otherwise, use the first address from the interface. If we do
+ * this, we also save this into the option cache as the server
+ * address.
+ */
+void
+get_server_source_address(struct in_addr *from,
+ struct option_state *options,
+ struct packet *packet) {
+ unsigned option_num;
+ struct option_cache *oc;
+ struct data_string d;
+ struct in_addr *a;
+
+ memset(&d, 0, sizeof(d));
+
+ option_num = DHO_DHCP_SERVER_IDENTIFIER;
+ oc = lookup_option(&dhcp_universe, options, option_num);
+ if (oc != NULL) {
+ if (evaluate_option_cache(&d, packet, NULL, NULL,
+ packet->options, options,
+ &global_scope, oc, MDL)) {
+ if (d.len == sizeof(*from)) {
+ memcpy(from, d.data, sizeof(*from));
+ data_string_forget(&d, MDL);
+ return;
+ }
+ data_string_forget(&d, MDL);
+ }
+ oc = NULL;
+ }
+
+ if (packet->interface->address_count > 0) {
+ if (option_cache_allocate(&oc, MDL)) {
+ a = &packet->interface->addresses[0];
+ if (make_const_data(&oc->expression,
+ (unsigned char *)a, sizeof(*a),
+ 0, 0, MDL)) {
+ option_code_hash_lookup(&oc->option,
+ dhcp_universe.code_hash,
+ &option_num, 0, MDL);
+ save_option(&dhcp_universe, options, oc);
+ }
+ option_cache_dereference(&oc, MDL);
+ }
+ *from = packet->interface->addresses[0];
+ } else {
+ memset(from, 0, sizeof(*from));
+ }
+}
+
+/*
+ * Look for the lowest numbered site code number and
+ * apply a log warning if it is less than 224. Do not
+ * permit site codes less than 128 (old code never did).
+ *
+ * Note that we could search option codes 224 down to 128
+ * on the hash table, but the table is (probably) smaller
+ * than that if it was declared as a standalone table with
+ * defaults. So we traverse the option code hash.
+ */
+static int
+find_min_site_code(struct universe *u)
+{
+ if (u->site_code_min)
+ return u->site_code_min;
+
+ /*
+ * Note that site_code_min has to be global as we can't pass an
+ * argument through hash_foreach(). The value 224 is taken from
+ * RFC 3942.
+ */
+ site_code_min = 224;
+ option_code_hash_foreach(u->code_hash, lowest_site_code);
+
+ if (site_code_min < 224) {
+ log_error("WARNING: site-local option codes less than 224 have "
+ "been deprecated by RFC3942. You have options "
+ "listed in site local space %s that number as low as "
+ "%d. Please investigate if these should be declared "
+ "as regular options rather than site-local options, "
+ "or migrated up past 224.",
+ u->name, site_code_min);
+ }
+
+ /*
+ * don't even bother logging, this is just silly, and never worked
+ * on any old version of software.
+ */
+ if (site_code_min < 128)
+ site_code_min = 128;
+
+ /*
+ * Cache the determined minimum site code on the universe structure.
+ * Note that due to the < 128 check above, a value of zero is
+ * impossible.
+ */
+ u->site_code_min = site_code_min;
+
+ return site_code_min;
+}
+
+static isc_result_t
+lowest_site_code(const void *key, unsigned len, void *object)
+{
+ struct option *option = object;
+
+ if (option->code < site_code_min)
+ site_code_min = option->code;
+
+ return ISC_R_SUCCESS;
+}
+
+static void
+maybe_return_agent_options(struct packet *packet, struct option_state *options)
+{
+ /* If there were agent options in the incoming packet, return
+ * them. Do not return the agent options if they were stashed
+ * on the lease. We do not check giaddr to detect the presence of
+ * a relay, as this excludes "l2" relay agents which have no giaddr
+ * to set.
+ *
+ * XXX: If the user configures options for the relay agent information
+ * (state->options->universes[agent_universe.index] is not NULL),
+ * we're still required to duplicate other values provided by the
+ * relay agent. So we need to merge the old values not configured
+ * by the user into the new state, not just give up.
+ */
+ if (!packet->agent_options_stashed &&
+ (packet->options != NULL) &&
+ packet->options->universe_count > agent_universe.index &&
+ packet->options->universes[agent_universe.index] != NULL &&
+ (options->universe_count <= agent_universe.index ||
+ options->universes[agent_universe.index] == NULL)) {
+ option_chain_head_reference
+ ((struct option_chain_head **)
+ &(options->universes[agent_universe.index]),
+ (struct option_chain_head *)
+ packet->options->universes[agent_universe.index], MDL);
+
+ if (options->universe_count <= agent_universe.index)
+ options->universe_count = agent_universe.index + 1;
+ }
+}
diff --git a/server/dhcpd.8 b/server/dhcpd.8
new file mode 100644
index 0000000..d00e758
--- /dev/null
+++ b/server/dhcpd.8
@@ -0,0 +1,805 @@
+.\" dhcpd.8
+.\"
+.\" Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.\" $Id: dhcpd.8,v 1.30.24.5 2011-05-20 14:33:28 tomasz Exp $
+.\"
+.TH dhcpd 8
+.SH NAME
+dhcpd - Dynamic Host Configuration Protocol Server
+.SH SYNOPSIS
+.B dhcpd
+[
+.B -p
+.I port
+]
+[
+.B -f
+]
+[
+.B -d
+]
+[
+.B -q
+]
+[
+.B -t
+|
+.B -T
+]
+[
+.B -4
+|
+.B -6
+]
+[
+.B -s
+.I server
+]
+[
+.B -cf
+.I config-file
+]
+[
+.B -lf
+.I lease-file
+]
+[
+.B -pf
+.I pid-file
+]
+[
+.B --no-pid
+]
+[
+.B -tf
+.I trace-output-file
+]
+[
+.B -play
+.I trace-playback-file
+]
+[
+.I if0
+[
+.I ...ifN
+]
+]
+
+.B dhcpd
+--version
+.SH DESCRIPTION
+The Internet Systems Consortium DHCP Server, dhcpd, implements the
+Dynamic Host Configuration Protocol (DHCP) and the Internet Bootstrap
+Protocol (BOOTP). DHCP allows hosts on a TCP/IP network to request
+and be assigned IP addresses, and also to discover information about
+the network to which they are attached. BOOTP provides similar
+functionality, with certain restrictions.
+.SH OPERATION
+.PP
+The DHCP protocol allows a host which is unknown to the network
+administrator to be automatically assigned a new IP address out of a
+pool of IP addresses for its network. In order for this to work, the
+network administrator allocates address pools in each subnet and
+enters them into the dhcpd.conf(5) file.
+.PP
+There are two versions of the DHCP protocol DHCPv4 and DHCPv6. At
+startup the server may be started for one or the other via the
+.B -4
+or
+.B -6
+arguments.
+.PP
+On startup, dhcpd reads the
+.IR dhcpd.conf
+file and stores a list of available addresses on each subnet in
+memory. When a client requests an address using the DHCP protocol,
+dhcpd allocates an address for it. Each client is assigned a lease,
+which expires after an amount of time chosen by the administrator (by
+default, one day). Before leases expire, the clients to which leases
+are assigned are expected to renew them in order to continue to use
+the addresses. Once a lease has expired, the client to which that
+lease was assigned is no longer permitted to use the leased IP
+address.
+.PP
+In order to keep track of leases across system reboots and server
+restarts, dhcpd keeps a list of leases it has assigned in the
+dhcpd.leases(5) file. Before dhcpd grants a lease to a host, it
+records the lease in this file and makes sure that the contents of the
+file are flushed to disk. This ensures that even in the event of a
+system crash, dhcpd will not forget about a lease that it has
+assigned. On startup, after reading the dhcpd.conf file, dhcpd
+reads the dhcpd.leases file to refresh its memory about what leases
+have been assigned.
+.PP
+New leases are appended to the end of the dhcpd.leases
+file. In order to prevent the file from becoming arbitrarily large,
+from time to time dhcpd creates a new dhcpd.leases file from its
+in-core lease database. Once this file has been written to disk, the
+old file is renamed
+.IR dhcpd.leases~ ,
+and the new file is renamed dhcpd.leases. If the system crashes in
+the middle of this process, whichever dhcpd.leases file remains will
+contain all the lease information, so there is no need for a special
+crash recovery process.
+.PP
+BOOTP support is also provided by this server. Unlike DHCP, the BOOTP
+protocol does not provide a protocol for recovering
+dynamically-assigned addresses once they are no longer needed. It is
+still possible to dynamically assign addresses to BOOTP clients, but
+some administrative process for reclaiming addresses is required. By
+default, leases are granted to BOOTP clients in perpetuity, although
+the network administrator may set an earlier cutoff date or a shorter
+lease length for BOOTP leases if that makes sense.
+.PP
+BOOTP clients may also be served in the old standard way, which is to
+simply provide a declaration in the dhcpd.conf file for each
+BOOTP client, permanently assigning an address to each client.
+.PP
+Whenever changes are made to the dhcpd.conf file, dhcpd must be
+restarted. To restart dhcpd, send a SIGTERM (signal 15) to the
+process ID contained in
+.IR RUNDIR/dhcpd.pid ,
+and then re-invoke dhcpd. Because the DHCP server database is not as
+lightweight as a BOOTP database, dhcpd does not automatically restart
+itself when it sees a change to the dhcpd.conf file.
+.PP
+Note: We get a lot of complaints about this. We realize that it would
+be nice if one could send a SIGHUP to the server and have it reload
+the database. This is not technically impossible, but it would
+require a great deal of work, our resources are extremely limited, and
+they can be better spent elsewhere. So please don't complain about
+this on the mailing list unless you're prepared to fund a project to
+implement this feature, or prepared to do it yourself.
+.SH COMMAND LINE
+.PP
+The names of the network interfaces on which dhcpd should listen for
+broadcasts may be specified on the command line. This should be done
+on systems where dhcpd is unable to identify non-broadcast interfaces,
+but should not be required on other systems. If no interface names
+are specified on the command line dhcpd will identify all network
+interfaces which are up, eliminating non-broadcast interfaces if
+possible, and listen for DHCP broadcasts on each interface.
+.PP
+.SH COMMAND LINE OPTIONS
+.TP
+.BI \-4
+Run as a DHCP server. This is the default and cannot be combined with
+\fB\-6\fR.
+.TP
+.BI \-6
+Run as a DHCPv6 server. This cannot be combined with \fB\-4\fR.
+.TP
+.BI \-p \ port
+The udp port number on which
+.B dhcpd
+should listen. If unspecified
+.B dhcpd
+uses the default port of 67. This is mostly useful for debugging
+purposes.
+.TP
+.BI \-s \ address
+Specify an address or host name to which
+.B dhcpd
+should send replies rather than the broadcast address (255.255.255.255).
+This option is only supported in IPv4.
+.TP
+.BI \-f
+Force
+.B dhcpd
+to run as a foreground process instead of as a daemon in the background.
+This is useful when running
+.B dhcpd
+under a debugger, or when running it
+out of inittab on System V systems.
+.TP
+.BI \-d
+Send log messages to the standard error descriptor.
+This can be useful for debugging, and also at sites where a
+complete log of all dhcp activity must be kept but syslogd is not
+reliable or otherwise cannot be used. Normally,
+.B dhcpd
+will log all
+output using the \fBsyslog(3)\fR function with the log facility set to
+LOG_DAEMON. Note that \fB\-d\fR implies \fB\-f\fR (the daemon will
+not fork itself into the background).
+.TP
+.BI \-q
+Be quiet at startup. This suppresses the printing of the entire
+copyright message during startup. This might be desirable when
+starting
+.B dhcpd
+from a system startup script (e.g., /etc/rc).
+.TP
+.BI \-t
+Test the configuration file. The server tests the configuration file
+for correct syntax, but will not attempt to perform any network
+operations. This can be used to test a new configuration file
+automatically before installing it.
+.TP
+.BI \-T
+Test the lease file. The server tests the lease file
+for correct syntax, but will not attempt to perform any network
+operations. This can be used to test a new leaes file
+automatically before installing it.
+.TP
+.BI \-tf \ tracefile
+Specify a file into which the entire startup state of the server and
+all the transactions it processes are logged. This can be
+useful in submitting bug reports - if you are getting a core dump
+every so often, you can start the server with the \fB-tf\fR option and
+then, when the server dumps core, the trace file will contain all the
+transactions that led up to it dumping core, so that the problem can
+be easily debugged with \fB-play\fR.
+.TP
+.BI \-play \ playfile
+Specify a file from which the entire startup state of the server and
+all the transactions it processed are read. The \fB-play\fR option
+must be specified with an alternate lease file,
+using the \fB-lf\fR switch, so that the DHCP server doesn't wipe out
+your existing lease file with its test data. The DHCP server will
+refuse to operate in playback mode unless you specify an alternate
+lease file.
+.TP
+.BI --version
+Print version number and exit.
+.PP
+.I Modifying default file locations:
+The following options can be used to modify the locations
+.B dhcpd
+uses for it's files. Because of the importance of using the same
+lease database at all times when running dhcpd in production, these
+options should be used \fBonly\fR for testing lease files or database
+files in a non-production environment.
+.TP
+.BI \-cf \ config-file
+Path to alternate configuration file.
+.TP
+.BI \-lf \ lease-file
+Path to alternate lease file.
+.TP
+.BI \-pf \ pid-file
+Path to alternate pid file.
+.TP
+.BI \--no-pid
+Option to disable writing pid files. By default the program
+will write a pid file. If the program is invoked with this
+option it will not check for an existing server process.
+.PP
+.SH CONFIGURATION
+The syntax of the dhcpd.conf(5) file is discussed separately. This
+section should be used as an overview of the configuration process,
+and the dhcpd.conf(5) documentation should be consulted for detailed
+reference information.
+.PP
+.SH Subnets
+dhcpd needs to know the subnet numbers and netmasks of all subnets for
+which it will be providing service. In addition, in order to
+dynamically allocate addresses, it must be assigned one or more ranges
+of addresses on each subnet which it can in turn assign to client
+hosts as they boot. Thus, a very simple configuration providing DHCP
+support might look like this:
+.nf
+.sp 1
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+ }
+.fi
+.PP
+Multiple address ranges may be specified like this:
+.nf
+.sp 1
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ range 239.252.197.113 239.252.197.250;
+ }
+.fi
+.PP
+If a subnet will only be provided with BOOTP service and no dynamic
+address assignment, the range clause can be left out entirely, but the
+subnet statement must appear.
+.PP
+.SH Lease Lengths
+DHCP leases can be assigned almost any length from zero seconds to
+infinity. What lease length makes sense for any given subnet, or for
+any given installation, will vary depending on the kinds of hosts
+being served.
+.PP
+For example, in an office environment where systems are added from
+time to time and removed from time to time, but move relatively
+infrequently, it might make sense to allow lease times of a month or
+more. In a final test environment on a manufacturing floor, it may
+make more sense to assign a maximum lease length of 30 minutes -
+enough time to go through a simple test procedure on a network
+appliance before packaging it up for delivery.
+.PP
+It is possible to specify two lease lengths: the default length that
+will be assigned if a client doesn't ask for any particular lease
+length, and a maximum lease length. These are specified as clauses
+to the subnet command:
+.nf
+.sp 1
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.107;
+ default-lease-time 600;
+ max-lease-time 7200;
+ }
+.fi
+.PP
+This particular subnet declaration specifies a default lease time of
+600 seconds (ten minutes), and a maximum lease time of 7200 seconds
+(two hours). Other common values would be 86400 (one day), 604800
+(one week) and 2592000 (30 days).
+.PP
+Each subnet need not have the same lease\(emin the case of an office
+environment and a manufacturing environment served by the same DHCP
+server, it might make sense to have widely disparate values for
+default and maximum lease times on each subnet.
+.SH BOOTP Support
+Each BOOTP client must be explicitly declared in the dhcpd.conf
+file. A very basic client declaration will specify the client
+network interface's hardware address and the IP address to assign to
+that client. If the client needs to be able to load a boot file from
+the server, that file's name must be specified. A simple bootp
+client declaration might look like this:
+.nf
+.sp 1
+ host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+ }
+.fi
+.SH Options
+DHCP (and also BOOTP with Vendor Extensions) provide a mechanism
+whereby the server can provide the client with information about how
+to configure its network interface (e.g., subnet mask), and also how
+the client can access various network services (e.g., DNS, IP routers,
+and so on).
+.PP
+These options can be specified on a per-subnet basis, and, for BOOTP
+clients, also on a per-client basis. In the event that a BOOTP
+client declaration specifies options that are also specified in its
+subnet declaration, the options specified in the client declaration
+take precedence. A reasonably complete DHCP configuration might
+look something like this:
+.nf
+.sp 1
+ subnet 239.252.197.0 netmask 255.255.255.0 {
+ range 239.252.197.10 239.252.197.250;
+ default-lease-time 600 max-lease-time 7200;
+ option subnet-mask 255.255.255.0;
+ option broadcast-address 239.252.197.255;
+ option routers 239.252.197.1;
+ option domain-name-servers 239.252.197.2, 239.252.197.3;
+ option domain-name "isc.org";
+ }
+.fi
+.PP
+A bootp host on that subnet that needs to be in a different domain and
+use a different name server might be declared as follows:
+.nf
+.sp 1
+ host haagen {
+ hardware ethernet 08:00:2b:4c:59:23;
+ fixed-address 239.252.197.9;
+ filename "/tftpboot/haagen.boot";
+ option domain-name-servers 192.5.5.1;
+ option domain-name "vix.com";
+ }
+.fi
+.PP
+A more complete description of the dhcpd.conf file syntax is provided
+in dhcpd.conf(5).
+.SH OMAPI
+The DHCP server provides the capability to modify some of its
+configuration while it is running, without stopping it, modifying its
+database files, and restarting it. This capability is currently
+provided using OMAPI - an API for manipulating remote objects. OMAPI
+clients connect to the server using TCP/IP, authenticate, and can then
+examine the server's current status and make changes to it.
+.PP
+Rather than implementing the underlying OMAPI protocol directly, user
+programs should use the dhcpctl API or OMAPI itself. Dhcpctl is a
+wrapper that handles some of the housekeeping chores that OMAPI does
+not do automatically. Dhcpctl and OMAPI are documented in \fBdhcpctl(3)\fR
+and \fBomapi(3)\fR.
+.PP
+OMAPI exports objects, which can then be examined and modified. The
+DHCP server exports the following objects: lease, host,
+failover-state and group. Each object has a number of methods that
+are provided: lookup, create, and destroy. In addition, it is
+possible to look at attributes that are stored on objects, and in some
+cases to modify those attributes.
+.SH THE LEASE OBJECT
+Leases can't currently be created or destroyed, but they can be looked
+up to examine and modify their state.
+.PP
+Leases have the following attributes:
+.PP
+.B state \fIinteger\fR lookup, examine
+.RS 0.5i
+.nf
+1 = free
+2 = active
+3 = expired
+4 = released
+5 = abandoned
+6 = reset
+7 = backup
+8 = reserved
+9 = bootp
+.fi
+.RE
+.PP
+.B ip-address \fIdata\fR lookup, examine
+.RS 0.5i
+The IP address of the lease.
+.RE
+.PP
+.B dhcp-client-identifier \fIdata\fR lookup, examine, update
+.RS 0.5i
+The
+client identifier that the client used when it acquired the lease.
+Not all clients send client identifiers, so this may be empty.
+.RE
+.PP
+.B client-hostname \fIdata\fR examine, update
+.RS 0.5i
+The value the client sent in the host-name option.
+.RE
+.PP
+.B host \fIhandle\fR examine
+.RS 0.5i
+the host declaration associated with this lease, if any.
+.RE
+.PP
+.B subnet \fIhandle\fR examine
+.RS 0.5i
+the subnet object associated with this lease (the subnet object is not
+currently supported).
+.RE
+.PP
+.B pool \fIhandle\fR examine
+.RS 0.5i
+the pool object associated with this lease (the pool object is not
+currently supported).
+.RE
+.PP
+.B billing-class \fIhandle\fR examine
+.RS 0.5i
+the handle to the class to which this lease is currently billed, if
+any (the class object is not currently supported).
+.RE
+.PP
+.B hardware-address \fIdata\fR examine, update
+.RS 0.5i
+the hardware address (chaddr) field sent by the client when it
+acquired its lease.
+.RE
+.PP
+.B hardware-type \fIinteger\fR examine, update
+.RS 0.5i
+the type of the network interface that the client reported when it
+acquired its lease.
+.RE
+.PP
+.B ends \fItime\fR examine
+.RS 0.5i
+the time when the lease's current state ends, as understood by the
+client.
+.RE
+.PP
+.B tstp \fItime\fR examine
+.RS 0.5i
+the time when the lease's current state ends, as understood by the
+server.
+.RE
+.B tsfp \fItime\fR examine
+.RS 0.5i
+the adjusted time when the lease's current state ends, as understood by
+the failover peer (if there is no failover peer, this value is
+undefined). Generally this value is only adjusted for expired, released,
+or reset leases while the server is operating in partner-down state, and
+otherwise is simply the value supplied by the peer.
+.RE
+.B atsfp \fItime\fR examine
+.RS 0.5i
+the actual tsfp value sent from the peer. This value is forgotten when a
+lease binding state change is made, to facilitate retransmission logic.
+.RE
+.PP
+.B cltt \fItime\fR examine
+.RS 0.5i
+The time of the last transaction with the client on this lease.
+.RE
+.SH THE HOST OBJECT
+Hosts can be created, destroyed, looked up, examined and modified.
+If a host declaration is created or deleted using OMAPI, that
+information will be recorded in the dhcpd.leases file. It is
+permissible to delete host declarations that are declared in the
+dhcpd.conf file.
+.PP
+Hosts have the following attributes:
+.PP
+.B name \fIdata\fR lookup, examine, modify
+.RS 0.5i
+the name of the host declaration. This name must be unique among all
+host declarations.
+.RE
+.PP
+.B group \fIhandle\fR examine, modify
+.RS 0.5i
+the named group associated with the host declaration, if there is one.
+.RE
+.PP
+.B hardware-address \fIdata\fR lookup, examine, modify
+.RS 0.5i
+the link-layer address that will be used to match the client, if any.
+Only valid if hardware-type is also present.
+.RE
+.PP
+.B hardware-type \fIinteger\fR lookup, examine, modify
+.RS 0.5i
+the type of the network interface that will be used to match the
+client, if any. Only valid if hardware-address is also present.
+.RE
+.PP
+.B dhcp-client-identifier \fIdata\fR lookup, examine, modify
+.RS 0.5i
+the dhcp-client-identifier option that will be used to match the
+client, if any.
+.RE
+.PP
+.B ip-address \fIdata\fR examine, modify
+.RS 0.5i
+a fixed IP address which is reserved for a DHCP client that matches
+this host declaration. The IP address will only be assigned to the
+client if it is valid for the network segment to which the client is
+connected.
+.RE
+.PP
+.B statements \fIdata\fR modify
+.RS 0.5i
+a list of statements in the format of the dhcpd.conf file that will be
+executed whenever a message from the client is being processed.
+.RE
+.PP
+.B known \fIinteger\fR examine, modify
+.RS 0.5i
+if nonzero, indicates that a client matching this host declaration
+will be treated as \fIknown\fR in pool permit lists. If zero, the
+client will not be treated as known.
+.RE
+.SH THE GROUP OBJECT
+Named groups can be created, destroyed, looked up, examined and
+modified. If a group declaration is created or deleted using OMAPI,
+that information will be recorded in the dhcpd.leases file. It is
+permissible to delete group declarations that are declared in the
+dhcpd.conf file.
+.PP
+Named groups currently can only be associated with
+hosts - this allows one set of statements to be efficiently attached
+to more than one host declaration.
+.PP
+Groups have the following attributes:
+.PP
+.B name \fIdata\fR
+.RS 0.5i
+the name of the group. All groups that are created using OMAPI must
+have names, and the names must be unique among all groups.
+.RE
+.PP
+.B statements \fIdata\fR
+.RS 0.5i
+a list of statements in the format of the dhcpd.conf file that will be
+executed whenever a message from a client whose host declaration
+references this group is processed.
+.RE
+.SH THE CONTROL OBJECT
+The control object allows you to shut the server down. If the server
+is doing failover with another peer, it will make a clean transition
+into the shutdown state and notify its peer, so that the peer can go
+into partner down, and then record the "recover" state in the lease
+file so that when the server is restarted, it will automatically
+resynchronize with its peer.
+.PP
+On shutdown the server will also attempt to cleanly shut down all
+OMAPI connections. If these connections do not go down cleanly after
+five seconds, they are shut down preemptively. It can take as much
+as 25 seconds from the beginning of the shutdown process to the time
+that the server actually exits.
+.PP
+To shut the server down, open its control object and set the state
+attribute to 2.
+.SH THE FAILOVER-STATE OBJECT
+The failover-state object is the object that tracks the state of the
+failover protocol as it is being managed for a given failover peer.
+The failover object has the following attributes (please see
+.B dhcpd.conf (5)
+for explanations about what these attributes mean):
+.PP
+.B name \fIdata\fR examine
+.RS 0.5i
+Indicates the name of the failover peer relationship, as described in
+the server's \fBdhcpd.conf\fR file.
+.RE
+.PP
+.B partner-address \fIdata\fR examine
+.RS 0.5i
+Indicates the failover partner's IP address.
+.RE
+.PP
+.B local-address \fIdata\fR examine
+.RS 0.5i
+Indicates the IP address that is being used by the DHCP server for
+this failover pair.
+.RE
+.PP
+.B partner-port \fIdata\fR examine
+.RS 0.5i
+Indicates the TCP port on which the failover partner is listening for
+failover protocol connections.
+.RE
+.PP
+.B local-port \fIdata\fR examine
+.RS 0.5i
+Indicates the TCP port on which the DHCP server is listening for
+failover protocol connections for this failover pair.
+.RE
+.PP
+.B max-outstanding-updates \fIinteger\fR examine
+.RS 0.5i
+Indicates the number of updates that can be outstanding and
+unacknowledged at any given time, in this failover relationship.
+.RE
+.PP
+.B mclt \fIinteger\fR examine
+.RS 0.5i
+Indicates the maximum client lead time in this failover relationship.
+.RE
+.PP
+.B load-balance-max-secs \fIinteger\fR examine
+.RS 0.5i
+Indicates the maximum value for the secs field in a client request
+before load balancing is bypassed.
+.RE
+.PP
+.B load-balance-hba \fIdata\fR examine
+.RS 0.5i
+Indicates the load balancing hash bucket array for this failover
+relationship.
+.RE
+.PP
+.B local-state \fIinteger\fR examine, modify
+.RS 0.5i
+Indicates the present state of the DHCP server in this failover
+relationship. Possible values for state are:
+.RE
+.RS 1i
+.PP
+.nf
+1 - startup
+2 - normal
+3 - communications interrupted
+4 - partner down
+5 - potential conflict
+6 - recover
+7 - paused
+8 - shutdown
+9 - recover done
+10 - resolution interrupted
+11 - conflict done
+254 - recover wait
+.fi
+.RE
+.PP
+.RS 0.5i
+(Note that some of the above values have changed since DHCP 3.0.x.)
+.RE
+.PP
+.RS 0.5i
+In general it is not a good idea to make changes to this state.
+However, in the case that the failover partner is known to be down, it
+can be useful to set the DHCP server's failover state to partner
+down. At this point the DHCP server will take over service of the
+failover partner's leases as soon as possible, and will give out
+normal leases, not leases that are restricted by MCLT. If you do put
+the DHCP server into the partner-down when the other DHCP server is
+not in the partner-down state, but is not reachable, IP address
+assignment conflicts are possible, even likely. Once a server has
+been put into partner-down mode, its failover partner must not be
+brought back online until communication is possible between the two
+servers.
+.RE
+.PP
+.B partner-state \fIinteger\fR examine
+.RS 0.5i
+Indicates the present state of the failover partner.
+.RE
+.PP
+.B local-stos \fIinteger\fR examine
+.RS 0.5i
+Indicates the time at which the DHCP server entered its present state
+in this failover relationship.
+.RE
+.PP
+.B partner-stos \fIinteger\fR examine
+.RS 0.5i
+Indicates the time at which the failover partner entered its present state.
+.RE
+.PP
+.B hierarchy \fIinteger\fR examine
+.RS 0.5i
+Indicates whether the DHCP server is primary (0) or secondary (1) in
+this failover relationship.
+.RE
+.PP
+.B last-packet-sent \fIinteger\fR examine
+.RS 0.5i
+Indicates the time at which the most recent failover packet was sent
+by this DHCP server to its failover partner.
+.RE
+.PP
+.B last-timestamp-received \fIinteger\fR examine
+.RS 0.5i
+Indicates the timestamp that was on the failover message most recently
+received from the failover partner.
+.RE
+.PP
+.B skew \fIinteger\fR examine
+.RS 0.5i
+Indicates the skew between the failover partner's clock and this DHCP
+server's clock
+.RE
+.PP
+.B max-response-delay \fIinteger\fR examine
+.RS 0.5i
+Indicates the time in seconds after which, if no message is received
+from the failover partner, the partner is assumed to be out of
+communication.
+.RE
+.PP
+.B cur-unacked-updates \fIinteger\fR examine
+.RS 0.5i
+Indicates the number of update messages that have been received from
+the failover partner but not yet processed.
+.RE
+.SH FILES
+.B ETCDIR/dhcpd.conf, DBDIR/dhcpd.leases, RUNDIR/dhcpd.pid,
+.B DBDIR/dhcpd.leases~.
+.SH SEE ALSO
+dhclient(8), dhcrelay(8), dhcpd.conf(5), dhcpd.leases(5)
+.SH AUTHOR
+.B dhcpd(8)
+was originally written by Ted Lemon under a contract with Vixie Labs.
+Funding for this project was provided by Internet Systems
+Consortium. Version 3 of the DHCP server was funded by Nominum, Inc.
+Information about Internet Systems Consortium is available at
+.B https://www.isc.org/\fR.
diff --git a/server/dhcpd.c b/server/dhcpd.c
new file mode 100644
index 0000000..448e164
--- /dev/null
+++ b/server/dhcpd.c
@@ -0,0 +1,1504 @@
+/* dhcpd.c
+
+ DHCP Server Daemon. */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+static const char copyright[] =
+"Copyright 2004-2011 Internet Systems Consortium.";
+static const char arr [] = "All rights reserved.";
+static const char message [] = "Internet Systems Consortium DHCP Server";
+static const char url [] =
+"For info, please visit https://www.isc.org/software/dhcp/";
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <syslog.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#if defined (PARANOIA)
+# include <sys/types.h>
+# include <unistd.h>
+# include <pwd.h>
+/* get around the ISC declaration of group */
+# define group real_group
+# include <grp.h>
+# undef group
+#endif /* PARANOIA */
+
+static void usage(void);
+
+struct iaddr server_identifier;
+int server_identifier_matched;
+
+#if defined (NSUPDATE)
+
+/* This stuff is always executed to figure the default values for certain
+ ddns variables. */
+
+char std_nsupdate [] = " \n\
+option server.ddns-hostname = \n\
+ pick (option fqdn.hostname, option host-name); \n\
+option server.ddns-domainname = config-option domain-name; \n\
+option server.ddns-rev-domainname = \"in-addr.arpa.\";";
+
+/* This is the old-style name service updater that is executed
+ whenever a lease is committed. It does not follow the DHCP-DNS
+ draft at all. */
+
+char old_nsupdate [] = " \n\
+on commit { \n\
+ if (not static and \n\
+ ((config-option server.ddns-updates = null) or \n\
+ (config-option server.ddns-updates != 0))) { \n\
+ set new-ddns-fwd-name = \n\
+ concat (pick (config-option server.ddns-hostname, \n\
+ option host-name), \".\", \n\
+ pick (config-option server.ddns-domainname, \n\
+ config-option domain-name)); \n\
+ if (defined (ddns-fwd-name) and ddns-fwd-name != new-ddns-fwd-name) { \n\
+ switch (ns-update (delete (IN, A, ddns-fwd-name, leased-address))) { \n\
+ case NOERROR: \n\
+ unset ddns-fwd-name; \n\
+ on expiry or release { \n\
+ } \n\
+ } \n\
+ } \n\
+ \n\
+ if (not defined (ddns-fwd-name)) { \n\
+ set ddns-fwd-name = new-ddns-fwd-name; \n\
+ if defined (ddns-fwd-name) { \n\
+ switch (ns-update (not exists (IN, A, ddns-fwd-name, null), \n\
+ add (IN, A, ddns-fwd-name, leased-address, \n\
+ lease-time / 2))) { \n\
+ default: \n\
+ unset ddns-fwd-name; \n\
+ break; \n\
+ \n\
+ case NOERROR: \n\
+ set ddns-rev-name = \n\
+ concat (binary-to-ascii (10, 8, \".\", \n\
+ reverse (1, \n\
+ leased-address)), \".\", \n\
+ pick (config-option server.ddns-rev-domainname, \n\
+ \"in-addr.arpa.\")); \n\
+ switch (ns-update (delete (IN, PTR, ddns-rev-name, null), \n\
+ add (IN, PTR, ddns-rev-name, ddns-fwd-name, \n\
+ lease-time / 2))) \n\
+ { \n\
+ default: \n\
+ unset ddns-rev-name; \n\
+ on release or expiry { \n\
+ switch (ns-update (delete (IN, A, ddns-fwd-name, \n\
+ leased-address))) { \n\
+ case NOERROR: \n\
+ unset ddns-fwd-name; \n\
+ break; \n\
+ } \n\
+ on release or expiry; \n\
+ } \n\
+ break; \n\
+ \n\
+ case NOERROR: \n\
+ on release or expiry { \n\
+ switch (ns-update (delete (IN, PTR, ddns-rev-name, null))) {\n\
+ case NOERROR: \n\
+ unset ddns-rev-name; \n\
+ break; \n\
+ } \n\
+ switch (ns-update (delete (IN, A, ddns-fwd-name, \n\
+ leased-address))) { \n\
+ case NOERROR: \n\
+ unset ddns-fwd-name; \n\
+ break; \n\
+ } \n\
+ on release or expiry; \n\
+ } \n\
+ } \n\
+ } \n\
+ } \n\
+ } \n\
+ unset new-ddns-fwd-name; \n\
+ } \n\
+}";
+
+#endif /* NSUPDATE */
+int ddns_update_style;
+
+const char *path_dhcpd_conf = _PATH_DHCPD_CONF;
+const char *path_dhcpd_db = _PATH_DHCPD_DB;
+const char *path_dhcpd_pid = _PATH_DHCPD_PID;
+/* False (default) => we write and use a pid file */
+isc_boolean_t no_pid_file = ISC_FALSE;
+
+int dhcp_max_agent_option_packet_length = DHCP_MTU_MAX;
+
+static omapi_auth_key_t *omapi_key = (omapi_auth_key_t *)0;
+int omapi_port;
+
+#if defined (TRACING)
+trace_type_t *trace_srandom;
+#endif
+
+static isc_result_t verify_addr (omapi_object_t *l, omapi_addr_t *addr) {
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t verify_auth (omapi_object_t *p, omapi_auth_key_t *a) {
+ if (a != omapi_key)
+ return DHCP_R_INVALIDKEY;
+ return ISC_R_SUCCESS;
+}
+
+static void omapi_listener_start (void *foo)
+{
+ omapi_object_t *listener;
+ isc_result_t result;
+ struct timeval tv;
+
+ listener = (omapi_object_t *)0;
+ result = omapi_generic_new (&listener, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Can't allocate new generic object: %s",
+ isc_result_totext (result));
+ result = omapi_protocol_listen (listener,
+ (unsigned)omapi_port, 1);
+ if (result == ISC_R_SUCCESS && omapi_key)
+ result = omapi_protocol_configure_security
+ (listener, verify_addr, verify_auth);
+ if (result != ISC_R_SUCCESS) {
+ log_error ("Can't start OMAPI protocol: %s",
+ isc_result_totext (result));
+ tv.tv_sec = cur_tv.tv_sec + 5;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout (&tv, omapi_listener_start, 0, 0, 0);
+ }
+ omapi_object_dereference (&listener, MDL);
+}
+
+#if defined (PARANOIA)
+/* to be used in one of two possible scenarios */
+static void setup_chroot (char *chroot_dir) {
+ if (geteuid())
+ log_fatal ("you must be root to use chroot");
+
+ if (chroot(chroot_dir)) {
+ log_fatal ("chroot(\"%s\"): %m", chroot_dir);
+ }
+ if (chdir ("/")) {
+ /* probably permission denied */
+ log_fatal ("chdir(\"/\"): %m");
+ }
+}
+#endif /* PARANOIA */
+
+#ifndef UNIT_TEST
+int
+main(int argc, char **argv) {
+ int fd;
+ int i, status;
+ struct servent *ent;
+ char *s;
+ int cftest = 0;
+ int lftest = 0;
+#ifndef DEBUG
+ int pid;
+ char pbuf [20];
+ int daemon = 1;
+#endif
+ int quiet = 0;
+ char *server = (char *)0;
+ isc_result_t result;
+ unsigned seed;
+ struct interface_info *ip;
+#if defined (NSUPDATE)
+ struct parse *parse;
+ int lose;
+#endif
+ int no_dhcpd_conf = 0;
+ int no_dhcpd_db = 0;
+ int no_dhcpd_pid = 0;
+#ifdef DHCPv6
+ int local_family_set = 0;
+#endif /* DHCPv6 */
+#if defined (TRACING)
+ char *traceinfile = (char *)0;
+ char *traceoutfile = (char *)0;
+#endif
+
+#if defined (PARANOIA)
+ char *set_user = 0;
+ char *set_group = 0;
+ char *set_chroot = 0;
+
+ uid_t set_uid = 0;
+ gid_t set_gid = 0;
+#endif /* PARANOIA */
+
+ /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
+ 2 (stderr) are open. To do this, we assume that when we
+ open a file the lowest available file descriptor is used. */
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 0)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 1)
+ fd = open("/dev/null", O_RDWR);
+ if (fd == 2)
+ log_perror = 0; /* No sense logging to /dev/null. */
+ else if (fd != -1)
+ close(fd);
+
+ /* Set up the isc and dns library managers */
+ status = dhcp_context_create();
+ if (status != ISC_R_SUCCESS)
+ log_fatal("Can't initialize context: %s",
+ isc_result_totext(status));
+
+ /* Set up the client classification system. */
+ classification_setup ();
+
+ /* Initialize the omapi system. */
+ result = omapi_init ();
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Can't initialize OMAPI: %s",
+ isc_result_totext (result));
+
+ /* Set up the OMAPI wrappers for common objects. */
+ dhcp_db_objects_setup ();
+ /* Set up the OMAPI wrappers for various server database internal
+ objects. */
+ dhcp_common_objects_setup ();
+
+ /* Initially, log errors to stderr as well as to syslogd. */
+ openlog ("dhcpd", LOG_NDELAY, DHCPD_LOG_FACILITY);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp (argv [i], "-p")) {
+ if (++i == argc)
+ usage ();
+ local_port = validate_port (argv [i]);
+ log_debug ("binding to user-specified port %d",
+ ntohs (local_port));
+ } else if (!strcmp (argv [i], "-f")) {
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ } else if (!strcmp (argv [i], "-d")) {
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ log_perror = -1;
+ } else if (!strcmp (argv [i], "-s")) {
+ if (++i == argc)
+ usage ();
+ server = argv [i];
+#if defined (PARANOIA)
+ } else if (!strcmp (argv [i], "-user")) {
+ if (++i == argc)
+ usage ();
+ set_user = argv [i];
+ } else if (!strcmp (argv [i], "-group")) {
+ if (++i == argc)
+ usage ();
+ set_group = argv [i];
+ } else if (!strcmp (argv [i], "-chroot")) {
+ if (++i == argc)
+ usage ();
+ set_chroot = argv [i];
+#endif /* PARANOIA */
+ } else if (!strcmp (argv [i], "-cf")) {
+ if (++i == argc)
+ usage ();
+ path_dhcpd_conf = argv [i];
+ no_dhcpd_conf = 1;
+ } else if (!strcmp (argv [i], "-lf")) {
+ if (++i == argc)
+ usage ();
+ path_dhcpd_db = argv [i];
+ no_dhcpd_db = 1;
+ } else if (!strcmp (argv [i], "-pf")) {
+ if (++i == argc)
+ usage ();
+ path_dhcpd_pid = argv [i];
+ no_dhcpd_pid = 1;
+ } else if (!strcmp(argv[i], "--no-pid")) {
+ no_pid_file = ISC_TRUE;
+ } else if (!strcmp (argv [i], "-t")) {
+ /* test configurations only */
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ cftest = 1;
+ log_perror = -1;
+ } else if (!strcmp (argv [i], "-T")) {
+ /* test configurations and lease file only */
+#ifndef DEBUG
+ daemon = 0;
+#endif
+ cftest = 1;
+ lftest = 1;
+ log_perror = -1;
+ } else if (!strcmp (argv [i], "-q")) {
+ quiet = 1;
+ quiet_interface_discovery = 1;
+#ifdef DHCPv6
+ } else if (!strcmp(argv[i], "-4")) {
+ if (local_family_set && (local_family != AF_INET)) {
+ log_fatal("Server cannot run in both IPv4 and "
+ "IPv6 mode at the same time.");
+ }
+ local_family = AF_INET;
+ local_family_set = 1;
+ } else if (!strcmp(argv[i], "-6")) {
+ if (local_family_set && (local_family != AF_INET6)) {
+ log_fatal("Server cannot run in both IPv4 and "
+ "IPv6 mode at the same time.");
+ }
+ local_family = AF_INET6;
+ local_family_set = 1;
+#endif /* DHCPv6 */
+ } else if (!strcmp (argv [i], "--version")) {
+ log_info("isc-dhcpd-%s", PACKAGE_VERSION);
+ exit (0);
+#if defined (TRACING)
+ } else if (!strcmp (argv [i], "-tf")) {
+ if (++i == argc)
+ usage ();
+ traceoutfile = argv [i];
+ } else if (!strcmp (argv [i], "-play")) {
+ if (++i == argc)
+ usage ();
+ traceinfile = argv [i];
+ trace_replay_init ();
+#endif /* TRACING */
+ } else if (argv [i][0] == '-') {
+ usage ();
+ } else {
+ struct interface_info *tmp =
+ (struct interface_info *)0;
+ if (strlen(argv[i]) >= sizeof(tmp->name))
+ log_fatal("%s: interface name too long "
+ "(is %ld)",
+ argv[i], (long)strlen(argv[i]));
+ result = interface_allocate (&tmp, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Insufficient memory to %s %s: %s",
+ "record interface", argv [i],
+ isc_result_totext (result));
+ strcpy (tmp -> name, argv [i]);
+ if (interfaces) {
+ interface_reference (&tmp -> next,
+ interfaces, MDL);
+ interface_dereference (&interfaces, MDL);
+ }
+ interface_reference (&interfaces, tmp, MDL);
+ tmp -> flags = INTERFACE_REQUESTED;
+ }
+ }
+
+ if (!no_dhcpd_conf && (s = getenv ("PATH_DHCPD_CONF"))) {
+ path_dhcpd_conf = s;
+ }
+
+#ifdef DHCPv6
+ if (local_family == AF_INET6) {
+ /* DHCPv6: override DHCPv4 lease and pid filenames */
+ if (!no_dhcpd_db) {
+ if ((s = getenv ("PATH_DHCPD6_DB")))
+ path_dhcpd_db = s;
+ else
+ path_dhcpd_db = _PATH_DHCPD6_DB;
+ }
+ if (!no_dhcpd_pid) {
+ if ((s = getenv ("PATH_DHCPD6_PID")))
+ path_dhcpd_pid = s;
+ else
+ path_dhcpd_pid = _PATH_DHCPD6_PID;
+ }
+ } else
+#else /* !DHCPv6 */
+ {
+ if (!no_dhcpd_db && (s = getenv ("PATH_DHCPD_DB"))) {
+ path_dhcpd_db = s;
+ }
+ if (!no_dhcpd_pid && (s = getenv ("PATH_DHCPD_PID"))) {
+ path_dhcpd_pid = s;
+ }
+ }
+#endif /* DHCPv6 */
+
+ /*
+ * convert relative path names to absolute, for files that need
+ * to be reopened after chdir() has been called
+ */
+ if (path_dhcpd_db[0] != '/') {
+ char *path = dmalloc(PATH_MAX, MDL);
+ if (path == NULL)
+ log_fatal("No memory for filename\n");
+ path_dhcpd_db = realpath(path_dhcpd_db, path);
+ if (path_dhcpd_db == NULL)
+ log_fatal("%s: %s", path, strerror(errno));
+ }
+
+ if (!quiet) {
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info (copyright);
+ log_info (arr);
+ log_info (url);
+ } else {
+ quiet = 0;
+ log_perror = 0;
+ }
+
+#if defined (TRACING)
+ trace_init (set_time, MDL);
+ if (traceoutfile) {
+ result = trace_begin (traceoutfile, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("Unable to begin trace: %s",
+ isc_result_totext (result));
+ }
+ interface_trace_setup ();
+ parse_trace_setup ();
+ trace_srandom = trace_type_register ("random-seed", (void *)0,
+ trace_seed_input,
+ trace_seed_stop, MDL);
+ trace_ddns_init();
+#endif
+
+#if defined (PARANOIA)
+ /* get user and group info if those options were given */
+ if (set_user) {
+ struct passwd *tmp_pwd;
+
+ if (geteuid())
+ log_fatal ("you must be root to set user");
+
+ if (!(tmp_pwd = getpwnam(set_user)))
+ log_fatal ("no such user: %s", set_user);
+
+ set_uid = tmp_pwd->pw_uid;
+
+ /* use the user's group as the default gid */
+ if (!set_group)
+ set_gid = tmp_pwd->pw_gid;
+ }
+
+ if (set_group) {
+/* get around the ISC declaration of group */
+#define group real_group
+ struct group *tmp_grp;
+
+ if (geteuid())
+ log_fatal ("you must be root to set group");
+
+ if (!(tmp_grp = getgrnam(set_group)))
+ log_fatal ("no such group: %s", set_group);
+
+ set_gid = tmp_grp->gr_gid;
+#undef group
+ }
+
+# if defined (EARLY_CHROOT)
+ if (set_chroot) setup_chroot (set_chroot);
+# endif /* EARLY_CHROOT */
+#endif /* PARANOIA */
+
+ /* Default to the DHCP/BOOTP port. */
+ if (!local_port)
+ {
+ if ((s = getenv ("DHCPD_PORT"))) {
+ local_port = validate_port (s);
+ log_debug ("binding to environment-specified port %d",
+ ntohs (local_port));
+ } else {
+ if (local_family == AF_INET) {
+ ent = getservbyname("dhcp", "udp");
+ if (ent == NULL) {
+ local_port = htons(67);
+ } else {
+ local_port = ent->s_port;
+ }
+ } else {
+ /* INSIST(local_family == AF_INET6); */
+ ent = getservbyname("dhcpv6-server", "udp");
+ if (ent == NULL) {
+ local_port = htons(547);
+ } else {
+ local_port = ent->s_port;
+ }
+ }
+#ifndef __CYGWIN32__ /* XXX */
+ endservent ();
+#endif
+ }
+ }
+
+ if (local_family == AF_INET) {
+ remote_port = htons(ntohs(local_port) + 1);
+ } else {
+ /* INSIST(local_family == AF_INET6); */
+ ent = getservbyname("dhcpv6-client", "udp");
+ if (ent == NULL) {
+ remote_port = htons(546);
+ } else {
+ remote_port = ent->s_port;
+ }
+ }
+
+ if (server) {
+ if (local_family != AF_INET) {
+ log_fatal("You can only specify address to send "
+ "replies to when running an IPv4 server.");
+ }
+ if (!inet_aton (server, &limited_broadcast)) {
+ struct hostent *he;
+ he = gethostbyname (server);
+ if (he) {
+ memcpy (&limited_broadcast,
+ he -> h_addr_list [0],
+ sizeof limited_broadcast);
+ } else
+ limited_broadcast.s_addr = INADDR_BROADCAST;
+ }
+ } else {
+ limited_broadcast.s_addr = INADDR_BROADCAST;
+ }
+
+ /* Get the current time... */
+ gettimeofday(&cur_tv, NULL);
+
+ /* Set up the initial dhcp option universe. */
+ initialize_common_option_spaces ();
+ initialize_server_option_spaces ();
+
+ /* Add the ddns update style enumeration prior to parsing. */
+ add_enumeration (&ddns_styles);
+ add_enumeration (&syslog_enum);
+#if defined (LDAP_CONFIGURATION)
+ add_enumeration (&ldap_methods);
+#if defined (LDAP_USE_SSL)
+ add_enumeration (&ldap_ssl_usage_enum);
+ add_enumeration (&ldap_tls_reqcert_enum);
+ add_enumeration (&ldap_tls_crlcheck_enum);
+#endif
+#endif
+
+ if (!group_allocate (&root_group, MDL))
+ log_fatal ("Can't allocate root group!");
+ root_group -> authoritative = 0;
+
+ /* Set up various hooks. */
+ dhcp_interface_setup_hook = dhcpd_interface_setup_hook;
+ bootp_packet_handler = do_packet;
+#ifdef DHCPv6
+ dhcpv6_packet_handler = do_packet6;
+#endif /* DHCPv6 */
+
+#if defined (NSUPDATE)
+ /* Set up the standard name service updater routine. */
+ parse = NULL;
+ status = new_parse(&parse, -1, std_nsupdate, sizeof(std_nsupdate) - 1,
+ "standard name service update routine", 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("can't begin parsing name service updater!");
+
+ if (parse != NULL) {
+ lose = 0;
+ if (!(parse_executable_statements(&root_group->statements,
+ parse, &lose, context_any))) {
+ end_parse(&parse);
+ log_fatal("can't parse standard name service updater!");
+ }
+ end_parse(&parse);
+ }
+#endif
+
+ /* Initialize icmp support... */
+ if (!cftest && !lftest)
+ icmp_startup (1, lease_pinged);
+
+#if defined (TRACING)
+ if (traceinfile) {
+ if (!no_dhcpd_db) {
+ log_error ("%s", "");
+ log_error ("** You must specify a lease file with -lf.");
+ log_error (" Dhcpd will not overwrite your default");
+ log_fatal (" lease file when playing back a trace. **");
+ }
+ trace_file_replay (traceinfile);
+
+#if defined (DEBUG_MEMORY_LEAKAGE) && \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ free_everything ();
+ omapi_print_dmalloc_usage_by_caller ();
+#endif
+
+ exit (0);
+ }
+#endif
+
+#ifdef DHCPv6
+ /* set up DHCPv6 hashes */
+ if (!ia_new_hash(&ia_na_active, DEFAULT_HASH_SIZE, MDL)) {
+ log_fatal("Out of memory creating hash for active IA_NA.");
+ }
+ if (!ia_new_hash(&ia_ta_active, DEFAULT_HASH_SIZE, MDL)) {
+ log_fatal("Out of memory creating hash for active IA_TA.");
+ }
+ if (!ia_new_hash(&ia_pd_active, DEFAULT_HASH_SIZE, MDL)) {
+ log_fatal("Out of memory creating hash for active IA_PD.");
+ }
+#endif /* DHCPv6 */
+
+ /* Read the dhcpd.conf file... */
+ if (readconf () != ISC_R_SUCCESS)
+ log_fatal ("Configuration file errors encountered -- exiting");
+
+ postconf_initialization (quiet);
+
+#if defined (PARANOIA) && !defined (EARLY_CHROOT)
+ if (set_chroot) setup_chroot (set_chroot);
+#endif /* PARANOIA && !EARLY_CHROOT */
+
+ /* test option should cause an early exit */
+ if (cftest && !lftest)
+ exit(0);
+
+ group_write_hook = group_writer;
+
+ /* Start up the database... */
+ db_startup (lftest);
+
+ if (lftest)
+ exit (0);
+
+ /* Discover all the network interfaces and initialize them. */
+ discover_interfaces(DISCOVER_SERVER);
+
+#ifdef DHCPv6
+ /*
+ * Remove addresses from our pools that we should not issue
+ * to clients.
+ *
+ * We currently have no support for this in IPv4. It is not
+ * as important in IPv4, as making pools with ranges that
+ * leave out interfaces and hosts is fairly straightforward
+ * using range notation, but not so handy with CIDR notation.
+ */
+ if (local_family == AF_INET6) {
+ mark_hosts_unavailable();
+ mark_phosts_unavailable();
+ mark_interfaces_unavailable();
+ }
+#endif /* DHCPv6 */
+
+
+ /* Make up a seed for the random number generator from current
+ time plus the sum of the last four bytes of each
+ interface's hardware address interpreted as an integer.
+ Not much entropy, but we're booting, so we're not likely to
+ find anything better. */
+ seed = 0;
+ for (ip = interfaces; ip; ip = ip -> next) {
+ int junk;
+ memcpy (&junk,
+ &ip -> hw_address.hbuf [ip -> hw_address.hlen -
+ sizeof seed], sizeof seed);
+ seed += junk;
+ }
+ srandom (seed + cur_time);
+#if defined (TRACING)
+ trace_seed_stash (trace_srandom, seed + cur_time);
+#endif
+ postdb_startup ();
+
+#ifdef DHCPv6
+ /*
+ * Set server DHCPv6 identifier.
+ * See dhcpv6.c for discussion of setting DUID.
+ */
+ if (set_server_duid_from_option() == ISC_R_SUCCESS) {
+ write_server_duid();
+ } else {
+ if (!server_duid_isset()) {
+ if (generate_new_server_duid() != ISC_R_SUCCESS) {
+ log_fatal("Unable to set server identifier.");
+ }
+ write_server_duid();
+ }
+ }
+#endif /* DHCPv6 */
+
+#ifndef DEBUG
+ if (daemon) {
+ /* First part of becoming a daemon... */
+ if ((pid = fork ()) < 0)
+ log_fatal ("Can't fork daemon: %m");
+ else if (pid)
+ exit (0);
+ }
+
+#if defined (PARANOIA)
+ /* change uid to the specified one */
+
+ if (set_gid) {
+ if (setgroups (0, (void *)0))
+ log_fatal ("setgroups: %m");
+ if (setgid (set_gid))
+ log_fatal ("setgid(%d): %m", (int) set_gid);
+ }
+
+ if (set_uid) {
+ if (setuid (set_uid))
+ log_fatal ("setuid(%d): %m", (int) set_uid);
+ }
+#endif /* PARANOIA */
+
+ /*
+ * Deal with pid files. If the user told us
+ * not to write a file we don't read one either
+ */
+ if (no_pid_file == ISC_FALSE) {
+ /*Read previous pid file. */
+ if ((i = open (path_dhcpd_pid, O_RDONLY)) >= 0) {
+ status = read(i, pbuf, (sizeof pbuf) - 1);
+ close (i);
+ if (status > 0) {
+ pbuf[status] = 0;
+ pid = atoi(pbuf);
+
+ /*
+ * If there was a previous server process and
+ * it is still running, abort
+ */
+ if (!pid ||
+ (pid != getpid() && kill(pid, 0) == 0))
+ log_fatal("There's already a "
+ "DHCP server running.");
+ }
+ }
+
+ /* Write new pid file. */
+ i = open(path_dhcpd_pid, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (i >= 0) {
+ sprintf(pbuf, "%d\n", (int) getpid());
+ IGNORE_RET (write(i, pbuf, strlen(pbuf)));
+ close(i);
+ } else {
+ log_error("Can't create PID file %s: %m.",
+ path_dhcpd_pid);
+ }
+ }
+
+ /* If we were requested to log to stdout on the command line,
+ keep doing so; otherwise, stop. */
+ if (log_perror == -1)
+ log_perror = 1;
+ else
+ log_perror = 0;
+
+ if (daemon) {
+ /* Become session leader and get pid... */
+ pid = setsid();
+
+ /* Close standard I/O descriptors. */
+ close(0);
+ close(1);
+ close(2);
+
+ /* Reopen them on /dev/null. */
+ open("/dev/null", O_RDWR);
+ open("/dev/null", O_RDWR);
+ open("/dev/null", O_RDWR);
+ log_perror = 0; /* No sense logging to /dev/null. */
+
+ IGNORE_RET (chdir("/"));
+ }
+#endif /* !DEBUG */
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || defined (DEBUG_MALLOC_POOL) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ dmalloc_cutoff_generation = dmalloc_generation;
+ dmalloc_longterm = dmalloc_outstanding;
+ dmalloc_outstanding = 0;
+#endif
+
+ omapi_set_int_value ((omapi_object_t *)dhcp_control_object,
+ (omapi_object_t *)0, "state", server_running);
+
+ /* Receive packets and dispatch them... */
+ dispatch ();
+
+ /* Not reached */
+ return 0;
+}
+#endif /* !UNIT_TEST */
+
+void postconf_initialization (int quiet)
+{
+ struct option_state *options = (struct option_state *)0;
+ struct data_string db;
+ struct option_cache *oc;
+ char *s;
+ isc_result_t result;
+#if defined (NSUPDATE)
+ struct parse *parse;
+#endif
+ int tmp;
+
+ /* Now try to get the lease file name. */
+ option_state_allocate (&options, MDL);
+
+ execute_statements_in_scope ((struct binding_value **)0,
+ (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ options, &global_scope,
+ root_group,
+ (struct group *)0);
+ memset (&db, 0, sizeof db);
+ oc = lookup_option (&server_universe, options, SV_LEASE_FILE_NAME);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ s = dmalloc (db.len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for lease db filename.");
+ memcpy (s, db.data, db.len);
+ s [db.len] = 0;
+ data_string_forget (&db, MDL);
+ path_dhcpd_db = s;
+ }
+
+ oc = lookup_option (&server_universe, options, SV_PID_FILE_NAME);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ s = dmalloc (db.len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for pid filename.");
+ memcpy (s, db.data, db.len);
+ s [db.len] = 0;
+ data_string_forget (&db, MDL);
+ path_dhcpd_pid = s;
+ }
+
+#ifdef DHCPv6
+ if (local_family == AF_INET6) {
+ /*
+ * Override lease file name with dhcpv6 lease file name,
+ * if it was set; then, do the same with the pid file name
+ */
+ oc = lookup_option(&server_universe, options,
+ SV_DHCPV6_LEASE_FILE_NAME);
+ if (oc &&
+ evaluate_option_cache(&db, NULL, NULL, NULL,
+ options, NULL, &global_scope,
+ oc, MDL)) {
+ s = dmalloc (db.len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for lease db filename.");
+ memcpy (s, db.data, db.len);
+ s [db.len] = 0;
+ data_string_forget (&db, MDL);
+ path_dhcpd_db = s;
+ }
+
+ oc = lookup_option(&server_universe, options,
+ SV_DHCPV6_PID_FILE_NAME);
+ if (oc &&
+ evaluate_option_cache(&db, NULL, NULL, NULL,
+ options, NULL, &global_scope,
+ oc, MDL)) {
+ s = dmalloc (db.len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for pid filename.");
+ memcpy (s, db.data, db.len);
+ s [db.len] = 0;
+ data_string_forget (&db, MDL);
+ path_dhcpd_pid = s;
+ }
+ }
+#endif /* DHCPv6 */
+
+ omapi_port = -1;
+ oc = lookup_option (&server_universe, options, SV_OMAPI_PORT);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 2) {
+ omapi_port = getUShort (db.data);
+ } else
+ log_fatal ("invalid omapi port data length");
+ data_string_forget (&db, MDL);
+ }
+
+ oc = lookup_option (&server_universe, options, SV_OMAPI_KEY);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ s = dmalloc (db.len + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for OMAPI key filename.");
+ memcpy (s, db.data, db.len);
+ s [db.len] = 0;
+ data_string_forget (&db, MDL);
+ result = omapi_auth_key_lookup_name (&omapi_key, s);
+ dfree (s, MDL);
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("OMAPI key %s: %s",
+ s, isc_result_totext (result));
+ }
+
+ oc = lookup_option (&server_universe, options, SV_LOCAL_PORT);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 2) {
+ local_port = htons (getUShort (db.data));
+ } else
+ log_fatal ("invalid local port data length");
+ data_string_forget (&db, MDL);
+ }
+
+ oc = lookup_option (&server_universe, options, SV_REMOTE_PORT);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 2) {
+ remote_port = htons (getUShort (db.data));
+ } else
+ log_fatal ("invalid remote port data length");
+ data_string_forget (&db, MDL);
+ }
+
+ oc = lookup_option (&server_universe, options,
+ SV_LIMITED_BROADCAST_ADDRESS);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 4) {
+ memcpy (&limited_broadcast, db.data, 4);
+ } else
+ log_fatal ("invalid broadcast address data length");
+ data_string_forget (&db, MDL);
+ }
+
+ oc = lookup_option (&server_universe, options,
+ SV_LOCAL_ADDRESS);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0, (struct client_state *)0,
+ options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 4) {
+ memcpy (&local_address, db.data, 4);
+ } else
+ log_fatal ("invalid local address data length");
+ data_string_forget (&db, MDL);
+ }
+
+ oc = lookup_option (&server_universe, options, SV_DDNS_UPDATE_STYLE);
+ if (oc) {
+ if (evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 1) {
+ ddns_update_style = db.data [0];
+ } else
+ log_fatal ("invalid dns update type");
+ data_string_forget (&db, MDL);
+ }
+ } else {
+ ddns_update_style = DDNS_UPDATE_STYLE_NONE;
+ }
+#if defined (NSUPDATE)
+ /* We no longer support ad_hoc, tell the user */
+ if (ddns_update_style == DDNS_UPDATE_STYLE_AD_HOC) {
+ log_fatal("ddns-update-style ad_hoc no longer supported");
+ }
+#else
+ /* If we don't have support for updates compiled in tell the user */
+ if (ddns_update_style != DDNS_UPDATE_STYLE_NONE) {
+ log_fatal("Support for ddns-update-style not compiled in");
+ }
+#endif
+
+ oc = lookup_option (&server_universe, options, SV_LOG_FACILITY);
+ if (oc) {
+ if (evaluate_option_cache (&db, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ options,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ if (db.len == 1) {
+ closelog ();
+ openlog ("dhcpd", LOG_NDELAY, db.data[0]);
+ /* Log the startup banner into the new
+ log file. */
+ if (!quiet) {
+ /* Don't log to stderr twice. */
+ tmp = log_perror;
+ log_perror = 0;
+ log_info("%s %s",
+ message, PACKAGE_VERSION);
+ log_info (copyright);
+ log_info (arr);
+ log_info (url);
+ log_perror = tmp;
+ }
+ } else
+ log_fatal ("invalid log facility");
+ data_string_forget (&db, MDL);
+ }
+ }
+
+ oc = lookup_option(&server_universe, options, SV_DELAYED_ACK);
+ if (oc &&
+ evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
+ &global_scope, oc, MDL)) {
+ if (db.len == 2) {
+ max_outstanding_acks = htons(getUShort(db.data));
+ } else {
+ log_fatal("invalid max delayed ACK count ");
+ }
+ data_string_forget(&db, MDL);
+ }
+
+ oc = lookup_option(&server_universe, options, SV_MAX_ACK_DELAY);
+ if (oc &&
+ evaluate_option_cache(&db, NULL, NULL, NULL, options, NULL,
+ &global_scope, oc, MDL)) {
+ u_int32_t timeval;
+
+ if (db.len != 4)
+ log_fatal("invalid max ack delay configuration");
+
+ timeval = getULong(db.data);
+ max_ack_delay_secs = timeval / 1000000;
+ max_ack_delay_usecs = timeval % 1000000;
+
+ data_string_forget(&db, MDL);
+ }
+
+ /* Don't need the options anymore. */
+ option_state_dereference (&options, MDL);
+
+#if defined (NSUPDATE)
+ /* If old-style ddns updates have been requested, parse the
+ old-style ddns updater. */
+ if (ddns_update_style == 1) {
+ struct executable_statement **e, *s;
+
+ if (root_group -> statements) {
+ s = (struct executable_statement *)0;
+ if (!executable_statement_allocate (&s, MDL))
+ log_fatal ("no memory for ddns updater");
+ executable_statement_reference
+ (&s -> next, root_group -> statements, MDL);
+ executable_statement_dereference
+ (&root_group -> statements, MDL);
+ executable_statement_reference
+ (&root_group -> statements, s, MDL);
+ s -> op = statements_statement;
+ e = &s -> data.statements;
+ executable_statement_dereference (&s, MDL);
+ } else {
+ e = &root_group -> statements;
+ }
+
+ /* Set up the standard name service updater routine. */
+ parse = NULL;
+ result = new_parse(&parse, -1, old_nsupdate,
+ sizeof(old_nsupdate) - 1,
+ "old name service update routine", 0);
+ if (result != ISC_R_SUCCESS)
+ log_fatal ("can't begin parsing old ddns updater!");
+
+ if (parse != NULL) {
+ tmp = 0;
+ if (!(parse_executable_statements(e, parse, &tmp,
+ context_any))) {
+ end_parse(&parse);
+ log_fatal("can't parse standard ddns updater!");
+ }
+ }
+ end_parse(&parse);
+ }
+#endif
+}
+
+void postdb_startup (void)
+{
+ /* Initialize the omapi listener state. */
+ if (omapi_port != -1) {
+ omapi_listener_start (0);
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Initialize the failover listener state. */
+ dhcp_failover_startup ();
+#endif
+
+ /*
+ * Begin our lease timeout background task.
+ */
+ schedule_all_ipv6_lease_timeouts();
+}
+
+/* Print usage message. */
+
+static void
+usage(void) {
+ log_info("%s %s", message, PACKAGE_VERSION);
+ log_info(copyright);
+ log_info(arr);
+
+ log_fatal("Usage: dhcpd [-p <UDP port #>] [-f] [-d] [-q] [-t|-T]\n"
+#ifdef DHCPv6
+ " [-4|-6] [-cf config-file] [-lf lease-file]\n"
+#else /* !DHCPv6 */
+ " [-cf config-file] [-lf lease-file]\n"
+#endif /* DHCPv6 */
+#if defined (PARANOIA)
+ /* meld into the following string */
+ " [-user user] [-group group] [-chroot dir]\n"
+#endif /* PARANOIA */
+#if defined (TRACING)
+ " [-tf trace-output-file]\n"
+ " [-play trace-input-file]\n"
+#endif /* TRACING */
+ " [-pf pid-file] [--no-pid] [-s server]\n"
+ " [if0 [...ifN]]");
+}
+
+void lease_pinged (from, packet, length)
+ struct iaddr from;
+ u_int8_t *packet;
+ int length;
+{
+ struct lease *lp;
+
+ /* Don't try to look up a pinged lease if we aren't trying to
+ ping one - otherwise somebody could easily make us churn by
+ just forging repeated ICMP EchoReply packets for us to look
+ up. */
+ if (!outstanding_pings)
+ return;
+
+ lp = (struct lease *)0;
+ if (!find_lease_by_ip_addr (&lp, from, MDL)) {
+ log_debug ("unexpected ICMP Echo Reply from %s",
+ piaddr (from));
+ return;
+ }
+
+ if (!lp -> state) {
+#if defined (FAILOVER_PROTOCOL)
+ if (!lp -> pool ||
+ !lp -> pool -> failover_peer)
+#endif
+ log_debug ("ICMP Echo Reply for %s late or spurious.",
+ piaddr (from));
+ goto out;
+ }
+
+ if (lp -> ends > cur_time) {
+ log_debug ("ICMP Echo reply while lease %s valid.",
+ piaddr (from));
+ }
+
+ /* At this point it looks like we pinged a lease and got a
+ response, which shouldn't have happened. */
+ data_string_forget (&lp -> state -> parameter_request_list, MDL);
+ free_lease_state (lp -> state, MDL);
+ lp -> state = (struct lease_state *)0;
+
+ abandon_lease (lp, "pinged before offer");
+ cancel_timeout (lease_ping_timeout, lp);
+ --outstanding_pings;
+ out:
+ lease_dereference (&lp, MDL);
+}
+
+void lease_ping_timeout (vlp)
+ void *vlp;
+{
+ struct lease *lp = vlp;
+
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ unsigned long previous_outstanding = dmalloc_outstanding;
+#endif
+
+ --outstanding_pings;
+ dhcp_reply (lp);
+
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
+ dmalloc_generation,
+ dmalloc_outstanding - previous_outstanding,
+ dmalloc_outstanding, dmalloc_longterm);
+#endif
+#if defined (DEBUG_MEMORY_LEAKAGE)
+ dmalloc_dump_outstanding ();
+#endif
+}
+
+int dhcpd_interface_setup_hook (struct interface_info *ip, struct iaddr *ia)
+{
+ struct subnet *subnet;
+ struct shared_network *share;
+ isc_result_t status;
+
+ /* Special case for fallback network - not sure why this is
+ necessary. */
+ if (!ia) {
+ const char *fnn = "fallback-net";
+ status = shared_network_allocate (&ip -> shared_network, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("No memory for shared subnet: %s",
+ isc_result_totext (status));
+ ip -> shared_network -> name = dmalloc (strlen (fnn) + 1, MDL);
+ strcpy (ip -> shared_network -> name, fnn);
+ return 1;
+ }
+
+ /* If there's a registered subnet for this address,
+ connect it together... */
+ subnet = (struct subnet *)0;
+ if (find_subnet (&subnet, *ia, MDL)) {
+ /* If this interface has multiple aliases on the same
+ subnet, ignore all but the first we encounter. */
+ if (!subnet -> interface) {
+ interface_reference (&subnet -> interface, ip, MDL);
+ subnet -> interface_address = *ia;
+ } else if (subnet -> interface != ip) {
+ log_error ("Multiple interfaces match the %s: %s %s",
+ "same subnet",
+ subnet -> interface -> name, ip -> name);
+ }
+ share = subnet -> shared_network;
+ if (ip -> shared_network &&
+ ip -> shared_network != share) {
+ log_fatal ("Interface %s matches multiple shared %s",
+ ip -> name, "networks");
+ } else {
+ if (!ip -> shared_network)
+ shared_network_reference
+ (&ip -> shared_network, share, MDL);
+ }
+
+ if (!share -> interface) {
+ interface_reference (&share -> interface, ip, MDL);
+ } else if (share -> interface != ip) {
+ log_error ("Multiple interfaces match the %s: %s %s",
+ "same shared network",
+ share -> interface -> name, ip -> name);
+ }
+ subnet_dereference (&subnet, MDL);
+ }
+ return 1;
+}
+
+static TIME shutdown_time;
+static int omapi_connection_count;
+enum dhcp_shutdown_state shutdown_state;
+
+isc_result_t dhcp_io_shutdown (omapi_object_t *obj, void *foo)
+{
+ /* Shut down all listeners. */
+ if (shutdown_state == shutdown_listeners &&
+ obj -> type == omapi_type_listener &&
+ obj -> inner &&
+ obj -> inner -> type == omapi_type_protocol_listener) {
+ omapi_listener_destroy (obj, MDL);
+ return ISC_R_SUCCESS;
+ }
+
+ /* Shut down all existing omapi connections. */
+ if (obj -> type == omapi_type_connection &&
+ obj -> inner &&
+ obj -> inner -> type == omapi_type_protocol) {
+ if (shutdown_state == shutdown_drop_omapi_connections) {
+ omapi_disconnect (obj, 1);
+ }
+ omapi_connection_count++;
+ if (shutdown_state == shutdown_omapi_connections) {
+ omapi_disconnect (obj, 0);
+ return ISC_R_SUCCESS;
+ }
+ }
+
+ /* Shutdown all DHCP interfaces. */
+ if (obj -> type == dhcp_type_interface &&
+ shutdown_state == shutdown_dhcp) {
+ dhcp_interface_remove (obj, (omapi_object_t *)0);
+ return ISC_R_SUCCESS;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t dhcp_io_shutdown_countdown (void *vlp)
+{
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *state;
+ int failover_connection_count = 0;
+#endif
+ struct timeval tv;
+
+ oncemore:
+ if (shutdown_state == shutdown_listeners ||
+ shutdown_state == shutdown_omapi_connections ||
+ shutdown_state == shutdown_drop_omapi_connections ||
+ shutdown_state == shutdown_dhcp) {
+ omapi_connection_count = 0;
+ omapi_io_state_foreach (dhcp_io_shutdown, 0);
+ }
+
+ if ((shutdown_state == shutdown_listeners ||
+ shutdown_state == shutdown_omapi_connections ||
+ shutdown_state == shutdown_drop_omapi_connections) &&
+ omapi_connection_count == 0) {
+ shutdown_state = shutdown_dhcp;
+ shutdown_time = cur_time;
+ goto oncemore;
+ } else if (shutdown_state == shutdown_listeners &&
+ cur_time - shutdown_time > 4) {
+ shutdown_state = shutdown_omapi_connections;
+ shutdown_time = cur_time;
+ } else if (shutdown_state == shutdown_omapi_connections &&
+ cur_time - shutdown_time > 4) {
+ shutdown_state = shutdown_drop_omapi_connections;
+ shutdown_time = cur_time;
+ } else if (shutdown_state == shutdown_drop_omapi_connections &&
+ cur_time - shutdown_time > 4) {
+ shutdown_state = shutdown_dhcp;
+ shutdown_time = cur_time;
+ goto oncemore;
+ } else if (shutdown_state == shutdown_dhcp &&
+ cur_time - shutdown_time > 4) {
+ shutdown_state = shutdown_done;
+ shutdown_time = cur_time;
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Set all failover peers into the shutdown state. */
+ if (shutdown_state == shutdown_dhcp) {
+ for (state = failover_states; state; state = state -> next) {
+ if (state -> me.state == normal) {
+ dhcp_failover_set_state (state, shut_down);
+ failover_connection_count++;
+ }
+ if (state -> me.state == shut_down &&
+ state -> partner.state != partner_down)
+ failover_connection_count++;
+ }
+ }
+
+ if (shutdown_state == shutdown_done) {
+ for (state = failover_states; state; state = state -> next) {
+ if (state -> me.state == shut_down) {
+ if (state -> link_to_peer)
+ dhcp_failover_link_dereference (&state -> link_to_peer,
+ MDL);
+ dhcp_failover_set_state (state, recover);
+ }
+ }
+#if defined (DEBUG_MEMORY_LEAKAGE) && \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ free_everything ();
+ omapi_print_dmalloc_usage_by_caller ();
+#endif
+ exit (0);
+ }
+#else
+ if (shutdown_state == shutdown_done) {
+#if defined (DEBUG_MEMORY_LEAKAGE) && \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ free_everything ();
+ omapi_print_dmalloc_usage_by_caller ();
+#endif
+ exit (0);
+ }
+#endif
+ if (shutdown_state == shutdown_dhcp &&
+#if defined(FAILOVER_PROTOCOL)
+ !failover_connection_count &&
+#endif
+ ISC_TRUE) {
+ shutdown_state = shutdown_done;
+ shutdown_time = cur_time;
+ goto oncemore;
+ }
+ tv.tv_sec = cur_tv.tv_sec + 1;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout (&tv,
+ (void (*)(void *))dhcp_io_shutdown_countdown, 0, 0, 0);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_set_control_state (control_object_state_t oldstate,
+ control_object_state_t newstate)
+{
+ if (newstate == server_shutdown) {
+ shutdown_time = cur_time;
+ shutdown_state = shutdown_listeners;
+ dhcp_io_shutdown_countdown (0);
+ return ISC_R_SUCCESS;
+ }
+ return DHCP_R_INVALIDARG;
+}
diff --git a/server/dhcpd.conf b/server/dhcpd.conf
new file mode 100644
index 0000000..5eab951
--- /dev/null
+++ b/server/dhcpd.conf
@@ -0,0 +1,104 @@
+# dhcpd.conf
+#
+# Sample configuration file for ISC dhcpd
+#
+
+# option definitions common to all supported networks...
+option domain-name "example.org";
+option domain-name-servers ns1.example.org, ns2.example.org;
+
+default-lease-time 600;
+max-lease-time 7200;
+
+# Use this to enble / disable dynamic dns updates globally.
+#ddns-update-style none;
+
+# If this DHCP server is the official DHCP server for the local
+# network, the authoritative directive should be uncommented.
+#authoritative;
+
+# Use this to send dhcp log messages to a different log file (you also
+# have to hack syslog.conf to complete the redirection).
+log-facility local7;
+
+# No service will be given on this subnet, but declaring it helps the
+# DHCP server to understand the network topology.
+
+subnet 10.152.187.0 netmask 255.255.255.0 {
+}
+
+# This is a very basic subnet declaration.
+
+subnet 10.254.239.0 netmask 255.255.255.224 {
+ range 10.254.239.10 10.254.239.20;
+ option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
+}
+
+# This declaration allows BOOTP clients to get dynamic addresses,
+# which we don't really recommend.
+
+subnet 10.254.239.32 netmask 255.255.255.224 {
+ range dynamic-bootp 10.254.239.40 10.254.239.60;
+ option broadcast-address 10.254.239.31;
+ option routers rtr-239-32-1.example.org;
+}
+
+# A slightly different configuration for an internal subnet.
+subnet 10.5.5.0 netmask 255.255.255.224 {
+ range 10.5.5.26 10.5.5.30;
+ option domain-name-servers ns1.internal.example.org;
+ option domain-name "internal.example.org";
+ option routers 10.5.5.1;
+ option broadcast-address 10.5.5.31;
+ default-lease-time 600;
+ max-lease-time 7200;
+}
+
+# Hosts which require special configuration options can be listed in
+# host statements. If no address is specified, the address will be
+# allocated dynamically (if possible), but the host-specific information
+# will still come from the host declaration.
+
+host passacaglia {
+ hardware ethernet 0:0:c0:5d:bd:95;
+ filename "vmunix.passacaglia";
+ server-name "toccata.fugue.com";
+}
+
+# Fixed IP addresses can also be specified for hosts. These addresses
+# should not also be listed as being available for dynamic assignment.
+# Hosts for which fixed IP addresses have been specified can boot using
+# BOOTP or DHCP. Hosts for which no fixed address is specified can only
+# be booted with DHCP, unless there is an address range on the subnet
+# to which a BOOTP client is connected which has the dynamic-bootp flag
+# set.
+host fantasia {
+ hardware ethernet 08:00:07:26:c0:a5;
+ fixed-address fantasia.fugue.com;
+}
+
+# You can declare a class of clients and then do address allocation
+# based on that. The example below shows a case where all clients
+# in a certain class get addresses on the 10.17.224/24 subnet, and all
+# other clients get addresses on the 10.0.29/24 subnet.
+
+class "foo" {
+ match if substring (option vendor-class-identifier, 0, 4) = "SUNW";
+}
+
+shared-network 224-29 {
+ subnet 10.17.224.0 netmask 255.255.255.0 {
+ option routers rtr-224.example.org;
+ }
+ subnet 10.0.29.0 netmask 255.255.255.0 {
+ option routers rtr-29.example.org;
+ }
+ pool {
+ allow members of "foo";
+ range 10.17.224.10 10.17.224.250;
+ }
+ pool {
+ deny members of "foo";
+ range 10.0.29.10 10.0.29.230;
+ }
+}
diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5
new file mode 100644
index 0000000..b6dc9f6
--- /dev/null
+++ b/server/dhcpd.conf.5
@@ -0,0 +1,3005 @@
+.\" dhcpd.conf.5
+.\"
+.\" Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\"
+.\" Support and other services are available for ISC products - see
+.\" https://www.isc.org for more information or to learn more about ISC.
+.\"
+.\" $Id: dhcpd.conf.5,v 1.106.18.6 2011-06-01 23:30:53 sar Exp $
+.\"
+.TH dhcpd.conf 5
+.SH NAME
+dhcpd.conf - dhcpd configuration file
+.SH DESCRIPTION
+The dhcpd.conf file contains configuration information for
+.IR dhcpd,
+the Internet Systems Consortium DHCP Server.
+.PP
+The dhcpd.conf file is a free-form ASCII text file. It is parsed by
+the recursive-descent parser built into dhcpd. 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 # character and
+end at the end of the line.
+.PP
+The file essentially consists of a list of statements. Statements
+fall into two broad categories - parameters and declarations.
+.PP
+Parameter statements either say how to do something (e.g., how long a
+lease to offer), whether to do something (e.g., should dhcpd provide
+addresses to unknown clients), or what parameters to provide to the
+client (e.g., use gateway 220.177.244.7).
+.PP
+Declarations are used to describe the topology of the
+network, to describe clients on the network, to provide addresses that
+can be assigned to clients, or to apply a group of parameters to a
+group of declarations. In any group of parameters and declarations,
+all parameters must be specified before any declarations which depend
+on those parameters may be specified.
+.PP
+Declarations about network topology include the \fIshared-network\fR
+and the \fIsubnet\fR declarations. If clients on a subnet are to be
+assigned addresses
+dynamically, a \fIrange\fR declaration must appear within the
+\fIsubnet\fR declaration. For clients with statically assigned
+addresses, or for installations where only known clients will be
+served, each such client must have a \fIhost\fR declaration. If
+parameters are to be applied to a group of declarations which are not
+related strictly on a per-subnet basis, the \fIgroup\fR declaration
+can be used.
+.PP
+For every subnet which will be served, and for every subnet
+to which the dhcp server is connected, there must be one \fIsubnet\fR
+declaration, which tells dhcpd how to recognize that an address is on
+that subnet. A \fIsubnet\fR declaration is required for each subnet
+even if no addresses will be dynamically allocated on that subnet.
+.PP
+Some installations have physical networks on which more than one IP
+subnet operates. For example, if there is a site-wide requirement
+that 8-bit subnet masks be used, but a department with a single
+physical ethernet network expands to the point where it has more than
+254 nodes, it may be necessary to run two 8-bit subnets on the same
+ethernet until such time as a new physical network can be added. In
+this case, the \fIsubnet\fR declarations for these two networks must be
+enclosed in a \fIshared-network\fR declaration.
+.PP
+Note that even when the \fIshared-network\fR declaration is absent, an
+empty one is created by the server to contain the \fIsubnet\fR (and any scoped
+parameters included in the \fIsubnet\fR). For practical purposes, this means
+that "stateless" DHCP clients, which are not tied to addresses (and therefore
+subnets) will receive the same configuration as stateful ones.
+.PP
+Some sites may have departments which have clients on more than one
+subnet, but it may be desirable to offer those clients a uniform set
+of parameters which are different than what would be offered to
+clients from other departments on the same subnet. For clients which
+will be declared explicitly with \fIhost\fR declarations, these
+declarations can be enclosed in a \fIgroup\fR declaration along with
+the parameters which are common to that department. For clients
+whose addresses will be dynamically assigned, class declarations and
+conditional declarations may be used to group parameter assignments
+based on information the client sends.
+.PP
+When a client is to be booted, its boot parameters are determined by
+consulting that client's \fIhost\fR declaration (if any), and then
+consulting any \fIclass\fR declarations matching the client,
+followed by the \fIpool\fR, \fIsubnet\fR and \fIshared-network\fR
+declarations for the IP address assigned to the client. Each of
+these declarations itself appears within a lexical scope, and all
+declarations at less specific lexical scopes are also consulted for
+client option declarations. Scopes are never considered
+twice, and if parameters are declared in more than one scope, the
+parameter declared in the most specific scope is the one that is
+used.
+.PP
+When dhcpd tries to find a \fIhost\fR declaration for a client, it
+first looks for a \fIhost\fR declaration which has a
+\fIfixed-address\fR declaration that lists an IP address that is valid
+for the subnet or shared network on which the client is booting. If
+it doesn't find any such entry, it tries to find an entry which has
+no \fIfixed-address\fR declaration.
+.SH EXAMPLES
+.PP
+A typical dhcpd.conf file will look something like this:
+.nf
+
+.I global parameters...
+
+subnet 204.254.239.0 netmask 255.255.255.224 {
+ \fIsubnet-specific parameters...\fR
+ range 204.254.239.10 204.254.239.30;
+}
+
+subnet 204.254.239.32 netmask 255.255.255.224 {
+ \fIsubnet-specific parameters...\fR
+ range 204.254.239.42 204.254.239.62;
+}
+
+subnet 204.254.239.64 netmask 255.255.255.224 {
+ \fIsubnet-specific parameters...\fR
+ range 204.254.239.74 204.254.239.94;
+}
+
+group {
+ \fIgroup-specific parameters...\fR
+ host zappo.test.isc.org {
+ \fIhost-specific parameters...\fR
+ }
+ host beppo.test.isc.org {
+ \fIhost-specific parameters...\fR
+ }
+ host harpo.test.isc.org {
+ \fIhost-specific parameters...\fR
+ }
+}
+
+.ce 1
+Figure 1
+
+.fi
+.PP
+Notice that at the beginning of the file, there's a place
+for global parameters. These might be things like the organization's
+domain name, the addresses of the name servers (if they are common to
+the entire organization), and so on. So, for example:
+.nf
+
+ option domain-name "isc.org";
+ option domain-name-servers ns1.isc.org, ns2.isc.org;
+
+.ce 1
+Figure 2
+.fi
+.PP
+As you can see in Figure 2, you can specify host addresses in
+parameters using their domain names rather than their numeric IP
+addresses. If a given hostname resolves to more than one IP address
+(for example, if that host has two ethernet interfaces), then where
+possible, both addresses are supplied to the client.
+.PP
+The most obvious reason for having subnet-specific parameters as
+shown in Figure 1 is that each subnet, of necessity, has its own
+router. So for the first subnet, for example, there should be
+something like:
+.nf
+
+ option routers 204.254.239.1;
+.fi
+.PP
+Note that the address here is specified numerically. This is not
+required - if you have a different domain name for each interface on
+your router, it's perfectly legitimate to use the domain name for that
+interface instead of the numeric address. However, in many cases
+there may be only one domain name for all of a router's IP addresses, and
+it would not be appropriate to use that name here.
+.PP
+In Figure 1 there is also a \fIgroup\fR statement, which provides
+common parameters for a set of three hosts - zappo, beppo and harpo.
+As you can see, these hosts are all in the test.isc.org domain, so it
+might make sense for a group-specific parameter to override the domain
+name supplied to these hosts:
+.nf
+
+ option domain-name "test.isc.org";
+.fi
+.PP
+Also, given the domain they're in, these are probably test machines.
+If we wanted to test the DHCP leasing mechanism, we might set the
+lease timeout somewhat shorter than the default:
+
+.nf
+ max-lease-time 120;
+ default-lease-time 120;
+.fi
+.PP
+You may have noticed that while some parameters start with the
+\fIoption\fR keyword, some do not. Parameters starting with the
+\fIoption\fR keyword correspond to actual DHCP options, while
+parameters that do not start with the option keyword either control
+the behavior of the DHCP server (e.g., how long a lease dhcpd will
+give out), or specify client parameters that are not optional in the
+DHCP protocol (for example, server-name and filename).
+.PP
+In Figure 1, each host had \fIhost-specific parameters\fR. These
+could include such things as the \fIhostname\fR option, the name of a
+file to upload (the \fIfilename\fR parameter) and the address of the
+server from which to upload the file (the \fInext-server\fR
+parameter). In general, any parameter can appear anywhere that
+parameters are allowed, and will be applied according to the scope in
+which the parameter appears.
+.PP
+Imagine that you have a site with a lot of NCD X-Terminals. These
+terminals come in a variety of models, and you want to specify the
+boot files for each model. One way to do this would be to have host
+declarations for each server and group them by model:
+.nf
+
+group {
+ filename "Xncd19r";
+ next-server ncd-booter;
+
+ host ncd1 { hardware ethernet 0:c0:c3:49:2b:57; }
+ host ncd4 { hardware ethernet 0:c0:c3:80:fc:32; }
+ host ncd8 { hardware ethernet 0:c0:c3:22:46:81; }
+}
+
+group {
+ filename "Xncd19c";
+ next-server ncd-booter;
+
+ host ncd2 { hardware ethernet 0:c0:c3:88:2d:81; }
+ host ncd3 { hardware ethernet 0:c0:c3:00:14:11; }
+}
+
+group {
+ filename "XncdHMX";
+ next-server ncd-booter;
+
+ host ncd1 { hardware ethernet 0:c0:c3:11:90:23; }
+ host ncd4 { hardware ethernet 0:c0:c3:91:a7:8; }
+ host ncd8 { hardware ethernet 0:c0:c3:cc:a:8f; }
+}
+.fi
+.SH ADDRESS POOLS
+.PP
+The
+.B pool
+declaration can be used to specify a pool of addresses that will be
+treated differently than another pool of addresses, even on the same
+network segment or subnet. For example, you may want to provide a
+large set of addresses that can be assigned to DHCP clients that are
+registered to your DHCP server, while providing a smaller set of
+addresses, possibly with short lease times, that are available for
+unknown clients. If you have a firewall, you may be able to arrange
+for addresses from one pool to be allowed access to the Internet,
+while addresses in another pool are not, thus encouraging users to
+register their DHCP clients. To do this, you would set up a pair of
+pool declarations:
+.PP
+.nf
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ option routers 10.0.0.254;
+
+ # Unknown clients get this pool.
+ pool {
+ option domain-name-servers bogus.example.com;
+ max-lease-time 300;
+ range 10.0.0.200 10.0.0.253;
+ allow unknown-clients;
+ }
+
+ # Known clients get this pool.
+ pool {
+ option domain-name-servers ns1.example.com, ns2.example.com;
+ max-lease-time 28800;
+ range 10.0.0.5 10.0.0.199;
+ deny unknown-clients;
+ }
+}
+.fi
+.PP
+It is also possible to set up entirely different subnets for known and
+unknown clients - address pools exist at the level of shared networks,
+so address ranges within pool declarations can be on different
+subnets.
+.PP
+As you can see in the preceding example, pools can have permit lists
+that control which clients are allowed access to the pool and which
+aren't. Each entry in a pool's permit list is introduced with the
+.I allow
+or \fIdeny\fR keyword. If a pool has a permit list, then only those
+clients that match specific entries on the permit list will be
+eligible to be assigned addresses from the pool. If a pool has a
+deny list, then only those clients that do not match any entries on
+the deny list will be eligible. If both permit and deny lists exist
+for a pool, then only clients that match the permit list and do not
+match the deny list will be allowed access.
+.SH DYNAMIC ADDRESS ALLOCATION
+Address allocation is actually only done when a client is in the INIT
+state and has sent a DHCPDISCOVER message. If the client thinks it
+has a valid lease and sends a DHCPREQUEST to initiate or renew that
+lease, the server has only three choices - it can ignore the
+DHCPREQUEST, send a DHCPNAK to tell the client it should stop using
+the address, or send a DHCPACK, telling the client to go ahead and use
+the address for a while.
+.PP
+If the server finds the address the client is requesting, and that
+address is available to the client, the server will send a DHCPACK.
+If the address is no longer available, or the client isn't permitted
+to have it, the server will send a DHCPNAK. If the server knows
+nothing about the address, it will remain silent, unless the address
+is incorrect for the network segment to which the client has been
+attached and the server is authoritative for that network segment, in
+which case the server will send a DHCPNAK even though it doesn't know
+about the address.
+.PP
+There may be a host declaration matching the client's identification.
+If that host declaration contains a fixed-address declaration that
+lists an IP address that is valid for the network segment to which the
+client is connected. In this case, the DHCP server will never do
+dynamic address allocation. In this case, the client is \fIrequired\fR
+to take the address specified in the host declaration. If the
+client sends a DHCPREQUEST for some other address, the server will respond
+with a DHCPNAK.
+.PP
+When the DHCP server allocates a new address for a client (remember,
+this only happens if the client has sent a DHCPDISCOVER), it first
+looks to see if the client already has a valid lease on an IP address,
+or if there is an old IP address the client had before that hasn't yet
+been reassigned. In that case, the server will take that address and
+check it to see if the client is still permitted to use it. If the
+client is no longer permitted to use it, the lease is freed if the
+server thought it was still in use - the fact that the client has sent
+a DHCPDISCOVER proves to the server that the client is no longer using
+the lease.
+.PP
+If no existing lease is found, or if the client is forbidden to
+receive the existing lease, then the server will look in the list of
+address pools for the network segment to which the client is attached
+for a lease that is not in use and that the client is permitted to
+have. It looks through each pool declaration in sequence (all
+.I range
+declarations that appear outside of pool declarations are grouped into
+a single pool with no permit list). If the permit list for the pool
+allows the client to be allocated an address from that pool, the pool
+is examined to see if there is an address available. If so, then the
+client is tentatively assigned that address. Otherwise, the next
+pool is tested. If no addresses are found that can be assigned to
+the client, no response is sent to the client.
+.PP
+If an address is found that the client is permitted to have, and that
+has never been assigned to any client before, the address is
+immediately allocated to the client. If the address is available for
+allocation but has been previously assigned to a different client, the
+server will keep looking in hopes of finding an address that has never
+before been assigned to a client.
+.PP
+The DHCP server generates the list of available IP addresses from a
+hash table. This means that the addresses are not sorted in any
+particular order, and so it is not possible to predict the order in
+which the DHCP server will allocate IP addresses. Users of previous
+versions of the ISC DHCP server may have become accustomed to the DHCP
+server allocating IP addresses in ascending order, but this is no
+longer possible, and there is no way to configure this behavior with
+version 3 of the ISC DHCP server.
+.SH IP ADDRESS CONFLICT PREVENTION
+The DHCP server checks IP addresses to see if they are in use before
+allocating them to clients. It does this by sending an ICMP Echo
+request message to the IP address being allocated. If no ICMP Echo
+reply is received within a second, the address is assumed to be free.
+This is only done for leases that have been specified in range
+statements, and only when the lease is thought by the DHCP server to
+be free - i.e., the DHCP server or its failover peer has not listed
+the lease as in use.
+.PP
+If a response is received to an ICMP Echo request, the DHCP server
+assumes that there is a configuration error - the IP address is in use
+by some host on the network that is not a DHCP client. It marks the
+address as abandoned, and will not assign it to clients.
+.PP
+If a DHCP client tries to get an IP address, but none are available,
+but there are abandoned IP addresses, then the DHCP server will
+attempt to reclaim an abandoned IP address. It marks one IP address
+as free, and then does the same ICMP Echo request check described
+previously. If there is no answer to the ICMP Echo request, the
+address is assigned to the client.
+.PP
+The DHCP server does not cycle through abandoned IP addresses if the
+first IP address it tries to reclaim is free. Rather, when the next
+DHCPDISCOVER comes in from the client, it will attempt a new
+allocation using the same method described here, and will typically
+try a new IP address.
+.SH DHCP FAILOVER
+This version of the ISC DHCP server supports the DHCP failover
+protocol as documented in draft-ietf-dhc-failover-12.txt. This is
+not a final protocol document, and we have not done interoperability
+testing with other vendors' implementations of this protocol, so you
+must not assume that this implementation conforms to the standard.
+If you wish to use the failover protocol, make sure that both failover
+peers are running the same version of the ISC DHCP server.
+.PP
+The failover protocol allows two DHCP servers (and no more than two)
+to share a common address pool. Each server will have about half of
+the available IP addresses in the pool at any given time for
+allocation. If one server fails, the other server will continue to
+renew leases out of the pool, and will allocate new addresses out of
+the roughly half of available addresses that it had when
+communications with the other server were lost.
+.PP
+It is possible during a prolonged failure to tell the remaining server
+that the other server is down, in which case the remaining server will
+(over time) reclaim all the addresses the other server had available
+for allocation, and begin to reuse them. This is called putting the
+server into the PARTNER-DOWN state.
+.PP
+You can put the server into the PARTNER-DOWN state either by using the
+.B omshell (1)
+command or by stopping the server, editing the last failover state
+declaration in the lease file, and restarting the server. If you use
+this last method, change the "my state" line to:
+.PP
+.nf
+.B failover peer "\fIname\fB" state {
+.B my state partner-down;
+.B peer state \fIstate\fB at \fIdate\fB;
+.B }
+.fi
+.PP
+It is only required to change "my state" as shown above.
+.PP
+When the other server comes back online, it should automatically
+detect that it has been offline and request a complete update from the
+server that was running in the PARTNER-DOWN state, and then both
+servers will resume processing together.
+.PP
+It is possible to get into a dangerous situation: if you put one
+server into the PARTNER-DOWN state, and then *that* server goes down,
+and the other server comes back up, the other server will not know
+that the first server was in the PARTNER-DOWN state, and may issue
+addresses previously issued by the other server to different clients,
+resulting in IP address conflicts. Before putting a server into
+PARTNER-DOWN state, therefore, make
+.I sure
+that the other server will not restart automatically.
+.PP
+The failover protocol defines a primary server role and a secondary
+server role. There are some differences in how primaries and
+secondaries act, but most of the differences simply have to do with
+providing a way for each peer to behave in the opposite way from the
+other. So one server must be configured as primary, and the other
+must be configured as secondary, and it doesn't matter too much which
+one is which.
+.SH FAILOVER STARTUP
+When a server starts that has not previously communicated with its
+failover peer, it must establish communications with its failover peer
+and synchronize with it before it can serve clients. This can happen
+either because you have just configured your DHCP servers to perform
+failover for the first time, or because one of your failover servers
+has failed catastrophically and lost its database.
+.PP
+The initial recovery process is designed to ensure that when one
+failover peer loses its database and then resynchronizes, any leases
+that the failed server gave out before it failed will be honored.
+When the failed server starts up, it notices that it has no saved
+failover state, and attempts to contact its peer.
+.PP
+When it has established contact, it asks the peer for a complete copy
+its peer's lease database. The peer then sends its complete database,
+and sends a message indicating that it is done. The failed server
+then waits until MCLT has passed, and once MCLT has passed both
+servers make the transition back into normal operation. This waiting
+period ensures that any leases the failed server may have given out
+while out of contact with its partner will have expired.
+.PP
+While the failed server is recovering, its partner remains in the
+partner-down state, which means that it is serving all clients. The
+failed server provides no service at all to DHCP clients until it has
+made the transition into normal operation.
+.PP
+In the case where both servers detect that they have never before
+communicated with their partner, they both come up in this recovery
+state and follow the procedure we have just described. In this case,
+no service will be provided to DHCP clients until MCLT has expired.
+.SH CONFIGURING FAILOVER
+In order to configure failover, you need to write a peer declaration
+that configures the failover protocol, and you need to write peer
+references in each pool declaration for which you want to do
+failover. You do not have to do failover for all pools on a given
+network segment. You must not tell one server it's doing failover
+on a particular address pool and tell the other it is not. You must
+not have any common address pools on which you are not doing
+failover. A pool declaration that utilizes failover would look like this:
+.PP
+.nf
+pool {
+ failover peer "foo";
+ \fIpool specific parameters\fR
+};
+.fi
+.PP
+The server currently does very little sanity checking, so if you
+configure it wrong, it will just fail in odd ways. I would recommend
+therefore that you either do failover or don't do failover, but don't
+do any mixed pools. Also, use the same master configuration file for
+both servers, and have a separate file that contains the peer
+declaration and includes the master file. This will help you to avoid
+configuration mismatches. As our implementation evolves, this will
+become less of a problem. A basic sample dhcpd.conf file for a
+primary server might look like this:
+.PP
+.nf
+failover peer "foo" {
+ primary;
+ address anthrax.rc.vix.com;
+ port 519;
+ peer address trantor.rc.vix.com;
+ peer port 520;
+ max-response-delay 60;
+ max-unacked-updates 10;
+ mclt 3600;
+ split 128;
+ load balance max seconds 3;
+}
+
+include "/etc/dhcpd.master";
+.fi
+.PP
+The statements in the peer declaration are as follows:
+.PP
+The
+.I primary
+and
+.I secondary
+statements
+.RS 0.25i
+.PP
+[ \fBprimary\fR | \fBsecondary\fR ]\fB;\fR
+.PP
+This determines whether the server is primary or secondary, as
+described earlier under DHCP FAILOVER.
+.RE
+.PP
+The
+.I address
+statement
+.RS 0.25i
+.PP
+.B address \fIaddress\fR\fB;\fR
+.PP
+The \fBaddress\fR statement declares the IP address or DNS name on which the
+server should listen for connections from its failover peer, and also the
+value to use for the DHCP Failover Protocol server identifier. Because this
+value is used as an identifier, it may not be omitted.
+.RE
+.PP
+The
+.I peer address
+statement
+.RS 0.25i
+.PP
+.B peer address \fIaddress\fR\fB;\fR
+.PP
+The \fBpeer address\fR statement declares the IP address or DNS name to
+which the server should connect to reach its failover peer for failover
+messages.
+.RE
+.PP
+The
+.I port
+statement
+.RS 0.25i
+.PP
+.B port \fIport-number\fR\fB;\fR
+.PP
+The \fBport\fR statement declares the TCP port on which the server
+should listen for connections from its failover peer. This statement
+may be omitted, in which case the IANA assigned port number 647 will be
+used by default.
+.RE
+.PP
+The
+.I peer port
+statement
+.RS 0.25i
+.PP
+.B peer port \fIport-number\fR\fB;\fR
+.PP
+The \fBpeer port\fR statement declares the TCP port to which the
+server should connect to reach its failover peer for failover
+messages. This statement may be omitted, in which case the IANA
+assigned port number 647 will be used by default.
+.RE
+.PP
+The
+.I max-response-delay
+statement
+.RS 0.25i
+.PP
+.B max-response-delay \fIseconds\fR\fB;\fR
+.PP
+The \fBmax-response-delay\fR statement tells the DHCP server how
+many seconds may pass without receiving a message from its failover
+peer before it assumes that connection has failed. This number
+should be small enough that a transient network failure that breaks
+the connection will not result in the servers being out of
+communication for a long time, but large enough that the server isn't
+constantly making and breaking connections. This parameter must be
+specified.
+.RE
+.PP
+The
+.I max-unacked-updates
+statement
+.RS 0.25i
+.PP
+.B max-unacked-updates \fIcount\fR\fB;\fR
+.PP
+The \fBmax-unacked-updates\fR statement tells the remote DHCP server how
+many BNDUPD messages it can send before it receives a BNDACK
+from the local system. We don't have enough operational experience
+to say what a good value for this is, but 10 seems to work. This
+parameter must be specified.
+.RE
+.PP
+The
+.I mclt
+statement
+.RS 0.25i
+.PP
+.B mclt \fIseconds\fR\fB;\fR
+.PP
+The \fBmclt\fR statement defines the Maximum Client Lead Time. It
+must be specified on the primary, and may not be specified on the
+secondary. This is the length of time for which a lease may be
+renewed by either failover peer without contacting the other. The
+longer you set this, the longer it will take for the running server to
+recover IP addresses after moving into PARTNER-DOWN state. The
+shorter you set it, the more load your servers will experience when
+they are not communicating. A value of something like 3600 is
+probably reasonable, but again bear in mind that we have no real
+operational experience with this.
+.RE
+.PP
+The
+.I split
+statement
+.RS 0.25i
+.PP
+.B split \fIindex\fR\fB;\fR
+.PP
+The split statement specifies the split between the primary and
+secondary for the purposes of load balancing. Whenever a client
+makes a DHCP request, the DHCP server runs a hash on the client
+identification, resulting in value from 0 to 255. This is used as
+an index into a 256 bit field. If the bit at that index is set,
+the primary is responsible. If the bit at that index is not set,
+the secondary is responsible. The \fBsplit\fR value determines
+how many of the leading bits are set to one. So, in practice, higher
+split values will cause the primary to serve more clients than the
+secondary. Lower split values, the converse. Legal values are between
+0 and 255, of which the most reasonable is 128.
+.RE
+.PP
+The
+.I hba
+statement
+.RS 0.25i
+.PP
+.B hba \fIcolon-separated-hex-list\fB;\fR
+.PP
+The hba statement specifies the split between the primary and
+secondary as a bitmap rather than a cutoff, which theoretically allows
+for finer-grained control. In practice, there is probably no need
+for such fine-grained control, however. An example hba statement:
+.PP
+.nf
+ hba ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
+ 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00;
+.fi
+.PP
+This is equivalent to a \fBsplit 128;\fR statement, and identical. The
+following two examples are also equivalent to a \fBsplit\fR of 128, but
+are not identical:
+.PP
+.nf
+ hba aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:
+ aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa:aa;
+
+ hba 55:55:55:55:55:55:55:55:55:55:55:55:55:55:55:55:
+ 55:55:55:55:55:55:55:55:55:55:55:55:55:55:55:55;
+.fi
+.PP
+They are equivalent, because half the bits are set to 0, half are set to
+1 (0xa and 0x5 are 1010 and 0101 binary respectively) and consequently this
+would roughly divide the clients equally between the servers. They are not
+identical, because the actual peers this would load balance to each server
+are different for each example.
+.PP
+You must only have \fBsplit\fR or \fBhba\fR defined, never both. For most
+cases, the fine-grained control that \fBhba\fR offers isn't necessary, and
+\fBsplit\fR should be used.
+.RE
+.PP
+The
+.I load balance max seconds
+statement
+.RS 0.25i
+.PP
+.B load balance max seconds \fIseconds\fR\fB;\fR
+.PP
+This statement allows you to configure a cutoff after which load
+balancing is disabled. The cutoff is based on the number of seconds
+since the client sent its first DHCPDISCOVER or DHCPREQUEST message,
+and only works with clients that correctly implement the \fIsecs\fR
+field - fortunately most clients do. We recommend setting this to
+something like 3 or 5. The effect of this is that if one of the
+failover peers gets into a state where it is responding to failover
+messages but not responding to some client requests, the other
+failover peer will take over its client load automatically as the
+clients retry.
+.RE
+.PP
+The
+.I auto-partner-down
+statement
+.RS 0.25i
+.PP
+.B auto-partner-down \fIseconds\fR\fB;\fR
+.PP
+This statement instructs the server to initiate a timed delay upon entering
+the communications-interrupted state (any situation of being out-of-contact
+with the remote failover peer). At the conclusion of the timer, the server
+will automatically enter the partner-down state. This permits the server
+to allocate leases from the partner's free lease pool after an STOS+MCLT
+timer expires, which can be dangerous if the partner is in fact operating
+at the time (the two servers will give conflicting bindings).
+.PP
+Think very carefully before enabling this feature. The partner-down and
+communications-interrupted states are intentionally segregated because
+there do exist situations where a failover server can fail to communicate
+with its peer, but still has the ability to receive and reply to requests
+from DHCP clients. In general, this feature should only be used in those
+deployments where the failover servers are directly connected to one
+another, such as by a dedicated hardwired link ("a heartbeat cable").
+.PP
+A zero value disables the auto-partner-down feature (also the default), and
+any positive value indicates the time in seconds to wait before automatically
+entering partner-down.
+.RE
+.PP
+The Failover pool balance statements.
+.RS 0.25i
+.PP
+ \fBmax-lease-misbalance \fIpercentage\fR\fB;\fR
+ \fBmax-lease-ownership \fIpercentage\fR\fB;\fR
+ \fBmin-balance \fIseconds\fR\fB;\fR
+ \fBmax-balance \fIseconds\fR\fB;\fR
+.PP
+This version of the DHCP Server evaluates pool balance on a schedule,
+rather than on demand as leases are allocated. The latter approach
+proved to be slightly klunky when pool misbalanced reach total
+saturation...when any server ran out of leases to assign, it also lost
+its ability to notice it had run dry.
+.PP
+In order to understand pool balance, some elements of its operation
+first need to be defined. First, there are \'free\' and \'backup\' leases.
+Both of these are referred to as \'free state leases\'. \'free\' and
+\'backup\'
+are \'the free states\' for the purpose of this document. The difference
+is that only the primary may allocate from \'free\' leases unless under
+special circumstances, and only the secondary may allocate \'backup\' leases.
+.PP
+When pool balance is performed, the only plausible expectation is to
+provide a 50/50 split of the free state leases between the two servers.
+This is because no one can predict which server will fail, regardless
+of the relative load placed upon the two servers, so giving each server
+half the leases gives both servers the same amount of \'failure endurance\'.
+Therefore, there is no way to configure any different behaviour, outside of
+some very small windows we will describe shortly.
+.PP
+The first thing calculated on any pool balance run is a value referred to
+as \'lts\', or "Leases To Send". This, simply, is the difference in the
+count of free and backup leases, divided by two. For the secondary,
+it is the difference in the backup and free leases, divided by two.
+The resulting value is signed: if it is positive, the local server is
+expected to hand out leases to retain a 50/50 balance. If it is negative,
+the remote server would need to send leases to balance the pool. Once
+the lts value reaches zero, the pool is perfectly balanced (give or take
+one lease in the case of an odd number of total free state leases).
+.PP
+The current approach is still something of a hybrid of the old approach,
+marked by the presence of the \fBmax-lease-misbalance\fR statement. This
+parameter configures what used to be a 10% fixed value in previous versions:
+if lts is less than free+backup * \fBmax-lease-misbalance\fR percent, then
+the server will skip balancing a given pool (it won't bother moving any
+leases, even if some leases "should" be moved). The meaning of this value
+is also somewhat overloaded, however, in that it also governs the estimation
+of when to attempt to balance the pool (which may then also be skipped over).
+The oldest leases in the free and backup states are examined. The time
+they have resided in their respective queues is used as an estimate to
+indicate how much time it is probable it would take before the leases at
+the top of the list would be consumed (and thus, how long it would take
+to use all leases in that state). This percentage is directly multiplied
+by this time, and fit into the schedule if it falls within
+the \fBmin-balance\fR and \fBmax-balance\fR configured values. The
+scheduled pool check time is only moved in a downwards direction, it is
+never increased. Lastly, if the lts is more than double this number in
+the negative direction, the local server will \'panic\' and transmit a
+Failover protocol POOLREQ message, in the hopes that the remote system
+will be woken up into action.
+.PP
+Once the lts value exceeds the \fBmax-lease-misbalance\fR percentage of
+total free state leases as described above, leases are moved to the remote
+server. This is done in two passes.
+.PP
+In the first pass, only leases whose most recent bound client would have
+been served by the remote server - according to the Load Balance Algorithm
+(see above \fBsplit\fR and \fBhba\fR configuration statements) - are given
+away to the peer. This first pass will happily continue to give away leases,
+decrementing the lts value by one for each, until the lts value has reached
+the negative of the total number of leases multiplied by
+the \fBmax-lease-ownership\fR percentage. So it is through this value that
+you can permit a small misbalance of the lease pools - for the purpose of
+giving the peer more than a 50/50 share of leases in the hopes that their
+clients might some day return and be allocated by the peer (operating
+normally). This process is referred to as \'MAC Address Affinity\', but this
+is somewhat misnamed: it applies equally to DHCP Client Identifier options.
+Note also that affinity is applied to leases when they enter the state
+\'free\' from \'expired\' or \'released\'. In this case also, leases will not
+be moved from free to backup if the secondary already has more than its
+share.
+.PP
+The second pass is only entered into if the first pass fails to reduce
+the lts underneath the total number of free state leases multiplied by
+the \fBmax-lease-ownership\fR percentage. In this pass, the oldest
+leases are given over to the peer without second thought about the Load
+Balance Algorithm, and this continues until the lts falls under this
+value. In this way, the local server will also happily keep a small
+percentage of the leases that would normally load balance to itself.
+.PP
+So, the \fBmax-lease-misbalance\fR value acts as a behavioural gate.
+Smaller values will cause more leases to transition states to balance
+the pools over time, higher values will decrease the amount of change
+(but may lead to pool starvation if there's a run on leases).
+.PP
+The \fBmax-lease-ownership\fR value permits a small (percentage) skew
+in the lease balance of a percentage of the total number of free state
+leases.
+.PP
+Finally, the \fBmin-balance\fR and \fBmax-balance\fR make certain that a
+scheduled rebalance event happens within a reasonable timeframe (not
+to be thrown off by, for example, a 7 year old free lease).
+.PP
+Plausible values for the percentages lie between 0 and 100, inclusive, but
+values over 50 are indistinguishable from one another (once lts exceeds
+50% of the free state leases, one server must therefore have 100% of the
+leases in its respective free state). It is recommended to select
+a \fBmax-lease-ownership\fR value that is lower than the value selected
+for the \fBmax-lease-misbalance\fR value. \fBmax-lease-ownership\fR
+defaults to 10, and \fBmax-lease-misbalance\fR defaults to 15.
+.PP
+Plausible values for the \fBmin-balance\fR and \fBmax-balance\fR times also
+range from 0 to (2^32)-1 (or the limit of your local time_t value), but
+default to values 60 and 3600 respectively (to place balance events between
+1 minute and 1 hour).
+.RE
+.SH CLIENT CLASSING
+Clients can be separated into classes, and treated differently
+depending on what class they are in. This separation can be done
+either with a conditional statement, or with a match statement within
+the class declaration. It is possible to specify a limit on the
+total number of clients within a particular class or subclass that may
+hold leases at one time, and it is possible to specify automatic
+subclassing based on the contents of the client packet.
+.PP
+To add clients to classes based on conditional evaluation, you can
+specify a matching expression in the class statement:
+.PP
+.nf
+class "ras-clients" {
+ match if substring (option dhcp-client-identifier, 1, 3) = "RAS";
+}
+.fi
+.PP
+Note that whether you use matching expressions or add statements (or
+both) to classify clients, you must always write a class declaration
+for any class that you use. If there will be no match statement and
+no in-scope statements for a class, the declaration should look like
+this:
+.PP
+.nf
+class "ras-clients" {
+}
+.fi
+.SH SUBCLASSES
+.PP
+In addition to classes, it is possible to declare subclasses. A
+subclass is a class with the same name as a regular class, but with a
+specific submatch expression which is hashed for quick matching.
+This is essentially a speed hack - the main difference between five
+classes with match expressions and one class with five subclasses is
+that it will be quicker to find the subclasses. Subclasses work as
+follows:
+.PP
+.nf
+class "allocation-class-1" {
+ match pick-first-value (option dhcp-client-identifier, hardware);
+}
+
+class "allocation-class-2" {
+ match pick-first-value (option dhcp-client-identifier, hardware);
+}
+
+subclass "allocation-class-1" 1:8:0:2b:4c:39:ad;
+subclass "allocation-class-2" 1:8:0:2b:a9:cc:e3;
+subclass "allocation-class-1" 1:0:0:c4:aa:29:44;
+
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ pool {
+ allow members of "allocation-class-1";
+ range 10.0.0.11 10.0.0.50;
+ }
+ pool {
+ allow members of "allocation-class-2";
+ range 10.0.0.51 10.0.0.100;
+ }
+}
+.fi
+.PP
+The data following the class name in the subclass declaration is a
+constant value to use in matching the match expression for the class.
+When class matching is done, the server will evaluate the match
+expression and then look the result up in the hash table. If it
+finds a match, the client is considered a member of both the class and
+the subclass.
+.PP
+Subclasses can be declared with or without scope. In the above
+example, the sole purpose of the subclass is to allow some clients
+access to one address pool, while other clients are given access to
+the other pool, so these subclasses are declared without scopes. If
+part of the purpose of the subclass were to define different parameter
+values for some clients, you might want to declare some subclasses
+with scopes.
+.PP
+In the above example, if you had a single client that needed some
+configuration parameters, while most didn't, you might write the
+following subclass declaration for that client:
+.PP
+.nf
+subclass "allocation-class-2" 1:08:00:2b:a1:11:31 {
+ option root-path "samsara:/var/diskless/alphapc";
+ filename "/tftpboot/netbsd.alphapc-diskless";
+}
+.fi
+.PP
+In this example, we've used subclassing as a way to control address
+allocation on a per-client basis. However, it's also possible to use
+subclassing in ways that are not specific to clients - for example, to
+use the value of the vendor-class-identifier option to determine what
+values to send in the vendor-encapsulated-options option. An example
+of this is shown under the VENDOR ENCAPSULATED OPTIONS head in the
+.B dhcp-options(5)
+manual page.
+.SH PER-CLASS LIMITS ON DYNAMIC ADDRESS ALLOCATION
+.PP
+You may specify a limit to the number of clients in a class that can
+be assigned leases. The effect of this will be to make it difficult
+for a new client in a class to get an address. Once a class with
+such a limit has reached its limit, the only way a new client in that
+class can get a lease is for an existing client to relinquish its
+lease, either by letting it expire, or by sending a DHCPRELEASE
+packet. Classes with lease limits are specified as follows:
+.PP
+.nf
+class "limited-1" {
+ lease limit 4;
+}
+.fi
+.PP
+This will produce a class in which a maximum of four members may hold
+a lease at one time.
+.SH SPAWNING CLASSES
+.PP
+It is possible to declare a
+.I spawning class\fR.
+A spawning class is a class that automatically produces subclasses
+based on what the client sends. The reason that spawning classes
+were created was to make it possible to create lease-limited classes
+on the fly. The envisioned application is a cable-modem environment
+where the ISP wishes to provide clients at a particular site with more
+than one IP address, but does not wish to provide such clients with
+their own subnet, nor give them an unlimited number of IP addresses
+from the network segment to which they are connected.
+.PP
+Many cable modem head-end systems can be configured to add a Relay
+Agent Information option to DHCP packets when relaying them to the
+DHCP server. These systems typically add a circuit ID or remote ID
+option that uniquely identifies the customer site. To take advantage
+of this, you can write a class declaration as follows:
+.PP
+.nf
+class "customer" {
+ spawn with option agent.circuit-id;
+ lease limit 4;
+}
+.fi
+.PP
+Now whenever a request comes in from a customer site, the circuit ID
+option will be checked against the class's hash table. If a subclass
+is found that matches the circuit ID, the client will be classified in
+that subclass and treated accordingly. If no subclass is found
+matching the circuit ID, a new one will be created and logged in the
+.B dhcpd.leases
+file, and the client will be classified in this new class. Once the
+client has been classified, it will be treated according to the rules
+of the class, including, in this case, being subject to the per-site
+limit of four leases.
+.PP
+The use of the subclass spawning mechanism is not restricted to relay
+agent options - this particular example is given only because it is a
+fairly straightforward one.
+.SH COMBINING MATCH, MATCH IF AND SPAWN WITH
+.PP
+In some cases, it may be useful to use one expression to assign a
+client to a particular class, and a second expression to put it into a
+subclass of that class. This can be done by combining the \fBmatch
+if\fR and \fBspawn with\fR statements, or the \fBmatch if\fR and
+\fBmatch\fR statements. For example:
+.PP
+.nf
+class "jr-cable-modems" {
+ match if option dhcp-vendor-identifier = "jrcm";
+ spawn with option agent.circuit-id;
+ lease limit 4;
+}
+
+class "dv-dsl-modems" {
+ match if option dhcp-vendor-identifier = "dvdsl";
+ spawn with option agent.circuit-id;
+ lease limit 16;
+}
+.fi
+.PP
+This allows you to have two classes that both have the same \fBspawn
+with\fR expression without getting the clients in the two classes
+confused with each other.
+.SH DYNAMIC DNS UPDATES
+.PP
+The DHCP server has the ability to dynamically update the Domain Name
+System. Within the configuration files, you can define how you want
+the Domain Name System to be updated. These updates are RFC 2136
+compliant so any DNS server supporting RFC 2136 should be able to
+accept updates from the DHCP server.
+.PP
+Two DNS update schemes are currently implemented, and another is
+planned. The two that are currently implemented are the ad-hoc DNS
+update mode and the interim DHCP-DNS interaction draft update mode.
+In the future we plan to add a third mode which will be the standard
+DNS update method based on the RFCS for DHCP-DNS interaction and DHCID
+The DHCP server must be configured to use one of the two
+currently-supported methods, or not to do dns updates.
+This can be done with the
+.I ddns-update-style
+configuration parameter.
+.SH THE AD-HOC DNS UPDATE SCHEME
+The ad-hoc Dynamic DNS update scheme is
+.B now deprecated
+and
+.B
+does not work.
+In future releases of the ISC DHCP server, this scheme will not likely be
+available. The interim scheme works, allows for failover, and should now be
+used. The following description is left here for informational purposes
+only.
+.PP
+The ad-hoc Dynamic DNS update scheme implemented in this version of
+the ISC DHCP server is a prototype design, which does not
+have much to do with the standard update method that is being
+standardized in the IETF DHC working group, but rather implements some
+very basic, yet useful, update capabilities. This mode
+.B does not work
+with the
+.I failover protocol
+because it does not account for the possibility of two different DHCP
+servers updating the same set of DNS records.
+.PP
+For the ad-hoc DNS update method, the client's FQDN is derived in two
+parts. First, the hostname is determined. Then, the domain name is
+determined, and appended to the hostname.
+.PP
+The DHCP server determines the client's hostname by first looking for
+a \fIddns-hostname\fR configuration option, and using that if it is
+present. If no such option is present, the server looks for a
+valid hostname in the FQDN option sent by the client. If one is
+found, it is used; otherwise, if the client sent a host-name option,
+that is used. Otherwise, if there is a host declaration that applies
+to the client, the name from that declaration will be used. If none
+of these applies, the server will not have a hostname for the client,
+and will not be able to do a DNS update.
+.PP
+The domain name is determined from the
+.I ddns-domainname
+configuration option. The default configuration for this option is:
+.nf
+.sp 1
+ option server.ddns-domainname = config-option domain-name;
+
+.fi
+So if this configuration option is not configured to a different
+value (over-riding the above default), or if a domain-name option
+has not been configured for the client's scope, then the server will
+not attempt to perform a DNS update.
+.PP
+The client's fully-qualified domain name, derived as we have
+described, is used as the name on which an "A" record will be stored.
+The A record will contain the IP address that the client was assigned
+in its lease. If there is already an A record with the same name in
+the DNS server, no update of either the A or PTR records will occur -
+this prevents a client from claiming that its hostname is the name of
+some network server. For example, if you have a fileserver called
+"fs.sneedville.edu", and the client claims its hostname is "fs", no
+DNS update will be done for that client, and an error message will be
+logged.
+.PP
+If the A record update succeeds, a PTR record update for the assigned
+IP address will be done, pointing to the A record. This update is
+unconditional - it will be done even if another PTR record of the same
+name exists. Since the IP address has been assigned to the DHCP
+server, this should be safe.
+.PP
+Please note that the current implementation assumes clients only have
+a single network interface. A client with two network interfaces
+will see unpredictable behavior. This is considered a bug, and will
+be fixed in a later release. It may be helpful to enable the
+.I one-lease-per-client
+parameter so that roaming clients do not trigger this same behavior.
+.PP
+The DHCP protocol normally involves a four-packet exchange - first the
+client sends a DHCPDISCOVER message, then the server sends a
+DHCPOFFER, then the client sends a DHCPREQUEST, then the server sends
+a DHCPACK. In the current version of the server, the server will do
+a DNS update after it has received the DHCPREQUEST, and before it has
+sent the DHCPACK. It only sends the DNS update if it has not sent
+one for the client's address before, in order to minimize the impact
+on the DHCP server.
+.PP
+When the client's lease expires, the DHCP server (if it is operating
+at the time, or when next it operates) will remove the client's A and
+PTR records from the DNS database. If the client releases its lease
+by sending a DHCPRELEASE message, the server will likewise remove the
+A and PTR records.
+.SH THE INTERIM DNS UPDATE SCHEME
+The interim DNS update scheme operates mostly according to several
+drafts considered by the IETF. While the drafts have since become
+RFCs the code was written before they were finalized and there are
+some differences between our code and the final RFCs. We plan to
+update our code, probably adding a standard DNS update option, at
+some time. The basic framework is similar with the main material
+difference being that a DHCID RR was assigned in the RFCs whereas
+our code continues to use an experimental TXT record. The format
+of the TXT record bears a resemblance to the DHCID RR but it is not
+equivalent (MD5 vs SHA1, field length differences etc).
+The standard RFCs are:
+.PP
+.nf
+.ce 3
+RFC 4701 (updated by RF5494)
+RFC 4702
+RFC 4703
+.fi
+.PP
+And the corresponding drafts were:
+.PP
+.nf
+.ce 3
+draft-ietf-dnsext-dhcid-rr-??.txt
+draft-ietf-dhc-fqdn-option-??.txt
+draft-ietf-dhc-ddns-resolution-??.txt
+.fi
+.PP
+Because our implementation is slightly different than the standard, we
+will briefly document the operation of this update style here.
+.PP
+The first point to understand about this style of DNS update is that
+unlike the ad-hoc style, the DHCP server does not necessarily
+always update both the A and the PTR records. The FQDN option
+includes a flag which, when sent by the client, indicates that the
+client wishes to update its own A record. In that case, the server
+can be configured either to honor the client's intentions or ignore
+them. This is done with the statement \fIallow client-updates;\fR or
+the statement \fIignore client-updates;\fR. By default, client
+updates are allowed.
+.PP
+If the server is configured to allow client updates, then if the
+client sends a fully-qualified domain name in the FQDN option, the
+server will use that name the client sent in the FQDN option to update
+the PTR record. For example, let us say that the client is a visitor
+from the "radish.org" domain, whose hostname is "jschmoe". The
+server is for the "example.org" domain. The DHCP client indicates in
+the FQDN option that its FQDN is "jschmoe.radish.org.". It also
+indicates that it wants to update its own A record. The DHCP server
+therefore does not attempt to set up an A record for the client, but
+does set up a PTR record for the IP address that it assigns the
+client, pointing at jschmoe.radish.org. Once the DHCP client has an
+IP address, it can update its own A record, assuming that the
+"radish.org" DNS server will allow it to do so.
+.PP
+If the server is configured not to allow client updates, or if the
+client doesn't want to do its own update, the server will simply
+choose a name for the client from either the fqdn option (if present)
+or the hostname option (if present). It will use its own
+domain name for the client, just as in the ad-hoc update scheme.
+It will then update both the A and PTR record, using the name that it
+chose for the client. If the client sends a fully-qualified domain
+name in the fqdn option, the server uses only the leftmost part of the
+domain name - in the example above, "jschmoe" instead of
+"jschmoe.radish.org".
+.PP
+Further, if the \fIignore client-updates;\fR directive is used, then
+the server will in addition send a response in the DHCP packet, using
+the FQDN Option, that implies to the client that it should perform its
+own updates if it chooses to do so. With \fIdeny client-updates;\fR, a
+response is sent which indicates the client may not perform updates.
+.PP
+Also, if the
+.I use-host-decl-names
+configuration option is enabled, then the host declaration's
+.I hostname
+will be used in place of the
+.I hostname
+option, and the same rules will apply as described above.
+.PP
+The other difference between the ad-hoc scheme and the interim
+scheme is that with the interim scheme, a method is used that
+allows more than one DHCP server to update the DNS database without
+accidentally deleting A records that shouldn't be deleted nor failing
+to add A records that should be added. The scheme works as follows:
+.PP
+When the DHCP server issues a client a new lease, it creates a text
+string that is an MD5 hash over the DHCP client's identification (see
+draft-ietf-dnsext-dhcid-rr-??.txt for details). The update adds an A
+record with the name the server chose and a TXT record containing the
+hashed identifier string (hashid). If this update succeeds, the
+server is done.
+.PP
+If the update fails because the A record already exists, then the DHCP
+server attempts to add the A record with the prerequisite that there
+must be a TXT record in the same name as the new A record, and that
+TXT record's contents must be equal to hashid. If this update
+succeeds, then the client has its A record and PTR record. If it
+fails, then the name the client has been assigned (or requested) is in
+use, and can't be used by the client. At this point the DHCP server
+gives up trying to do a DNS update for the client until the client
+chooses a new name.
+.PP
+The interim DNS update scheme is called interim for two reasons.
+First, it does not quite follow the RFCs. The RFCs call for a
+new DHCID RRtype while he interim DNS update scheme uses a TXT record.
+The ddns-resolution draft called for the DHCP server to put a DHCID RR
+on the PTR record, but the \fIinterim\fR update method does not do this.
+In the final RFC this requirement was relaxed such that a server may
+add a DHCID RR to the PTR record.
+.PP
+In addition to these differences, the server also does not update very
+aggressively. Because each DNS update involves a round trip to the
+DNS server, there is a cost associated with doing updates even if they
+do not actually modify the DNS database. So the DHCP server tracks
+whether or not it has updated the record in the past (this information
+is stored on the lease) and does not attempt to update records that it
+thinks it has already updated.
+.PP
+This can lead to cases where the DHCP server adds a record, and then
+the record is deleted through some other mechanism, but the server
+never again updates the DNS because it thinks the data is already
+there. In this case the data can be removed from the lease through
+operator intervention, and once this has been done, the DNS will be
+updated the next time the client renews.
+.SH DYNAMIC DNS UPDATE SECURITY
+.PP
+When you set your DNS server up to allow updates from the DHCP server,
+you may be exposing it to unauthorized updates. To avoid this, you
+should use TSIG signatures - a method of cryptographically signing
+updates using a shared secret key. As long as you protect the
+secrecy of this key, your updates should also be secure. Note,
+however, that the DHCP protocol itself provides no security, and that
+clients can therefore provide information to the DHCP server which the
+DHCP server will then use in its updates, with the constraints
+described previously.
+.PP
+The DNS server must be configured to allow updates for any zone that
+the DHCP server will be updating. For example, let us say that
+clients in the sneedville.edu domain will be assigned addresses on the
+10.10.17.0/24 subnet. In that case, you will need a key declaration
+for the TSIG key you will be using, and also two zone declarations -
+one for the zone containing A records that will be updates and one for
+the zone containing PTR records - for ISC BIND, something like this:
+.PP
+.nf
+key DHCP_UPDATER {
+ algorithm HMAC-MD5.SIG-ALG.REG.INT;
+ secret pRP5FapFoJ95JEL06sv4PQ==;
+};
+
+zone "example.org" {
+ type master;
+ file "example.org.db";
+ allow-update { key DHCP_UPDATER; };
+};
+
+zone "17.10.10.in-addr.arpa" {
+ type master;
+ file "10.10.17.db";
+ allow-update { key DHCP_UPDATER; };
+};
+.fi
+.PP
+You will also have to configure your DHCP server to do updates to
+these zones. To do so, you need to add something like this to your
+dhcpd.conf file:
+.PP
+.nf
+key DHCP_UPDATER {
+ algorithm HMAC-MD5.SIG-ALG.REG.INT;
+ secret pRP5FapFoJ95JEL06sv4PQ==;
+};
+
+zone EXAMPLE.ORG. {
+ primary 127.0.0.1;
+ key DHCP_UPDATER;
+}
+
+zone 17.127.10.in-addr.arpa. {
+ primary 127.0.0.1;
+ key DHCP_UPDATER;
+}
+.fi
+.PP
+The \fIprimary\fR statement specifies the IP address of the name
+server whose zone information is to be updated.
+.PP
+Note that the zone declarations have to correspond to authority
+records in your name server - in the above example, there must be an
+SOA record for "example.org." and for "17.10.10.in-addr.arpa.". For
+example, if there were a subdomain "foo.example.org" with no separate
+SOA, you could not write a zone declaration for "foo.example.org."
+Also keep in mind that zone names in your DHCP configuration should end in a
+"."; this is the preferred syntax. If you do not end your zone name in a
+".", the DHCP server will figure it out. Also note that in the DHCP
+configuration, zone names are not encapsulated in quotes where there are in
+the DNS configuration.
+.PP
+You should choose your own secret key, of course. The ISC BIND 8 and
+9 distributions come with a program for generating secret keys called
+dnssec-keygen. The version that comes with BIND 9 is likely to produce a
+substantially more random key, so we recommend you use that one even
+if you are not using BIND 9 as your DNS server. If you are using BIND 9's
+dnssec-keygen, the above key would be created as follows:
+.PP
+.nf
+ dnssec-keygen -a HMAC-MD5 -b 128 -n USER DHCP_UPDATER
+.fi
+.PP
+If you are using the BIND 8 dnskeygen program, the following command will
+generate a key as seen above:
+.PP
+.nf
+ dnskeygen -H 128 -u -c -n DHCP_UPDATER
+.fi
+.PP
+You may wish to enable logging of DNS updates on your DNS server.
+To do so, you might write a logging statement like the following:
+.PP
+.nf
+logging {
+ channel update_debug {
+ file "/var/log/update-debug.log";
+ severity debug 3;
+ print-category yes;
+ print-severity yes;
+ print-time yes;
+ };
+ channel security_info {
+ file "/var/log/named-auth.info";
+ severity info;
+ print-category yes;
+ print-severity yes;
+ print-time yes;
+ };
+
+ category update { update_debug; };
+ category security { security_info; };
+};
+.fi
+.PP
+You must create the /var/log/named-auth.info and
+/var/log/update-debug.log files before starting the name server. For
+more information on configuring ISC BIND, consult the documentation
+that accompanies it.
+.SH REFERENCE: EVENTS
+.PP
+There are three kinds of events that can happen regarding a lease, and
+it is possible to declare statements that occur when any of these
+events happen. These events are the commit event, when the server
+has made a commitment of a certain lease to a client, the release
+event, when the client has released the server from its commitment,
+and the expiry event, when the commitment expires.
+.PP
+To declare a set of statements to execute when an event happens, you
+must use the \fBon\fR statement, followed by the name of the event,
+followed by a series of statements to execute when the event happens,
+enclosed in braces. Events are used to implement DNS
+updates, so you should not define your own event handlers if you are
+using the built-in DNS update mechanism.
+.PP
+The built-in version of the DNS update mechanism is in a text
+string towards the top of server/dhcpd.c. If you want to use events
+for things other than DNS updates, and you also want DNS updates, you
+will have to start out by copying this code into your dhcpd.conf file
+and modifying it.
+.SH REFERENCE: DECLARATIONS
+.PP
+.B The
+.I include
+.B statement
+.PP
+.nf
+ \fBinclude\fR \fI"filename"\fR\fB;\fR
+.fi
+.PP
+The \fIinclude\fR statement is used to read in a named file, and process
+the contents of that file as though it were entered in place of the
+include statement.
+.PP
+.B The
+.I shared-network
+.B statement
+.PP
+.nf
+ \fBshared-network\fR \fIname\fR \fB{\fR
+ [ \fIparameters\fR ]
+ [ \fIdeclarations\fR ]
+ \fB}\fR
+.fi
+.PP
+The \fIshared-network\fR statement is used to inform the DHCP server
+that some IP subnets actually share the same physical network. Any
+subnets in a shared network should be declared within a
+\fIshared-network\fR statement. Parameters specified in the
+\fIshared-network\fR statement will be used when booting clients on
+those subnets unless parameters provided at the subnet or host level
+override them. If any subnet in a shared network has addresses
+available for dynamic allocation, those addresses are collected into a
+common pool for that shared network and assigned to clients as needed.
+There is no way to distinguish on which subnet of a shared network a
+client should boot.
+.PP
+.I Name
+should be the name of the shared network. This name is used when
+printing debugging messages, so it should be descriptive for the
+shared network. The name may have the syntax of a valid domain name
+(although it will never be used as such), or it may be any arbitrary
+name, enclosed in quotes.
+.PP
+.B The
+.I subnet
+.B statement
+.PP
+.nf
+ \fBsubnet\fR \fIsubnet-number\fR \fBnetmask\fR \fInetmask\fR \fB{\fR
+ [ \fIparameters\fR ]
+ [ \fIdeclarations\fR ]
+ \fB}\fR
+.fi
+.PP
+The \fIsubnet\fR statement is used to provide dhcpd with enough
+information to tell whether or not an IP address is on that subnet.
+It may also be used to provide subnet-specific parameters and to
+specify what addresses may be dynamically allocated to clients booting
+on that subnet. Such addresses are specified using the \fIrange\fR
+declaration.
+.PP
+The
+.I subnet-number
+should be an IP address or domain name which resolves to the subnet
+number of the subnet being described. The
+.I netmask
+should be an IP address or domain name which resolves to the subnet mask
+of the subnet being described. The subnet number, together with the
+netmask, are sufficient to determine whether any given IP address is
+on the specified subnet.
+.PP
+Although a netmask must be given with every subnet declaration, it is
+recommended that if there is any variance in subnet masks at a site, a
+subnet-mask option statement be used in each subnet declaration to set
+the desired subnet mask, since any subnet-mask option statement will
+override the subnet mask declared in the subnet statement.
+.PP
+.B The
+.I subnet6
+.B statement
+.PP
+.nf
+ \fBsubnet6\fR \fIsubnet6-number\fR \fB{\fR
+ [ \fIparameters\fR ]
+ [ \fIdeclarations\fR ]
+ \fB}\fR
+.fi
+.PP
+The \fIsubnet6\fR statement is used to provide dhcpd with enough
+information to tell whether or not an IPv6 address is on that subnet6.
+It may also be used to provide subnet-specific parameters and to
+specify what addresses may be dynamically allocated to clients booting
+on that subnet.
+.PP
+The
+.I subnet6-number
+should be an IPv6 network identifier, specified as ip6-address/bits.
+.PP
+.B The
+.I range
+.B statement
+.PP
+.nf
+.B range\fR [ \fBdynamic-bootp\fR ] \fIlow-address\fR [ \fIhigh-address\fR]\fB;\fR
+.fi
+.PP
+For any subnet on which addresses will be assigned dynamically, there
+must be at least one \fIrange\fR statement. The range statement
+gives the lowest and highest IP addresses in a range. All IP
+addresses in the range should be in the subnet in which the
+\fIrange\fR statement is declared. The \fIdynamic-bootp\fR flag may
+be specified if addresses in the specified range may be dynamically
+assigned to BOOTP clients as well as DHCP clients. When specifying a
+single address, \fIhigh-address\fR can be omitted.
+.PP
+.B The
+.I range6
+.B statement
+.PP
+.nf
+.B range6\fR \fIlow-address\fR \fIhigh-address\fR\fB;\fR
+.B range6\fR \fIsubnet6-number\fR\fB;\fR
+.B range6\fR \fIsubnet6-number\fR \fBtemporary\fR\fB;\fR
+.B range6\fR \fIaddress\fR \fBtemporary\fR\fB;\fR
+.fi
+.PP
+For any IPv6 subnet6 on which addresses will be assigned dynamically, there
+must be at least one \fIrange6\fR statement. The \fIrange6\fR statement
+can either be the lowest and highest IPv6 addresses in a \fIrange6\fR, or
+use CIDR notation, specified as ip6-address/bits. All IP addresses
+in the \fIrange6\fR should be in the subnet6 in which the
+\fIrange6\fR statement is declared.
+.PP
+The \fItemporary\fR variant makes the prefix (by default on 64 bits) available
+for temporary (RFC 4941) addresses. A new address per prefix in the shared
+network is computed at each request with an IA_TA option. Release and Confirm
+ignores temporary addresses.
+.PP
+Any IPv6 addresses given to hosts with \fIfixed-address6\fR are excluded
+from the \fIrange6\fR, as are IPv6 addresses on the server itself.
+.PP
+.PP
+.B The
+.I prefix6
+.B statement
+.PP
+.nf
+.B prefix6\fR \fIlow-address\fR \fIhigh-address\fR \fB/\fR \fIbits\fR\fB;\fR
+.fi
+.PP
+The \fIprefix6\fR is the \fIrange6\fR equivalent for Prefix Delegation
+(RFC 3633). Prefixes of \fIbits\fR length are assigned between
+\fIlow-address\fR and \fIhigh-address\fR.
+.PP
+Any IPv6 prefixes given to static entries (hosts) with \fIfixed-prefix6\fR
+are excluded from the \fIprefix6\fR.
+.PP
+This statement is currently global but it should have a shared-network scope.
+.PP
+.B The
+.I host
+.B statement
+.PP
+.nf
+ \fBhost\fR \fIhostname\fR {
+ [ \fIparameters\fR ]
+ [ \fIdeclarations\fR ]
+ \fB}\fR
+.fi
+.PP
+The
+.B host
+declaration provides a scope in which to provide configuration information about
+a specific client, and also provides a way to assign a client a fixed address.
+The host declaration provides a way for the DHCP server to identify a DHCP or
+BOOTP client, and also a way to assign the client a static IP address.
+.PP
+If it is desirable to be able to boot a DHCP or BOOTP client on more than one
+subnet with fixed addresses, more than one address may be specified in the
+.I fixed-address
+declaration, or more than one
+.B host
+statement may be specified matching the same client.
+.PP
+If client-specific boot parameters must change based on the network
+to which the client is attached, then multiple
+.B host
+declarations should be used. The
+.B host
+declarations will only match a client if one of their
+.I fixed-address
+statements is viable on the subnet (or shared network) where the client is
+attached. Conversely, for a
+.B host
+declaration to match a client being allocated a dynamic address, it must not
+have any
+.I fixed-address
+statements. You may therefore need a mixture of
+.B host
+declarations for any given client...some having
+.I fixed-address
+statements, others without.
+.PP
+.I hostname
+should be a name identifying the host. If a \fIhostname\fR option is
+not specified for the host, \fIhostname\fR is used.
+.PP
+\fIHost\fR declarations are matched to actual DHCP or BOOTP clients
+by matching the \fRdhcp-client-identifier\fR option specified in the
+\fIhost\fR declaration to the one supplied by the client, or, if the
+\fIhost\fR declaration or the client does not provide a
+\fRdhcp-client-identifier\fR option, by matching the \fIhardware\fR
+parameter in the \fIhost\fR declaration to the network hardware
+address supplied by the client. BOOTP clients do not normally
+provide a \fIdhcp-client-identifier\fR, so the hardware address must
+be used for all clients that may boot using the BOOTP protocol.
+.PP
+DHCPv6 servers can use the \fIhost-identifier option\fR parameter in
+the \fIhost\fR declaration, and specify any option with a fixed value
+to identify hosts.
+.PP
+Please be aware that
+.B only
+the \fIdhcp-client-identifier\fR option and the hardware address can be
+used to match a host declaration, or the \fIhost-identifier option\fR
+parameter for DHCPv6 servers. For example, it is not possible to
+match a host declaration to a \fIhost-name\fR option. This is
+because the host-name option cannot be guaranteed to be unique for any
+given client, whereas both the hardware address and
+\fIdhcp-client-identifier\fR option are at least theoretically
+guaranteed to be unique to a given client.
+.PP
+.B The
+.I group
+.B statement
+.PP
+.nf
+ \fBgroup\fR {
+ [ \fIparameters\fR ]
+ [ \fIdeclarations\fR ]
+ \fB}\fR
+.fi
+.PP
+The group statement is used simply to apply one or more parameters to
+a group of declarations. It can be used to group hosts, shared
+networks, subnets, or even other groups.
+.SH REFERENCE: ALLOW AND DENY
+The
+.I allow
+and
+.I deny
+statements can be used to control the response of the DHCP server to
+various sorts of requests. The allow and deny keywords actually have
+different meanings depending on the context. In a pool context, these
+keywords can be used to set up access lists for address allocation
+pools. In other contexts, the keywords simply control general server
+behavior with respect to clients based on scope. In a non-pool
+context, the
+.I ignore
+keyword can be used in place of the
+.I deny
+keyword to prevent logging of denied requests.
+.PP
+.SH ALLOW DENY AND IGNORE IN SCOPE
+The following usages of allow and deny will work in any scope,
+although it is not recommended that they be used in pool
+declarations.
+.PP
+.B The
+.I unknown-clients
+.B keyword
+.PP
+ \fBallow unknown-clients;\fR
+ \fBdeny unknown-clients;\fR
+ \fBignore unknown-clients;\fR
+.PP
+The \fBunknown-clients\fR flag is used to tell dhcpd whether
+or not to dynamically assign addresses to unknown clients. Dynamic
+address assignment to unknown clients is \fBallow\fRed by default.
+An unknown client is simply a client that has no host declaration.
+.PP
+The use of this option is now \fIdeprecated\fR. If you are trying to
+restrict access on your network to known clients, you should use \fBdeny
+unknown-clients;\fR inside of your address pool, as described under the
+heading ALLOW AND DENY WITHIN POOL DECLARATIONS.
+.PP
+.B The
+.I bootp
+.B keyword
+.PP
+ \fBallow bootp;\fR
+ \fBdeny bootp;\fR
+ \fBignore bootp;\fR
+.PP
+The \fBbootp\fR flag is used to tell dhcpd whether
+or not to respond to bootp queries. Bootp queries are \fBallow\fRed
+by default.
+.PP
+This option does not satisfy the requirement of failover peers for denying
+dynamic bootp clients. The \fBdeny dynamic bootp clients;\fR option should
+be used instead. See the ALLOW AND DENY WITHIN POOL DECLARATIONS section
+of this man page for more details.
+.PP
+.B The
+.I booting
+.B keyword
+.PP
+ \fBallow booting;\fR
+ \fBdeny booting;\fR
+ \fBignore booting;\fR
+.PP
+The \fBbooting\fR flag is used to tell dhcpd whether or not to respond
+to queries from a particular client. This keyword only has meaning
+when it appears in a host declaration. By default, booting is
+\fBallow\fRed, but if it is disabled for a particular client, then
+that client will not be able to get an address from the DHCP server.
+.PP
+.B The
+.I duplicates
+.B keyword
+.PP
+ \fBallow duplicates;\fR
+ \fBdeny duplicates;\fR
+.PP
+Host declarations can match client messages based on the DHCP Client
+Identifier option or based on the client's network hardware type and
+MAC address. If the MAC address is used, the host declaration will
+match any client with that MAC address - even clients with different
+client identifiers. This doesn't normally happen, but is possible
+when one computer has more than one operating system installed on it -
+for example, Microsoft Windows and NetBSD or Linux.
+.PP
+The \fBduplicates\fR flag tells the DHCP server that if a request is
+received from a client that matches the MAC address of a host
+declaration, any other leases matching that MAC address should be
+discarded by the server, even if the UID is not the same. This is a
+violation of the DHCP protocol, but can prevent clients whose client
+identifiers change regularly from holding many leases at the same time.
+By default, duplicates are \fBallow\fRed.
+.PP
+.B The
+.I declines
+.B keyword
+.PP
+ \fBallow declines;\fR
+ \fBdeny declines;\fR
+ \fBignore declines;\fR
+.PP
+The DHCPDECLINE message is used by DHCP clients to indicate that the
+lease the server has offered is not valid. When the server receives
+a DHCPDECLINE for a particular address, it normally abandons that
+address, assuming that some unauthorized system is using it.
+Unfortunately, a malicious or buggy client can, using DHCPDECLINE
+messages, completely exhaust the DHCP server's allocation pool. The
+server will reclaim these leases, but while the client is running
+through the pool, it may cause serious thrashing in the DNS, and it
+will also cause the DHCP server to forget old DHCP client address
+allocations.
+.PP
+The \fBdeclines\fR flag tells the DHCP server whether or not to honor
+DHCPDECLINE messages. If it is set to \fBdeny\fR or \fBignore\fR in
+a particular scope, the DHCP server will not respond to DHCPDECLINE
+messages.
+.PP
+.B The
+.I client-updates
+.B keyword
+.PP
+ \fBallow client-updates;\fR
+ \fBdeny client-updates;\fR
+.PP
+The \fBclient-updates\fR flag tells the DHCP server whether or not to
+honor the client's intention to do its own update of its A record.
+This is only relevant when doing \fIinterim\fR DNS updates. See the
+documentation under the heading THE INTERIM DNS UPDATE SCHEME for
+details.
+.PP
+.B The
+.I leasequery
+.B keyword
+.PP
+ \fBallow leasequery;\fR
+ \fBdeny leasequery;\fR
+.PP
+The \fBleasequery\fR flag tells the DHCP server whether or not to
+answer DHCPLEASEQUERY packets. The answer to a DHCPLEASEQUERY packet
+includes information about a specific lease, such as when it was
+issued and when it will expire. By default, the server will not
+respond to these packets.
+.SH ALLOW AND DENY WITHIN POOL DECLARATIONS
+.PP
+The uses of the allow and deny keywords shown in the previous section
+work pretty much the same way whether the client is sending a
+DHCPDISCOVER or a DHCPREQUEST message - an address will be allocated
+to the client (either the old address it's requesting, or a new
+address) and then that address will be tested to see if it's okay to
+let the client have it. If the client requested it, and it's not
+okay, the server will send a DHCPNAK message. Otherwise, the server
+will simply not respond to the client. If it is okay to give the
+address to the client, the server will send a DHCPACK message.
+.PP
+The primary motivation behind pool declarations is to have address
+allocation pools whose allocation policies are different. A client
+may be denied access to one pool, but allowed access to another pool
+on the same network segment. In order for this to work, access
+control has to be done during address allocation, not after address
+allocation is done.
+.PP
+When a DHCPREQUEST message is processed, address allocation simply
+consists of looking up the address the client is requesting and seeing
+if it's still available for the client. If it is, then the DHCP
+server checks both the address pool permit lists and the relevant
+in-scope allow and deny statements to see if it's okay to give the
+lease to the client. In the case of a DHCPDISCOVER message, the
+allocation process is done as described previously in the ADDRESS
+ALLOCATION section.
+.PP
+When declaring permit lists for address allocation pools, the
+following syntaxes are recognized following the allow or deny keywords:
+.PP
+ \fBknown-clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any client that has a host declaration (i.e., is known).
+A client is known if it has a host declaration in \fIany\fR scope, not
+just the current scope.
+.PP
+ \fBunknown-clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any client that has no host declaration (i.e., is not
+known).
+.PP
+ \fBmembers of "\fRclass\fB";\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any client that is a member of the named class.
+.PP
+ \fBdynamic bootp clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any bootp client.
+.PP
+ \fBauthenticated clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any client that has been authenticated using the DHCP
+authentication protocol. This is not yet supported.
+.PP
+ \fBunauthenticated clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to any client that has not been authenticated using the DHCP
+authentication protocol. This is not yet supported.
+.PP
+ \fBall clients;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool to all clients. This can be used when you want to write a
+pool declaration for some reason, but hold it in reserve, or when you
+want to renumber your network quickly, and thus want the server to
+force all clients that have been allocated addresses from this pool to
+obtain new addresses immediately when they next renew.
+.PP
+ \fBafter \fItime\fR\fB;\fR
+.PP
+If specified, this statement either allows or prevents allocation from
+this pool after a given date. This can be used when you want to move
+clients from one pool to another. The server adjusts the regular lease
+time so that the latest expiry time is at the given time+min-lease-time.
+A short min-lease-time enforces a step change, whereas a longer
+min-lease-time allows for a gradual change.
+\fItime\fR is either second since epoch, or a UTC time string e.g.
+4 2007/08/24 09:14:32 or a string with time zone offset in seconds
+e.g. 4 2007/08/24 11:14:32 -7200
+.SH REFERENCE: PARAMETERS
+The
+.I adaptive-lease-time-threshold
+statement
+.RS 0.25i
+.PP
+.B adaptive-lease-time-threshold \fIpercentage\fR\fB;\fR
+.PP
+When the number of allocated leases within a pool rises above
+the \fIpercentage\fR given in this statement, the DHCP server decreases
+the lease length for new clients within this pool to \fImin-lease-time\fR
+seconds. Clients renewing an already valid (long) leases get at least the
+remaining time from the current lease. Since the leases expire faster,
+the server may either recover more quickly or avoid pool exhaustion
+entirely. Once the number of allocated leases drop below the threshold,
+the server reverts back to normal lease times. Valid percentages are
+between 1 and 99.
+.RE
+.PP
+The
+.I always-broadcast
+statement
+.RS 0.25i
+.PP
+.B always-broadcast \fIflag\fR\fB;\fR
+.PP
+The DHCP and BOOTP protocols both require DHCP and BOOTP clients to
+set the broadcast bit in the flags field of the BOOTP message header.
+Unfortunately, some DHCP and BOOTP clients do not do this, and
+therefore may not receive responses from the DHCP server. The DHCP
+server can be made to always broadcast its responses to clients by
+setting this flag to \'on\' for the relevant scope; relevant scopes would be
+inside a conditional statement, as a parameter for a class, or as a parameter
+for a host declaration. To avoid creating excess broadcast traffic on your
+network, we recommend that you restrict the use of this option to as few
+clients as possible. For example, the Microsoft DHCP client is known not
+to have this problem, as are the OpenTransport and ISC DHCP clients.
+.RE
+.PP
+The
+.I always-reply-rfc1048
+statement
+.RS 0.25i
+.PP
+.B always-reply-rfc1048 \fIflag\fR\fB;\fR
+.PP
+Some BOOTP clients expect RFC1048-style responses, but do not follow
+RFC1048 when sending their requests. You can tell that a client is
+having this problem if it is not getting the options you have
+configured for it and if you see in the server log the message
+"(non-rfc1048)" printed with each BOOTREQUEST that is logged.
+.PP
+If you want to send rfc1048 options to such a client, you can set the
+.B always-reply-rfc1048
+option in that client's host declaration, and the DHCP server will
+respond with an RFC-1048-style vendor options field. This flag can
+be set in any scope, and will affect all clients covered by that
+scope.
+.RE
+.PP
+The
+.I authoritative
+statement
+.RS 0.25i
+.PP
+.B authoritative;
+.PP
+.B not authoritative;
+.PP
+The DHCP server will normally assume that the configuration
+information about a given network segment is not known to be correct
+and is not authoritative. This is so that if a naive user installs a
+DHCP server not fully understanding how to configure it, it does not
+send spurious DHCPNAK messages to clients that have obtained addresses
+from a legitimate DHCP server on the network.
+.PP
+Network administrators setting up authoritative DHCP servers for their
+networks should always write \fBauthoritative;\fR at the top of their
+configuration file to indicate that the DHCP server \fIshould\fR send
+DHCPNAK messages to misconfigured clients. If this is not done,
+clients will be unable to get a correct IP address after changing
+subnets until their old lease has expired, which could take quite a
+long time.
+.PP
+Usually, writing \fBauthoritative;\fR at the top level of the file
+should be sufficient. However, if a DHCP server is to be set up so
+that it is aware of some networks for which it is authoritative and
+some networks for which it is not, it may be more appropriate to
+declare authority on a per-network-segment basis.
+.PP
+Note that the most specific scope for which the concept of authority
+makes any sense is the physical network segment - either a
+shared-network statement or a subnet statement that is not contained
+within a shared-network statement. It is not meaningful to specify
+that the server is authoritative for some subnets within a shared
+network, but not authoritative for others, nor is it meaningful to
+specify that the server is authoritative for some host declarations
+and not others.
+.RE
+.PP
+The \fIboot-unknown-clients\fR statement
+.RS 0.25i
+.PP
+.B boot-unknown-clients \fIflag\fB;\fR
+.PP
+If the \fIboot-unknown-clients\fR statement is present and has a value
+of \fIfalse\fR or \fIoff\fR, then clients for which there is no
+.I host
+declaration will not be allowed to obtain IP addresses. If this
+statement is not present or has a value of \fItrue\fR or \fIon\fR,
+then clients without host declarations will be allowed to obtain IP
+addresses, as long as those addresses are not restricted by
+.I allow
+and \fIdeny\fR statements within their \fIpool\fR declarations.
+.RE
+.PP
+The \fIdb-time-format\fR statement
+.RS 0.25i
+.PP
+.B db-time-format \fR[ \fIdefault\fR | \fIlocal\fR ] \fB;\fR
+.PP
+The DHCP server software outputs several timestamps when writing leases to
+persistent storage. This configuration parameter selects one of two output
+formats. The \fIdefault\fR format prints the day, date, and time in UTC,
+while the \fIlocal\fR format prints the system seconds-since-epoch, and
+helpfully provides the day and time in the system timezone in a comment.
+The time formats are described in detail in the dhcpd.leases(5) manpage.
+.RE
+.PP
+The \fIddns-hostname\fR statement
+.RS 0.25i
+.PP
+.B ddns-hostname \fIname\fB;\fR
+.PP
+The \fIname\fR parameter should be the hostname that will be used in
+setting up the client's A and PTR records. If no ddns-hostname is
+specified in scope, then the server will derive the hostname
+automatically, using an algorithm that varies for each of the
+different update methods.
+.RE
+.PP
+The \fIddns-domainname\fR statement
+.RS 0.25i
+.PP
+.B ddns-domainname \fIname\fB;\fR
+.PP
+The \fIname\fR parameter should be the domain name that will be
+appended to the client's hostname to form a fully-qualified
+domain-name (FQDN).
+.RE
+.PP
+The \fIddns-rev-domainname\fR statement
+.RS 0.25i
+.PP
+.B ddns-rev-domainname \fIname\fB;\fR
+The \fIname\fR parameter should be the domain name that will be
+appended to the client's reversed IP address to produce a name for use
+in the client's PTR record. By default, this is "in-addr.arpa.", but
+the default can be overridden here.
+.PP
+The reversed IP address to which this domain name is appended is
+always the IP address of the client, in dotted quad notation, reversed
+- for example, if the IP address assigned to the client is
+10.17.92.74, then the reversed IP address is 74.92.17.10. So a
+client with that IP address would, by default, be given a PTR record
+of 10.17.92.74.in-addr.arpa.
+.RE
+.PP
+The \fIddns-update-style\fR parameter
+.RS 0.25i
+.PP
+.B ddns-update-style \fIstyle\fB;\fR
+.PP
+The
+.I style
+parameter must be one of \fBad-hoc\fR, \fBinterim\fR or \fBnone\fR.
+The \fIddns-update-style\fR statement is only meaningful in the outer
+scope - it is evaluated once after reading the dhcpd.conf file, rather
+than each time a client is assigned an IP address, so there is no way
+to use different DNS update styles for different clients. The default
+is \fBnone\fR.
+.RE
+.PP
+.B The
+.I ddns-updates
+.B statement
+.RS 0.25i
+.PP
+ \fBddns-updates \fIflag\fR\fB;\fR
+.PP
+The \fIddns-updates\fR parameter controls whether or not the server will
+attempt to do a DNS update when a lease is confirmed. Set this to \fIoff\fR
+if the server should not attempt to do updates within a certain scope.
+The \fIddns-updates\fR parameter is on by default. To disable DNS
+updates in all scopes, it is preferable to use the
+\fIddns-update-style\fR statement, setting the style to \fInone\fR.
+.RE
+.PP
+The
+.I default-lease-time
+statement
+.RS 0.25i
+.PP
+.B default-lease-time \fItime\fR\fB;\fR
+.PP
+.I Time
+should be the length in seconds that will be assigned to a lease if
+the client requesting the lease does not ask for a specific expiration
+time. This is used for both DHCPv4 and DHCPv6 leases (it is also known
+as the "valid lifetime" in DHCPv6).
+The default is 43200 seconds.
+.RE
+.PP
+The
+.I delayed-ack
+and
+.I max-ack-delay
+statements
+.RS 0.25i
+.PP
+.B delayed-ack \fIcount\fR\fB;\fR
+.B max-ack-delay \fImicroseconds\fR\fB;\fR
+.PP
+.I Count
+should be an integer value from zero to 2^16-1, and defaults to 28. The
+count represents how many DHCPv4 replies maximum will be queued pending
+transmission until after a database commit event. If this number is
+reached, a database commit event (commonly resulting in fsync() and
+representing a performance penalty) will be made, and the reply packets
+will be transmitted in a batch afterwards. This preserves the RFC2131
+direction that "stable storage" be updated prior to replying to clients.
+Should the DHCPv4 sockets "go dry" (select() returns immediately with no
+read sockets), the commit is made and any queued packets are transmitted.
+.PP
+Similarly, \fImicroseconds\fR indicates how many microseconds are permitted
+to pass inbetween queuing a packet pending an fsync, and performing the
+fsync. Valid values range from 0 to 2^32-1, and defaults to 250,000 (1/4 of
+a second).
+.PP
+Please note that as delayed-ack is currently experimental, the delayed-ack
+feature is not compiled in by default, but must be enabled at compile time
+with \'./configure --enable-delayed-ack\'.
+.RE
+.PP
+The
+.I do-forward-updates
+statement
+.RS 0.25i
+.PP
+.B do-forward-updates \fIflag\fB;\fR
+.PP
+The \fIdo-forward-updates\fR statement instructs the DHCP server as
+to whether it should attempt to update a DHCP client's A record
+when the client acquires or renews a lease. This statement has no
+effect unless DNS updates are enabled and \fBddns-update-style\fR is
+set to \fBinterim\fR. Forward updates are enabled by default. If
+this statement is used to disable forward updates, the DHCP server
+will never attempt to update the client's A record, and will only ever
+attempt to update the client's PTR record if the client supplies an
+FQDN that should be placed in the PTR record using the fqdn option.
+If forward updates are enabled, the DHCP server will still honor the
+setting of the \fBclient-updates\fR flag.
+.RE
+.PP
+The
+.I dynamic-bootp-lease-cutoff
+statement
+.RS 0.25i
+.PP
+.B dynamic-bootp-lease-cutoff \fIdate\fB;\fR
+.PP
+The \fIdynamic-bootp-lease-cutoff\fR statement sets the ending time
+for all leases assigned dynamically to BOOTP clients. Because BOOTP
+clients do not have any way of renewing leases, and don't know that
+their leases could expire, by default dhcpd assigns infinite leases
+to all BOOTP clients. However, it may make sense in some situations
+to set a cutoff date for all BOOTP leases - for example, the end of a
+school term, or the time at night when a facility is closed and all
+machines are required to be powered off.
+.PP
+.I Date
+should be the date on which all assigned BOOTP leases will end. The
+date is specified in the form:
+.PP
+.ce 1
+W YYYY/MM/DD HH:MM:SS
+.PP
+W is the day of the week expressed as a number
+from zero (Sunday) to six (Saturday). YYYY is the year, including the
+century. MM is the month expressed as a number from 1 to 12. DD is
+the day of the month, counting from 1. HH is the hour, from zero to
+23. MM is the minute and SS is the second. The time is always in
+Coordinated Universal Time (UTC), not local time.
+.RE
+.PP
+The
+.I dynamic-bootp-lease-length
+statement
+.RS 0.25i
+.PP
+.B dynamic-bootp-lease-length\fR \fIlength\fR\fB;\fR
+.PP
+The \fIdynamic-bootp-lease-length\fR statement is used to set the
+length of leases dynamically assigned to BOOTP clients. At some
+sites, it may be possible to assume that a lease is no longer in
+use if its holder has not used BOOTP or DHCP to get its address within
+a certain time period. The period is specified in \fIlength\fR as a
+number of seconds. If a client reboots using BOOTP during the
+timeout period, the lease duration is reset to \fIlength\fR, so a
+BOOTP client that boots frequently enough will never lose its lease.
+Needless to say, this parameter should be adjusted with extreme
+caution.
+.RE
+.PP
+The
+.I filename
+statement
+.RS 0.25i
+.PP
+.B filename\fR \fB"\fR\fIfilename\fR\fB";\fR
+.PP
+The \fIfilename\fR statement can be used to specify the name of the
+initial boot file which is to be loaded by a client. The
+.I filename
+should be a filename recognizable to whatever file transfer protocol
+the client can be expected to use to load the file.
+.RE
+.PP
+The
+.I fixed-address
+declaration
+.RS 0.25i
+.PP
+.B fixed-address address\fR [\fB,\fR \fIaddress\fR ... ]\fB;\fR
+.PP
+The \fIfixed-address\fR declaration is used to assign one or more fixed
+IP addresses to a client. It should only appear in a \fIhost\fR
+declaration. If more than one address is supplied, then when the
+client boots, it will be assigned the address that corresponds to the
+network on which it is booting. If none of the addresses in the
+\fIfixed-address\fR statement are valid for the network to which the client
+is connected, that client will not match the \fIhost\fR declaration
+containing that \fIfixed-address\fR declaration. Each \fIaddress\fR
+in the \fIfixed-address\fR declaration should be either an IP address or
+a domain name that resolves to one or more IP addresses.
+.RE
+.PP
+The
+.I fixed-address6
+declaration
+.RS 0.25i
+.PP
+.B fixed-address6 ip6-address\fR ;\fR
+.PP
+The \fIfixed-address6\fR declaration is used to assign a fixed
+IPv6 addresses to a client. It should only appear in a \fIhost\fR
+declaration.
+.RE
+.PP
+The
+.I get-lease-hostnames
+statement
+.RS 0.25i
+.PP
+.B get-lease-hostnames\fR \fIflag\fR\fB;\fR
+.PP
+The \fIget-lease-hostnames\fR statement is used to tell dhcpd whether
+or not to look up the domain name corresponding to the IP address of
+each address in the lease pool and use that address for the DHCP
+\fIhostname\fR option. If \fIflag\fR is true, then this lookup is
+done for all addresses in the current scope. By default, or if
+\fIflag\fR is false, no lookups are done.
+.RE
+.PP
+The
+.I hardware
+statement
+.RS 0.25i
+.PP
+.B hardware \fIhardware-type hardware-address\fB;\fR
+.PP
+In order for a BOOTP client to be recognized, its network hardware
+address must be declared using a \fIhardware\fR clause in the
+.I host
+statement.
+.I hardware-type
+must be the name of a physical hardware interface type. Currently,
+only the
+.B ethernet
+and
+.B token-ring
+types are recognized, although support for a
+.B fddi
+hardware type (and others) would also be desirable.
+The
+.I hardware-address
+should be a set of hexadecimal octets (numbers from 0 through ff)
+separated by colons. The \fIhardware\fR statement may also be used
+for DHCP clients.
+.RE
+.PP
+The
+.I host-identifier option
+statement
+.RS 0.25i
+.PP
+.B host-identifier option \fIoption-name option-data\fB;\fR
+.PP
+This identifies a DHCPv6 client in a
+.I host
+statement.
+.I option-name
+is any option, and
+.I option-data
+is the value for the option that the client will send. The
+.I option-data
+must be a constant value.
+.RE
+.PP
+The
+.I infinite-is-reserved
+statement
+.RS 0.25i
+.PP
+.B infinite-is-reserved \fIflag\fB;\fR
+.PP
+ISC DHCP now supports \'reserved\' leases. See the section on RESERVED LEASES
+below. If this \fIflag\fR is on, the server will automatically reserve leases
+allocated to clients which requested an infinite (0xffffffff) lease-time.
+.PP
+The default is off.
+.RE
+.PP
+The
+.I lease-file-name
+statement
+.RS 0.25i
+.PP
+.B lease-file-name \fIname\fB;\fR
+.PP
+.I Name
+should be the name of the DHCP server's lease file. By default, this
+is DBDIR/dhcpd.leases. This statement \fBmust\fR appear in the outer
+scope of the configuration file - if it appears in some other scope,
+it will have no effect. Furthermore, it has no effect if overridden
+by the
+.B -lf
+flag or the
+.B PATH_DHCPD_DB
+environment variable.
+.RE
+.PP
+The
+.I limit-addrs-per-ia
+statement
+.RS 0.25i
+.PP
+.B limit-addrs-per-ia \fInumber\fB;\fR
+.PP
+By default, the DHCPv6 server will limit clients to one IAADDR per IA
+option, meaning one address. If you wish to permit clients to hang onto
+multiple addresses at a time, configure a larger \fInumber\fR here.
+.PP
+Note that there is no present method to configure the server to forcibly
+configure the client with one IP address per each subnet on a shared network.
+This is left to future work.
+.RE
+.PP
+The
+.I dhcpv6-lease-file-name
+statement
+.RS 0.25i
+.PP
+.B dhcpv6-lease-file-name \fIname\fB;\fR
+.PP
+.I Name
+is the name of the lease file to use if and only if the server is running
+in DHCPv6 mode. By default, this is DBDIR/dhcpd6.leases. This statement,
+like
+.I lease-file-name,
+\fBmust\fR appear in the outer scope of the configuration file. It
+has no effect if overridden by the
+.B -lf
+flag or the
+.B PATH_DHCPD6_DB
+environment variable. If
+.I dhcpv6-lease-file-name
+is not specified, but
+.I lease-file-name
+is, the latter value will be used.
+.RE
+.PP
+The
+.I local-port
+statement
+.RS 0.25i
+.PP
+.B local-port \fIport\fB;\fR
+.PP
+This statement causes the DHCP server to listen for DHCP requests on
+the UDP port specified in \fIport\fR, rather than on port 67.
+.RE
+.PP
+The
+.I local-address
+statement
+.RS 0.25i
+.PP
+.B local-address \fIaddress\fB;\fR
+.PP
+This statement causes the DHCP server to listen for DHCP requests sent
+to the specified \fIaddress\fR, rather than requests sent to all addresses.
+Since serving directly attached DHCP clients implies that the server must
+respond to requests sent to the all-ones IP address, this option cannot be
+used if clients are on directly attached networks...it is only realistically
+useful for a server whose only clients are reached via unicasts, such as via
+DHCP relay agents.
+.PP
+Note: This statement is only effective if the server was compiled using
+the USE_SOCKETS #define statement, which is default on a small number of
+operating systems, and must be explicitly chosen at compile-time for all
+others. You can be sure if your server is compiled with USE_SOCKETS if
+you see lines of this format at startup:
+.PP
+ Listening on Socket/eth0
+.PP
+Note also that since this bind()s all DHCP sockets to the specified
+address, that only one address may be supported in a daemon at a given
+time.
+.RE
+.PP
+The
+.I log-facility
+statement
+.RS 0.25i
+.PP
+.B log-facility \fIfacility\fB;\fR
+.PP
+This statement causes the DHCP server to do all of its logging on the
+specified log facility once the dhcpd.conf file has been read. By
+default the DHCP server logs to the daemon facility. Possible log
+facilities include auth, authpriv, cron, daemon, ftp, kern, lpr, mail,
+mark, news, ntp, security, syslog, user, uucp, and local0 through
+local7. Not all of these facilities are available on all systems,
+and there may be other facilities available on other systems.
+.PP
+In addition to setting this value, you may need to modify your
+.I syslog.conf
+file to configure logging of the DHCP server. For example, you might
+add a line like this:
+.PP
+.nf
+ local7.debug /var/log/dhcpd.log
+.fi
+.PP
+The syntax of the \fIsyslog.conf\fR file may be different on some
+operating systems - consult the \fIsyslog.conf\fR manual page to be
+sure. To get syslog to start logging to the new file, you must first
+create the file with correct ownership and permissions (usually, the
+same owner and permissions of your /var/log/messages or
+/usr/adm/messages file should be fine) and send a SIGHUP to syslogd.
+Some systems support log rollover using a shell script or program
+called newsyslog or logrotate, and you may be able to configure this
+as well so that your log file doesn't grow uncontrollably.
+.PP
+Because the \fIlog-facility\fR setting is controlled by the dhcpd.conf
+file, log messages printed while parsing the dhcpd.conf file or before
+parsing it are logged to the default log facility. To prevent this,
+see the README file included with this distribution, which describes
+how to change the default log facility. When this parameter is used,
+the DHCP server prints its startup message a second time after parsing
+the configuration file, so that the log will be as complete as
+possible.
+.RE
+.PP
+The
+.I max-lease-time
+statement
+.RS 0.25i
+.PP
+.B max-lease-time \fItime\fR\fB;\fR
+.PP
+.I Time
+should be the maximum length in seconds that will be assigned to a
+lease.
+If not defined, the default maximum lease time is 86400.
+The only exception to this is that Dynamic BOOTP lease
+lengths, which are not specified by the client, are not limited by
+this maximum.
+.RE
+.PP
+The
+.I min-lease-time
+statement
+.RS 0.25i
+.PP
+.B min-lease-time \fItime\fR\fB;\fR
+.PP
+.I Time
+should be the minimum length in seconds that will be assigned to a
+lease.
+The default is the minimum of 300 seconds or
+\fBmax-lease-time\fR.
+.RE
+.PP
+The
+.I min-secs
+statement
+.RS 0.25i
+.PP
+.B min-secs \fIseconds\fR\fB;\fR
+.PP
+.I Seconds
+should be the minimum number of seconds since a client began trying to
+acquire a new lease before the DHCP server will respond to its request.
+The number of seconds is based on what the client reports, and the maximum
+value that the client can report is 255 seconds. Generally, setting this
+to one will result in the DHCP server not responding to the client's first
+request, but always responding to its second request.
+.PP
+This can be used
+to set up a secondary DHCP server which never offers an address to a client
+until the primary server has been given a chance to do so. If the primary
+server is down, the client will bind to the secondary server, but otherwise
+clients should always bind to the primary. Note that this does not, by
+itself, permit a primary server and a secondary server to share a pool of
+dynamically-allocatable addresses.
+.RE
+.PP
+The
+.I next-server
+statement
+.RS 0.25i
+.PP
+.B next-server\fR \fIserver-name\fR\fB;\fR
+.PP
+The \fInext-server\fR statement is used to specify the host address of
+the server from which the initial boot file (specified in the
+\fIfilename\fR statement) is to be loaded. \fIServer-name\fR should
+be a numeric IP address or a domain name.
+.RE
+.PP
+The
+.I omapi-port
+statement
+.RS 0.25i
+.PP
+.B omapi-port\fR \fIport\fR\fB;\fR
+.PP
+The \fIomapi-port\fR statement causes the DHCP server to listen for
+OMAPI connections on the specified port. This statement is required
+to enable the OMAPI protocol, which is used to examine and modify the
+state of the DHCP server as it is running.
+.RE
+.PP
+The
+.I one-lease-per-client
+statement
+.RS 0.25i
+.PP
+.B one-lease-per-client \fIflag\fR\fB;\fR
+.PP
+If this flag is enabled, whenever a client sends a DHCPREQUEST for a
+particular lease, the server will automatically free any other leases
+the client holds. This presumes that when the client sends a
+DHCPREQUEST, it has forgotten any lease not mentioned in the
+DHCPREQUEST - i.e., the client has only a single network interface
+.I and
+it does not remember leases it's holding on networks to which it is
+not currently attached. Neither of these assumptions are guaranteed
+or provable, so we urge caution in the use of this statement.
+.RE
+.PP
+The
+.I pid-file-name
+statement
+.RS 0.25i
+.PP
+.B pid-file-name
+.I name\fR\fB;\fR
+.PP
+.I Name
+should be the name of the DHCP server's process ID file. This is the
+file in which the DHCP server's process ID is stored when the server
+starts. By default, this is RUNDIR/dhcpd.pid. Like the
+.I lease-file-name
+statement, this statement must appear in the outer scope
+of the configuration file. It has no effect if overridden by the
+.B -pf
+flag or the
+.B PATH_DHCPD_PID
+environment variable.
+.PP
+The
+.I dhcpv6-pid-file-name
+statement
+.RS 0.25i
+.PP
+.B dhcpv6-pid-file-name \fIname\fB;\fR
+.PP
+.I Name
+is the name of the pid file to use if and only if the server is running
+in DHCPv6 mode. By default, this is DBDIR/dhcpd6.pid. This statement,
+like
+.I pid-file-name,
+\fBmust\fR appear in the outer scope of the configuration file. It
+has no effect if overridden by the
+.B -pf
+flag or the
+.B PATH_DHCPD6_PID
+environment variable. If
+.I dhcpv6-pid-file-name
+is not specified, but
+.I pid-file-name
+is, the latter value will be used.
+.RE
+.PP
+The
+.I ping-check
+statement
+.RS 0.25i
+.PP
+.B ping-check
+.I flag\fR\fB;\fR
+.PP
+When the DHCP server is considering dynamically allocating an IP
+address to a client, it first sends an ICMP Echo request (a \fIping\fR)
+to the address being assigned. It waits for a second, and if no
+ICMP Echo response has been heard, it assigns the address. If a
+response \fIis\fR heard, the lease is abandoned, and the server does
+not respond to the client.
+.PP
+This \fIping check\fR introduces a default one-second delay in responding
+to DHCPDISCOVER messages, which can be a problem for some clients. The
+default delay of one second may be configured using the ping-timeout
+parameter. The ping-check configuration parameter can be used to control
+checking - if its value is false, no ping check is done.
+.RE
+.PP
+The
+.I ping-timeout
+statement
+.RS 0.25i
+.PP
+.B ping-timeout
+.I seconds\fR\fB;\fR
+.PP
+If the DHCP server determined it should send an ICMP echo request (a
+\fIping\fR) because the ping-check statement is true, ping-timeout allows
+you to configure how many seconds the DHCP server should wait for an
+ICMP Echo response to be heard, if no ICMP Echo response has been received
+before the timeout expires, it assigns the address. If a response \fIis\fR
+heard, the lease is abandoned, and the server does not respond to the client.
+If no value is set, ping-timeout defaults to 1 second.
+.RE
+.PP
+The
+.I preferred-lifetime
+statement
+.RS 0.25i
+.PP
+.B preferred-lifetime
+.I seconds\fR\fB;\fR
+.PP
+IPv6 addresses have \'valid\' and \'preferred\' lifetimes. The valid lifetime
+determines at what point at lease might be said to have expired, and is no
+longer useable. A preferred lifetime is an advisory condition to help
+applications move off of the address and onto currently valid addresses
+(should there still be any open TCP sockets or similar).
+.PP
+The preferred lifetime defaults to the renew+rebind timers, or 3/4 the
+default lease time if none were specified.
+.RE
+.PP
+The
+.I remote-port
+statement
+.RS 0.25i
+.PP
+.B remote-port \fIport\fB;\fR
+.PP
+This statement causes the DHCP server to transmit DHCP responses to DHCP
+clients upon the UDP port specified in \fIport\fR, rather than on port 68.
+In the event that the UDP response is transmitted to a DHCP Relay, the
+server generally uses the \fBlocal-port\fR configuration value. Should the
+DHCP Relay happen to be addressed as 127.0.0.1, however, the DHCP Server
+transmits its response to the \fBremote-port\fR configuration value. This
+is generally only useful for testing purposes, and this configuration value
+should generally not be used.
+.RE
+.PP
+The
+.I server-identifier
+statement
+.RS 0.25i
+.PP
+.B server-identifier \fIhostname\fR\fB;\fR
+.PP
+The server-identifier statement can be used to define the value that
+is sent in the DHCP Server Identifier option for a given scope. The
+value specified \fBmust\fR be an IP address for the DHCP server, and
+must be reachable by all clients served by a particular scope.
+.PP
+The use of the server-identifier statement is not recommended - the only
+reason to use it is to force a value other than the default value to be
+sent on occasions where the default value would be incorrect. The default
+value is the first IP address associated with the physical network interface
+on which the request arrived.
+.PP
+The usual case where the
+\fIserver-identifier\fR statement needs to be sent is when a physical
+interface has more than one IP address, and the one being sent by default
+isn't appropriate for some or all clients served by that interface.
+Another common case is when an alias is defined for the purpose of
+having a consistent IP address for the DHCP server, and it is desired
+that the clients use this IP address when contacting the server.
+.PP
+Supplying a value for the dhcp-server-identifier option is equivalent
+to using the server-identifier statement.
+.RE
+.PP
+The
+.I server-duid
+statement
+.RS 0.25i
+.PP
+.B server-duid \fILLT\fR [ \fIhardware-type\fR \fItimestamp\fR \fIhardware-address\fR ] \fB;\fR
+
+.B server-duid \fIEN\fR \fIenterprise-number\fR \fIenterprise-identifier\fR \fB;\fR
+
+.B server-duid \fILL\fR [ \fIhardware-type\fR \fIhardware-address\fR ] \fB;\fR
+.PP
+The server-duid statement configures the server DUID. You may pick either
+LLT (link local address plus time), EN (enterprise), or LL (link local).
+.PP
+If you choose LLT or LL, you may specify the exact contents of the DUID.
+Otherwise the server will generate a DUID of the specified type.
+.PP
+If you choose EN, you must include the enterprise number and the
+enterprise-identifier.
+.PP
+The default server-duid type is LLT.
+.RE
+.PP
+The
+.I server-name
+statement
+.RS 0.25i
+.PP
+.B server-name "\fIname\fB";\fR
+.PP
+The \fIserver-name\fR statement can be used to inform the client of
+the name of the server from which it is booting. \fIName\fR should
+be the name that will be provided to the client.
+.RE
+.PP
+The
+.I site-option-space
+statement
+.RS 0.25i
+.PP
+.B site-option-space "\fIname\fB";\fR
+.PP
+The \fIsite-option-space\fR statement can be used to determine from
+what option space site-local options will be taken. This can be used
+in much the same way as the \fIvendor-option-space\fR statement.
+Site-local options in DHCP are those options whose numeric codes are
+greater than 224. These options are intended for site-specific
+uses, but are frequently used by vendors of embedded hardware that
+contains DHCP clients. Because site-specific options are allocated
+on an ad hoc basis, it is quite possible that one vendor's DHCP client
+might use the same option code that another vendor's client uses, for
+different purposes. The \fIsite-option-space\fR option can be used
+to assign a different set of site-specific options for each such
+vendor, using conditional evaluation (see \fBdhcp-eval (5)\fR for
+details).
+.RE
+.PP
+The
+.I stash-agent-options
+statement
+.RS 0.25i
+.PP
+.B stash-agent-options \fIflag\fB;\fR
+.PP
+If the \fIstash-agent-options\fR parameter is true for a given client,
+the server will record the relay agent information options sent during
+the client's initial DHCPREQUEST message when the client was in the
+SELECTING state and behave as if those options are included in all
+subsequent DHCPREQUEST messages sent in the RENEWING state. This
+works around a problem with relay agent information options, which is
+that they usually not appear in DHCPREQUEST messages sent by the
+client in the RENEWING state, because such messages are unicast
+directly to the server and not sent through a relay agent.
+.RE
+.PP
+The
+.I update-conflict-detection
+statement
+.RS 0.25i
+.PP
+.B update-conflict-detection \fIflag\fB;\fR
+.PP
+If the \fIupdate-conflict-detection\fR parameter is true, the server will
+perform standard DHCID multiple-client, one-name conflict detection. If
+the parameter has been set false, the server will skip this check and
+instead simply tear down any previous bindings to install the new
+binding without question. The default is true.
+.RE
+.PP
+The
+.I update-optimization
+statement
+.RS 0.25i
+.PP
+.B update-optimization \fIflag\fB;\fR
+.PP
+If the \fIupdate-optimization\fR parameter is false for a given client,
+the server will attempt a DNS update for that client each time the
+client renews its lease, rather than only attempting an update when it
+appears to be necessary. This will allow the DNS to heal from
+database inconsistencies more easily, but the cost is that the DHCP
+server must do many more DNS updates. We recommend leaving this option
+enabled, which is the default. This option only affects the behavior of
+the interim DNS update scheme, and has no effect on the ad-hoc DNS update
+scheme. If this parameter is not specified, or is true, the DHCP server
+will only update when the client information changes, the client gets a
+different lease, or the client's lease expires.
+.RE
+.PP
+The
+.I update-static-leases
+statement
+.RS 0.25i
+.PP
+.B update-static-leases \fIflag\fB;\fR
+.PP
+The \fIupdate-static-leases\fR flag, if enabled, causes the DHCP
+server to do DNS updates for clients even if those clients are being
+assigned their IP address using a \fIfixed-address\fR statement - that
+is, the client is being given a static assignment. This can only
+work with the \fIinterim\fR DNS update scheme. It is not
+recommended because the DHCP server has no way to tell that the update
+has been done, and therefore will not delete the record when it is not
+in use. Also, the server must attempt the update each time the
+client renews its lease, which could have a significant performance
+impact in environments that place heavy demands on the DHCP server.
+.RE
+.PP
+The
+.I use-host-decl-names
+statement
+.RS 0.25i
+.PP
+.B use-host-decl-names \fIflag\fB;\fR
+.PP
+If the \fIuse-host-decl-names\fR parameter is true in a given scope,
+then for every host declaration within that scope, the name provided
+for the host declaration will be supplied to the client as its
+hostname. So, for example,
+.PP
+.nf
+ group {
+ use-host-decl-names on;
+
+ host joe {
+ hardware ethernet 08:00:2b:4c:29:32;
+ fixed-address joe.fugue.com;
+ }
+ }
+
+is equivalent to
+
+ host joe {
+ hardware ethernet 08:00:2b:4c:29:32;
+ fixed-address joe.fugue.com;
+ option host-name "joe";
+ }
+.fi
+.PP
+An \fIoption host-name\fR statement within a host declaration will
+override the use of the name in the host declaration.
+.PP
+It should be noted here that most DHCP clients completely ignore the
+host-name option sent by the DHCP server, and there is no way to
+configure them not to do this. So you generally have a choice of
+either not having any hostname to client IP address mapping that the
+client will recognize, or doing DNS updates. It is beyond
+the scope of this document to describe how to make this
+determination.
+.RE
+.PP
+The
+.I use-lease-addr-for-default-route
+statement
+.RS 0.25i
+.PP
+.B use-lease-addr-for-default-route \fIflag\fR\fB;\fR
+.PP
+If the \fIuse-lease-addr-for-default-route\fR parameter is true in a
+given scope, then instead of sending the value specified in the
+routers option (or sending no value at all), the IP address of the
+lease being assigned is sent to the client. This supposedly causes
+Win95 machines to ARP for all IP addresses, which can be helpful if
+your router is configured for proxy ARP. The use of this feature is
+not recommended, because it won't work for many DHCP clients.
+.RE
+.PP
+The
+.I vendor-option-space
+statement
+.RS 0.25i
+.PP
+.B vendor-option-space \fIstring\fR\fB;\fR
+.PP
+The \fIvendor-option-space\fR parameter determines from what option
+space vendor options are taken. The use of this configuration
+parameter is illustrated in the \fBdhcp-options(5)\fR manual page, in
+the \fIVENDOR ENCAPSULATED OPTIONS\fR section.
+.RE
+.SH SETTING PARAMETER VALUES USING EXPRESSIONS
+Sometimes it's helpful to be able to set the value of a DHCP server
+parameter based on some value that the client has sent. To do this,
+you can use expression evaluation. The
+.B dhcp-eval(5)
+manual page describes how to write expressions. To assign the result
+of an evaluation to an option, define the option as follows:
+.nf
+.sp 1
+ \fImy-parameter \fB= \fIexpression \fB;\fR
+.fi
+.PP
+For example:
+.nf
+.sp 1
+ ddns-hostname = binary-to-ascii (16, 8, "-",
+ substring (hardware, 1, 6));
+.fi
+.RE
+.SH RESERVED LEASES
+It's often useful to allocate a single address to a single client, in
+approximate perpetuity. Host statements with \fBfixed-address\fR clauses
+exist to a certain extent to serve this purpose, but because host statements
+are intended to approximate \'static configuration\', they suffer from not
+being referenced in a littany of other Server Services, such as dynamic DNS,
+failover, \'on events\' and so forth.
+.PP
+If a standard dynamic lease, as from any range statement, is marked
+\'reserved\', then the server will only allocate this lease to the client it
+is identified by (be that by client identifier or hardware address).
+.PP
+In practice, this means that the lease follows the normal state engine, enters
+ACTIVE state when the client is bound to it, expires, or is released, and any
+events or services that would normally be supplied during these events are
+processed normally, as with any other dynamic lease. The only difference
+is that failover servers treat reserved leases as special when they enter
+the FREE or BACKUP states - each server applies the lease into the state it
+may allocate from - and the leases are not placed on the queue for allocation
+to other clients. Instead they may only be \'found\' by client identity. The
+result is that the lease is only offered to the returning client.
+.PP
+Care should probably be taken to ensure that the client only has one lease
+within a given subnet that it is identified by.
+.PP
+Leases may be set \'reserved\' either through OMAPI, or through the
+\'infinite-is-reserved\' configuration option (if this is applicable to your
+environment and mixture of clients).
+.PP
+It should also be noted that leases marked \'reserved\' are effectively treated
+the same as leases marked \'bootp\'.
+.RE
+.SH REFERENCE: OPTION STATEMENTS
+DHCP option statements are documented in the
+.B dhcp-options(5)
+manual page.
+.SH REFERENCE: EXPRESSIONS
+Expressions used in DHCP option statements and elsewhere are
+documented in the
+.B dhcp-eval(5)
+manual page.
+.SH SEE ALSO
+dhcpd(8), dhcpd.leases(5), dhcp-options(5), dhcp-eval(5), RFC2132, RFC2131.
+.SH AUTHOR
+.B dhcpd.conf(5)
+was written by Ted Lemon
+under a contract with Vixie Labs. Funding
+for this project was provided by Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at
+.B https://www.isc.org.
diff --git a/server/dhcpd.leases.5 b/server/dhcpd.leases.5
new file mode 100644
index 0000000..ba584b1
--- /dev/null
+++ b/server/dhcpd.leases.5
@@ -0,0 +1,289 @@
+.\" dhcpd.leases.5
+.\"
+.\" Copyright (c) 2004,2009 by Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (c) 1996-2003 by Internet Software Consortium
+.\"
+.\" 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 ISC DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Internet Systems Consortium, Inc.
+.\" 950 Charter Street
+.\" Redwood City, CA 94063
+.\" <info@isc.org>
+.\" https://www.isc.org/
+.\"
+.\" This software has been written for Internet Systems Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about Internet Systems Consortium, see
+.\" ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+.\" see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+.\" ``http://www.nominum.com''.
+.\"
+.\" $Id: dhcpd.leases.5,v 1.14.24.2 2011-04-22 13:30:14 tomasz Exp $
+.\"
+.TH dhcpd.leases 5
+.SH NAME
+dhcpd.leases - DHCP client lease database
+.SH DESCRIPTION
+The Internet Systems Consortium DHCP Server keeps a persistent
+database of leases that it has assigned. This database is a free-form
+ASCII file containing a series of lease declarations. Every time a
+lease is acquired, renewed or released, its new value is recorded at
+the end of the lease file. So if more than one declaration appears
+for a given lease, the last one in the file is the current one.
+.PP
+When dhcpd is first installed, there is no lease database. However,
+dhcpd requires that a lease database be present before it will start.
+To make the initial lease database, just create an empty file called
+DBDIR/dhcpd.leases. You can do this with:
+.PP
+.nf
+ touch DBDIR/dhcpd.leases
+.fi
+.PP
+In order to prevent the lease database from growing without bound, the
+file is rewritten from time to time. First, a temporary lease
+database is created and all known leases are dumped to it. Then, the
+old lease database is renamed DBDIR/dhcpd.leases~. Finally, the
+newly written lease database is moved into place.
+.SH FORMAT
+Lease descriptions are stored in a format that is parsed by the same
+recursive descent parser used to read the
+.B dhcpd.conf(5)
+and
+.B dhclient.conf(5)
+files. Lease files can contain lease declarations, and also group and
+subgroup declarations, host declarations and failover state
+declarations. Group, subgroup and host declarations are used to
+record objects created using the OMAPI protocol.
+.PP
+The lease file is a log-structured file - whenever a lease changes,
+the contents of that lease are written to the end of the file. This
+means that it is entirely possible and quite reasonable for there to
+be two or more declarations of the same lease in the lease file at the
+same time. In that case, the instance of that particular lease that
+appears last in the file is the one that is in effect.
+.PP
+Group, subgroup and host declarations in the lease file are handled in
+the same manner, except that if any of these objects are deleted, a
+\fIrubout\fR is written to the lease file. This is just the same
+declaration, with \fB{ deleted; }\fR in the scope of the
+declaration. When the lease file is rewritten, any such rubouts that
+can be eliminated are eliminated. It is possible to delete a
+declaration in the \fBdhcpd.conf\fR file; in this case, the rubout
+can never be eliminated from the \fBdhcpd.leases\fR file.
+.SH THE LEASE DECLARATION
+.PP
+.B lease \fIip-address\fB { \fIstatements...\fB }
+.PP
+Each lease declaration includes the single IP address that has been
+leased to the client. The statements within the braces define the
+duration of the lease and to whom it is assigned.
+.PP
+.nf
+.B starts \fIdate\fB;\fR
+.B ends \fIdate\fB;\fR
+.B tstp \fIdate\fB;\fR
+.B tsfp \fIdate\fB;\fR
+.B atsfp \fIdate\fB;\fR
+.B cltt \fIdate\fB;\fR
+.fi
+.PP
+The start and end time of a lease are recorded using the \fBstarts\fR
+and \fBends\fR statements. The \fBtstp\fR statement is specified if
+the failover protocol is being used, and indicates what time the peer
+has been told the lease expires. The \fBtsfp\fR statement is
+also specified if the failover protocol is being used, and indicates
+the lease expiry time that the peer has acknowledged.
+The \fBatsfp\fR statement is the actual time sent from the failover
+partner.
+The \fBcltt\fR statement is the client's last transaction time.
+.PP
+The \fIdate\fR is specified in two ways, depending on the configuration
+value for the \fBdb-time-format\fR parameter. If it was set to \fIdefault\fR,
+then the \fIdate\fR fields appear as follows:
+.PP
+.I weekday year\fB/\fImonth\fB/\fIday hour\fB:\fIminute\fB:\fIsecond\fR
+.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. The day of week is ignored on input. 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.
+.PP
+Lease times are specified in Universal Coordinated Time (UTC), not in
+the local time zone. There is probably nowhere in the world where the
+times recorded on a lease are always the same as wall clock times. On
+most unix machines, you can display the current time in UTC by typing
+\fBdate -u\fR.
+.PP
+If the \fBdb-time-format\fR was configured to \fIlocal\fR, then
+the \fIdate\fR fields appear as follows:
+.PP
+ \fBepoch\fR \fI<seconds-since-epoch>\fR\fB; #\fR \fI<day-name> <month-name>
+<day-number> <hours>\fR\fB:\fR\fI<minutes>\fR\fB:\fR\fI<seconds> <year>\fR
+.PP
+The \fIseconds-since-epoch\fR is as according to the system's local clock (often
+referred to as "unix time"). The \fB#\fR symbol supplies a comment that
+describes what actual time this is as according to the system's configured
+timezone, at the time the value was written. It is provided only for human
+inspection.
+.PP
+If a lease will never expire, \fIdate\fR is \fBnever\fR instead of an
+actual date.
+.PP
+.B hardware \fIhardware-type mac-address\fB;\fR
+.PP
+The hardware statement records the MAC address of the network
+interface on which the lease will be used. It is specified as a
+series of hexadecimal octets, separated by colons.
+.PP
+.B uid \fIclient-identifier\fB;\fR
+.PP
+The \fBuid\fR statement records the client identifier used by the
+client to acquire the lease. Clients are not required to send client
+identifiers, and this statement only appears if the client did in fact
+send one. Client identifiers are normally an ARP type (1 for
+ethernet) followed by the MAC address, just like in the \fBhardware\fI
+statement, but this is not required.
+.PP
+The client identifier is recorded as a colon-separated hexadecimal
+list or as a quoted string. If it is recorded as a quoted string and
+it contains one or more non-printable characters, those characters are
+represented as octal escapes - a backslash character followed by three
+octal digits.
+.PP
+.B client-hostname "\fIhostname\fB";\fR
+.PP
+Most DHCP clients will send their hostname in the \fIhost-name\fR
+option. If a client sends its hostname in this way, the hostname is
+recorded on the lease with a \fBclient-hostname\fR statement. This
+is not required by the protocol, however, so many specialized DHCP
+clients do not send a host-name option.
+.PP
+.B abandoned;
+.PP
+The \fBabandoned\fR statement indicates that the DHCP server has
+abandoned the lease. In that case, the \fBabandoned\fR statement
+will be used to indicate that the lease should not be reassigned.
+Please see the \fBdhcpd.conf(5)\fR manual page for information about
+abandoned leases.
+.PP
+.B binding state \fIstate\fB;
+.B next binding state \fIstate\fB;
+.PP
+The \fBbinding state\fR statement declares the lease's binding state.
+When the DHCP server is not configured to use the failover protocol, a
+lease's binding state will be either \fBactive\fR or \fBfree\fR. The
+failover protocol adds some additional transitional states, as well as
+the \fBbackup\fR state, which indicates that the lease is available
+for allocation by the failover secondary.
+.PP
+The \fBnext binding state\fR statement indicates what state the lease
+will move to when the current state expires. The time when the
+current state expires is specified in the \fIends\fR statement.
+.PP
+.B option agent.circuit-id \fIstring\fR;
+.B option agent.remote-id \fIstring\fR;
+.PP
+The \fBoption agent.circuit-id\fR and \fBoption agent.remote-id\fR
+statements are used to record the circuit ID and remote ID options
+send by the relay agent, if the relay agent uses the \fIrelay agent
+information option\fR. This allows these options to be used
+consistently in conditional evaluations even when the client is
+contacting the server directly rather than through its relay agent.
+.PP
+.B set \fIvariable\fB = \fIvalue\fB;
+.PP
+The \fBset\fR statement sets the value of a variable on the lease.
+For general information on variables, see the \fBdhcp-eval(5)\fR
+manual page.
+.PP
+.B The \fIddns-text\fB variable
+.PP
+The \fIddns-text\fR variable is used to record the value of the
+client's TXT identification record when the interim ddns update
+style has been used to update the DNS for a particular lease.
+.PP
+.B The \fIddns-fwd-name\fB variable
+.PP
+The \fIddns-fwd-name\fB variable records the value of the name used in
+updating the client's A record if a DDNS update has been successfully
+done by the server. The server may also have used this name to
+update the client's PTR record.
+.PP
+.B The \fIddns-client-fqdn\fB variable
+.PP
+If the server is configured to use the interim ddns update style, and
+is also configured to allow clients to update their own fqdns, and the
+client did in fact update its own fqdn, then the
+\fIddns-client-fqdn\fR variable records the name that the client has
+indicated it is using. This is the name that the server will have
+used to update the client's PTR record in this case.
+.PP
+.B The \fIddns-rev-name\fB variable
+.PP
+If the server successfully updates the client's PTR record, this
+variable will record the name that the DHCP server used for the PTR
+record. The name to which the PTR record points will be either the
+\fIddns-fwd-name\fR or the \fIddns-client-fqdn\fR.
+.PP
+.B The \fIvendor-class-identifier\fB variable
+.PP
+The server retains the client-supplied Vendor Class Identifier option
+for informational purposes, and to render them in DHCPLEASEQUERY responses.
+.PP
+.B on \fIevents\fB { \fIstatements...\fB }
+The \fBon\fI statement records a list of statements to execute if a
+certain event occurs. The possible events that can occur for an
+active lease are \fBrelease\fR and \fBexpiry\fR. More than one event
+can be specified - if so, the events are separated by '|' characters.
+.PP
+.B bootp;
+.B reserved;
+These two statements are effectively flags. If present, they indicate that
+the BOOTP and RESERVED failover flags, respectively, should be set. BOOTP
+and RESERVED dynamic leases are treated differently than normal dynamic leases,
+as they may only be used by the client to which they are currently allocated.
+.RE
+.SH THE FAILOVER PEER STATE DECLARATION
+The state of any failover peering arrangements is also recorded in the
+lease file, using the \fBfailover peer\fR statement:
+.PP
+.nf
+.B failover peer "\fIname\fB" state {
+.B my state \fIstate\fB at \fIdate\fB;
+.B peer state \fIstate\fB at \fIdate\fB;
+.B }
+.fi
+.PP
+The states of the peer named \fIname\fR is being recorded. Both the
+state of the running server (\fBmy state\fR) and the other failover
+partner (\fIpeer state\fR) are recorded. The following states are
+possible: \fBunknown-state\fR, \fBpartner-down\fR, \fBnormal\fR,
+\fBcommunications-interrupted\fR, \fBresolution-interrupted\fR,
+\fBpotential-conflict\fR, \fBrecover\fR, \fBrecover-done\fR,
+\fBshutdown\fR, \fBpaused\fR, and \fBstartup\fR.
+.B DBDIR/dhcpd.leases
+.SH SEE ALSO
+dhcpd(8), dhcp-options(5), dhcp-eval(5), dhcpd.conf(5), RFC2132, RFC2131.
+.SH AUTHOR
+.B dhcpd(8)
+was written by Ted Lemon
+under a contract with Vixie Labs. Funding
+for this project was provided by Internet Systems Consortium.
+Information about Internet Systems Consortium can be found at:
+.B https://www.isc.org/
diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c
new file mode 100644
index 0000000..9daff89
--- /dev/null
+++ b/server/dhcpleasequery.c
@@ -0,0 +1,1268 @@
+/*
+ * Copyright (C) 2006-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "dhcpd.h"
+
+/*
+ * TODO: RFC4388 specifies that the server SHOULD return the same
+ * options it would for a DHCREQUEST message, if no Parameter
+ * Request List option (option 55) is passed. We do not do that.
+ *
+ * TODO: RFC4388 specifies the creation of a "non-sensitive options"
+ * configuration list, and that these SHOULD be returned. We
+ * have no such list.
+ *
+ * TODO: RFC4388 says the server SHOULD use RFC3118, "Authentication
+ * for DHCP Messages".
+ *
+ * TODO: RFC4388 specifies that you SHOULD insure that you cannot be
+ * DoS'ed by DHCPLEASEQUERY message.
+ */
+
+/*
+ * If you query by hardware address or by client ID, then you may have
+ * more than one IP address for your query argument. We need to do two
+ * things:
+ *
+ * 1. Find the most recent lease.
+ * 2. Find all additional IP addresses for the query argument.
+ *
+ * We do this by looking through all of the leases associated with a
+ * given hardware address or client ID. We use the cltt (client last
+ * transaction time) of the lease, which only has a resolution of one
+ * second, so we might not actually give the very latest IP.
+ */
+
+static struct lease*
+next_hw(const struct lease *lease) {
+ /* INSIST(lease != NULL); */
+ return lease->n_hw;
+}
+
+static struct lease*
+next_uid(const struct lease *lease) {
+ /* INSIST(lease != NULL); */
+ return lease->n_uid;
+}
+
+void
+get_newest_lease(struct lease **retval,
+ struct lease *lease,
+ struct lease *(*next)(const struct lease *)) {
+
+ struct lease *p;
+ struct lease *newest;
+
+ /* INSIST(newest != NULL); */
+ /* INSIST(next != NULL); */
+
+ *retval = NULL;
+
+ if (lease == NULL) {
+ return;
+ }
+
+ newest = lease;
+ for (p=next(lease); p != NULL; p=next(p)) {
+ if (newest->binding_state == FTS_ACTIVE) {
+ if ((p->binding_state == FTS_ACTIVE) &&
+ (p->cltt > newest->cltt)) {
+ newest = p;
+ }
+ } else {
+ if (p->ends > newest->ends) {
+ newest = p;
+ }
+ }
+ }
+
+ lease_reference(retval, newest, MDL);
+}
+
+static int
+get_associated_ips(const struct lease *lease,
+ struct lease *(*next)(const struct lease *),
+ const struct lease *newest,
+ u_int32_t *associated_ips,
+ unsigned int associated_ips_size) {
+
+ const struct lease *p;
+ int cnt;
+
+ /* INSIST(next != NULL); */
+ /* INSIST(associated_ips != NULL); */
+
+ if (lease == NULL) {
+ return 0;
+ }
+
+ cnt = 0;
+ for (p=lease; p != NULL; p=next(p)) {
+ if ((p->binding_state == FTS_ACTIVE) && (p != newest)) {
+ if (cnt < associated_ips_size) {
+ memcpy(&associated_ips[cnt],
+ p->ip_addr.iabuf,
+ sizeof(associated_ips[cnt]));
+ }
+ cnt++;
+ }
+ }
+ return cnt;
+}
+
+
+void
+dhcpleasequery(struct packet *packet, int ms_nulltp) {
+ char msgbuf[256];
+ char dbg_info[128];
+ struct iaddr cip;
+ struct iaddr gip;
+ struct data_string uid;
+ struct hardware h;
+ struct lease *tmp_lease;
+ struct lease *lease;
+ int want_associated_ip;
+ int assoc_ip_cnt;
+ u_int32_t assoc_ips[40]; /* XXXSK: arbitrary maximum number of IPs */
+ const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);
+
+ unsigned char dhcpMsgType;
+ const char *dhcp_msg_type_name;
+ struct subnet *subnet;
+ struct group *relay_group;
+ struct option_state *options;
+ struct option_cache *oc;
+ int allow_leasequery;
+ int ignorep;
+ u_int32_t lease_duration;
+ u_int32_t time_renewal;
+ u_int32_t time_rebinding;
+ u_int32_t time_expiry;
+ u_int32_t client_last_transaction_time;
+ struct sockaddr_in to;
+ struct in_addr siaddr;
+ struct data_string prl;
+ struct data_string *prl_ptr;
+
+ int i;
+ struct interface_info *interface;
+
+ /* INSIST(packet != NULL); */
+
+ /*
+ * Prepare log information.
+ */
+ snprintf(msgbuf, sizeof(msgbuf),
+ "DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));
+
+ /*
+ * We can't reply if there is no giaddr field.
+ */
+ if (!packet->raw->giaddr.s_addr) {
+ log_info("%s: missing giaddr, ciaddr is %s, no reply sent",
+ msgbuf, inet_ntoa(packet->raw->ciaddr));
+ return;
+ }
+
+ /*
+ * Initially we use the 'giaddr' subnet options scope to determine if
+ * the giaddr-identified relay agent is permitted to perform a
+ * leasequery. The subnet is not required, and may be omitted, in
+ * which case we are essentially interrogating the root options class
+ * to find a globally permit.
+ */
+ gip.len = sizeof(packet->raw->giaddr);
+ memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));
+
+ subnet = NULL;
+ find_subnet(&subnet, gip, MDL);
+ if (subnet != NULL)
+ relay_group = subnet->group;
+ else
+ relay_group = root_group;
+
+ subnet_dereference(&subnet, MDL);
+
+ options = NULL;
+ if (!option_state_allocate(&options, MDL)) {
+ log_error("No memory for option state.");
+ log_info("%s: out of memory, no reply sent", msgbuf);
+ return;
+ }
+
+ execute_statements_in_scope(NULL,
+ packet,
+ NULL,
+ NULL,
+ packet->options,
+ options,
+ &global_scope,
+ relay_group,
+ NULL);
+
+ for (i=packet->class_count-1; i>=0; i--) {
+ execute_statements_in_scope(NULL,
+ packet,
+ NULL,
+ NULL,
+ packet->options,
+ options,
+ &global_scope,
+ packet->classes[i]->group,
+ relay_group);
+ }
+
+ /*
+ * Because LEASEQUERY has some privacy concerns, default to deny.
+ */
+ allow_leasequery = 0;
+
+ /*
+ * See if we are authorized to do LEASEQUERY.
+ */
+ oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
+ if (oc != NULL) {
+ allow_leasequery = evaluate_boolean_option_cache(&ignorep,
+ packet, NULL, NULL, packet->options,
+ options, &global_scope, oc, MDL);
+ }
+
+ if (!allow_leasequery) {
+ log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
+ option_state_dereference(&options, MDL);
+ return;
+ }
+
+
+ /*
+ * Copy out the client IP address.
+ */
+ cip.len = sizeof(packet->raw->ciaddr);
+ memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));
+
+ /*
+ * If the client IP address is valid (not all zero), then we
+ * are looking for information about that IP address.
+ */
+ assoc_ip_cnt = 0;
+ lease = tmp_lease = NULL;
+ if (memcmp(cip.iabuf, "\0\0\0", 4)) {
+
+ want_associated_ip = 0;
+
+ snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
+ find_lease_by_ip_addr(&lease, cip, MDL);
+
+
+ } else {
+
+ want_associated_ip = 1;
+
+ /*
+ * If the client IP address is all zero, then we will
+ * either look up by the client identifier (if we have
+ * one), or by the MAC address.
+ */
+
+ memset(&uid, 0, sizeof(uid));
+ if (get_option(&uid,
+ &dhcp_universe,
+ packet,
+ NULL,
+ NULL,
+ packet->options,
+ NULL,
+ packet->options,
+ &global_scope,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ MDL)) {
+
+ snprintf(dbg_info,
+ sizeof(dbg_info),
+ "client-id %s",
+ print_hex_1(uid.len, uid.data, 60));
+
+ find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
+ data_string_forget(&uid, MDL);
+ get_newest_lease(&lease, tmp_lease, next_uid);
+ assoc_ip_cnt = get_associated_ips(tmp_lease,
+ next_uid,
+ lease,
+ assoc_ips,
+ nassoc_ips);
+
+ } else {
+
+ if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
+ log_info("%s: hardware length too long, "
+ "no reply sent", msgbuf);
+ option_state_dereference(&options, MDL);
+ return;
+ }
+
+ h.hlen = packet->raw->hlen + 1;
+ h.hbuf[0] = packet->raw->htype;
+ memcpy(&h.hbuf[1],
+ packet->raw->chaddr,
+ packet->raw->hlen);
+
+ snprintf(dbg_info,
+ sizeof(dbg_info),
+ "MAC address %s",
+ print_hw_addr(h.hbuf[0],
+ h.hlen - 1,
+ &h.hbuf[1]));
+
+ find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
+ get_newest_lease(&lease, tmp_lease, next_hw);
+ assoc_ip_cnt = get_associated_ips(tmp_lease,
+ next_hw,
+ lease,
+ assoc_ips,
+ nassoc_ips);
+
+ }
+
+ lease_dereference(&tmp_lease, MDL);
+
+ if (lease != NULL) {
+ memcpy(&packet->raw->ciaddr,
+ lease->ip_addr.iabuf,
+ sizeof(packet->raw->ciaddr));
+ }
+
+ /*
+ * Log if we have too many IP addresses associated
+ * with this client.
+ */
+ if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
+ log_info("%d IP addresses associated with %s, "
+ "only %d sent in reply.",
+ assoc_ip_cnt, dbg_info, nassoc_ips);
+ }
+ }
+
+ /*
+ * We now know the query target too, so can report this in
+ * our log message.
+ */
+ snprintf(msgbuf, sizeof(msgbuf),
+ "DHCPLEASEQUERY from %s for %s",
+ inet_ntoa(packet->raw->giaddr), dbg_info);
+
+ /*
+ * Figure our our return type.
+ */
+ if (lease == NULL) {
+ dhcpMsgType = DHCPLEASEUNKNOWN;
+ dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
+ } else {
+ if (lease->binding_state == FTS_ACTIVE) {
+ dhcpMsgType = DHCPLEASEACTIVE;
+ dhcp_msg_type_name = "DHCPLEASEACTIVE";
+ } else {
+ dhcpMsgType = DHCPLEASEUNASSIGNED;
+ dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
+ }
+ }
+
+ /*
+ * Set options that only make sense if we have an active lease.
+ */
+
+ if (dhcpMsgType == DHCPLEASEACTIVE)
+ {
+ /*
+ * RFC 4388 uses the PRL to request options for the agent to
+ * receive that are "about" the client. It is confusing
+ * because in some cases it wants to know what was sent to
+ * the client (lease times, adjusted), and in others it wants
+ * to know information the client sent. You're supposed to
+ * know this on a case-by-case basis.
+ *
+ * "Name servers", "domain name", and the like from the relay
+ * agent's scope seems less than useful. Our options are to
+ * restart the option cache from the lease's best point of view
+ * (execute statements from the lease pool's group), or to
+ * simply restart the option cache from empty.
+ *
+ * I think restarting the option cache from empty best
+ * approaches RFC 4388's intent; specific options are included.
+ */
+ option_state_dereference(&options, MDL);
+
+ if (!option_state_allocate(&options, MDL)) {
+ log_error("%s: out of memory, no reply sent", msgbuf);
+ lease_dereference(&lease, MDL);
+ return;
+ }
+
+ /*
+ * Set the hardware address fields.
+ */
+
+ packet->raw->hlen = lease->hardware_addr.hlen - 1;
+ packet->raw->htype = lease->hardware_addr.hbuf[0];
+ memcpy(packet->raw->chaddr,
+ &lease->hardware_addr.hbuf[1],
+ sizeof(packet->raw->chaddr));
+
+ /*
+ * Set client identifier option.
+ */
+ if (lease->uid_len > 0) {
+ if (!add_option(options,
+ DHO_DHCP_CLIENT_IDENTIFIER,
+ lease->uid,
+ lease->uid_len)) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+
+
+ /*
+ * Calculate T1 and T2, the times when the client
+ * tries to extend its lease on its networking
+ * address.
+ * These seem to be hard-coded in ISC DHCP, to 0.5 and
+ * 0.875 of the lease time.
+ */
+
+ lease_duration = lease->ends - lease->starts;
+ time_renewal = lease->starts +
+ (lease_duration / 2);
+ time_rebinding = lease->starts +
+ (lease_duration / 2) +
+ (lease_duration / 4) +
+ (lease_duration / 8);
+
+ if (time_renewal > cur_time) {
+ if (time_renewal < cur_time)
+ time_renewal = 0;
+ else
+ time_renewal = htonl(time_renewal - cur_time);
+
+ if (!add_option(options,
+ DHO_DHCP_RENEWAL_TIME,
+ &time_renewal,
+ sizeof(time_renewal))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+
+ if (time_rebinding > cur_time) {
+ time_rebinding = htonl(time_rebinding - cur_time);
+
+ if (!add_option(options,
+ DHO_DHCP_REBINDING_TIME,
+ &time_rebinding,
+ sizeof(time_rebinding))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+
+ if (lease->ends > cur_time) {
+ if (time_expiry < cur_time) {
+ log_error("Impossible condition at %s:%d.",
+ MDL);
+
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ return;
+ }
+ time_expiry = htonl(lease->ends - cur_time);
+ if (!add_option(options,
+ DHO_DHCP_LEASE_TIME,
+ &time_expiry,
+ sizeof(time_expiry))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+
+ /* Supply the Vendor-Class-Identifier. */
+ if (lease->scope != NULL) {
+ struct data_string vendor_class;
+
+ memset(&vendor_class, 0, sizeof(vendor_class));
+
+ if (find_bound_string(&vendor_class, lease->scope,
+ "vendor-class-identifier")) {
+ if (!add_option(options,
+ DHO_VENDOR_CLASS_IDENTIFIER,
+ (void *)vendor_class.data,
+ vendor_class.len)) {
+ option_state_dereference(&options,
+ MDL);
+ lease_dereference(&lease, MDL);
+ log_error("%s: error adding vendor "
+ "class identifier, no reply "
+ "sent", msgbuf);
+ data_string_forget(&vendor_class, MDL);
+ return;
+ }
+ data_string_forget(&vendor_class, MDL);
+ }
+ }
+
+ /*
+ * Set the relay agent info.
+ *
+ * Note that because agent info is appended without regard
+ * to the PRL in cons_options(), this will be sent as the
+ * last option in the packet whether it is listed on PRL or
+ * not.
+ */
+
+ if (lease->agent_options != NULL) {
+ int idx = agent_universe.index;
+ struct option_chain_head **tmp1 =
+ (struct option_chain_head **)
+ &(options->universes[idx]);
+ struct option_chain_head *tmp2 =
+ (struct option_chain_head *)
+ lease->agent_options;
+
+ option_chain_head_reference(tmp1, tmp2, MDL);
+ }
+
+ /*
+ * Set the client last transaction time.
+ * We check to make sure we have a timestamp. For
+ * lease files that were saved before running a
+ * timestamp-aware version of the server, this may
+ * not be set.
+ */
+
+ if (lease->cltt != MIN_TIME) {
+ if (cur_time > lease->cltt) {
+ client_last_transaction_time =
+ htonl(cur_time - lease->cltt);
+ } else {
+ client_last_transaction_time = htonl(0);
+ }
+ if (!add_option(options,
+ DHO_CLIENT_LAST_TRANSACTION_TIME,
+ &client_last_transaction_time,
+ sizeof(client_last_transaction_time))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+
+ /*
+ * Set associated IPs, if requested and there are some.
+ */
+ if (want_associated_ip && (assoc_ip_cnt > 0)) {
+ if (!add_option(options,
+ DHO_ASSOCIATED_IP,
+ assoc_ips,
+ assoc_ip_cnt * sizeof(assoc_ips[0]))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: out of memory, no reply sent",
+ msgbuf);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Set the message type.
+ */
+
+ packet->raw->op = BOOTREPLY;
+
+ /*
+ * Set DHCP message type.
+ */
+ if (!add_option(options,
+ DHO_DHCP_MESSAGE_TYPE,
+ &dhcpMsgType,
+ sizeof(dhcpMsgType))) {
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+ log_info("%s: error adding option, no reply sent", msgbuf);
+ return;
+ }
+
+ /*
+ * Log the message we've received.
+ */
+ log_info("%s", msgbuf);
+
+ /*
+ * Figure out which address to use to send from.
+ */
+ get_server_source_address(&siaddr, options, packet);
+
+ /*
+ * Set up the option buffer.
+ */
+
+ memset(&prl, 0, sizeof(prl));
+ oc = lookup_option(&dhcp_universe, options,
+ DHO_DHCP_PARAMETER_REQUEST_LIST);
+ if (oc != NULL) {
+ evaluate_option_cache(&prl,
+ packet,
+ NULL,
+ NULL,
+ packet->options,
+ options,
+ &global_scope,
+ oc,
+ MDL);
+ }
+ if (prl.len > 0) {
+ prl_ptr = &prl;
+ } else {
+ prl_ptr = NULL;
+ }
+
+ packet->packet_length = cons_options(packet,
+ packet->raw,
+ lease,
+ NULL,
+ 0,
+ packet->options,
+ options,
+ &global_scope,
+ 0,
+ 0,
+ 0,
+ prl_ptr,
+ NULL);
+
+ data_string_forget(&prl, MDL); /* SK: safe, even if empty */
+ option_state_dereference(&options, MDL);
+ lease_dereference(&lease, MDL);
+
+ to.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ to.sin_len = sizeof(to);
+#endif
+ memset(to.sin_zero, 0, sizeof(to.sin_zero));
+
+ /*
+ * Leasequery packets are be sent to the gateway address.
+ */
+ to.sin_addr = packet->raw->giaddr;
+ if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
+ to.sin_port = local_port;
+ } else {
+ to.sin_port = remote_port; /* XXXSK: For debugging. */
+ }
+
+ /*
+ * The fallback_interface lets us send with a real IP
+ * address. The packet interface sends from all-zeros.
+ */
+ if (fallback_interface != NULL) {
+ interface = fallback_interface;
+ } else {
+ interface = packet->interface;
+ }
+
+ /*
+ * Report what we're sending.
+ */
+ log_info("%s to %s for %s (%d associated IPs)",
+ dhcp_msg_type_name,
+ inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);
+
+ send_packet(interface,
+ NULL,
+ packet->raw,
+ packet->packet_length,
+ siaddr,
+ &to,
+ NULL);
+}
+
+#ifdef DHCPv6
+
+/*
+ * TODO: RFC5007 query-by-clientid.
+ *
+ * TODO: RFC5007 look at the pools according to the link-address.
+ *
+ * TODO: get fixed leases too.
+ *
+ * TODO: RFC5007 ORO in query-options.
+ *
+ * TODO: RFC5007 lq-relay-data.
+ *
+ * TODO: RFC5007 lq-client-link.
+ *
+ * Note: the code is still nearly compliant and usable for the target
+ * case with these missing features!
+ */
+
+/*
+ * The structure to handle a leasequery.
+ */
+struct lq6_state {
+ struct packet *packet;
+ struct data_string client_id;
+ struct data_string server_id;
+ struct data_string lq_query;
+ uint8_t query_type;
+ struct in6_addr link_addr;
+ struct option_state *query_opts;
+
+ struct option_state *reply_opts;
+ unsigned cursor;
+ union reply_buffer {
+ unsigned char data[65536];
+ struct dhcpv6_packet reply;
+ } buf;
+};
+
+/*
+ * Options that we want to send.
+ */
+static const int required_opts_lq[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+ D6O_STATUS_CODE,
+ D6O_CLIENT_DATA,
+ D6O_LQ_RELAY_DATA,
+ D6O_LQ_CLIENT_LINK,
+ 0
+};
+static const int required_opt_CLIENT_DATA[] = {
+ D6O_CLIENTID,
+ D6O_IAADDR,
+ D6O_IAPREFIX,
+ D6O_CLT_TIME,
+ 0
+};
+
+/*
+ * Get the lq-query option from the packet.
+ */
+static isc_result_t
+get_lq_query(struct lq6_state *lq)
+{
+ struct data_string *lq_query = &lq->lq_query;
+ struct packet *packet = lq->packet;
+ struct option_cache *oc;
+
+ /*
+ * Verify our lq_query structure is empty.
+ */
+ if ((lq_query->data != NULL) || (lq_query->len != 0)) {
+ return DHCP_R_INVALIDARG;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_LQ_QUERY);
+ if (oc == NULL) {
+ return ISC_R_NOTFOUND;
+ }
+
+ if (!evaluate_option_cache(lq_query, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ return ISC_R_FAILURE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Message validation, RFC 5007 section 4.2.1:
+ * dhcpv6.c:valid_client_msg() - unicast + lq-query option.
+ */
+static int
+valid_query_msg(struct lq6_state *lq) {
+ struct packet *packet = lq->packet;
+ int ret_val = 0;
+ struct option_cache *oc;
+
+ /* INSIST((lq != NULL) || (packet != NULL)); */
+
+ switch (get_client_id(packet, &lq->client_id)) {
+ case ISC_R_SUCCESS:
+ break;
+ case ISC_R_NOTFOUND:
+ log_debug("Discarding %s from %s; "
+ "client identifier missing",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ default:
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Client Identifier",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
+ if (oc != NULL) {
+ if (evaluate_option_cache(&lq->server_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_debug("Discarding %s from %s; "
+ "server identifier found "
+ "(CLIENTID %s, SERVERID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(lq->client_id.len,
+ lq->client_id.data, 60),
+ print_hex_2(lq->server_id.len,
+ lq->server_id.data, 60));
+ } else {
+ log_debug("Discarding %s from %s; "
+ "server identifier found "
+ "(CLIENTID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ print_hex_1(lq->client_id.len,
+ lq->client_id.data, 60),
+ piaddr(packet->client_addr));
+ }
+ goto exit;
+ }
+
+ switch (get_lq_query(lq)) {
+ case ISC_R_SUCCESS:
+ break;
+ case ISC_R_NOTFOUND:
+ log_debug("Discarding %s from %s; lq-query missing",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ default:
+ log_error("Error processing %s from %s; "
+ "unable to evaluate LQ-Query",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ }
+
+ /* looks good */
+ ret_val = 1;
+
+exit:
+ if (!ret_val) {
+ if (lq->client_id.len > 0) {
+ data_string_forget(&lq->client_id, MDL);
+ }
+ if (lq->server_id.len > 0) {
+ data_string_forget(&lq->server_id, MDL);
+ }
+ if (lq->lq_query.len > 0) {
+ data_string_forget(&lq->lq_query, MDL);
+ }
+ }
+ return ret_val;
+}
+
+/*
+ * Set an error in a status-code option (from set_status_code).
+ */
+static int
+set_error(struct lq6_state *lq, u_int16_t code, const char *message) {
+ struct data_string d;
+ int ret_val;
+
+ memset(&d, 0, sizeof(d));
+ d.len = sizeof(code) + strlen(message);
+ if (!buffer_allocate(&d.buffer, d.len, MDL)) {
+ log_fatal("set_error: no memory for status code.");
+ }
+ d.data = d.buffer->data;
+ putUShort(d.buffer->data, code);
+ memcpy(d.buffer->data + sizeof(code), message, d.len - sizeof(code));
+ if (!save_option_buffer(&dhcpv6_universe, lq->reply_opts,
+ d.buffer, (unsigned char *)d.data, d.len,
+ D6O_STATUS_CODE, 0)) {
+ log_error("set_error: error saving status code.");
+ ret_val = 0;
+ } else {
+ ret_val = 1;
+ }
+ data_string_forget(&d, MDL);
+ return ret_val;
+}
+
+/*
+ * Process a by-address lease query.
+ */
+static int
+process_lq_by_address(struct lq6_state *lq) {
+ struct packet *packet = lq->packet;
+ struct option_cache *oc;
+ struct ipv6_pool *pool = NULL;
+ struct data_string data;
+ struct in6_addr addr;
+ struct iasubopt *iaaddr = NULL;
+ struct option_state *opt_state = NULL;
+ u_int32_t lifetime;
+ unsigned opt_cursor;
+ int ret_val = 0;
+
+ /*
+ * Get the IAADDR.
+ */
+ oc = lookup_option(&dhcpv6_universe, lq->query_opts, D6O_IAADDR);
+ if (oc == NULL) {
+ if (!set_error(lq, STATUS_MalformedQuery,
+ "No OPTION_IAADDR.")) {
+ log_error("process_lq_by_address: unable "
+ "to set MalformedQuery status code.");
+ return 0;
+ }
+ return 1;
+ }
+ memset(&data, 0, sizeof(data));
+ if (!evaluate_option_cache(&data, packet,
+ NULL, NULL,
+ lq->query_opts, NULL,
+ &global_scope, oc, MDL) ||
+ (data.len < IAADDR_OFFSET)) {
+ log_error("process_lq_by_address: error evaluating IAADDR.");
+ goto exit;
+ }
+ memcpy(&addr, data.data, sizeof(addr));
+ data_string_forget(&data, MDL);
+
+ /*
+ * Find the lease.
+ * Note the RFC 5007 says to use the link-address to find the link
+ * or the ia-aadr when it is :: but in any case the ia-addr has
+ * to be on the link, so we ignore the link-address here.
+ */
+ if (find_ipv6_pool(&pool, D6O_IA_NA, &addr) != ISC_R_SUCCESS) {
+ if (!set_error(lq, STATUS_NotConfigured,
+ "Address not in a pool.")) {
+ log_error("process_lq_by_address: unable "
+ "to set NotConfigured status code.");
+ goto exit;
+ }
+ ret_val = 1;
+ goto exit;
+ }
+ if (iasubopt_hash_lookup(&iaaddr, pool->leases, &addr,
+ sizeof(addr), MDL) == 0) {
+ ret_val = 1;
+ goto exit;
+ }
+ if ((iaaddr == NULL) || (iaaddr->state != FTS_ACTIVE) ||
+ (iaaddr->ia == NULL) || (iaaddr->ia->iaid_duid.len <= 4)) {
+ ret_val = 1;
+ goto exit;
+ }
+
+ /*
+ * Build the client-data option (with client-id, ia-addr and clt-time).
+ */
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("process_lq_by_address: "
+ "no memory for option state.");
+ goto exit;
+ }
+
+ data_string_copy(&data, &iaaddr->ia->iaid_duid, MDL);
+ data.data += 4;
+ data.len -= 4;
+ if (!save_option_buffer(&dhcpv6_universe, opt_state,
+ NULL, (unsigned char *)data.data, data.len,
+ D6O_CLIENTID, 0)) {
+ log_error("process_lq_by_address: error saving client ID.");
+ goto exit;
+ }
+ data_string_forget(&data, MDL);
+
+ data.len = IAADDR_OFFSET;
+ if (!buffer_allocate(&data.buffer, data.len, MDL)) {
+ log_error("process_lq_by_address: no memory for ia-addr.");
+ goto exit;
+ }
+ data.data = data.buffer->data;
+ memcpy(data.buffer->data, &iaaddr->addr, 16);
+ lifetime = iaaddr->prefer;
+ putULong(data.buffer->data + 16, lifetime);
+ lifetime = iaaddr->valid;
+ putULong(data.buffer->data + 20, lifetime);
+ if (!save_option_buffer(&dhcpv6_universe, opt_state,
+ NULL, (unsigned char *)data.data, data.len,
+ D6O_IAADDR, 0)) {
+ log_error("process_lq_by_address: error saving ia-addr.");
+ goto exit;
+ }
+ data_string_forget(&data, MDL);
+
+ lifetime = htonl(iaaddr->ia->cltt);
+ if (!save_option_buffer(&dhcpv6_universe, opt_state,
+ NULL, (unsigned char *)&lifetime, 4,
+ D6O_CLT_TIME, 0)) {
+ log_error("process_lq_by_address: error saving clt time.");
+ goto exit;
+ }
+
+ /*
+ * Store the client-data option.
+ */
+ opt_cursor = lq->cursor;
+ putUShort(lq->buf.data + lq->cursor, (unsigned)D6O_CLIENT_DATA);
+ lq->cursor += 2;
+ /* Skip option length. */
+ lq->cursor += 2;
+
+ lq->cursor += store_options6((char *)lq->buf.data + lq->cursor,
+ sizeof(lq->buf) - lq->cursor,
+ opt_state, lq->packet,
+ required_opt_CLIENT_DATA, NULL);
+ /* Reset the length. */
+ putUShort(lq->buf.data + opt_cursor + 2,
+ lq->cursor - (opt_cursor + 4));
+
+ /* Done. */
+ ret_val = 1;
+
+ exit:
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (pool != NULL)
+ ipv6_pool_dereference(&pool, MDL);
+ if (iaaddr != NULL)
+ iasubopt_dereference(&iaaddr, MDL);
+ if (opt_state != NULL)
+ option_state_dereference(&opt_state, MDL);
+ return ret_val;
+}
+
+
+/*
+ * Process a lease query.
+ */
+void
+dhcpv6_leasequery(struct data_string *reply_ret, struct packet *packet) {
+ static struct lq6_state lq;
+ struct option_cache *oc;
+ int allow_lq;
+
+ /*
+ * Initialize the lease query state.
+ */
+ lq.packet = NULL;
+ memset(&lq.client_id, 0, sizeof(lq.client_id));
+ memset(&lq.server_id, 0, sizeof(lq.server_id));
+ memset(&lq.lq_query, 0, sizeof(lq.lq_query));
+ lq.query_opts = NULL;
+ lq.reply_opts = NULL;
+ packet_reference(&lq.packet, packet, MDL);
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_query_msg(&lq)) {
+ goto exit;
+ }
+
+ /*
+ * Prepare our reply.
+ */
+ if (!option_state_allocate(&lq.reply_opts, MDL)) {
+ log_error("dhcpv6_leasequery: no memory for option state.");
+ goto exit;
+ }
+ execute_statements_in_scope(NULL, lq.packet, NULL, NULL,
+ lq.packet->options, lq.reply_opts,
+ &global_scope, root_group, NULL);
+
+ lq.buf.reply.msg_type = DHCPV6_LEASEQUERY_REPLY;
+
+ memcpy(lq.buf.reply.transaction_id,
+ lq.packet->dhcpv6_transaction_id,
+ sizeof(lq.buf.reply.transaction_id));
+
+ /*
+ * Because LEASEQUERY has some privacy concerns, default to deny.
+ */
+ allow_lq = 0;
+
+ /*
+ * See if we are authorized to do LEASEQUERY.
+ */
+ oc = lookup_option(&server_universe, lq.reply_opts, SV_LEASEQUERY);
+ if (oc != NULL) {
+ allow_lq = evaluate_boolean_option_cache(NULL,
+ lq.packet,
+ NULL, NULL,
+ lq.packet->options,
+ lq.reply_opts,
+ &global_scope,
+ oc, MDL);
+ }
+
+ if (!allow_lq) {
+ log_info("dhcpv6_leasequery: not allowed, query ignored.");
+ goto exit;
+ }
+
+ /*
+ * Same than transmission of REPLY message in RFC 3315:
+ * server-id
+ * client-id
+ */
+
+ oc = lookup_option(&dhcpv6_universe, lq.reply_opts, D6O_SERVERID);
+ if (oc == NULL) {
+ /* If not already in options, get from query then global. */
+ if (lq.server_id.data == NULL)
+ copy_server_duid(&lq.server_id, MDL);
+ if (!save_option_buffer(&dhcpv6_universe,
+ lq.reply_opts,
+ NULL,
+ (unsigned char *)lq.server_id.data,
+ lq.server_id.len,
+ D6O_SERVERID,
+ 0)) {
+ log_error("dhcpv6_leasequery: "
+ "error saving server identifier.");
+ goto exit;
+ }
+ }
+
+ if (!save_option_buffer(&dhcpv6_universe,
+ lq.reply_opts,
+ lq.client_id.buffer,
+ (unsigned char *)lq.client_id.data,
+ lq.client_id.len,
+ D6O_CLIENTID,
+ 0)) {
+ log_error("dhcpv6_leasequery: "
+ "error saving client identifier.");
+ goto exit;
+ }
+
+ lq.cursor = 4;
+
+ /*
+ * Decode the lq-query option.
+ */
+
+ if (lq.lq_query.len <= LQ_QUERY_OFFSET) {
+ if (!set_error(&lq, STATUS_MalformedQuery,
+ "OPTION_LQ_QUERY too short.")) {
+ log_error("dhcpv6_leasequery: unable "
+ "to set MalformedQuery status code.");
+ goto exit;
+ }
+ goto done;
+ }
+
+ lq.query_type = lq.lq_query.data [0];
+ memcpy(&lq.link_addr, lq.lq_query.data + 1, sizeof(lq.link_addr));
+ switch (lq.query_type) {
+ case LQ6QT_BY_ADDRESS:
+ break;
+ case LQ6QT_BY_CLIENTID:
+ if (!set_error(&lq, STATUS_UnknownQueryType,
+ "QUERY_BY_CLIENTID not supported.")) {
+ log_error("dhcpv6_leasequery: unable to "
+ "set UnknownQueryType status code.");
+ goto exit;
+ }
+ goto done;
+ default:
+ if (!set_error(&lq, STATUS_UnknownQueryType,
+ "Unknown query-type.")) {
+ log_error("dhcpv6_leasequery: unable to "
+ "set UnknownQueryType status code.");
+ goto exit;
+ }
+ goto done;
+ }
+
+ if (!option_state_allocate(&lq.query_opts, MDL)) {
+ log_error("dhcpv6_leasequery: no memory for option state.");
+ goto exit;
+ }
+ if (!parse_option_buffer(lq.query_opts,
+ lq.lq_query.data + LQ_QUERY_OFFSET,
+ lq.lq_query.len - LQ_QUERY_OFFSET,
+ &dhcpv6_universe)) {
+ log_error("dhcpv6_leasequery: error parsing query-options.");
+ if (!set_error(&lq, STATUS_MalformedQuery,
+ "Bad query-options.")) {
+ log_error("dhcpv6_leasequery: unable "
+ "to set MalformedQuery status code.");
+ goto exit;
+ }
+ goto done;
+ }
+
+ /* Do it. */
+ if (!process_lq_by_address(&lq))
+ goto exit;
+
+ done:
+ /* Store the options. */
+ lq.cursor += store_options6((char *)lq.buf.data + lq.cursor,
+ sizeof(lq.buf) - lq.cursor,
+ lq.reply_opts,
+ lq.packet,
+ required_opts_lq,
+ NULL);
+
+ /* Return our reply to the caller. */
+ reply_ret->len = lq.cursor;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, lq.cursor, MDL)) {
+ log_fatal("dhcpv6_leasequery: no memory to store Reply.");
+ }
+ memcpy(reply_ret->buffer->data, lq.buf.data, lq.cursor);
+ reply_ret->data = reply_ret->buffer->data;
+
+ exit:
+ /* Cleanup. */
+ if (lq.packet != NULL)
+ packet_dereference(&lq.packet, MDL);
+ if (lq.client_id.data != NULL)
+ data_string_forget(&lq.client_id, MDL);
+ if (lq.server_id.data != NULL)
+ data_string_forget(&lq.server_id, MDL);
+ if (lq.lq_query.data != NULL)
+ data_string_forget(&lq.lq_query, MDL);
+ if (lq.query_opts != NULL)
+ option_state_dereference(&lq.query_opts, MDL);
+ if (lq.reply_opts != NULL)
+ option_state_dereference(&lq.reply_opts, MDL);
+}
+
+#endif /* DHCPv6 */
diff --git a/server/dhcpv6.c b/server/dhcpv6.c
new file mode 100644
index 0000000..4538882
--- /dev/null
+++ b/server/dhcpv6.c
@@ -0,0 +1,6056 @@
+/*
+ * Copyright (C) 2006-2011 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "dhcpd.h"
+
+#ifdef DHCPv6
+
+/*
+ * We use print_hex_1() to output DUID values. We could actually output
+ * the DUID with more information... MAC address if using type 1 or 3,
+ * and so on. However, RFC 3315 contains Grave Warnings against actually
+ * attempting to understand a DUID.
+ */
+
+/*
+ * TODO: gettext() or other method of localization for the messages
+ * for status codes (and probably for log formats eventually)
+ * TODO: refactoring (simplify, simplify, simplify)
+ * TODO: support multiple shared_networks on each interface (this
+ * will allow the server to issue multiple IPv6 addresses to
+ * a single interface)
+ */
+
+/*
+ * DHCPv6 Reply workflow assist. A Reply packet is built by various
+ * different functions; this gives us one location where we keep state
+ * regarding a reply.
+ */
+struct reply_state {
+ /* root level persistent state */
+ struct shared_network *shared;
+ struct host_decl *host;
+ struct subnet *subnet; /* Used to match fixed-addrs to subnet scopes. */
+ struct option_state *opt_state;
+ struct packet *packet;
+ struct data_string client_id;
+
+ /* IA level persistent state */
+ unsigned ia_count;
+ unsigned pd_count;
+ unsigned client_resources;
+ isc_boolean_t resources_included;
+ isc_boolean_t static_lease;
+ unsigned static_prefixes;
+ struct ia_xx *ia;
+ struct ia_xx *old_ia;
+ struct option_state *reply_ia;
+ struct data_string fixed;
+
+ /* IAADDR/PREFIX level persistent state */
+ struct iasubopt *lease;
+
+ /*
+ * "t1", "t2", preferred, and valid lifetimes records for calculating
+ * t1 and t2 (min/max).
+ */
+ u_int32_t renew, rebind, prefer, valid;
+
+ /* Client-requested valid and preferred lifetimes. */
+ u_int32_t client_valid, client_prefer;
+
+ /* Chosen values to transmit for valid and preferred lifetimes. */
+ u_int32_t send_valid, send_prefer;
+
+ /* Preferred prefix length (-1 is any). */
+ int preflen;
+
+ /* Index into the data field that has been consumed. */
+ unsigned cursor;
+
+ union reply_buffer {
+ unsigned char data[65536];
+ struct dhcpv6_packet reply;
+ } buf;
+};
+
+/*
+ * Prototypes local to this file.
+ */
+static int get_encapsulated_IA_state(struct option_state **enc_opt_state,
+ struct data_string *enc_opt_data,
+ struct packet *packet,
+ struct option_cache *oc,
+ int offset);
+static void build_dhcpv6_reply(struct data_string *, struct packet *);
+static isc_result_t shared_network_from_packet6(struct shared_network **shared,
+ struct packet *packet);
+static void seek_shared_host(struct host_decl **hp,
+ struct shared_network *shared);
+static isc_boolean_t fixed_matches_shared(struct host_decl *host,
+ struct shared_network *shared);
+static isc_result_t reply_process_ia_na(struct reply_state *reply,
+ struct option_cache *ia);
+static isc_result_t reply_process_ia_ta(struct reply_state *reply,
+ struct option_cache *ia);
+static isc_result_t reply_process_addr(struct reply_state *reply,
+ struct option_cache *addr);
+static isc_boolean_t address_is_owned(struct reply_state *reply,
+ struct iaddr *addr);
+static isc_boolean_t temporary_is_available(struct reply_state *reply,
+ struct iaddr *addr);
+static isc_result_t find_client_temporaries(struct reply_state *reply);
+static isc_result_t reply_process_try_addr(struct reply_state *reply,
+ struct iaddr *addr);
+static isc_result_t find_client_address(struct reply_state *reply);
+static isc_result_t reply_process_is_addressed(struct reply_state *reply,
+ struct binding_scope **scope,
+ struct group *group);
+static isc_result_t reply_process_send_addr(struct reply_state *reply,
+ struct iaddr *addr);
+static struct iasubopt *lease_compare(struct iasubopt *alpha,
+ struct iasubopt *beta);
+static isc_result_t reply_process_ia_pd(struct reply_state *reply,
+ struct option_cache *ia_pd);
+static isc_result_t reply_process_prefix(struct reply_state *reply,
+ struct option_cache *pref);
+static isc_boolean_t prefix_is_owned(struct reply_state *reply,
+ struct iaddrcidrnet *pref);
+static isc_result_t find_client_prefix(struct reply_state *reply);
+static isc_result_t reply_process_try_prefix(struct reply_state *reply,
+ struct iaddrcidrnet *pref);
+static isc_result_t reply_process_is_prefixed(struct reply_state *reply,
+ struct binding_scope **scope,
+ struct group *group);
+static isc_result_t reply_process_send_prefix(struct reply_state *reply,
+ struct iaddrcidrnet *pref);
+static struct iasubopt *prefix_compare(struct reply_state *reply,
+ struct iasubopt *alpha,
+ struct iasubopt *beta);
+static int find_hosts_by_duid_chaddr(struct host_decl **host,
+ const struct data_string *client_id);
+/*
+ * This function returns the time since DUID time start for the
+ * given time_t value.
+ */
+static u_int32_t
+duid_time(time_t when) {
+ /*
+ * This time is modulo 2^32.
+ */
+ while ((when - DUID_TIME_EPOCH) > 4294967295u) {
+ /* use 2^31 to avoid spurious compiler warnings */
+ when -= 2147483648u;
+ when -= 2147483648u;
+ }
+
+ return when - DUID_TIME_EPOCH;
+}
+
+
+/*
+ * Server DUID.
+ *
+ * This must remain the same for the lifetime of this server, because
+ * clients return the server DUID that we sent them in Request packets.
+ *
+ * We pick the server DUID like this:
+ *
+ * 1. Check dhcpd.conf - any value the administrator has configured
+ * overrides any possible values.
+ * 2. Check the leases.txt - we want to use the previous value if
+ * possible.
+ * 3. Check if dhcpd.conf specifies a type of server DUID to use,
+ * and generate that type.
+ * 4. Generate a type 1 (time + hardware address) DUID.
+ */
+static struct data_string server_duid;
+
+/*
+ * Check if the server_duid has been set.
+ */
+isc_boolean_t
+server_duid_isset(void) {
+ return (server_duid.data != NULL);
+}
+
+/*
+ * Return the server_duid.
+ */
+void
+copy_server_duid(struct data_string *ds, const char *file, int line) {
+ data_string_copy(ds, &server_duid, file, line);
+}
+
+/*
+ * Set the server DUID to a specified value. This is used when
+ * the server DUID is stored in persistent memory (basically the
+ * leases.txt file).
+ */
+void
+set_server_duid(struct data_string *new_duid) {
+ /* INSIST(new_duid != NULL); */
+ /* INSIST(new_duid->data != NULL); */
+
+ if (server_duid_isset()) {
+ data_string_forget(&server_duid, MDL);
+ }
+ data_string_copy(&server_duid, new_duid, MDL);
+}
+
+
+/*
+ * Set the server DUID based on the D6O_SERVERID option. This handles
+ * the case where the administrator explicitly put it in the dhcpd.conf
+ * file.
+ */
+isc_result_t
+set_server_duid_from_option(void) {
+ struct option_state *opt_state;
+ struct option_cache *oc;
+ struct data_string option_duid;
+ isc_result_t ret_val;
+
+ opt_state = NULL;
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_fatal("No memory for server DUID.");
+ }
+
+ execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL,
+ opt_state, &global_scope, root_group, NULL);
+
+ oc = lookup_option(&dhcpv6_universe, opt_state, D6O_SERVERID);
+ if (oc == NULL) {
+ ret_val = ISC_R_NOTFOUND;
+ } else {
+ memset(&option_duid, 0, sizeof(option_duid));
+ if (!evaluate_option_cache(&option_duid, NULL, NULL, NULL,
+ opt_state, NULL, &global_scope,
+ oc, MDL)) {
+ ret_val = ISC_R_UNEXPECTED;
+ } else {
+ set_server_duid(&option_duid);
+ data_string_forget(&option_duid, MDL);
+ ret_val = ISC_R_SUCCESS;
+ }
+ }
+
+ option_state_dereference(&opt_state, MDL);
+
+ return ret_val;
+}
+
+/*
+ * DUID layout, as defined in RFC 3315, section 9.
+ *
+ * We support type 1 (hardware address plus time) and type 3 (hardware
+ * address).
+ *
+ * We can support type 2 for specific vendors in the future, if they
+ * publish the specification. And of course there may be additional
+ * types later.
+ */
+static int server_duid_type = DUID_LLT;
+
+/*
+ * Set the DUID type.
+ */
+void
+set_server_duid_type(int type) {
+ server_duid_type = type;
+}
+
+/*
+ * Generate a new server DUID. This is done if there was no DUID in
+ * the leases.txt or in the dhcpd.conf file.
+ */
+isc_result_t
+generate_new_server_duid(void) {
+ struct interface_info *p;
+ u_int32_t time_val;
+ struct data_string generated_duid;
+
+ /*
+ * Verify we have a type that we support.
+ */
+ if ((server_duid_type != DUID_LL) && (server_duid_type != DUID_LLT)) {
+ log_error("Invalid DUID type %d specified, "
+ "only LL and LLT types supported", server_duid_type);
+ return DHCP_R_INVALIDARG;
+ }
+
+ /*
+ * Find an interface with a hardware address.
+ * Any will do. :)
+ */
+ for (p = interfaces; p != NULL; p = p->next) {
+ if (p->hw_address.hlen > 0) {
+ break;
+ }
+ }
+ if (p == NULL) {
+ return ISC_R_UNEXPECTED;
+ }
+
+ /*
+ * Build our DUID.
+ */
+ memset(&generated_duid, 0, sizeof(generated_duid));
+ if (server_duid_type == DUID_LLT) {
+ time_val = duid_time(time(NULL));
+ generated_duid.len = 8 + p->hw_address.hlen - 1;
+ if (!buffer_allocate(&generated_duid.buffer,
+ generated_duid.len, MDL)) {
+ log_fatal("No memory for server DUID.");
+ }
+ generated_duid.data = generated_duid.buffer->data;
+ putUShort(generated_duid.buffer->data, DUID_LLT);
+ putUShort(generated_duid.buffer->data + 2,
+ p->hw_address.hbuf[0]);
+ putULong(generated_duid.buffer->data + 4, time_val);
+ memcpy(generated_duid.buffer->data + 8,
+ p->hw_address.hbuf+1, p->hw_address.hlen-1);
+ } else if (server_duid_type == DUID_LL) {
+ generated_duid.len = 4 + p->hw_address.hlen - 1;
+ if (!buffer_allocate(&generated_duid.buffer,
+ generated_duid.len, MDL)) {
+ log_fatal("No memory for server DUID.");
+ }
+ generated_duid.data = generated_duid.buffer->data;
+ putUShort(generated_duid.buffer->data, DUID_LL);
+ putUShort(generated_duid.buffer->data + 2,
+ p->hw_address.hbuf[0]);
+ memcpy(generated_duid.buffer->data + 4,
+ p->hw_address.hbuf+1, p->hw_address.hlen-1);
+ } else {
+ log_fatal("Unsupported server DUID type %d.", server_duid_type);
+ }
+
+ set_server_duid(&generated_duid);
+ data_string_forget(&generated_duid, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Get the client identifier from the packet.
+ */
+isc_result_t
+get_client_id(struct packet *packet, struct data_string *client_id) {
+ struct option_cache *oc;
+
+ /*
+ * Verify our client_id structure is empty.
+ */
+ if ((client_id->data != NULL) || (client_id->len != 0)) {
+ return DHCP_R_INVALIDARG;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID);
+ if (oc == NULL) {
+ return ISC_R_NOTFOUND;
+ }
+
+ if (!evaluate_option_cache(client_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ return ISC_R_FAILURE;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Message validation, defined in RFC 3315, sections 15.2, 15.5, 15.7:
+ *
+ * Servers MUST discard any Solicit messages that do not include a
+ * Client Identifier option or that do include a Server Identifier
+ * option.
+ */
+int
+valid_client_msg(struct packet *packet, struct data_string *client_id) {
+ int ret_val;
+ struct option_cache *oc;
+ struct data_string data;
+
+ ret_val = 0;
+ memset(client_id, 0, sizeof(*client_id));
+ memset(&data, 0, sizeof(data));
+
+ switch (get_client_id(packet, client_id)) {
+ case ISC_R_SUCCESS:
+ break;
+ case ISC_R_NOTFOUND:
+ log_debug("Discarding %s from %s; "
+ "client identifier missing",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ default:
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Client Identifier",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ }
+
+ /*
+ * Required by RFC 3315, section 15.
+ */
+ if (packet->unicast) {
+ log_debug("Discarding %s from %s; packet sent unicast "
+ "(CLIENTID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(client_id->len, client_id->data, 60));
+ goto exit;
+ }
+
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
+ if (oc != NULL) {
+ if (evaluate_option_cache(&data, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_debug("Discarding %s from %s; "
+ "server identifier found "
+ "(CLIENTID %s, SERVERID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(client_id->len,
+ client_id->data, 60),
+ print_hex_2(data.len,
+ data.data, 60));
+ } else {
+ log_debug("Discarding %s from %s; "
+ "server identifier found "
+ "(CLIENTID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ print_hex_1(client_id->len,
+ client_id->data, 60),
+ piaddr(packet->client_addr));
+ }
+ goto exit;
+ }
+
+ /* looks good */
+ ret_val = 1;
+
+exit:
+ if (data.len > 0) {
+ data_string_forget(&data, MDL);
+ }
+ if (!ret_val) {
+ if (client_id->len > 0) {
+ data_string_forget(client_id, MDL);
+ }
+ }
+ return ret_val;
+}
+
+/*
+ * Response validation, defined in RFC 3315, sections 15.4, 15.6, 15.8,
+ * 15.9 (slightly different wording, but same meaning):
+ *
+ * Servers MUST discard any received Request message that meet any of
+ * the following conditions:
+ *
+ * - the message does not include a Server Identifier option.
+ * - the contents of the Server Identifier option do not match the
+ * server's DUID.
+ * - the message does not include a Client Identifier option.
+ */
+int
+valid_client_resp(struct packet *packet,
+ struct data_string *client_id,
+ struct data_string *server_id)
+{
+ int ret_val;
+ struct option_cache *oc;
+
+ /* INSIST((duid.data != NULL) && (duid.len > 0)); */
+
+ ret_val = 0;
+ memset(client_id, 0, sizeof(*client_id));
+ memset(server_id, 0, sizeof(*server_id));
+
+ switch (get_client_id(packet, client_id)) {
+ case ISC_R_SUCCESS:
+ break;
+ case ISC_R_NOTFOUND:
+ log_debug("Discarding %s from %s; "
+ "client identifier missing",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ default:
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Client Identifier",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+ goto exit;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
+ if (oc == NULL) {
+ log_debug("Discarding %s from %s: "
+ "server identifier missing (CLIENTID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(client_id->len, client_id->data, 60));
+ goto exit;
+ }
+ if (!evaluate_option_cache(server_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Server Identifier (CLIENTID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(client_id->len, client_id->data, 60));
+ goto exit;
+ }
+ if ((server_duid.len != server_id->len) ||
+ (memcmp(server_duid.data, server_id->data, server_duid.len) != 0)) {
+ log_debug("Discarding %s from %s; "
+ "not our server identifier "
+ "(CLIENTID %s, SERVERID %s, server DUID %s)",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(client_id->len, client_id->data, 60),
+ print_hex_2(server_id->len, server_id->data, 60),
+ print_hex_3(server_duid.len, server_duid.data, 60));
+ goto exit;
+ }
+
+ /* looks good */
+ ret_val = 1;
+
+exit:
+ if (!ret_val) {
+ if (server_id->len > 0) {
+ data_string_forget(server_id, MDL);
+ }
+ if (client_id->len > 0) {
+ data_string_forget(client_id, MDL);
+ }
+ }
+ return ret_val;
+}
+
+/*
+ * Information request validation, defined in RFC 3315, section 15.12:
+ *
+ * Servers MUST discard any received Information-request message that
+ * meets any of the following conditions:
+ *
+ * - The message includes a Server Identifier option and the DUID in
+ * the option does not match the server's DUID.
+ *
+ * - The message includes an IA option.
+ */
+int
+valid_client_info_req(struct packet *packet, struct data_string *server_id) {
+ int ret_val;
+ struct option_cache *oc;
+ struct data_string client_id;
+ char client_id_str[80]; /* print_hex_1() uses maximum 60 characters,
+ plus a few more for extra information */
+
+ ret_val = 0;
+ memset(server_id, 0, sizeof(*server_id));
+
+ /*
+ * Make a string that we can print out to give more
+ * information about the client if we need to.
+ *
+ * By RFC 3315, Section 18.1.5 clients SHOULD have a
+ * client-id on an Information-request packet, but it
+ * is not strictly necessary.
+ */
+ if (get_client_id(packet, &client_id) == ISC_R_SUCCESS) {
+ snprintf(client_id_str, sizeof(client_id_str), " (CLIENTID %s)",
+ print_hex_1(client_id.len, client_id.data, 60));
+ data_string_forget(&client_id, MDL);
+ } else {
+ client_id_str[0] = '\0';
+ }
+
+ /*
+ * Required by RFC 3315, section 15.
+ */
+ if (packet->unicast) {
+ log_debug("Discarding %s from %s; packet sent unicast%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr), client_id_str);
+ goto exit;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ if (oc != NULL) {
+ log_debug("Discarding %s from %s; "
+ "IA_NA option present%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr), client_id_str);
+ goto exit;
+ }
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA);
+ if (oc != NULL) {
+ log_debug("Discarding %s from %s; "
+ "IA_TA option present%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr), client_id_str);
+ goto exit;
+ }
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+ if (oc != NULL) {
+ log_debug("Discarding %s from %s; "
+ "IA_PD option present%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr), client_id_str);
+ goto exit;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(server_id, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("Error processing %s from %s; "
+ "unable to evaluate Server Identifier%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr), client_id_str);
+ goto exit;
+ }
+ if ((server_duid.len != server_id->len) ||
+ (memcmp(server_duid.data, server_id->data,
+ server_duid.len) != 0)) {
+ log_debug("Discarding %s from %s; "
+ "not our server identifier "
+ "(SERVERID %s, server DUID %s)%s",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ print_hex_1(server_id->len,
+ server_id->data, 60),
+ print_hex_2(server_duid.len,
+ server_duid.data, 60),
+ client_id_str);
+ goto exit;
+ }
+ }
+
+ /* looks good */
+ ret_val = 1;
+
+exit:
+ if (!ret_val) {
+ if (server_id->len > 0) {
+ data_string_forget(server_id, MDL);
+ }
+ }
+ return ret_val;
+}
+
+/*
+ * Options that we want to send, in addition to what was requested
+ * via the ORO.
+ */
+static const int required_opts[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+ D6O_STATUS_CODE,
+ D6O_PREFERENCE,
+ 0
+};
+static const int required_opts_NAA[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+ D6O_STATUS_CODE,
+ 0
+};
+static const int required_opts_solicit[] = {
+ D6O_CLIENTID,
+ D6O_SERVERID,
+ D6O_IA_NA,
+ D6O_IA_TA,
+ D6O_IA_PD,
+ D6O_RAPID_COMMIT,
+ D6O_STATUS_CODE,
+ D6O_RECONF_ACCEPT,
+ D6O_PREFERENCE,
+ 0
+};
+static const int required_opts_agent[] = {
+ D6O_INTERFACE_ID,
+ D6O_RELAY_MSG,
+ 0
+};
+static const int required_opts_IA[] = {
+ D6O_IAADDR,
+ D6O_STATUS_CODE,
+ 0
+};
+static const int required_opts_IA_PD[] = {
+ D6O_IAPREFIX,
+ D6O_STATUS_CODE,
+ 0
+};
+static const int required_opts_STATUS_CODE[] = {
+ D6O_STATUS_CODE,
+ 0
+};
+
+/*
+ * Extracts from packet contents an IA_* option, storing the IA structure
+ * in its entirety in enc_opt_data, and storing any decoded DHCPv6 options
+ * in enc_opt_state for later lookup and evaluation. The 'offset' indicates
+ * where in the IA_* the DHCPv6 options commence.
+ */
+static int
+get_encapsulated_IA_state(struct option_state **enc_opt_state,
+ struct data_string *enc_opt_data,
+ struct packet *packet,
+ struct option_cache *oc,
+ int offset)
+{
+ /*
+ * Get the raw data for the encapsulated options.
+ */
+ memset(enc_opt_data, 0, sizeof(*enc_opt_data));
+ if (!evaluate_option_cache(enc_opt_data, packet,
+ NULL, NULL, packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("get_encapsulated_IA_state: "
+ "error evaluating raw option.");
+ return 0;
+ }
+ if (enc_opt_data->len < offset) {
+ log_error("get_encapsulated_IA_state: raw option too small.");
+ data_string_forget(enc_opt_data, MDL);
+ return 0;
+ }
+
+ /*
+ * Now create the option state structure, and pass it to the
+ * function that parses options.
+ */
+ *enc_opt_state = NULL;
+ if (!option_state_allocate(enc_opt_state, MDL)) {
+ log_error("get_encapsulated_IA_state: no memory for options.");
+ data_string_forget(enc_opt_data, MDL);
+ return 0;
+ }
+ if (!parse_option_buffer(*enc_opt_state,
+ enc_opt_data->data + offset,
+ enc_opt_data->len - offset,
+ &dhcpv6_universe)) {
+ log_error("get_encapsulated_IA_state: error parsing options.");
+ option_state_dereference(enc_opt_state, MDL);
+ data_string_forget(enc_opt_data, MDL);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+set_status_code(u_int16_t status_code, const char *status_message,
+ struct option_state *opt_state)
+{
+ struct data_string d;
+ int ret_val;
+
+ memset(&d, 0, sizeof(d));
+ d.len = sizeof(status_code) + strlen(status_message);
+ if (!buffer_allocate(&d.buffer, d.len, MDL)) {
+ log_fatal("set_status_code: no memory for status code.");
+ }
+ d.data = d.buffer->data;
+ putUShort(d.buffer->data, status_code);
+ memcpy(d.buffer->data + sizeof(status_code),
+ status_message, d.len - sizeof(status_code));
+ if (!save_option_buffer(&dhcpv6_universe, opt_state,
+ d.buffer, (unsigned char *)d.data, d.len,
+ D6O_STATUS_CODE, 0)) {
+ log_error("set_status_code: error saving status code.");
+ ret_val = 0;
+ } else {
+ ret_val = 1;
+ }
+ data_string_forget(&d, MDL);
+ return ret_val;
+}
+
+/*
+ * We have a set of operations we do to set up the reply packet, which
+ * is the same for many message types.
+ */
+static int
+start_reply(struct packet *packet,
+ const struct data_string *client_id,
+ const struct data_string *server_id,
+ struct option_state **opt_state,
+ struct dhcpv6_packet *reply)
+{
+ struct option_cache *oc;
+ const unsigned char *server_id_data;
+ int server_id_len;
+
+ /*
+ * Build our option state for reply.
+ */
+ *opt_state = NULL;
+ if (!option_state_allocate(opt_state, MDL)) {
+ log_error("start_reply: no memory for option_state.");
+ return 0;
+ }
+ execute_statements_in_scope(NULL, packet, NULL, NULL,
+ packet->options, *opt_state,
+ &global_scope, root_group, NULL);
+
+ /*
+ * A small bit of special handling for Solicit messages.
+ *
+ * We could move the logic into a flag, but for now just check
+ * explicitly.
+ */
+ if (packet->dhcpv6_msg_type == DHCPV6_SOLICIT) {
+ reply->msg_type = DHCPV6_ADVERTISE;
+
+ /*
+ * If:
+ * - this message type supports rapid commit (Solicit), and
+ * - the server is configured to supply a rapid commit, and
+ * - the client requests a rapid commit,
+ * Then we add a rapid commit option, and send Reply (instead
+ * of an Advertise).
+ */
+ oc = lookup_option(&dhcpv6_universe,
+ *opt_state, D6O_RAPID_COMMIT);
+ if (oc != NULL) {
+ oc = lookup_option(&dhcpv6_universe,
+ packet->options, D6O_RAPID_COMMIT);
+ if (oc != NULL) {
+ /* Rapid-commit in action. */
+ reply->msg_type = DHCPV6_REPLY;
+ } else {
+ /* Don't want a rapid-commit in advertise. */
+ delete_option(&dhcpv6_universe,
+ *opt_state, D6O_RAPID_COMMIT);
+ }
+ }
+ } else {
+ reply->msg_type = DHCPV6_REPLY;
+ /* Delete the rapid-commit from the sent options. */
+ oc = lookup_option(&dhcpv6_universe,
+ *opt_state, D6O_RAPID_COMMIT);
+ if (oc != NULL) {
+ delete_option(&dhcpv6_universe,
+ *opt_state, D6O_RAPID_COMMIT);
+ }
+ }
+
+ /*
+ * Use the client's transaction identifier for the reply.
+ */
+ memcpy(reply->transaction_id, packet->dhcpv6_transaction_id,
+ sizeof(reply->transaction_id));
+
+ /*
+ * RFC 3315, section 18.2 says we need server identifier and
+ * client identifier.
+ *
+ * If the server ID is defined via the configuration file, then
+ * it will already be present in the option state at this point,
+ * so we don't need to set it.
+ *
+ * If we have a server ID passed in from the caller,
+ * use that, otherwise use the global DUID.
+ */
+ oc = lookup_option(&dhcpv6_universe, *opt_state, D6O_SERVERID);
+ if (oc == NULL) {
+ if (server_id == NULL) {
+ server_id_data = server_duid.data;
+ server_id_len = server_duid.len;
+ } else {
+ server_id_data = server_id->data;
+ server_id_len = server_id->len;
+ }
+ if (!save_option_buffer(&dhcpv6_universe, *opt_state,
+ NULL, (unsigned char *)server_id_data,
+ server_id_len, D6O_SERVERID, 0)) {
+ log_error("start_reply: "
+ "error saving server identifier.");
+ return 0;
+ }
+ }
+
+ if (client_id->buffer != NULL) {
+ if (!save_option_buffer(&dhcpv6_universe, *opt_state,
+ client_id->buffer,
+ (unsigned char *)client_id->data,
+ client_id->len,
+ D6O_CLIENTID, 0)) {
+ log_error("start_reply: error saving "
+ "client identifier.");
+ return 0;
+ }
+ }
+
+ /*
+ * If the client accepts reconfiguration, let it know that we
+ * will send them.
+ *
+ * Note: we don't actually do this yet, but DOCSIS requires we
+ * claim to.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_RECONF_ACCEPT);
+ if (oc != NULL) {
+ if (!save_option_buffer(&dhcpv6_universe, *opt_state,
+ NULL, (unsigned char *)"", 0,
+ D6O_RECONF_ACCEPT, 0)) {
+ log_error("start_reply: "
+ "error saving RECONF_ACCEPT option.");
+ option_state_dereference(opt_state, MDL);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Try to get the IPv6 address the client asked for from the
+ * pool.
+ *
+ * addr is the result (should be a pointer to NULL on entry)
+ * pool is the pool to search in
+ * requested_addr is the address the client wants
+ */
+static isc_result_t
+try_client_v6_address(struct iasubopt **addr,
+ struct ipv6_pool *pool,
+ const struct data_string *requested_addr)
+{
+ struct in6_addr tmp_addr;
+ isc_result_t result;
+
+ if (requested_addr->len < sizeof(tmp_addr)) {
+ return DHCP_R_INVALIDARG;
+ }
+ memcpy(&tmp_addr, requested_addr->data, sizeof(tmp_addr));
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr)) {
+ return ISC_R_FAILURE;
+ }
+
+ /*
+ * The address is not covered by this (or possibly any) dynamic
+ * range.
+ */
+ if (!ipv6_in_pool(&tmp_addr, pool)) {
+ return ISC_R_ADDRNOTAVAIL;
+ }
+
+ if (lease6_exists(pool, &tmp_addr)) {
+ return ISC_R_ADDRINUSE;
+ }
+
+ result = iasubopt_allocate(addr, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ (*addr)->addr = tmp_addr;
+ (*addr)->plen = 0;
+
+ /* Default is soft binding for 2 minutes. */
+ result = add_lease6(pool, *addr, cur_time + 120);
+ if (result != ISC_R_SUCCESS) {
+ iasubopt_dereference(addr, MDL);
+ }
+ return result;
+}
+
+/*
+ * Get an IPv6 address for the client.
+ *
+ * addr is the result (should be a pointer to NULL on entry)
+ * packet is the information about the packet from the client
+ * requested_iaaddr is a hint from the client
+ * client_id is the DUID for the client
+ */
+static isc_result_t
+pick_v6_address(struct iasubopt **addr, struct shared_network *shared_network,
+ const struct data_string *client_id)
+{
+ struct ipv6_pool *p;
+ int i;
+ int start_pool;
+ unsigned int attempts;
+ char tmp_buf[INET6_ADDRSTRLEN];
+
+ /*
+ * No address pools, we're done.
+ */
+ if (shared_network->ipv6_pools == NULL) {
+ log_debug("Unable to pick client address: "
+ "no IPv6 pools on this shared network");
+ return ISC_R_NORESOURCES;
+ }
+ for (i = 0;; i++) {
+ p = shared_network->ipv6_pools[i];
+ if (p == NULL) {
+ log_debug("Unable to pick client address: "
+ "no IPv6 address pools "
+ "on this shared network");
+ return ISC_R_NORESOURCES;
+ }
+ if (p->pool_type == D6O_IA_NA) {
+ break;
+ }
+ }
+
+ /*
+ * Otherwise try to get a lease from the first subnet possible.
+ *
+ * We start looking at the last pool we allocated from, unless
+ * it had a collision trying to allocate an address. This will
+ * tend to move us into less-filled pools.
+ */
+ start_pool = shared_network->last_ipv6_pool;
+ i = start_pool;
+ do {
+
+ p = shared_network->ipv6_pools[i];
+ if ((p->pool_type == D6O_IA_NA) &&
+ (create_lease6(p, addr, &attempts, client_id,
+ cur_time + 120) == ISC_R_SUCCESS)) {
+ /*
+ * Record the pool used (or next one if there
+ * was a collision).
+ */
+ if (attempts > 1) {
+ i++;
+ if (shared_network->ipv6_pools[i] == NULL) {
+ i = 0;
+ }
+ }
+ shared_network->last_ipv6_pool = i;
+
+ log_debug("Picking pool address %s",
+ inet_ntop(AF_INET6, &((*addr)->addr),
+ tmp_buf, sizeof(tmp_buf)));
+ return ISC_R_SUCCESS;
+ }
+
+ i++;
+ if (shared_network->ipv6_pools[i] == NULL) {
+ i = 0;
+ }
+ } while (i != start_pool);
+
+ /*
+ * If we failed to pick an IPv6 address from any of the subnets.
+ * Presumably that means we have no addresses for the client.
+ */
+ log_debug("Unable to pick client address: no addresses available");
+ return ISC_R_NORESOURCES;
+}
+
+/*
+ * Try to get the IPv6 prefix the client asked for from the
+ * prefix pool.
+ *
+ * pref is the result (should be a pointer to NULL on entry)
+ * pool is the prefix pool to search in
+ * requested_pref is the address the client wants
+ */
+static isc_result_t
+try_client_v6_prefix(struct iasubopt **pref,
+ struct ipv6_pool *pool,
+ const struct data_string *requested_pref)
+{
+ u_int8_t tmp_plen;
+ struct in6_addr tmp_pref;
+ struct iaddr ia;
+ isc_result_t result;
+
+ if (requested_pref->len < sizeof(tmp_plen) + sizeof(tmp_pref)) {
+ return DHCP_R_INVALIDARG;
+ }
+ tmp_plen = (int) requested_pref->data[0];
+ if ((tmp_plen < 3) || (tmp_plen > 128)) {
+ return ISC_R_FAILURE;
+ }
+ memcpy(&tmp_pref, requested_pref->data + 1, sizeof(tmp_pref));
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_pref)) {
+ return ISC_R_FAILURE;
+ }
+ ia.len = 16;
+ memcpy(&ia.iabuf, &tmp_pref, 16);
+ if (!is_cidr_mask_valid(&ia, (int) tmp_plen)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (((int)tmp_plen != pool->units) ||
+ !ipv6_in_pool(&tmp_pref, pool)) {
+ return ISC_R_FAILURE;
+ }
+
+ if (prefix6_exists(pool, &tmp_pref, tmp_plen)) {
+ return ISC_R_ADDRINUSE;
+ }
+
+ result = iasubopt_allocate(pref, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ (*pref)->addr = tmp_pref;
+ (*pref)->plen = tmp_plen;
+
+ /* Default is soft binding for 2 minutes. */
+ result = add_lease6(pool, *pref, cur_time + 120);
+ if (result != ISC_R_SUCCESS) {
+ iasubopt_dereference(pref, MDL);
+ }
+ return result;
+}
+
+/*
+ * Get an IPv6 prefix for the client.
+ *
+ * pref is the result (should be a pointer to NULL on entry)
+ * packet is the information about the packet from the client
+ * requested_iaprefix is a hint from the client
+ * plen is -1 or the requested prefix length
+ * client_id is the DUID for the client
+ */
+static isc_result_t
+pick_v6_prefix(struct iasubopt **pref, int plen,
+ struct shared_network *shared_network,
+ const struct data_string *client_id)
+{
+ struct ipv6_pool *p;
+ int i;
+ unsigned int attempts;
+ char tmp_buf[INET6_ADDRSTRLEN];
+
+ /*
+ * No prefix pools, we're done.
+ */
+ if (shared_network->ipv6_pools == NULL) {
+ log_debug("Unable to pick client prefix: "
+ "no IPv6 pools on this shared network");
+ return ISC_R_NORESOURCES;
+ }
+ for (i = 0;; i++) {
+ p = shared_network->ipv6_pools[i];
+ if (p == NULL) {
+ log_debug("Unable to pick client prefix: "
+ "no IPv6 prefix pools "
+ "on this shared network");
+ return ISC_R_NORESOURCES;
+ }
+ if (p->pool_type == D6O_IA_PD) {
+ break;
+ }
+ }
+
+ /*
+ * Otherwise try to get a prefix.
+ */
+ for (i = 0;; i++) {
+ p = shared_network->ipv6_pools[i];
+ if (p == NULL) {
+ break;
+ }
+ if (p->pool_type != D6O_IA_PD) {
+ continue;
+ }
+
+ /*
+ * Try only pools with the requested prefix length if any.
+ */
+ if ((plen >= 0) && (p->units != plen)) {
+ continue;
+ }
+
+ if (create_prefix6(p, pref, &attempts, client_id,
+ cur_time + 120) == ISC_R_SUCCESS) {
+ log_debug("Picking pool prefix %s/%u",
+ inet_ntop(AF_INET6, &((*pref)->addr),
+ tmp_buf, sizeof(tmp_buf)),
+ (unsigned) (*pref)->plen);
+ return ISC_R_SUCCESS;
+ }
+ }
+
+ /*
+ * If we failed to pick an IPv6 prefix
+ * Presumably that means we have no prefixes for the client.
+ */
+ log_debug("Unable to pick client prefix: no prefixes available");
+ return ISC_R_NORESOURCES;
+}
+
+/*
+ * lease_to_client() is called from several messages to construct a
+ * reply that contains all that we know about the client's correct lease
+ * (or projected lease).
+ *
+ * Solicit - "Soft" binding, ignore unknown addresses or bindings, just
+ * send what we "may" give them on a request.
+ *
+ * Request - "Hard" binding, but ignore supplied addresses (just provide what
+ * the client should really use).
+ *
+ * Renew - "Hard" binding, but client-supplied addresses are 'real'. Error
+ * Rebind out any "wrong" addresses the client sends. This means we send
+ * an empty IA_NA with a status code of NoBinding or NotOnLink or
+ * possibly send the address with zeroed lifetimes.
+ *
+ * Information-Request - No binding.
+ *
+ * The basic structure is to traverse the client-supplied data first, and
+ * validate and echo back any contents that can be. If the client-supplied
+ * data does not error out (on renew/rebind as above), but we did not send
+ * any addresses, attempt to allocate one.
+ */
+/* TODO: look at client hints for lease times */
+static void
+lease_to_client(struct data_string *reply_ret,
+ struct packet *packet,
+ const struct data_string *client_id,
+ const struct data_string *server_id)
+{
+ static struct reply_state reply;
+ struct option_cache *oc;
+ struct data_string packet_oro;
+ isc_boolean_t no_resources_avail;
+
+ /* Locate the client. */
+ if (shared_network_from_packet6(&reply.shared,
+ packet) != ISC_R_SUCCESS)
+ goto exit;
+
+ /*
+ * Initialize the reply.
+ */
+ packet_reference(&reply.packet, packet, MDL);
+ data_string_copy(&reply.client_id, client_id, MDL);
+
+ if (!start_reply(packet, client_id, server_id, &reply.opt_state,
+ &reply.buf.reply))
+ goto exit;
+
+ /* Set the write cursor to just past the reply header. */
+ reply.cursor = REPLY_OPTIONS_INDEX;
+
+ /*
+ * Get the ORO from the packet, if any.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ORO);
+ memset(&packet_oro, 0, sizeof(packet_oro));
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&packet_oro, packet,
+ NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("lease_to_client: error evaluating ORO.");
+ goto exit;
+ }
+ }
+
+ /*
+ * Find a host record that matches from the packet, if any, and is
+ * valid for the shared network the client is on.
+ */
+ if (find_hosts_by_uid(&reply.host, client_id->data, client_id->len,
+ MDL))
+ seek_shared_host(&reply.host, reply.shared);
+
+ if ((reply.host == NULL) &&
+ find_hosts_by_option(&reply.host, packet, packet->options, MDL))
+ seek_shared_host(&reply.host, reply.shared);
+
+ /*
+ * Check for 'hardware' matches last, as some of the synthesis methods
+ * are not considered to be as reliable.
+ */
+ if ((reply.host == NULL) &&
+ find_hosts_by_duid_chaddr(&reply.host, client_id))
+ seek_shared_host(&reply.host, reply.shared);
+
+ /* Process the client supplied IA's onto the reply buffer. */
+ reply.ia_count = 0;
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ no_resources_avail = ISC_FALSE;
+ for (; oc != NULL ; oc = oc->next) {
+ isc_result_t status;
+
+ /* Start counting resources (addresses) offered. */
+ reply.client_resources = 0;
+ reply.resources_included = ISC_FALSE;
+
+ status = reply_process_ia_na(&reply, oc);
+
+ /*
+ * We continue to try other IA's whether we can address
+ * this one or not. Any other result is an immediate fail.
+ */
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_NORESOURCES))
+ goto exit;
+
+ /*
+ * If any address cannot be given to any IA, then set the
+ * NoAddrsAvail status code.
+ */
+ if (reply.client_resources == 0)
+ no_resources_avail = ISC_TRUE;
+ }
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA);
+ for (; oc != NULL ; oc = oc->next) {
+ isc_result_t status;
+
+ /* Start counting resources (addresses) offered. */
+ reply.client_resources = 0;
+ reply.resources_included = ISC_FALSE;
+
+ status = reply_process_ia_ta(&reply, oc);
+
+ /*
+ * We continue to try other IA's whether we can address
+ * this one or not. Any other result is an immediate fail.
+ */
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_NORESOURCES))
+ goto exit;
+
+ /*
+ * If any address cannot be given to any IA, then set the
+ * NoAddrsAvail status code.
+ */
+ if (reply.client_resources == 0)
+ no_resources_avail = ISC_TRUE;
+ }
+
+ /* Same for IA_PD's. */
+ reply.pd_count = 0;
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+ for (; oc != NULL ; oc = oc->next) {
+ isc_result_t status;
+
+ /* Start counting resources (prefixes) offered. */
+ reply.client_resources = 0;
+ reply.resources_included = ISC_FALSE;
+
+ status = reply_process_ia_pd(&reply, oc);
+
+ /*
+ * We continue to try other IA_PD's whether we can address
+ * this one or not. Any other result is an immediate fail.
+ */
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_NORESOURCES))
+ goto exit;
+
+ /*
+ * If any prefix cannot be given to any IA_PD, then
+ * set the NoPrefixAvail status code.
+ */
+ if (reply.client_resources == 0)
+ no_resources_avail = ISC_TRUE;
+ }
+
+ /*
+ * Make no reply if we gave no resources and is not
+ * for Information-Request.
+ */
+ if ((reply.ia_count == 0) && (reply.pd_count == 0)) {
+ if (reply.packet->dhcpv6_msg_type !=
+ DHCPV6_INFORMATION_REQUEST)
+ goto exit;
+
+ /*
+ * Because we only execute statements on a per-IA basis,
+ * we need to execute statements in any non-IA reply to
+ * source configuration.
+ */
+ execute_statements_in_scope(NULL, reply.packet, NULL, NULL,
+ reply.packet->options,
+ reply.opt_state, &global_scope,
+ reply.shared->group, root_group);
+
+ /* Bring in any configuration from a host record. */
+ if (reply.host != NULL)
+ execute_statements_in_scope(NULL, reply.packet, NULL,
+ NULL, reply.packet->options,
+ reply.opt_state,
+ &global_scope,
+ reply.host->group,
+ reply.shared->group);
+ }
+
+ /*
+ * RFC3315 section 17.2.2 (Solicit):
+ *
+ * If the server will not assign any addresses to any IAs in a
+ * subsequent Request from the client, the server MUST send an
+ * Advertise message to the client that includes only a Status
+ * Code option with code NoAddrsAvail and a status message for
+ * the user, a Server Identifier option with the server's DUID,
+ * and a Client Identifier option with the client's DUID.
+ *
+ * Section 18.2.1 (Request):
+ *
+ * If the server cannot assign any addresses to an IA in the
+ * message from the client, the server MUST include the IA in
+ * the Reply message with no addresses in the IA and a Status
+ * Code option in the IA containing status code NoAddrsAvail.
+ *
+ * Section 18.1.8 (Client Behavior):
+ *
+ * Leave unchanged any information about addresses the client has
+ * recorded in the IA but that were not included in the IA from
+ * the server.
+ * Sends a Renew/Rebind if the IA is not in the Reply message.
+ */
+ if (no_resources_avail && (reply.ia_count != 0) &&
+ (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT))
+ {
+ /* Set the NoAddrsAvail status code. */
+ if (!set_status_code(STATUS_NoAddrsAvail,
+ "No addresses available for this "
+ "interface.", reply.opt_state)) {
+ log_error("lease_to_client: Unable to set "
+ "NoAddrsAvail status code.");
+ goto exit;
+ }
+
+ /* Rewind the cursor to the start. */
+ reply.cursor = REPLY_OPTIONS_INDEX;
+
+ /*
+ * Produce an advertise that includes only:
+ *
+ * Status code.
+ * Server DUID.
+ * Client DUID.
+ */
+ reply.buf.reply.msg_type = DHCPV6_ADVERTISE;
+ reply.cursor += store_options6((char *)reply.buf.data +
+ reply.cursor,
+ sizeof(reply.buf) -
+ reply.cursor,
+ reply.opt_state, reply.packet,
+ required_opts_NAA,
+ NULL);
+ } else if (no_resources_avail && (reply.ia_count == 0) &&
+ (reply.packet->dhcpv6_msg_type == DHCPV6_SOLICIT))
+ {
+ /* Set the NoPrefixAvail status code. */
+ if (!set_status_code(STATUS_NoPrefixAvail,
+ "No prefixes available for this "
+ "interface.", reply.opt_state)) {
+ log_error("lease_to_client: Unable to set "
+ "NoPrefixAvail status code.");
+ goto exit;
+ }
+
+ /* Rewind the cursor to the start. */
+ reply.cursor = REPLY_OPTIONS_INDEX;
+
+ /*
+ * Produce an advertise that includes only:
+ *
+ * Status code.
+ * Server DUID.
+ * Client DUID.
+ */
+ reply.buf.reply.msg_type = DHCPV6_ADVERTISE;
+ reply.cursor += store_options6((char *)reply.buf.data +
+ reply.cursor,
+ sizeof(reply.buf) -
+ reply.cursor,
+ reply.opt_state, reply.packet,
+ required_opts_NAA,
+ NULL);
+ } else {
+ /*
+ * Having stored the client's IA's, store any options that
+ * will fit in the remaining space.
+ */
+ reply.cursor += store_options6((char *)reply.buf.data +
+ reply.cursor,
+ sizeof(reply.buf) -
+ reply.cursor,
+ reply.opt_state, reply.packet,
+ required_opts_solicit,
+ &packet_oro);
+ }
+
+ /* Return our reply to the caller. */
+ reply_ret->len = reply.cursor;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply.cursor, MDL)) {
+ log_fatal("No memory to store Reply.");
+ }
+ memcpy(reply_ret->buffer->data, reply.buf.data, reply.cursor);
+ reply_ret->data = reply_ret->buffer->data;
+
+ exit:
+ /* Cleanup. */
+ if (reply.shared != NULL)
+ shared_network_dereference(&reply.shared, MDL);
+ if (reply.host != NULL)
+ host_dereference(&reply.host, MDL);
+ if (reply.opt_state != NULL)
+ option_state_dereference(&reply.opt_state, MDL);
+ if (reply.packet != NULL)
+ packet_dereference(&reply.packet, MDL);
+ if (reply.client_id.data != NULL)
+ data_string_forget(&reply.client_id, MDL);
+ reply.renew = reply.rebind = reply.prefer = reply.valid = 0;
+ reply.cursor = 0;
+}
+
+/* Process a client-supplied IA_NA. This may append options to the tail of
+ * the reply packet being built in the reply_state structure.
+ */
+static isc_result_t
+reply_process_ia_na(struct reply_state *reply, struct option_cache *ia) {
+ isc_result_t status = ISC_R_SUCCESS;
+ u_int32_t iaid;
+ unsigned ia_cursor;
+ struct option_state *packet_ia;
+ struct option_cache *oc;
+ struct data_string ia_data, data;
+
+ /* Initialize values that will get cleaned up on return. */
+ packet_ia = NULL;
+ memset(&ia_data, 0, sizeof(ia_data));
+ memset(&data, 0, sizeof(data));
+ /*
+ * Note that find_client_address() may set reply->lease.
+ */
+
+ /* Make sure there is at least room for the header. */
+ if ((reply->cursor + IA_NA_OFFSET + 4) > sizeof(reply->buf)) {
+ log_error("reply_process_ia_na: Reply too long for IA.");
+ return ISC_R_NOSPACE;
+ }
+
+
+ /* Fetch the IA_NA contents. */
+ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet,
+ ia, IA_NA_OFFSET)) {
+ log_error("reply_process_ia_na: error evaluating ia");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Extract IA_NA header contents. */
+ iaid = getULong(ia_data.data);
+ reply->renew = getULong(ia_data.data + 4);
+ reply->rebind = getULong(ia_data.data + 8);
+
+ /* Create an IA_NA structure. */
+ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data,
+ reply->client_id.len, MDL) != ISC_R_SUCCESS) {
+ log_error("reply_process_ia_na: no memory for ia.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ reply->ia->ia_type = D6O_IA_NA;
+
+ /* Cache pre-existing IA, if any. */
+ ia_hash_lookup(&reply->old_ia, ia_na_active,
+ (unsigned char *)reply->ia->iaid_duid.data,
+ reply->ia->iaid_duid.len, MDL);
+
+ /*
+ * Create an option cache to carry the IA_NA option contents, and
+ * execute any user-supplied values into it.
+ */
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /* Check & cache the fixed host record. */
+ if ((reply->host != NULL) && (reply->host->fixed_addr != NULL)) {
+ struct iaddr tmp_addr;
+
+ if (!evaluate_option_cache(&reply->fixed, NULL, NULL, NULL,
+ NULL, NULL, &global_scope,
+ reply->host->fixed_addr, MDL)) {
+ log_error("reply_process_ia_na: unable to evaluate "
+ "fixed address.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ if (reply->fixed.len < 16) {
+ log_error("reply_process_ia_na: invalid fixed address.");
+ status = DHCP_R_INVALIDARG;
+ goto cleanup;
+ }
+
+ /* Find the static lease's subnet. */
+ tmp_addr.len = 16;
+ memcpy(tmp_addr.iabuf, reply->fixed.data, 16);
+
+ if (find_grouped_subnet(&reply->subnet, reply->shared,
+ tmp_addr, MDL) == 0)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ reply->static_lease = ISC_TRUE;
+ } else
+ reply->static_lease = ISC_FALSE;
+
+ /*
+ * Save the cursor position at the start of the IA, so we can
+ * set length and adjust t1/t2 values later. We write a temporary
+ * header out now just in case we decide to adjust the packet
+ * within sub-process functions.
+ */
+ ia_cursor = reply->cursor;
+
+ /* Initialize the IA_NA header. First the code. */
+ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_NA);
+ reply->cursor += 2;
+
+ /* Then option length. */
+ putUShort(reply->buf.data + reply->cursor, 0x0Cu);
+ reply->cursor += 2;
+
+ /* Then IA_NA header contents; IAID. */
+ putULong(reply->buf.data + reply->cursor, iaid);
+ reply->cursor += 4;
+
+ /* We store the client's t1 for now, and may over-ride it later. */
+ putULong(reply->buf.data + reply->cursor, reply->renew);
+ reply->cursor += 4;
+
+ /* We store the client's t2 for now, and may over-ride it later. */
+ putULong(reply->buf.data + reply->cursor, reply->rebind);
+ reply->cursor += 4;
+
+ /*
+ * For each address in this IA_NA, decide what to do about it.
+ *
+ * Guidelines:
+ *
+ * The client leaves unchanged any infomation about addresses
+ * it has recorded but are not included ("cancel/break" below).
+ * A not included IA ("cleanup" below) could give a Renew/Rebind.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR);
+ reply->valid = reply->prefer = 0xffffffff;
+ reply->client_valid = reply->client_prefer = 0;
+ for (; oc != NULL ; oc = oc->next) {
+ status = reply_process_addr(reply, oc);
+
+ /*
+ * Canceled means we did not allocate addresses to the
+ * client, but we're "done" with this IA - we set a status
+ * code. So transmit this reply, e.g., move on to the next
+ * IA.
+ */
+ if (status == ISC_R_CANCELED)
+ break;
+
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_ADDRINUSE) &&
+ (status != ISC_R_ADDRNOTAVAIL))
+ goto cleanup;
+ }
+
+ reply->ia_count++;
+
+ /*
+ * If we fell through the above and never gave the client
+ * an address, give it one now.
+ */
+ if ((status != ISC_R_CANCELED) && (reply->client_resources == 0)) {
+ status = find_client_address(reply);
+
+ if (status == ISC_R_NORESOURCES) {
+ switch (reply->packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ /*
+ * No address for any IA is handled
+ * by the caller.
+ */
+ /* FALL THROUGH */
+
+ case DHCPV6_REQUEST:
+ /* Section 18.2.1 (Request):
+ *
+ * If the server cannot assign any addresses to
+ * an IA in the message from the client, the
+ * server MUST include the IA in the Reply
+ * message with no addresses in the IA and a
+ * Status Code option in the IA containing
+ * status code NoAddrsAvail.
+ */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia,
+ MDL))
+ {
+ log_error("reply_process_ia_na: No "
+ "memory for option state "
+ "wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ if (!set_status_code(STATUS_NoAddrsAvail,
+ "No addresses available "
+ "for this interface.",
+ reply->reply_ia)) {
+ log_error("reply_process_ia_na: Unable "
+ "to set NoAddrsAvail status "
+ "code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ status = ISC_R_SUCCESS;
+ break;
+
+ default:
+ /*
+ * RFC 3315 does not tell us to emit a status
+ * code in this condition, or anything else.
+ *
+ * If we included non-allocated addresses
+ * (zeroed lifetimes) in an IA, then the client
+ * will deconfigure them.
+ *
+ * So we want to include the IA even if we
+ * can't give it a new address if it includes
+ * zeroed lifetime addresses.
+ *
+ * We don't want to include the IA if we
+ * provide zero addresses including zeroed
+ * lifetimes.
+ */
+ if (reply->resources_included)
+ status = ISC_R_SUCCESS;
+ else
+ goto cleanup;
+ break;
+ }
+ }
+
+ if (status != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ reply->cursor += store_options6((char *)reply->buf.data + reply->cursor,
+ sizeof(reply->buf) - reply->cursor,
+ reply->reply_ia, reply->packet,
+ required_opts_IA, NULL);
+
+ /* Reset the length of this IA to match what was just written. */
+ putUShort(reply->buf.data + ia_cursor + 2,
+ reply->cursor - (ia_cursor + 4));
+
+ /*
+ * T1/T2 time selection is kind of weird. We actually use DHCP
+ * (v4) scoped options as handy existing places where these might
+ * be configured by an administrator. A value of zero tells the
+ * client it may choose its own renewal time.
+ */
+ reply->renew = 0;
+ oc = lookup_option(&dhcp_universe, reply->opt_state,
+ DHO_DHCP_RENEWAL_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, &global_scope,
+ oc, MDL) ||
+ (data.len != 4)) {
+ log_error("Invalid renewal time.");
+ } else {
+ reply->renew = getULong(data.data);
+ }
+
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ }
+ putULong(reply->buf.data + ia_cursor + 8, reply->renew);
+
+ /* Now T2. */
+ reply->rebind = 0;
+ oc = lookup_option(&dhcp_universe, reply->opt_state,
+ DHO_DHCP_REBINDING_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, &global_scope,
+ oc, MDL) ||
+ (data.len != 4)) {
+ log_error("Invalid rebinding time.");
+ } else {
+ reply->rebind = getULong(data.data);
+ }
+
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ }
+ putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
+
+ /*
+ * If this is not a 'soft' binding, consume the new changes into
+ * the database (if any have been attached to the ia_na).
+ *
+ * Loop through the assigned dynamic addresses, referencing the
+ * leases onto this IA_NA rather than any old ones, and updating
+ * pool timers for each (if any).
+ */
+ if ((status != ISC_R_CANCELED) && !reply->static_lease &&
+ (reply->buf.reply.msg_type == DHCPV6_REPLY) &&
+ (reply->ia->num_iasubopt != 0)) {
+ struct iasubopt *tmp;
+ struct data_string *ia_id;
+ int i;
+
+ for (i = 0 ; i < reply->ia->num_iasubopt ; i++) {
+ tmp = reply->ia->iasubopt[i];
+
+ if (tmp->ia != NULL)
+ ia_dereference(&tmp->ia, MDL);
+ ia_reference(&tmp->ia, reply->ia, MDL);
+
+ /* Commit 'hard' bindings. */
+ tmp->hard_lifetime_end_time =
+ tmp->soft_lifetime_end_time;
+ tmp->soft_lifetime_end_time = 0;
+ renew_lease6(tmp->ipv6_pool, tmp);
+ schedule_lease_timeout(tmp->ipv6_pool);
+
+#if defined (NSUPDATE)
+ /*
+ * Perform ddns updates.
+ */
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_DDNS_UPDATES);
+ if ((oc == NULL) ||
+ evaluate_boolean_option_cache(NULL, reply->packet,
+ NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ &tmp->scope,
+ oc, MDL)) {
+ ddns_updates(reply->packet, NULL, NULL,
+ tmp, NULL, reply->opt_state);
+ }
+#endif
+ }
+
+ /* Remove any old ia from the hash. */
+ if (reply->old_ia != NULL) {
+ ia_id = &reply->old_ia->iaid_duid;
+ ia_hash_delete(ia_na_active,
+ (unsigned char *)ia_id->data,
+ ia_id->len, MDL);
+ ia_dereference(&reply->old_ia, MDL);
+ }
+
+ /* Put new ia into the hash. */
+ reply->ia->cltt = cur_time;
+ ia_id = &reply->ia->iaid_duid;
+ ia_hash_add(ia_na_active, (unsigned char *)ia_id->data,
+ ia_id->len, reply->ia, MDL);
+
+ write_ia(reply->ia);
+ }
+
+ cleanup:
+ if (packet_ia != NULL)
+ option_state_dereference(&packet_ia, MDL);
+ if (reply->reply_ia != NULL)
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (ia_data.data != NULL)
+ data_string_forget(&ia_data, MDL);
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (reply->ia != NULL)
+ ia_dereference(&reply->ia, MDL);
+ if (reply->old_ia != NULL)
+ ia_dereference(&reply->old_ia, MDL);
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+ if (reply->fixed.data != NULL)
+ data_string_forget(&reply->fixed, MDL);
+ if (reply->subnet != NULL)
+ subnet_dereference(&reply->subnet, MDL);
+
+ /*
+ * ISC_R_CANCELED is a status code used by the addr processing to
+ * indicate we're replying with a status code. This is still a
+ * success at higher layers.
+ */
+ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status);
+}
+
+/*
+ * Process an IAADDR within a given IA_xA, storing any IAADDR reply contents
+ * into the reply's current ia-scoped option cache. Returns ISC_R_CANCELED
+ * in the event we are replying with a status code and do not wish to process
+ * more IAADDRs within this IA.
+ */
+static isc_result_t
+reply_process_addr(struct reply_state *reply, struct option_cache *addr) {
+ u_int32_t pref_life, valid_life;
+ struct binding_scope **scope;
+ struct group *group;
+ struct subnet *subnet;
+ struct iaddr tmp_addr;
+ struct option_cache *oc;
+ struct data_string iaaddr, data;
+ isc_result_t status = ISC_R_SUCCESS;
+
+ /* Initializes values that will be cleaned up. */
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ memset(&data, 0, sizeof(data));
+ /* Note that reply->lease may be set by address_is_owned() */
+
+ /*
+ * There is no point trying to process an incoming address if there
+ * is no room for an outgoing address.
+ */
+ if ((reply->cursor + 28) > sizeof(reply->buf)) {
+ log_error("reply_process_addr: Out of room for address.");
+ return ISC_R_NOSPACE;
+ }
+
+ /* Extract this IAADDR option. */
+ if (!evaluate_option_cache(&iaaddr, reply->packet, NULL, NULL,
+ reply->packet->options, NULL, &global_scope,
+ addr, MDL) ||
+ (iaaddr.len < IAADDR_OFFSET)) {
+ log_error("reply_process_addr: error evaluating IAADDR.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* The first 16 bytes are the IPv6 address. */
+ pref_life = getULong(iaaddr.data + 16);
+ valid_life = getULong(iaaddr.data + 20);
+
+ if ((reply->client_valid == 0) ||
+ (reply->client_valid > valid_life))
+ reply->client_valid = valid_life;
+
+ if ((reply->client_prefer == 0) ||
+ (reply->client_prefer > pref_life))
+ reply->client_prefer = pref_life;
+
+ /*
+ * Clients may choose to send :: as an address, with the idea to give
+ * hints about preferred-lifetime or valid-lifetime.
+ */
+ tmp_addr.len = 16;
+ memset(tmp_addr.iabuf, 0, 16);
+ if (!memcmp(iaaddr.data, tmp_addr.iabuf, 16)) {
+ /* Status remains success; we just ignore this one. */
+ goto cleanup;
+ }
+
+ /* tmp_addr len remains 16 */
+ memcpy(tmp_addr.iabuf, iaaddr.data, 16);
+
+ /*
+ * Verify that this address is on the client's network.
+ */
+ for (subnet = reply->shared->subnets ; subnet != NULL ;
+ subnet = subnet->next_sibling) {
+ if (addr_eq(subnet_number(tmp_addr, subnet->netmask),
+ subnet->net))
+ break;
+ }
+
+ /* Address not found on shared network. */
+ if (subnet == NULL) {
+ /* Ignore this address on 'soft' bindings. */
+ if (reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) {
+ /* disable rapid commit */
+ reply->buf.reply.msg_type = DHCPV6_ADVERTISE;
+ delete_option(&dhcpv6_universe,
+ reply->opt_state,
+ D6O_RAPID_COMMIT);
+ /* status remains success */
+ goto cleanup;
+ }
+
+ /*
+ * RFC3315 section 18.2.1:
+ *
+ * If the server finds that the prefix on one or more IP
+ * addresses in any IA in the message from the client is not
+ * appropriate for the link to which the client is connected,
+ * the server MUST return the IA to the client with a Status
+ * Code option with the value NotOnLink.
+ */
+ if (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) {
+ /* Rewind the IA_NA to empty. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ log_error("reply_process_addr: No memory for "
+ "option state wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /* Append a NotOnLink status code. */
+ if (!set_status_code(STATUS_NotOnLink,
+ "Address not for use on this "
+ "link.", reply->reply_ia)) {
+ log_error("reply_process_addr: Failure "
+ "setting status code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Fin (no more IAADDRs). */
+ status = ISC_R_CANCELED;
+ goto cleanup;
+ }
+
+ /*
+ * RFC3315 sections 18.2.3 and 18.2.4 have identical language:
+ *
+ * If the server finds that any of the addresses are not
+ * appropriate for the link to which the client is attached,
+ * the server returns the address to the client with lifetimes
+ * of 0.
+ */
+ if ((reply->packet->dhcpv6_msg_type != DHCPV6_RENEW) &&
+ (reply->packet->dhcpv6_msg_type != DHCPV6_REBIND)) {
+ log_error("It is impossible to lease a client that is "
+ "not sending a solicit, request, renew, or "
+ "rebind.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->send_prefer = reply->send_valid = 0;
+ goto send_addr;
+ }
+
+ /* Verify the address belongs to the client. */
+ if (!address_is_owned(reply, &tmp_addr)) {
+ /*
+ * For solicit and request, any addresses included are
+ * 'requested' addresses. For rebind, we actually have
+ * no direction on what to do from 3315 section 18.2.4!
+ * So I think the best bet is to try and give it out, and if
+ * we can't, zero lifetimes.
+ */
+ if ((reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) ||
+ (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) ||
+ (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND)) {
+ status = reply_process_try_addr(reply, &tmp_addr);
+
+ /*
+ * If the address is in use, or isn't in any dynamic
+ * range, continue as normal. If any other error was
+ * found, error out.
+ */
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_ADDRINUSE) &&
+ (status != ISC_R_ADDRNOTAVAIL))
+ goto cleanup;
+
+ /*
+ * If we didn't honor this lease, for solicit and
+ * request we simply omit it from our answer. For
+ * rebind, we send it with zeroed lifetimes.
+ */
+ if (reply->lease == NULL) {
+ if (reply->packet->dhcpv6_msg_type ==
+ DHCPV6_REBIND) {
+ reply->send_prefer = 0;
+ reply->send_valid = 0;
+ goto send_addr;
+ }
+
+ /* status remains success - ignore */
+ goto cleanup;
+ }
+ /*
+ * RFC3315 section 18.2.3:
+ *
+ * If the server cannot find a client entry for the IA the
+ * server returns the IA containing no addresses with a Status
+ * Code option set to NoBinding in the Reply message.
+ *
+ * On mismatch we (ab)use this pretending we have not the IA
+ * as soon as we have not an address.
+ */
+ } else if (reply->packet->dhcpv6_msg_type == DHCPV6_RENEW) {
+ /* Rewind the IA_NA to empty. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ log_error("reply_process_addr: No memory for "
+ "option state wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /* Append a NoBinding status code. */
+ if (!set_status_code(STATUS_NoBinding,
+ "Address not bound to this "
+ "interface.", reply->reply_ia)) {
+ log_error("reply_process_addr: Unable to "
+ "attach status code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Fin (no more IAADDRs). */
+ status = ISC_R_CANCELED;
+ goto cleanup;
+ } else {
+ log_error("It is impossible to lease a client that is "
+ "not sending a solicit, request, renew, or "
+ "rebind message.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ if (reply->static_lease) {
+ if (reply->host == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ scope = &global_scope;
+ group = reply->subnet->group;
+ } else {
+ if (reply->lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ scope = &reply->lease->scope;
+ group = reply->lease->ipv6_pool->subnet->group;
+ }
+
+ /*
+ * If client_resources is nonzero, then the reply_process_is_addressed
+ * function has executed configuration state into the reply option
+ * cache. We will use that valid cache to derive configuration for
+ * whether or not to engage in additional addresses, and similar.
+ */
+ if (reply->client_resources != 0) {
+ unsigned limit = 1;
+
+ /*
+ * Does this client have "enough" addresses already? Default
+ * to one. Everybody gets one, and one should be enough for
+ * anybody.
+ */
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_LIMIT_ADDRS_PER_IA);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet,
+ NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_addr: unable to "
+ "evaluate addrs-per-ia value.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ limit = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ /*
+ * If we wish to limit the client to a certain number of
+ * addresses, then omit the address from the reply.
+ */
+ if (reply->client_resources >= limit)
+ goto cleanup;
+ }
+
+ status = reply_process_is_addressed(reply, scope, group);
+ if (status != ISC_R_SUCCESS)
+ goto cleanup;
+
+ send_addr:
+ status = reply_process_send_addr(reply, &tmp_addr);
+
+ cleanup:
+ if (iaaddr.data != NULL)
+ data_string_forget(&iaaddr, MDL);
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+
+ return status;
+}
+
+/*
+ * Verify the address belongs to the client. If we've got a host
+ * record with a fixed address, it has to be the assigned address
+ * (fault out all else). Otherwise it's a dynamic address, so lookup
+ * that address and make sure it belongs to this DUID:IAID pair.
+ */
+static isc_boolean_t
+address_is_owned(struct reply_state *reply, struct iaddr *addr) {
+ int i;
+
+ /*
+ * This faults out addresses that don't match fixed addresses.
+ */
+ if (reply->static_lease) {
+ if (reply->fixed.data == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ if (memcmp(addr->iabuf, reply->fixed.data, 16) == 0)
+ return ISC_TRUE;
+
+ return ISC_FALSE;
+ }
+
+ if ((reply->old_ia == NULL) || (reply->old_ia->num_iasubopt == 0))
+ return ISC_FALSE;
+
+ for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
+ struct iasubopt *tmp;
+
+ tmp = reply->old_ia->iasubopt[i];
+
+ if (memcmp(addr->iabuf, &tmp->addr, 16) == 0) {
+ iasubopt_reference(&reply->lease, tmp, MDL);
+ return ISC_TRUE;
+ }
+ }
+
+ return ISC_FALSE;
+}
+
+/* Process a client-supplied IA_TA. This may append options to the tail of
+ * the reply packet being built in the reply_state structure.
+ */
+static isc_result_t
+reply_process_ia_ta(struct reply_state *reply, struct option_cache *ia) {
+ isc_result_t status = ISC_R_SUCCESS;
+ u_int32_t iaid;
+ unsigned ia_cursor;
+ struct option_state *packet_ia;
+ struct option_cache *oc;
+ struct data_string ia_data, data;
+ struct data_string iaaddr;
+ u_int32_t pref_life, valid_life;
+ struct iaddr tmp_addr;
+
+ /* Initialize values that will get cleaned up on return. */
+ packet_ia = NULL;
+ memset(&ia_data, 0, sizeof(ia_data));
+ memset(&data, 0, sizeof(data));
+ memset(&iaaddr, 0, sizeof(iaaddr));
+
+ /* Make sure there is at least room for the header. */
+ if ((reply->cursor + IA_TA_OFFSET + 4) > sizeof(reply->buf)) {
+ log_error("reply_process_ia_ta: Reply too long for IA.");
+ return ISC_R_NOSPACE;
+ }
+
+
+ /* Fetch the IA_TA contents. */
+ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet,
+ ia, IA_TA_OFFSET)) {
+ log_error("reply_process_ia_ta: error evaluating ia");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Extract IA_TA header contents. */
+ iaid = getULong(ia_data.data);
+
+ /* Create an IA_TA structure. */
+ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data,
+ reply->client_id.len, MDL) != ISC_R_SUCCESS) {
+ log_error("reply_process_ia_ta: no memory for ia.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ reply->ia->ia_type = D6O_IA_TA;
+
+ /* Cache pre-existing IA, if any. */
+ ia_hash_lookup(&reply->old_ia, ia_ta_active,
+ (unsigned char *)reply->ia->iaid_duid.data,
+ reply->ia->iaid_duid.len, MDL);
+
+ /*
+ * Create an option cache to carry the IA_TA option contents, and
+ * execute any user-supplied values into it.
+ */
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /*
+ * Temporary leases are dynamic by definition.
+ */
+ reply->static_lease = ISC_FALSE;
+
+ /*
+ * Save the cursor position at the start of the IA, so we can
+ * set length later. We write a temporary
+ * header out now just in case we decide to adjust the packet
+ * within sub-process functions.
+ */
+ ia_cursor = reply->cursor;
+
+ /* Initialize the IA_TA header. First the code. */
+ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_TA);
+ reply->cursor += 2;
+
+ /* Then option length. */
+ putUShort(reply->buf.data + reply->cursor, 0x04u);
+ reply->cursor += 2;
+
+ /* Then IA_TA header contents; IAID. */
+ putULong(reply->buf.data + reply->cursor, iaid);
+ reply->cursor += 4;
+
+ /*
+ * Deal with an IAADDR for lifetimes.
+ * For all or none, process IAADDRs as hints.
+ */
+ reply->valid = reply->prefer = 0xffffffff;
+ reply->client_valid = reply->client_prefer = 0;
+ oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAADDR);
+ for (; oc != NULL; oc = oc->next) {
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ if (!evaluate_option_cache(&iaaddr, reply->packet,
+ NULL, NULL,
+ reply->packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (iaaddr.len < IAADDR_OFFSET)) {
+ log_error("reply_process_ia_ta: error "
+ "evaluating IAADDR.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ /* The first 16 bytes are the IPv6 address. */
+ pref_life = getULong(iaaddr.data + 16);
+ valid_life = getULong(iaaddr.data + 20);
+
+ if ((reply->client_valid == 0) ||
+ (reply->client_valid > valid_life))
+ reply->client_valid = valid_life;
+
+ if ((reply->client_prefer == 0) ||
+ (reply->client_prefer > pref_life))
+ reply->client_prefer = pref_life;
+
+ /* Nothing more if something has failed. */
+ if (status == ISC_R_CANCELED)
+ continue;
+
+ tmp_addr.len = 16;
+ memcpy(tmp_addr.iabuf, iaaddr.data, 16);
+ if (!temporary_is_available(reply, &tmp_addr))
+ goto bad_temp;
+ status = reply_process_is_addressed(reply,
+ &reply->lease->scope,
+ reply->shared->group);
+ if (status != ISC_R_SUCCESS)
+ goto bad_temp;
+ status = reply_process_send_addr(reply, &tmp_addr);
+ if (status != ISC_R_SUCCESS)
+ goto bad_temp;
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+ continue;
+
+ bad_temp:
+ /* Rewind the IA_TA to empty. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ status = ISC_R_CANCELED;
+ reply->client_resources = 0;
+ reply->resources_included = ISC_FALSE;
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+ }
+ reply->ia_count++;
+
+ /*
+ * Give the client temporary addresses.
+ */
+ if (reply->client_resources != 0)
+ goto store;
+ status = find_client_temporaries(reply);
+ if (status == ISC_R_NORESOURCES) {
+ switch (reply->packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ /*
+ * No address for any IA is handled
+ * by the caller.
+ */
+ /* FALL THROUGH */
+
+ case DHCPV6_REQUEST:
+ /* Section 18.2.1 (Request):
+ *
+ * If the server cannot assign any addresses to
+ * an IA in the message from the client, the
+ * server MUST include the IA in the Reply
+ * message with no addresses in the IA and a
+ * Status Code option in the IA containing
+ * status code NoAddrsAvail.
+ */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ log_error("reply_process_ia_ta: No "
+ "memory for option state wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ if (!set_status_code(STATUS_NoAddrsAvail,
+ "No addresses available "
+ "for this interface.",
+ reply->reply_ia)) {
+ log_error("reply_process_ia_ta: Unable "
+ "to set NoAddrsAvail status code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ status = ISC_R_SUCCESS;
+ break;
+
+ default:
+ /*
+ * We don't want to include the IA if we
+ * provide zero addresses including zeroed
+ * lifetimes.
+ */
+ if (reply->resources_included)
+ status = ISC_R_SUCCESS;
+ else
+ goto cleanup;
+ break;
+ }
+ } else if (status != ISC_R_SUCCESS)
+ goto cleanup;
+
+ store:
+ reply->cursor += store_options6((char *)reply->buf.data + reply->cursor,
+ sizeof(reply->buf) - reply->cursor,
+ reply->reply_ia, reply->packet,
+ required_opts_IA, NULL);
+
+ /* Reset the length of this IA to match what was just written. */
+ putUShort(reply->buf.data + ia_cursor + 2,
+ reply->cursor - (ia_cursor + 4));
+
+ /*
+ * Consume the new changes into the database (if any have been
+ * attached to the ia_ta).
+ *
+ * Loop through the assigned dynamic addresses, referencing the
+ * leases onto this IA_TA rather than any old ones, and updating
+ * pool timers for each (if any).
+ */
+ if ((status != ISC_R_CANCELED) &&
+ (reply->buf.reply.msg_type == DHCPV6_REPLY) &&
+ (reply->ia->num_iasubopt != 0)) {
+ struct iasubopt *tmp;
+ struct data_string *ia_id;
+ int i;
+
+ for (i = 0 ; i < reply->ia->num_iasubopt ; i++) {
+ tmp = reply->ia->iasubopt[i];
+
+ if (tmp->ia != NULL)
+ ia_dereference(&tmp->ia, MDL);
+ ia_reference(&tmp->ia, reply->ia, MDL);
+
+ /* Commit 'hard' bindings. */
+ tmp->hard_lifetime_end_time =
+ tmp->soft_lifetime_end_time;
+ tmp->soft_lifetime_end_time = 0;
+ renew_lease6(tmp->ipv6_pool, tmp);
+ schedule_lease_timeout(tmp->ipv6_pool);
+
+#if defined (NSUPDATE)
+ /*
+ * Perform ddns updates.
+ */
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_DDNS_UPDATES);
+ if ((oc == NULL) ||
+ evaluate_boolean_option_cache(NULL, reply->packet,
+ NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ &tmp->scope,
+ oc, MDL)) {
+ ddns_updates(reply->packet, NULL, NULL,
+ tmp, NULL, reply->opt_state);
+ }
+#endif
+ }
+
+ /* Remove any old ia from the hash. */
+ if (reply->old_ia != NULL) {
+ ia_id = &reply->old_ia->iaid_duid;
+ ia_hash_delete(ia_ta_active,
+ (unsigned char *)ia_id->data,
+ ia_id->len, MDL);
+ ia_dereference(&reply->old_ia, MDL);
+ }
+
+ /* Put new ia into the hash. */
+ reply->ia->cltt = cur_time;
+ ia_id = &reply->ia->iaid_duid;
+ ia_hash_add(ia_ta_active, (unsigned char *)ia_id->data,
+ ia_id->len, reply->ia, MDL);
+
+ write_ia(reply->ia);
+ }
+
+ cleanup:
+ if (packet_ia != NULL)
+ option_state_dereference(&packet_ia, MDL);
+ if (iaaddr.data != NULL)
+ data_string_forget(&iaaddr, MDL);
+ if (reply->reply_ia != NULL)
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (ia_data.data != NULL)
+ data_string_forget(&ia_data, MDL);
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (reply->ia != NULL)
+ ia_dereference(&reply->ia, MDL);
+ if (reply->old_ia != NULL)
+ ia_dereference(&reply->old_ia, MDL);
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+
+ /*
+ * ISC_R_CANCELED is a status code used by the addr processing to
+ * indicate we're replying with other addresses. This is still a
+ * success at higher layers.
+ */
+ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status);
+}
+
+/*
+ * Verify the temporary address is available.
+ */
+static isc_boolean_t
+temporary_is_available(struct reply_state *reply, struct iaddr *addr) {
+ struct in6_addr tmp_addr;
+ struct subnet *subnet;
+ struct ipv6_pool *pool;
+ int i;
+
+ memcpy(&tmp_addr, addr->iabuf, sizeof(tmp_addr));
+ /*
+ * Clients may choose to send :: as an address, with the idea to give
+ * hints about preferred-lifetime or valid-lifetime.
+ * So this is not a request for this address.
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp_addr))
+ return ISC_FALSE;
+
+ /*
+ * Verify that this address is on the client's network.
+ */
+ for (subnet = reply->shared->subnets ; subnet != NULL ;
+ subnet = subnet->next_sibling) {
+ if (addr_eq(subnet_number(*addr, subnet->netmask),
+ subnet->net))
+ break;
+ }
+
+ /* Address not found on shared network. */
+ if (subnet == NULL)
+ return ISC_FALSE;
+
+ /*
+ * Check if this address is owned (must be before next step).
+ */
+ if (address_is_owned(reply, addr))
+ return ISC_TRUE;
+
+ /*
+ * Verify that this address is in a temporary pool and try to get it.
+ */
+ if (reply->shared->ipv6_pools == NULL)
+ return ISC_FALSE;
+ for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
+ if (pool->pool_type != D6O_IA_TA)
+ continue;
+ if (ipv6_in_pool(&tmp_addr, pool))
+ break;
+ }
+ if (pool == NULL)
+ return ISC_FALSE;
+ if (lease6_exists(pool, &tmp_addr))
+ return ISC_FALSE;
+ if (iasubopt_allocate(&reply->lease, MDL) != ISC_R_SUCCESS)
+ return ISC_FALSE;
+ reply->lease->addr = tmp_addr;
+ reply->lease->plen = 0;
+ /* Default is soft binding for 2 minutes. */
+ if (add_lease6(pool, reply->lease, cur_time + 120) != ISC_R_SUCCESS)
+ return ISC_FALSE;
+
+ return ISC_TRUE;
+}
+
+/*
+ * Get a temporary address per prefix.
+ */
+static isc_result_t
+find_client_temporaries(struct reply_state *reply) {
+ struct shared_network *shared;
+ int i;
+ struct ipv6_pool *p;
+ isc_result_t status;
+ unsigned int attempts;
+ struct iaddr send_addr;
+
+ /*
+ * No pools, we're done.
+ */
+ shared = reply->shared;
+ if (shared->ipv6_pools == NULL) {
+ log_debug("Unable to get client addresses: "
+ "no IPv6 pools on this shared network");
+ return ISC_R_NORESOURCES;
+ }
+
+ status = ISC_R_NORESOURCES;
+ for (i = 0;; i++) {
+ p = shared->ipv6_pools[i];
+ if (p == NULL) {
+ break;
+ }
+ if (p->pool_type != D6O_IA_TA) {
+ continue;
+ }
+
+ /*
+ * Get an address in this temporary pool.
+ */
+ status = create_lease6(p, &reply->lease, &attempts,
+ &reply->client_id, cur_time + 120);
+ if (status != ISC_R_SUCCESS) {
+ log_debug("Unable to get a temporary address.");
+ goto cleanup;
+ }
+
+ status = reply_process_is_addressed(reply,
+ &reply->lease->scope,
+ reply->lease->ipv6_pool->subnet->group);
+ if (status != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ send_addr.len = 16;
+ memcpy(send_addr.iabuf, &reply->lease->addr, 16);
+ status = reply_process_send_addr(reply, &send_addr);
+ if (status != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ if (reply->lease != NULL) {
+ iasubopt_dereference(&reply->lease, MDL);
+ }
+ }
+
+ cleanup:
+ if (reply->lease != NULL) {
+ iasubopt_dereference(&reply->lease, MDL);
+ }
+ return status;
+}
+
+/*
+ * This function only returns failure on 'hard' failures. If it succeeds,
+ * it will leave a lease structure behind.
+ */
+static isc_result_t
+reply_process_try_addr(struct reply_state *reply, struct iaddr *addr) {
+ isc_result_t status = ISC_R_NORESOURCES;
+ struct ipv6_pool *pool;
+ int i;
+ struct data_string data_addr;
+
+ if ((reply == NULL) || (reply->shared == NULL) ||
+ (reply->shared->ipv6_pools == NULL) || (addr == NULL) ||
+ (reply->lease != NULL))
+ return DHCP_R_INVALIDARG;
+
+ memset(&data_addr, 0, sizeof(data_addr));
+ data_addr.len = addr->len;
+ data_addr.data = addr->iabuf;
+
+ for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
+ if (pool->pool_type != D6O_IA_NA)
+ continue;
+ status = try_client_v6_address(&reply->lease, pool,
+ &data_addr);
+ if (status == ISC_R_SUCCESS)
+ break;
+ }
+
+ /* Note that this is just pedantry. There is no allocation to free. */
+ data_string_forget(&data_addr, MDL);
+ /* Return just the most recent status... */
+ return status;
+}
+
+/* Look around for an address to give the client. First, look through the
+ * old IA for addresses we can extend. Second, try to allocate a new address.
+ * Finally, actually add that address into the current reply IA.
+ */
+static isc_result_t
+find_client_address(struct reply_state *reply) {
+ struct iaddr send_addr;
+ isc_result_t status = ISC_R_NORESOURCES;
+ struct iasubopt *lease, *best_lease = NULL;
+ struct binding_scope **scope;
+ struct group *group;
+ int i;
+
+ if (reply->static_lease) {
+ if (reply->host == NULL)
+ return DHCP_R_INVALIDARG;
+
+ send_addr.len = 16;
+ memcpy(send_addr.iabuf, reply->fixed.data, 16);
+
+ status = ISC_R_SUCCESS;
+ scope = &global_scope;
+ group = reply->subnet->group;
+ goto send_addr;
+ }
+
+ if (reply->old_ia != NULL) {
+ for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
+ struct shared_network *candidate_shared;
+
+ lease = reply->old_ia->iasubopt[i];
+ candidate_shared = lease->ipv6_pool->shared_network;
+
+ /*
+ * Look for the best lease on the client's shared
+ * network.
+ */
+ if (candidate_shared == reply->shared) {
+ best_lease = lease_compare(lease, best_lease);
+ }
+ }
+ }
+
+ /* Try to pick a new address if we didn't find one, or if we found an
+ * abandoned lease.
+ */
+ if ((best_lease == NULL) || (best_lease->state == FTS_ABANDONED)) {
+ status = pick_v6_address(&reply->lease, reply->shared,
+ &reply->client_id);
+ } else if (best_lease != NULL) {
+ iasubopt_reference(&reply->lease, best_lease, MDL);
+ status = ISC_R_SUCCESS;
+ }
+
+ /* Pick the abandoned lease as a last resort. */
+ if ((status == ISC_R_NORESOURCES) && (best_lease != NULL)) {
+ /* I don't see how this is supposed to be done right now. */
+ log_error("Reclaiming abandoned addresses is not yet "
+ "supported. Treating this as an out of space "
+ "condition.");
+ /* iasubopt_reference(&reply->lease, best_lease, MDL); */
+ }
+
+ /* Give up now if we didn't find a lease. */
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (reply->lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ /* Draw binding scopes from the lease's binding scope, and config
+ * from the lease's containing subnet and higher. Note that it may
+ * be desirable to place the group attachment directly in the pool.
+ */
+ scope = &reply->lease->scope;
+ group = reply->lease->ipv6_pool->subnet->group;
+
+ send_addr.len = 16;
+ memcpy(send_addr.iabuf, &reply->lease->addr, 16);
+
+ send_addr:
+ status = reply_process_is_addressed(reply, scope, group);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = reply_process_send_addr(reply, &send_addr);
+ return status;
+}
+
+/* Once an address is found for a client, perform several common functions;
+ * Calculate and store valid and preferred lease times, draw client options
+ * into the option state.
+ */
+static isc_result_t
+reply_process_is_addressed(struct reply_state *reply,
+ struct binding_scope **scope, struct group *group)
+{
+ isc_result_t status = ISC_R_SUCCESS;
+ struct data_string data;
+ struct option_cache *oc;
+
+ /* Initialize values we will cleanup. */
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Bring configured options into the root packet level cache - start
+ * with the lease's closest enclosing group (passed in by the caller
+ * as 'group').
+ */
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options, reply->opt_state,
+ scope, group, root_group);
+
+ /*
+ * If there is a host record, over-ride with values configured there,
+ * without re-evaluating configuration from the previously executed
+ * group or its common enclosers.
+ */
+ if (reply->host != NULL)
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, scope,
+ reply->host->group, group);
+
+ /* Determine valid lifetime. */
+ if (reply->client_valid == 0)
+ reply->send_valid = DEFAULT_DEFAULT_LEASE_TIME;
+ else
+ reply->send_valid = reply->client_valid;
+
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_DEFAULT_LEASE_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_is_addressed: unable to "
+ "evaluate default lease time");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->send_valid = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ if (reply->client_prefer == 0)
+ reply->send_prefer = reply->send_valid;
+ else
+ reply->send_prefer = reply->client_prefer;
+
+ if (reply->send_prefer >= reply->send_valid)
+ reply->send_prefer = (reply->send_valid / 2) +
+ (reply->send_valid / 8);
+
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_PREFER_LIFETIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_is_addressed: unable to "
+ "evaluate preferred lease time");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->send_prefer = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ /* Note lowest values for later calculation of renew/rebind times. */
+ if (reply->prefer > reply->send_prefer)
+ reply->prefer = reply->send_prefer;
+
+ if (reply->valid > reply->send_valid)
+ reply->valid = reply->send_valid;
+
+#if 0
+ /*
+ * XXX: Old 4.0.0 alpha code would change the host {} record
+ * XXX: uid upon lease assignment. This was intended to cover the
+ * XXX: case where a client first identifies itself using vendor
+ * XXX: options in a solicit, or request, but later neglects to include
+ * XXX: these options in a Renew or Rebind. It is not clear that this
+ * XXX: is required, and has some startling ramifications (such as
+ * XXX: how to recover this dynamic host {} state across restarts).
+ */
+ if (reply->host != NULL)
+ change_host_uid(host, reply->client_id->data,
+ reply->client_id->len);
+#endif /* 0 */
+
+ /* Perform dynamic lease related update work. */
+ if (reply->lease != NULL) {
+ /* Cached lifetimes */
+ reply->lease->prefer = reply->send_prefer;
+ reply->lease->valid = reply->send_valid;
+
+ /* Advance (or rewind) the valid lifetime. */
+ if (reply->buf.reply.msg_type == DHCPV6_REPLY) {
+ reply->lease->soft_lifetime_end_time =
+ cur_time + reply->send_valid;
+ /* Wait before renew! */
+ }
+
+ status = ia_add_iasubopt(reply->ia, reply->lease, MDL);
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("reply_process_is_addressed: Unable to "
+ "attach lease to new IA: %s",
+ isc_result_totext(status));
+ }
+
+ /*
+ * If this is a new lease, make sure it is attached somewhere.
+ */
+ if (reply->lease->ia == NULL) {
+ ia_reference(&reply->lease->ia, reply->ia, MDL);
+ }
+ }
+
+ /* Bring a copy of the relevant options into the IA scope. */
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options, reply->reply_ia,
+ scope, group, root_group);
+
+ /*
+ * And bring in host record configuration, if any, but not to overlap
+ * the previous group or its common enclosers.
+ */
+ if (reply->host != NULL)
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->reply_ia, scope,
+ reply->host->group, group);
+
+ cleanup:
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+
+ if (status == ISC_R_SUCCESS)
+ reply->client_resources++;
+
+ return status;
+}
+
+/* Simply send an IAADDR within the IA scope as described. */
+static isc_result_t
+reply_process_send_addr(struct reply_state *reply, struct iaddr *addr) {
+ isc_result_t status = ISC_R_SUCCESS;
+ struct data_string data;
+
+ memset(&data, 0, sizeof(data));
+
+ /* Now append the lease. */
+ data.len = IAADDR_OFFSET;
+ if (!buffer_allocate(&data.buffer, data.len, MDL)) {
+ log_error("reply_process_send_addr: out of memory"
+ "allocating new IAADDR buffer.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ data.data = data.buffer->data;
+
+ memcpy(data.buffer->data, addr->iabuf, 16);
+ putULong(data.buffer->data + 16, reply->send_prefer);
+ putULong(data.buffer->data + 20, reply->send_valid);
+
+ if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia,
+ data.buffer, data.buffer->data,
+ data.len, D6O_IAADDR, 0)) {
+ log_error("reply_process_send_addr: unable "
+ "to save IAADDR option");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->resources_included = ISC_TRUE;
+
+ cleanup:
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+
+ return status;
+}
+
+/* Choose the better of two leases. */
+static struct iasubopt *
+lease_compare(struct iasubopt *alpha, struct iasubopt *beta) {
+ if (alpha == NULL)
+ return beta;
+ if (beta == NULL)
+ return alpha;
+
+ switch(alpha->state) {
+ case FTS_ACTIVE:
+ switch(beta->state) {
+ case FTS_ACTIVE:
+ /* Choose the lease with the longest lifetime (most
+ * likely the most recently allocated).
+ */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return beta;
+ else
+ return alpha;
+
+ case FTS_EXPIRED:
+ case FTS_ABANDONED:
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ case FTS_EXPIRED:
+ switch (beta->state) {
+ case FTS_ACTIVE:
+ return beta;
+
+ case FTS_EXPIRED:
+ /* Choose the most recently expired lease. */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return beta;
+ else if ((alpha->hard_lifetime_end_time ==
+ beta->hard_lifetime_end_time) &&
+ (alpha->soft_lifetime_end_time <
+ beta->soft_lifetime_end_time))
+ return beta;
+ else
+ return alpha;
+
+ case FTS_ABANDONED:
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ case FTS_ABANDONED:
+ switch (beta->state) {
+ case FTS_ACTIVE:
+ case FTS_EXPIRED:
+ return alpha;
+
+ case FTS_ABANDONED:
+ /* Choose the lease that was abandoned longest ago. */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ log_fatal("Triple impossible condition at %s:%d.", MDL);
+ return NULL;
+}
+
+/* Process a client-supplied IA_PD. This may append options to the tail of
+ * the reply packet being built in the reply_state structure.
+ */
+static isc_result_t
+reply_process_ia_pd(struct reply_state *reply, struct option_cache *ia) {
+ isc_result_t status = ISC_R_SUCCESS;
+ u_int32_t iaid;
+ unsigned ia_cursor;
+ struct option_state *packet_ia;
+ struct option_cache *oc;
+ struct data_string ia_data, data;
+
+ /* Initialize values that will get cleaned up on return. */
+ packet_ia = NULL;
+ memset(&ia_data, 0, sizeof(ia_data));
+ memset(&data, 0, sizeof(data));
+ /*
+ * Note that find_client_prefix() may set reply->lease.
+ */
+
+ /* Make sure there is at least room for the header. */
+ if ((reply->cursor + IA_PD_OFFSET + 4) > sizeof(reply->buf)) {
+ log_error("reply_process_ia_pd: Reply too long for IA.");
+ return ISC_R_NOSPACE;
+ }
+
+
+ /* Fetch the IA_PD contents. */
+ if (!get_encapsulated_IA_state(&packet_ia, &ia_data, reply->packet,
+ ia, IA_PD_OFFSET)) {
+ log_error("reply_process_ia_pd: error evaluating ia");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Extract IA_PD header contents. */
+ iaid = getULong(ia_data.data);
+ reply->renew = getULong(ia_data.data + 4);
+ reply->rebind = getULong(ia_data.data + 8);
+
+ /* Create an IA_PD structure. */
+ if (ia_allocate(&reply->ia, iaid, (char *)reply->client_id.data,
+ reply->client_id.len, MDL) != ISC_R_SUCCESS) {
+ log_error("reply_process_ia_pd: no memory for ia.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ reply->ia->ia_type = D6O_IA_PD;
+
+ /* Cache pre-existing IA_PD, if any. */
+ ia_hash_lookup(&reply->old_ia, ia_pd_active,
+ (unsigned char *)reply->ia->iaid_duid.data,
+ reply->ia->iaid_duid.len, MDL);
+
+ /*
+ * Create an option cache to carry the IA_PD option contents, and
+ * execute any user-supplied values into it.
+ */
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /* Check & count the fixed prefix host records. */
+ reply->static_prefixes = 0;
+ if ((reply->host != NULL) && (reply->host->fixed_prefix != NULL)) {
+ struct iaddrcidrnetlist *fp;
+
+ for (fp = reply->host->fixed_prefix; fp != NULL;
+ fp = fp->next) {
+ reply->static_prefixes += 1;
+ }
+ }
+
+ /*
+ * Save the cursor position at the start of the IA_PD, so we can
+ * set length and adjust t1/t2 values later. We write a temporary
+ * header out now just in case we decide to adjust the packet
+ * within sub-process functions.
+ */
+ ia_cursor = reply->cursor;
+
+ /* Initialize the IA_PD header. First the code. */
+ putUShort(reply->buf.data + reply->cursor, (unsigned)D6O_IA_PD);
+ reply->cursor += 2;
+
+ /* Then option length. */
+ putUShort(reply->buf.data + reply->cursor, 0x0Cu);
+ reply->cursor += 2;
+
+ /* Then IA_PD header contents; IAID. */
+ putULong(reply->buf.data + reply->cursor, iaid);
+ reply->cursor += 4;
+
+ /* We store the client's t1 for now, and may over-ride it later. */
+ putULong(reply->buf.data + reply->cursor, reply->renew);
+ reply->cursor += 4;
+
+ /* We store the client's t2 for now, and may over-ride it later. */
+ putULong(reply->buf.data + reply->cursor, reply->rebind);
+ reply->cursor += 4;
+
+ /*
+ * For each prefix in this IA_PD, decide what to do about it.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet_ia, D6O_IAPREFIX);
+ reply->valid = reply->prefer = 0xffffffff;
+ reply->client_valid = reply->client_prefer = 0;
+ reply->preflen = -1;
+ for (; oc != NULL ; oc = oc->next) {
+ status = reply_process_prefix(reply, oc);
+
+ /*
+ * Canceled means we did not allocate prefixes to the
+ * client, but we're "done" with this IA - we set a status
+ * code. So transmit this reply, e.g., move on to the next
+ * IA.
+ */
+ if (status == ISC_R_CANCELED)
+ break;
+
+ if ((status != ISC_R_SUCCESS) && (status != ISC_R_ADDRINUSE))
+ goto cleanup;
+ }
+
+ reply->pd_count++;
+
+ /*
+ * If we fell through the above and never gave the client
+ * a prefix, give it one now.
+ */
+ if ((status != ISC_R_CANCELED) && (reply->client_resources == 0)) {
+ status = find_client_prefix(reply);
+
+ if (status == ISC_R_NORESOURCES) {
+ switch (reply->packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ /*
+ * No prefix for any IA is handled
+ * by the caller.
+ */
+ /* FALL THROUGH */
+
+ case DHCPV6_REQUEST:
+ /* Same than for addresses. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia,
+ MDL))
+ {
+ log_error("reply_process_ia_pd: No "
+ "memory for option state "
+ "wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ if (!set_status_code(STATUS_NoPrefixAvail,
+ "No prefixes available "
+ "for this interface.",
+ reply->reply_ia)) {
+ log_error("reply_process_ia_pd: "
+ "Unable to set "
+ "NoPrefixAvail status "
+ "code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ status = ISC_R_SUCCESS;
+ break;
+
+ default:
+ if (reply->resources_included)
+ status = ISC_R_SUCCESS;
+ else
+ goto cleanup;
+ break;
+ }
+ }
+
+ if (status != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ reply->cursor += store_options6((char *)reply->buf.data + reply->cursor,
+ sizeof(reply->buf) - reply->cursor,
+ reply->reply_ia, reply->packet,
+ required_opts_IA_PD, NULL);
+
+ /* Reset the length of this IA_PD to match what was just written. */
+ putUShort(reply->buf.data + ia_cursor + 2,
+ reply->cursor - (ia_cursor + 4));
+
+ /*
+ * T1/T2 time selection is kind of weird. We actually use DHCP
+ * (v4) scoped options as handy existing places where these might
+ * be configured by an administrator. A value of zero tells the
+ * client it may choose its own renewal time.
+ */
+ reply->renew = 0;
+ oc = lookup_option(&dhcp_universe, reply->opt_state,
+ DHO_DHCP_RENEWAL_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, &global_scope,
+ oc, MDL) ||
+ (data.len != 4)) {
+ log_error("Invalid renewal time.");
+ } else {
+ reply->renew = getULong(data.data);
+ }
+
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ }
+ putULong(reply->buf.data + ia_cursor + 8, reply->renew);
+
+ /* Now T2. */
+ reply->rebind = 0;
+ oc = lookup_option(&dhcp_universe, reply->opt_state,
+ DHO_DHCP_REBINDING_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, &global_scope,
+ oc, MDL) ||
+ (data.len != 4)) {
+ log_error("Invalid rebinding time.");
+ } else {
+ reply->rebind = getULong(data.data);
+ }
+
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ }
+ putULong(reply->buf.data + ia_cursor + 12, reply->rebind);
+
+ /*
+ * If this is not a 'soft' binding, consume the new changes into
+ * the database (if any have been attached to the ia_pd).
+ *
+ * Loop through the assigned dynamic prefixes, referencing the
+ * prefixes onto this IA_PD rather than any old ones, and updating
+ * prefix pool timers for each (if any).
+ */
+ if ((status != ISC_R_CANCELED) && (reply->static_prefixes == 0) &&
+ (reply->buf.reply.msg_type == DHCPV6_REPLY) &&
+ (reply->ia->num_iasubopt != 0)) {
+ struct iasubopt *tmp;
+ struct data_string *ia_id;
+ int i;
+
+ for (i = 0 ; i < reply->ia->num_iasubopt ; i++) {
+ tmp = reply->ia->iasubopt[i];
+
+ if (tmp->ia != NULL)
+ ia_dereference(&tmp->ia, MDL);
+ ia_reference(&tmp->ia, reply->ia, MDL);
+
+ /* Commit 'hard' bindings. */
+ tmp->hard_lifetime_end_time =
+ tmp->soft_lifetime_end_time;
+ tmp->soft_lifetime_end_time = 0;
+ renew_lease6(tmp->ipv6_pool, tmp);
+ schedule_lease_timeout(tmp->ipv6_pool);
+ }
+
+ /* Remove any old ia from the hash. */
+ if (reply->old_ia != NULL) {
+ ia_id = &reply->old_ia->iaid_duid;
+ ia_hash_delete(ia_pd_active,
+ (unsigned char *)ia_id->data,
+ ia_id->len, MDL);
+ ia_dereference(&reply->old_ia, MDL);
+ }
+
+ /* Put new ia into the hash. */
+ reply->ia->cltt = cur_time;
+ ia_id = &reply->ia->iaid_duid;
+ ia_hash_add(ia_pd_active, (unsigned char *)ia_id->data,
+ ia_id->len, reply->ia, MDL);
+
+ write_ia(reply->ia);
+ }
+
+ cleanup:
+ if (packet_ia != NULL)
+ option_state_dereference(&packet_ia, MDL);
+ if (reply->reply_ia != NULL)
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (ia_data.data != NULL)
+ data_string_forget(&ia_data, MDL);
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (reply->ia != NULL)
+ ia_dereference(&reply->ia, MDL);
+ if (reply->old_ia != NULL)
+ ia_dereference(&reply->old_ia, MDL);
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+
+ /*
+ * ISC_R_CANCELED is a status code used by the prefix processing to
+ * indicate we're replying with a status code. This is still a
+ * success at higher layers.
+ */
+ return((status == ISC_R_CANCELED) ? ISC_R_SUCCESS : status);
+}
+
+/*
+ * Process an IAPREFIX within a given IA_PD, storing any IAPREFIX reply
+ * contents into the reply's current ia_pd-scoped option cache. Returns
+ * ISC_R_CANCELED in the event we are replying with a status code and do
+ * not wish to process more IAPREFIXes within this IA_PD.
+ */
+static isc_result_t
+reply_process_prefix(struct reply_state *reply, struct option_cache *pref) {
+ u_int32_t pref_life, valid_life;
+ struct binding_scope **scope;
+ struct iaddrcidrnet tmp_pref;
+ struct option_cache *oc;
+ struct data_string iapref, data;
+ isc_result_t status = ISC_R_SUCCESS;
+
+ /* Initializes values that will be cleaned up. */
+ memset(&iapref, 0, sizeof(iapref));
+ memset(&data, 0, sizeof(data));
+ /* Note that reply->lease may be set by prefix_is_owned() */
+
+ /*
+ * There is no point trying to process an incoming prefix if there
+ * is no room for an outgoing prefix.
+ */
+ if ((reply->cursor + 29) > sizeof(reply->buf)) {
+ log_error("reply_process_prefix: Out of room for prefix.");
+ return ISC_R_NOSPACE;
+ }
+
+ /* Extract this IAPREFIX option. */
+ if (!evaluate_option_cache(&iapref, reply->packet, NULL, NULL,
+ reply->packet->options, NULL, &global_scope,
+ pref, MDL) ||
+ (iapref.len < IAPREFIX_OFFSET)) {
+ log_error("reply_process_prefix: error evaluating IAPREFIX.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * Layout: preferred and valid lifetimes followed by the prefix
+ * length and the IPv6 address.
+ */
+ pref_life = getULong(iapref.data);
+ valid_life = getULong(iapref.data + 4);
+
+ if ((reply->client_valid == 0) ||
+ (reply->client_valid > valid_life))
+ reply->client_valid = valid_life;
+
+ if ((reply->client_prefer == 0) ||
+ (reply->client_prefer > pref_life))
+ reply->client_prefer = pref_life;
+
+ /*
+ * Clients may choose to send ::/0 as a prefix, with the idea to give
+ * hints about preferred-lifetime or valid-lifetime.
+ */
+ tmp_pref.lo_addr.len = 16;
+ memset(tmp_pref.lo_addr.iabuf, 0, 16);
+ if ((iapref.data[8] == 0) &&
+ (memcmp(iapref.data + 9, tmp_pref.lo_addr.iabuf, 16) == 0)) {
+ /* Status remains success; we just ignore this one. */
+ goto cleanup;
+ }
+
+ /*
+ * Clients may choose to send ::/X as a prefix to specify a
+ * preferred/requested prefix length. Note X is never zero here.
+ */
+ tmp_pref.bits = (int) iapref.data[8];
+ if (reply->preflen < 0) {
+ /* Cache the first preferred prefix length. */
+ reply->preflen = tmp_pref.bits;
+ }
+ if (memcmp(iapref.data + 9, tmp_pref.lo_addr.iabuf, 16) == 0) {
+ goto cleanup;
+ }
+
+ memcpy(tmp_pref.lo_addr.iabuf, iapref.data + 9, 16);
+
+ /* Verify the prefix belongs to the client. */
+ if (!prefix_is_owned(reply, &tmp_pref)) {
+ /* Same than for addresses. */
+ if ((reply->packet->dhcpv6_msg_type == DHCPV6_SOLICIT) ||
+ (reply->packet->dhcpv6_msg_type == DHCPV6_REQUEST) ||
+ (reply->packet->dhcpv6_msg_type == DHCPV6_REBIND)) {
+ status = reply_process_try_prefix(reply, &tmp_pref);
+
+ /* Either error out or skip this prefix. */
+ if ((status != ISC_R_SUCCESS) &&
+ (status != ISC_R_ADDRINUSE))
+ goto cleanup;
+
+ if (reply->lease == NULL) {
+ if (reply->packet->dhcpv6_msg_type ==
+ DHCPV6_REBIND) {
+ reply->send_prefer = 0;
+ reply->send_valid = 0;
+ goto send_pref;
+ }
+
+ /* status remains success - ignore */
+ goto cleanup;
+ }
+ /*
+ * RFC3633 section 18.2.3:
+ *
+ * If the delegating router cannot find a binding
+ * for the requesting router's IA_PD the delegating
+ * router returns the IA_PD containing no prefixes
+ * with a Status Code option set to NoBinding in the
+ * Reply message.
+ *
+ * On mismatch we (ab)use this pretending we have not the IA
+ * as soon as we have not a prefix.
+ */
+ } else if (reply->packet->dhcpv6_msg_type == DHCPV6_RENEW) {
+ /* Rewind the IA_PD to empty. */
+ option_state_dereference(&reply->reply_ia, MDL);
+ if (!option_state_allocate(&reply->reply_ia, MDL)) {
+ log_error("reply_process_prefix: No memory "
+ "for option state wipe.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ /* Append a NoBinding status code. */
+ if (!set_status_code(STATUS_NoBinding,
+ "Prefix not bound to this "
+ "interface.", reply->reply_ia)) {
+ log_error("reply_process_prefix: Unable to "
+ "attach status code.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Fin (no more IAPREFIXes). */
+ status = ISC_R_CANCELED;
+ goto cleanup;
+ } else {
+ log_error("It is impossible to lease a client that is "
+ "not sending a solicit, request, renew, or "
+ "rebind message.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ if (reply->static_prefixes > 0) {
+ if (reply->host == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ scope = &global_scope;
+ } else {
+ if (reply->lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ scope = &reply->lease->scope;
+ }
+
+ /*
+ * If client_resources is nonzero, then the reply_process_is_prefixed
+ * function has executed configuration state into the reply option
+ * cache. We will use that valid cache to derive configuration for
+ * whether or not to engage in additional prefixes, and similar.
+ */
+ if (reply->client_resources != 0) {
+ unsigned limit = 1;
+
+ /*
+ * Does this client have "enough" prefixes already? Default
+ * to one. Everybody gets one, and one should be enough for
+ * anybody.
+ */
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_LIMIT_PREFS_PER_IA);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet,
+ NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_prefix: unable to "
+ "evaluate prefs-per-ia value.");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ limit = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ /*
+ * If we wish to limit the client to a certain number of
+ * prefixes, then omit the prefix from the reply.
+ */
+ if (reply->client_resources >= limit)
+ goto cleanup;
+ }
+
+ status = reply_process_is_prefixed(reply, scope, reply->shared->group);
+ if (status != ISC_R_SUCCESS)
+ goto cleanup;
+
+ send_pref:
+ status = reply_process_send_prefix(reply, &tmp_pref);
+
+ cleanup:
+ if (iapref.data != NULL)
+ data_string_forget(&iapref, MDL);
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+ if (reply->lease != NULL)
+ iasubopt_dereference(&reply->lease, MDL);
+
+ return status;
+}
+
+/*
+ * Verify the prefix belongs to the client. If we've got a host
+ * record with fixed prefixes, it has to be an assigned prefix
+ * (fault out all else). Otherwise it's a dynamic prefix, so lookup
+ * that prefix and make sure it belongs to this DUID:IAID pair.
+ */
+static isc_boolean_t
+prefix_is_owned(struct reply_state *reply, struct iaddrcidrnet *pref) {
+ struct iaddrcidrnetlist *l;
+ int i;
+
+ /*
+ * This faults out prefixes that don't match fixed prefixes.
+ */
+ if (reply->static_prefixes > 0) {
+ for (l = reply->host->fixed_prefix; l != NULL; l = l->next) {
+ if ((pref->bits == l->cidrnet.bits) &&
+ (memcmp(pref->lo_addr.iabuf,
+ l->cidrnet.lo_addr.iabuf, 16) == 0))
+ return ISC_TRUE;
+ }
+ return ISC_FALSE;
+ }
+
+ if ((reply->old_ia == NULL) ||
+ (reply->old_ia->num_iasubopt == 0))
+ return ISC_FALSE;
+
+ for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
+ struct iasubopt *tmp;
+
+ tmp = reply->old_ia->iasubopt[i];
+
+ if ((pref->bits == (int) tmp->plen) &&
+ memcmp(pref->lo_addr.iabuf, &tmp->addr, 16) == 0) {
+ iasubopt_reference(&reply->lease, tmp, MDL);
+ return ISC_TRUE;
+ }
+ }
+
+ return ISC_FALSE;
+}
+
+/*
+ * This function only returns failure on 'hard' failures. If it succeeds,
+ * it will leave a prefix structure behind.
+ */
+static isc_result_t
+reply_process_try_prefix(struct reply_state *reply,
+ struct iaddrcidrnet *pref) {
+ isc_result_t status = ISC_R_NORESOURCES;
+ struct ipv6_pool *pool;
+ int i;
+ struct data_string data_pref;
+
+ if ((reply == NULL) || (reply->shared == NULL) ||
+ (reply->shared->ipv6_pools == NULL) || (pref == NULL) ||
+ (reply->lease != NULL))
+ return DHCP_R_INVALIDARG;
+
+ memset(&data_pref, 0, sizeof(data_pref));
+ data_pref.len = 17;
+ if (!buffer_allocate(&data_pref.buffer, data_pref.len, MDL)) {
+ log_error("reply_process_try_prefix: out of memory.");
+ return ISC_R_NOMEMORY;
+ }
+ data_pref.data = data_pref.buffer->data;
+ data_pref.buffer->data[0] = (u_int8_t) pref->bits;
+ memcpy(data_pref.buffer->data + 1, pref->lo_addr.iabuf, 16);
+
+ for (i = 0 ; (pool = reply->shared->ipv6_pools[i]) != NULL ; i++) {
+ if (pool->pool_type != D6O_IA_PD)
+ continue;
+ status = try_client_v6_prefix(&reply->lease, pool,
+ &data_pref);
+ /* If we found it in this pool (either in use or available),
+ there is no need to look further. */
+ if ( (status == ISC_R_SUCCESS) || (status == ISC_R_ADDRINUSE) )
+ break;
+ }
+
+ data_string_forget(&data_pref, MDL);
+ /* Return just the most recent status... */
+ return status;
+}
+
+/* Look around for a prefix to give the client. First, look through the old
+ * IA_PD for prefixes we can extend. Second, try to allocate a new prefix.
+ * Finally, actually add that prefix into the current reply IA_PD.
+ */
+static isc_result_t
+find_client_prefix(struct reply_state *reply) {
+ struct iaddrcidrnet send_pref;
+ isc_result_t status = ISC_R_NORESOURCES;
+ struct iasubopt *prefix, *best_prefix = NULL;
+ struct binding_scope **scope;
+ int i;
+
+ if (reply->static_prefixes > 0) {
+ struct iaddrcidrnetlist *l;
+
+ if (reply->host == NULL)
+ return DHCP_R_INVALIDARG;
+
+ for (l = reply->host->fixed_prefix; l != NULL; l = l->next) {
+ if (l->cidrnet.bits == reply->preflen)
+ break;
+ }
+ if (l == NULL) {
+ /*
+ * If no fixed prefix has the preferred length,
+ * get the first one.
+ */
+ l = reply->host->fixed_prefix;
+ }
+ memcpy(&send_pref, &l->cidrnet, sizeof(send_pref));
+
+ status = ISC_R_SUCCESS;
+ scope = &global_scope;
+ goto send_pref;
+ }
+
+ if (reply->old_ia != NULL) {
+ for (i = 0 ; i < reply->old_ia->num_iasubopt ; i++) {
+ struct shared_network *candidate_shared;
+
+ prefix = reply->old_ia->iasubopt[i];
+ candidate_shared = prefix->ipv6_pool->shared_network;
+
+ /*
+ * Consider this prefix if it is in a global pool or
+ * if it is scoped in a pool under the client's shared
+ * network.
+ */
+ if (candidate_shared == NULL ||
+ candidate_shared == reply->shared) {
+ best_prefix = prefix_compare(reply, prefix,
+ best_prefix);
+ }
+ }
+ }
+
+ /* Try to pick a new prefix if we didn't find one, or if we found an
+ * abandoned prefix.
+ */
+ if ((best_prefix == NULL) || (best_prefix->state == FTS_ABANDONED)) {
+ status = pick_v6_prefix(&reply->lease, reply->preflen,
+ reply->shared, &reply->client_id);
+ } else if (best_prefix != NULL) {
+ iasubopt_reference(&reply->lease, best_prefix, MDL);
+ status = ISC_R_SUCCESS;
+ }
+
+ /* Pick the abandoned prefix as a last resort. */
+ if ((status == ISC_R_NORESOURCES) && (best_prefix != NULL)) {
+ /* I don't see how this is supposed to be done right now. */
+ log_error("Reclaiming abandoned prefixes is not yet "
+ "supported. Treating this as an out of space "
+ "condition.");
+ /* iasubopt_reference(&reply->lease, best_prefix, MDL); */
+ }
+
+ /* Give up now if we didn't find a prefix. */
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (reply->lease == NULL)
+ log_fatal("Impossible condition at %s:%d.", MDL);
+
+ scope = &reply->lease->scope;
+
+ send_pref.lo_addr.len = 16;
+ memcpy(send_pref.lo_addr.iabuf, &reply->lease->addr, 16);
+ send_pref.bits = (int) reply->lease->plen;
+
+ send_pref:
+ status = reply_process_is_prefixed(reply, scope, reply->shared->group);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = reply_process_send_prefix(reply, &send_pref);
+ return status;
+}
+
+/* Once a prefix is found for a client, perform several common functions;
+ * Calculate and store valid and preferred prefix times, draw client options
+ * into the option state.
+ */
+static isc_result_t
+reply_process_is_prefixed(struct reply_state *reply,
+ struct binding_scope **scope, struct group *group)
+{
+ isc_result_t status = ISC_R_SUCCESS;
+ struct data_string data;
+ struct option_cache *oc;
+
+ /* Initialize values we will cleanup. */
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Bring configured options into the root packet level cache - start
+ * with the lease's closest enclosing group (passed in by the caller
+ * as 'group').
+ */
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options, reply->opt_state,
+ scope, group, root_group);
+
+ /*
+ * If there is a host record, over-ride with values configured there,
+ * without re-evaluating configuration from the previously executed
+ * group or its common enclosers.
+ */
+ if (reply->host != NULL)
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state, scope,
+ reply->host->group, group);
+
+ /* Determine valid lifetime. */
+ if (reply->client_valid == 0)
+ reply->send_valid = DEFAULT_DEFAULT_LEASE_TIME;
+ else
+ reply->send_valid = reply->client_valid;
+
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_DEFAULT_LEASE_TIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_is_prefixed: unable to "
+ "evaluate default prefix time");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->send_valid = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ if (reply->client_prefer == 0)
+ reply->send_prefer = reply->send_valid;
+ else
+ reply->send_prefer = reply->client_prefer;
+
+ if (reply->send_prefer >= reply->send_valid)
+ reply->send_prefer = (reply->send_valid / 2) +
+ (reply->send_valid / 8);
+
+ oc = lookup_option(&server_universe, reply->opt_state,
+ SV_PREFER_LIFETIME);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&data, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->opt_state,
+ scope, oc, MDL) ||
+ (data.len != 4)) {
+ log_error("reply_process_is_prefixed: unable to "
+ "evaluate preferred prefix time");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->send_prefer = getULong(data.data);
+ data_string_forget(&data, MDL);
+ }
+
+ /* Note lowest values for later calculation of renew/rebind times. */
+ if (reply->prefer > reply->send_prefer)
+ reply->prefer = reply->send_prefer;
+
+ if (reply->valid > reply->send_valid)
+ reply->valid = reply->send_valid;
+
+ /* Perform dynamic prefix related update work. */
+ if (reply->lease != NULL) {
+ /* Cached lifetimes */
+ reply->lease->prefer = reply->send_prefer;
+ reply->lease->valid = reply->send_valid;
+
+ /* Advance (or rewind) the valid lifetime. */
+ if (reply->buf.reply.msg_type == DHCPV6_REPLY) {
+ reply->lease->soft_lifetime_end_time =
+ cur_time + reply->send_valid;
+ /* Wait before renew! */
+ }
+
+ status = ia_add_iasubopt(reply->ia, reply->lease, MDL);
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("reply_process_is_prefixed: Unable to "
+ "attach prefix to new IA_PD: %s",
+ isc_result_totext(status));
+ }
+
+ /*
+ * If this is a new prefix, make sure it is attached somewhere.
+ */
+ if (reply->lease->ia == NULL) {
+ ia_reference(&reply->lease->ia, reply->ia, MDL);
+ }
+ }
+
+ /* Bring a copy of the relevant options into the IA_PD scope. */
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options, reply->reply_ia,
+ scope, group, root_group);
+
+ /*
+ * And bring in host record configuration, if any, but not to overlap
+ * the previous group or its common enclosers.
+ */
+ if (reply->host != NULL)
+ execute_statements_in_scope(NULL, reply->packet, NULL, NULL,
+ reply->packet->options,
+ reply->reply_ia, scope,
+ reply->host->group, group);
+
+ cleanup:
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+
+ if (status == ISC_R_SUCCESS)
+ reply->client_resources++;
+
+ return status;
+}
+
+/* Simply send an IAPREFIX within the IA_PD scope as described. */
+static isc_result_t
+reply_process_send_prefix(struct reply_state *reply,
+ struct iaddrcidrnet *pref) {
+ isc_result_t status = ISC_R_SUCCESS;
+ struct data_string data;
+
+ memset(&data, 0, sizeof(data));
+
+ /* Now append the prefix. */
+ data.len = IAPREFIX_OFFSET;
+ if (!buffer_allocate(&data.buffer, data.len, MDL)) {
+ log_error("reply_process_send_prefix: out of memory"
+ "allocating new IAPREFIX buffer.");
+ status = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ data.data = data.buffer->data;
+
+ putULong(data.buffer->data, reply->send_prefer);
+ putULong(data.buffer->data + 4, reply->send_valid);
+ data.buffer->data[8] = pref->bits;
+ memcpy(data.buffer->data + 9, pref->lo_addr.iabuf, 16);
+
+ if (!append_option_buffer(&dhcpv6_universe, reply->reply_ia,
+ data.buffer, data.buffer->data,
+ data.len, D6O_IAPREFIX, 0)) {
+ log_error("reply_process_send_prefix: unable "
+ "to save IAPREFIX option");
+ status = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ reply->resources_included = ISC_TRUE;
+
+ cleanup:
+ if (data.data != NULL)
+ data_string_forget(&data, MDL);
+
+ return status;
+}
+
+/* Choose the better of two prefixes. */
+static struct iasubopt *
+prefix_compare(struct reply_state *reply,
+ struct iasubopt *alpha, struct iasubopt *beta) {
+ if (alpha == NULL)
+ return beta;
+ if (beta == NULL)
+ return alpha;
+
+ if (reply->preflen >= 0) {
+ if ((alpha->plen == reply->preflen) &&
+ (beta->plen != reply->preflen))
+ return alpha;
+ if ((beta->plen == reply->preflen) &&
+ (alpha->plen != reply->preflen))
+ return beta;
+ }
+
+ switch(alpha->state) {
+ case FTS_ACTIVE:
+ switch(beta->state) {
+ case FTS_ACTIVE:
+ /* Choose the prefix with the longest lifetime (most
+ * likely the most recently allocated).
+ */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return beta;
+ else
+ return alpha;
+
+ case FTS_EXPIRED:
+ case FTS_ABANDONED:
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ case FTS_EXPIRED:
+ switch (beta->state) {
+ case FTS_ACTIVE:
+ return beta;
+
+ case FTS_EXPIRED:
+ /* Choose the most recently expired prefix. */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return beta;
+ else if ((alpha->hard_lifetime_end_time ==
+ beta->hard_lifetime_end_time) &&
+ (alpha->soft_lifetime_end_time <
+ beta->soft_lifetime_end_time))
+ return beta;
+ else
+ return alpha;
+
+ case FTS_ABANDONED:
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ case FTS_ABANDONED:
+ switch (beta->state) {
+ case FTS_ACTIVE:
+ case FTS_EXPIRED:
+ return alpha;
+
+ case FTS_ABANDONED:
+ /* Choose the prefix that was abandoned longest ago. */
+ if (alpha->hard_lifetime_end_time <
+ beta->hard_lifetime_end_time)
+ return alpha;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ log_fatal("Triple impossible condition at %s:%d.", MDL);
+ return NULL;
+}
+
+/*
+ * Solicit is how a client starts requesting addresses.
+ *
+ * If the client asks for rapid commit, and we support it, we will
+ * allocate the addresses and reply.
+ *
+ * Otherwise we will send an advertise message.
+ */
+
+static void
+dhcpv6_solicit(struct data_string *reply_ret, struct packet *packet) {
+ struct data_string client_id;
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_client_msg(packet, &client_id)) {
+ return;
+ }
+
+ lease_to_client(reply_ret, packet, &client_id, NULL);
+
+ /*
+ * Clean up.
+ */
+ data_string_forget(&client_id, MDL);
+}
+
+/*
+ * Request is how a client actually requests addresses.
+ *
+ * Very similar to Solicit handling, except the server DUID is required.
+ */
+
+/* TODO: reject unicast messages, unless we set unicast option */
+static void
+dhcpv6_request(struct data_string *reply_ret, struct packet *packet) {
+ struct data_string client_id;
+ struct data_string server_id;
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_client_resp(packet, &client_id, &server_id)) {
+ return;
+ }
+
+ /*
+ * Issue our lease.
+ */
+ lease_to_client(reply_ret, packet, &client_id, &server_id);
+
+ /*
+ * Cleanup.
+ */
+ data_string_forget(&client_id, MDL);
+ data_string_forget(&server_id, MDL);
+}
+
+/* Find a DHCPv6 packet's shared network from hints in the packet.
+ */
+static isc_result_t
+shared_network_from_packet6(struct shared_network **shared,
+ struct packet *packet)
+{
+ const struct packet *chk_packet;
+ const struct in6_addr *link_addr, *first_link_addr;
+ struct iaddr tmp_addr;
+ struct subnet *subnet;
+ isc_result_t status;
+
+ if ((shared == NULL) || (*shared != NULL) || (packet == NULL))
+ return DHCP_R_INVALIDARG;
+
+ /*
+ * First, find the link address where the packet from the client
+ * first appeared (if this packet was relayed).
+ */
+ first_link_addr = NULL;
+ chk_packet = packet->dhcpv6_container_packet;
+ while (chk_packet != NULL) {
+ link_addr = &chk_packet->dhcpv6_link_address;
+ if (!IN6_IS_ADDR_UNSPECIFIED(link_addr) &&
+ !IN6_IS_ADDR_LINKLOCAL(link_addr)) {
+ first_link_addr = link_addr;
+ break;
+ }
+ chk_packet = chk_packet->dhcpv6_container_packet;
+ }
+
+ /*
+ * If there is a relayed link address, find the subnet associated
+ * with that, and use that to get the appropriate
+ * shared_network.
+ */
+ if (first_link_addr != NULL) {
+ tmp_addr.len = sizeof(*first_link_addr);
+ memcpy(tmp_addr.iabuf,
+ first_link_addr, sizeof(*first_link_addr));
+ subnet = NULL;
+ if (!find_subnet(&subnet, tmp_addr, MDL)) {
+ log_debug("No subnet found for link-address %s.",
+ piaddr(tmp_addr));
+ return ISC_R_NOTFOUND;
+ }
+ status = shared_network_reference(shared,
+ subnet->shared_network, MDL);
+ subnet_dereference(&subnet, MDL);
+
+ /*
+ * If there is no link address, we will use the interface
+ * that this packet came in on to pick the shared_network.
+ */
+ } else if (packet->interface != NULL) {
+ status = shared_network_reference(shared,
+ packet->interface->shared_network,
+ MDL);
+ if (packet->dhcpv6_container_packet != NULL) {
+ log_info("[L2 Relay] No link address in relay packet "
+ "assuming L2 relay and using receiving "
+ "interface");
+ }
+
+ } else {
+ /*
+ * We shouldn't be able to get here but if there is no link
+ * address and no interface we don't know where to get the
+ * pool from log an error and return an error.
+ */
+ log_error("No interface and no link address "
+ "can't determine pool");
+ status = DHCP_R_INVALIDARG;
+ }
+
+ return status;
+}
+
+/*
+ * When a client thinks it might be on a new link, it sends a
+ * Confirm message.
+ *
+ * From RFC3315 section 18.2.2:
+ *
+ * When the server receives a Confirm message, the server determines
+ * whether the addresses in the Confirm message are appropriate for the
+ * link to which the client is attached. If all of the addresses in the
+ * Confirm message pass this test, the server returns a status of
+ * Success. If any of the addresses do not pass this test, the server
+ * returns a status of NotOnLink. If the server is unable to perform
+ * this test (for example, the server does not have information about
+ * prefixes on the link to which the client is connected), or there were
+ * no addresses in any of the IAs sent by the client, the server MUST
+ * NOT send a reply to the client.
+ */
+
+static void
+dhcpv6_confirm(struct data_string *reply_ret, struct packet *packet) {
+ struct shared_network *shared;
+ struct subnet *subnet;
+ struct option_cache *ia, *ta, *oc;
+ struct data_string cli_enc_opt_data, iaaddr, client_id, packet_oro;
+ struct option_state *cli_enc_opt_state, *opt_state;
+ struct iaddr cli_addr;
+ int pass;
+ isc_boolean_t inappropriate, has_addrs;
+ char reply_data[65536];
+ struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data;
+ int reply_ofs = (int)(offsetof(struct dhcpv6_packet, options));
+
+ /*
+ * Basic client message validation.
+ */
+ memset(&client_id, 0, sizeof(client_id));
+ if (!valid_client_msg(packet, &client_id)) {
+ return;
+ }
+
+ /*
+ * Do not process Confirms that do not have IA's we do not recognize.
+ */
+ ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ ta = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_TA);
+ if ((ia == NULL) && (ta == NULL))
+ return;
+
+ /*
+ * IA_PD's are simply ignored.
+ */
+ delete_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+
+ /*
+ * Bit of variable initialization.
+ */
+ opt_state = cli_enc_opt_state = NULL;
+ memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data));
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ memset(&packet_oro, 0, sizeof(packet_oro));
+
+ /* Determine what shared network the client is connected to. We
+ * must not respond if we don't have any information about the
+ * network the client is on.
+ */
+ shared = NULL;
+ if ((shared_network_from_packet6(&shared, packet) != ISC_R_SUCCESS) ||
+ (shared == NULL))
+ goto exit;
+
+ /* If there are no recorded subnets, then we have no
+ * information about this subnet - ignore Confirms.
+ */
+ subnet = shared->subnets;
+ if (subnet == NULL)
+ goto exit;
+
+ /* Are the addresses in all the IA's appropriate for that link? */
+ has_addrs = inappropriate = ISC_FALSE;
+ pass = D6O_IA_NA;
+ while(!inappropriate) {
+ /* If we've reached the end of the IA_NA pass, move to the
+ * IA_TA pass.
+ */
+ if ((pass == D6O_IA_NA) && (ia == NULL)) {
+ pass = D6O_IA_TA;
+ ia = ta;
+ }
+
+ /* If we've reached the end of all passes, we're done. */
+ if (ia == NULL)
+ break;
+
+ if (((pass == D6O_IA_NA) &&
+ !get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia, IA_NA_OFFSET)) ||
+ ((pass == D6O_IA_TA) &&
+ !get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia, IA_TA_OFFSET))) {
+ goto exit;
+ }
+
+ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state,
+ D6O_IAADDR);
+
+ for ( ; oc != NULL ; oc = oc->next) {
+ if (!evaluate_option_cache(&iaaddr, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (iaaddr.len < IAADDR_OFFSET)) {
+ log_error("dhcpv6_confirm: "
+ "error evaluating IAADDR.");
+ goto exit;
+ }
+
+ /* Copy out the IPv6 address for processing. */
+ cli_addr.len = 16;
+ memcpy(cli_addr.iabuf, iaaddr.data, 16);
+
+ data_string_forget(&iaaddr, MDL);
+
+ /* Record that we've processed at least one address. */
+ has_addrs = ISC_TRUE;
+
+ /* Find out if any subnets cover this address. */
+ for (subnet = shared->subnets ; subnet != NULL ;
+ subnet = subnet->next_sibling) {
+ if (addr_eq(subnet_number(cli_addr,
+ subnet->netmask),
+ subnet->net))
+ break;
+ }
+
+ /* If we reach the end of the subnet list, and no
+ * subnet matches the client address, then it must
+ * be inappropriate to the link (so far as our
+ * configuration says). Once we've found one
+ * inappropriate address, there is no reason to
+ * continue searching.
+ */
+ if (subnet == NULL) {
+ inappropriate = ISC_TRUE;
+ break;
+ }
+ }
+
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+
+ /* Advance to the next IA_*. */
+ ia = ia->next;
+ }
+
+ /* If the client supplied no addresses, do not reply. */
+ if (!has_addrs)
+ goto exit;
+
+ /*
+ * Set up reply.
+ */
+ if (!start_reply(packet, &client_id, NULL, &opt_state, reply)) {
+ goto exit;
+ }
+
+ /*
+ * Set our status.
+ */
+ if (inappropriate) {
+ if (!set_status_code(STATUS_NotOnLink,
+ "Some of the addresses are not on link.",
+ opt_state)) {
+ goto exit;
+ }
+ } else {
+ if (!set_status_code(STATUS_Success,
+ "All addresses still on link.",
+ opt_state)) {
+ goto exit;
+ }
+ }
+
+ /*
+ * Only one option: add it.
+ */
+ reply_ofs += store_options6(reply_data+reply_ofs,
+ sizeof(reply_data)-reply_ofs,
+ opt_state, packet,
+ required_opts, &packet_oro);
+
+ /*
+ * Return our reply to the caller.
+ */
+ reply_ret->len = reply_ofs;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) {
+ log_fatal("No memory to store reply.");
+ }
+ reply_ret->data = reply_ret->buffer->data;
+ memcpy(reply_ret->buffer->data, reply, reply_ofs);
+
+exit:
+ /* Cleanup any stale data strings. */
+ if (cli_enc_opt_data.buffer != NULL)
+ data_string_forget(&cli_enc_opt_data, MDL);
+ if (iaaddr.buffer != NULL)
+ data_string_forget(&iaaddr, MDL);
+ if (client_id.buffer != NULL)
+ data_string_forget(&client_id, MDL);
+ if (packet_oro.buffer != NULL)
+ data_string_forget(&packet_oro, MDL);
+
+ /* Release any stale option states. */
+ if (cli_enc_opt_state != NULL)
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ if (opt_state != NULL)
+ option_state_dereference(&opt_state, MDL);
+}
+
+/*
+ * Renew is when a client wants to extend its lease/prefix, at time T1.
+ *
+ * We handle this the same as if the client wants a new lease/prefix,
+ * except for the error code of when addresses don't match.
+ */
+
+/* TODO: reject unicast messages, unless we set unicast option */
+static void
+dhcpv6_renew(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+ struct data_string server_id;
+
+ /*
+ * Validate the request.
+ */
+ if (!valid_client_resp(packet, &client_id, &server_id)) {
+ return;
+ }
+
+ /*
+ * Renew our lease.
+ */
+ lease_to_client(reply, packet, &client_id, &server_id);
+
+ /*
+ * Cleanup.
+ */
+ data_string_forget(&server_id, MDL);
+ data_string_forget(&client_id, MDL);
+}
+
+/*
+ * Rebind is when a client wants to extend its lease, at time T2.
+ *
+ * We handle this the same as if the client wants a new lease, except
+ * for the error code of when addresses don't match.
+ */
+
+static void
+dhcpv6_rebind(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+
+ if (!valid_client_msg(packet, &client_id)) {
+ return;
+ }
+
+ lease_to_client(reply, packet, &client_id, NULL);
+
+ data_string_forget(&client_id, MDL);
+}
+
+static void
+ia_na_match_decline(const struct data_string *client_id,
+ const struct data_string *iaaddr,
+ struct iasubopt *lease)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+
+ log_error("Client %s reports address %s is "
+ "already in use by another host!",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iaaddr->data,
+ tmp_addr, sizeof(tmp_addr)));
+ if (lease != NULL) {
+ decline_lease6(lease->ipv6_pool, lease);
+ lease->ia->cltt = cur_time;
+ write_ia(lease->ia);
+ }
+}
+
+static void
+ia_na_nomatch_decline(const struct data_string *client_id,
+ const struct data_string *iaaddr,
+ u_int32_t *ia_na_id,
+ struct packet *packet,
+ char *reply_data,
+ int *reply_ofs,
+ int reply_len)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+ struct option_state *host_opt_state;
+ int len;
+
+ log_info("Client %s declines address %s, which is not offered to it.",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr)));
+
+ /*
+ * Create state for this IA_NA.
+ */
+ host_opt_state = NULL;
+ if (!option_state_allocate(&host_opt_state, MDL)) {
+ log_error("ia_na_nomatch_decline: out of memory "
+ "allocating option_state.");
+ goto exit;
+ }
+
+ if (!set_status_code(STATUS_NoBinding, "Decline for unknown address.",
+ host_opt_state)) {
+ goto exit;
+ }
+
+ /*
+ * Insure we have enough space
+ */
+ if (reply_len < (*reply_ofs + 16)) {
+ log_error("ia_na_nomatch_decline: "
+ "out of space for reply packet.");
+ goto exit;
+ }
+
+ /*
+ * Put our status code into the reply packet.
+ */
+ len = store_options6(reply_data+(*reply_ofs)+16,
+ reply_len-(*reply_ofs)-16,
+ host_opt_state, packet,
+ required_opts_STATUS_CODE, NULL);
+
+ /*
+ * Store the non-encapsulated option data for this
+ * IA_NA into our reply packet. Defined in RFC 3315,
+ * section 22.4.
+ */
+ /* option number */
+ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_NA);
+ /* option length */
+ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12);
+ /* IA_NA, copied from the client */
+ memcpy(reply_data+(*reply_ofs)+4, ia_na_id, 4);
+ /* t1 and t2, odd that we need them, but here it is */
+ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0);
+ putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0);
+
+ /*
+ * Get ready for next IA_NA.
+ */
+ *reply_ofs += (len + 16);
+
+exit:
+ option_state_dereference(&host_opt_state, MDL);
+}
+
+static void
+iterate_over_ia_na(struct data_string *reply_ret,
+ struct packet *packet,
+ const struct data_string *client_id,
+ const struct data_string *server_id,
+ const char *packet_type,
+ void (*ia_na_match)(),
+ void (*ia_na_nomatch)())
+{
+ struct option_state *opt_state;
+ struct host_decl *packet_host;
+ struct option_cache *ia;
+ struct option_cache *oc;
+ /* cli_enc_... variables come from the IA_NA/IA_TA options */
+ struct data_string cli_enc_opt_data;
+ struct option_state *cli_enc_opt_state;
+ struct host_decl *host;
+ struct option_state *host_opt_state;
+ struct data_string iaaddr;
+ struct data_string fixed_addr;
+ int iaaddr_is_found;
+ char reply_data[65536];
+ struct dhcpv6_packet *reply = (struct dhcpv6_packet *)reply_data;
+ int reply_ofs = (int)(offsetof(struct dhcpv6_packet, options));
+ char status_msg[32];
+ struct iasubopt *lease;
+ struct ia_xx *existing_ia_na;
+ int i;
+ struct data_string key;
+ u_int32_t iaid;
+
+ /*
+ * Initialize to empty values, in case we have to exit early.
+ */
+ opt_state = NULL;
+ memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data));
+ cli_enc_opt_state = NULL;
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ memset(&fixed_addr, 0, sizeof(fixed_addr));
+ host_opt_state = NULL;
+ lease = NULL;
+
+ /*
+ * Find the host record that matches from the packet, if any.
+ */
+ packet_host = NULL;
+ if (!find_hosts_by_uid(&packet_host,
+ client_id->data, client_id->len, MDL)) {
+ packet_host = NULL;
+ /*
+ * Note: In general, we don't expect a client to provide
+ * enough information to match by option for these
+ * types of messages, but if we don't have a UID
+ * match we can check anyway.
+ */
+ if (!find_hosts_by_option(&packet_host,
+ packet, packet->options, MDL)) {
+ packet_host = NULL;
+
+ if (!find_hosts_by_duid_chaddr(&packet_host,
+ client_id))
+ packet_host = NULL;
+ }
+ }
+
+ /*
+ * Set our reply information.
+ */
+ reply->msg_type = DHCPV6_REPLY;
+ memcpy(reply->transaction_id, packet->dhcpv6_transaction_id,
+ sizeof(reply->transaction_id));
+
+ /*
+ * Build our option state for reply.
+ */
+ opt_state = NULL;
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("iterate_over_ia_na: no memory for option_state.");
+ goto exit;
+ }
+ execute_statements_in_scope(NULL, packet, NULL, NULL,
+ packet->options, opt_state,
+ &global_scope, root_group, NULL);
+
+ /*
+ * RFC 3315, section 18.2.7 tells us which options to include.
+ */
+ oc = lookup_option(&dhcpv6_universe, opt_state, D6O_SERVERID);
+ if (oc == NULL) {
+ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
+ (unsigned char *)server_duid.data,
+ server_duid.len, D6O_SERVERID, 0)) {
+ log_error("iterate_over_ia_na: "
+ "error saving server identifier.");
+ goto exit;
+ }
+ }
+
+ if (!save_option_buffer(&dhcpv6_universe, opt_state,
+ client_id->buffer,
+ (unsigned char *)client_id->data,
+ client_id->len,
+ D6O_CLIENTID, 0)) {
+ log_error("iterate_over_ia_na: "
+ "error saving client identifier.");
+ goto exit;
+ }
+
+ snprintf(status_msg, sizeof(status_msg), "%s received.", packet_type);
+ if (!set_status_code(STATUS_Success, status_msg, opt_state)) {
+ goto exit;
+ }
+
+ /*
+ * Add our options that are not associated with any IA_NA or IA_TA.
+ */
+ reply_ofs += store_options6(reply_data+reply_ofs,
+ sizeof(reply_data)-reply_ofs,
+ opt_state, packet,
+ required_opts, NULL);
+
+ /*
+ * Loop through the IA_NA reported by the client, and deal with
+ * addresses reported as already in use.
+ */
+ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_NA);
+ ia != NULL; ia = ia->next) {
+ iaaddr_is_found = 0;
+
+ if (!get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia, IA_NA_OFFSET)) {
+ goto exit;
+ }
+
+ iaid = getULong(cli_enc_opt_data.data);
+
+ /*
+ * XXX: It is possible that we can get multiple addresses
+ * sent by the client. We don't send multiple
+ * addresses, so this indicates a client error.
+ * We should check for multiple IAADDR options, log
+ * if found, and set as an error.
+ */
+ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state,
+ D6O_IAADDR);
+ if (oc == NULL) {
+ /* no address given for this IA, ignore */
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+ continue;
+ }
+
+ memset(&iaaddr, 0, sizeof(iaaddr));
+ if (!evaluate_option_cache(&iaaddr, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("iterate_over_ia_na: "
+ "error evaluating IAADDR.");
+ goto exit;
+ }
+
+ /*
+ * Now we need to figure out which host record matches
+ * this IA_NA and IAADDR (encapsulated option contents
+ * matching a host record by option).
+ *
+ * XXX: We don't currently track IA_NA separately, but
+ * we will need to do this!
+ */
+ host = NULL;
+ if (!find_hosts_by_option(&host, packet,
+ cli_enc_opt_state, MDL)) {
+ if (packet_host != NULL) {
+ host = packet_host;
+ } else {
+ host = NULL;
+ }
+ }
+ while (host != NULL) {
+ if (host->fixed_addr != NULL) {
+ if (!evaluate_option_cache(&fixed_addr, NULL,
+ NULL, NULL, NULL,
+ NULL, &global_scope,
+ host->fixed_addr,
+ MDL)) {
+ log_error("iterate_over_ia_na: error "
+ "evaluating host address.");
+ goto exit;
+ }
+ if ((iaaddr.len >= 16) &&
+ !memcmp(fixed_addr.data, iaaddr.data, 16)) {
+ data_string_forget(&fixed_addr, MDL);
+ break;
+ }
+ data_string_forget(&fixed_addr, MDL);
+ }
+ host = host->n_ipaddr;
+ }
+
+ if ((host == NULL) && (iaaddr.len >= IAADDR_OFFSET)) {
+ /*
+ * Find existing IA_NA.
+ */
+ if (ia_make_key(&key, iaid,
+ (char *)client_id->data,
+ client_id->len,
+ MDL) != ISC_R_SUCCESS) {
+ log_fatal("iterate_over_ia_na: no memory for "
+ "key.");
+ }
+
+ existing_ia_na = NULL;
+ if (ia_hash_lookup(&existing_ia_na, ia_na_active,
+ (unsigned char *)key.data,
+ key.len, MDL)) {
+ /*
+ * Make sure this address is in the IA_NA.
+ */
+ for (i=0; i<existing_ia_na->num_iasubopt; i++) {
+ struct iasubopt *tmp;
+ struct in6_addr *in6_addr;
+
+ tmp = existing_ia_na->iasubopt[i];
+ in6_addr = &tmp->addr;
+ if (memcmp(in6_addr,
+ iaaddr.data, 16) == 0) {
+ iasubopt_reference(&lease,
+ tmp, MDL);
+ break;
+ }
+ }
+ }
+
+ data_string_forget(&key, MDL);
+ }
+
+ if ((host != NULL) || (lease != NULL)) {
+ ia_na_match(client_id, &iaaddr, lease);
+ } else {
+ ia_na_nomatch(client_id, &iaaddr,
+ (u_int32_t *)cli_enc_opt_data.data,
+ packet, reply_data, &reply_ofs,
+ sizeof(reply_data));
+ }
+
+ if (lease != NULL) {
+ iasubopt_dereference(&lease, MDL);
+ }
+
+ data_string_forget(&iaaddr, MDL);
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+ }
+
+ /*
+ * Return our reply to the caller.
+ */
+ reply_ret->len = reply_ofs;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ofs, MDL)) {
+ log_fatal("No memory to store reply.");
+ }
+ reply_ret->data = reply_ret->buffer->data;
+ memcpy(reply_ret->buffer->data, reply, reply_ofs);
+
+exit:
+ if (lease != NULL) {
+ iasubopt_dereference(&lease, MDL);
+ }
+ if (host_opt_state != NULL) {
+ option_state_dereference(&host_opt_state, MDL);
+ }
+ if (fixed_addr.buffer != NULL) {
+ data_string_forget(&fixed_addr, MDL);
+ }
+ if (iaaddr.buffer != NULL) {
+ data_string_forget(&iaaddr, MDL);
+ }
+ if (cli_enc_opt_state != NULL) {
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ }
+ if (cli_enc_opt_data.buffer != NULL) {
+ data_string_forget(&cli_enc_opt_data, MDL);
+ }
+ if (opt_state != NULL) {
+ option_state_dereference(&opt_state, MDL);
+ }
+}
+
+/*
+ * Decline means a client has detected that something else is using an
+ * address we gave it.
+ *
+ * Since we're only dealing with fixed leases for now, there's not
+ * much we can do, other that log the occurrence.
+ *
+ * When we start issuing addresses from pools, then we will have to
+ * record our declined addresses and issue another. In general with
+ * IPv6 there is no worry about DoS by clients exhausting space, but
+ * we still need to be aware of this possibility.
+ */
+
+/* TODO: reject unicast messages, unless we set unicast option */
+/* TODO: IA_TA */
+static void
+dhcpv6_decline(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+ struct data_string server_id;
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_client_resp(packet, &client_id, &server_id)) {
+ return;
+ }
+
+ /*
+ * Undefined for IA_PD.
+ */
+ delete_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+
+ /*
+ * And operate on each IA_NA in this packet.
+ */
+ iterate_over_ia_na(reply, packet, &client_id, &server_id, "Decline",
+ ia_na_match_decline, ia_na_nomatch_decline);
+
+ data_string_forget(&server_id, MDL);
+ data_string_forget(&client_id, MDL);
+}
+
+static void
+ia_na_match_release(const struct data_string *client_id,
+ const struct data_string *iaaddr,
+ struct iasubopt *lease)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+
+ log_info("Client %s releases address %s",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr)));
+ if (lease != NULL) {
+ release_lease6(lease->ipv6_pool, lease);
+ lease->ia->cltt = cur_time;
+ write_ia(lease->ia);
+ }
+}
+
+static void
+ia_na_nomatch_release(const struct data_string *client_id,
+ const struct data_string *iaaddr,
+ u_int32_t *ia_na_id,
+ struct packet *packet,
+ char *reply_data,
+ int *reply_ofs,
+ int reply_len)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+ struct option_state *host_opt_state;
+ int len;
+
+ log_info("Client %s releases address %s, which is not leased to it.",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iaaddr->data, tmp_addr, sizeof(tmp_addr)));
+
+ /*
+ * Create state for this IA_NA.
+ */
+ host_opt_state = NULL;
+ if (!option_state_allocate(&host_opt_state, MDL)) {
+ log_error("ia_na_nomatch_release: out of memory "
+ "allocating option_state.");
+ goto exit;
+ }
+
+ if (!set_status_code(STATUS_NoBinding,
+ "Release for non-leased address.",
+ host_opt_state)) {
+ goto exit;
+ }
+
+ /*
+ * Insure we have enough space
+ */
+ if (reply_len < (*reply_ofs + 16)) {
+ log_error("ia_na_nomatch_release: "
+ "out of space for reply packet.");
+ goto exit;
+ }
+
+ /*
+ * Put our status code into the reply packet.
+ */
+ len = store_options6(reply_data+(*reply_ofs)+16,
+ reply_len-(*reply_ofs)-16,
+ host_opt_state, packet,
+ required_opts_STATUS_CODE, NULL);
+
+ /*
+ * Store the non-encapsulated option data for this
+ * IA_NA into our reply packet. Defined in RFC 3315,
+ * section 22.4.
+ */
+ /* option number */
+ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_NA);
+ /* option length */
+ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12);
+ /* IA_NA, copied from the client */
+ memcpy(reply_data+(*reply_ofs)+4, ia_na_id, 4);
+ /* t1 and t2, odd that we need them, but here it is */
+ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0);
+ putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0);
+
+ /*
+ * Get ready for next IA_NA.
+ */
+ *reply_ofs += (len + 16);
+
+exit:
+ option_state_dereference(&host_opt_state, MDL);
+}
+
+static void
+ia_pd_match_release(const struct data_string *client_id,
+ const struct data_string *iapref,
+ struct iasubopt *prefix)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+
+ log_info("Client %s releases prefix %s/%u",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iapref->data + 9,
+ tmp_addr, sizeof(tmp_addr)),
+ (unsigned) getUChar(iapref->data + 8));
+ if (prefix != NULL) {
+ release_lease6(prefix->ipv6_pool, prefix);
+ prefix->ia->cltt = cur_time;
+ write_ia(prefix->ia);
+ }
+}
+
+static void
+ia_pd_nomatch_release(const struct data_string *client_id,
+ const struct data_string *iapref,
+ u_int32_t *ia_pd_id,
+ struct packet *packet,
+ char *reply_data,
+ int *reply_ofs,
+ int reply_len)
+{
+ char tmp_addr[INET6_ADDRSTRLEN];
+ struct option_state *host_opt_state;
+ int len;
+
+ log_info("Client %s releases prefix %s/%u, which is not leased to it.",
+ print_hex_1(client_id->len, client_id->data, 60),
+ inet_ntop(AF_INET6, iapref->data + 9,
+ tmp_addr, sizeof(tmp_addr)),
+ (unsigned) getUChar(iapref->data + 8));
+
+ /*
+ * Create state for this IA_PD.
+ */
+ host_opt_state = NULL;
+ if (!option_state_allocate(&host_opt_state, MDL)) {
+ log_error("ia_pd_nomatch_release: out of memory "
+ "allocating option_state.");
+ goto exit;
+ }
+
+ if (!set_status_code(STATUS_NoBinding,
+ "Release for non-leased prefix.",
+ host_opt_state)) {
+ goto exit;
+ }
+
+ /*
+ * Insure we have enough space
+ */
+ if (reply_len < (*reply_ofs + 16)) {
+ log_error("ia_pd_nomatch_release: "
+ "out of space for reply packet.");
+ goto exit;
+ }
+
+ /*
+ * Put our status code into the reply packet.
+ */
+ len = store_options6(reply_data+(*reply_ofs)+16,
+ reply_len-(*reply_ofs)-16,
+ host_opt_state, packet,
+ required_opts_STATUS_CODE, NULL);
+
+ /*
+ * Store the non-encapsulated option data for this
+ * IA_PD into our reply packet. Defined in RFC 3315,
+ * section 22.4.
+ */
+ /* option number */
+ putUShort((unsigned char *)reply_data+(*reply_ofs), D6O_IA_PD);
+ /* option length */
+ putUShort((unsigned char *)reply_data+(*reply_ofs)+2, len + 12);
+ /* IA_PD, copied from the client */
+ memcpy(reply_data+(*reply_ofs)+4, ia_pd_id, 4);
+ /* t1 and t2, odd that we need them, but here it is */
+ putULong((unsigned char *)reply_data+(*reply_ofs)+8, 0);
+ putULong((unsigned char *)reply_data+(*reply_ofs)+12, 0);
+
+ /*
+ * Get ready for next IA_PD.
+ */
+ *reply_ofs += (len + 16);
+
+exit:
+ option_state_dereference(&host_opt_state, MDL);
+}
+
+static void
+iterate_over_ia_pd(struct data_string *reply_ret,
+ struct packet *packet,
+ const struct data_string *client_id,
+ const struct data_string *server_id,
+ const char *packet_type,
+ void (*ia_pd_match)(),
+ void (*ia_pd_nomatch)())
+{
+ struct data_string reply_new;
+ int reply_len;
+ struct option_state *opt_state;
+ struct host_decl *packet_host;
+ struct option_cache *ia;
+ struct option_cache *oc;
+ /* cli_enc_... variables come from the IA_PD options */
+ struct data_string cli_enc_opt_data;
+ struct option_state *cli_enc_opt_state;
+ struct host_decl *host;
+ struct option_state *host_opt_state;
+ struct data_string iaprefix;
+ int iaprefix_is_found;
+ char reply_data[65536];
+ int reply_ofs;
+ struct iasubopt *prefix;
+ struct ia_xx *existing_ia_pd;
+ int i;
+ struct data_string key;
+ u_int32_t iaid;
+
+ /*
+ * Initialize to empty values, in case we have to exit early.
+ */
+ memset(&reply_new, 0, sizeof(reply_new));
+ opt_state = NULL;
+ memset(&cli_enc_opt_data, 0, sizeof(cli_enc_opt_data));
+ cli_enc_opt_state = NULL;
+ memset(&iaprefix, 0, sizeof(iaprefix));
+ host_opt_state = NULL;
+ prefix = NULL;
+
+ /*
+ * Compute the available length for the reply.
+ */
+ reply_len = sizeof(reply_data) - reply_ret->len;
+ reply_ofs = 0;
+
+ /*
+ * Find the host record that matches from the packet, if any.
+ */
+ packet_host = NULL;
+ if (!find_hosts_by_uid(&packet_host,
+ client_id->data, client_id->len, MDL)) {
+ packet_host = NULL;
+ /*
+ * Note: In general, we don't expect a client to provide
+ * enough information to match by option for these
+ * types of messages, but if we don't have a UID
+ * match we can check anyway.
+ */
+ if (!find_hosts_by_option(&packet_host,
+ packet, packet->options, MDL)) {
+ packet_host = NULL;
+
+ if (!find_hosts_by_duid_chaddr(&packet_host,
+ client_id))
+ packet_host = NULL;
+ }
+ }
+
+ /*
+ * Build our option state for reply.
+ */
+ opt_state = NULL;
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("iterate_over_ia_pd: no memory for option_state.");
+ goto exit;
+ }
+ execute_statements_in_scope(NULL, packet, NULL, NULL,
+ packet->options, opt_state,
+ &global_scope, root_group, NULL);
+
+ /*
+ * Loop through the IA_PD reported by the client, and deal with
+ * prefixes reported as already in use.
+ */
+ for (ia = lookup_option(&dhcpv6_universe, packet->options, D6O_IA_PD);
+ ia != NULL; ia = ia->next) {
+ iaprefix_is_found = 0;
+
+ if (!get_encapsulated_IA_state(&cli_enc_opt_state,
+ &cli_enc_opt_data,
+ packet, ia, IA_PD_OFFSET)) {
+ goto exit;
+ }
+
+ iaid = getULong(cli_enc_opt_data.data);
+
+ oc = lookup_option(&dhcpv6_universe, cli_enc_opt_state,
+ D6O_IAPREFIX);
+ if (oc == NULL) {
+ /* no prefix given for this IA_PD, ignore */
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+ continue;
+ }
+
+ for (; oc != NULL; oc = oc->next) {
+ memset(&iaprefix, 0, sizeof(iaprefix));
+ if (!evaluate_option_cache(&iaprefix, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("iterate_over_ia_pd: "
+ "error evaluating IAPREFIX.");
+ goto exit;
+ }
+
+ /*
+ * Now we need to figure out which host record matches
+ * this IA_PD and IAPREFIX (encapsulated option contents
+ * matching a host record by option).
+ *
+ * XXX: We don't currently track IA_PD separately, but
+ * we will need to do this!
+ */
+ host = NULL;
+ if (!find_hosts_by_option(&host, packet,
+ cli_enc_opt_state, MDL)) {
+ if (packet_host != NULL) {
+ host = packet_host;
+ } else {
+ host = NULL;
+ }
+ }
+ while (host != NULL) {
+ if (host->fixed_prefix != NULL) {
+ struct iaddrcidrnetlist *l;
+ int plen = (int) getUChar(iaprefix.data + 8);
+
+ for (l = host->fixed_prefix; l != NULL;
+ l = l->next) {
+ if (plen != l->cidrnet.bits)
+ continue;
+ if (memcmp(iaprefix.data + 9,
+ l->cidrnet.lo_addr.iabuf,
+ 16) == 0)
+ break;
+ }
+ if ((l != NULL) && (iaprefix.len >= 17))
+ break;
+ }
+ host = host->n_ipaddr;
+ }
+
+ if ((host == NULL) && (iaprefix.len >= IAPREFIX_OFFSET)) {
+ /*
+ * Find existing IA_PD.
+ */
+ if (ia_make_key(&key, iaid,
+ (char *)client_id->data,
+ client_id->len,
+ MDL) != ISC_R_SUCCESS) {
+ log_fatal("iterate_over_ia_pd: no memory for "
+ "key.");
+ }
+
+ existing_ia_pd = NULL;
+ if (ia_hash_lookup(&existing_ia_pd, ia_pd_active,
+ (unsigned char *)key.data,
+ key.len, MDL)) {
+ /*
+ * Make sure this prefix is in the IA_PD.
+ */
+ for (i = 0;
+ i < existing_ia_pd->num_iasubopt;
+ i++) {
+ struct iasubopt *tmp;
+ u_int8_t plen;
+
+ plen = getUChar(iaprefix.data + 8);
+ tmp = existing_ia_pd->iasubopt[i];
+ if ((tmp->plen == plen) &&
+ (memcmp(&tmp->addr,
+ iaprefix.data + 9,
+ 16) == 0)) {
+ iasubopt_reference(&prefix,
+ tmp, MDL);
+ break;
+ }
+ }
+ }
+
+ data_string_forget(&key, MDL);
+ }
+
+ if ((host != NULL) || (prefix != NULL)) {
+ ia_pd_match(client_id, &iaprefix, prefix);
+ } else {
+ ia_pd_nomatch(client_id, &iaprefix,
+ (u_int32_t *)cli_enc_opt_data.data,
+ packet, reply_data, &reply_ofs,
+ reply_len - reply_ofs);
+ }
+
+ if (prefix != NULL) {
+ iasubopt_dereference(&prefix, MDL);
+ }
+
+ data_string_forget(&iaprefix, MDL);
+ }
+
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ data_string_forget(&cli_enc_opt_data, MDL);
+ }
+
+ /*
+ * Return our reply to the caller.
+ * The IA_NA routine has already filled at least the header.
+ */
+ reply_new.len = reply_ret->len + reply_ofs;
+ if (!buffer_allocate(&reply_new.buffer, reply_new.len, MDL)) {
+ log_fatal("No memory to store reply.");
+ }
+ reply_new.data = reply_new.buffer->data;
+ memcpy(reply_new.buffer->data,
+ reply_ret->buffer->data, reply_ret->len);
+ memcpy(reply_new.buffer->data + reply_ret->len,
+ reply_data, reply_ofs);
+ data_string_forget(reply_ret, MDL);
+ data_string_copy(reply_ret, &reply_new, MDL);
+ data_string_forget(&reply_new, MDL);
+
+exit:
+ if (prefix != NULL) {
+ iasubopt_dereference(&prefix, MDL);
+ }
+ if (host_opt_state != NULL) {
+ option_state_dereference(&host_opt_state, MDL);
+ }
+ if (iaprefix.buffer != NULL) {
+ data_string_forget(&iaprefix, MDL);
+ }
+ if (cli_enc_opt_state != NULL) {
+ option_state_dereference(&cli_enc_opt_state, MDL);
+ }
+ if (cli_enc_opt_data.buffer != NULL) {
+ data_string_forget(&cli_enc_opt_data, MDL);
+ }
+ if (opt_state != NULL) {
+ option_state_dereference(&opt_state, MDL);
+ }
+}
+
+/*
+ * Release means a client is done with the leases.
+ */
+
+/* TODO: reject unicast messages, unless we set unicast option */
+static void
+dhcpv6_release(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+ struct data_string server_id;
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_client_resp(packet, &client_id, &server_id)) {
+ return;
+ }
+
+ /*
+ * And operate on each IA_NA in this packet.
+ */
+ iterate_over_ia_na(reply, packet, &client_id, &server_id, "Release",
+ ia_na_match_release, ia_na_nomatch_release);
+
+ /*
+ * And operate on each IA_PD in this packet.
+ */
+ iterate_over_ia_pd(reply, packet, &client_id, &server_id, "Release",
+ ia_pd_match_release, ia_pd_nomatch_release);
+
+ data_string_forget(&server_id, MDL);
+ data_string_forget(&client_id, MDL);
+}
+
+/*
+ * Information-Request is used by clients who have obtained an address
+ * from other means, but want configuration information from the server.
+ */
+
+static void
+dhcpv6_information_request(struct data_string *reply, struct packet *packet) {
+ struct data_string client_id;
+ struct data_string server_id;
+
+ /*
+ * Validate our input.
+ */
+ if (!valid_client_info_req(packet, &server_id)) {
+ return;
+ }
+
+ /*
+ * Get our client ID, if there is one.
+ */
+ memset(&client_id, 0, sizeof(client_id));
+ if (get_client_id(packet, &client_id) != ISC_R_SUCCESS) {
+ data_string_forget(&client_id, MDL);
+ }
+
+ /*
+ * Use the lease_to_client() function. This will work fine,
+ * because the valid_client_info_req() insures that we
+ * don't have any IA that would cause us to allocate
+ * resources to the client.
+ */
+ lease_to_client(reply, packet, &client_id,
+ server_id.data != NULL ? &server_id : NULL);
+
+ /*
+ * Cleanup.
+ */
+ if (client_id.data != NULL) {
+ data_string_forget(&client_id, MDL);
+ }
+ data_string_forget(&server_id, MDL);
+}
+
+/*
+ * The Relay-forw message is sent by relays. It typically contains a
+ * single option, which encapsulates an entire packet.
+ *
+ * We need to build an encapsulated reply.
+ */
+
+/* XXX: this is very, very similar to do_packet6(), and should probably
+ be combined in a clever way */
+static void
+dhcpv6_relay_forw(struct data_string *reply_ret, struct packet *packet) {
+ struct option_cache *oc;
+ struct data_string enc_opt_data;
+ struct packet *enc_packet;
+ unsigned char msg_type;
+ const struct dhcpv6_packet *msg;
+ const struct dhcpv6_relay_packet *relay;
+ struct data_string enc_reply;
+ char link_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ char peer_addr[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+ struct data_string a_opt, packet_ero;
+ struct option_state *opt_state;
+ static char reply_data[65536];
+ struct dhcpv6_relay_packet *reply;
+ int reply_ofs;
+
+ /*
+ * Initialize variables for early exit.
+ */
+ opt_state = NULL;
+ memset(&a_opt, 0, sizeof(a_opt));
+ memset(&packet_ero, 0, sizeof(packet_ero));
+ memset(&enc_reply, 0, sizeof(enc_reply));
+ memset(&enc_opt_data, 0, sizeof(enc_opt_data));
+ enc_packet = NULL;
+
+ /*
+ * Get our encapsulated relay message.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
+ if (oc == NULL) {
+ inet_ntop(AF_INET6, &packet->dhcpv6_link_address,
+ link_addr, sizeof(link_addr));
+ inet_ntop(AF_INET6, &packet->dhcpv6_peer_address,
+ peer_addr, sizeof(peer_addr));
+ log_info("Relay-forward from %s with link address=%s and "
+ "peer address=%s missing Relay Message option.",
+ piaddr(packet->client_addr), link_addr, peer_addr);
+ goto exit;
+ }
+
+ if (!evaluate_option_cache(&enc_opt_data, NULL, NULL, NULL,
+ NULL, NULL, &global_scope, oc, MDL)) {
+ log_error("dhcpv6_forw_relay: error evaluating "
+ "relayed message.");
+ goto exit;
+ }
+
+ if (!packet6_len_okay((char *)enc_opt_data.data, enc_opt_data.len)) {
+ log_error("dhcpv6_forw_relay: encapsulated packet too short.");
+ goto exit;
+ }
+
+ /*
+ * Build a packet structure from this encapsulated packet.
+ */
+ enc_packet = NULL;
+ if (!packet_allocate(&enc_packet, MDL)) {
+ log_error("dhcpv6_forw_relay: "
+ "no memory for encapsulated packet.");
+ goto exit;
+ }
+
+ if (!option_state_allocate(&enc_packet->options, MDL)) {
+ log_error("dhcpv6_forw_relay: "
+ "no memory for encapsulated packet's options.");
+ goto exit;
+ }
+
+ enc_packet->client_port = packet->client_port;
+ enc_packet->client_addr = packet->client_addr;
+ interface_reference(&enc_packet->interface, packet->interface, MDL);
+ enc_packet->dhcpv6_container_packet = packet;
+
+ msg_type = enc_opt_data.data[0];
+ if ((msg_type == DHCPV6_RELAY_FORW) ||
+ (msg_type == DHCPV6_RELAY_REPL)) {
+ relay = (struct dhcpv6_relay_packet *)enc_opt_data.data;
+ enc_packet->dhcpv6_msg_type = relay->msg_type;
+
+ /* relay-specific data */
+ enc_packet->dhcpv6_hop_count = relay->hop_count;
+ memcpy(&enc_packet->dhcpv6_link_address,
+ relay->link_address, sizeof(relay->link_address));
+ memcpy(&enc_packet->dhcpv6_peer_address,
+ relay->peer_address, sizeof(relay->peer_address));
+
+ if (!parse_option_buffer(enc_packet->options,
+ relay->options,
+ enc_opt_data.len-sizeof(*relay),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ goto exit;
+ }
+ } else {
+ msg = (struct dhcpv6_packet *)enc_opt_data.data;
+ enc_packet->dhcpv6_msg_type = msg->msg_type;
+
+ /* message-specific data */
+ memcpy(enc_packet->dhcpv6_transaction_id,
+ msg->transaction_id,
+ sizeof(enc_packet->dhcpv6_transaction_id));
+
+ if (!parse_option_buffer(enc_packet->options,
+ msg->options,
+ enc_opt_data.len-sizeof(*msg),
+ &dhcpv6_universe)) {
+ /* no logging here, as parse_option_buffer() logs all
+ cases where it fails */
+ goto exit;
+ }
+ }
+
+ /*
+ * This is recursive. It is possible to exceed maximum packet size.
+ * XXX: This will cause the packet send to fail.
+ */
+ build_dhcpv6_reply(&enc_reply, enc_packet);
+
+ /*
+ * If we got no encapsulated data, then it is discarded, and
+ * our reply-forw is also discarded.
+ */
+ if (enc_reply.data == NULL) {
+ goto exit;
+ }
+
+ /*
+ * Now we can use the reply_data buffer.
+ * Packet header stuff all comes from the forward message.
+ */
+ reply = (struct dhcpv6_relay_packet *)reply_data;
+ reply->msg_type = DHCPV6_RELAY_REPL;
+ reply->hop_count = packet->dhcpv6_hop_count;
+ memcpy(reply->link_address, &packet->dhcpv6_link_address,
+ sizeof(reply->link_address));
+ memcpy(reply->peer_address, &packet->dhcpv6_peer_address,
+ sizeof(reply->peer_address));
+ reply_ofs = (int)(offsetof(struct dhcpv6_relay_packet, options));
+
+ /*
+ * Get the reply option state.
+ */
+ opt_state = NULL;
+ if (!option_state_allocate(&opt_state, MDL)) {
+ log_error("dhcpv6_relay_forw: no memory for option state.");
+ goto exit;
+ }
+
+ /*
+ * Append the interface-id if present.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_INTERFACE_ID);
+ if (oc != NULL) {
+ if (!evaluate_option_cache(&a_opt, packet,
+ NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("dhcpv6_relay_forw: error evaluating "
+ "Interface ID.");
+ goto exit;
+ }
+ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
+ (unsigned char *)a_opt.data,
+ a_opt.len,
+ D6O_INTERFACE_ID, 0)) {
+ log_error("dhcpv6_relay_forw: error saving "
+ "Interface ID.");
+ goto exit;
+ }
+ data_string_forget(&a_opt, MDL);
+ }
+
+ /*
+ * Append our encapsulated stuff for caller.
+ */
+ if (!save_option_buffer(&dhcpv6_universe, opt_state, NULL,
+ (unsigned char *)enc_reply.data,
+ enc_reply.len,
+ D6O_RELAY_MSG, 0)) {
+ log_error("dhcpv6_relay_forw: error saving Relay MSG.");
+ goto exit;
+ }
+
+ /*
+ * Get the ERO if any.
+ */
+ oc = lookup_option(&dhcpv6_universe, packet->options, D6O_ERO);
+ if (oc != NULL) {
+ unsigned req;
+ int i;
+
+ if (!evaluate_option_cache(&packet_ero, packet,
+ NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL) ||
+ (packet_ero.len & 1)) {
+ log_error("dhcpv6_relay_forw: error evaluating ERO.");
+ goto exit;
+ }
+
+ /* Decode and apply the ERO. */
+ for (i = 0; i < packet_ero.len; i += 2) {
+ req = getUShort(packet_ero.data + i);
+ /* Already in the reply? */
+ oc = lookup_option(&dhcpv6_universe, opt_state, req);
+ if (oc != NULL)
+ continue;
+ /* Get it from the packet if present. */
+ oc = lookup_option(&dhcpv6_universe,
+ packet->options,
+ req);
+ if (oc == NULL)
+ continue;
+ if (!evaluate_option_cache(&a_opt, packet,
+ NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("dhcpv6_relay_forw: error "
+ "evaluating option %u.", req);
+ goto exit;
+ }
+ if (!save_option_buffer(&dhcpv6_universe,
+ opt_state,
+ NULL,
+ (unsigned char *)a_opt.data,
+ a_opt.len,
+ req,
+ 0)) {
+ log_error("dhcpv6_relay_forw: error saving "
+ "option %u.", req);
+ goto exit;
+ }
+ data_string_forget(&a_opt, MDL);
+ }
+ }
+
+ reply_ofs += store_options6(reply_data + reply_ofs,
+ sizeof(reply_data) - reply_ofs,
+ opt_state, packet,
+ required_opts_agent, &packet_ero);
+
+ /*
+ * Return our reply to the caller.
+ */
+ reply_ret->len = reply_ofs;
+ reply_ret->buffer = NULL;
+ if (!buffer_allocate(&reply_ret->buffer, reply_ret->len, MDL)) {
+ log_fatal("No memory to store reply.");
+ }
+ reply_ret->data = reply_ret->buffer->data;
+ memcpy(reply_ret->buffer->data, reply_data, reply_ofs);
+
+exit:
+ if (opt_state != NULL)
+ option_state_dereference(&opt_state, MDL);
+ if (a_opt.data != NULL) {
+ data_string_forget(&a_opt, MDL);
+ }
+ if (packet_ero.data != NULL) {
+ data_string_forget(&packet_ero, MDL);
+ }
+ if (enc_reply.data != NULL) {
+ data_string_forget(&enc_reply, MDL);
+ }
+ if (enc_opt_data.data != NULL) {
+ data_string_forget(&enc_opt_data, MDL);
+ }
+ if (enc_packet != NULL) {
+ packet_dereference(&enc_packet, MDL);
+ }
+}
+
+static void
+dhcpv6_discard(struct packet *packet) {
+ /* INSIST(packet->msg_type > 0); */
+ /* INSIST(packet->msg_type < dhcpv6_type_name_max); */
+
+ log_debug("Discarding %s from %s; message type not handled by server",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr));
+}
+
+static void
+build_dhcpv6_reply(struct data_string *reply, struct packet *packet) {
+ memset(reply, 0, sizeof(*reply));
+ switch (packet->dhcpv6_msg_type) {
+ case DHCPV6_SOLICIT:
+ dhcpv6_solicit(reply, packet);
+ break;
+ case DHCPV6_ADVERTISE:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_REQUEST:
+ dhcpv6_request(reply, packet);
+ break;
+ case DHCPV6_CONFIRM:
+ dhcpv6_confirm(reply, packet);
+ break;
+ case DHCPV6_RENEW:
+ dhcpv6_renew(reply, packet);
+ break;
+ case DHCPV6_REBIND:
+ dhcpv6_rebind(reply, packet);
+ break;
+ case DHCPV6_REPLY:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_RELEASE:
+ dhcpv6_release(reply, packet);
+ break;
+ case DHCPV6_DECLINE:
+ dhcpv6_decline(reply, packet);
+ break;
+ case DHCPV6_RECONFIGURE:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_INFORMATION_REQUEST:
+ dhcpv6_information_request(reply, packet);
+ break;
+ case DHCPV6_RELAY_FORW:
+ dhcpv6_relay_forw(reply, packet);
+ break;
+ case DHCPV6_RELAY_REPL:
+ dhcpv6_discard(packet);
+ break;
+ case DHCPV6_LEASEQUERY:
+ dhcpv6_leasequery(reply, packet);
+ break;
+ case DHCPV6_LEASEQUERY_REPLY:
+ dhcpv6_discard(packet);
+ break;
+ default:
+ /* XXX: would be nice if we had "notice" level,
+ as syslog, for this */
+ log_info("Discarding unknown DHCPv6 message type %d "
+ "from %s", packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr));
+ }
+}
+
+static void
+log_packet_in(const struct packet *packet) {
+ struct data_string s;
+ u_int32_t tid;
+ char tmp_addr[INET6_ADDRSTRLEN];
+ const void *addr;
+
+ memset(&s, 0, sizeof(s));
+
+ if (packet->dhcpv6_msg_type < dhcpv6_type_name_max) {
+ data_string_sprintfa(&s, "%s message from %s port %d",
+ dhcpv6_type_names[packet->dhcpv6_msg_type],
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ } else {
+ data_string_sprintfa(&s,
+ "Unknown message type %d from %s port %d",
+ packet->dhcpv6_msg_type,
+ piaddr(packet->client_addr),
+ ntohs(packet->client_port));
+ }
+ if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
+ (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
+ addr = &packet->dhcpv6_link_address;
+ data_string_sprintfa(&s, ", link address %s",
+ inet_ntop(AF_INET6, addr,
+ tmp_addr, sizeof(tmp_addr)));
+ addr = &packet->dhcpv6_peer_address;
+ data_string_sprintfa(&s, ", peer address %s",
+ inet_ntop(AF_INET6, addr,
+ tmp_addr, sizeof(tmp_addr)));
+ } else {
+ tid = 0;
+ memcpy(((char *)&tid)+1, packet->dhcpv6_transaction_id, 3);
+ data_string_sprintfa(&s, ", transaction ID 0x%06X", tid);
+
+/*
+ oc = lookup_option(&dhcpv6_universe, packet->options,
+ D6O_CLIENTID);
+ if (oc != NULL) {
+ memset(&tmp_ds, 0, sizeof(tmp_ds_));
+ if (!evaluate_option_cache(&tmp_ds, packet, NULL, NULL,
+ packet->options, NULL,
+ &global_scope, oc, MDL)) {
+ log_error("Error evaluating Client Identifier");
+ } else {
+ data_strint_sprintf(&s, ", client ID %s",
+
+ data_string_forget(&tmp_ds, MDL);
+ }
+ }
+*/
+
+ }
+ log_info("%s", s.data);
+
+ data_string_forget(&s, MDL);
+}
+
+void
+dhcpv6(struct packet *packet) {
+ struct data_string reply;
+ struct sockaddr_in6 to_addr;
+ int send_ret;
+
+ /*
+ * Log a message that we received this packet.
+ */
+ log_packet_in(packet);
+
+ /*
+ * Build our reply packet.
+ */
+ build_dhcpv6_reply(&reply, packet);
+
+ if (reply.data != NULL) {
+ /*
+ * Send our reply, if we have one.
+ */
+ memset(&to_addr, 0, sizeof(to_addr));
+ to_addr.sin6_family = AF_INET6;
+ if ((packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) ||
+ (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL)) {
+ to_addr.sin6_port = local_port;
+ } else {
+ to_addr.sin6_port = remote_port;
+ }
+
+#if defined (REPLY_TO_SOURCE_PORT)
+ /*
+ * This appears to have been included for testing so we would
+ * not need a root client, but was accidently left in the
+ * final code. We continue to include it in case
+ * some users have come to rely upon it, but leave
+ * it off by default as it's a bad idea.
+ */
+ to_addr.sin6_port = packet->client_port;
+#endif
+
+ memcpy(&to_addr.sin6_addr, packet->client_addr.iabuf,
+ sizeof(to_addr.sin6_addr));
+
+ log_info("Sending %s to %s port %d",
+ dhcpv6_type_names[reply.data[0]],
+ piaddr(packet->client_addr),
+ ntohs(to_addr.sin6_port));
+
+ send_ret = send_packet6(packet->interface,
+ reply.data, reply.len, &to_addr);
+ if (send_ret != reply.len) {
+ log_error("dhcpv6: send_packet6() sent %d of %d bytes",
+ send_ret, reply.len);
+ }
+ data_string_forget(&reply, MDL);
+ }
+}
+
+static void
+seek_shared_host(struct host_decl **hp, struct shared_network *shared) {
+ struct host_decl *nofixed = NULL;
+ struct host_decl *seek, *hold = NULL;
+
+ /*
+ * Seek forward through fixed addresses for the right link.
+ *
+ * Note: how to do this for fixed prefixes???
+ */
+ host_reference(&hold, *hp, MDL);
+ host_dereference(hp, MDL);
+ seek = hold;
+ while (seek != NULL) {
+ if (seek->fixed_addr == NULL)
+ nofixed = seek;
+ else if (fixed_matches_shared(seek, shared))
+ break;
+
+ seek = seek->n_ipaddr;
+ }
+
+ if ((seek == NULL) && (nofixed != NULL))
+ seek = nofixed;
+
+ if (seek != NULL)
+ host_reference(hp, seek, MDL);
+}
+
+static isc_boolean_t
+fixed_matches_shared(struct host_decl *host, struct shared_network *shared) {
+ struct subnet *subnet;
+ struct data_string addr;
+ isc_boolean_t matched;
+ struct iaddr fixed;
+
+ if (host->fixed_addr == NULL)
+ return ISC_FALSE;
+
+ memset(&addr, 0, sizeof(addr));
+ if (!evaluate_option_cache(&addr, NULL, NULL, NULL, NULL, NULL,
+ &global_scope, host->fixed_addr, MDL))
+ return ISC_FALSE;
+
+ if (addr.len < 16) {
+ data_string_forget(&addr, MDL);
+ return ISC_FALSE;
+ }
+
+ fixed.len = 16;
+ memcpy(fixed.iabuf, addr.data, 16);
+
+ matched = ISC_FALSE;
+ for (subnet = shared->subnets ; subnet != NULL ;
+ subnet = subnet->next_sibling) {
+ if (addr_eq(subnet_number(fixed, subnet->netmask),
+ subnet->net)) {
+ matched = ISC_TRUE;
+ break;
+ }
+ }
+
+ data_string_forget(&addr, MDL);
+ return matched;
+}
+
+/*
+ * find_host_by_duid_chaddr() synthesizes a DHCPv4-like 'hardware'
+ * parameter from a DHCPv6 supplied DUID (client-identifier option),
+ * and may seek to use client or relay supplied hardware addresses.
+ */
+static int
+find_hosts_by_duid_chaddr(struct host_decl **host,
+ const struct data_string *client_id) {
+ static int once_htype;
+ int htype, hlen;
+ const unsigned char *chaddr;
+
+ /*
+ * The DUID-LL and DUID-LLT must have a 2-byte DUID type and 2-byte
+ * htype.
+ */
+ if (client_id->len < 4)
+ return 0;
+
+ /*
+ * The third and fourth octets of the DUID-LL and DUID-LLT
+ * is the hardware type, but in 16 bits.
+ */
+ htype = getUShort(client_id->data + 2);
+ hlen = 0;
+ chaddr = NULL;
+
+ /* The first two octets of the DUID identify the type. */
+ switch(getUShort(client_id->data)) {
+ case DUID_LLT:
+ if (client_id->len > 8) {
+ hlen = client_id->len - 8;
+ chaddr = client_id->data + 8;
+ }
+ break;
+
+ case DUID_LL:
+ /*
+ * Note that client_id->len must be greater than or equal
+ * to four to get to this point in the function.
+ */
+ hlen = client_id->len - 4;
+ chaddr = client_id->data + 4;
+ break;
+
+ default:
+ break;
+ }
+
+ if (hlen == 0)
+ return 0;
+
+ /*
+ * XXX: DHCPv6 gives a 16-bit field for the htype. DHCPv4 gives an
+ * 8-bit field. To change the semantics of the generic 'hardware'
+ * structure, we would have to adjust many DHCPv4 sources (from
+ * interface to DHCPv4 lease code), and we would have to update the
+ * 'hardware' config directive (probably being reverse compatible and
+ * providing a new upgrade/replacement primitive). This is a little
+ * too much to change for now. Hopefully we will revisit this before
+ * hardware types exceeding 8 bits are assigned.
+ */
+ if ((htype & 0xFF00) && !once_htype) {
+ once_htype = 1;
+ log_error("Attention: At least one client advertises a "
+ "hardware type of %d, which exceeds the software "
+ "limitation of 255.", htype);
+ }
+
+ return find_hosts_by_haddr(host, htype, chaddr, hlen, MDL);
+}
+
+#endif /* DHCPv6 */
+
diff --git a/server/failover.c b/server/failover.c
new file mode 100644
index 0000000..97e7d73
--- /dev/null
+++ b/server/failover.c
@@ -0,0 +1,6391 @@
+/* failover.c
+
+ Failover protocol support code... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+
+#if defined (FAILOVER_PROTOCOL)
+dhcp_failover_state_t *failover_states;
+static isc_result_t do_a_failover_option (omapi_object_t *,
+ dhcp_failover_link_t *);
+dhcp_failover_listener_t *failover_listeners;
+
+static isc_result_t failover_message_reference (failover_message_t **,
+ failover_message_t *,
+ const char *file, int line);
+static isc_result_t failover_message_dereference (failover_message_t **,
+ const char *file, int line);
+
+static void dhcp_failover_pool_balance(dhcp_failover_state_t *state);
+static void dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state);
+static int dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
+ isc_boolean_t *sendreq);
+static inline int secondary_not_hoarding(dhcp_failover_state_t *state,
+ struct pool *p);
+
+
+void dhcp_failover_startup ()
+{
+ dhcp_failover_state_t *state;
+ isc_result_t status;
+ struct timeval tv;
+
+ for (state = failover_states; state; state = state -> next) {
+ dhcp_failover_state_transition (state, "startup");
+
+ if (state -> pool_count == 0) {
+ log_error ("failover peer declaration with no %s",
+ "referring pools.");
+ log_error ("In order to use failover, you MUST %s",
+ "refer to your main failover declaration");
+ log_error ("in each pool declaration. You MUST %s",
+ "NOT use range declarations outside");
+ log_fatal ("of pool declarations.");
+ }
+ /* In case the peer is already running, immediately try
+ to establish a connection with it. */
+ status = dhcp_failover_link_initiate ((omapi_object_t *)state);
+ if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) {
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +90 dhcp_failover_reconnect");
+#endif
+ tv . tv_sec = cur_time + 90;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_reconnect, state,
+ (tvref_t)
+ dhcp_failover_state_reference,
+ (tvunref_t)
+ dhcp_failover_state_dereference);
+ log_error ("failover peer %s: %s", state -> name,
+ isc_result_totext (status));
+ }
+
+ status = (dhcp_failover_listen
+ ((omapi_object_t *)state));
+ if (status != ISC_R_SUCCESS) {
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +90 %s",
+ "dhcp_failover_listener_restart");
+#endif
+ tv . tv_sec = cur_time + 90;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_listener_restart,
+ state,
+ (tvref_t)omapi_object_reference,
+ (tvunref_t)omapi_object_dereference);
+ }
+ }
+}
+
+int dhcp_failover_write_all_states ()
+{
+ dhcp_failover_state_t *state;
+
+ for (state = failover_states; state; state = state -> next) {
+ if (!write_failover_state (state))
+ return 0;
+ }
+ return 1;
+}
+
+isc_result_t enter_failover_peer (peer)
+ dhcp_failover_state_t *peer;
+{
+ dhcp_failover_state_t *dup = (dhcp_failover_state_t *)0;
+ isc_result_t status;
+
+ status = find_failover_peer (&dup, peer -> name, MDL);
+ if (status == ISC_R_NOTFOUND) {
+ if (failover_states) {
+ dhcp_failover_state_reference (&peer -> next,
+ failover_states, MDL);
+ dhcp_failover_state_dereference (&failover_states,
+ MDL);
+ }
+ dhcp_failover_state_reference (&failover_states, peer, MDL);
+ return ISC_R_SUCCESS;
+ }
+ dhcp_failover_state_dereference (&dup, MDL);
+ if (status == ISC_R_SUCCESS)
+ return ISC_R_EXISTS;
+ return status;
+}
+
+isc_result_t find_failover_peer (peer, name, file, line)
+ dhcp_failover_state_t **peer;
+ const char *name;
+ const char *file;
+ int line;
+{
+ dhcp_failover_state_t *p;
+
+ for (p = failover_states; p; p = p -> next)
+ if (!strcmp (name, p -> name))
+ break;
+ if (p)
+ return dhcp_failover_state_reference (peer, p, file, line);
+ return ISC_R_NOTFOUND;
+}
+
+/* The failover protocol has three objects associated with it. For
+ each failover partner declaration in the dhcpd.conf file, primary
+ or secondary, there is a failover_state object. For any primary or
+ secondary state object that has a connection to its peer, there is
+ also a failover_link object, which has its own input state separate
+ from the failover protocol state for managing the actual bytes
+ coming in off the wire. Finally, there will be one listener object
+ for every distinct port number associated with a secondary
+ failover_state object. Normally all secondary failover_state
+ objects are expected to listen on the same port number, so there
+ need be only one listener object, but if different port numbers are
+ specified for each failover object, there could be as many as one
+ listener object for each secondary failover_state object. */
+
+/* This, then, is the implementation of the failover link object. */
+
+isc_result_t dhcp_failover_link_initiate (omapi_object_t *h)
+{
+ isc_result_t status;
+ dhcp_failover_link_t *obj;
+ dhcp_failover_state_t *state;
+ omapi_object_t *o;
+ int i;
+ struct data_string ds;
+ omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
+ omapi_addr_t local_addr;
+
+ /* Find the failover state in the object chain. */
+ for (o = h; o -> outer; o = o -> outer)
+ ;
+ for (; o; o = o -> inner) {
+ if (o -> type == dhcp_type_failover_state)
+ break;
+ }
+ if (!o)
+ return DHCP_R_INVALIDARG;
+ state = (dhcp_failover_state_t *)o;
+
+ obj = (dhcp_failover_link_t *)0;
+ status = dhcp_failover_link_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ option_cache_reference (&obj -> peer_address,
+ state -> partner.address, MDL);
+ obj -> peer_port = state -> partner.port;
+ dhcp_failover_state_reference (&obj -> state_object, state, MDL);
+
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_option_cache (&ds, (struct packet *)0, (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope, obj -> peer_address, MDL)) {
+ dhcp_failover_link_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Make an omapi address list out of a buffer containing zero or more
+ IPv4 addresses. */
+ status = omapi_addr_list_new (&addrs, ds.len / 4, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_link_dereference (&obj, MDL);
+ return status;
+ }
+
+ for (i = 0; i < addrs -> count; i++) {
+ addrs -> addresses [i].addrtype = AF_INET;
+ addrs -> addresses [i].addrlen = sizeof (struct in_addr);
+ memcpy (addrs -> addresses [i].address,
+ &ds.data [i * 4], sizeof (struct in_addr));
+ addrs -> addresses [i].port = obj -> peer_port;
+ }
+ data_string_forget (&ds, MDL);
+
+ /* Now figure out the local address that we're supposed to use. */
+ if (!state -> me.address ||
+ !evaluate_option_cache (&ds, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope, state -> me.address,
+ MDL)) {
+ memset (&local_addr, 0, sizeof local_addr);
+ local_addr.addrtype = AF_INET;
+ local_addr.addrlen = sizeof (struct in_addr);
+ if (!state -> server_identifier.len) {
+ log_fatal ("failover peer %s: no local address.",
+ state -> name);
+ }
+ } else {
+ if (ds.len != sizeof (struct in_addr)) {
+ log_error("failover peer %s: 'address' parameter "
+ "fails to resolve to an IPv4 address",
+ state->name);
+ data_string_forget (&ds, MDL);
+ dhcp_failover_link_dereference (&obj, MDL);
+ omapi_addr_list_dereference (&addrs, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ local_addr.addrtype = AF_INET;
+ local_addr.addrlen = ds.len;
+ memcpy (local_addr.address, ds.data, ds.len);
+ if (!state -> server_identifier.len)
+ data_string_copy (&state -> server_identifier,
+ &ds, MDL);
+ data_string_forget (&ds, MDL);
+ local_addr.port = 0; /* Let the O.S. choose. */
+ }
+
+ status = omapi_connect_list ((omapi_object_t *)obj,
+ addrs, &local_addr);
+ omapi_addr_list_dereference (&addrs, MDL);
+
+ dhcp_failover_link_dereference (&obj, MDL);
+ return status;
+}
+
+isc_result_t dhcp_failover_link_signal (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ isc_result_t status;
+ dhcp_failover_link_t *link;
+ omapi_object_t *c;
+ dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
+ char *sname;
+ int slen;
+ struct timeval tv;
+
+ if (h -> type != dhcp_type_failover_link) {
+ /* XXX shouldn't happen. Put an assert here? */
+ return ISC_R_UNEXPECTED;
+ }
+ link = (dhcp_failover_link_t *)h;
+
+ if (!strcmp (name, "connect")) {
+ if (link -> state_object -> i_am == primary) {
+ status = dhcp_failover_send_connect (h);
+ if (status != ISC_R_SUCCESS) {
+ log_info ("dhcp_failover_send_connect: %s",
+ isc_result_totext (status));
+ omapi_disconnect (h -> outer, 1);
+ }
+ } else
+ status = ISC_R_SUCCESS;
+ /* Allow the peer fifteen seconds to send us a
+ startup message. */
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +15 %s",
+ "dhcp_failover_link_startup_timeout");
+#endif
+ tv . tv_sec = cur_time + 15;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_link_startup_timeout,
+ link,
+ (tvref_t)dhcp_failover_link_reference,
+ (tvunref_t)dhcp_failover_link_dereference);
+ return status;
+ }
+
+ if (!strcmp (name, "disconnect")) {
+ if (link -> state_object) {
+ dhcp_failover_state_reference (&state,
+ link -> state_object, MDL);
+ link -> state = dhcp_flink_disconnected;
+
+ /* Make the transition. */
+ if (state->link_to_peer == link)
+ dhcp_failover_state_transition(link->state_object, name);
+
+ /* Schedule an attempt to reconnect. */
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info("add_timeout +5 dhcp_failover_reconnect");
+#endif
+ tv.tv_sec = cur_time + 5;
+ tv.tv_usec = cur_tv.tv_usec;
+ add_timeout(&tv, dhcp_failover_reconnect, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+
+ dhcp_failover_state_dereference (&state, MDL);
+ }
+ return ISC_R_SUCCESS;
+ }
+
+ if (!strcmp (name, "status")) {
+ if (link -> state_object) {
+ isc_result_t status;
+
+ status = va_arg(ap, isc_result_t);
+
+ if ((status == ISC_R_HOSTUNREACH) || (status == ISC_R_TIMEDOUT)) {
+ dhcp_failover_state_reference (&state,
+ link -> state_object, MDL);
+ link -> state = dhcp_flink_disconnected;
+
+ /* Make the transition. */
+ dhcp_failover_state_transition (link -> state_object,
+ "disconnect");
+
+ /* Start trying to reconnect. */
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +5 %s",
+ "dhcp_failover_reconnect");
+#endif
+ tv . tv_sec = cur_time + 5;
+ tv . tv_usec = 0;
+ add_timeout (&tv, dhcp_failover_reconnect,
+ state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ }
+ dhcp_failover_state_dereference (&state, MDL);
+ }
+ return ISC_R_SUCCESS;
+ }
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "ready")) {
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ if (!h -> outer || h -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ c = h -> outer;
+
+ /* We get here because we requested that we be woken up after
+ some number of bytes were read, and that number of bytes
+ has in fact been read. */
+ switch (link -> state) {
+ case dhcp_flink_start:
+ link -> state = dhcp_flink_message_length_wait;
+ if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS)
+ break;
+ case dhcp_flink_message_length_wait:
+ next_message:
+ link -> state = dhcp_flink_message_wait;
+ link -> imsg = dmalloc (sizeof (failover_message_t), MDL);
+ if (!link -> imsg) {
+ status = ISC_R_NOMEMORY;
+ dhcp_flink_fail:
+ if (link -> imsg) {
+ failover_message_dereference (&link->imsg,
+ MDL);
+ }
+ link -> state = dhcp_flink_disconnected;
+ log_info ("message length wait: %s",
+ isc_result_totext (status));
+ omapi_disconnect (c, 1);
+ /* XXX just blow away the protocol state now?
+ XXX or will disconnect blow it away? */
+ return ISC_R_UNEXPECTED;
+ }
+ memset (link -> imsg, 0, sizeof (failover_message_t));
+ link -> imsg -> refcnt = 1;
+ /* Get the length: */
+ omapi_connection_get_uint16 (c, &link -> imsg_len);
+ link -> imsg_count = 0; /* Bytes read. */
+
+ /* Ensure the message is of valid length. */
+ if (link->imsg_len < DHCP_FAILOVER_MIN_MESSAGE_SIZE ||
+ link->imsg_len > DHCP_FAILOVER_MAX_MESSAGE_SIZE) {
+ status = ISC_R_UNEXPECTED;
+ goto dhcp_flink_fail;
+ }
+
+ if ((omapi_connection_require (c, link -> imsg_len - 2U)) !=
+ ISC_R_SUCCESS)
+ break;
+ case dhcp_flink_message_wait:
+ /* Read in the message. At this point we have the
+ entire message in the input buffer. For each
+ incoming value ID, set a bit in the bitmask
+ indicating that we've gotten it. Maybe flag an
+ error message if the bit is already set. Once
+ we're done reading, we can check the bitmask to
+ make sure that the required fields for each message
+ have been included. */
+
+ link -> imsg_count += 2; /* Count the length as read. */
+
+ /* Get message type. */
+ omapi_connection_copyout (&link -> imsg -> type, c, 1);
+ link -> imsg_count++;
+
+ /* Get message payload offset. */
+ omapi_connection_copyout (&link -> imsg_payoff, c, 1);
+ link -> imsg_count++;
+
+ /* Get message time. */
+ omapi_connection_get_uint32 (c, &link -> imsg -> time);
+ link -> imsg_count += 4;
+
+ /* Get transaction ID. */
+ omapi_connection_get_uint32 (c, &link -> imsg -> xid);
+ link -> imsg_count += 4;
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+# if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
+ if (link->imsg->type == FTM_CONTACT)
+ goto skip_contact;
+# endif
+ log_info ("link: message %s payoff %d time %ld xid %ld",
+ dhcp_failover_message_name (link -> imsg -> type),
+ link -> imsg_payoff,
+ (unsigned long)link -> imsg -> time,
+ (unsigned long)link -> imsg -> xid);
+# if !defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
+ skip_contact:
+# endif
+#endif
+ /* Skip over any portions of the message header that we
+ don't understand. */
+ if (link -> imsg_payoff - link -> imsg_count) {
+ omapi_connection_copyout ((unsigned char *)0, c,
+ (link -> imsg_payoff -
+ link -> imsg_count));
+ link -> imsg_count = link -> imsg_payoff;
+ }
+
+ /* Now start sucking options off the wire. */
+ while (link -> imsg_count < link -> imsg_len) {
+ status = do_a_failover_option (c, link);
+ if (status != ISC_R_SUCCESS)
+ goto dhcp_flink_fail;
+ }
+
+ /* If it's a connect message, try to associate it with
+ a state object. */
+ /* XXX this should be authenticated! */
+ if (link -> imsg -> type == FTM_CONNECT) {
+ const char *errmsg;
+ int reason;
+
+ if (!(link->imsg->options_present &
+ FTB_RELATIONSHIP_NAME)) {
+ errmsg = "missing relationship-name";
+ reason = FTR_INVALID_PARTNER;
+ goto badconnect;
+ }
+
+ /* See if we can find a failover_state object that
+ matches this connection. This message should only
+ be received by a secondary from a primary. */
+ for (s = failover_states; s; s = s -> next) {
+ if (dhcp_failover_state_match_by_name(s,
+ &link->imsg->relationship_name))
+ state = s;
+ }
+
+ /* If we can't find a failover protocol state
+ for this remote host, drop the connection */
+ if (!state) {
+ errmsg = "unknown failover relationship name";
+ reason = FTR_INVALID_PARTNER;
+
+ badconnect:
+ /* XXX Send a refusal message first?
+ XXX Look in protocol spec for guidance. */
+
+ if (state != NULL) {
+ sname = state->name;
+ slen = strlen(sname);
+ } else if (link->imsg->options_present &
+ FTB_RELATIONSHIP_NAME) {
+ sname = (char *)link->imsg->
+ relationship_name.data;
+ slen = link->imsg->relationship_name.count;
+ } else {
+ sname = "unknown";
+ slen = strlen(sname);
+ }
+
+ log_error("Failover CONNECT from %.*s: %s",
+ slen, sname, errmsg);
+ dhcp_failover_send_connectack
+ ((omapi_object_t *)link, state,
+ reason, errmsg);
+ log_info ("failover: disconnect: %s", errmsg);
+ omapi_disconnect (c, 0);
+ link -> state = dhcp_flink_disconnected;
+ return ISC_R_SUCCESS;
+ }
+
+ if ((cur_time > link -> imsg -> time &&
+ cur_time - link -> imsg -> time > 60) ||
+ (cur_time < link -> imsg -> time &&
+ link -> imsg -> time - cur_time > 60)) {
+ errmsg = "time offset too large";
+ reason = FTR_TIMEMISMATCH;
+ goto badconnect;
+ }
+
+ if (!(link -> imsg -> options_present & FTB_HBA) ||
+ link -> imsg -> hba.count != 32) {
+ errmsg = "invalid HBA";
+ reason = FTR_HBA_CONFLICT; /* XXX */
+ goto badconnect;
+ }
+ if (state -> hba)
+ dfree (state -> hba, MDL);
+ state -> hba = dmalloc (32, MDL);
+ if (!state -> hba) {
+ errmsg = "no memory";
+ reason = FTR_MISC_REJECT;
+ goto badconnect;
+ }
+ memcpy (state -> hba, link -> imsg -> hba.data, 32);
+
+ if (!link -> state_object)
+ dhcp_failover_state_reference
+ (&link -> state_object, state, MDL);
+ if (!link -> peer_address)
+ option_cache_reference
+ (&link -> peer_address,
+ state -> partner.address, MDL);
+ }
+
+ /* If we don't have a state object at this point, it's
+ some kind of bogus situation, so just drop the
+ connection. */
+ if (!link -> state_object) {
+ log_info ("failover: connect: no matching state.");
+ omapi_disconnect (c, 1);
+ link -> state = dhcp_flink_disconnected;
+ return DHCP_R_INVALIDARG;
+ }
+
+ /* Once we have the entire message, and we've validated
+ it as best we can here, pass it to the parent. */
+ omapi_signal ((omapi_object_t *)link -> state_object,
+ "message", link);
+ link -> state = dhcp_flink_message_length_wait;
+ if (link -> imsg)
+ failover_message_dereference (&link -> imsg, MDL);
+ /* XXX This is dangerous because we could get into a tight
+ XXX loop reading input without servicing any other stuff.
+ XXX There needs to be a way to relinquish control but
+ XXX get it back immediately if there's no other work to
+ XXX do. */
+ if ((omapi_connection_require (c, 2)) == ISC_R_SUCCESS)
+ goto next_message;
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t do_a_failover_option (c, link)
+ omapi_object_t *c;
+ dhcp_failover_link_t *link;
+{
+ u_int16_t option_code;
+ u_int16_t option_len;
+ unsigned char *op;
+ unsigned op_size;
+ unsigned op_count;
+ int i;
+
+ if (link -> imsg_count + 2 > link -> imsg_len) {
+ log_error ("FAILOVER: message overflow at option code.");
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* Get option code. */
+ omapi_connection_get_uint16 (c, &option_code);
+ link -> imsg_count += 2;
+
+ if (link -> imsg_count + 2 > link -> imsg_len) {
+ log_error ("FAILOVER: message overflow at length.");
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* Get option length. */
+ omapi_connection_get_uint16 (c, &option_len);
+ link -> imsg_count += 2;
+
+ if (link -> imsg_count + option_len > link -> imsg_len) {
+ log_error ("FAILOVER: message overflow at data.");
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* If it's an unknown code, skip over it. */
+ if ((option_code > FTO_MAX) ||
+ (ft_options[option_code].type == FT_UNDEF)) {
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ log_debug (" option code %d (%s) len %d (not recognized)",
+ option_code,
+ dhcp_failover_option_name (option_code),
+ option_len);
+#endif
+ omapi_connection_copyout ((unsigned char *)0, c, option_len);
+ link -> imsg_count += option_len;
+ return ISC_R_SUCCESS;
+ }
+
+ /* If it's the digest, do it now. */
+ if (ft_options [option_code].type == FT_DIGEST) {
+ link -> imsg_count += option_len;
+ if (link -> imsg_count != link -> imsg_len) {
+ log_error ("FAILOVER: digest not at end of message");
+ return DHCP_R_PROTOCOLERROR;
+ }
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ log_debug (" option %s len %d",
+ ft_options [option_code].name, option_len);
+#endif
+ /* For now, just dump it. */
+ omapi_connection_copyout ((unsigned char *)0, c, option_len);
+ return ISC_R_SUCCESS;
+ }
+
+ /* Only accept an option once. */
+ if (link -> imsg -> options_present & ft_options [option_code].bit) {
+ log_error ("FAILOVER: duplicate option %s",
+ ft_options [option_code].name);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ /* Make sure the option is appropriate for this type of message.
+ Really, any option is generally allowed for any message, and the
+ cases where this is not true are too complicated to represent in
+ this way - what this code is doing is to just avoid saving the
+ value of an option we don't have any way to use, which allows
+ us to make the failover_message structure smaller. */
+ if (ft_options [option_code].bit &&
+ !(fto_allowed [link -> imsg -> type] &
+ ft_options [option_code].bit)) {
+ omapi_connection_copyout ((unsigned char *)0, c, option_len);
+ link -> imsg_count += option_len;
+ return ISC_R_SUCCESS;
+ }
+
+ /* Figure out how many elements, how big they are, and where
+ to store them. */
+ if (ft_options [option_code].num_present) {
+ /* If this option takes a fixed number of elements,
+ we expect the space for them to be preallocated,
+ and we can just read the data in. */
+
+ op = ((unsigned char *)link -> imsg) +
+ ft_options [option_code].offset;
+ op_size = ft_sizes [ft_options [option_code].type];
+ op_count = ft_options [option_code].num_present;
+
+ if (option_len != op_size * op_count) {
+ log_error ("FAILOVER: option size (%d:%d), option %s",
+ option_len,
+ (ft_sizes [ft_options [option_code].type] *
+ ft_options [option_code].num_present),
+ ft_options [option_code].name);
+ return DHCP_R_PROTOCOLERROR;
+ }
+ } else {
+ failover_option_t *fo;
+
+ /* FT_DDNS* are special - one or two bytes of status
+ followed by the client FQDN. */
+ if (ft_options [option_code].type == FT_DDNS1 ||
+ ft_options [option_code].type == FT_DDNS1) {
+ ddns_fqdn_t *ddns =
+ ((ddns_fqdn_t *)
+ (((char *)link -> imsg) +
+ ft_options [option_code].offset));
+
+ op_count = (ft_options [option_code].type == FT_DDNS1
+ ? 1 : 2);
+
+ omapi_connection_copyout (&ddns -> codes [0],
+ c, op_count);
+ link -> imsg_count += op_count;
+ if (op_count == 1)
+ ddns -> codes [1] = 0;
+ op_size = 1;
+ op_count = option_len - op_count;
+
+ ddns -> length = op_count;
+ ddns -> data = dmalloc (op_count, MDL);
+ if (!ddns -> data) {
+ log_error ("FAILOVER: no memory getting%s(%d)",
+ " DNS data ", op_count);
+
+ /* Actually, NO_MEMORY, but if we lose here
+ we have to drop the connection. */
+ return DHCP_R_PROTOCOLERROR;
+ }
+ omapi_connection_copyout (ddns -> data, c, op_count);
+ goto out;
+ }
+
+ /* A zero for num_present means that any number of
+ elements can appear, so we have to figure out how
+ many we got from the length of the option, and then
+ fill out a failover_option structure describing the
+ data. */
+ op_size = ft_sizes [ft_options [option_code].type];
+
+ /* Make sure that option data length is a multiple of the
+ size of the data type being sent. */
+ if (op_size > 1 && option_len % op_size) {
+ log_error ("FAILOVER: option_len %d not %s%d",
+ option_len, "multiple of ", op_size);
+ return DHCP_R_PROTOCOLERROR;
+ }
+
+ op_count = option_len / op_size;
+
+ fo = ((failover_option_t *)
+ (((char *)link -> imsg) +
+ ft_options [option_code].offset));
+
+ fo -> count = op_count;
+ fo -> data = dmalloc (option_len, MDL);
+ if (!fo -> data) {
+ log_error ("FAILOVER: no memory getting %s (%d)",
+ "option data", op_count);
+
+ return DHCP_R_PROTOCOLERROR;
+ }
+ op = fo -> data;
+ }
+
+ /* For single-byte message values and multi-byte values that
+ don't need swapping, just read them in all at once. */
+ if (op_size == 1 || ft_options [option_code].type == FT_IPADDR) {
+ omapi_connection_copyout ((unsigned char *)op, c, option_len);
+ link -> imsg_count += option_len;
+
+ /*
+ * As of 3.1.0, many option codes were changed to conform to
+ * draft revision 12 (which alphabetized, then renumbered all
+ * the option codes without preserving the version option code
+ * nor bumping its value). As it turns out, the message codes
+ * for CONNECT and CONNECTACK turn out the same, so it tries
+ * its darndest to connect, and falls short (when TLS_REQUEST
+ * comes up size 2 rather than size 1 as draft revision 12 also
+ * mandates).
+ *
+ * The VENDOR_CLASS code in 3.0.x was 11, which is now the HBA
+ * code. Both work out to be arbitrarily long text-or-byte
+ * strings, so they pass parsing.
+ *
+ * Note that it is possible (or intentional), if highly
+ * improbable, for the HBA bit array to exactly match
+ * isc-V3.0.x. Warning here is not an issue; if it really is
+ * 3.0.x, there will be a protocol error later on. If it isn't
+ * actually 3.0.x, then I guess the lucky user will have to
+ * live with a weird warning.
+ */
+ if ((option_code == 11) && (option_len > 9) &&
+ (strncmp((const char *)op, "isc-V3.0.", 9) == 0)) {
+ log_error("WARNING: failover as of versions 3.1.0 and "
+ "on are not reverse compatible with "
+ "versions 3.0.x.");
+ }
+
+ goto out;
+ }
+
+ /* For values that require swapping, read them in one at a time
+ using routines that swap bytes. */
+ for (i = 0; i < op_count; i++) {
+ switch (ft_options [option_code].type) {
+ case FT_UINT32:
+ omapi_connection_get_uint32 (c, (u_int32_t *)op);
+ op += 4;
+ link -> imsg_count += 4;
+ break;
+
+ case FT_UINT16:
+ omapi_connection_get_uint16 (c, (u_int16_t *)op);
+ op += 2;
+ link -> imsg_count += 2;
+ break;
+
+ default:
+ /* Everything else should have been handled
+ already. */
+ log_error ("FAILOVER: option %s: bad type %d",
+ ft_options [option_code].name,
+ ft_options [option_code].type);
+ return DHCP_R_PROTOCOLERROR;
+ }
+ }
+ out:
+ /* Remember that we got this option. */
+ link -> imsg -> options_present |= ft_options [option_code].bit;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_link_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+
+ /* Never valid to set these. */
+ if (!omapi_ds_strcmp (name, "link-port") ||
+ !omapi_ds_strcmp (name, "link-name") ||
+ !omapi_ds_strcmp (name, "link-state"))
+ return ISC_R_NOPERM;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_failover_link_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ dhcp_failover_link_t *link;
+
+ if (h -> type != omapi_type_protocol)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)h;
+
+ if (!omapi_ds_strcmp (name, "link-port")) {
+ return omapi_make_int_value (value, name,
+ (int)link -> peer_port, MDL);
+ } else if (!omapi_ds_strcmp (name, "link-state")) {
+ if (link -> state < 0 ||
+ link -> state >= dhcp_flink_state_max)
+ return omapi_make_string_value (value, name,
+ "invalid link state",
+ MDL);
+ return omapi_make_string_value
+ (value, name,
+ dhcp_flink_state_names [link -> state], MDL);
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_failover_link_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ dhcp_failover_link_t *link;
+ if (h -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)h;
+
+ if (link -> peer_address)
+ option_cache_dereference (&link -> peer_address, file, line);
+ if (link -> imsg)
+ failover_message_dereference (&link -> imsg, file, line);
+ if (link -> state_object)
+ dhcp_failover_state_dereference (&link -> state_object,
+ file, line);
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t dhcp_failover_link_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *l)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+
+ if (l -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)l;
+
+ status = omapi_connection_put_name (c, "link-port");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (int));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, link -> peer_port);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "link-state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (link -> state < 0 ||
+ link -> state >= dhcp_flink_state_max)
+ status = omapi_connection_put_string (c, "invalid link state");
+ else
+ status = (omapi_connection_put_string
+ (c, dhcp_flink_state_names [link -> state]));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (link -> inner && link -> inner -> type -> stuff_values)
+ return (*(link -> inner -> type -> stuff_values)) (c, id,
+ link -> inner);
+ return ISC_R_SUCCESS;
+}
+
+/* Set up a listener for the omapi protocol. The handle stored points to
+ a listener object, not a protocol object. */
+
+isc_result_t dhcp_failover_listen (omapi_object_t *h)
+{
+ isc_result_t status;
+ dhcp_failover_listener_t *obj, *l;
+ omapi_value_t *value = (omapi_value_t *)0;
+ omapi_addr_t local_addr;
+ unsigned long port;
+
+ status = omapi_get_value_str (h, (omapi_object_t *)0,
+ "local-port", &value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (!value -> value) {
+ omapi_value_dereference (&value, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ status = omapi_get_int_value (&port, value -> value);
+ omapi_value_dereference (&value, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ local_addr.port = port;
+
+ status = omapi_get_value_str (h, (omapi_object_t *)0,
+ "local-address", &value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (!value -> value) {
+ nogood:
+ omapi_value_dereference (&value, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ if (value -> value -> type != omapi_datatype_data ||
+ value -> value -> u.buffer.len != sizeof (struct in_addr))
+ goto nogood;
+
+ memcpy (local_addr.address, value -> value -> u.buffer.value,
+ value -> value -> u.buffer.len);
+ local_addr.addrlen = value -> value -> u.buffer.len;
+ local_addr.addrtype = AF_INET;
+
+ omapi_value_dereference (&value, MDL);
+
+ /* Are we already listening on this port and address? */
+ for (l = failover_listeners; l; l = l -> next) {
+ if (l -> address.port == local_addr.port &&
+ l -> address.addrtype == local_addr.addrtype &&
+ l -> address.addrlen == local_addr.addrlen &&
+ !memcmp (l -> address.address, local_addr.address,
+ local_addr.addrlen))
+ break;
+ }
+ /* Already listening. */
+ if (l)
+ return ISC_R_SUCCESS;
+
+ obj = (dhcp_failover_listener_t *)0;
+ status = dhcp_failover_listener_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ obj -> address = local_addr;
+
+ status = omapi_listen_addr ((omapi_object_t *)obj, &obj -> address, 1);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_listener_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_listener_dereference (&obj, MDL);
+ return status;
+ }
+
+ /* Put this listener on the list. */
+ if (failover_listeners) {
+ dhcp_failover_listener_reference (&obj -> next,
+ failover_listeners, MDL);
+ dhcp_failover_listener_dereference (&failover_listeners, MDL);
+ }
+ dhcp_failover_listener_reference (&failover_listeners, obj, MDL);
+
+ return dhcp_failover_listener_dereference (&obj, MDL);
+}
+
+/* Signal handler for protocol listener - if we get a connect signal,
+ create a new protocol connection, otherwise pass the signal down. */
+
+isc_result_t dhcp_failover_listener_signal (omapi_object_t *o,
+ const char *name, va_list ap)
+{
+ isc_result_t status;
+ omapi_connection_object_t *c;
+ dhcp_failover_link_t *obj;
+ dhcp_failover_listener_t *p;
+ dhcp_failover_state_t *s, *state = (dhcp_failover_state_t *)0;
+
+ if (!o || o -> type != dhcp_type_failover_listener)
+ return DHCP_R_INVALIDARG;
+ p = (dhcp_failover_listener_t *)o;
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "connect")) {
+ if (p -> inner && p -> inner -> type -> signal_handler)
+ return (*(p -> inner -> type -> signal_handler))
+ (p -> inner, name, ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ c = va_arg (ap, omapi_connection_object_t *);
+ if (!c || c -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ /* See if we can find a failover_state object that
+ matches this connection. */
+ for (s = failover_states; s; s = s -> next) {
+ if (dhcp_failover_state_match
+ (s, (u_int8_t *)&c -> remote_addr.sin_addr,
+ sizeof c -> remote_addr.sin_addr)) {
+ state = s;
+ break;
+ }
+ }
+ if (!state) {
+ log_info ("failover: listener: no matching state");
+ omapi_disconnect ((omapi_object_t *)c, 1);
+ return(ISC_R_NOTFOUND);
+ }
+
+ obj = (dhcp_failover_link_t *)0;
+ status = dhcp_failover_link_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ obj -> peer_port = ntohs (c -> remote_addr.sin_port);
+
+ status = omapi_object_reference (&obj -> outer,
+ (omapi_object_t *)c, MDL);
+ if (status != ISC_R_SUCCESS) {
+ lose:
+ dhcp_failover_link_dereference (&obj, MDL);
+ log_info ("failover: listener: picayune failure.");
+ omapi_disconnect ((omapi_object_t *)c, 1);
+ return status;
+ }
+
+ status = omapi_object_reference (&c -> inner,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto lose;
+
+ status = dhcp_failover_state_reference (&obj -> state_object,
+ state, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto lose;
+
+ omapi_signal_in ((omapi_object_t *)obj, "connect");
+
+ return dhcp_failover_link_dereference (&obj, MDL);
+}
+
+isc_result_t dhcp_failover_listener_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != dhcp_type_failover_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_failover_listener_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != dhcp_type_failover_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_failover_listener_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ dhcp_failover_listener_t *l;
+
+ if (h -> type != dhcp_type_failover_listener)
+ return DHCP_R_INVALIDARG;
+ l = (dhcp_failover_listener_t *)h;
+ if (l -> next)
+ dhcp_failover_listener_dereference (&l -> next, file, line);
+
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t dhcp_failover_listener_stuff (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *p)
+{
+ if (p -> type != dhcp_type_failover_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (p -> inner && p -> inner -> type -> stuff_values)
+ return (*(p -> inner -> type -> stuff_values)) (c, id,
+ p -> inner);
+ return ISC_R_SUCCESS;
+}
+
+/* Set up master state machine for the failover protocol. */
+
+isc_result_t dhcp_failover_register (omapi_object_t *h)
+{
+ isc_result_t status;
+ dhcp_failover_state_t *obj;
+ unsigned long port;
+ omapi_value_t *value = (omapi_value_t *)0;
+
+ status = omapi_get_value_str (h, (omapi_object_t *)0,
+ "local-port", &value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (!value -> value) {
+ omapi_value_dereference (&value, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ status = omapi_get_int_value (&port, value -> value);
+ omapi_value_dereference (&value, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ obj = (dhcp_failover_state_t *)0;
+ dhcp_failover_state_allocate (&obj, MDL);
+ obj -> me.port = port;
+
+ status = omapi_listen ((omapi_object_t *)obj, port, 1);
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_state_dereference (&obj, MDL);
+ return status;
+ }
+
+ status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_state_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ dhcp_failover_state_dereference (&obj, MDL);
+ return status;
+}
+
+/* Signal handler for protocol state machine. */
+
+isc_result_t dhcp_failover_state_signal (omapi_object_t *o,
+ const char *name, va_list ap)
+{
+ isc_result_t status;
+ dhcp_failover_state_t *state;
+ dhcp_failover_link_t *link;
+ struct timeval tv;
+
+ if (!o || o -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+ state = (dhcp_failover_state_t *)o;
+
+ /* Not a signal we recognize? */
+ if (strcmp (name, "disconnect") &&
+ strcmp (name, "message")) {
+ if (state -> inner && state -> inner -> type -> signal_handler)
+ return (*(state -> inner -> type -> signal_handler))
+ (state -> inner, name, ap);
+ return ISC_R_NOTFOUND;
+ }
+
+ /* Handle connect signals by seeing what state we're in
+ and potentially doing a state transition. */
+ if (!strcmp (name, "disconnect")) {
+ link = va_arg (ap, dhcp_failover_link_t *);
+
+ dhcp_failover_link_dereference (&state -> link_to_peer, MDL);
+ dhcp_failover_state_transition (state, "disconnect");
+ if (state -> i_am == primary) {
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +90 %s",
+ "dhcp_failover_reconnect");
+#endif
+ tv . tv_sec = cur_time + 90;
+ tv . tv_usec = 0;
+ add_timeout (&tv, dhcp_failover_reconnect,
+ state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)
+ dhcp_failover_state_dereference);
+ }
+ } else if (!strcmp (name, "message")) {
+ link = va_arg (ap, dhcp_failover_link_t *);
+
+ if (link -> imsg -> type == FTM_CONNECT) {
+ /* If we already have a link to the peer, it must be
+ dead, so drop it.
+ XXX Is this the right thing to do?
+ XXX Probably not - what if both peers start at
+ XXX the same time? */
+ if (state -> link_to_peer) {
+ dhcp_failover_send_connectack
+ ((omapi_object_t *)link, state,
+ FTR_DUP_CONNECTION,
+ "already connected");
+ omapi_disconnect (link -> outer, 1);
+ return ISC_R_SUCCESS;
+ }
+ if (!(link -> imsg -> options_present & FTB_MCLT)) {
+ dhcp_failover_send_connectack
+ ((omapi_object_t *)link, state,
+ FTR_INVALID_MCLT,
+ "no MCLT provided");
+ omapi_disconnect (link -> outer, 1);
+ return ISC_R_SUCCESS;
+ }
+
+ dhcp_failover_link_reference (&state -> link_to_peer,
+ link, MDL);
+ status = (dhcp_failover_send_connectack
+ ((omapi_object_t *)link, state, 0, 0));
+ if (status != ISC_R_SUCCESS) {
+ dhcp_failover_link_dereference
+ (&state -> link_to_peer, MDL);
+ log_info ("dhcp_failover_send_connectack: %s",
+ isc_result_totext (status));
+ omapi_disconnect (link -> outer, 1);
+ return ISC_R_SUCCESS;
+ }
+ if (link -> imsg -> options_present & FTB_MAX_UNACKED)
+ state -> partner.max_flying_updates =
+ link -> imsg -> max_unacked;
+ if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
+ state -> partner.max_response_delay =
+ link -> imsg -> receive_timer;
+ state -> mclt = link -> imsg -> mclt;
+ dhcp_failover_send_state (state);
+ cancel_timeout (dhcp_failover_link_startup_timeout,
+ link);
+ } else if (link -> imsg -> type == FTM_CONNECTACK) {
+ const char *errmsg;
+ char errbuf[1024];
+ int reason;
+
+ cancel_timeout (dhcp_failover_link_startup_timeout,
+ link);
+
+ if (!(link->imsg->options_present &
+ FTB_RELATIONSHIP_NAME)) {
+ errmsg = "missing relationship-name";
+ reason = FTR_INVALID_PARTNER;
+ goto badconnectack;
+ }
+
+ if (link->imsg->options_present & FTB_REJECT_REASON) {
+ /* XXX: add message option to text output. */
+ log_error ("Failover CONNECT to %s rejected: %s",
+ state ? state->name : "unknown",
+ (dhcp_failover_reject_reason_print
+ (link -> imsg -> reject_reason)));
+ /* XXX print message from peer if peer sent message. */
+ omapi_disconnect (link -> outer, 1);
+ return ISC_R_SUCCESS;
+ }
+
+ if (!dhcp_failover_state_match_by_name(state,
+ &link->imsg->relationship_name)) {
+ /* XXX: Overflow results in log truncation, safe. */
+ snprintf(errbuf, sizeof(errbuf), "remote failover "
+ "relationship name %.*s does not match",
+ (int)link->imsg->relationship_name.count,
+ link->imsg->relationship_name.data);
+ errmsg = errbuf;
+ reason = FTR_INVALID_PARTNER;
+ badconnectack:
+ log_error("Failover CONNECTACK from %s: %s",
+ state->name, errmsg);
+ dhcp_failover_send_disconnect ((omapi_object_t *)link,
+ reason, errmsg);
+ omapi_disconnect (link -> outer, 0);
+ return ISC_R_SUCCESS;
+ }
+
+ if (state -> link_to_peer) {
+ errmsg = "already connected";
+ reason = FTR_DUP_CONNECTION;
+ goto badconnectack;
+ }
+
+ if ((cur_time > link -> imsg -> time &&
+ cur_time - link -> imsg -> time > 60) ||
+ (cur_time < link -> imsg -> time &&
+ link -> imsg -> time - cur_time > 60)) {
+ errmsg = "time offset too large";
+ reason = FTR_TIMEMISMATCH;
+ goto badconnectack;
+ }
+
+ dhcp_failover_link_reference (&state -> link_to_peer,
+ link, MDL);
+#if 0
+ /* XXX This is probably the right thing to do, but
+ XXX for release three, to make the smallest possible
+ XXX change, we are doing this when the peer state
+ XXX changes instead. */
+ if (state -> me.state == startup)
+ dhcp_failover_set_state (state,
+ state -> saved_state);
+ else
+#endif
+ dhcp_failover_send_state (state);
+
+ if (link -> imsg -> options_present & FTB_MAX_UNACKED)
+ state -> partner.max_flying_updates =
+ link -> imsg -> max_unacked;
+ if (link -> imsg -> options_present & FTB_RECEIVE_TIMER)
+ state -> partner.max_response_delay =
+ link -> imsg -> receive_timer;
+#if defined (DEBUG_FAILOVER_CONTACT_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)state -> partner.max_response_delay / 3,
+ "dhcp_failover_send_contact");
+#endif
+ tv . tv_sec = cur_time +
+ (int)state -> partner.max_response_delay / 3;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_send_contact, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+#if defined (DEBUG_FAILOVER_CONTACT_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)state -> me.max_response_delay,
+ "dhcp_failover_timeout");
+#endif
+ tv . tv_sec = cur_time +
+ (int)state -> me.max_response_delay;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_timeout, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ } else if (link -> imsg -> type == FTM_DISCONNECT) {
+ if (link -> imsg -> reject_reason) {
+ log_error ("Failover DISCONNECT from %s: %s",
+ state ? state->name : "unknown",
+ (dhcp_failover_reject_reason_print
+ (link -> imsg -> reject_reason)));
+ }
+ omapi_disconnect (link -> outer, 1);
+ } else if (link -> imsg -> type == FTM_BNDUPD) {
+ dhcp_failover_process_bind_update (state,
+ link -> imsg);
+ } else if (link -> imsg -> type == FTM_BNDACK) {
+ dhcp_failover_process_bind_ack (state, link -> imsg);
+ } else if (link -> imsg -> type == FTM_UPDREQ) {
+ dhcp_failover_process_update_request (state,
+ link -> imsg);
+ } else if (link -> imsg -> type == FTM_UPDREQALL) {
+ dhcp_failover_process_update_request_all
+ (state, link -> imsg);
+ } else if (link -> imsg -> type == FTM_UPDDONE) {
+ dhcp_failover_process_update_done (state,
+ link -> imsg);
+ } else if (link -> imsg -> type == FTM_POOLREQ) {
+ dhcp_failover_pool_reqbalance(state);
+ } else if (link -> imsg -> type == FTM_POOLRESP) {
+ log_info ("pool response: %ld leases",
+ (unsigned long)
+ link -> imsg -> addresses_transferred);
+ } else if (link -> imsg -> type == FTM_STATE) {
+ dhcp_failover_peer_state_changed (state,
+ link -> imsg);
+ }
+
+ /* Add a timeout so that if the partner doesn't send
+ another message for the maximum transmit idle time
+ plus a grace of one second, we close the
+ connection. */
+ if (state -> link_to_peer &&
+ state -> link_to_peer == link &&
+ state -> link_to_peer -> state != dhcp_flink_disconnected)
+ {
+#if defined (DEBUG_FAILOVER_CONTACT_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)state -> me.max_response_delay,
+ "dhcp_failover_timeout");
+#endif
+ tv . tv_sec = cur_time +
+ (int)state -> me.max_response_delay;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_timeout, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+
+ }
+ }
+
+ /* Handle all the events we care about... */
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_state_transition (dhcp_failover_state_t *state,
+ const char *name)
+{
+ isc_result_t status;
+
+ /* XXX Check these state transitions against the spec! */
+ if (!strcmp (name, "disconnect")) {
+ if (state -> link_to_peer) {
+ log_info ("peer %s: disconnected", state -> name);
+ if (state -> link_to_peer -> state_object)
+ dhcp_failover_state_dereference
+ (&state -> link_to_peer -> state_object, MDL);
+ dhcp_failover_link_dereference (&state -> link_to_peer,
+ MDL);
+ }
+ cancel_timeout (dhcp_failover_send_contact, state);
+ cancel_timeout (dhcp_failover_timeout, state);
+ cancel_timeout (dhcp_failover_startup_timeout, state);
+
+ switch (state -> me.state == startup ?
+ state -> saved_state : state -> me.state) {
+ /* In these situations, we remain in the current
+ * state, or if in startup enter those states.
+ */
+ case communications_interrupted:
+ case conflict_done:
+ case partner_down:
+ case paused:
+ case recover:
+ case recover_done:
+ case recover_wait:
+ case resolution_interrupted:
+ case shut_down:
+ /* Already in the right state? */
+ if (state -> me.state == startup)
+ return (dhcp_failover_set_state
+ (state, state -> saved_state));
+ return ISC_R_SUCCESS;
+
+ case potential_conflict:
+ return dhcp_failover_set_state
+ (state, resolution_interrupted);
+
+ case normal:
+ return dhcp_failover_set_state
+ (state, communications_interrupted);
+
+ case unknown_state:
+ return dhcp_failover_set_state
+ (state, resolution_interrupted);
+
+ default:
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break; /* can't happen. */
+ }
+ } else if (!strcmp (name, "connect")) {
+ switch (state -> me.state) {
+ case communications_interrupted:
+ status = dhcp_failover_set_state (state, normal);
+ dhcp_failover_send_updates (state);
+ return status;
+
+ case resolution_interrupted:
+ return dhcp_failover_set_state (state,
+ potential_conflict);
+
+ case conflict_done:
+ case partner_down:
+ case potential_conflict:
+ case normal:
+ case recover:
+ case shut_down:
+ case paused:
+ case unknown_state:
+ case recover_done:
+ case startup:
+ case recover_wait:
+ return dhcp_failover_send_state (state);
+
+ default:
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break;
+ }
+ } else if (!strcmp (name, "startup")) {
+ dhcp_failover_set_state (state, startup);
+ return ISC_R_SUCCESS;
+ } else if (!strcmp (name, "connect-timeout")) {
+ switch (state -> me.state) {
+ case communications_interrupted:
+ case partner_down:
+ case resolution_interrupted:
+ case paused:
+ case startup:
+ case shut_down:
+ case conflict_done:
+ return ISC_R_SUCCESS;
+
+ case normal:
+ case recover:
+ case recover_wait:
+ case recover_done:
+ case unknown_state:
+ return dhcp_failover_set_state
+ (state, communications_interrupted);
+
+ case potential_conflict:
+ return dhcp_failover_set_state
+ (state, resolution_interrupted);
+
+ default:
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break;
+ }
+ }
+ return DHCP_R_INVALIDARG;
+}
+
+isc_result_t dhcp_failover_set_service_state (dhcp_failover_state_t *state)
+{
+ switch (state -> me.state) {
+ case unknown_state:
+ state -> service_state = not_responding;
+ state -> nrr = " (my state unknown)";
+ break;
+
+ case partner_down:
+ state -> service_state = service_partner_down;
+ state -> nrr = "";
+ break;
+
+ case normal:
+ state -> service_state = cooperating;
+ state -> nrr = "";
+ break;
+
+ case communications_interrupted:
+ state -> service_state = not_cooperating;
+ state -> nrr = "";
+ break;
+
+ case resolution_interrupted:
+ case potential_conflict:
+ case conflict_done:
+ state -> service_state = not_responding;
+ state -> nrr = " (resolving conflicts)";
+ break;
+
+ case recover:
+ state -> service_state = not_responding;
+ state -> nrr = " (recovering)";
+ break;
+
+ case shut_down:
+ state -> service_state = not_responding;
+ state -> nrr = " (shut down)";
+ break;
+
+ case paused:
+ state -> service_state = not_responding;
+ state -> nrr = " (paused)";
+ break;
+
+ case recover_wait:
+ state -> service_state = not_responding;
+ state -> nrr = " (recover wait)";
+ break;
+
+ case recover_done:
+ state -> service_state = not_responding;
+ state -> nrr = " (recover done)";
+ break;
+
+ case startup:
+ state -> service_state = service_startup;
+ state -> nrr = " (startup)";
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d.\n", MDL);
+ break;
+ }
+
+ /* Some peer states can require us not to respond, even if our
+ state doesn't. */
+ /* XXX hm. I suspect this isn't true anymore. */
+ if (state -> service_state != not_responding) {
+ switch (state -> partner.state) {
+ case partner_down:
+ state -> service_state = not_responding;
+ state -> nrr = " (peer demands: recovering)";
+ break;
+
+ case potential_conflict:
+ case conflict_done:
+ case resolution_interrupted:
+ state -> service_state = not_responding;
+ state -> nrr = " (peer demands: resolving conflicts)";
+ break;
+
+ /* Other peer states don't affect our behaviour. */
+ default:
+ break;
+ }
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_set_state (dhcp_failover_state_t *state,
+ enum failover_state new_state)
+{
+ enum failover_state saved_state;
+ TIME saved_stos;
+ struct pool *p;
+ struct shared_network *s;
+ struct lease *l;
+ struct timeval tv;
+
+ /* If we're in certain states where we're sending updates, and the peer
+ * state changes, we need to re-schedule any pending updates just to
+ * be on the safe side. This results in retransmission.
+ */
+ switch (state -> me.state) {
+ case normal:
+ case potential_conflict:
+ case partner_down:
+ if (state -> ack_queue_tail) {
+ struct lease *lp;
+
+ /* Zap the flags. */
+ for (lp = state -> ack_queue_head; lp; lp = lp -> next_pending)
+ lp -> flags = ((lp -> flags & ~ON_ACK_QUEUE) |
+ ON_UPDATE_QUEUE);
+
+ /* Now hook the ack queue to the beginning of the update
+ queue. */
+ if (state -> update_queue_head) {
+ lease_reference (&state -> ack_queue_tail -> next_pending,
+ state -> update_queue_head, MDL);
+ lease_dereference (&state -> update_queue_head, MDL);
+ }
+ lease_reference (&state -> update_queue_head,
+ state -> ack_queue_head, MDL);
+ if (!state -> update_queue_tail) {
+#if defined (POINTER_DEBUG)
+ if (state -> ack_queue_tail -> next_pending) {
+ log_error ("next pending on ack queue tail.");
+ abort ();
+ }
+#endif
+ lease_reference (&state -> update_queue_tail,
+ state -> ack_queue_tail, MDL);
+ }
+ lease_dereference (&state -> ack_queue_tail, MDL);
+ lease_dereference (&state -> ack_queue_head, MDL);
+ state -> cur_unacked_updates = 0;
+ }
+ /* We will re-queue a timeout later, if applicable. */
+ cancel_timeout (dhcp_failover_keepalive, state);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Tentatively make the transition. */
+ saved_state = state -> me.state;
+ saved_stos = state -> me.stos;
+
+ /* Keep the old stos if we're going into recover_wait or if we're
+ coming into or out of startup. */
+ if (new_state != recover_wait && new_state != startup &&
+ saved_state != startup)
+ state -> me.stos = cur_time;
+
+ /* If we're in shutdown, peer is in partner_down, and we're moving
+ to recover, we can skip waiting for MCLT to expire. This happens
+ when a server is moved administratively into shutdown prior to
+ actually shutting down. Of course, if there are any updates
+ pending we can't actually do this. */
+ if (new_state == recover && saved_state == shut_down &&
+ state -> partner.state == partner_down &&
+ !state -> update_queue_head && !state -> ack_queue_head)
+ state -> me.stos = cur_time - state -> mclt;
+
+ state -> me.state = new_state;
+ if (new_state == startup && saved_state != startup)
+ state -> saved_state = saved_state;
+
+ /* If we can't record the new state, we can't make a state transition. */
+ if (!write_failover_state (state) || !commit_leases ()) {
+ log_error ("Unable to record current failover state for %s",
+ state -> name);
+ state -> me.state = saved_state;
+ state -> me.stos = saved_stos;
+ return ISC_R_IOERROR;
+ }
+
+ log_info ("failover peer %s: I move from %s to %s",
+ state -> name, dhcp_failover_state_name_print (saved_state),
+ dhcp_failover_state_name_print (state -> me.state));
+
+ /* If we were in startup and we just left it, cancel the timeout. */
+ if (new_state != startup && saved_state == startup)
+ cancel_timeout (dhcp_failover_startup_timeout, state);
+
+ /*
+ * If the state changes for any reason, cancel 'delayed auto state
+ * changes' (currently there is just the one).
+ */
+ cancel_timeout(dhcp_failover_auto_partner_down, state);
+
+ /* Set our service state. */
+ dhcp_failover_set_service_state (state);
+
+ /* Tell the peer about it. */
+ if (state -> link_to_peer)
+ dhcp_failover_send_state (state);
+
+ switch (new_state) {
+ case communications_interrupted:
+ /*
+ * There is an optional feature to automatically enter partner
+ * down after a timer expires, upon entering comms-interrupted.
+ * This feature is generally not safe except in specific
+ * circumstances.
+ *
+ * A zero value (also the default) disables it.
+ */
+ if (state->auto_partner_down == 0)
+ break;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info("add_timeout +%lu dhcp_failover_auto_partner_down",
+ (unsigned long)state->auto_partner_down);
+#endif
+ tv.tv_sec = cur_time + state->auto_partner_down;
+ tv.tv_usec = 0;
+ add_timeout(&tv, dhcp_failover_auto_partner_down, state,
+ (tvref_t)omapi_object_reference,
+ (tvunref_t)omapi_object_dereference);
+ break;
+
+ case normal:
+ /* Upon entering normal state, the server is expected to retransmit
+ * all pending binding updates. This is a good opportunity to
+ * rebalance the pool (potentially making new pending updates),
+ * which also schedules the next pool rebalance.
+ */
+ dhcp_failover_pool_balance(state);
+ dhcp_failover_generate_update_queue(state, 0);
+
+ if (state->update_queue_tail != NULL) {
+ dhcp_failover_send_updates(state);
+ log_info("Sending updates to %s.", state->name);
+ }
+
+ break;
+
+ case potential_conflict:
+ if (state -> i_am == primary)
+ dhcp_failover_send_update_request (state);
+ break;
+
+ case startup:
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +15 %s",
+ "dhcp_failover_startup_timeout");
+#endif
+ tv . tv_sec = cur_time + 15;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_startup_timeout,
+ state,
+ (tvref_t)omapi_object_reference,
+ (tvunref_t)
+ omapi_object_dereference);
+ break;
+
+ /* If we come back in recover_wait and there's still waiting
+ to do, set a timeout. */
+ case recover_wait:
+ if (state -> me.stos + state -> mclt > cur_time) {
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)(cur_time -
+ state -> me.stos + state -> mclt),
+ "dhcp_failover_startup_timeout");
+#endif
+ tv . tv_sec = (int)(state -> me.stos + state -> mclt);
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_recover_done,
+ state,
+ (tvref_t)omapi_object_reference,
+ (tvunref_t)
+ omapi_object_dereference);
+ } else
+ dhcp_failover_recover_done (state);
+ break;
+
+ case recover:
+ /* XXX: We're supposed to calculate if updreq or updreqall is
+ * needed. In theory, we should only have to updreqall if we
+ * are positive we lost our stable storage.
+ */
+ if (state -> link_to_peer)
+ dhcp_failover_send_update_request_all (state);
+ break;
+
+ case partner_down:
+ /* For every expired lease, set a timeout for it to become free. */
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ if (p -> failover_peer == state) {
+ for (l = p->expired ; l ; l = l->next) {
+ l->tsfp = state->me.stos + state->mclt;
+ l->sort_time = (l->tsfp > l->ends) ?
+ l->tsfp : l->ends;
+ }
+ if (p->expired &&
+ (p->expired->sort_time < p->next_event_time)) {
+
+ p->next_event_time = p->expired->sort_time;
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)(cur_time - p->next_event_time),
+ "pool_timer");
+#endif
+ tv.tv_sec = p->next_event_time;
+ tv.tv_usec = 0;
+ add_timeout(&tv, pool_timer, p,
+ (tvref_t)pool_reference,
+ (tvunref_t)pool_dereference);
+ }
+ }
+ }
+ }
+ break;
+
+
+ default:
+ break;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_peer_state_changed (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ enum failover_state previous_state = state -> partner.state;
+ enum failover_state new_state;
+ int startupp;
+
+ new_state = msg -> server_state;
+ startupp = (msg -> server_flags & FTF_SERVER_STARTUP) ? 1 : 0;
+
+ if (state -> partner.state == new_state && state -> me.state) {
+ switch (state -> me.state) {
+ case startup:
+ dhcp_failover_set_state (state, state -> saved_state);
+ return ISC_R_SUCCESS;
+
+ case unknown_state:
+ case normal:
+ case potential_conflict:
+ case recover_done:
+ case shut_down:
+ case paused:
+ case recover_wait:
+ return ISC_R_SUCCESS;
+
+ /* If we get a peer state change when we're
+ disconnected, we always process it. */
+ case partner_down:
+ case communications_interrupted:
+ case resolution_interrupted:
+ case recover:
+ case conflict_done:
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break;
+ }
+ }
+
+ state -> partner.state = new_state;
+
+ log_info ("failover peer %s: peer moves from %s to %s",
+ state -> name,
+ dhcp_failover_state_name_print (previous_state),
+ dhcp_failover_state_name_print (state -> partner.state));
+
+ if (!write_failover_state (state) || !commit_leases ()) {
+ /* This is bad, but it's not fatal. Of course, if we
+ can't write to the lease database, we're not going to
+ get much done anyway. */
+ log_error ("Unable to record current failover state for %s",
+ state -> name);
+ }
+
+ /* Quickly validate the new state as being one of the 13 known
+ * states.
+ */
+ switch (new_state) {
+ case unknown_state:
+ case startup:
+ case normal:
+ case communications_interrupted:
+ case partner_down:
+ case potential_conflict:
+ case recover:
+ case paused:
+ case shut_down:
+ case recover_done:
+ case resolution_interrupted:
+ case conflict_done:
+ case recover_wait:
+ break;
+
+ default:
+ log_error("failover peer %s: Invalid state: %d", state->name,
+ new_state);
+ dhcp_failover_set_state(state, shut_down);
+ return ISC_R_SUCCESS;
+ }
+
+ /* Do any state transitions that are required as a result of the
+ peer's state transition. */
+
+ switch (state -> me.state == startup ?
+ state -> saved_state : state -> me.state) {
+ case normal:
+ switch (new_state) {
+ case normal:
+ dhcp_failover_state_pool_check (state);
+ break;
+
+ case partner_down:
+ if (state -> me.state == startup)
+ dhcp_failover_set_state (state, recover);
+ else
+ dhcp_failover_set_state (state,
+ potential_conflict);
+ break;
+
+ case potential_conflict:
+ case resolution_interrupted:
+ case conflict_done:
+ /* None of these transitions should ever occur. */
+ log_error("Peer %s: Invalid state transition %s "
+ "to %s.", state->name,
+ dhcp_failover_state_name_print(previous_state),
+ dhcp_failover_state_name_print(new_state));
+ dhcp_failover_set_state (state, shut_down);
+ break;
+
+ case recover:
+ case shut_down:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ case paused:
+ dhcp_failover_set_state (state,
+ communications_interrupted);
+ break;
+
+ default:
+ /* recover_wait, recover_done, unknown_state, startup,
+ * communications_interrupted
+ */
+ break;
+ }
+ break;
+
+ case recover:
+ switch (new_state) {
+ case recover:
+ log_info ("failover peer %s: requesting %s",
+ state -> name, "full update from peer");
+ /* Don't send updreqall if we're really in the
+ startup state, because that will result in two
+ being sent. */
+ if (state -> me.state == recover)
+ dhcp_failover_send_update_request_all (state);
+ break;
+
+ case potential_conflict:
+ case resolution_interrupted:
+ case conflict_done:
+ case normal:
+ dhcp_failover_set_state (state, potential_conflict);
+ break;
+
+ case partner_down:
+ case communications_interrupted:
+ /* We're supposed to send an update request at this
+ point. */
+ /* XXX we don't currently have code here to do any
+ XXX clever detection of when we should send an
+ XXX UPDREQALL message rather than an UPDREQ
+ XXX message. What to do, what to do? */
+ /* Currently when we enter recover state, no matter
+ * the reason, we send an UPDREQALL. So, it makes
+ * the most sense to stick to that until something
+ * better is done.
+ * Furthermore, we only want to send the update
+ * request if we are not in startup state.
+ */
+ if (state -> me.state == recover)
+ dhcp_failover_send_update_request_all (state);
+ break;
+
+ case shut_down:
+ /* XXX We're not explicitly told what to do in this
+ XXX case, but this transition is consistent with
+ XXX what is elsewhere in the draft. */
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ /* We can't really do anything in this case. */
+ default:
+ /* paused, recover_done, recover_wait, unknown_state,
+ * startup.
+ */
+ break;
+ }
+ break;
+
+ case potential_conflict:
+ switch (new_state) {
+ case normal:
+ /* This is an illegal transition. */
+ log_error("Peer %s moves to normal during conflict "
+ "resolution - panic, shutting down.",
+ state->name);
+ dhcp_failover_set_state(state, shut_down);
+ break;
+
+ case conflict_done:
+ if (previous_state == potential_conflict)
+ dhcp_failover_send_update_request (state);
+ else
+ log_error("Peer %s: Unexpected move to "
+ "conflict-done.", state->name);
+ break;
+
+ case recover_done:
+ case recover_wait:
+ case potential_conflict:
+ case partner_down:
+ case communications_interrupted:
+ case resolution_interrupted:
+ case paused:
+ break;
+
+ case recover:
+ dhcp_failover_set_state (state, recover);
+ break;
+
+ case shut_down:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ default:
+ /* unknown_state, startup */
+ break;
+ }
+ break;
+
+ case conflict_done:
+ switch (new_state) {
+ case normal:
+ case shut_down:
+ dhcp_failover_set_state(state, new_state);
+ break;
+
+ default:
+ log_fatal("Peer %s: Invalid attempt to move from %s "
+ "to %s while local state is conflict-done.",
+ state->name,
+ dhcp_failover_state_name_print(previous_state),
+ dhcp_failover_state_name_print(new_state));
+ }
+ break;
+
+ case partner_down:
+ /* Take no action if other server is starting up. */
+ if (startupp)
+ break;
+
+ switch (new_state) {
+ /* This is where we should be. */
+ case recover:
+ case recover_wait:
+ break;
+
+ case recover_done:
+ dhcp_failover_set_state (state, normal);
+ break;
+
+ case normal:
+ case potential_conflict:
+ case partner_down:
+ case communications_interrupted:
+ case resolution_interrupted:
+ case conflict_done:
+ dhcp_failover_set_state (state, potential_conflict);
+ break;
+
+ default:
+ /* shut_down, paused, unknown_state, startup */
+ break;
+ }
+ break;
+
+ case communications_interrupted:
+ switch (new_state) {
+ case paused:
+ /* Stick with the status quo. */
+ break;
+
+ /* If we're in communications-interrupted and an
+ amnesic peer connects, go to the partner_down
+ state immediately. */
+ case recover:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ case normal:
+ case communications_interrupted:
+ case recover_done:
+ case recover_wait:
+ /* XXX so we don't need to do this specially in
+ XXX the CONNECT and CONNECTACK handlers. */
+ dhcp_failover_send_updates (state);
+ dhcp_failover_set_state (state, normal);
+ break;
+
+ case potential_conflict:
+ case partner_down:
+ case resolution_interrupted:
+ case conflict_done:
+ dhcp_failover_set_state (state, potential_conflict);
+ break;
+
+ case shut_down:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ default:
+ /* unknown_state, startup */
+ break;
+ }
+ break;
+
+ case resolution_interrupted:
+ switch (new_state) {
+ case normal:
+ case recover:
+ case potential_conflict:
+ case partner_down:
+ case communications_interrupted:
+ case resolution_interrupted:
+ case conflict_done:
+ case recover_done:
+ case recover_wait:
+ dhcp_failover_set_state (state, potential_conflict);
+ break;
+
+ case shut_down:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ default:
+ /* paused, unknown_state, startup */
+ break;
+ }
+ break;
+
+ /* Make no transitions while in recover_wait...just wait. */
+ case recover_wait:
+ break;
+
+ case recover_done:
+ switch (new_state) {
+ case recover_done:
+ log_error("Both servers have entered recover-done!");
+ case normal:
+ dhcp_failover_set_state (state, normal);
+ break;
+
+ case shut_down:
+ dhcp_failover_set_state (state, partner_down);
+ break;
+
+ default:
+ /* potential_conflict, partner_down,
+ * communications_interrupted, resolution_interrupted,
+ * paused, recover, recover_wait, unknown_state,
+ * startup.
+ */
+ break;
+ }
+ break;
+
+ /* We are essentially dead in the water when we're in
+ either shut_down or paused states, and do not do any
+ automatic state transitions. */
+ case shut_down:
+ case paused:
+ break;
+
+ /* XXX: Shouldn't this be a fatal condition? */
+ case unknown_state:
+ break;
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ break;
+
+ }
+
+ /* If we didn't make a transition out of startup as a result of
+ the peer's state change, do it now as a result of the fact that
+ we got a state change from the peer. */
+ if (state -> me.state == startup && state -> saved_state != startup)
+ dhcp_failover_set_state (state, state -> saved_state);
+
+ /* For now, just set the service state based on the peer's state
+ if necessary. */
+ dhcp_failover_set_service_state (state);
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Balance operation manual entry; startup, entrance to normal state. No
+ * sense sending a POOLREQ at this stage; the peer is likely about to schedule
+ * their own rebalance event upon entering normal themselves.
+ */
+static void
+dhcp_failover_pool_balance(dhcp_failover_state_t *state)
+{
+ /* Cancel pending event. */
+ cancel_timeout(dhcp_failover_pool_rebalance, state);
+ state->sched_balance = 0;
+
+ dhcp_failover_pool_dobalance(state, NULL);
+}
+
+/*
+ * Balance operation entry from timer event. Once per timer interval is
+ * the only time we want to emit POOLREQs (asserting an interrupt in our
+ * peer).
+ */
+void
+dhcp_failover_pool_rebalance(void *failover_state)
+{
+ dhcp_failover_state_t *state;
+ isc_boolean_t sendreq = ISC_FALSE;
+
+ state = (dhcp_failover_state_t *)failover_state;
+
+ /* Clear scheduled event indicator. */
+ state->sched_balance = 0;
+
+ if (dhcp_failover_pool_dobalance(state, &sendreq))
+ dhcp_failover_send_updates(state);
+
+ if (sendreq)
+ dhcp_failover_send_poolreq(state);
+}
+
+/*
+ * Balance operation entry from POOLREQ protocol message. Do not permit a
+ * POOLREQ to send back a POOLREQ. Ping pong.
+ */
+static void
+dhcp_failover_pool_reqbalance(dhcp_failover_state_t *state)
+{
+ int queued;
+
+ /* Cancel pending event. */
+ cancel_timeout(dhcp_failover_pool_rebalance, state);
+ state->sched_balance = 0;
+
+ queued = dhcp_failover_pool_dobalance(state, NULL);
+
+ dhcp_failover_send_poolresp(state, queued);
+
+ if (queued)
+ dhcp_failover_send_updates(state);
+ else
+ log_info("peer %s: Got POOLREQ, answering negatively! "
+ "Peer may be out of leases or database inconsistent.",
+ state->name);
+}
+
+/*
+ * Do the meat of the work common to all forms of pool rebalance. If the
+ * caller deems it appropriate to transmit POOLREQ messages, it can use the
+ * sendreq pointer to pass in the address of a FALSE value which this function
+ * will conditionally turn TRUE if a POOLREQ is determined to be necessary.
+ * A NULL value may be passed, in which case no action is taken.
+ */
+static int
+dhcp_failover_pool_dobalance(dhcp_failover_state_t *state,
+ isc_boolean_t *sendreq)
+{
+ int lts, total, thresh, hold, panic, pass;
+ int leases_queued = 0;
+ struct lease *lp = (struct lease *)0;
+ struct lease *next = (struct lease *)0;
+ struct shared_network *s;
+ struct pool *p;
+ binding_state_t peer_lease_state;
+ binding_state_t my_lease_state;
+ struct lease **lq;
+ int (*log_func)(const char *, ...);
+ const char *result, *reqlog;
+
+ if (state -> me.state != normal)
+ return 0;
+
+ state->last_balance = cur_time;
+
+ for (s = shared_networks ; s ; s = s->next) {
+ for (p = s->pools ; p ; p = p->next) {
+ if (p->failover_peer != state)
+ continue;
+
+ /* Right now we're giving the peer half of the free leases.
+ If we have more leases than the peer (i.e., more than
+ half), then the number of leases we have, less the number
+ of leases the peer has, will be how many more leases we
+ have than the peer has. So if we send half that number
+ to the peer, we should be even. */
+ if (p->failover_peer->i_am == primary) {
+ lts = (p->free_leases - p->backup_leases) / 2;
+ peer_lease_state = FTS_BACKUP;
+ my_lease_state = FTS_FREE;
+ lq = &p->free;
+ } else {
+ lts = (p->backup_leases - p->free_leases) / 2;
+ peer_lease_state = FTS_FREE;
+ my_lease_state = FTS_BACKUP;
+ lq = &p->backup;
+ }
+
+ total = p->backup_leases + p->free_leases;
+
+ thresh = ((total * state->max_lease_misbalance) + 50) / 100;
+ hold = ((total * state->max_lease_ownership) + 50) / 100;
+
+ /*
+ * If we need leases (so lts is negative) more than negative
+ * double the thresh%, panic and send poolreq to hopefully wake
+ * up the peer (but more likely the db is inconsistent). But,
+ * if this comes out zero, switch to -1 so that the POOLREQ is
+ * sent on lts == -2 rather than right away at -1.
+ *
+ * Note that we do not subtract -1 from panic all the time
+ * because thresh% and hold% may come out to the same number,
+ * and that is correct operation...where thresh% and hold% are
+ * both -1, we want to send poolreq when lts reaches -3. So,
+ * "-3 < -2", lts < panic.
+ */
+ panic = thresh * -2;
+
+ if (panic == 0)
+ panic = -1;
+
+ if ((sendreq != NULL) && (lts < panic)) {
+ reqlog = " (requesting peer rebalance!)";
+ *sendreq = ISC_TRUE;
+ } else
+ reqlog = "";
+
+ log_info("balancing pool %lx %s total %d free %d "
+ "backup %d lts %d max-own (+/-)%d%s",
+ (unsigned long)p,
+ (p->shared_network ?
+ p->shared_network->name : ""), p->lease_count,
+ p->free_leases, p->backup_leases, lts, hold,
+ reqlog);
+
+ /* In the first pass, try to allocate leases to the
+ * peer which it would normally be responsible for (if
+ * the lease has a hardware address or client-identifier,
+ * and the load-balance-algorithm chooses the peer to
+ * answer that address), up to a hold% excess in the peer's
+ * favor. In the second pass, just send the oldest (first
+ * on the list) leases up to a hold% excess in our favor.
+ *
+ * This could make for additional pool rebalance
+ * events, but preserving MAC possession should be
+ * worth it.
+ */
+ pass = 0;
+ lease_reference(&lp, *lq, MDL);
+
+ while (lp) {
+ if (next)
+ lease_dereference(&next, MDL);
+ if (lp->next)
+ lease_reference(&next, lp->next, MDL);
+
+ /*
+ * Stop if the pool is 'balanced enough.'
+ *
+ * The pool is balanced enough if:
+ *
+ * 1) We're on the first run through and the peer has
+ * its fair share of leases already (lts reaches
+ * -hold).
+ * 2) We're on the second run through, we are shifting
+ * never-used leases, and there is a perfectly even
+ * balance (lts reaches zero).
+ * 3) Second run through, we are shifting previously
+ * used leases, and the local system has its fair
+ * share but no more (lts reaches hold).
+ *
+ * Note that this is implemented below in 3,2,1 order.
+ */
+ if (pass) {
+ if (lp->ends) {
+ if (lts <= hold)
+ break;
+ } else {
+ if (lts <= 0)
+ break;
+ }
+ } else if (lts <= -hold)
+ break;
+
+ if (pass || peer_wants_lease(lp)) {
+ --lts;
+ ++leases_queued;
+ lp->next_binding_state = peer_lease_state;
+ lp->tstp = cur_time;
+ lp->starts = cur_time;
+
+ if (!supersede_lease(lp, NULL, 0, 1, 0) ||
+ !write_lease(lp))
+ log_error("can't commit lease %s on "
+ "giveaway", piaddr(lp->ip_addr));
+ }
+
+ lease_dereference(&lp, MDL);
+ if (next)
+ lease_reference(&lp, next, MDL);
+ else if (!pass) {
+ pass = 1;
+ lease_reference(&lp, *lq, MDL);
+ }
+ }
+
+ if (next)
+ lease_dereference(&next, MDL);
+ if (lp)
+ lease_dereference(&lp, MDL);
+
+ if (lts > thresh) {
+ result = "IMBALANCED";
+ log_func = log_error;
+ } else {
+ result = "balanced";
+ log_func = log_info;
+ }
+
+ log_func("%s pool %lx %s total %d free %d backup %d "
+ "lts %d max-misbal %d", result, (unsigned long)p,
+ (p->shared_network ?
+ p->shared_network->name : ""), p->lease_count,
+ p->free_leases, p->backup_leases, lts, thresh);
+
+ /* Recalculate next rebalance event timer. */
+ dhcp_failover_pool_check(p);
+ }
+ }
+
+ if (leases_queued)
+ commit_leases();
+
+ return leases_queued;
+}
+
+/* dhcp_failover_pool_check: Called whenever FREE or BACKUP leases change
+ * states, on both servers. Check the scheduled time to rebalance the pool
+ * and lower it if applicable.
+ */
+void
+dhcp_failover_pool_check(struct pool *pool)
+{
+ dhcp_failover_state_t *peer;
+ TIME est1, est2;
+ struct timeval tv;
+
+ peer = pool->failover_peer;
+
+ if(!peer || peer->me.state != normal)
+ return;
+
+ /* Estimate the time left until lease exhaustion.
+ * The first lease on the backup or free lists is also the oldest
+ * lease. It is reasonable to guess that it will take at least
+ * as much time for a pool to run out of leases, as the present
+ * age of the oldest lease (seconds since it expired).
+ *
+ * Note that this isn't so sane of an assumption if the oldest
+ * lease is a virgin (ends = 0), we wind up sending this against
+ * the max_balance bounds check.
+ */
+ if(pool->free && pool->free->ends < cur_time)
+ est1 = cur_time - pool->free->ends;
+ else
+ est1 = 0;
+
+ if(pool->backup && pool->backup->ends < cur_time)
+ est2 = cur_time - pool->backup->ends;
+ else
+ est2 = 0;
+
+ /* We don't want to schedule rebalance for when we think we'll run
+ * out of leases, we want to schedule the rebalance for when we think
+ * the disparity will be 'large enough' to warrant action.
+ */
+ est1 = ((est1 * peer->max_lease_misbalance) + 50) / 100;
+ est2 = ((est2 * peer->max_lease_misbalance) + 50) / 100;
+
+ /* Guess when the local system will begin issuing POOLREQ panic
+ * attacks because "max_lease_misbalance*2" has been exceeded.
+ */
+ if(peer->i_am == primary)
+ est1 *= 2;
+ else
+ est2 *= 2;
+
+ /* Select the smallest time. */
+ if(est1 > est2)
+ est1 = est2;
+
+ /* Bounded by the maximum configured value. */
+ if(est1 > peer->max_balance)
+ est1 = peer->max_balance;
+
+ /* Project this time into the future. */
+ est1 += cur_time;
+
+ /* Do not move the time down under the minimum. */
+ est2 = peer->last_balance + peer->min_balance;
+ if(peer->last_balance && (est1 < est2))
+ est1 = est2;
+
+ /* Introduce a random delay. */
+ est1 += random() % 5;
+
+ /* Do not move the time forward, or reset to the same time. */
+ if(peer->sched_balance) {
+ if (est1 >= peer->sched_balance)
+ return;
+
+ /* We are about to schedule the time down, cancel the
+ * current timeout.
+ */
+ cancel_timeout(dhcp_failover_pool_rebalance, peer);
+ }
+
+ /* The time is different, and lower, use it. */
+ peer->sched_balance = est1;
+
+#if defined(DEBUG_FAILOVER_TIMING)
+ log_info("add_timeout +%d dhcp_failover_pool_rebalance",
+ (int)(est1 - cur_time));
+#endif
+ tv.tv_sec = est1;
+ tv.tv_usec = 0;
+ add_timeout(&tv, dhcp_failover_pool_rebalance, peer,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+}
+
+int dhcp_failover_state_pool_check (dhcp_failover_state_t *state)
+{
+ struct shared_network *s;
+ struct pool *p;
+
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ if (p -> failover_peer != state)
+ continue;
+ dhcp_failover_pool_check (p);
+ }
+ }
+ return 0;
+}
+
+isc_result_t dhcp_failover_send_updates (dhcp_failover_state_t *state)
+{
+ struct lease *lp = (struct lease *)0;
+ isc_result_t status;
+
+ /* Can't update peer if we're not talking to it! */
+ if (!state -> link_to_peer)
+ return ISC_R_SUCCESS;
+
+ /* If there are acks pending, transmit them prior to potentially
+ * sending new updates for the same lease.
+ */
+ if (state->toack_queue_head != NULL)
+ dhcp_failover_send_acks(state);
+
+ while ((state -> partner.max_flying_updates >
+ state -> cur_unacked_updates) && state -> update_queue_head) {
+ /* Grab the head of the update queue. */
+ lease_reference (&lp, state -> update_queue_head, MDL);
+
+ /* Send the update to the peer. */
+ status = dhcp_failover_send_bind_update (state, lp);
+ if (status != ISC_R_SUCCESS) {
+ lease_dereference (&lp, MDL);
+ return status;
+ }
+ lp -> flags &= ~ON_UPDATE_QUEUE;
+
+ /* Take it off the head of the update queue and put the next
+ item in the update queue at the head. */
+ lease_dereference (&state -> update_queue_head, MDL);
+ if (lp -> next_pending) {
+ lease_reference (&state -> update_queue_head,
+ lp -> next_pending, MDL);
+ lease_dereference (&lp -> next_pending, MDL);
+ } else {
+ lease_dereference (&state -> update_queue_tail, MDL);
+ }
+
+ if (state -> ack_queue_head) {
+ lease_reference
+ (&state -> ack_queue_tail -> next_pending,
+ lp, MDL);
+ lease_dereference (&state -> ack_queue_tail, MDL);
+ } else {
+ lease_reference (&state -> ack_queue_head, lp, MDL);
+ }
+#if defined (POINTER_DEBUG)
+ if (lp -> next_pending) {
+ log_error ("ack_queue_tail: lp -> next_pending");
+ abort ();
+ }
+#endif
+ lease_reference (&state -> ack_queue_tail, lp, MDL);
+ lp -> flags |= ON_ACK_QUEUE;
+ lease_dereference (&lp, MDL);
+
+ /* Count the object as an unacked update. */
+ state -> cur_unacked_updates++;
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* Queue an update for a lease. Always returns 1 at this point - it's
+ not an error for this to be called on a lease for which there's no
+ failover peer. */
+
+int dhcp_failover_queue_update (struct lease *lease, int immediate)
+{
+ dhcp_failover_state_t *state;
+
+ if (!lease -> pool ||
+ !lease -> pool -> failover_peer)
+ return 1;
+
+ /* If it's already on the update queue, leave it there. */
+ if (lease -> flags & ON_UPDATE_QUEUE)
+ return 1;
+
+ /* Get the failover state structure for this lease. */
+ state = lease -> pool -> failover_peer;
+
+ /* If it's on the ack queue, take it off. */
+ if (lease -> flags & ON_ACK_QUEUE)
+ dhcp_failover_ack_queue_remove (state, lease);
+
+ if (state -> update_queue_head) {
+ lease_reference (&state -> update_queue_tail -> next_pending,
+ lease, MDL);
+ lease_dereference (&state -> update_queue_tail, MDL);
+ } else {
+ lease_reference (&state -> update_queue_head, lease, MDL);
+ }
+#if defined (POINTER_DEBUG)
+ if (lease -> next_pending) {
+ log_error ("next pending on update queue lease.");
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (lease);
+#endif
+ abort ();
+ }
+#endif
+ lease_reference (&state -> update_queue_tail, lease, MDL);
+ lease -> flags |= ON_UPDATE_QUEUE;
+ if (immediate)
+ dhcp_failover_send_updates (state);
+ return 1;
+}
+
+int dhcp_failover_send_acks (dhcp_failover_state_t *state)
+{
+ failover_message_t *msg = (failover_message_t *)0;
+
+ /* Must commit all leases prior to acking them. */
+ if (!commit_leases ())
+ return 0;
+
+ while (state -> toack_queue_head) {
+ failover_message_reference
+ (&msg, state -> toack_queue_head, MDL);
+ failover_message_dereference
+ (&state -> toack_queue_head, MDL);
+ if (msg -> next) {
+ failover_message_reference
+ (&state -> toack_queue_head, msg -> next, MDL);
+ }
+
+ dhcp_failover_send_bind_ack (state, msg, 0, (const char *)0);
+
+ failover_message_dereference (&msg, MDL);
+ }
+
+ if (state -> toack_queue_tail)
+ failover_message_dereference (&state -> toack_queue_tail, MDL);
+ state -> pending_acks = 0;
+
+ return 1;
+}
+
+void dhcp_failover_toack_queue_timeout (void *vs)
+{
+ dhcp_failover_state_t *state = vs;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_toack_queue_timeout");
+#endif
+
+ dhcp_failover_send_acks (state);
+}
+
+/* Queue an ack for a message. There is currently no way to queue a
+ negative ack -- these need to be sent directly. */
+
+int dhcp_failover_queue_ack (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ struct timeval tv;
+
+ if (state -> toack_queue_head) {
+ failover_message_reference
+ (&state -> toack_queue_tail -> next, msg, MDL);
+ failover_message_dereference (&state -> toack_queue_tail, MDL);
+ } else {
+ failover_message_reference (&state -> toack_queue_head,
+ msg, MDL);
+ }
+ failover_message_reference (&state -> toack_queue_tail, msg, MDL);
+
+ state -> pending_acks++;
+
+ /* Flush the toack queue whenever we exceed half the number of
+ allowed unacked updates. */
+ if (state -> pending_acks >= state -> partner.max_flying_updates / 2) {
+ dhcp_failover_send_acks (state);
+ }
+
+ /* Schedule a timeout to flush the ack queue. */
+ if (state -> pending_acks > 0) {
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +2 %s",
+ "dhcp_failover_toack_queue_timeout");
+#endif
+ tv . tv_sec = cur_time + 2;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_toack_queue_timeout, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ }
+
+ return 1;
+}
+
+void dhcp_failover_ack_queue_remove (dhcp_failover_state_t *state,
+ struct lease *lease)
+{
+ struct lease *lp;
+
+ if (!(lease -> flags & ON_ACK_QUEUE))
+ return;
+
+ if (state -> ack_queue_head == lease) {
+ lease_dereference (&state -> ack_queue_head, MDL);
+ if (lease -> next_pending) {
+ lease_reference (&state -> ack_queue_head,
+ lease -> next_pending, MDL);
+ lease_dereference (&lease -> next_pending, MDL);
+ } else {
+ lease_dereference (&state -> ack_queue_tail, MDL);
+ }
+ } else {
+ for (lp = state -> ack_queue_head;
+ lp && lp -> next_pending != lease;
+ lp = lp -> next_pending)
+ ;
+
+ if (!lp)
+ return;
+
+ lease_dereference (&lp -> next_pending, MDL);
+ if (lease -> next_pending) {
+ lease_reference (&lp -> next_pending,
+ lease -> next_pending, MDL);
+ lease_dereference (&lease -> next_pending, MDL);
+ } else {
+ lease_dereference (&state -> ack_queue_tail, MDL);
+ if (lp -> next_pending) {
+ log_error ("state -> ack_queue_tail");
+ abort ();
+ }
+ lease_reference (&state -> ack_queue_tail, lp, MDL);
+ }
+ }
+
+ lease -> flags &= ~ON_ACK_QUEUE;
+ /* Multiple acks on one XID is an error and may cause badness. */
+ lease->last_xid = 0;
+ /* XXX: this violates draft-failover. We can't send another
+ * update just because we forgot about an old one that hasn't
+ * been acked yet.
+ */
+ state -> cur_unacked_updates--;
+
+ /*
+ * When updating leases as a result of an ack, we defer the commit
+ * for performance reasons. When there are no more acks pending,
+ * do a commit.
+ */
+ if (state -> cur_unacked_updates == 0) {
+ commit_leases();
+ }
+}
+
+isc_result_t dhcp_failover_state_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+
+ /* This list of successful returns is completely wrong, but the
+ fastest way to make dhcpctl do something vaguely sane when
+ you try to change the local state. */
+
+ if (!omapi_ds_strcmp (name, "name")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "partner-address")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "local-address")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "partner-port")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "local-port")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "mclt")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "partner-state")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "local-state")) {
+ unsigned long l;
+ status = omapi_get_int_value (&l, value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ return dhcp_failover_set_state ((dhcp_failover_state_t *)h, l);
+ } else if (!omapi_ds_strcmp (name, "partner-stos")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "local-stos")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "hierarchy")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "skew")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
+ return ISC_R_SUCCESS;
+ }
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+void dhcp_failover_keepalive (void *vs)
+{
+}
+
+void dhcp_failover_reconnect (void *vs)
+{
+ dhcp_failover_state_t *state = vs;
+ isc_result_t status;
+ struct timeval tv;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_reconnect");
+#endif
+ /* If we already connected the other way, let the connection
+ recovery code initiate any retry that may be required. */
+ if (state -> link_to_peer)
+ return;
+
+ status = dhcp_failover_link_initiate ((omapi_object_t *)state);
+ if (status != ISC_R_SUCCESS && status != DHCP_R_INCOMPLETE) {
+ log_info ("failover peer %s: %s", state -> name,
+ isc_result_totext (status));
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info("add_timeout +90 dhcp_failover_reconnect");
+#endif
+ tv . tv_sec = cur_time + 90;
+ tv . tv_usec = 0;
+ add_timeout(&tv, dhcp_failover_reconnect, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ }
+}
+
+void dhcp_failover_startup_timeout (void *vs)
+{
+ dhcp_failover_state_t *state = vs;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_startup_timeout");
+#endif
+
+ dhcp_failover_state_transition (state, "disconnect");
+}
+
+void dhcp_failover_link_startup_timeout (void *vl)
+{
+ dhcp_failover_link_t *link = vl;
+ omapi_object_t *p;
+
+ for (p = (omapi_object_t *)link; p -> inner; p = p -> inner)
+ ;
+ for (; p; p = p -> outer)
+ if (p -> type == omapi_type_connection)
+ break;
+ if (p) {
+ log_info ("failover: link startup timeout");
+ omapi_disconnect (p, 1);
+ }
+}
+
+void dhcp_failover_listener_restart (void *vs)
+{
+ dhcp_failover_state_t *state = vs;
+ isc_result_t status;
+ struct timeval tv;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_listener_restart");
+#endif
+
+ status = dhcp_failover_listen ((omapi_object_t *)state);
+ if (status != ISC_R_SUCCESS) {
+ log_info ("failover peer %s: %s", state -> name,
+ isc_result_totext (status));
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +90 %s",
+ "dhcp_failover_listener_restart");
+#endif
+ tv . tv_sec = cur_time + 90;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_listener_restart, state,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ }
+}
+
+void
+dhcp_failover_auto_partner_down(void *vs)
+{
+ dhcp_failover_state_t *state = vs;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info("dhcp_failover_auto_partner_down");
+#endif
+
+ dhcp_failover_set_state(state, partner_down);
+}
+
+isc_result_t dhcp_failover_state_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ dhcp_failover_state_t *s;
+ struct option_cache *oc;
+ struct data_string ds;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+ s = (dhcp_failover_state_t *)h;
+
+ if (!omapi_ds_strcmp (name, "name")) {
+ if (s -> name)
+ return omapi_make_string_value (value,
+ name, s -> name, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "partner-address")) {
+ oc = s -> partner.address;
+ getaddr:
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_option_cache (&ds, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ return ISC_R_NOTFOUND;
+ }
+ status = omapi_make_const_value (value,
+ name, ds.data, ds.len, MDL);
+ /* Disgusting kludge: */
+ if (oc == s -> me.address && !s -> server_identifier.len)
+ data_string_copy (&s -> server_identifier, &ds, MDL);
+ data_string_forget (&ds, MDL);
+ return status;
+ } else if (!omapi_ds_strcmp (name, "local-address")) {
+ oc = s -> me.address;
+ goto getaddr;
+ } else if (!omapi_ds_strcmp (name, "partner-port")) {
+ return omapi_make_int_value (value, name,
+ s -> partner.port, MDL);
+ } else if (!omapi_ds_strcmp (name, "local-port")) {
+ return omapi_make_int_value (value,
+ name, s -> me.port, MDL);
+ } else if (!omapi_ds_strcmp (name, "max-outstanding-updates")) {
+ return omapi_make_uint_value (value, name,
+ s -> me.max_flying_updates,
+ MDL);
+ } else if (!omapi_ds_strcmp (name, "mclt")) {
+ return omapi_make_uint_value (value, name, s -> mclt, MDL);
+ } else if (!omapi_ds_strcmp (name, "load-balance-max-secs")) {
+ return omapi_make_int_value (value, name,
+ s -> load_balance_max_secs, MDL);
+ } else if (!omapi_ds_strcmp (name, "load-balance-hba")) {
+ if (s -> hba)
+ return omapi_make_const_value (value, name,
+ s -> hba, 32, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "partner-state")) {
+ return omapi_make_uint_value (value, name,
+ s -> partner.state, MDL);
+ } else if (!omapi_ds_strcmp (name, "local-state")) {
+ return omapi_make_uint_value (value, name,
+ s -> me.state, MDL);
+ } else if (!omapi_ds_strcmp (name, "partner-stos")) {
+ return omapi_make_int_value (value, name,
+ s -> partner.stos, MDL);
+ } else if (!omapi_ds_strcmp (name, "local-stos")) {
+ return omapi_make_int_value (value, name,
+ s -> me.stos, MDL);
+ } else if (!omapi_ds_strcmp (name, "hierarchy")) {
+ return omapi_make_uint_value (value, name, s -> i_am, MDL);
+ } else if (!omapi_ds_strcmp (name, "last-packet-sent")) {
+ return omapi_make_int_value (value, name,
+ s -> last_packet_sent, MDL);
+ } else if (!omapi_ds_strcmp (name, "last-timestamp-received")) {
+ return omapi_make_int_value (value, name,
+ s -> last_timestamp_received,
+ MDL);
+ } else if (!omapi_ds_strcmp (name, "skew")) {
+ return omapi_make_int_value (value, name, s -> skew, MDL);
+ } else if (!omapi_ds_strcmp (name, "max-response-delay")) {
+ return omapi_make_uint_value (value, name,
+ s -> me.max_response_delay,
+ MDL);
+ } else if (!omapi_ds_strcmp (name, "cur-unacked-updates")) {
+ return omapi_make_int_value (value, name,
+ s -> cur_unacked_updates, MDL);
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_failover_state_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ dhcp_failover_state_t *s;
+
+ if (h -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+ s = (dhcp_failover_state_t *)h;
+
+ if (s -> link_to_peer)
+ dhcp_failover_link_dereference (&s -> link_to_peer, file, line);
+ if (s -> name) {
+ dfree (s -> name, MDL);
+ s -> name = (char *)0;
+ }
+ if (s -> partner.address)
+ option_cache_dereference (&s -> partner.address, file, line);
+ if (s -> me.address)
+ option_cache_dereference (&s -> me.address, file, line);
+ if (s -> hba) {
+ dfree (s -> hba, file, line);
+ s -> hba = (u_int8_t *)0;
+ }
+ if (s -> update_queue_head)
+ lease_dereference (&s -> update_queue_head, file, line);
+ if (s -> update_queue_tail)
+ lease_dereference (&s -> update_queue_tail, file, line);
+ if (s -> ack_queue_head)
+ lease_dereference (&s -> ack_queue_head, file, line);
+ if (s -> ack_queue_tail)
+ lease_dereference (&s -> ack_queue_tail, file, line);
+ if (s -> send_update_done)
+ lease_dereference (&s -> send_update_done, file, line);
+ if (s -> toack_queue_head)
+ failover_message_dereference (&s -> toack_queue_head,
+ file, line);
+ if (s -> toack_queue_tail)
+ failover_message_dereference (&s -> toack_queue_tail,
+ file, line);
+ return ISC_R_SUCCESS;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t dhcp_failover_state_stuff (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ dhcp_failover_state_t *s;
+ omapi_connection_object_t *conn;
+ isc_result_t status;
+
+ if (c -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+ conn = (omapi_connection_object_t *)c;
+
+ if (h -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+ s = (dhcp_failover_state_t *)h;
+
+ status = omapi_connection_put_name (c, "name");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_string (c, s -> name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "partner-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof s -> partner.address);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c, (u_int8_t *)&s -> partner.address,
+ sizeof s -> partner.address);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "partner-port");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, (u_int32_t)s -> partner.port);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "local-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof s -> me.address);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c, (u_int8_t *)&s -> me.address,
+ sizeof s -> me.address);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "local-port");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.port);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "max-outstanding-updates");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c,
+ s -> me.max_flying_updates);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "mclt");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, s -> mclt);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "load-balance-max-secs");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (u_int32_t)s -> load_balance_max_secs));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+
+ if (s -> hba) {
+ status = omapi_connection_put_name (c, "load-balance-hba");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, 32);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c, s -> hba, 32);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ status = omapi_connection_put_name (c, "partner-state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, s -> partner.state);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "local-state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, s -> me.state);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "partner-stos");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c,
+ (u_int32_t)s -> partner.stos);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "local-stos");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, (u_int32_t)s -> me.stos);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "hierarchy");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, s -> i_am);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "last-packet-sent");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (u_int32_t)s -> last_packet_sent));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "last-timestamp-received");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (u_int32_t)s -> last_timestamp_received));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "skew");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, (u_int32_t)s -> skew);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "max-response-delay");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (u_int32_t)s -> me.max_response_delay));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "cur-unacked-updates");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (u_int32_t)s -> cur_unacked_updates));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (h -> inner && h -> inner -> type -> stuff_values)
+ return (*(h -> inner -> type -> stuff_values)) (c, id,
+ h -> inner);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_state_lookup (omapi_object_t **sp,
+ omapi_object_t *id,
+ omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ dhcp_failover_state_t *s;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ /* First see if we were sent a handle. */
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (sp, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*sp) -> type != dhcp_type_failover_state) {
+ omapi_object_dereference (sp, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ }
+
+ /* Look the failover state up by peer name. */
+ status = omapi_get_value_str (ref, id, "name", &tv);
+ if (status == ISC_R_SUCCESS) {
+ for (s = failover_states; s; s = s -> next) {
+ unsigned l = strlen (s -> name);
+ if (l == tv -> value -> u.buffer.len &&
+ !memcmp (s -> name,
+ tv -> value -> u.buffer.value, l))
+ break;
+ }
+ omapi_value_dereference (&tv, MDL);
+
+ /* If we already have a lease, and it's not the same one,
+ then the query was invalid. */
+ if (*sp && *sp != (omapi_object_t *)s) {
+ omapi_object_dereference (sp, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!s) {
+ if (*sp)
+ omapi_object_dereference (sp, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*sp)
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (sp, (omapi_object_t *)s, MDL);
+ }
+
+ /* If we get to here without finding a lease, no valid key was
+ specified. */
+ if (!*sp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_failover_state_create (omapi_object_t **sp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_failover_state_remove (omapi_object_t *sp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+int dhcp_failover_state_match (dhcp_failover_state_t *state,
+ u_int8_t *addr, unsigned addrlen)
+{
+ struct data_string ds;
+ int i;
+
+ memset (&ds, 0, sizeof ds);
+ if (evaluate_option_cache (&ds, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ state -> partner.address, MDL)) {
+ for (i = 0; i + addrlen - 1 < ds.len; i += addrlen) {
+ if (!memcmp (&ds.data [i],
+ addr, addrlen)) {
+ data_string_forget (&ds, MDL);
+ return 1;
+ }
+ }
+ data_string_forget (&ds, MDL);
+ }
+ return 0;
+}
+
+int
+dhcp_failover_state_match_by_name(state, name)
+ dhcp_failover_state_t *state;
+ failover_option_t *name;
+{
+ if ((strlen(state->name) == name->count) &&
+ (memcmp(state->name, name->data, name->count) == 0))
+ return 1;
+
+ return 0;
+}
+
+const char *dhcp_failover_reject_reason_print (int reason)
+{
+ static char resbuf[sizeof("Undefined-255: This reason code is not defined "
+ "in the protocol standard.")];
+
+ if ((reason > 0xff) || (reason < 0))
+ return "Reason code out of range.";
+
+ switch (reason) {
+ case FTR_ILLEGAL_IP_ADDR:
+ return "Illegal IP address (not part of any address pool).";
+
+ case FTR_FATAL_CONFLICT:
+ return "Fatal conflict exists: address in use by other client.";
+
+ case FTR_MISSING_BINDINFO:
+ return "Missing binding information.";
+
+ case FTR_TIMEMISMATCH:
+ return "Connection rejected, time mismatch too great.";
+
+ case FTR_INVALID_MCLT:
+ return "Connection rejected, invalid MCLT.";
+
+ case FTR_MISC_REJECT:
+ return "Connection rejected, unknown reason.";
+
+ case FTR_DUP_CONNECTION:
+ return "Connection rejected, duplicate connection.";
+
+ case FTR_INVALID_PARTNER:
+ return "Connection rejected, invalid failover partner.";
+
+ case FTR_TLS_UNSUPPORTED:
+ return "TLS not supported.";
+
+ case FTR_TLS_UNCONFIGURED:
+ return "TLS supported but not configured.";
+
+ case FTR_TLS_REQUIRED:
+ return "TLS required but not supported by partner.";
+
+ case FTR_DIGEST_UNSUPPORTED:
+ return "Message digest not supported.";
+
+ case FTR_DIGEST_UNCONFIGURED:
+ return "Message digest not configured.";
+
+ case FTR_VERSION_MISMATCH:
+ return "Protocol version mismatch.";
+
+ case FTR_OUTDATED_BIND_INFO:
+ return "Outdated binding information.";
+
+ case FTR_LESS_CRIT_BIND_INFO:
+ return "Less critical binding information.";
+
+ case FTR_NO_TRAFFIC:
+ return "No traffic within sufficient time.";
+
+ case FTR_HBA_CONFLICT:
+ return "Hash bucket assignment conflict.";
+
+ case FTR_IP_NOT_RESERVED:
+ return "IP not reserved on this server.";
+
+ case FTR_IP_DIGEST_FAILURE:
+ return "Message digest failed to compare.";
+
+ case FTR_IP_MISSING_DIGEST:
+ return "Missing message digest.";
+
+ case FTR_UNKNOWN:
+ return "Unknown Error.";
+
+ default:
+ sprintf(resbuf, "Undefined-%d: This reason code is not defined in the "
+ "protocol standard.", reason);
+ return resbuf;
+ }
+}
+
+const char *dhcp_failover_state_name_print (enum failover_state state)
+{
+ switch (state) {
+ default:
+ case unknown_state:
+ return "unknown-state";
+
+ case partner_down:
+ return "partner-down";
+
+ case normal:
+ return "normal";
+
+ case conflict_done:
+ return "conflict-done";
+
+ case communications_interrupted:
+ return "communications-interrupted";
+
+ case resolution_interrupted:
+ return "resolution-interrupted";
+
+ case potential_conflict:
+ return "potential-conflict";
+
+ case recover:
+ return "recover";
+
+ case recover_done:
+ return "recover-done";
+
+ case recover_wait:
+ return "recover-wait";
+
+ case shut_down:
+ return "shutdown";
+
+ case paused:
+ return "paused";
+
+ case startup:
+ return "startup";
+ }
+}
+
+const char *dhcp_failover_message_name (unsigned type)
+{
+ static char messbuf[sizeof("unknown-message-255")];
+
+ if (type > 0xff)
+ return "invalid-message";
+
+ switch (type) {
+ case FTM_POOLREQ:
+ return "pool-request";
+
+ case FTM_POOLRESP:
+ return "pool-response";
+
+ case FTM_BNDUPD:
+ return "bind-update";
+
+ case FTM_BNDACK:
+ return "bind-ack";
+
+ case FTM_CONNECT:
+ return "connect";
+
+ case FTM_CONNECTACK:
+ return "connect-ack";
+
+ case FTM_UPDREQ:
+ return "update-request";
+
+ case FTM_UPDDONE:
+ return "update-done";
+
+ case FTM_UPDREQALL:
+ return "update-request-all";
+
+ case FTM_STATE:
+ return "state";
+
+ case FTM_CONTACT:
+ return "contact";
+
+ case FTM_DISCONNECT:
+ return "disconnect";
+
+ default:
+ sprintf(messbuf, "unknown-message-%u", type);
+ return messbuf;
+ }
+}
+
+const char *dhcp_failover_option_name (unsigned type)
+{
+ static char optbuf[sizeof("unknown-option-65535")];
+
+ if (type > 0xffff)
+ return "invalid-option";
+
+ switch (type) {
+ case FTO_ADDRESSES_TRANSFERRED:
+ return "addresses-transferred";
+
+ case FTO_ASSIGNED_IP_ADDRESS:
+ return "assigned-ip-address";
+
+ case FTO_BINDING_STATUS:
+ return "binding-status";
+
+ case FTO_CLIENT_IDENTIFIER:
+ return "client-identifier";
+
+ case FTO_CHADDR:
+ return "chaddr";
+
+ case FTO_CLTT:
+ return "cltt";
+
+ case FTO_DDNS:
+ return "ddns";
+
+ case FTO_DELAYED_SERVICE:
+ return "delayed-service";
+
+ case FTO_HBA:
+ return "hba";
+
+ case FTO_IP_FLAGS:
+ return "ip-flags";
+
+ case FTO_LEASE_EXPIRY:
+ return "lease-expiry";
+
+ case FTO_MAX_UNACKED:
+ return "max-unacked";
+
+ case FTO_MCLT:
+ return "mclt";
+
+ case FTO_MESSAGE:
+ return "message";
+
+ case FTO_MESSAGE_DIGEST:
+ return "message-digest";
+
+ case FTO_POTENTIAL_EXPIRY:
+ return "potential-expiry";
+
+ case FTO_PROTOCOL_VERSION:
+ return "protocol-version";
+
+ case FTO_RECEIVE_TIMER:
+ return "receive-timer";
+
+ case FTO_REJECT_REASON:
+ return "reject-reason";
+
+ case FTO_RELATIONSHIP_NAME:
+ return "relationship-name";
+
+ case FTO_REPLY_OPTIONS:
+ return "reply-options";
+
+ case FTO_REQUEST_OPTIONS:
+ return "request-options";
+
+ case FTO_SERVER_FLAGS:
+ return "server-flags";
+
+ case FTO_SERVER_STATE:
+ return "server-state";
+
+ case FTO_STOS:
+ return "stos";
+
+ case FTO_TLS_REPLY:
+ return "tls-reply";
+
+ case FTO_TLS_REQUEST:
+ return "tls-request";
+
+ case FTO_VENDOR_CLASS:
+ return "vendor-class";
+
+ case FTO_VENDOR_OPTIONS:
+ return "vendor-options";
+
+ default:
+ sprintf(optbuf, "unknown-option-%u", type);
+ return optbuf;
+ }
+}
+
+failover_option_t *dhcp_failover_option_printf (unsigned code,
+ char *obuf,
+ unsigned *obufix,
+ unsigned obufmax,
+ const char *fmt, ...)
+{
+ va_list va;
+ char tbuf [256];
+
+ /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
+ * It is unclear what the effects of truncation here are, or
+ * how that condition should be handled. It seems that this
+ * function is used for formatting messages in the failover
+ * command channel. For now the safest thing is for
+ * overflow-truncation to cause a fatal log.
+ */
+ va_start (va, fmt);
+ if (vsnprintf (tbuf, sizeof tbuf, fmt, va) >= sizeof tbuf)
+ log_fatal ("%s: vsnprintf would truncate",
+ "dhcp_failover_make_option");
+ va_end (va);
+
+ return dhcp_failover_make_option (code, obuf, obufix, obufmax,
+ strlen (tbuf), tbuf);
+}
+
+failover_option_t *dhcp_failover_make_option (unsigned code,
+ char *obuf, unsigned *obufix,
+ unsigned obufmax, ...)
+{
+ va_list va;
+ struct failover_option_info *info;
+ int i;
+ unsigned size, count;
+ unsigned val;
+ u_int8_t *iaddr;
+ unsigned ilen = 0;
+ u_int8_t *bval;
+ char *txt = NULL;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char tbuf [256];
+#endif
+
+ /* Note that the failover_option structure is used differently on
+ input than on output - on input, count is an element count, and
+ on output it's the number of bytes total in the option, including
+ the option code and option length. */
+ failover_option_t option, *op;
+
+
+ /* Bogus option code? */
+ if (code < 1 || code > FTO_MAX || ft_options [code].type == FT_UNDEF) {
+ return &null_failover_option;
+ }
+ info = &ft_options [code];
+
+ va_start (va, obufmax);
+
+ /* Get the number of elements and the size of the buffer we need
+ to allocate. */
+ if (info -> type == FT_DDNS || info -> type == FT_DDNS1) {
+ count = info -> type == FT_DDNS ? 1 : 2;
+ size = va_arg (va, int) + count;
+ } else {
+ /* Find out how many items in this list. */
+ if (info -> num_present)
+ count = info -> num_present;
+ else
+ count = va_arg (va, int);
+
+ /* Figure out size. */
+ switch (info -> type) {
+ case FT_UINT8:
+ case FT_BYTES:
+ case FT_DIGEST:
+ size = count;
+ break;
+
+ case FT_TEXT_OR_BYTES:
+ case FT_TEXT:
+ txt = va_arg (va, char *);
+ size = count;
+ break;
+
+ case FT_IPADDR:
+ ilen = va_arg (va, unsigned);
+ size = count * ilen;
+ break;
+
+ case FT_UINT32:
+ size = count * 4;
+ break;
+
+ case FT_UINT16:
+ size = count * 2;
+ break;
+
+ default:
+ /* shouldn't get here. */
+ log_fatal ("bogus type in failover_make_option: %d",
+ info -> type);
+ return &null_failover_option;
+ }
+ }
+
+ size += 4;
+
+ /* Allocate a buffer for the option. */
+ option.count = size;
+ option.data = dmalloc (option.count, MDL);
+ if (!option.data) {
+ va_end (va);
+ return &null_failover_option;
+ }
+
+ /* Put in the option code and option length. */
+ putUShort (option.data, code);
+ putUShort (&option.data [2], size - 4);
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
+ * It is unclear what the effects of truncation here are, or
+ * how that condition should be handled. It seems that this
+ * message may be sent over the failover command channel.
+ * For now the safest thing is for overflow-truncation to cause
+ * a fatal log.
+ */
+ if (snprintf (tbuf, sizeof tbuf, " (%s<%d>", info -> name,
+ option.count) >= sizeof tbuf)
+ log_fatal ("dhcp_failover_make_option: tbuf overflow");
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+
+ /* Now put in the data. */
+ switch (info -> type) {
+ case FT_UINT8:
+ for (i = 0; i < count; i++) {
+ val = va_arg (va, unsigned);
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /* %Audit% Cannot exceed 24 bytes. %2004.06.17,Safe% */
+ sprintf (tbuf, " %d", val);
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+ option.data [i + 4] = val;
+ }
+ break;
+
+ case FT_IPADDR:
+ for (i = 0; i < count; i++) {
+ iaddr = va_arg (va, u_int8_t *);
+ if (ilen != 4) {
+ dfree (option.data, MDL);
+ log_error ("IP addrlen=%d, should be 4.",
+ ilen);
+ va_end (va);
+ return &null_failover_option;
+ }
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /*%Audit% Cannot exceed 17 bytes. %2004.06.17,Safe%*/
+ sprintf (tbuf, " %u.%u.%u.%u",
+ iaddr [0], iaddr [1], iaddr [2], iaddr [3]);
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+ memcpy (&option.data [4 + i * ilen], iaddr, ilen);
+ }
+ break;
+
+ case FT_UINT32:
+ for (i = 0; i < count; i++) {
+ val = va_arg (va, unsigned);
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
+ sprintf (tbuf, " %d", val);
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+ putULong (&option.data [4 + i * 4], val);
+ }
+ break;
+
+ case FT_BYTES:
+ case FT_DIGEST:
+ bval = va_arg (va, u_int8_t *);
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ for (i = 0; i < count; i++) {
+ /* 23 bytes plus nul, safe. */
+ sprintf (tbuf, " %d", bval [i]);
+ failover_print (obuf, obufix, obufmax, tbuf);
+ }
+#endif
+ memcpy (&option.data [4], bval, count);
+ break;
+
+ /* On output, TEXT_OR_BYTES is _always_ text, and always NUL
+ terminated. Note that the caller should be careful not
+ to provide a format and data that amount to more than 256
+ bytes of data, since it will cause a fatal error. */
+ case FT_TEXT_OR_BYTES:
+ case FT_TEXT:
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /* %Audit% Truncation causes panic. %2004.06.17,Revisit%
+ * It is unclear what the effects of truncation here are, or
+ * how that condition should be handled. It seems that this
+ * function is used for formatting messages in the failover
+ * command channel. For now the safest thing is for
+ * overflow-truncation to cause a fatal log.
+ */
+ if (snprintf (tbuf, sizeof tbuf, "\"%s\"", txt) >= sizeof tbuf)
+ log_fatal ("dhcp_failover_make_option: tbuf overflow");
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+ memcpy (&option.data [4], txt, count);
+ break;
+
+ case FT_DDNS:
+ case FT_DDNS1:
+ option.data [4] = va_arg (va, unsigned);
+ if (count == 2)
+ option.data [5] = va_arg (va, unsigned);
+ bval = va_arg (va, u_int8_t *);
+ memcpy (&option.data [4 + count], bval, size - count - 4);
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ for (i = 4; i < size; i++) {
+ /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
+ sprintf (tbuf, " %d", option.data [i]);
+ failover_print (obuf, obufix, obufmax, tbuf);
+ }
+#endif
+ break;
+
+ case FT_UINT16:
+ for (i = 0; i < count; i++) {
+ val = va_arg (va, u_int32_t);
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ /*%Audit% Cannot exceed 24 bytes. %2004.06.17,Safe%*/
+ sprintf (tbuf, " %d", val);
+ failover_print (obuf, obufix, obufmax, tbuf);
+#endif
+ putUShort (&option.data [4 + i * 2], val);
+ }
+ break;
+
+ case FT_UNDEF:
+ default:
+ break;
+ }
+
+#if defined DEBUG_FAILOVER_MESSAGES
+ failover_print (obuf, obufix, obufmax, ")");
+#endif
+ va_end (va);
+
+ /* Now allocate a place to store what we just set up. */
+ op = dmalloc (sizeof (failover_option_t), MDL);
+ if (!op) {
+ dfree (option.data, MDL);
+ return &null_failover_option;
+ }
+
+ *op = option;
+ return op;
+}
+
+/* Send a failover message header. */
+
+isc_result_t dhcp_failover_put_message (dhcp_failover_link_t *link,
+ omapi_object_t *connection,
+ int msg_type, u_int32_t xid, ...)
+{
+ unsigned size = 0;
+ int bad_option = 0;
+ int opix = 0;
+ va_list list;
+ failover_option_t *option;
+ unsigned char *opbuf;
+ isc_result_t status = ISC_R_SUCCESS;
+ unsigned char cbuf;
+ struct timeval tv;
+
+ /* Run through the argument list once to compute the length of
+ the option portion of the message. */
+ va_start (list, xid);
+ while ((option = va_arg (list, failover_option_t *))) {
+ if (option != &skip_failover_option)
+ size += option -> count;
+ if (option == &null_failover_option)
+ bad_option = 1;
+ }
+ va_end (list);
+
+ /* Allocate an option buffer, unless we got an error. */
+ if (!bad_option && size) {
+ opbuf = dmalloc (size, MDL);
+ if (!opbuf)
+ status = ISC_R_NOMEMORY;
+ } else
+ opbuf = (unsigned char *)0;
+
+ va_start (list, xid);
+ while ((option = va_arg (list, failover_option_t *))) {
+ if (option == &skip_failover_option)
+ continue;
+ if (!bad_option && opbuf)
+ memcpy (&opbuf [opix],
+ option -> data, option -> count);
+ if (option != &null_failover_option &&
+ option != &skip_failover_option) {
+ opix += option -> count;
+ dfree (option -> data, MDL);
+ dfree (option, MDL);
+ }
+ }
+ va_end(list);
+
+ if (bad_option)
+ return DHCP_R_INVALIDARG;
+
+ /* Now send the message header. */
+
+ /* Message length. */
+ status = omapi_connection_put_uint16 (connection, size + 12);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+
+ /* Message type. */
+ cbuf = msg_type;
+ status = omapi_connection_copyin (connection, &cbuf, 1);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+
+ /* Payload offset. */
+ cbuf = 12;
+ status = omapi_connection_copyin (connection, &cbuf, 1);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+
+ /* Current time. */
+ status = omapi_connection_put_uint32 (connection, (u_int32_t)cur_time);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+
+ /* Transaction ID. */
+ status = omapi_connection_put_uint32(connection, xid);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+
+ /* Payload. */
+ if (opbuf) {
+ status = omapi_connection_copyin (connection, opbuf, size);
+ if (status != ISC_R_SUCCESS)
+ goto err;
+ dfree (opbuf, MDL);
+ }
+ if (link -> state_object &&
+ link -> state_object -> link_to_peer == link) {
+#if defined (DEBUG_FAILOVER_CONTACT_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)(link -> state_object ->
+ partner.max_response_delay) / 3,
+ "dhcp_failover_send_contact");
+#endif
+ tv . tv_sec = cur_time +
+ (int)(link -> state_object ->
+ partner.max_response_delay) / 3;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_send_contact, link -> state_object,
+ (tvref_t)dhcp_failover_state_reference,
+ (tvunref_t)dhcp_failover_state_dereference);
+ }
+ return status;
+
+ err:
+ if (opbuf)
+ dfree (opbuf, MDL);
+ log_info ("dhcp_failover_put_message: something went wrong.");
+ omapi_disconnect (connection, 1);
+ return status;
+}
+
+void dhcp_failover_timeout (void *vstate)
+{
+ dhcp_failover_state_t *state = vstate;
+ dhcp_failover_link_t *link;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_timeout");
+#endif
+
+ if (!state || state -> type != dhcp_type_failover_state)
+ return;
+ link = state -> link_to_peer;
+ if (!link ||
+ !link -> outer ||
+ link -> outer -> type != omapi_type_connection)
+ return;
+
+ log_error ("timeout waiting for failover peer %s", state -> name);
+
+ /* If we haven't gotten a timely response, blow away the connection.
+ This will cause the state to change automatically. */
+ omapi_disconnect (link -> outer, 1);
+}
+
+void dhcp_failover_send_contact (void *vstate)
+{
+ dhcp_failover_state_t *state = vstate;
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+
+#if defined(DEBUG_FAILOVER_MESSAGES) && \
+ defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+ failover_print(obuf, &obufix, sizeof(obuf), "(contact");
+#endif
+
+#if defined (DEBUG_FAILOVER_CONTACT_TIMING)
+ log_info ("dhcp_failover_send_contact");
+#endif
+
+ if (!state || state -> type != dhcp_type_failover_state)
+ return;
+ link = state -> link_to_peer;
+ if (!link ||
+ !link -> outer ||
+ link -> outer -> type != omapi_type_connection)
+ return;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_CONTACT, link->xid++,
+ (failover_option_t *)0));
+
+#if defined(DEBUG_FAILOVER_MESSAGES) && \
+ defined(DEBUG_FAILOVER_CONTACT_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print(obuf, &obufix, sizeof(obuf), " (failed)");
+ failover_print(obuf, &obufix, sizeof(obuf), ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return;
+}
+
+isc_result_t dhcp_failover_send_state (dhcp_failover_state_t *state)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(state");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state || state -> type != dhcp_type_failover_state)
+ return DHCP_R_INVALIDARG;
+ link = state -> link_to_peer;
+ if (!link ||
+ !link -> outer ||
+ link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_STATE, link->xid++,
+ dhcp_failover_make_option (FTO_SERVER_STATE, FMA,
+ (state -> me.state == startup
+ ? state -> saved_state
+ : state -> me.state)),
+ dhcp_failover_make_option
+ (FTO_SERVER_FLAGS, FMA,
+ (state -> service_state == service_startup
+ ? FTF_SERVER_STARTUP : 0)),
+ dhcp_failover_make_option (FTO_STOS, FMA, state -> me.stos),
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return ISC_R_SUCCESS;
+}
+
+/* Send a connect message. */
+
+isc_result_t dhcp_failover_send_connect (omapi_object_t *l)
+{
+ dhcp_failover_link_t *link;
+ dhcp_failover_state_t *state;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(connect");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!l || l -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)l;
+ state = link -> state_object;
+ if (!l -> outer || l -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status =
+ (dhcp_failover_put_message
+ (link, l -> outer,
+ FTM_CONNECT, link->xid++,
+ dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
+ strlen(state->name), state->name),
+ dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
+ state -> me.max_flying_updates),
+ dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
+ state -> me.max_response_delay),
+ dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
+ "isc-%s", PACKAGE_VERSION),
+ dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
+ DHCP_FAILOVER_VERSION),
+ dhcp_failover_make_option (FTO_TLS_REQUEST, FMA,
+ 0, 0),
+ dhcp_failover_make_option (FTO_MCLT, FMA,
+ state -> mclt),
+ (state -> hba
+ ? dhcp_failover_make_option (FTO_HBA, FMA, 32, state -> hba)
+ : &skip_failover_option),
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+isc_result_t dhcp_failover_send_connectack (omapi_object_t *l,
+ dhcp_failover_state_t *state,
+ int reason, const char *errmsg)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(connectack");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!l || l -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)l;
+ if (!l -> outer || l -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status =
+ (dhcp_failover_put_message
+ (link, l -> outer,
+ FTM_CONNECTACK, link->imsg->xid,
+ state
+ ? dhcp_failover_make_option(FTO_RELATIONSHIP_NAME, FMA,
+ strlen(state->name), state->name)
+ : (link->imsg->options_present & FTB_RELATIONSHIP_NAME)
+ ? &link->imsg->relationship_name
+ : &skip_failover_option,
+ state
+ ? dhcp_failover_make_option (FTO_MAX_UNACKED, FMA,
+ state -> me.max_flying_updates)
+ : &skip_failover_option,
+ state
+ ? dhcp_failover_make_option (FTO_RECEIVE_TIMER, FMA,
+ state -> me.max_response_delay)
+ : &skip_failover_option,
+ dhcp_failover_option_printf(FTO_VENDOR_CLASS, FMA,
+ "isc-%s", PACKAGE_VERSION),
+ dhcp_failover_make_option (FTO_PROTOCOL_VERSION, FMA,
+ DHCP_FAILOVER_VERSION),
+ (link->imsg->options_present & FTB_TLS_REQUEST)
+ ? dhcp_failover_make_option(FTO_TLS_REPLY, FMA,
+ 0, 0)
+ : &skip_failover_option,
+ reason
+ ? dhcp_failover_make_option (FTO_REJECT_REASON,
+ FMA, reason)
+ : &skip_failover_option,
+ (reason && errmsg)
+ ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
+ strlen (errmsg), errmsg)
+ : &skip_failover_option,
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+isc_result_t dhcp_failover_send_disconnect (omapi_object_t *l,
+ int reason,
+ const char *message)
+{
+ dhcp_failover_link_t *link;
+ dhcp_failover_state_t *state;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(disconnect");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!l || l -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)l;
+ state = link -> state_object;
+ if (!l -> outer || l -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ if (!message && reason)
+ message = dhcp_failover_reject_reason_print (reason);
+
+ status = (dhcp_failover_put_message
+ (link, l -> outer,
+ FTM_DISCONNECT, link->xid++,
+ dhcp_failover_make_option (FTO_REJECT_REASON,
+ FMA, reason),
+ (message
+ ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
+ strlen (message), message)
+ : &skip_failover_option),
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+/* Send a Bind Update message. */
+
+isc_result_t dhcp_failover_send_bind_update (dhcp_failover_state_t *state,
+ struct lease *lease)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+ int flags = 0;
+ binding_state_t transmit_state;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(bndupd");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ transmit_state = lease->desired_binding_state;
+ if (lease->flags & RESERVED_LEASE) {
+ /* If we are listing an allocable (not yet ACTIVE etc) lease
+ * as reserved, toggle to the peer's 'free state', per the
+ * draft. This gives the peer permission to alloc it to the
+ * chaddr/uid-named client.
+ */
+ if ((state->i_am == primary) && (transmit_state == FTS_FREE))
+ transmit_state = FTS_BACKUP;
+ else if ((state->i_am == secondary) &&
+ (transmit_state == FTS_BACKUP))
+ transmit_state = FTS_FREE;
+
+ flags |= FTF_IP_FLAG_RESERVE;
+ }
+ if (lease->flags & BOOTP_LEASE)
+ flags |= FTF_IP_FLAG_BOOTP;
+
+ /* last_xid == 0 is illegal, seek past zero if we hit it. */
+ if (link->xid == 0)
+ link->xid = 1;
+
+ lease->last_xid = link->xid++;
+
+ /*
+ * Our very next action is to transmit a binding update relating to
+ * this lease over the wire, and although there is a BNDACK, there is
+ * no BNDACKACK or BNDACKACKACK...the basic issue as we send a BNDUPD,
+ * we may not receive a BNDACK. This non-reception does not imply the
+ * peer did not receive and process the BNDUPD. So at this point, we
+ * must divest any state that would be dangerous to retain under the
+ * impression the peer has been updated. Normally state changes like
+ * this are processed in supersede_lease(), but in this case we need a
+ * very late binding.
+ *
+ * In failover rules, a server is permitted to work forward in certain
+ * directions from a given lease's state; active leases may be
+ * extended, so forth. There is an 'optimization' in the failover
+ * draft that permits a server to 'rewind' any work they have not
+ * informed the peer. Since we can't know if the peer received our
+ * update but was unable to acknowledge it, we make this change on
+ * transmit rather than upon receiving the acknowledgement.
+ *
+ * XXX: Frequent lease commits are undesirable. This should hopefully
+ * only trigger when a server is sending a lease /state change/, and
+ * not merely an update such as with a renewal.
+ */
+ if (lease->rewind_binding_state != lease->binding_state) {
+ lease->rewind_binding_state = lease->binding_state;
+
+ write_lease(lease);
+ commit_leases();
+ }
+
+ /* Send the update. */
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_BNDUPD, lease->last_xid,
+ dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
+ lease -> ip_addr.len,
+ lease -> ip_addr.iabuf),
+ dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
+ lease -> desired_binding_state),
+ lease -> uid_len
+ ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
+ lease -> uid_len,
+ lease -> uid)
+ : &skip_failover_option,
+ lease -> hardware_addr.hlen
+ ? dhcp_failover_make_option (FTO_CHADDR, FMA,
+ lease -> hardware_addr.hlen,
+ lease -> hardware_addr.hbuf)
+ : &skip_failover_option,
+ dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
+ lease -> ends),
+ dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
+ lease -> tstp),
+ dhcp_failover_make_option (FTO_STOS, FMA,
+ lease -> starts),
+ (lease->cltt != 0) ?
+ dhcp_failover_make_option(FTO_CLTT, FMA, lease->cltt) :
+ &skip_failover_option, /* No CLTT */
+ flags ? dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
+ flags) :
+ &skip_failover_option, /* No IP_FLAGS */
+ &skip_failover_option, /* XXX DDNS */
+ &skip_failover_option, /* XXX request options */
+ &skip_failover_option, /* XXX reply options */
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+/* Send a Bind ACK message. */
+
+isc_result_t dhcp_failover_send_bind_ack (dhcp_failover_state_t *state,
+ failover_message_t *msg,
+ int reason, const char *message)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(bndack");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ if (!message && reason)
+ message = dhcp_failover_reject_reason_print (reason);
+
+ /* Send the update. */
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_BNDACK, msg->xid,
+ dhcp_failover_make_option (FTO_ASSIGNED_IP_ADDRESS, FMA,
+ sizeof msg -> assigned_addr,
+ &msg -> assigned_addr),
+#ifdef DO_BNDACK_SHOULD_NOT
+ dhcp_failover_make_option (FTO_BINDING_STATUS, FMA,
+ msg -> binding_status),
+ (msg -> options_present & FTB_CLIENT_IDENTIFIER)
+ ? dhcp_failover_make_option (FTO_CLIENT_IDENTIFIER, FMA,
+ msg -> client_identifier.count,
+ msg -> client_identifier.data)
+ : &skip_failover_option,
+ (msg -> options_present & FTB_CHADDR)
+ ? dhcp_failover_make_option (FTO_CHADDR, FMA,
+ msg -> chaddr.count,
+ msg -> chaddr.data)
+ : &skip_failover_option,
+ dhcp_failover_make_option (FTO_LEASE_EXPIRY, FMA,
+ msg -> expiry),
+ dhcp_failover_make_option (FTO_POTENTIAL_EXPIRY, FMA,
+ msg -> potential_expiry),
+ dhcp_failover_make_option (FTO_STOS, FMA,
+ msg -> stos),
+ (msg->options_present & FTB_CLTT) ?
+ dhcp_failover_make_option(FTO_CLTT, FMA, msg->cltt) :
+ &skip_failover_option, /* No CLTT in the msg to ack. */
+ ((msg->options_present & FTB_IP_FLAGS) && msg->ip_flags) ?
+ dhcp_failover_make_option(FTO_IP_FLAGS, FMA,
+ msg->ip_flags)
+ : &skip_failover_option,
+#endif /* DO_BNDACK_SHOULD_NOT */
+ reason
+ ? dhcp_failover_make_option(FTO_REJECT_REASON, FMA, reason)
+ : &skip_failover_option,
+ (reason && message)
+ ? dhcp_failover_make_option (FTO_MESSAGE, FMA,
+ strlen (message), message)
+ : &skip_failover_option,
+#ifdef DO_BNDACK_SHOULD_NOT
+ &skip_failover_option, /* XXX DDNS */
+ &skip_failover_option, /* XXX request options */
+ &skip_failover_option, /* XXX reply options */
+#endif /* DO_BNDACK_SHOULD_NOT */
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+isc_result_t dhcp_failover_send_poolreq (dhcp_failover_state_t *state)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(poolreq");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_POOLREQ, link->xid++,
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+isc_result_t dhcp_failover_send_poolresp (dhcp_failover_state_t *state,
+ int leases)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(poolresp");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_POOLRESP, link->imsg->xid,
+ dhcp_failover_make_option (FTO_ADDRESSES_TRANSFERRED, FMA,
+ leases),
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ return status;
+}
+
+isc_result_t dhcp_failover_send_update_request (dhcp_failover_state_t *state)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(updreq");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ if (state -> curUPD)
+ return ISC_R_ALREADYRUNNING;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_UPDREQ, link->xid++,
+ (failover_option_t *)0));
+
+ if (status == ISC_R_SUCCESS)
+ state -> curUPD = FTM_UPDREQ;
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ log_info ("Sent update request message to %s", state -> name);
+ return status;
+}
+
+isc_result_t dhcp_failover_send_update_request_all (dhcp_failover_state_t
+ *state)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(updreqall");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ /* If there is an UPDREQ in progress, then upgrade to UPDREQALL. */
+ if (state -> curUPD && (state -> curUPD != FTM_UPDREQ))
+ return ISC_R_ALREADYRUNNING;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_UPDREQALL, link->xid++,
+ (failover_option_t *)0));
+
+ if (status == ISC_R_SUCCESS)
+ state -> curUPD = FTM_UPDREQALL;
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+ log_info ("Sent update request all message to %s", state -> name);
+ return status;
+}
+
+isc_result_t dhcp_failover_send_update_done (dhcp_failover_state_t *state)
+{
+ dhcp_failover_link_t *link;
+ isc_result_t status;
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ char obuf [64];
+ unsigned obufix = 0;
+
+# define FMA obuf, &obufix, sizeof obuf
+ failover_print (FMA, "(upddone");
+#else
+# define FMA (char *)0, (unsigned *)0, 0
+#endif
+
+ if (!state -> link_to_peer ||
+ state -> link_to_peer -> type != dhcp_type_failover_link)
+ return DHCP_R_INVALIDARG;
+ link = (dhcp_failover_link_t *)state -> link_to_peer;
+
+ if (!link -> outer || link -> outer -> type != omapi_type_connection)
+ return DHCP_R_INVALIDARG;
+
+ status = (dhcp_failover_put_message
+ (link, link -> outer,
+ FTM_UPDDONE, state->updxid,
+ (failover_option_t *)0));
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+ if (status != ISC_R_SUCCESS)
+ failover_print (FMA, " (failed)");
+ failover_print (FMA, ")");
+ if (obufix) {
+ log_debug ("%s", obuf);
+ }
+#endif
+
+ log_info ("Sent update done message to %s", state -> name);
+
+ state->updxid--; /* Paranoia, just so it mismatches. */
+
+ /* There may be uncommitted leases at this point (since
+ dhcp_failover_process_bind_ack() doesn't commit leases);
+ commit the lease file. */
+ commit_leases();
+
+ return status;
+}
+
+/*
+ * failover_lease_is_better() compares the binding update in 'msg' with
+ * the current lease in 'lease'. If the determination is that the binding
+ * update shouldn't be allowed to update/crush more critical binding info
+ * on the lease, the lease is preferred. A value of true is returned if the
+ * local lease is preferred, or false if the remote binding update is
+ * preferred.
+ *
+ * For now this function is hopefully simplistic and trivial. It may be that
+ * a more detailed system of preferences is required, so this is something we
+ * should monitor as we gain experience with these dueling events.
+ */
+static isc_boolean_t
+failover_lease_is_better(dhcp_failover_state_t *state, struct lease *lease,
+ failover_message_t *msg)
+{
+ binding_state_t local_state;
+ TIME msg_cltt;
+
+ if (lease->binding_state != lease->desired_binding_state)
+ local_state = lease->desired_binding_state;
+ else
+ local_state = lease->binding_state;
+
+ if ((msg->options_present & FTB_CLTT) != 0)
+ msg_cltt = msg->cltt;
+ else
+ msg_cltt = 0;
+
+ switch(local_state) {
+ case FTS_ACTIVE:
+ if (msg->binding_status == FTS_ACTIVE) {
+ if (msg_cltt < lease->cltt)
+ return ISC_TRUE;
+ else if (msg_cltt > lease->cltt)
+ return ISC_FALSE;
+ else if (state->i_am == primary)
+ return ISC_TRUE;
+ else
+ return ISC_FALSE;
+ } else if (msg->binding_status == FTS_EXPIRED) {
+ return ISC_FALSE;
+ }
+ /* FALL THROUGH */
+
+ case FTS_FREE:
+ case FTS_BACKUP:
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ if (msg->binding_status == FTS_ACTIVE)
+ return ISC_FALSE;
+ else if (state->i_am == primary)
+ return ISC_TRUE;
+ else
+ return ISC_FALSE;
+ /* FALL THROUGH to impossible condition */
+
+ default:
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ }
+
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ /* Silence compiler warning. */
+ return ISC_FALSE;
+}
+
+isc_result_t dhcp_failover_process_bind_update (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ struct lease *lt, *lease;
+ struct iaddr ia;
+ int reason = FTR_MISC_REJECT;
+ const char *message;
+ int new_binding_state;
+ int send_to_backup = 0;
+ int required_options;
+ isc_boolean_t chaddr_changed = ISC_FALSE;
+ isc_boolean_t ident_changed = ISC_FALSE;
+
+ /* Validate the binding update. */
+ required_options = FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS;
+ if ((msg->options_present & required_options) != required_options) {
+ message = "binding update lacks required options";
+ reason = FTR_MISSING_BINDINFO;
+ goto bad;
+ }
+
+ ia.len = sizeof msg -> assigned_addr;
+ memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
+
+ lease = (struct lease *)0;
+ lt = (struct lease *)0;
+ if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
+ message = "unknown IP address";
+ reason = FTR_ILLEGAL_IP_ADDR;
+ goto bad;
+ }
+
+ /*
+ * If this lease is covered by a different failover peering
+ * relationship, assert an error.
+ */
+ if ((lease->pool == NULL) || (lease->pool->failover_peer == NULL) ||
+ (lease->pool->failover_peer != state)) {
+ message = "IP address is covered by a different failover "
+ "relationship state";
+ reason = FTR_ILLEGAL_IP_ADDR;
+ goto bad;
+ }
+
+ /*
+ * Dueling updates: This happens when both servers send a BNDUPD
+ * at the same time. We want the best update to win, which means
+ * we reject if we think ours is better, or cancel if we think the
+ * peer's is better. We only assert a problem if the lease is on
+ * the ACK queue, not on the UPDATE queue. This means that after
+ * accepting this server's BNDUPD, we will send our own BNDUPD
+ * /after/ sending the BNDACK (this order was recently enforced in
+ * queue processing).
+ */
+ if ((lease->flags & ON_ACK_QUEUE) != 0) {
+ if (failover_lease_is_better(state, lease, msg)) {
+ message = "incoming update is less critical than "
+ "outgoing update";
+ reason = FTR_LESS_CRIT_BIND_INFO;
+ goto bad;
+ } else {
+ /* This makes it so we ignore any spurious ACKs. */
+ dhcp_failover_ack_queue_remove(state, lease);
+ }
+ }
+
+ /* Install the new info. Start by taking a copy to markup. */
+ if (!lease_copy (&lt, lease, MDL)) {
+ message = "no memory";
+ goto bad;
+ }
+
+ if (msg -> options_present & FTB_CHADDR) {
+ if (msg->binding_status == FTS_ABANDONED) {
+ message = "BNDUPD to ABANDONED with a CHADDR";
+ goto bad;
+ }
+ if (msg -> chaddr.count > sizeof lt -> hardware_addr.hbuf) {
+ message = "chaddr too long";
+ goto bad;
+ }
+
+ if ((lt->hardware_addr.hlen != msg->chaddr.count) ||
+ (memcmp(lt->hardware_addr.hbuf, msg->chaddr.data,
+ msg->chaddr.count) != 0))
+ chaddr_changed = ISC_TRUE;
+
+ lt -> hardware_addr.hlen = msg -> chaddr.count;
+ memcpy (lt -> hardware_addr.hbuf, msg -> chaddr.data,
+ msg -> chaddr.count);
+ } else if (msg->binding_status == FTS_ACTIVE ||
+ msg->binding_status == FTS_EXPIRED ||
+ msg->binding_status == FTS_RELEASED) {
+ message = "BNDUPD without CHADDR";
+ reason = FTR_MISSING_BINDINFO;
+ goto bad;
+ } else if (msg->binding_status == FTS_ABANDONED) {
+ chaddr_changed = ISC_TRUE;
+ lt->hardware_addr.hlen = 0;
+ if (lt->scope)
+ binding_scope_dereference(&lt->scope, MDL);
+ }
+
+ /* There is no explicit message content to indicate that the client
+ * supplied no client-identifier. So if we don't hear of a value,
+ * we discard the last one.
+ */
+ if (msg->options_present & FTB_CLIENT_IDENTIFIER) {
+ if (msg->binding_status == FTS_ABANDONED) {
+ message = "BNDUPD to ABANDONED with client-id";
+ goto bad;
+ }
+
+ if ((lt->uid_len != msg->client_identifier.count) ||
+ (lt->uid == NULL) || /* Sanity; should never happen. */
+ (memcmp(lt->uid, msg->client_identifier.data,
+ lt->uid_len) != 0))
+ ident_changed = ISC_TRUE;
+
+ lt->uid_len = msg->client_identifier.count;
+
+ /* Allocate the lt->uid buffer if we haven't already, or
+ * re-allocate the lt-uid buffer if we have one that is not
+ * large enough. Otherwise, just use the extant buffer.
+ */
+ if (!lt->uid || lt->uid == lt->uid_buf ||
+ lt->uid_len > lt->uid_max) {
+ if (lt->uid && lt->uid != lt->uid_buf)
+ dfree(lt->uid, MDL);
+
+ if (lt->uid_len > sizeof(lt->uid_buf)) {
+ lt->uid_max = lt->uid_len;
+ lt->uid = dmalloc(lt->uid_len, MDL);
+ if (!lt->uid) {
+ message = "no memory";
+ goto bad;
+ }
+ } else {
+ lt->uid_max = sizeof(lt->uid_buf);
+ lt->uid = lt->uid_buf;
+ }
+ }
+ memcpy (lt -> uid,
+ msg -> client_identifier.data, lt -> uid_len);
+ } else if (lt->uid && msg->binding_status != FTS_RESET &&
+ msg->binding_status != FTS_FREE &&
+ msg->binding_status != FTS_BACKUP) {
+ ident_changed = ISC_TRUE;
+ if (lt->uid != lt->uid_buf)
+ dfree (lt->uid, MDL);
+ lt->uid = NULL;
+ lt->uid_max = lt->uid_len = 0;
+ }
+
+ /*
+ * A server's configuration can assign a 'binding scope';
+ *
+ * set var = "value";
+ *
+ * The problem with these binding scopes is that they are refreshed
+ * when the server processes a client's DHCP packet. A local binding
+ * scope is trash, then, when the lease has been assigned by the
+ * partner server. There is no real way to detect this, a peer may
+ * be updating us (as through potential conflict) with a binding we
+ * sent them, but we can trivially detect the /problematic/ case;
+ *
+ * lease is free.
+ * primary allocates lease to client A, assigns ddns name A.
+ * primary fails.
+ * secondary enters partner down.
+ * lease expires, and is set free.
+ * lease is allocated to client B and given ddns name B.
+ * primary recovers.
+ *
+ * The binding update in this case will be active->active, but the
+ * client identification on the lease will have changed. The ddns
+ * update on client A will have leaked if we just remove the binding
+ * scope blindly.
+ */
+ if (msg->binding_status == FTS_ACTIVE &&
+ (chaddr_changed || ident_changed)) {
+ ddns_removals(lease, NULL, NULL);
+
+ if (lease->scope != NULL)
+ binding_scope_dereference(&lease->scope, MDL);
+ }
+
+ /* XXX Times may need to be adjusted based on clock skew! */
+ if (msg -> options_present & FTB_STOS) {
+ lt -> starts = msg -> stos;
+ }
+ if (msg -> options_present & FTB_LEASE_EXPIRY) {
+ lt -> ends = msg -> expiry;
+ }
+ if (msg->options_present & FTB_POTENTIAL_EXPIRY) {
+ lt->atsfp = lt->tsfp = msg->potential_expiry;
+ }
+ if (msg->options_present & FTB_IP_FLAGS) {
+ if (msg->ip_flags & FTF_IP_FLAG_RESERVE) {
+ if ((((state->i_am == primary) &&
+ (lease->binding_state == FTS_FREE)) ||
+ ((state->i_am == secondary) &&
+ (lease->binding_state == FTS_BACKUP))) &&
+ !(lease->flags & RESERVED_LEASE)) {
+ message = "Address is not reserved.";
+ reason = FTR_IP_NOT_RESERVED;
+ goto bad;
+ }
+
+ lt->flags |= RESERVED_LEASE;
+ } else
+ lt->flags &= ~RESERVED_LEASE;
+
+ if (msg->ip_flags & FTF_IP_FLAG_BOOTP) {
+ if ((((state->i_am == primary) &&
+ (lease->binding_state == FTS_FREE)) ||
+ ((state->i_am == secondary) &&
+ (lease->binding_state == FTS_BACKUP))) &&
+ !(lease->flags & BOOTP_LEASE)) {
+ message = "Address is not allocated to BOOTP.";
+ goto bad;
+ }
+ lt->flags |= BOOTP_LEASE;
+ } else
+ lt->flags &= ~BOOTP_LEASE;
+
+ if (msg->ip_flags & ~(FTF_IP_FLAG_RESERVE | FTF_IP_FLAG_BOOTP))
+ log_info("Unknown IP-flags set in BNDUPD (0x%x).",
+ msg->ip_flags);
+ } else /* Flags may only not appear if the values are zero. */
+ lt->flags &= ~(RESERVED_LEASE | BOOTP_LEASE);
+
+#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
+ log_info ("processing state transition for %s: %s to %s",
+ piaddr (lease -> ip_addr),
+ binding_state_print (lease -> binding_state),
+ binding_state_print (msg -> binding_status));
+#endif
+
+ /* If we're in normal state, make sure the state transition
+ we got is valid. */
+ if (state -> me.state == normal) {
+ new_binding_state =
+ (normal_binding_state_transition_check
+ (lease, state, msg -> binding_status,
+ msg -> potential_expiry));
+ /* XXX if the transition the peer asked for isn't
+ XXX allowed, maybe we should make the transition
+ XXX into potential-conflict at this point. */
+ } else {
+ new_binding_state =
+ (conflict_binding_state_transition_check
+ (lease, state, msg -> binding_status,
+ msg -> potential_expiry));
+ }
+ if (new_binding_state != msg -> binding_status) {
+ char outbuf [100];
+
+ if (snprintf (outbuf, sizeof outbuf,
+ "%s: invalid state transition: %s to %s",
+ piaddr (lease -> ip_addr),
+ binding_state_print (lease -> binding_state),
+ binding_state_print (msg -> binding_status))
+ >= sizeof outbuf)
+ log_fatal ("%s: impossible outbuf overflow",
+ "dhcp_failover_process_bind_update");
+
+ dhcp_failover_send_bind_ack (state, msg,
+ FTR_FATAL_CONFLICT,
+ outbuf);
+ goto out;
+ }
+ if (new_binding_state == FTS_EXPIRED ||
+ new_binding_state == FTS_RELEASED ||
+ new_binding_state == FTS_RESET) {
+ lt -> next_binding_state = FTS_FREE;
+
+ /* Mac address affinity. Assign the lease to
+ * BACKUP state if we are the primary and the
+ * peer is more likely to reallocate this lease
+ * to a returning client.
+ */
+ if ((state->i_am == primary) &&
+ !(lt->flags & (RESERVED_LEASE | BOOTP_LEASE)))
+ send_to_backup = peer_wants_lease(lt);
+ } else {
+ lt -> next_binding_state = new_binding_state;
+ }
+ msg -> binding_status = lt -> next_binding_state;
+
+ /*
+ * If we accept a peer's binding update, then we can't rewind a
+ * lease behind the peer's state.
+ */
+ lease->rewind_binding_state = lt->next_binding_state;
+
+ /* Try to install the new information. */
+ if (!supersede_lease (lease, lt, 0, 0, 0) ||
+ !write_lease (lease)) {
+ message = "database update failed";
+ bad:
+ dhcp_failover_send_bind_ack (state, msg, reason, message);
+ goto out;
+ } else {
+ dhcp_failover_queue_ack (state, msg);
+ }
+
+ /* If it is probably wise, assign lease to backup state if the peer
+ * is not already hoarding leases.
+ */
+ if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
+ lease->next_binding_state = FTS_BACKUP;
+ lease->tstp = cur_time;
+ lease->starts = cur_time;
+
+ if (!supersede_lease(lease, NULL, 0, 1, 0) ||
+ !write_lease(lease))
+ log_error("can't commit lease %s for mac addr "
+ "affinity", piaddr(lease->ip_addr));
+
+ dhcp_failover_send_updates(state);
+ }
+
+ out:
+ if (lt)
+ lease_dereference (&lt, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+/* This was hairy enough I didn't want to do it all in an if statement.
+ *
+ * Returns: Truth is the secondary is allowed to get more leases based upon
+ * MAC address affinity. False otherwise.
+ */
+static inline int
+secondary_not_hoarding(dhcp_failover_state_t *state, struct pool *p) {
+ int total;
+ int hold;
+ int lts;
+
+ total = p->free_leases + p->backup_leases;
+
+ /* How many leases is one side or the other allowed to "hold"? */
+ hold = ((total * state->max_lease_ownership) + 50) / 100;
+
+ /* If we were to send leases (or if the secondary were to send us
+ * leases in the negative direction), how many would that be?
+ */
+ lts = (p->free_leases - p->backup_leases) / 2;
+
+ /* The peer is not hoarding leases if we would send them more leases
+ * (or they would take fewer leases) than the maximum they are allowed
+ * to hold (the negative hold).
+ */
+ return(lts > -hold);
+}
+
+isc_result_t dhcp_failover_process_bind_ack (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ struct lease *lt = (struct lease *)0;
+ struct lease *lease = (struct lease *)0;
+ struct iaddr ia;
+ const char *message = "no memory";
+ u_int32_t pot_expire;
+ int send_to_backup = ISC_FALSE;
+ struct timeval tv;
+
+ ia.len = sizeof msg -> assigned_addr;
+ memcpy (ia.iabuf, &msg -> assigned_addr, ia.len);
+
+ if (!find_lease_by_ip_addr (&lease, ia, MDL)) {
+ message = "no such lease";
+ goto bad;
+ }
+
+ /* XXX check for conflicts. */
+ if (msg -> options_present & FTB_REJECT_REASON) {
+ log_error ("bind update on %s from %s rejected: %.*s",
+ piaddr (ia), state -> name,
+ (int)((msg -> options_present & FTB_MESSAGE)
+ ? msg -> message.count
+ : strlen (dhcp_failover_reject_reason_print
+ (msg -> reject_reason))),
+ (msg -> options_present & FTB_MESSAGE)
+ ? (const char *)(msg -> message.data)
+ : (dhcp_failover_reject_reason_print
+ (msg -> reject_reason)));
+ goto unqueue;
+ }
+
+ /* Silently discard acks for leases we did not update (or multiple
+ * acks).
+ */
+ if (!lease->last_xid)
+ goto unqueue;
+
+ if (lease->last_xid != msg->xid) {
+ message = "xid mismatch";
+ goto bad;
+ }
+
+ /* XXX Times may need to be adjusted based on clock skew! */
+ if (msg->options_present & FTO_POTENTIAL_EXPIRY)
+ pot_expire = msg->potential_expiry;
+ else
+ pot_expire = lease->tstp;
+
+ /* If the lease was desired to enter a binding state, we set
+ * such a value upon transmitting a bndupd. We do not clear it
+ * if we receive a bndupd in the meantime (or change the state
+ * of the lease again ourselves), but we do set binding_state
+ * if we get a bndupd.
+ *
+ * So desired_binding_state tells us what we sent a bndupd for,
+ * and binding_state tells us what we have since determined in
+ * the meantime.
+ */
+ if (lease->desired_binding_state == FTS_EXPIRED ||
+ lease->desired_binding_state == FTS_RESET ||
+ lease->desired_binding_state == FTS_RELEASED)
+ {
+ /* It is not a problem to do this directly as we call
+ * supersede_lease immediately after: the lease is requeued
+ * even if its sort order (tsfp) has changed.
+ */
+ lease->atsfp = lease->tsfp = pot_expire;
+ if ((state->i_am == secondary) &&
+ (lease->flags & RESERVED_LEASE))
+ lease->next_binding_state = FTS_BACKUP;
+ else
+ lease->next_binding_state = FTS_FREE;
+
+ /* Clear this condition for the next go-round. */
+ lease->desired_binding_state = lease->next_binding_state;
+
+ /* The peer will have made this state change, so set rewind. */
+ lease->rewind_binding_state = lease->next_binding_state;
+
+ supersede_lease(lease, (struct lease *)0, 0, 0, 0);
+ write_lease(lease);
+
+ /* Lease has returned to FREE state from the
+ * transitional states. If the lease 'belongs'
+ * to a client that would be served by the
+ * peer, process a binding update now to send
+ * the lease to backup state. But not if we
+ * think we already have.
+ */
+ if (state->i_am == primary &&
+ !(lease->flags & (RESERVED_LEASE | BOOTP_LEASE)) &&
+ peer_wants_lease(lease))
+ send_to_backup = ISC_TRUE;
+
+ if (!send_to_backup && state->me.state == normal)
+ commit_leases();
+ } else {
+ /* XXX It could be a problem to do this directly if the lease
+ * XXX is sorted by tsfp.
+ */
+ lease->atsfp = lease->tsfp = pot_expire;
+ if (lease->desired_binding_state != lease->binding_state) {
+ lease->next_binding_state =
+ lease->desired_binding_state;
+ supersede_lease(lease,
+ (struct lease *)0, 0, 0, 0);
+ }
+ write_lease(lease);
+ /* Commit the lease only after a two-second timeout,
+ so that if we get a bunch of acks in quick
+ succession (e.g., when stealing leases from the
+ secondary), we do not do an immediate commit for
+ each one. */
+ tv.tv_sec = cur_time + 2;
+ tv.tv_usec = 0;
+ add_timeout(&tv, commit_leases_timeout, (void *)0, 0, 0);
+ }
+
+ unqueue:
+ dhcp_failover_ack_queue_remove (state, lease);
+
+ /* If we are supposed to send an update done after we send
+ this lease, go ahead and send it. */
+ if (state -> send_update_done == lease) {
+ lease_dereference (&state -> send_update_done, MDL);
+ dhcp_failover_send_update_done (state);
+ }
+
+ /* Now that the lease is off the ack queue, consider putting it
+ * back on the update queue for mac address affinity.
+ */
+ if (send_to_backup && secondary_not_hoarding(state, lease->pool)) {
+ lease->next_binding_state = FTS_BACKUP;
+ lease->tstp = lease->starts = cur_time;
+
+ if (!supersede_lease(lease, NULL, 0, 1, 0) ||
+ !write_lease(lease))
+ log_error("can't commit lease %s for "
+ "client affinity", piaddr(lease->ip_addr));
+
+ if (state->me.state == normal)
+ commit_leases();
+ }
+
+ /* If there are updates pending, we've created space to send at
+ least one. */
+ dhcp_failover_send_updates (state);
+
+ out:
+ lease_dereference (&lease, MDL);
+ if (lt)
+ lease_dereference (&lt, MDL);
+
+ return ISC_R_SUCCESS;
+
+ bad:
+ log_info ("bind update on %s got ack from %s: %s.",
+ piaddr (ia), state -> name, message);
+ goto out;
+}
+
+isc_result_t dhcp_failover_generate_update_queue (dhcp_failover_state_t *state,
+ int everythingp)
+{
+ struct shared_network *s;
+ struct pool *p;
+ struct lease *l;
+ int i;
+#define FREE_LEASES 0
+#define ACTIVE_LEASES 1
+#define EXPIRED_LEASES 2
+#define ABANDONED_LEASES 3
+#define BACKUP_LEASES 4
+#define RESERVED_LEASES 5
+ struct lease **lptr[RESERVED_LEASES+1];
+
+ /* Loop through each pool in each shared network and call the
+ expiry routine on the pool. */
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ if (p->failover_peer != state)
+ continue;
+
+ lptr[FREE_LEASES] = &p->free;
+ lptr[ACTIVE_LEASES] = &p->active;
+ lptr[EXPIRED_LEASES] = &p->expired;
+ lptr[ABANDONED_LEASES] = &p->abandoned;
+ lptr[BACKUP_LEASES] = &p->backup;
+ lptr[RESERVED_LEASES] = &p->reserved;
+
+ for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+ for (l = *(lptr [i]); l; l = l -> next) {
+ if ((l->flags & ON_QUEUE) == 0 &&
+ (everythingp ||
+ (l->tstp > l->atsfp) ||
+ (i == EXPIRED_LEASES))) {
+ l -> desired_binding_state = l -> binding_state;
+ dhcp_failover_queue_update (l, 0);
+ }
+ }
+ }
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+dhcp_failover_process_update_request (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ if (state->send_update_done) {
+ log_info("Received update request while old update still "
+ "flying! Silently discarding old request.");
+ lease_dereference(&state->send_update_done, MDL);
+ }
+
+ /* Generate a fresh update queue. */
+ dhcp_failover_generate_update_queue (state, 0);
+
+ state->updxid = msg->xid;
+
+ /* If there's anything on the update queue (there shouldn't be
+ anything on the ack queue), trigger an update done message
+ when we get an ack for that lease. */
+ if (state -> update_queue_tail) {
+ lease_reference (&state -> send_update_done,
+ state -> update_queue_tail, MDL);
+ dhcp_failover_send_updates (state);
+ log_info ("Update request from %s: sending update",
+ state -> name);
+ } else {
+ /* Otherwise, there are no updates to send, so we can
+ just send an UPDDONE message immediately. */
+ dhcp_failover_send_update_done (state);
+ log_info ("Update request from %s: nothing pending",
+ state -> name);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+dhcp_failover_process_update_request_all (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ if (state->send_update_done) {
+ log_info("Received update request while old update still "
+ "flying! Silently discarding old request.");
+ lease_dereference(&state->send_update_done, MDL);
+ }
+
+ /* Generate a fresh update queue that includes every lease. */
+ dhcp_failover_generate_update_queue (state, 1);
+
+ state->updxid = msg->xid;
+
+ if (state -> update_queue_tail) {
+ lease_reference (&state -> send_update_done,
+ state -> update_queue_tail, MDL);
+ dhcp_failover_send_updates (state);
+ log_info ("Update request all from %s: sending update",
+ state -> name);
+ } else {
+ /* This should really never happen, but it could happen
+ on a server that currently has no leases configured. */
+ dhcp_failover_send_update_done (state);
+ log_info ("Update request all from %s: nothing pending",
+ state -> name);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+dhcp_failover_process_update_done (dhcp_failover_state_t *state,
+ failover_message_t *msg)
+{
+ struct timeval tv;
+
+ log_info ("failover peer %s: peer update completed.",
+ state -> name);
+
+ state -> curUPD = 0;
+
+ switch (state -> me.state) {
+ case unknown_state:
+ case partner_down:
+ case normal:
+ case communications_interrupted:
+ case resolution_interrupted:
+ case shut_down:
+ case paused:
+ case recover_done:
+ case startup:
+ case recover_wait:
+ break; /* shouldn't happen. */
+
+ /* We got the UPDDONE, so we can go into normal state! */
+ case potential_conflict:
+ if (state->partner.state == conflict_done) {
+ if (state->i_am == secondary) {
+ dhcp_failover_set_state (state, normal);
+ } else {
+ log_error("Secondary is in conflict_done "
+ "state after conflict resolution, "
+ "this is illegal.");
+ dhcp_failover_set_state (state, shut_down);
+ }
+ } else {
+ if (state->i_am == primary)
+ dhcp_failover_set_state (state, conflict_done);
+ else
+ log_error("Spurious update-done message.");
+ }
+
+ break;
+
+ case conflict_done:
+ log_error("Spurious update-done message.");
+ break;
+
+ case recover:
+ /* Wait for MCLT to expire before moving to recover_done,
+ except that if both peers come up in recover, there is
+ no point in waiting for MCLT to expire - this probably
+ indicates the initial startup of a newly-configured
+ failover pair. */
+ if (state -> me.stos + state -> mclt > cur_time &&
+ state -> partner.state != recover &&
+ state -> partner.state != recover_done) {
+ dhcp_failover_set_state (state, recover_wait);
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("add_timeout +%d %s",
+ (int)(cur_time -
+ state -> me.stos + state -> mclt),
+ "dhcp_failover_recover_done");
+#endif
+ tv . tv_sec = (int)(state -> me.stos + state -> mclt);
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ dhcp_failover_recover_done,
+ state,
+ (tvref_t)omapi_object_reference,
+ (tvunref_t)
+ omapi_object_dereference);
+ } else
+ dhcp_failover_recover_done (state);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void dhcp_failover_recover_done (void *sp)
+{
+ dhcp_failover_state_t *state = sp;
+
+#if defined (DEBUG_FAILOVER_TIMING)
+ log_info ("dhcp_failover_recover_done");
+#endif
+
+ dhcp_failover_set_state (state, recover_done);
+}
+
+#if defined (DEBUG_FAILOVER_MESSAGES)
+/* Print hunks of failover messages, doing line breaks as appropriate.
+ Note that this assumes syslog is being used, rather than, e.g., the
+ Windows NT logging facility, where just dumping the whole message in
+ one hunk would be more appropriate. */
+
+void failover_print (char *obuf,
+ unsigned *obufix, unsigned obufmax, const char *s)
+{
+ int len = strlen (s);
+
+ while (len + *obufix + 1 >= obufmax) {
+ log_debug ("%s", obuf);
+ if (!*obufix) {
+ log_debug ("%s", s);
+ *obufix = 0;
+ return;
+ }
+ *obufix = 0;
+ }
+ strcpy (&obuf [*obufix], s);
+ *obufix += len;
+}
+#endif /* defined (DEBUG_FAILOVER_MESSAGES) */
+
+/* Taken from draft-ietf-dhc-loadb-01.txt: */
+/* A "mixing table" of 256 distinct values, in pseudo-random order. */
+unsigned char loadb_mx_tbl[256] = {
+ 251, 175, 119, 215, 81, 14, 79, 191, 103, 49,
+ 181, 143, 186, 157, 0, 232, 31, 32, 55, 60,
+ 152, 58, 17, 237, 174, 70, 160, 144, 220, 90,
+ 57, 223, 59, 3, 18, 140, 111, 166, 203, 196,
+ 134, 243, 124, 95, 222, 179, 197, 65, 180, 48,
+ 36, 15, 107, 46, 233, 130, 165, 30, 123, 161,
+ 209, 23, 97, 16, 40, 91, 219, 61, 100, 10,
+ 210, 109, 250, 127, 22, 138, 29, 108, 244, 67,
+ 207, 9, 178, 204, 74, 98, 126, 249, 167, 116,
+ 34, 77, 193, 200, 121, 5, 20, 113, 71, 35,
+ 128, 13, 182, 94, 25, 226, 227, 199, 75, 27,
+ 41, 245, 230, 224, 43, 225, 177, 26, 155, 150,
+ 212, 142, 218, 115, 241, 73, 88, 105, 39, 114,
+ 62, 255, 192, 201, 145, 214, 168, 158, 221, 148,
+ 154, 122, 12, 84, 82, 163, 44, 139, 228, 236,
+ 205, 242, 217, 11, 187, 146, 159, 64, 86, 239,
+ 195, 42, 106, 198, 118, 112, 184, 172, 87, 2,
+ 173, 117, 176, 229, 247, 253, 137, 185, 99, 164,
+ 102, 147, 45, 66, 231, 52, 141, 211, 194, 206,
+ 246, 238, 56, 110, 78, 248, 63, 240, 189, 93,
+ 92, 51, 53, 183, 19, 171, 72, 50, 33, 104,
+ 101, 69, 8, 252, 83, 120, 76, 135, 85, 54,
+ 202, 125, 188, 213, 96, 235, 136, 208, 162, 129,
+ 190, 132, 156, 38, 47, 1, 7, 254, 24, 4,
+ 216, 131, 89, 21, 28, 133, 37, 153, 149, 80,
+ 170, 68, 6, 169, 234, 151 };
+
+static unsigned char loadb_p_hash (const unsigned char *, unsigned);
+static unsigned char loadb_p_hash (const unsigned char *key, unsigned len)
+{
+ unsigned char hash = len;
+ int i;
+ for(i = len; i > 0; )
+ hash = loadb_mx_tbl [hash ^ (key [--i])];
+ return hash;
+}
+
+int load_balance_mine (struct packet *packet, dhcp_failover_state_t *state)
+{
+ struct option_cache *oc;
+ struct data_string ds;
+ unsigned char hbaix;
+ int hm;
+
+ if (state -> load_balance_max_secs < ntohs (packet -> raw -> secs)) {
+ return 1;
+ }
+
+ /* If we don't have a hash bucket array, we can't tell if this
+ one's ours, so we assume it's not. */
+ if (!state -> hba)
+ return 0;
+
+ oc = lookup_option (&dhcp_universe, packet -> options,
+ DHO_DHCP_CLIENT_IDENTIFIER);
+ memset (&ds, 0, sizeof ds);
+ if (oc &&
+ evaluate_option_cache (&ds, packet, (struct lease *)0,
+ (struct client_state *)0,
+ packet -> options, (struct option_state *)0,
+ &global_scope, oc, MDL)) {
+ hbaix = loadb_p_hash (ds.data, ds.len);
+
+ data_string_forget(&ds, MDL);
+ } else {
+ hbaix = loadb_p_hash (packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+ }
+
+ hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
+
+ if (state -> i_am == primary)
+ return hm;
+ else
+ return !hm;
+}
+
+/* The inverse of load_balance_mine ("load balance theirs"). We can't
+ * use the regular load_balance_mine() and invert it because of the case
+ * where there might not be an HBA, and we want to indicate false here
+ * in this case only.
+ */
+int
+peer_wants_lease(struct lease *lp)
+{
+ dhcp_failover_state_t *state;
+ unsigned char hbaix;
+ int hm;
+
+ if (!lp->pool)
+ return 0;
+
+ state = lp->pool->failover_peer;
+
+ if (!state || !state->hba)
+ return 0;
+
+ if (lp->uid_len)
+ hbaix = loadb_p_hash(lp->uid, lp->uid_len);
+ else if (lp->hardware_addr.hlen > 1)
+ /* Skip the first byte, which is the hardware type, and is
+ * not included during actual load balancing checks above
+ * since it is separate from the packet header chaddr field.
+ * The remainder of the hardware address should be identical
+ * to the chaddr contents.
+ */
+ hbaix = loadb_p_hash(lp->hardware_addr.hbuf + 1,
+ lp->hardware_addr.hlen - 1);
+ else /* impossible to categorize into LBA */
+ return 0;
+
+ hm = state->hba[(hbaix >> 3) & 0x1F] & (1 << (hbaix & 0x07));
+
+ if (state->i_am == primary)
+ return !hm;
+ else
+ return hm;
+}
+
+/* This deals with what to do with bind updates when
+ we're in the normal state
+
+ Note that tsfp had better be set from the latest bind update
+ _before_ this function is called! */
+
+binding_state_t
+normal_binding_state_transition_check (struct lease *lease,
+ dhcp_failover_state_t *state,
+ binding_state_t binding_state,
+ u_int32_t tsfp)
+{
+ binding_state_t new_state;
+
+ /* If there is no transition, it's no problem. */
+ if (binding_state == lease -> binding_state)
+ return binding_state;
+
+ switch (lease -> binding_state) {
+ case FTS_FREE:
+ case FTS_ABANDONED:
+ switch (binding_state) {
+ case FTS_ACTIVE:
+ case FTS_ABANDONED:
+ case FTS_BACKUP:
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_RESET:
+ /* If the lease was free, and our peer is primary,
+ then it can make it active, or abandoned, or
+ backup. Abandoned is treated like free in
+ this case. */
+ if (state -> i_am == secondary)
+ return binding_state;
+
+ /* Otherwise, it can't legitimately do any sort of
+ state transition. Because the lease was free,
+ and the error has already been made, we allow the
+ peer to change its state anyway, but log a warning
+ message in hopes that the error will be fixed. */
+ case FTS_FREE: /* for compiler */
+ new_state = binding_state;
+ goto out;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ case FTS_ACTIVE:
+ /* The secondary can't change the state of an active
+ lease. */
+ if (state -> i_am == primary) {
+ /* Except that the client may send the DHCPRELEASE
+ to the secondary, and we have to accept that. */
+ if (binding_state == FTS_RELEASED)
+ return binding_state;
+ new_state = lease -> binding_state;
+ goto out;
+ }
+
+ /* So this is only for transitions made by the primary: */
+ switch (binding_state) {
+ case FTS_FREE:
+ case FTS_BACKUP:
+ /* Can't set a lease to free or backup until the
+ peer agrees that it's expired. */
+ if (tsfp > cur_time) {
+ new_state = lease -> binding_state;
+ goto out;
+ }
+ return binding_state;
+
+ case FTS_EXPIRED:
+ /* XXX 65 should be the clock skew between the peers
+ XXX plus a fudge factor. This code will result
+ XXX in problems if MCLT is really short or the
+ XXX max-lease-time is really short (less than the
+ XXX fudge factor. */
+ if (lease -> ends - 65 > cur_time) {
+ new_state = lease -> binding_state;
+ goto out;
+ }
+
+ case FTS_RELEASED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ case FTS_ACTIVE:
+ return binding_state;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ break;
+ case FTS_EXPIRED:
+ switch (binding_state) {
+ case FTS_BACKUP:
+ case FTS_FREE:
+ /* Can't set a lease to free or backup until the
+ peer agrees that it's expired. */
+ if (tsfp > cur_time) {
+ new_state = lease -> binding_state;
+ goto out;
+ }
+ return binding_state;
+
+ case FTS_ACTIVE:
+ case FTS_RELEASED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ case FTS_EXPIRED:
+ return binding_state;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ case FTS_RELEASED:
+ switch (binding_state) {
+ case FTS_FREE:
+ case FTS_BACKUP:
+
+ /* These are invalid state transitions - should we
+ prevent them? */
+ case FTS_EXPIRED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ case FTS_ACTIVE:
+ case FTS_RELEASED:
+ return binding_state;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ case FTS_RESET:
+ switch (binding_state) {
+ case FTS_FREE:
+ case FTS_BACKUP:
+ /* Can't set a lease to free or backup until the
+ peer agrees that it's expired. */
+ if (tsfp > cur_time) {
+ new_state = lease -> binding_state;
+ goto out;
+ }
+ return binding_state;
+
+ case FTS_ACTIVE:
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ return binding_state;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ case FTS_BACKUP:
+ switch (binding_state) {
+ case FTS_ACTIVE:
+ case FTS_ABANDONED:
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_RESET:
+ /* If the lease was in backup, and our peer
+ is secondary, then it can make it active
+ or abandoned. */
+ if (state -> i_am == primary)
+ return binding_state;
+
+ /* Either the primary or the secondary can
+ reasonably move a lease from the backup
+ state to the free state. */
+ case FTS_FREE:
+ return binding_state;
+
+ case FTS_BACKUP:
+ new_state = lease -> binding_state;
+ goto out;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ out:
+ return new_state;
+}
+
+/* Determine whether the state transition is okay when we're potentially
+ in conflict with the peer. */
+binding_state_t
+conflict_binding_state_transition_check (struct lease *lease,
+ dhcp_failover_state_t *state,
+ binding_state_t binding_state,
+ u_int32_t tsfp)
+{
+ binding_state_t new_state;
+
+ /* If there is no transition, it's no problem. */
+ if (binding_state == lease -> binding_state)
+ new_state = binding_state;
+ else {
+ switch (lease -> binding_state) {
+ /* If we think the lease is not in use, then the
+ state into which the partner put it is just fine,
+ whatever it is. */
+ case FTS_FREE:
+ case FTS_ABANDONED:
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_RESET:
+ case FTS_BACKUP:
+ new_state = binding_state;
+ break;
+
+ /* If we think the lease *is* in use, then we're not
+ going to take the partner's change if the partner
+ thinks it's free. */
+ case FTS_ACTIVE:
+ switch (binding_state) {
+ case FTS_FREE:
+ case FTS_BACKUP:
+ new_state = lease -> binding_state;
+ break;
+
+ case FTS_EXPIRED:
+ /* If we don't agree about expiry, it's
+ * invalid. 65 should allow for max
+ * clock skew (60) plus some fudge.
+ * XXX: should we refetch cur_time?
+ */
+ if ((lease->ends - 65) > cur_time)
+ new_state = lease->binding_state;
+ else
+ new_state = binding_state;
+ break;
+
+ /* RELEASED, RESET, and ABANDONED indicate
+ * that our partner has information about
+ * this lease that we did not witness. Our
+ * partner wins.
+ */
+ case FTS_RELEASED:
+ case FTS_RESET:
+ case FTS_ABANDONED:
+ new_state = binding_state;
+ break;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ break;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return FTS_RESET;
+ }
+ }
+ return new_state;
+}
+
+/* We can reallocate a lease under the following circumstances:
+
+ (1) It belongs to us - it's FTS_FREE, and we're primary, or it's
+ FTS_BACKUP, and we're secondary.
+ (2) We're in partner_down, and the lease is not active, and we
+ can be sure that the other server didn't make it active.
+ We can only be sure that the server didn't make it active
+ when we are in the partner_down state and one of the following
+ two conditions holds:
+ (a) in the case that the time sent from the peer is earlier than
+ the time we entered the partner_down state, at least MCLT has
+ gone by since we entered partner_down, or
+ (b) in the case that the time sent from the peer is later than
+ the time when we entered partner_down, the current time is
+ later than the time sent from the peer by at least MCLT. */
+
+int lease_mine_to_reallocate (struct lease *lease)
+{
+ dhcp_failover_state_t *peer;
+
+ if (lease && lease->pool &&
+ (peer = lease->pool->failover_peer)) {
+ /*
+ * In addition to the normal rules governing wether a server
+ * is allowed to operate changes on a lease, the server is
+ * allowed to operate on a lease from the standpoint of the
+ * most conservative guess of the peer's state for this lease.
+ */
+ switch (lease->binding_state) {
+ case FTS_ACTIVE:
+ /* ACTIVE leases may not be reallocated. */
+ return 0;
+
+ case FTS_FREE:
+ case FTS_ABANDONED:
+ /* FREE leases may only be allocated by the primary,
+ * unless the secondary is acting in partner_down
+ * state and stos+mclt or tsfp+mclt has expired,
+ * whichever is greater.
+ *
+ * ABANDONED are treated the same as FREE for all
+ * purposes here. Note that servers will only try
+ * for ABANDONED leases as a last resort anyway.
+ */
+ if (peer -> i_am == primary)
+ return 1;
+
+ return(peer->service_state == service_partner_down &&
+ ((lease->tsfp < peer->me.stos) ?
+ (peer->me.stos + peer->mclt < cur_time) :
+ (lease->tsfp + peer->mclt < cur_time)));
+
+ case FTS_RELEASED:
+ case FTS_EXPIRED:
+ /*
+ * These leases are generally untouchable until the
+ * peer acknowledges their state change. However, as
+ * this is impossible if the peer is offline, the
+ * failover protocol permits an 'optimization' to
+ * rewind the lease to a previous state that the server
+ * is allowed to operate on, if that was the state that
+ * was last acknowledged by the peer.
+ *
+ * So if a lease was free, was allocated by this
+ * server, and expired without ever being transmitted
+ * to the peer, it can be returned to free and given
+ * to any new client legally.
+ */
+ if ((peer->i_am == primary) &&
+ (lease->rewind_binding_state == FTS_FREE))
+ return 1;
+ if ((peer->i_am == secondary) &&
+ (lease->rewind_binding_state == FTS_BACKUP))
+ return 1;
+
+ /* FALL THROUGH (released, expired, reset) */
+ case FTS_RESET:
+ /*
+ * Released, expired, and reset leases go onto the
+ * 'expired' queue all together. Upon entry into
+ * partner-down state, this queue of leases has their
+ * tsfp values modified to equal stos+mclt, the point
+ * at which the server is allowed to remove them from
+ * these transitional states.
+ *
+ * Note that although tsfp has been possibly extended
+ * past the actual tsfp we received from the peer, we
+ * don't have to take any special action. Since tsfp
+ * will be equal to the current time when the lease
+ * transitions to free, tsfp will not be used to grant
+ * lease-times longer than the MCLT to clients, which
+ * is the only danger for this sort of modification.
+ */
+ return((peer->service_state == service_partner_down) &&
+ (lease->tsfp < cur_time));
+
+ case FTS_BACKUP:
+ /* Only the secondary may allocate BACKUP leases,
+ * unless in partner_down state in which case at
+ * least TSFP+MCLT or STOS+MCLT must have expired,
+ * whichever is greater.
+ */
+ if (peer->i_am == secondary)
+ return 1;
+
+ return((peer->service_state == service_partner_down) &&
+ ((lease->tsfp < peer->me.stos) ?
+ (peer->me.stos + peer->mclt < cur_time) :
+ (lease->tsfp + peer->mclt < cur_time)));
+
+ default:
+ /* All lease states appear above. */
+ log_fatal("Impossible case at %s:%d.", MDL);
+ break;
+ }
+ return 0;
+ }
+ if (lease)
+ return(lease->binding_state == FTS_FREE ||
+ lease->binding_state == FTS_BACKUP);
+ else
+ return 0;
+}
+
+static isc_result_t failover_message_reference (failover_message_t **mp,
+ failover_message_t *m,
+ const char *file, int line)
+{
+ *mp = m;
+ m -> refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t failover_message_dereference (failover_message_t **mp,
+ const char *file, int line)
+{
+ failover_message_t *m;
+ m = (*mp);
+ m -> refcnt--;
+ if (m -> refcnt == 0) {
+ if (m -> next)
+ failover_message_dereference (&m -> next,
+ file, line);
+ if (m -> chaddr.data)
+ dfree (m -> chaddr.data, file, line);
+ if (m -> client_identifier.data)
+ dfree (m -> client_identifier.data, file, line);
+ if (m -> hba.data)
+ dfree (m -> hba.data, file, line);
+ if (m -> message.data)
+ dfree (m -> message.data, file, line);
+ if (m -> reply_options.data)
+ dfree (m -> reply_options.data, file, line);
+ if (m -> request_options.data)
+ dfree (m -> request_options.data, file, line);
+ if (m -> vendor_class.data)
+ dfree (m -> vendor_class.data, file, line);
+ if (m -> vendor_options.data)
+ dfree (m -> vendor_options.data, file, line);
+ if (m -> ddns.data)
+ dfree (m -> ddns.data, file, line);
+ dfree (*mp, file, line);
+ }
+ *mp = 0;
+ return ISC_R_SUCCESS;
+}
+
+OMAPI_OBJECT_ALLOC (dhcp_failover_state, dhcp_failover_state_t,
+ dhcp_type_failover_state)
+OMAPI_OBJECT_ALLOC (dhcp_failover_listener, dhcp_failover_listener_t,
+ dhcp_type_failover_listener)
+OMAPI_OBJECT_ALLOC (dhcp_failover_link, dhcp_failover_link_t,
+ dhcp_type_failover_link)
+#endif /* defined (FAILOVER_PROTOCOL) */
+
+const char *binding_state_print (enum failover_state state)
+{
+ switch (state) {
+ case FTS_FREE:
+ return "free";
+ break;
+
+ case FTS_ACTIVE:
+ return "active";
+ break;
+
+ case FTS_EXPIRED:
+ return "expired";
+ break;
+
+ case FTS_RELEASED:
+ return "released";
+ break;
+
+ case FTS_ABANDONED:
+ return "abandoned";
+ break;
+
+ case FTS_RESET:
+ return "reset";
+ break;
+
+ case FTS_BACKUP:
+ return "backup";
+ break;
+
+ default:
+ return "unknown";
+ break;
+ }
+}
diff --git a/server/ldap.c b/server/ldap.c
new file mode 100644
index 0000000..5003228
--- /dev/null
+++ b/server/ldap.c
@@ -0,0 +1,2004 @@
+/* ldap.c
+
+ Routines for reading the configuration from LDAP */
+
+/*
+ * Copyright (c) 2003-2006 Ntelos, Inc.
+ * 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 LDAP module was written by Brian Masney <masneyb@ntelos.net>. Its
+ * development was sponsored by Ntelos, Inc. (www.ntelos.com).
+ */
+
+#include "dhcpd.h"
+#include <signal.h>
+#include <errno.h>
+
+#if defined(LDAP_CONFIGURATION)
+
+#if defined(LDAP_CASA_AUTH)
+#include "ldap_casa.h"
+#endif
+
+static LDAP * ld = NULL;
+static char *ldap_server = NULL,
+ *ldap_username = NULL,
+ *ldap_password = NULL,
+ *ldap_base_dn = NULL,
+ *ldap_dhcp_server_cn = NULL,
+ *ldap_debug_file = NULL;
+static int ldap_port = LDAP_PORT,
+ ldap_method = LDAP_METHOD_DYNAMIC,
+ ldap_referrals = -1,
+ ldap_debug_fd = -1;
+#if defined (LDAP_USE_SSL)
+static int ldap_use_ssl = -1, /* try TLS if possible */
+ ldap_tls_reqcert = -1,
+ ldap_tls_crlcheck = -1;
+static char *ldap_tls_ca_file = NULL,
+ *ldap_tls_ca_dir = NULL,
+ *ldap_tls_cert = NULL,
+ *ldap_tls_key = NULL,
+ *ldap_tls_ciphers = NULL,
+ *ldap_tls_randfile = NULL;
+#endif
+static struct ldap_config_stack *ldap_stack = NULL;
+
+typedef struct ldap_dn_node {
+ struct ldap_dn_node *next;
+ size_t refs;
+ char *dn;
+} ldap_dn_node;
+
+static ldap_dn_node *ldap_service_dn_head = NULL;
+static ldap_dn_node *ldap_service_dn_tail = NULL;
+
+
+static char *
+x_strncat(char *dst, const char *src, size_t dst_size)
+{
+ size_t len = strlen(dst);
+ return strncat(dst, src, dst_size > len ? dst_size - len - 1: 0);
+}
+
+static void
+ldap_parse_class (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ x_strncat (cfile->inbuf, "class \"", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, "\" {\n", LDAP_BUFFER_SIZE);
+
+ item->close_brace = 1;
+ ldap_value_free_len (tempbv);
+}
+
+
+static void
+ldap_parse_subclass (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv, **classdata;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ if ((classdata = ldap_get_values_len (ld, item->ldent,
+ "dhcpClassData")) == NULL ||
+ classdata[0] == NULL)
+ {
+ if (classdata != NULL)
+ ldap_value_free_len (classdata);
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ x_strncat (cfile->inbuf, "subclass ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, classdata[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE);
+
+ item->close_brace = 1;
+ ldap_value_free_len (tempbv);
+ ldap_value_free_len (classdata);
+}
+
+
+static void
+ldap_parse_host (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv, **hwaddr;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ hwaddr = ldap_get_values_len (ld, item->ldent, "dhcpHWAddress");
+
+ x_strncat (cfile->inbuf, "host ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+
+ if (hwaddr != NULL && hwaddr[0] != NULL)
+ {
+ x_strncat (cfile->inbuf, " {\nhardware ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, hwaddr[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (hwaddr);
+ }
+
+ item->close_brace = 1;
+ ldap_value_free_len (tempbv);
+}
+
+
+static void
+ldap_parse_shared_network (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ x_strncat (cfile->inbuf, "shared-network \"", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, "\" {\n", LDAP_BUFFER_SIZE);
+
+ item->close_brace = 1;
+ ldap_value_free_len (tempbv);
+}
+
+
+static void
+parse_netmask (int netmask, char *netmaskbuf)
+{
+ unsigned long nm;
+ int i;
+
+ nm = 0;
+ for (i=1; i <= netmask; i++)
+ {
+ nm |= 1 << (32 - i);
+ }
+
+ sprintf (netmaskbuf, "%d.%d.%d.%d", (int) (nm >> 24) & 0xff,
+ (int) (nm >> 16) & 0xff,
+ (int) (nm >> 8) & 0xff,
+ (int) nm & 0xff);
+}
+
+
+static void
+ldap_parse_subnet (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv, **netmaskstr;
+ char netmaskbuf[sizeof("255.255.255.255")];
+ int i;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ if ((netmaskstr = ldap_get_values_len (ld, item->ldent,
+ "dhcpNetmask")) == NULL ||
+ netmaskstr[0] == NULL)
+ {
+ if (netmaskstr != NULL)
+ ldap_value_free_len (netmaskstr);
+ ldap_value_free_len (tempbv);
+
+ return;
+ }
+
+ x_strncat (cfile->inbuf, "subnet ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+
+ x_strncat (cfile->inbuf, " netmask ", LDAP_BUFFER_SIZE);
+ parse_netmask (strtol (netmaskstr[0]->bv_val, NULL, 10), netmaskbuf);
+ x_strncat (cfile->inbuf, netmaskbuf, LDAP_BUFFER_SIZE);
+
+ x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE);
+
+ ldap_value_free_len (tempbv);
+ ldap_value_free_len (netmaskstr);
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL)
+ {
+ for (i=0; tempbv[i] != NULL; i++)
+ {
+ x_strncat (cfile->inbuf, "range", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ }
+ }
+
+ item->close_brace = 1;
+}
+
+
+static void
+ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv;
+ int i;
+
+ x_strncat (cfile->inbuf, "pool {\n", LDAP_BUFFER_SIZE);
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "range", LDAP_BUFFER_SIZE);
+ for (i=0; tempbv[i] != NULL; i++)
+ {
+ x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE);
+ }
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL)
+ {
+ for (i=0; tempbv[i] != NULL; i++)
+ {
+ x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ }
+ ldap_value_free_len (tempbv);
+ }
+
+ item->close_brace = 1;
+}
+
+
+static void
+ldap_parse_group (struct ldap_config_stack *item, struct parse *cfile)
+{
+ x_strncat (cfile->inbuf, "group {\n", LDAP_BUFFER_SIZE);
+ item->close_brace = 1;
+}
+
+
+static void
+ldap_parse_key (struct ldap_config_stack *item, struct parse *cfile)
+{
+ struct berval **tempbv;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "key ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeyAlgorithm")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "algorithm ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeySecret")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "secret ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ item->close_brace = 1;
+}
+
+
+static void
+ldap_parse_zone (struct ldap_config_stack *item, struct parse *cfile)
+{
+ char *cnFindStart, *cnFindEnd;
+ struct berval **tempbv;
+ char *keyCn;
+ size_t len;
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "zone ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpDnsZoneServer")) != NULL)
+ {
+ x_strncat (cfile->inbuf, "primary ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE);
+
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeyDN")) != NULL)
+ {
+ cnFindStart = strchr(tempbv[0]->bv_val,'=');
+ if (cnFindStart != NULL)
+ cnFindEnd = strchr(++cnFindStart,',');
+ else
+ cnFindEnd = NULL;
+
+ if (cnFindEnd != NULL && cnFindEnd > cnFindStart)
+ {
+ len = cnFindEnd - cnFindStart;
+ keyCn = dmalloc (len + 1, MDL);
+ }
+ else
+ {
+ len = 0;
+ keyCn = NULL;
+ }
+
+ if (keyCn != NULL)
+ {
+ strncpy (keyCn, cnFindStart, len);
+ keyCn[len] = '\0';
+
+ x_strncat (cfile->inbuf, "key ", LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, keyCn, LDAP_BUFFER_SIZE);
+ x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE);
+
+ dfree (keyCn, MDL);
+ }
+
+ ldap_value_free_len (tempbv);
+ }
+
+ item->close_brace = 1;
+}
+
+
+static void
+add_to_config_stack (LDAPMessage * res, LDAPMessage * ent)
+{
+ struct ldap_config_stack *ns;
+
+ ns = dmalloc (sizeof (*ns), MDL);
+ ns->res = res;
+ ns->ldent = ent;
+ ns->close_brace = 0;
+ ns->processed = 0;
+ ns->next = ldap_stack;
+ ldap_stack = ns;
+}
+
+
+static void
+ldap_stop()
+{
+ struct sigaction old, new;
+
+ if (ld == NULL)
+ return;
+
+ /*
+ ** ldap_unbind after a LDAP_SERVER_DOWN result
+ ** causes a SIGPIPE and dhcpd gets terminated,
+ ** since it doesn't handle it...
+ */
+
+ new.sa_flags = 0;
+ new.sa_handler = SIG_IGN;
+ sigemptyset (&new.sa_mask);
+ sigaction (SIGPIPE, &new, &old);
+
+ ldap_unbind_ext_s (ld, NULL, NULL);
+ ld = NULL;
+
+ sigaction (SIGPIPE, &old, &new);
+}
+
+
+static char *
+_do_lookup_dhcp_string_option (struct option_state *options, int option_name)
+{
+ struct option_cache *oc;
+ struct data_string db;
+ char *ret;
+
+ memset (&db, 0, sizeof (db));
+ oc = lookup_option (&server_universe, options, option_name);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet*) NULL,
+ (struct lease *) NULL,
+ (struct client_state *) NULL, options,
+ (struct option_state *) NULL,
+ &global_scope, oc, MDL) &&
+ db.data != NULL && *db.data != '\0')
+
+ {
+ ret = dmalloc (db.len + 1, MDL);
+ if (ret == NULL)
+ log_fatal ("no memory for ldap option %d value", option_name);
+
+ memcpy (ret, db.data, db.len);
+ ret[db.len] = 0;
+ data_string_forget (&db, MDL);
+ }
+ else
+ ret = NULL;
+
+ return (ret);
+}
+
+
+static int
+_do_lookup_dhcp_int_option (struct option_state *options, int option_name)
+{
+ struct option_cache *oc;
+ struct data_string db;
+ int ret;
+
+ memset (&db, 0, sizeof (db));
+ oc = lookup_option (&server_universe, options, option_name);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet*) NULL,
+ (struct lease *) NULL,
+ (struct client_state *) NULL, options,
+ (struct option_state *) NULL,
+ &global_scope, oc, MDL) &&
+ db.data != NULL && *db.data != '\0')
+ {
+ ret = strtol ((const char *) db.data, NULL, 10);
+ data_string_forget (&db, MDL);
+ }
+ else
+ ret = 0;
+
+ return (ret);
+}
+
+
+static int
+_do_lookup_dhcp_enum_option (struct option_state *options, int option_name)
+{
+ struct option_cache *oc;
+ struct data_string db;
+ int ret = -1;
+
+ memset (&db, 0, sizeof (db));
+ oc = lookup_option (&server_universe, options, option_name);
+ if (oc &&
+ evaluate_option_cache (&db, (struct packet*) NULL,
+ (struct lease *) NULL,
+ (struct client_state *) NULL, options,
+ (struct option_state *) NULL,
+ &global_scope, oc, MDL) &&
+ db.data != NULL && *db.data != '\0')
+ {
+ if (db.len == 1)
+ ret = db.data [0];
+ else
+ log_fatal ("invalid option name %d", option_name);
+
+ data_string_forget (&db, MDL);
+ }
+ else
+ ret = 0;
+
+ return (ret);
+}
+
+int
+ldap_rebind_cb (LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msgid, void *parms)
+{
+ int ret;
+ LDAPURLDesc *ldapurl = NULL;
+ char *who = NULL;
+ struct berval creds;
+
+ log_info("LDAP rebind to '%s'", url);
+ if ((ret = ldap_url_parse(url, &ldapurl)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Can not parse ldap rebind url '%s': %s",
+ url, ldap_err2string(ret));
+ return ret;
+ }
+
+
+#if defined (LDAP_USE_SSL)
+ if (strcasecmp(ldapurl->lud_scheme, "ldaps") == 0)
+ {
+ int opt = LDAP_OPT_X_TLS_HARD;
+ if ((ret = ldap_set_option (ld, LDAP_OPT_X_TLS, &opt)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot init LDAPS session to %s:%d: %s",
+ ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret));
+ return ret;
+ }
+ else
+ {
+ log_info ("LDAPS session successfully enabled to %s", ldap_server);
+ }
+ }
+ else
+ if (strcasecmp(ldapurl->lud_scheme, "ldap") == 0 &&
+ ldap_use_ssl != LDAP_SSL_OFF)
+ {
+ if ((ret = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot start TLS session to %s:%d: %s",
+ ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret));
+ return ret;
+ }
+ else
+ {
+ log_info ("TLS session successfully started to %s:%d",
+ ldapurl->lud_host, ldapurl->lud_port);
+ }
+ }
+#endif
+
+
+ if (ldap_username != NULL || *ldap_username != '\0')
+ {
+ who = ldap_username;
+ creds.bv_val = strdup(ldap_password);
+ creds.bv_len = strlen(ldap_password);
+ }
+
+ if ((ret = ldap_sasl_bind_s (ld, who, LDAP_SASL_SIMPLE, &creds,
+ NULL, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot login into ldap server %s:%d: %s",
+ ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret));
+ }
+ return ret;
+}
+
+static void
+ldap_start (void)
+{
+ struct option_state *options;
+ int ret, version;
+ char *uri = NULL;
+ struct berval creds;
+
+ if (ld != NULL)
+ return;
+
+ if (ldap_server == NULL)
+ {
+ options = NULL;
+ option_state_allocate (&options, MDL);
+
+ execute_statements_in_scope ((struct binding_value **) NULL,
+ (struct packet *) NULL, (struct lease *) NULL,
+ (struct client_state *) NULL, (struct option_state *) NULL,
+ options, &global_scope, root_group, (struct group *) NULL);
+
+ ldap_server = _do_lookup_dhcp_string_option (options, SV_LDAP_SERVER);
+ ldap_dhcp_server_cn = _do_lookup_dhcp_string_option (options,
+ SV_LDAP_DHCP_SERVER_CN);
+ ldap_port = _do_lookup_dhcp_int_option (options, SV_LDAP_PORT);
+ ldap_base_dn = _do_lookup_dhcp_string_option (options, SV_LDAP_BASE_DN);
+ ldap_method = _do_lookup_dhcp_enum_option (options, SV_LDAP_METHOD);
+ ldap_debug_file = _do_lookup_dhcp_string_option (options,
+ SV_LDAP_DEBUG_FILE);
+ ldap_referrals = _do_lookup_dhcp_enum_option (options, SV_LDAP_REFERRALS);
+
+#if defined (LDAP_USE_SSL)
+ ldap_use_ssl = _do_lookup_dhcp_enum_option (options, SV_LDAP_SSL);
+ if( ldap_use_ssl != LDAP_SSL_OFF)
+ {
+ ldap_tls_reqcert = _do_lookup_dhcp_enum_option (options, SV_LDAP_TLS_REQCERT);
+ ldap_tls_ca_file = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CA_FILE);
+ ldap_tls_ca_dir = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CA_DIR);
+ ldap_tls_cert = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CERT);
+ ldap_tls_key = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_KEY);
+ ldap_tls_crlcheck = _do_lookup_dhcp_enum_option (options, SV_LDAP_TLS_CRLCHECK);
+ ldap_tls_ciphers = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_CIPHERS);
+ ldap_tls_randfile = _do_lookup_dhcp_string_option (options, SV_LDAP_TLS_RANDFILE);
+ }
+#endif
+
+#if defined (LDAP_CASA_AUTH)
+ if (!load_uname_pwd_from_miCASA(&ldap_username,&ldap_password))
+ {
+#if defined (DEBUG_LDAP)
+ log_info ("Authentication credential taken from file");
+#endif
+#endif
+
+ ldap_username = _do_lookup_dhcp_string_option (options, SV_LDAP_USERNAME);
+ ldap_password = _do_lookup_dhcp_string_option (options, SV_LDAP_PASSWORD);
+
+#if defined (LDAP_CASA_AUTH)
+ }
+#endif
+
+ option_state_dereference (&options, MDL);
+ }
+
+ if (ldap_server == NULL || ldap_base_dn == NULL)
+ {
+ log_info ("Not searching LDAP since ldap-server, ldap-port and ldap-base-dn were not specified in the config file");
+ ldap_method = LDAP_METHOD_STATIC;
+ return;
+ }
+
+ if (ldap_debug_file != NULL && ldap_debug_fd == -1)
+ {
+ if ((ldap_debug_fd = open (ldap_debug_file, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR)) < 0)
+ log_error ("Error opening debug LDAP log file %s: %s", ldap_debug_file,
+ strerror (errno));
+ }
+
+#if defined (DEBUG_LDAP)
+ log_info ("Connecting to LDAP server %s:%d", ldap_server, ldap_port);
+#endif
+
+#if defined (LDAP_USE_SSL)
+ if (ldap_use_ssl == -1)
+ {
+ /*
+ ** There was no "ldap-ssl" option in dhcpd.conf (also not "off").
+ ** Let's try, if we can use an anonymous TLS session without to
+ ** verify the server certificate -- if not continue without TLS.
+ */
+ int opt = LDAP_OPT_X_TLS_ALLOW;
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &opt)) != LDAP_SUCCESS)
+ {
+ log_error ("Warning: Cannot set LDAP TLS require cert option to 'allow': %s",
+ ldap_err2string (ret));
+ }
+ }
+
+ if (ldap_use_ssl != LDAP_SSL_OFF)
+ {
+ if (ldap_tls_reqcert != -1)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &ldap_tls_reqcert)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS require cert option: %s",
+ ldap_err2string (ret));
+ }
+ }
+
+ if( ldap_tls_ca_file != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE,
+ ldap_tls_ca_file)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS CA certificate file %s: %s",
+ ldap_tls_ca_file, ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_ca_dir != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR,
+ ldap_tls_ca_dir)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS CA certificate dir %s: %s",
+ ldap_tls_ca_dir, ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_cert != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE,
+ ldap_tls_cert)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS client certificate file %s: %s",
+ ldap_tls_cert, ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_key != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE,
+ ldap_tls_key)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS certificate key file %s: %s",
+ ldap_tls_key, ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_crlcheck != -1)
+ {
+ int opt = ldap_tls_crlcheck;
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CRLCHECK,
+ &opt)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS crl check option: %s",
+ ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_ciphers != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
+ ldap_tls_ciphers)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS cipher suite %s: %s",
+ ldap_tls_ciphers, ldap_err2string (ret));
+ }
+ }
+ if( ldap_tls_randfile != NULL)
+ {
+ if ((ret = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
+ ldap_tls_randfile)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot set LDAP TLS random file %s: %s",
+ ldap_tls_randfile, ldap_err2string (ret));
+ }
+ }
+ }
+#endif
+
+ /* enough for 'ldap://+ + hostname + ':' + port number */
+ uri = malloc(strlen(ldap_server) + 16);
+ if (uri == NULL)
+ {
+ log_error ("Cannot build ldap init URI %s:%d", ldap_server, ldap_port);
+ return;
+ }
+
+ sprintf(uri, "ldap://%s:%d", ldap_server, ldap_port);
+ ldap_initialize(&ld, uri);
+
+ if (ld == NULL)
+ {
+ log_error ("Cannot init ldap session to %s:%d", ldap_server, ldap_port);
+ return;
+ }
+
+ free(uri);
+
+ version = LDAP_VERSION3;
+ if ((ret = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version)) != LDAP_OPT_SUCCESS)
+ {
+ log_error ("Cannot set LDAP version to %d: %s", version,
+ ldap_err2string (ret));
+ }
+
+ if (ldap_referrals != -1)
+ {
+ if ((ret = ldap_set_option (ld, LDAP_OPT_REFERRALS, ldap_referrals ?
+ LDAP_OPT_ON : LDAP_OPT_OFF)) != LDAP_OPT_SUCCESS)
+ {
+ log_error ("Cannot %s LDAP referrals option: %s",
+ (ldap_referrals ? "enable" : "disable"),
+ ldap_err2string (ret));
+ }
+ }
+
+ if ((ret = ldap_set_rebind_proc(ld, ldap_rebind_cb, NULL)) != LDAP_SUCCESS)
+ {
+ log_error ("Warning: Cannot set ldap rebind procedure: %s",
+ ldap_err2string (ret));
+ }
+
+#if defined (LDAP_USE_SSL)
+ if (ldap_use_ssl == LDAP_SSL_LDAPS ||
+ (ldap_use_ssl == LDAP_SSL_ON && ldap_port == LDAPS_PORT))
+ {
+ int opt = LDAP_OPT_X_TLS_HARD;
+ if ((ret = ldap_set_option (ld, LDAP_OPT_X_TLS, &opt)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot init LDAPS session to %s:%d: %s",
+ ldap_server, ldap_port, ldap_err2string (ret));
+ ldap_stop();
+ return;
+ }
+ else
+ {
+ log_info ("LDAPS session successfully enabled to %s:%d",
+ ldap_server, ldap_port);
+ }
+ }
+ else if (ldap_use_ssl != LDAP_SSL_OFF)
+ {
+ if ((ret = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot start TLS session to %s:%d: %s",
+ ldap_server, ldap_port, ldap_err2string (ret));
+ ldap_stop();
+ return;
+ }
+ else
+ {
+ log_info ("TLS session successfully started to %s:%d",
+ ldap_server, ldap_port);
+ }
+ }
+#endif
+
+ if (ldap_username != NULL && *ldap_username != '\0')
+ {
+ creds.bv_val = strdup(ldap_password);
+ creds.bv_len = strlen(ldap_password);
+
+ if ((ret = ldap_sasl_bind_s (ld, ldap_username, LDAP_SASL_SIMPLE,
+ &creds, NULL, NULL, NULL)) != LDAP_SUCCESS)
+ {
+ log_error ("Error: Cannot login into ldap server %s:%d: %s",
+ ldap_server, ldap_port, ldap_err2string (ret));
+ ldap_stop();
+ return;
+ }
+ }
+
+#if defined (DEBUG_LDAP)
+ log_info ("Successfully logged into LDAP server %s", ldap_server);
+#endif
+}
+
+
+static void
+parse_external_dns (LDAPMessage * ent)
+{
+ char *search[] = {"dhcpOptionsDN", "dhcpSharedNetworkDN", "dhcpSubnetDN",
+ "dhcpGroupDN", "dhcpHostDN", "dhcpClassesDN",
+ "dhcpPoolDN", NULL};
+ LDAPMessage * newres, * newent;
+ struct berval **tempbv;
+ int i, j, ret;
+#if defined (DEBUG_LDAP)
+ char *dn;
+
+ dn = ldap_get_dn (ld, ent);
+ if (dn != NULL)
+ {
+ log_info ("Parsing external DNs for '%s'", dn);
+ ldap_memfree (dn);
+ }
+#endif
+
+ if (ld == NULL)
+ ldap_start ();
+ if (ld == NULL)
+ return;
+
+ for (i=0; search[i] != NULL; i++)
+ {
+ if ((tempbv = ldap_get_values_len (ld, ent, search[i])) == NULL)
+ continue;
+
+ for (j=0; tempbv[j] != NULL; j++)
+ {
+ if (*tempbv[j]->bv_val == '\0')
+ continue;
+
+ if ((ret = ldap_search_ext_s(ld, tempbv[j]->bv_val, LDAP_SCOPE_BASE,
+ "objectClass=*", NULL, 0, NULL,
+ NULL, NULL, 0, &newres)) != LDAP_SUCCESS)
+ {
+ ldap_value_free_len (tempbv);
+ ldap_stop();
+ return;
+ }
+
+#if defined (DEBUG_LDAP)
+ log_info ("Adding contents of subtree '%s' to config stack from '%s' reference", tempbv[j], search[i]);
+#endif
+ for (newent = ldap_first_entry (ld, newres);
+ newent != NULL;
+ newent = ldap_next_entry (ld, newent))
+ {
+#if defined (DEBUG_LDAP)
+ dn = ldap_get_dn (ld, newent);
+ if (dn != NULL)
+ {
+ log_info ("Adding LDAP result set starting with '%s' to config stack", dn);
+ ldap_memfree (dn);
+ }
+#endif
+
+ add_to_config_stack (newres, newent);
+ /* don't free newres here */
+ }
+ }
+
+ ldap_value_free_len (tempbv);
+ }
+}
+
+
+static void
+free_stack_entry (struct ldap_config_stack *item)
+{
+ struct ldap_config_stack *look_ahead_pointer = item;
+ int may_free_msg = 1;
+
+ while (look_ahead_pointer->next != NULL)
+ {
+ look_ahead_pointer = look_ahead_pointer->next;
+ if (look_ahead_pointer->res == item->res)
+ {
+ may_free_msg = 0;
+ break;
+ }
+ }
+
+ if (may_free_msg)
+ ldap_msgfree (item->res);
+
+ dfree (item, MDL);
+}
+
+
+static void
+next_ldap_entry (struct parse *cfile)
+{
+ struct ldap_config_stack *temp_stack;
+
+ if (ldap_stack != NULL && ldap_stack->close_brace)
+ {
+ x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE);
+ ldap_stack->close_brace = 0;
+ }
+
+ while (ldap_stack != NULL &&
+ (ldap_stack->ldent == NULL ||
+ (ldap_stack->ldent = ldap_next_entry (ld, ldap_stack->ldent)) == NULL))
+ {
+ if (ldap_stack->close_brace)
+ {
+ x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE);
+ ldap_stack->close_brace = 0;
+ }
+
+ temp_stack = ldap_stack;
+ ldap_stack = ldap_stack->next;
+ free_stack_entry (temp_stack);
+ }
+
+ if (ldap_stack != NULL && ldap_stack->close_brace)
+ {
+ x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE);
+ ldap_stack->close_brace = 0;
+ }
+}
+
+
+static char
+check_statement_end (const char *statement)
+{
+ char *ptr;
+
+ if (statement == NULL || *statement == '\0')
+ return ('\0');
+
+ /*
+ ** check if it ends with "}", e.g.:
+ ** "zone my.domain. { ... }"
+ ** optionally followed by spaces
+ */
+ ptr = strrchr (statement, '}');
+ if (ptr != NULL)
+ {
+ /* skip following white-spaces */
+ for (++ptr; isspace ((int)*ptr); ptr++);
+
+ /* check if we reached the end */
+ if (*ptr == '\0')
+ return ('}'); /* yes, block end */
+ else
+ return (*ptr);
+ }
+
+ /*
+ ** this should not happen, but...
+ ** check if it ends with ";", e.g.:
+ ** "authoritative;"
+ ** optionally followed by spaces
+ */
+ ptr = strrchr (statement, ';');
+ if (ptr != NULL)
+ {
+ /* skip following white-spaces */
+ for (++ptr; isspace ((int)*ptr); ptr++);
+
+ /* check if we reached the end */
+ if (*ptr == '\0')
+ return (';'); /* ends with a ; */
+ else
+ return (*ptr);
+ }
+
+ return ('\0');
+}
+
+
+static isc_result_t
+ldap_parse_entry_options (LDAPMessage *ent, char *buffer, size_t size,
+ int *lease_limit)
+{
+ struct berval **tempbv;
+ int i;
+
+ if (ent == NULL || buffer == NULL || size == 0)
+ return (ISC_R_FAILURE);
+
+ if ((tempbv = ldap_get_values_len (ld, ent, "dhcpStatements")) != NULL)
+ {
+ for (i=0; tempbv[i] != NULL; i++)
+ {
+ if (lease_limit != NULL &&
+ strncasecmp ("lease limit ", tempbv[i]->bv_val, 12) == 0)
+ {
+ *lease_limit = (int) strtol ((tempbv[i]->bv_val) + 12, NULL, 10);
+ continue;
+ }
+
+ x_strncat (buffer, tempbv[i]->bv_val, size);
+
+ switch((int) check_statement_end (tempbv[i]->bv_val))
+ {
+ case '}':
+ case ';':
+ x_strncat (buffer, "\n", size);
+ break;
+ default:
+ x_strncat (buffer, ";\n", size);
+ break;
+ }
+ }
+ ldap_value_free_len (tempbv);
+ }
+
+ if ((tempbv = ldap_get_values_len (ld, ent, "dhcpOption")) != NULL)
+ {
+ for (i=0; tempbv[i] != NULL; i++)
+ {
+ x_strncat (buffer, "option ", size);
+ x_strncat (buffer, tempbv[i]->bv_val, size);
+ switch ((int) check_statement_end (tempbv[i]->bv_val))
+ {
+ case ';':
+ x_strncat (buffer, "\n", size);
+ break;
+ default:
+ x_strncat (buffer, ";\n", size);
+ break;
+ }
+ }
+ ldap_value_free_len (tempbv);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+
+static void
+ldap_generate_config_string (struct parse *cfile)
+{
+ struct berval **objectClass;
+ char *dn;
+ struct ldap_config_stack *entry;
+ LDAPMessage * ent, * res;
+ int i, ignore, found;
+ int ret;
+
+ if (ld == NULL)
+ ldap_start ();
+ if (ld == NULL)
+ return;
+
+ entry = ldap_stack;
+ if ((objectClass = ldap_get_values_len (ld, entry->ldent,
+ "objectClass")) == NULL)
+ return;
+
+ ignore = 0;
+ found = 1;
+ for (i=0; objectClass[i] != NULL; i++)
+ {
+ if (strcasecmp (objectClass[i]->bv_val, "dhcpSharedNetwork") == 0)
+ ldap_parse_shared_network (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpClass") == 0)
+ ldap_parse_class (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubnet") == 0)
+ ldap_parse_subnet (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpPool") == 0)
+ ldap_parse_pool (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpGroup") == 0)
+ ldap_parse_group (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpTSigKey") == 0)
+ ldap_parse_key (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpDnsZone") == 0)
+ ldap_parse_zone (entry, cfile);
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpHost") == 0)
+ {
+ if (ldap_method == LDAP_METHOD_STATIC)
+ ldap_parse_host (entry, cfile);
+ else
+ {
+ ignore = 1;
+ break;
+ }
+ }
+ else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubClass") == 0)
+ {
+ if (ldap_method == LDAP_METHOD_STATIC)
+ ldap_parse_subclass (entry, cfile);
+ else
+ {
+ ignore = 1;
+ break;
+ }
+ }
+ else
+ found = 0;
+
+ if (found && cfile->inbuf[0] == '\0')
+ {
+ ignore = 1;
+ break;
+ }
+ }
+
+ ldap_value_free_len (objectClass);
+
+ if (ignore)
+ {
+ next_ldap_entry (cfile);
+ return;
+ }
+
+ ldap_parse_entry_options(entry->ldent, cfile->inbuf,
+ LDAP_BUFFER_SIZE-1, NULL);
+
+ dn = ldap_get_dn (ld, entry->ldent);
+
+#if defined(DEBUG_LDAP)
+ if (dn != NULL)
+ log_info ("Found LDAP entry '%s'", dn);
+#endif
+
+ if (dn == NULL ||
+ (ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL,
+ "objectClass=*", NULL, 0, NULL, NULL,
+ NULL, 0, &res)) != LDAP_SUCCESS)
+ {
+ if (dn)
+ ldap_memfree (dn);
+
+ ldap_stop();
+ return;
+ }
+
+ ldap_memfree (dn);
+
+ if ((ent = ldap_first_entry (ld, res)) != NULL)
+ {
+ add_to_config_stack (res, ent);
+ parse_external_dns (entry->ldent);
+ }
+ else
+ {
+ ldap_msgfree (res);
+ parse_external_dns (entry->ldent);
+ next_ldap_entry (cfile);
+ }
+}
+
+
+static void
+ldap_close_debug_fd()
+{
+ if (ldap_debug_fd != -1)
+ {
+ close (ldap_debug_fd);
+ ldap_debug_fd = -1;
+ }
+}
+
+
+static void
+ldap_write_debug (const void *buff, size_t size)
+{
+ if (ldap_debug_fd != -1)
+ {
+ if (write (ldap_debug_fd, buff, size) < 0)
+ {
+ log_error ("Error writing to LDAP debug file %s: %s."
+ " Disabling log file.", ldap_debug_file,
+ strerror (errno));
+ ldap_close_debug_fd();
+ }
+ }
+}
+
+static int
+ldap_read_function (struct parse *cfile)
+{
+ cfile->inbuf[0] = '\0';
+ cfile->buflen = 0;
+
+ while (ldap_stack != NULL && *cfile->inbuf == '\0')
+ ldap_generate_config_string (cfile);
+
+ if (ldap_stack == NULL && *cfile->inbuf == '\0')
+ return (EOF);
+
+ cfile->bufix = 1;
+ cfile->buflen = strlen (cfile->inbuf) - 1;
+ if (cfile->buflen > 0)
+ ldap_write_debug (cfile->inbuf, cfile->buflen);
+
+#if defined (DEBUG_LDAP)
+ log_info ("Sending config line '%s'", cfile->inbuf);
+#endif
+
+ return (cfile->inbuf[0]);
+}
+
+
+static char *
+ldap_get_host_name (LDAPMessage * ent)
+{
+ struct berval **name;
+ char *ret;
+
+ ret = NULL;
+ if ((name = ldap_get_values_len (ld, ent, "cn")) == NULL || name[0] == NULL)
+ {
+ if (name != NULL)
+ ldap_value_free_len (name);
+
+#if defined (DEBUG_LDAP)
+ ret = ldap_get_dn (ld, ent);
+ if (ret != NULL)
+ {
+ log_info ("Cannot get cn attribute for LDAP entry %s", ret);
+ ldap_memfree(ret);
+ }
+#endif
+ return (NULL);
+ }
+
+ ret = dmalloc (strlen (name[0]->bv_val) + 1, MDL);
+ strcpy (ret, name[0]->bv_val);
+ ldap_value_free_len (name);
+
+ return (ret);
+}
+
+
+static int
+getfqhostname(char *fqhost, size_t size)
+{
+#if defined(MAXHOSTNAMELEN)
+ char hname[MAXHOSTNAMELEN];
+#else
+ char hname[65];
+#endif
+ struct hostent *hp;
+
+ if(NULL == fqhost || 1 >= size)
+ return -1;
+
+ memset(hname, 0, sizeof(hname));
+ if( gethostname(hname, sizeof(hname)-1))
+ return -1;
+
+ if(NULL == (hp = gethostbyname(hname)))
+ return -1;
+
+ strncpy(fqhost, hp->h_name, size-1);
+ fqhost[size-1] = '\0';
+ return 0;
+}
+
+
+isc_result_t
+ldap_read_config (void)
+{
+ LDAPMessage * ldres, * hostres, * ent, * hostent;
+ char hfilter[1024], sfilter[1024], fqdn[257];
+ char *buffer, *hostdn;
+ ldap_dn_node *curr = NULL;
+ struct parse *cfile;
+ struct utsname unme;
+ isc_result_t res;
+ size_t length;
+ int ret, cnt;
+ struct berval **tempbv = NULL;
+
+ if (ld == NULL)
+ ldap_start ();
+ if (ld == NULL)
+ return (ldap_server == NULL ? ISC_R_SUCCESS : ISC_R_FAILURE);
+
+ buffer = dmalloc (LDAP_BUFFER_SIZE+1, MDL);
+ if (buffer == NULL)
+ return (ISC_R_FAILURE);
+
+ cfile = (struct parse *) NULL;
+ res = new_parse (&cfile, -1, buffer, LDAP_BUFFER_SIZE, "LDAP", 0);
+ if (res != ISC_R_SUCCESS)
+ return (res);
+
+ uname (&unme);
+ if (ldap_dhcp_server_cn != NULL)
+ {
+ snprintf (hfilter, sizeof (hfilter),
+ "(&(objectClass=dhcpServer)(cn=%s))", ldap_dhcp_server_cn);
+ }
+ else
+ {
+ if(0 == getfqhostname(fqdn, sizeof(fqdn)))
+ {
+ snprintf (hfilter, sizeof (hfilter),
+ "(&(objectClass=dhcpServer)(|(cn=%s)(cn=%s)))",
+ unme.nodename, fqdn);
+ }
+ else
+ {
+ snprintf (hfilter, sizeof (hfilter),
+ "(&(objectClass=dhcpServer)(cn=%s))", unme.nodename);
+ }
+
+ }
+ hostres = NULL;
+ if ((ret = ldap_search_ext_s (ld, ldap_base_dn, LDAP_SCOPE_SUBTREE,
+ hfilter, NULL, 0, NULL, NULL, NULL, 0,
+ &hostres)) != LDAP_SUCCESS)
+ {
+ log_error ("Cannot find host LDAP entry %s %s",
+ ((ldap_dhcp_server_cn == NULL)?(unme.nodename):(ldap_dhcp_server_cn)), hfilter);
+ if(NULL != hostres)
+ ldap_msgfree (hostres);
+ ldap_stop();
+ return (ISC_R_FAILURE);
+ }
+
+ if ((hostent = ldap_first_entry (ld, hostres)) == NULL)
+ {
+ log_error ("Error: Cannot find LDAP entry matching %s", hfilter);
+ ldap_msgfree (hostres);
+ ldap_stop();
+ return (ISC_R_FAILURE);
+ }
+
+ hostdn = ldap_get_dn (ld, hostent);
+#if defined(DEBUG_LDAP)
+ if (hostdn != NULL)
+ log_info ("Found dhcpServer LDAP entry '%s'", hostdn);
+#endif
+
+ if (hostdn == NULL ||
+ (tempbv = ldap_get_values_len (ld, hostent, "dhcpServiceDN")) == NULL ||
+ tempbv[0] == NULL)
+ {
+ log_error ("Error: Cannot find LDAP entry matching %s", hfilter);
+
+ if (tempbv != NULL)
+ ldap_value_free_len (tempbv);
+
+ if (hostdn)
+ ldap_memfree (hostdn);
+ ldap_msgfree (hostres);
+ ldap_stop();
+ return (ISC_R_FAILURE);
+ }
+
+#if defined(DEBUG_LDAP)
+ log_info ("LDAP: Parsing dhcpServer options '%s' ...", hostdn);
+#endif
+
+ cfile->inbuf[0] = '\0';
+ ldap_parse_entry_options(hostent, cfile->inbuf, LDAP_BUFFER_SIZE, NULL);
+ cfile->buflen = strlen (cfile->inbuf);
+ if(cfile->buflen > 0)
+ {
+ ldap_write_debug (cfile->inbuf, cfile->buflen);
+
+ res = conf_file_subparse (cfile, root_group, ROOT_GROUP);
+ if (res != ISC_R_SUCCESS)
+ {
+ log_error ("LDAP: cannot parse dhcpServer entry '%s'", hostdn);
+ ldap_memfree (hostdn);
+ ldap_stop();
+ return res;
+ }
+ cfile->inbuf[0] = '\0';
+ }
+ ldap_msgfree (hostres);
+
+ /*
+ ** attach ldap (tree) read function now
+ */
+ cfile->bufix = cfile->buflen = 0;
+ cfile->read_function = ldap_read_function;
+
+ res = ISC_R_SUCCESS;
+ for (cnt=0; tempbv[cnt] != NULL; cnt++)
+ {
+ snprintf(sfilter, sizeof(sfilter), "(&(objectClass=dhcpService)"
+ "(|(dhcpPrimaryDN=%s)(dhcpSecondaryDN=%s)))",
+ hostdn, hostdn);
+ ldres = NULL;
+ if ((ret = ldap_search_ext_s (ld, tempbv[cnt]->bv_val, LDAP_SCOPE_BASE,
+ sfilter, NULL, 0, NULL, NULL, NULL,
+ 0, &ldres)) != LDAP_SUCCESS)
+ {
+ log_error ("Error searching for dhcpServiceDN '%s': %s. Please update the LDAP entry '%s'",
+ tempbv[cnt]->bv_val, ldap_err2string (ret), hostdn);
+ if(NULL != ldres)
+ ldap_msgfree(ldres);
+ res = ISC_R_FAILURE;
+ break;
+ }
+
+ if ((ent = ldap_first_entry (ld, ldres)) == NULL)
+ {
+ log_error ("Error: Cannot find dhcpService DN '%s' with primary or secondary server reference. Please update the LDAP server entry '%s'",
+ tempbv[cnt]->bv_val, hostdn);
+
+ ldap_msgfree(ldres);
+ res = ISC_R_FAILURE;
+ break;
+ }
+
+ /*
+ ** FIXME: how to free the remembered dn's on exit?
+ ** This should be OK if dmalloc registers the
+ ** memory it allocated and frees it on exit..
+ */
+
+ curr = dmalloc (sizeof (*curr), MDL);
+ if (curr != NULL)
+ {
+ length = strlen (tempbv[cnt]->bv_val);
+ curr->dn = dmalloc (length + 1, MDL);
+ if (curr->dn == NULL)
+ {
+ dfree (curr, MDL);
+ curr = NULL;
+ }
+ else
+ strcpy (curr->dn, tempbv[cnt]->bv_val);
+ }
+
+ if (curr != NULL)
+ {
+ curr->refs++;
+
+ /* append to service-dn list */
+ if (ldap_service_dn_tail != NULL)
+ ldap_service_dn_tail->next = curr;
+ else
+ ldap_service_dn_head = curr;
+
+ ldap_service_dn_tail = curr;
+ }
+ else
+ log_fatal ("no memory to remember ldap service dn");
+
+#if defined (DEBUG_LDAP)
+ log_info ("LDAP: Parsing dhcpService DN '%s' ...", tempbv[cnt]);
+#endif
+ add_to_config_stack (ldres, ent);
+ res = conf_file_subparse (cfile, root_group, ROOT_GROUP);
+ if (res != ISC_R_SUCCESS)
+ {
+ log_error ("LDAP: cannot parse dhcpService entry '%s'", tempbv[cnt]->bv_val);
+ break;
+ }
+ }
+
+ end_parse (&cfile);
+ ldap_close_debug_fd();
+
+ ldap_memfree (hostdn);
+ ldap_value_free_len (tempbv);
+
+ if (res != ISC_R_SUCCESS)
+ {
+ struct ldap_config_stack *temp_stack;
+
+ while ((curr = ldap_service_dn_head) != NULL)
+ {
+ ldap_service_dn_head = curr->next;
+ dfree (curr->dn, MDL);
+ dfree (curr, MDL);
+ }
+
+ ldap_service_dn_tail = NULL;
+
+ while ((temp_stack = ldap_stack) != NULL)
+ {
+ ldap_stack = temp_stack->next;
+ free_stack_entry (temp_stack);
+ }
+
+ ldap_stop();
+ }
+
+ /* Unbind from ldap immediately after reading config in static mode. */
+ if (ldap_method == LDAP_METHOD_STATIC)
+ ldap_stop();
+
+ return (res);
+}
+
+
+/* This function will parse the dhcpOption and dhcpStatements field in the LDAP
+ entry if it exists. Right now, type will be either HOST_DECL or CLASS_DECL.
+ If we are parsing a HOST_DECL, this always returns 0. If we are parsing a
+ CLASS_DECL, this will return what the current lease limit is in LDAP. If
+ there is no lease limit specified, we return 0 */
+
+static int
+ldap_parse_options (LDAPMessage * ent, struct group *group,
+ int type, struct host_decl *host,
+ struct class **class)
+{
+ int declaration, lease_limit;
+ char option_buffer[8192];
+ enum dhcp_token token;
+ struct parse *cfile;
+ isc_result_t res;
+ const char *val;
+
+ lease_limit = 0;
+ *option_buffer = '\0';
+
+ /* This block of code will try to find the parent of the host, and
+ if it is a group object, fetch the options and apply to the host. */
+ if (type == HOST_DECL)
+ {
+ char *hostdn, *basedn, *temp1, *temp2, filter[1024];
+ LDAPMessage *groupdn, *entry;
+ int ret;
+
+ hostdn = ldap_get_dn (ld, ent);
+ if( hostdn != NULL)
+ {
+ basedn = NULL;
+
+ temp1 = strchr (hostdn, '=');
+ if (temp1 != NULL)
+ temp1 = strchr (++temp1, '=');
+ if (temp1 != NULL)
+ temp2 = strchr (++temp1, ',');
+ else
+ temp2 = NULL;
+
+ if (temp2 != NULL)
+ {
+ snprintf (filter, sizeof(filter),
+ "(&(cn=%.*s)(objectClass=dhcpGroup))",
+ (int)(temp2 - temp1), temp1);
+
+ basedn = strchr (temp1, ',');
+ if (basedn != NULL)
+ ++basedn;
+ }
+
+ if (basedn != NULL && *basedn != '\0')
+ {
+ ret = ldap_search_ext_s (ld, basedn, LDAP_SCOPE_SUBTREE, filter,
+ NULL, 0, NULL, NULL, NULL, 0, &groupdn);
+ if (ret == LDAP_SUCCESS)
+ {
+ if ((entry = ldap_first_entry (ld, groupdn)) != NULL)
+ {
+ res = ldap_parse_entry_options (entry, option_buffer,
+ sizeof(option_buffer) - 1,
+ &lease_limit);
+ if (res != ISC_R_SUCCESS)
+ {
+ /* reset option buffer discarding any results */
+ *option_buffer = '\0';
+ lease_limit = 0;
+ }
+ }
+ ldap_msgfree( groupdn);
+ }
+ }
+ ldap_memfree( hostdn);
+ }
+ }
+
+ res = ldap_parse_entry_options (ent, option_buffer, sizeof(option_buffer) - 1,
+ &lease_limit);
+ if (res != ISC_R_SUCCESS)
+ return (lease_limit);
+
+ option_buffer[sizeof(option_buffer) - 1] = '\0';
+ if (*option_buffer == '\0')
+ return (lease_limit);
+
+ cfile = (struct parse *) NULL;
+ res = new_parse (&cfile, -1, option_buffer, strlen (option_buffer),
+ type == HOST_DECL ? "LDAP-HOST" : "LDAP-SUBCLASS", 0);
+ if (res != ISC_R_SUCCESS)
+ return (lease_limit);
+
+#if defined (DEBUG_LDAP)
+ log_info ("Sending the following options: '%s'", option_buffer);
+#endif
+
+ declaration = 0;
+ do
+ {
+ token = peek_token (&val, NULL, cfile);
+ if (token == END_OF_FILE)
+ break;
+ declaration = parse_statement (cfile, group, type, host, declaration);
+ } while (1);
+
+ end_parse (&cfile);
+
+ return (lease_limit);
+}
+
+
+
+int
+find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen,
+ const unsigned char *haddr, const char *file, int line)
+{
+ char buf[128], *type_str;
+ LDAPMessage * res, *ent;
+ struct host_decl * host;
+ isc_result_t status;
+ ldap_dn_node *curr;
+ int ret;
+
+ if (ldap_method == LDAP_METHOD_STATIC)
+ return (0);
+
+ if (ld == NULL)
+ ldap_start ();
+ if (ld == NULL)
+ return (0);
+
+ switch (htype)
+ {
+ case HTYPE_ETHER:
+ type_str = "ethernet";
+ break;
+ case HTYPE_IEEE802:
+ type_str = "token-ring";
+ break;
+ case HTYPE_FDDI:
+ type_str = "fddi";
+ break;
+ default:
+ log_info ("Ignoring unknown type %d", htype);
+ return (0);
+ }
+
+ /*
+ ** FIXME: It is not guaranteed, that the dhcpHWAddress attribute
+ ** contains _exactly_ "type addr" with one space between!
+ */
+ snprintf (buf, sizeof (buf),
+ "(&(objectClass=dhcpHost)(dhcpHWAddress=%s %s))",
+ type_str, print_hw_addr (htype, hlen, haddr));
+
+ res = ent = NULL;
+ for (curr = ldap_service_dn_head;
+ curr != NULL && *curr->dn != '\0';
+ curr = curr->next)
+ {
+#if defined (DEBUG_LDAP)
+ log_info ("Searching for %s in LDAP tree %s", buf, curr->dn);
+#endif
+ ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0,
+ NULL, NULL, NULL, 0, &res);
+
+ if(ret == LDAP_SERVER_DOWN)
+ {
+ log_info ("LDAP server was down, trying to reconnect...");
+
+ ldap_stop();
+ ldap_start();
+ if(ld == NULL)
+ {
+ log_info ("LDAP reconnect failed - try again later...");
+ return (0);
+ }
+
+ ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL,
+ 0, NULL, NULL, NULL, 0, &res);
+ }
+
+ if (ret == LDAP_SUCCESS)
+ {
+ if( (ent = ldap_first_entry (ld, res)) != NULL)
+ break; /* search OK and have entry */
+
+#if defined (DEBUG_LDAP)
+ log_info ("No host entry for %s in LDAP tree %s",
+ buf, curr->dn);
+#endif
+ if(res)
+ {
+ ldap_msgfree (res);
+ res = NULL;
+ }
+ }
+ else
+ {
+ if(res)
+ {
+ ldap_msgfree (res);
+ res = NULL;
+ }
+
+ if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS)
+ {
+ log_error ("Cannot search for %s in LDAP tree %s: %s", buf,
+ curr->dn, ldap_err2string (ret));
+ ldap_stop();
+ return (0);
+ }
+#if defined (DEBUG_LDAP)
+ else
+ {
+ log_info ("ldap_search_ext_s returned %s when searching for %s in %s",
+ ldap_err2string (ret), buf, curr->dn);
+ }
+#endif
+ }
+ }
+
+ if (res && ent)
+ {
+#if defined (DEBUG_LDAP)
+ char *dn = ldap_get_dn (ld, ent);
+ if (dn != NULL)
+ {
+ log_info ("Found dhcpHWAddress LDAP entry %s", dn);
+ ldap_memfree(dn);
+ }
+#endif
+
+ host = (struct host_decl *)0;
+ status = host_allocate (&host, MDL);
+ if (status != ISC_R_SUCCESS)
+ {
+ log_fatal ("can't allocate host decl struct: %s",
+ isc_result_totext (status));
+ ldap_msgfree (res);
+ return (0);
+ }
+
+ host->name = ldap_get_host_name (ent);
+ if (host->name == NULL)
+ {
+ host_dereference (&host, MDL);
+ ldap_msgfree (res);
+ return (0);
+ }
+
+ if (!clone_group (&host->group, root_group, MDL))
+ {
+ log_fatal ("can't clone group for host %s", host->name);
+ host_dereference (&host, MDL);
+ ldap_msgfree (res);
+ return (0);
+ }
+
+ ldap_parse_options (ent, host->group, HOST_DECL, host, NULL);
+
+ *hp = host;
+ ldap_msgfree (res);
+ return (1);
+ }
+
+
+ if(res) ldap_msgfree (res);
+ return (0);
+}
+
+
+int
+find_subclass_in_ldap (struct class *class, struct class **newclass,
+ struct data_string *data)
+{
+ LDAPMessage * res, * ent;
+ int ret, lease_limit;
+ isc_result_t status;
+ ldap_dn_node *curr;
+ char buf[1024];
+
+ if (ldap_method == LDAP_METHOD_STATIC)
+ return (0);
+
+ if (ld == NULL)
+ ldap_start ();
+ if (ld == NULL)
+ return (0);
+
+ snprintf (buf, sizeof (buf),
+ "(&(objectClass=dhcpSubClass)(cn=%s)(dhcpClassData=%s))",
+ print_hex_1 (data->len, data->data, 60),
+ print_hex_2 (strlen (class->name), (u_int8_t *) class->name, 60));
+#if defined (DEBUG_LDAP)
+ log_info ("Searching LDAP for %s", buf);
+#endif
+
+ res = ent = NULL;
+ for (curr = ldap_service_dn_head;
+ curr != NULL && *curr->dn != '\0';
+ curr = curr->next)
+ {
+#if defined (DEBUG_LDAP)
+ log_info ("Searching for %s in LDAP tree %s", buf, curr->dn);
+#endif
+ ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf, NULL, 0,
+ NULL, NULL, NULL, 0, &res);
+
+ if(ret == LDAP_SERVER_DOWN)
+ {
+ log_info ("LDAP server was down, trying to reconnect...");
+
+ ldap_stop();
+ ldap_start();
+
+ if(ld == NULL)
+ {
+ log_info ("LDAP reconnect failed - try again later...");
+ return (0);
+ }
+
+ ret = ldap_search_ext_s (ld, curr->dn, LDAP_SCOPE_SUBTREE, buf,
+ NULL, 0, NULL, NULL, NULL, 0, &res);
+ }
+
+ if (ret == LDAP_SUCCESS)
+ {
+ if( (ent = ldap_first_entry (ld, res)) != NULL)
+ break; /* search OK and have entry */
+
+#if defined (DEBUG_LDAP)
+ log_info ("No subclass entry for %s in LDAP tree %s",
+ buf, curr->dn);
+#endif
+ if(res)
+ {
+ ldap_msgfree (res);
+ res = NULL;
+ }
+ }
+ else
+ {
+ if(res)
+ {
+ ldap_msgfree (res);
+ res = NULL;
+ }
+
+ if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS)
+ {
+ log_error ("Cannot search for %s in LDAP tree %s: %s", buf,
+ curr->dn, ldap_err2string (ret));
+ ldap_stop();
+ return (0);
+ }
+#if defined (DEBUG_LDAP)
+ else
+ {
+ log_info ("ldap_search_ext_s returned %s when searching for %s in %s",
+ ldap_err2string (ret), buf, curr->dn);
+ }
+#endif
+ }
+ }
+
+ if (res && ent)
+ {
+#if defined (DEBUG_LDAP)
+ char *dn = ldap_get_dn (ld, ent);
+ if (dn != NULL)
+ {
+ log_info ("Found subclass LDAP entry %s", dn);
+ ldap_memfree(dn);
+ }
+#endif
+
+ status = class_allocate (newclass, MDL);
+ if (status != ISC_R_SUCCESS)
+ {
+ log_error ("Cannot allocate memory for a new class");
+ ldap_msgfree (res);
+ return (0);
+ }
+
+ group_reference (&(*newclass)->group, class->group, MDL);
+ class_reference (&(*newclass)->superclass, class, MDL);
+ lease_limit = ldap_parse_options (ent, (*newclass)->group,
+ CLASS_DECL, NULL, newclass);
+ if (lease_limit == 0)
+ (*newclass)->lease_limit = class->lease_limit;
+ else
+ class->lease_limit = lease_limit;
+
+ if ((*newclass)->lease_limit)
+ {
+ (*newclass)->billed_leases =
+ dmalloc ((*newclass)->lease_limit * sizeof (struct lease *), MDL);
+ if (!(*newclass)->billed_leases)
+ {
+ log_error ("no memory for billing");
+ class_dereference (newclass, MDL);
+ ldap_msgfree (res);
+ return (0);
+ }
+ memset ((*newclass)->billed_leases, 0,
+ ((*newclass)->lease_limit * sizeof (*newclass)->billed_leases));
+ }
+
+ data_string_copy (&(*newclass)->hash_string, data, MDL);
+
+ ldap_msgfree (res);
+ return (1);
+ }
+
+ if(res) ldap_msgfree (res);
+ return (0);
+}
+
+#endif
diff --git a/server/ldap_casa.c b/server/ldap_casa.c
new file mode 100644
index 0000000..952d9b9
--- /dev/null
+++ b/server/ldap_casa.c
@@ -0,0 +1,159 @@
+/* ldap_casa.c
+
+ CASA routines for DHCPD... */
+
+/* Copyright (c) 2006 Novell, Inc.
+
+ * 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 ISC, ISC DHCP, 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 INTERNET SYSTEMS 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 ISC 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 file was written by S Kalyanasundaram <skalyanasundaram@novell.com>
+ */
+
+/*
+ * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ */
+
+#if defined(LDAP_CASA_AUTH)
+#include "ldap_casa.h"
+#include "dhcpd.h"
+
+int
+load_casa (void)
+{
+ if( !(casaIDK = dlopen(MICASA_LIB,RTLD_LAZY)))
+ return 0;
+ p_miCASAGetCredential = (CASA_GetCredential_T) dlsym(casaIDK, "miCASAGetCredential");
+ p_miCASASetCredential = (CASA_SetCredential_T) dlsym(casaIDK, "miCASASetCredential");
+ p_miCASARemoveCredential = (CASA_RemoveCredential_T) dlsym(casaIDK, "miCASARemoveCredential");
+
+ if((p_miCASAGetCredential == NULL) ||
+ (p_miCASASetCredential == NULL) ||
+ (p_miCASARemoveCredential == NULL))
+ {
+ if(casaIDK)
+ dlclose(casaIDK);
+ casaIDK = NULL;
+ p_miCASAGetCredential = NULL;
+ p_miCASASetCredential = NULL;
+ p_miCASARemoveCredential = NULL;
+ return 0;
+ }
+ else
+ return 1;
+}
+
+static void
+release_casa(void)
+{
+ if(casaIDK)
+ {
+ dlclose(casaIDK);
+ casaIDK = NULL;
+ }
+
+ p_miCASAGetCredential = NULL;
+ p_miCASASetCredential = NULL;
+ p_miCASARemoveCredential = NULL;
+
+}
+
+int
+load_uname_pwd_from_miCASA (char **ldap_username, char **ldap_password)
+ {
+ int result = 0;
+ uint32_t credentialtype = SSCS_CRED_TYPE_SERVER_F;
+ SSCS_BASIC_CREDENTIAL credential;
+ SSCS_SECRET_ID_T applicationSecretId;
+ char *tempVar = NULL;
+
+ const char applicationName[10] = "dhcp-ldap";
+
+ if ( load_casa() )
+ {
+ memset(&credential, 0, sizeof(SSCS_BASIC_CREDENTIAL));
+ memset(&applicationSecretId, 0, sizeof(SSCS_SECRET_ID_T));
+
+ applicationSecretId.len = strlen(applicationName) + 1;
+ memcpy (applicationSecretId.id, applicationName, applicationSecretId.len);
+
+ credential.unFlags = USERNAME_TYPE_CN_F;
+
+ result = p_miCASAGetCredential (0,
+ &applicationSecretId,NULL,&credentialtype,
+ &credential,NULL);
+
+ if(credential.unLen)
+ {
+ tempVar = dmalloc (credential.unLen + 1, MDL);
+ if (!tempVar)
+ log_fatal ("no memory for ldap_username");
+ memcpy(tempVar , credential.username, credential.unLen);
+ *ldap_username = tempVar;
+
+ tempVar = dmalloc (credential.pwordLen + 1, MDL);
+ if (!tempVar)
+ log_fatal ("no memory for ldap_password");
+ memcpy(tempVar, credential.password, credential.pwordLen);
+ *ldap_password = tempVar;
+
+#if defined (DEBUG_LDAP)
+ log_info ("Authentication credential taken from CASA");
+#endif
+
+ release_casa();
+ return 1;
+
+ }
+ else
+ {
+ release_casa();
+ return 0;
+ }
+ }
+ else
+ return 0; //casa libraries not loaded
+ }
+
+#endif /* LDAP_CASA_AUTH */
+
diff --git a/server/mdb.c b/server/mdb.c
new file mode 100644
index 0000000..c5bf73e
--- /dev/null
+++ b/server/mdb.c
@@ -0,0 +1,2989 @@
+/* mdb.c
+
+ Server-specific in-memory database support. */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include "omapip/hash.h"
+
+struct subnet *subnets;
+struct shared_network *shared_networks;
+host_hash_t *host_hw_addr_hash;
+host_hash_t *host_uid_hash;
+host_hash_t *host_name_hash;
+lease_id_hash_t *lease_uid_hash;
+lease_ip_hash_t *lease_ip_addr_hash;
+lease_id_hash_t *lease_hw_addr_hash;
+
+/*
+ * We allow users to specify any option as a host identifier.
+ *
+ * Any host is uniquely identified by the combination of
+ * option type & option data.
+ *
+ * We expect people will only use a few types of options as host
+ * identifier. Because of this, we store a list with an entry for
+ * each option type. Each of these has a hash table, which contains
+ * hash of the option data.
+ */
+typedef struct host_id_info {
+ struct option *option;
+ host_hash_t *values_hash;
+ struct host_id_info *next;
+} host_id_info_t;
+
+static host_id_info_t *host_id_info = NULL;
+
+int numclasseswritten;
+
+omapi_object_type_t *dhcp_type_host;
+
+isc_result_t enter_class(cd, dynamicp, commit)
+ struct class *cd;
+ int dynamicp;
+ int commit;
+{
+ if (!collections -> classes) {
+ /* A subclass with no parent is invalid. */
+ if (cd->name == NULL)
+ return DHCP_R_INVALIDARG;
+
+ class_reference (&collections -> classes, cd, MDL);
+ } else if (cd->name != NULL) { /* regular class */
+ struct class *c = 0;
+
+ if (find_class(&c, cd->name, MDL) != ISC_R_NOTFOUND) {
+ class_dereference(&c, MDL);
+ return ISC_R_EXISTS;
+ }
+
+ /* Find the tail. */
+ for (c = collections -> classes;
+ c -> nic; c = c -> nic)
+ /* nothing */ ;
+ class_reference (&c -> nic, cd, MDL);
+ }
+
+ if (dynamicp && commit) {
+ const char *name = cd->name;
+
+ if (name == NULL) {
+ name = cd->superclass->name;
+ }
+
+ write_named_billing_class ((const unsigned char *)name, 0, cd);
+ if (!commit_leases ())
+ return ISC_R_IOERROR;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+
+/* Variable to check if we're starting the server. The server will init as
+ * starting - but just to be safe start out as false to avoid triggering new
+ * special-case code
+ * XXX: There is actually a server_startup state...which is never entered...
+ */
+#define SS_NOSYNC 1
+#define SS_QFOLLOW 2
+static int server_starting = 0;
+
+static int find_uid_statement (struct executable_statement *esp,
+ void *vp, int condp)
+{
+ struct executable_statement **evp = vp;
+
+ if (esp -> op == supersede_option_statement &&
+ esp -> data.option &&
+ (esp -> data.option -> option -> universe ==
+ &dhcp_universe) &&
+ (esp -> data.option -> option -> code ==
+ DHO_DHCP_CLIENT_IDENTIFIER)) {
+ if (condp) {
+ log_error ("dhcp client identifier may not be %s",
+ "specified conditionally.");
+ } else if (!(*evp)) {
+ executable_statement_reference (evp, esp, MDL);
+ return 1;
+ } else {
+ log_error ("only one dhcp client identifier may be %s",
+ "specified");
+ }
+ }
+ return 0;
+}
+
+
+static host_id_info_t *
+find_host_id_info(unsigned int option_code) {
+ host_id_info_t *p;
+
+ for (p=host_id_info; p != NULL; p = p->next) {
+ if (p->option->code == option_code) {
+ break;
+ }
+ }
+ return p;
+}
+
+/* Debugging code */
+#if 0
+isc_result_t
+print_host(const void *name, unsigned len, void *value) {
+ struct host_decl *h;
+ printf("--------------\n");
+ printf("name:'%s'\n", print_hex_1(len, name, 60));
+ printf("len:%d\n", len);
+ h = (struct host_decl *)value;
+ printf("host @%p is '%s'\n", h, h->name);
+ return ISC_R_SUCCESS;
+}
+
+void
+hash_print_hosts(struct hash_table *h) {
+ hash_foreach(h, print_host);
+ printf("--------------\n");
+}
+#endif /* 0 */
+
+void
+change_host_uid(struct host_decl *host, const char *uid, int len) {
+ /* XXX: should consolidate this type of code throughout */
+ if (host_uid_hash == NULL) {
+ if (!host_new_hash(&host_uid_hash, HOST_HASH_SIZE, MDL)) {
+ log_fatal("Can't allocate host/uid hash");
+ }
+ }
+
+ /*
+ * Remove the old entry, if one exists.
+ */
+ if (host->client_identifier.data != NULL) {
+ host_hash_delete(host_uid_hash,
+ host->client_identifier.data,
+ host->client_identifier.len,
+ MDL);
+ data_string_forget(&host->client_identifier, MDL);
+ }
+
+ /*
+ * Set our new value.
+ */
+ memset(&host->client_identifier, 0, sizeof(host->client_identifier));
+ host->client_identifier.len = len;
+ if (!buffer_allocate(&host->client_identifier.buffer, len, MDL)) {
+ log_fatal("Can't allocate uid buffer");
+ }
+ host->client_identifier.data = host->client_identifier.buffer->data;
+ memcpy((char *)host->client_identifier.data, uid, len);
+
+ /*
+ * And add to hash.
+ */
+ host_hash_add(host_uid_hash, host->client_identifier.data,
+ host->client_identifier.len, host, MDL);
+}
+
+isc_result_t enter_host (hd, dynamicp, commit)
+ struct host_decl *hd;
+ int dynamicp;
+ int commit;
+{
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *np = (struct host_decl *)0;
+ struct executable_statement *esp;
+ host_id_info_t *h_id_info;
+
+ if (!host_name_hash) {
+ if (!host_new_hash(&host_name_hash, HOST_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate host name hash");
+ host_hash_add (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), hd, MDL);
+ } else {
+ host_hash_lookup (&hp, host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), MDL);
+
+ /* If it's deleted, we can supersede it. */
+ if (hp && (hp -> flags & HOST_DECL_DELETED)) {
+ host_hash_delete (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), MDL);
+ /* If the old entry wasn't dynamic, then we
+ always have to keep the deletion. */
+ if (hp -> flags & HOST_DECL_STATIC) {
+ hd -> flags |= HOST_DECL_STATIC;
+ }
+ host_dereference (&hp, MDL);
+ }
+
+ /* If we are updating an existing host declaration, we
+ can just delete it and add it again. */
+ if (hp && hp == hd) {
+ host_dereference (&hp, MDL);
+ delete_host (hd, 0);
+ if (!write_host (hd))
+ return ISC_R_IOERROR;
+ hd -> flags &= ~HOST_DECL_DELETED;
+ }
+
+ /* If there isn't already a host decl matching this
+ address, add it to the hash table. */
+ if (!hp) {
+ host_hash_add (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), hd, MDL);
+ } else {
+ /* XXX actually, we have to delete the old one
+ XXX carefully and replace it. Not done yet. */
+ host_dereference (&hp, MDL);
+ return ISC_R_EXISTS;
+ }
+ }
+
+ if (hd -> n_ipaddr)
+ host_dereference (&hd -> n_ipaddr, MDL);
+
+ if (!hd -> type)
+ hd -> type = dhcp_type_host;
+
+ if (hd -> interface.hlen) {
+ if (!host_hw_addr_hash) {
+ if (!host_new_hash(&host_hw_addr_hash,
+ HOST_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate host/hw hash");
+ } else {
+ /* If there isn't already a host decl matching this
+ address, add it to the hash table. */
+ host_hash_lookup (&hp, host_hw_addr_hash,
+ hd -> interface.hbuf,
+ hd -> interface.hlen, MDL);
+ }
+ if (!hp)
+ host_hash_add (host_hw_addr_hash, hd -> interface.hbuf,
+ hd -> interface.hlen, hd, MDL);
+ else {
+ /* If there was already a host declaration for
+ this hardware address, add this one to the
+ end of the list. */
+ for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr)
+ ;
+ host_reference (&np -> n_ipaddr, hd, MDL);
+ host_dereference (&hp, MDL);
+ }
+ }
+
+ /* See if there's a statement that sets the client identifier.
+ This is a kludge - the client identifier really shouldn't be
+ set with an executable statement. */
+ esp = (struct executable_statement *)0;
+ if (executable_statement_foreach (hd -> group -> statements,
+ find_uid_statement, &esp, 0)) {
+ evaluate_option_cache (&hd -> client_identifier,
+ (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0, &global_scope,
+ esp -> data.option, MDL);
+ }
+
+ /* If we got a client identifier, hash this entry by
+ client identifier. */
+ if (hd -> client_identifier.len) {
+ /* If there's no uid hash, make one; otherwise, see if
+ there's already an entry in the hash for this host. */
+ if (!host_uid_hash) {
+ if (!host_new_hash(&host_uid_hash,
+ HOST_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate host/uid hash");
+
+ host_hash_add (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len,
+ hd, MDL);
+ } else {
+ /* If there's already a host declaration for this
+ client identifier, add this one to the end of the
+ list. Otherwise, add it to the hash table. */
+ if (host_hash_lookup (&hp, host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len,
+ MDL)) {
+ /* Don't link it in twice... */
+ if (!np) {
+ for (np = hp; np -> n_ipaddr;
+ np = np -> n_ipaddr) {
+ if (hd == np)
+ break;
+ }
+ if (hd != np)
+ host_reference (&np -> n_ipaddr,
+ hd, MDL);
+ }
+ host_dereference (&hp, MDL);
+ } else {
+ host_hash_add (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len,
+ hd, MDL);
+ }
+ }
+ }
+
+
+ /*
+ * If we use an option as our host identifier, record it here.
+ */
+ if (hd->host_id_option != NULL) {
+ /*
+ * Look for the host identifier information for this option,
+ * and create a new entry if there is none.
+ */
+ h_id_info = find_host_id_info(hd->host_id_option->code);
+ if (h_id_info == NULL) {
+ h_id_info = dmalloc(sizeof(*h_id_info), MDL);
+ if (h_id_info == NULL) {
+ log_fatal("No memory for host-identifier "
+ "option information.");
+ }
+ option_reference(&h_id_info->option,
+ hd->host_id_option, MDL);
+ if (!host_new_hash(&h_id_info->values_hash,
+ HOST_HASH_SIZE, MDL)) {
+ log_fatal("No memory for host-identifier "
+ "option hash.");
+ }
+ h_id_info->next = host_id_info;
+ host_id_info = h_id_info;
+ }
+
+ if (host_hash_lookup(&hp, h_id_info->values_hash,
+ hd->host_id.data, hd->host_id.len, MDL)) {
+ /*
+ * If this option is already present, then add
+ * this host to the list in n_ipaddr, unless
+ * we have already done so previously.
+ *
+ * XXXSK: This seems scary to me, but I don't
+ * fully understand how these are used.
+ * Shouldn't there be multiple lists, or
+ * maybe we should just forbid duplicates?
+ */
+ if (np == NULL) {
+ np = hp;
+ while (np->n_ipaddr != NULL) {
+ np = np->n_ipaddr;
+ }
+ if (hd != np) {
+ host_reference(&np->n_ipaddr, hd, MDL);
+ }
+ }
+ host_dereference(&hp, MDL);
+ } else {
+ host_hash_add(h_id_info->values_hash,
+ hd->host_id.data,
+ hd->host_id.len,
+ hd, MDL);
+ }
+ }
+
+ if (dynamicp && commit) {
+ if (!write_host (hd))
+ return ISC_R_IOERROR;
+ if (!commit_leases ())
+ return ISC_R_IOERROR;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+
+isc_result_t delete_class (cp, commit)
+ struct class *cp;
+ int commit;
+{
+ cp->flags |= CLASS_DECL_DELETED;
+
+ /* do the write first as we won't be leaving it in any data
+ structures, unlike the host objects */
+
+ if (commit) {
+ write_named_billing_class ((unsigned char *)cp->name, 0, cp);
+ if (!commit_leases ())
+ return ISC_R_IOERROR;
+ }
+
+ unlink_class(&cp); /* remove from collections */
+
+ class_dereference(&cp, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+
+isc_result_t delete_host (hd, commit)
+ struct host_decl *hd;
+ int commit;
+{
+ struct host_decl *hp = (struct host_decl *)0;
+ struct host_decl *np = (struct host_decl *)0;
+ struct host_decl *foo;
+ int hw_head = 0, uid_head = 1;
+
+ /* Don't need to do it twice. */
+ if (hd -> flags & HOST_DECL_DELETED)
+ return ISC_R_SUCCESS;
+
+ /* But we do need to do it once! :') */
+ hd -> flags |= HOST_DECL_DELETED;
+
+ if (hd -> interface.hlen) {
+ if (host_hw_addr_hash) {
+ if (host_hash_lookup (&hp, host_hw_addr_hash,
+ hd -> interface.hbuf,
+ hd -> interface.hlen, MDL)) {
+ if (hp == hd) {
+ host_hash_delete (host_hw_addr_hash,
+ hd -> interface.hbuf,
+ hd -> interface.hlen, MDL);
+ hw_head = 1;
+ } else {
+ np = (struct host_decl *)0;
+ foo = (struct host_decl *)0;
+ host_reference (&foo, hp, MDL);
+ while (foo) {
+ if (foo == hd)
+ break;
+ if (np)
+ host_dereference (&np, MDL);
+ host_reference (&np, foo, MDL);
+ host_dereference (&foo, MDL);
+ if (np -> n_ipaddr)
+ host_reference (&foo, np -> n_ipaddr, MDL);
+ }
+
+ if (foo) {
+ host_dereference (&np -> n_ipaddr, MDL);
+ if (hd -> n_ipaddr)
+ host_reference (&np -> n_ipaddr,
+ hd -> n_ipaddr, MDL);
+ host_dereference (&foo, MDL);
+ }
+ if (np)
+ host_dereference (&np, MDL);
+ }
+ host_dereference (&hp, MDL);
+ }
+ }
+ }
+
+ /* If we got a client identifier, hash this entry by
+ client identifier. */
+ if (hd -> client_identifier.len) {
+ if (host_uid_hash) {
+ if (host_hash_lookup (&hp, host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len, MDL)) {
+ if (hp == hd) {
+ host_hash_delete (host_uid_hash,
+ hd -> client_identifier.data,
+ hd -> client_identifier.len, MDL);
+ uid_head = 1;
+ } else {
+ np = (struct host_decl *)0;
+ foo = (struct host_decl *)0;
+ host_reference (&foo, hp, MDL);
+ while (foo) {
+ if (foo == hd)
+ break;
+ if (np)
+ host_dereference (&np, MDL);
+ host_reference (&np, foo, MDL);
+ host_dereference (&foo, MDL);
+ if (np -> n_ipaddr)
+ host_reference (&foo, np -> n_ipaddr, MDL);
+ }
+
+ if (foo) {
+ host_dereference (&np -> n_ipaddr, MDL);
+ if (hd -> n_ipaddr)
+ host_reference (&np -> n_ipaddr,
+ hd -> n_ipaddr, MDL);
+ host_dereference (&foo, MDL);
+ }
+ if (np)
+ host_dereference (&np, MDL);
+ }
+ host_dereference (&hp, MDL);
+ }
+ }
+ }
+
+ if (hd->host_id_option != NULL) {
+ option_dereference(&hd->host_id_option, MDL);
+ data_string_forget(&hd->host_id, MDL);
+ }
+
+ if (hd -> n_ipaddr) {
+ if (uid_head && hd -> n_ipaddr -> client_identifier.len) {
+ host_hash_add
+ (host_uid_hash,
+ hd -> n_ipaddr -> client_identifier.data,
+ hd -> n_ipaddr -> client_identifier.len,
+ hd -> n_ipaddr, MDL);
+ }
+ if (hw_head && hd -> n_ipaddr -> interface.hlen) {
+ host_hash_add (host_hw_addr_hash,
+ hd -> n_ipaddr -> interface.hbuf,
+ hd -> n_ipaddr -> interface.hlen,
+ hd -> n_ipaddr, MDL);
+ }
+ host_dereference (&hd -> n_ipaddr, MDL);
+ }
+
+ if (host_name_hash) {
+ if (host_hash_lookup (&hp, host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), MDL)) {
+ if (hp == hd && !(hp -> flags & HOST_DECL_STATIC)) {
+ host_hash_delete (host_name_hash,
+ (unsigned char *)hd -> name,
+ strlen (hd -> name), MDL);
+ }
+ host_dereference (&hp, MDL);
+ }
+ }
+
+ if (commit) {
+ if (!write_host (hd))
+ return ISC_R_IOERROR;
+ if (!commit_leases ())
+ return ISC_R_IOERROR;
+ }
+ return ISC_R_SUCCESS;
+}
+
+int find_hosts_by_haddr (struct host_decl **hp, int htype,
+ const unsigned char *haddr, unsigned hlen,
+ const char *file, int line)
+{
+ struct hardware h;
+#if defined(LDAP_CONFIGURATION)
+ int ret;
+
+ if ((ret = find_haddr_in_ldap (hp, htype, hlen, haddr, file, line)))
+ return ret;
+#endif
+
+ h.hlen = hlen + 1;
+ h.hbuf [0] = htype;
+ memcpy (&h.hbuf [1], haddr, hlen);
+
+ return host_hash_lookup (hp, host_hw_addr_hash,
+ h.hbuf, h.hlen, file, line);
+}
+
+int find_hosts_by_uid (struct host_decl **hp,
+ const unsigned char *data, unsigned len,
+ const char *file, int line)
+{
+ return host_hash_lookup (hp, host_uid_hash, data, len, file, line);
+}
+
+int
+find_hosts_by_option(struct host_decl **hp,
+ struct packet *packet,
+ struct option_state *opt_state,
+ const char *file, int line) {
+ host_id_info_t *p;
+ struct option_cache *oc;
+ struct data_string data;
+ int found;
+
+ for (p = host_id_info; p != NULL; p = p->next) {
+ oc = lookup_option(p->option->universe,
+ opt_state, p->option->code);
+ if (oc != NULL) {
+ memset(&data, 0, sizeof(data));
+ if (!evaluate_option_cache(&data, packet, NULL, NULL,
+ opt_state, NULL,
+ &global_scope, oc,
+ MDL)) {
+ log_error("Error evaluating option cache");
+ return 0;
+ }
+
+ found = host_hash_lookup(hp, p->values_hash,
+ data.data, data.len,
+ file, line);
+
+ data_string_forget(&data, MDL);
+
+ if (found) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* More than one host_decl can be returned by find_hosts_by_haddr or
+ find_hosts_by_uid, and each host_decl can have multiple addresses.
+ Loop through the list of hosts, and then for each host, through the
+ list of addresses, looking for an address that's in the same shared
+ network as the one specified. Store the matching address through
+ the addr pointer, update the host pointer to point at the host_decl
+ that matched, and return the subnet that matched. */
+
+int find_host_for_network (struct subnet **sp, struct host_decl **host,
+ struct iaddr *addr, struct shared_network *share)
+{
+ int i;
+ struct iaddr ip_address;
+ struct host_decl *hp;
+ struct data_string fixed_addr;
+
+ memset (&fixed_addr, 0, sizeof fixed_addr);
+
+ for (hp = *host; hp; hp = hp -> n_ipaddr) {
+ if (!hp -> fixed_addr)
+ continue;
+ if (!evaluate_option_cache (&fixed_addr, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ hp -> fixed_addr, MDL))
+ continue;
+ for (i = 0; i < fixed_addr.len; i += 4) {
+ ip_address.len = 4;
+ memcpy (ip_address.iabuf,
+ fixed_addr.data + i, 4);
+ if (find_grouped_subnet (sp, share, ip_address, MDL)) {
+ struct host_decl *tmp = (struct host_decl *)0;
+ *addr = ip_address;
+ /* This is probably not necessary, but
+ just in case *host is the only reference
+ to that host declaration, make a temporary
+ reference so that dereferencing it doesn't
+ dereference hp out from under us. */
+ host_reference (&tmp, *host, MDL);
+ host_dereference (host, MDL);
+ host_reference (host, hp, MDL);
+ host_dereference (&tmp, MDL);
+ data_string_forget (&fixed_addr, MDL);
+ return 1;
+ }
+ }
+ data_string_forget (&fixed_addr, MDL);
+ }
+ return 0;
+}
+
+void new_address_range (cfile, low, high, subnet, pool, lpchain)
+ struct parse *cfile;
+ struct iaddr low, high;
+ struct subnet *subnet;
+ struct pool *pool;
+ struct lease **lpchain;
+{
+#if defined(COMPACT_LEASES)
+ struct lease *address_range;
+#endif
+ unsigned min, max, i;
+ char lowbuf [16], highbuf [16], netbuf [16];
+ struct shared_network *share = subnet -> shared_network;
+ struct lease *lt = (struct lease *)0;
+#if !defined(COMPACT_LEASES)
+ isc_result_t status;
+#endif
+
+ /* All subnets should have attached shared network structures. */
+ if (!share) {
+ strcpy (netbuf, piaddr (subnet -> net));
+ log_fatal ("No shared network for network %s (%s)",
+ netbuf, piaddr (subnet -> netmask));
+ }
+
+ /* Initialize the hash table if it hasn't been done yet. */
+ if (!lease_uid_hash) {
+ if (!lease_id_new_hash(&lease_uid_hash, LEASE_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate lease/uid hash");
+ }
+ if (!lease_ip_addr_hash) {
+ if (!lease_ip_new_hash(&lease_ip_addr_hash, LEASE_HASH_SIZE,
+ MDL))
+ log_fatal ("Can't allocate lease/ip hash");
+ }
+ if (!lease_hw_addr_hash) {
+ if (!lease_id_new_hash(&lease_hw_addr_hash, LEASE_HASH_SIZE,
+ MDL))
+ log_fatal ("Can't allocate lease/hw hash");
+ }
+
+ /* Make sure that high and low addresses are in this subnet. */
+ if (!addr_eq(subnet->net, subnet_number(low, subnet->netmask))) {
+ strcpy(lowbuf, piaddr(low));
+ strcpy(netbuf, piaddr(subnet->net));
+ log_fatal("bad range, address %s not in subnet %s netmask %s",
+ lowbuf, netbuf, piaddr(subnet->netmask));
+ }
+
+ if (!addr_eq(subnet->net, subnet_number(high, subnet->netmask))) {
+ strcpy(highbuf, piaddr(high));
+ strcpy(netbuf, piaddr(subnet->net));
+ log_fatal("bad range, address %s not in subnet %s netmask %s",
+ highbuf, netbuf, piaddr(subnet->netmask));
+ }
+
+ /* Get the high and low host addresses... */
+ max = host_addr (high, subnet -> netmask);
+ min = host_addr (low, subnet -> netmask);
+
+ /* Allow range to be specified high-to-low as well as low-to-high. */
+ if (min > max) {
+ max = min;
+ min = host_addr (high, subnet -> netmask);
+ }
+
+ /* Get a lease structure for each address in the range. */
+#if defined (COMPACT_LEASES)
+ address_range = new_leases (max - min + 1, MDL);
+ if (!address_range) {
+ strcpy (lowbuf, piaddr (low));
+ strcpy (highbuf, piaddr (high));
+ log_fatal ("No memory for address range %s-%s.",
+ lowbuf, highbuf);
+ }
+#endif
+
+ /* Fill out the lease structures with some minimal information. */
+ for (i = 0; i < max - min + 1; i++) {
+ struct lease *lp = (struct lease *)0;
+#if defined (COMPACT_LEASES)
+ omapi_object_initialize ((omapi_object_t *)&address_range [i],
+ dhcp_type_lease,
+ 0, sizeof (struct lease), MDL);
+ lease_reference (&lp, &address_range [i], MDL);
+#else
+ status = lease_allocate (&lp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("No memory for lease %s: %s",
+ piaddr (ip_addr (subnet -> net,
+ subnet -> netmask,
+ i + min)),
+ isc_result_totext (status));
+#endif
+ lp->ip_addr = ip_addr(subnet->net, subnet->netmask, i + min);
+ lp->starts = MIN_TIME;
+ lp->ends = MIN_TIME;
+ subnet_reference(&lp->subnet, subnet, MDL);
+ pool_reference(&lp->pool, pool, MDL);
+ lp->binding_state = FTS_FREE;
+ lp->next_binding_state = FTS_FREE;
+ lp->rewind_binding_state = FTS_FREE;
+ lp->flags = 0;
+
+ /* Remember the lease in the IP address hash. */
+ if (find_lease_by_ip_addr (&lt, lp -> ip_addr, MDL)) {
+ if (lt -> pool) {
+ parse_warn (cfile,
+ "lease %s is declared twice!",
+ piaddr (lp -> ip_addr));
+ } else
+ pool_reference (&lt -> pool, pool, MDL);
+ lease_dereference (&lt, MDL);
+ } else
+ lease_ip_hash_add(lease_ip_addr_hash,
+ lp->ip_addr.iabuf, lp->ip_addr.len,
+ lp, MDL);
+ /* Put the lease on the chain for the caller. */
+ if (lpchain) {
+ if (*lpchain) {
+ lease_reference (&lp -> next, *lpchain, MDL);
+ lease_dereference (lpchain, MDL);
+ }
+ lease_reference (lpchain, lp, MDL);
+ }
+ lease_dereference (&lp, MDL);
+ }
+}
+
+int find_subnet (struct subnet **sp,
+ struct iaddr addr, const char *file, int line)
+{
+ struct subnet *rv;
+
+ for (rv = subnets; rv; rv = rv -> next_subnet) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
+ if (subnet_reference (sp, rv,
+ file, line) != ISC_R_SUCCESS)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int find_grouped_subnet (struct subnet **sp,
+ struct shared_network *share, struct iaddr addr,
+ const char *file, int line)
+{
+ struct subnet *rv;
+
+ for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
+ if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net)) {
+ if (subnet_reference (sp, rv,
+ file, line) != ISC_R_SUCCESS)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* XXX: could speed up if everyone had a prefix length */
+int
+subnet_inner_than(const struct subnet *subnet,
+ const struct subnet *scan,
+ int warnp) {
+ if (addr_eq(subnet_number(subnet->net, scan->netmask), scan->net) ||
+ addr_eq(subnet_number(scan->net, subnet->netmask), subnet->net)) {
+ char n1buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255")];
+ int i, j;
+ for (i = 0; i < 128; i++)
+ if (subnet->netmask.iabuf[3 - (i >> 3)]
+ & (1 << (i & 7)))
+ break;
+ for (j = 0; j < 128; j++)
+ if (scan->netmask.iabuf[3 - (j >> 3)] &
+ (1 << (j & 7)))
+ break;
+ if (warnp) {
+ strcpy(n1buf, piaddr(subnet->net));
+ log_error("Warning: subnet %s/%d overlaps subnet %s/%d",
+ n1buf, 32 - i,
+ piaddr(scan->net), 32 - j);
+ }
+ if (i < j)
+ return 1;
+ }
+ return 0;
+}
+
+/* Enter a new subnet into the subnet list. */
+void enter_subnet (subnet)
+ struct subnet *subnet;
+{
+ struct subnet *scan = (struct subnet *)0;
+ struct subnet *next = (struct subnet *)0;
+ struct subnet *prev = (struct subnet *)0;
+
+ /* Check for duplicates... */
+ if (subnets)
+ subnet_reference (&next, subnets, MDL);
+ while (next) {
+ subnet_reference (&scan, next, MDL);
+ subnet_dereference (&next, MDL);
+
+ /* When we find a conflict, make sure that the
+ subnet with the narrowest subnet mask comes
+ first. */
+ if (subnet_inner_than (subnet, scan, 1)) {
+ if (prev) {
+ if (prev -> next_subnet)
+ subnet_dereference (&prev -> next_subnet, MDL);
+ subnet_reference (&prev -> next_subnet, subnet, MDL);
+ subnet_dereference (&prev, MDL);
+ } else {
+ subnet_dereference (&subnets, MDL);
+ subnet_reference (&subnets, subnet, MDL);
+ }
+ subnet_reference (&subnet -> next_subnet, scan, MDL);
+ subnet_dereference (&scan, MDL);
+ return;
+ }
+ subnet_reference (&prev, scan, MDL);
+ subnet_dereference (&scan, MDL);
+ }
+ if (prev)
+ subnet_dereference (&prev, MDL);
+
+ /* XXX use the BSD radix tree code instead of a linked list. */
+ if (subnets) {
+ subnet_reference (&subnet -> next_subnet, subnets, MDL);
+ subnet_dereference (&subnets, MDL);
+ }
+ subnet_reference (&subnets, subnet, MDL);
+}
+
+/* Enter a new shared network into the shared network list. */
+
+void enter_shared_network (share)
+ struct shared_network *share;
+{
+ if (shared_networks) {
+ shared_network_reference (&share -> next,
+ shared_networks, MDL);
+ shared_network_dereference (&shared_networks, MDL);
+ }
+ shared_network_reference (&shared_networks, share, MDL);
+}
+
+void new_shared_network_interface (cfile, share, name)
+ struct parse *cfile;
+ struct shared_network *share;
+ const char *name;
+{
+ struct interface_info *ip;
+ isc_result_t status;
+
+ if (share -> interface) {
+ parse_warn (cfile,
+ "A subnet or shared network can't be connected %s",
+ "to two interfaces.");
+ return;
+ }
+
+ for (ip = interfaces; ip; ip = ip -> next)
+ if (!strcmp (ip -> name, name))
+ break;
+ if (!ip) {
+ status = interface_allocate (&ip, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("new_shared_network_interface %s: %s",
+ name, isc_result_totext (status));
+ if (strlen (name) > sizeof ip -> name) {
+ memcpy (ip -> name, name, (sizeof ip -> name) - 1);
+ ip -> name [(sizeof ip -> name) - 1] = 0;
+ } else
+ strcpy (ip -> name, name);
+ if (interfaces) {
+ interface_reference (&ip -> next, interfaces, MDL);
+ interface_dereference (&interfaces, MDL);
+ }
+ interface_reference (&interfaces, ip, MDL);
+ ip -> flags = INTERFACE_REQUESTED;
+ /* XXX this is a reference loop. */
+ shared_network_reference (&ip -> shared_network, share, MDL);
+ interface_reference (&share -> interface, ip, MDL);
+ }
+}
+
+/* Enter a lease into the system. This is called by the parser each
+ time it reads in a new lease. If the subnet for that lease has
+ already been read in (usually the case), just update that lease;
+ otherwise, allocate temporary storage for the lease and keep it around
+ until we're done reading in the config file. */
+
+void enter_lease (lease)
+ struct lease *lease;
+{
+ struct lease *comp = (struct lease *)0;
+
+ if (find_lease_by_ip_addr (&comp, lease -> ip_addr, MDL)) {
+ if (!comp -> pool) {
+ log_error ("undeclared lease found in database: %s",
+ piaddr (lease -> ip_addr));
+ } else
+ pool_reference (&lease -> pool, comp -> pool, MDL);
+
+ if (comp -> subnet)
+ subnet_reference (&lease -> subnet,
+ comp -> subnet, MDL);
+ lease_ip_hash_delete(lease_ip_addr_hash,
+ lease->ip_addr.iabuf, lease->ip_addr.len,
+ MDL);
+ lease_dereference (&comp, MDL);
+ }
+
+ /* The only way a lease can get here without a subnet is if it's in
+ the lease file, but not in the dhcpd.conf file. In this case, we
+ *should* keep it around until it's expired, but never reallocate it
+ or renew it. Currently, to maintain consistency, we are not doing
+ this.
+ XXX fix this so that the lease is kept around until it expires.
+ XXX this will be important in IPv6 with addresses that become
+ XXX non-renewable as a result of a renumbering event. */
+
+ if (!lease -> subnet) {
+ log_error ("lease %s: no subnet.", piaddr (lease -> ip_addr));
+ return;
+ }
+ lease_ip_hash_add(lease_ip_addr_hash, lease->ip_addr.iabuf,
+ lease->ip_addr.len, lease, MDL);
+}
+
+/* Replace the data in an existing lease with the data in a new lease;
+ adjust hash tables to suit, and insertion sort the lease into the
+ list of leases by expiry time so that we can always find the oldest
+ lease. */
+
+int supersede_lease (comp, lease, commit, propogate, pimmediate)
+ struct lease *comp, *lease;
+ int commit;
+ int propogate;
+ int pimmediate;
+{
+ struct lease *lp, **lq, *prev;
+ struct timeval tv;
+#if defined (FAILOVER_PROTOCOL)
+ int do_pool_check = 0;
+
+ /* We must commit leases before sending updates regarding them
+ to failover peers. It is, therefore, an error to set pimmediate
+ and not commit. */
+ if (pimmediate && !commit)
+ return 0;
+#endif
+
+ /* If there is no sample lease, just do the move. */
+ if (!lease)
+ goto just_move_it;
+
+ /* Static leases are not currently kept in the database... */
+ if (lease -> flags & STATIC_LEASE)
+ return 1;
+
+ /* If the existing lease hasn't expired and has a different
+ unique identifier or, if it doesn't have a unique
+ identifier, a different hardware address, then the two
+ leases are in conflict. If the existing lease has a uid
+ and the new one doesn't, but they both have the same
+ hardware address, and dynamic bootp is allowed on this
+ lease, then we allow that, in case a dynamic BOOTP lease is
+ requested *after* a DHCP lease has been assigned. */
+
+ if (lease -> binding_state != FTS_ABANDONED &&
+ lease -> next_binding_state != FTS_ABANDONED &&
+ comp -> binding_state == FTS_ACTIVE &&
+ (((comp -> uid && lease -> uid) &&
+ (comp -> uid_len != lease -> uid_len ||
+ memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
+ (!comp -> uid &&
+ ((comp -> hardware_addr.hlen !=
+ lease -> hardware_addr.hlen) ||
+ memcmp (comp -> hardware_addr.hbuf,
+ lease -> hardware_addr.hbuf,
+ comp -> hardware_addr.hlen))))) {
+ log_error ("Lease conflict at %s",
+ piaddr (comp -> ip_addr));
+ }
+
+ /* If there's a Unique ID, dissociate it from the hash
+ table and free it if necessary. */
+ if (comp->uid) {
+ uid_hash_delete(comp);
+ if (comp->uid != comp->uid_buf) {
+ dfree(comp->uid, MDL);
+ comp->uid_max = 0;
+ comp->uid_len = 0;
+ }
+ comp -> uid = (unsigned char *)0;
+ }
+
+ /* If there's a hardware address, remove the lease from its
+ * old position in the hash bucket's ordered list.
+ */
+ if (comp->hardware_addr.hlen)
+ hw_hash_delete(comp);
+
+ /* If the lease has been billed to a class, remove the billing. */
+ if (comp -> billing_class != lease -> billing_class) {
+ if (comp -> billing_class)
+ unbill_class (comp, comp -> billing_class);
+ if (lease -> billing_class)
+ bill_class (comp, lease -> billing_class);
+ }
+
+ /* Copy the data files, but not the linkages. */
+ comp -> starts = lease -> starts;
+ if (lease -> uid) {
+ if (lease -> uid_len <= sizeof (lease -> uid_buf)) {
+ memcpy (comp -> uid_buf,
+ lease -> uid, lease -> uid_len);
+ comp -> uid = &comp -> uid_buf [0];
+ comp -> uid_max = sizeof comp -> uid_buf;
+ comp -> uid_len = lease -> uid_len;
+ } else if (lease -> uid != &lease -> uid_buf [0]) {
+ comp -> uid = lease -> uid;
+ comp -> uid_max = lease -> uid_max;
+ lease -> uid = (unsigned char *)0;
+ lease -> uid_max = 0;
+ comp -> uid_len = lease -> uid_len;
+ lease -> uid_len = 0;
+ } else {
+ log_fatal ("corrupt lease uid."); /* XXX */
+ }
+ } else {
+ comp -> uid = (unsigned char *)0;
+ comp -> uid_len = comp -> uid_max = 0;
+ }
+ if (comp -> host)
+ host_dereference (&comp -> host, MDL);
+ host_reference (&comp -> host, lease -> host, MDL);
+ comp -> hardware_addr = lease -> hardware_addr;
+ comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
+ (comp -> flags & ~EPHEMERAL_FLAGS));
+ if (comp -> scope)
+ binding_scope_dereference (&comp -> scope, MDL);
+ if (lease -> scope) {
+ binding_scope_reference (&comp -> scope, lease -> scope, MDL);
+ binding_scope_dereference (&lease -> scope, MDL);
+ }
+
+ if (comp -> agent_options)
+ option_chain_head_dereference (&comp -> agent_options, MDL);
+ if (lease -> agent_options) {
+ /* Only retain the agent options if the lease is still
+ affirmatively associated with a client. */
+ if (lease -> next_binding_state == FTS_ACTIVE ||
+ lease -> next_binding_state == FTS_EXPIRED)
+ option_chain_head_reference (&comp -> agent_options,
+ lease -> agent_options,
+ MDL);
+ option_chain_head_dereference (&lease -> agent_options, MDL);
+ }
+
+ /* Record the hostname information in the lease. */
+ if (comp -> client_hostname)
+ dfree (comp -> client_hostname, MDL);
+ comp -> client_hostname = lease -> client_hostname;
+ lease -> client_hostname = (char *)0;
+
+ if (lease -> on_expiry) {
+ if (comp -> on_expiry)
+ executable_statement_dereference (&comp -> on_expiry,
+ MDL);
+ executable_statement_reference (&comp -> on_expiry,
+ lease -> on_expiry,
+ MDL);
+ }
+ if (lease -> on_commit) {
+ if (comp -> on_commit)
+ executable_statement_dereference (&comp -> on_commit,
+ MDL);
+ executable_statement_reference (&comp -> on_commit,
+ lease -> on_commit,
+ MDL);
+ }
+ if (lease -> on_release) {
+ if (comp -> on_release)
+ executable_statement_dereference (&comp -> on_release,
+ MDL);
+ executable_statement_reference (&comp -> on_release,
+ lease -> on_release, MDL);
+ }
+
+ /* Record the lease in the uid hash if necessary. */
+ if (comp->uid)
+ uid_hash_add(comp);
+
+ /* Record it in the hardware address hash if necessary. */
+ if (comp->hardware_addr.hlen)
+ hw_hash_add(comp);
+
+ comp->cltt = lease->cltt;
+#if defined (FAILOVER_PROTOCOL)
+ comp->tstp = lease->tstp;
+ comp->tsfp = lease->tsfp;
+ comp->atsfp = lease->atsfp;
+#endif /* FAILOVER_PROTOCOL */
+ comp->ends = lease->ends;
+ comp->next_binding_state = lease->next_binding_state;
+
+ /*
+ * If we have a control block pointer copy it in.
+ * We don't zero out an older ponter as it is still
+ * in use. We shouldn't need to overwrite an
+ * old pointer with a new one as the old transaction
+ * should have been cancelled before getting here.
+ */
+ if (lease->ddns_cb != NULL)
+ comp->ddns_cb = lease->ddns_cb;
+
+ just_move_it:
+#if defined (FAILOVER_PROTOCOL)
+ /*
+ * Atsfp should be cleared upon any state change that implies
+ * propagation whether supersede_lease was given a copy lease
+ * structure or not (often from the pool_timer()).
+ */
+ if (propogate)
+ comp->atsfp = 0;
+#endif /* FAILOVER_PROTOCOL */
+
+ if (!comp -> pool) {
+ log_error ("Supersede_lease: lease %s with no pool.",
+ piaddr (comp -> ip_addr));
+ return 0;
+ }
+
+ /* Figure out which queue it's on. */
+ switch (comp -> binding_state) {
+ case FTS_FREE:
+ if (comp->flags & RESERVED_LEASE)
+ lq = &comp->pool->reserved;
+ else {
+ lq = &comp->pool->free;
+ comp->pool->free_leases--;
+ }
+
+#if defined(FAILOVER_PROTOCOL)
+ do_pool_check = 1;
+#endif
+ break;
+
+ case FTS_ACTIVE:
+ lq = &comp -> pool -> active;
+ break;
+
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_RESET:
+ lq = &comp -> pool -> expired;
+ break;
+
+ case FTS_ABANDONED:
+ lq = &comp -> pool -> abandoned;
+ break;
+
+ case FTS_BACKUP:
+ if (comp->flags & RESERVED_LEASE)
+ lq = &comp->pool->reserved;
+ else {
+ lq = &comp->pool->backup;
+ comp->pool->backup_leases--;
+ }
+
+#if defined(FAILOVER_PROTOCOL)
+ do_pool_check = 1;
+#endif
+ break;
+
+ default:
+ log_error ("Lease with bogus binding state: %d",
+ comp -> binding_state);
+#if defined (BINDING_STATE_DEBUG)
+ abort ();
+#endif
+ return 0;
+ }
+
+ /* Remove the lease from its current place in its current
+ timer sequence. */
+ /* XXX this is horrid. */
+ prev = (struct lease *)0;
+ for (lp = *lq; lp; lp = lp -> next) {
+ if (lp == comp)
+ break;
+ prev = lp;
+ }
+
+ if (!lp) {
+ log_fatal("Lease with binding state %s not on its queue.",
+ (comp->binding_state < 1 ||
+ comp->binding_state > FTS_LAST)
+ ? "unknown"
+ : binding_state_names[comp->binding_state - 1]);
+ }
+
+ if (prev) {
+ lease_dereference (&prev -> next, MDL);
+ if (comp -> next) {
+ lease_reference (&prev -> next, comp -> next, MDL);
+ lease_dereference (&comp -> next, MDL);
+ }
+ } else {
+ lease_dereference (lq, MDL);
+ if (comp -> next) {
+ lease_reference (lq, comp -> next, MDL);
+ lease_dereference (&comp -> next, MDL);
+ }
+ }
+
+ /* Make the state transition. */
+ if (commit || !pimmediate)
+ make_binding_state_transition (comp);
+
+ /* Put the lease back on the appropriate queue. If the lease
+ is corrupt (as detected by lease_enqueue), don't go any farther. */
+ if (!lease_enqueue (comp))
+ return 0;
+
+ /* If this is the next lease that will timeout on the pool,
+ zap the old timeout and set the timeout on this pool to the
+ time that the lease's next event will happen.
+
+ We do not actually set the timeout unless commit is true -
+ we don't want to thrash the timer queue when reading the
+ lease database. Instead, the database code calls the
+ expiry event on each pool after reading in the lease file,
+ and the expiry code sets the timer if there's anything left
+ to expire after it's run any outstanding expiry events on
+ the pool. */
+ if ((commit || !pimmediate) &&
+ comp -> sort_time != MIN_TIME &&
+ comp -> sort_time > cur_time &&
+ (comp -> sort_time < comp -> pool -> next_event_time ||
+ comp -> pool -> next_event_time == MIN_TIME)) {
+ comp -> pool -> next_event_time = comp -> sort_time;
+ tv . tv_sec = comp -> pool -> next_event_time;
+ tv . tv_usec = 0;
+ add_timeout (&tv,
+ pool_timer, comp -> pool,
+ (tvref_t)pool_reference,
+ (tvunref_t)pool_dereference);
+ }
+
+ if (commit) {
+#if defined(FAILOVER_PROTOCOL)
+ /*
+ * If commit and propogate are set, then we can save a
+ * possible fsync later in BNDUPD socket transmission by
+ * stepping the rewind state forward to the new state, in
+ * case it has changed. This is only worth doing if the
+ * failover connection is currently connected, as in this
+ * case it is likely we will be transmitting to the peer very
+ * shortly.
+ */
+ if (propogate && (comp->pool->failover_peer != NULL) &&
+ ((comp->pool->failover_peer->service_state ==
+ cooperating) ||
+ (comp->pool->failover_peer->service_state ==
+ not_responding)))
+ comp->rewind_binding_state = comp->binding_state;
+#endif
+
+ if (!write_lease (comp))
+ return 0;
+ if ((server_starting & SS_NOSYNC) == 0) {
+ if (!commit_leases ())
+ return 0;
+ }
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ if (propogate) {
+ comp -> desired_binding_state = comp -> binding_state;
+ if (!dhcp_failover_queue_update (comp, pimmediate))
+ return 0;
+ }
+ if (do_pool_check && comp->pool->failover_peer)
+ dhcp_failover_pool_check(comp->pool);
+#endif
+
+ /* If the current binding state has already expired, do an
+ expiry event right now. */
+ /* XXX At some point we should optimize this so that we don't
+ XXX write the lease twice, but this is a safe way to fix the
+ XXX problem for 3.0 (I hope!). */
+ if ((commit || !pimmediate) &&
+ comp -> sort_time < cur_time &&
+ comp -> next_binding_state != comp -> binding_state)
+ pool_timer (comp -> pool);
+
+ return 1;
+}
+
+void make_binding_state_transition (struct lease *lease)
+{
+#if defined (FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *peer;
+
+ if (lease && lease -> pool && lease -> pool -> failover_peer)
+ peer = lease -> pool -> failover_peer;
+ else
+ peer = (dhcp_failover_state_t *)0;
+#endif
+
+ /* If the lease was active and is now no longer active, but isn't
+ released, then it just expired, so do the expiry event. */
+ if (lease -> next_binding_state != lease -> binding_state &&
+ ((
+#if defined (FAILOVER_PROTOCOL)
+ peer &&
+ (lease->binding_state == FTS_EXPIRED ||
+ lease->binding_state == FTS_ACTIVE) &&
+ (lease->next_binding_state == FTS_FREE ||
+ lease->next_binding_state == FTS_BACKUP)) ||
+ (!peer &&
+#endif
+ lease -> binding_state == FTS_ACTIVE &&
+ lease -> next_binding_state != FTS_RELEASED))) {
+#if defined (NSUPDATE)
+ ddns_removals(lease, NULL, NULL);
+#endif
+ if (lease -> on_expiry) {
+ execute_statements ((struct binding_value **)0,
+ (struct packet *)0, lease,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0, /* XXX */
+ &lease -> scope,
+ lease -> on_expiry);
+ if (lease -> on_expiry)
+ executable_statement_dereference
+ (&lease -> on_expiry, MDL);
+ }
+
+ /* No sense releasing a lease after it's expired. */
+ if (lease -> on_release)
+ executable_statement_dereference (&lease -> on_release,
+ MDL);
+ /* Get rid of client-specific bindings that are only
+ correct when the lease is active. */
+ if (lease -> billing_class)
+ unbill_class (lease, lease -> billing_class);
+ if (lease -> agent_options)
+ option_chain_head_dereference (&lease -> agent_options,
+ MDL);
+ if (lease -> client_hostname) {
+ dfree (lease -> client_hostname, MDL);
+ lease -> client_hostname = (char *)0;
+ }
+ if (lease -> host)
+ host_dereference (&lease -> host, MDL);
+
+ /* Send the expiry time to the peer. */
+ lease -> tstp = lease -> ends;
+ }
+
+ /* If the lease was active and is now released, do the release
+ event. */
+ if (lease -> next_binding_state != lease -> binding_state &&
+ ((
+#if defined (FAILOVER_PROTOCOL)
+ peer &&
+ lease -> binding_state == FTS_RELEASED &&
+ (lease -> next_binding_state == FTS_FREE ||
+ lease -> next_binding_state == FTS_BACKUP)) ||
+ (!peer &&
+#endif
+ lease -> binding_state == FTS_ACTIVE &&
+ lease -> next_binding_state == FTS_RELEASED))) {
+#if defined (NSUPDATE)
+ /*
+ * Note: ddns_removals() is also iterated when the lease
+ * enters state 'released' in 'release_lease()'. The below
+ * is caught when a peer receives a BNDUPD from a failover
+ * peer; it may not have received the client's release (it
+ * may have been offline).
+ *
+ * We could remove the call from release_lease() because
+ * it will also catch here on the originating server after the
+ * peer acknowledges the state change. However, there could
+ * be many hours inbetween, and in this case we /know/ the
+ * client is no longer using the lease when we receive the
+ * release message. This is not true of expiry, where the
+ * peer may have extended the lease.
+ */
+ ddns_removals(lease, NULL, NULL);
+#endif
+ if (lease -> on_release) {
+ execute_statements ((struct binding_value **)0,
+ (struct packet *)0, lease,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0, /* XXX */
+ &lease -> scope,
+ lease -> on_release);
+ executable_statement_dereference (&lease -> on_release,
+ MDL);
+ }
+
+ /* A released lease can't expire. */
+ if (lease -> on_expiry)
+ executable_statement_dereference (&lease -> on_expiry,
+ MDL);
+
+ /* Get rid of client-specific bindings that are only
+ correct when the lease is active. */
+ if (lease -> billing_class)
+ unbill_class (lease, lease -> billing_class);
+ if (lease -> agent_options)
+ option_chain_head_dereference (&lease -> agent_options,
+ MDL);
+ if (lease -> client_hostname) {
+ dfree (lease -> client_hostname, MDL);
+ lease -> client_hostname = (char *)0;
+ }
+ if (lease -> host)
+ host_dereference (&lease -> host, MDL);
+
+ /* Send the release time (should be == cur_time) to the
+ peer. */
+ lease -> tstp = lease -> ends;
+ }
+
+#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
+ log_debug ("lease %s moves from %s to %s",
+ piaddr (lease -> ip_addr),
+ binding_state_print (lease -> binding_state),
+ binding_state_print (lease -> next_binding_state));
+#endif
+
+ lease -> binding_state = lease -> next_binding_state;
+ switch (lease -> binding_state) {
+ case FTS_ACTIVE:
+#if defined (FAILOVER_PROTOCOL)
+ if (lease -> pool && lease -> pool -> failover_peer)
+ lease -> next_binding_state = FTS_EXPIRED;
+ else
+#endif
+ lease -> next_binding_state = FTS_FREE;
+ break;
+
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_ABANDONED:
+ case FTS_RESET:
+ lease -> next_binding_state = FTS_FREE;
+#if defined(FAILOVER_PROTOCOL)
+ /* If we are not in partner_down, leases don't go from
+ EXPIRED to FREE on a timeout - only on an update.
+ If we're in partner_down, they expire at mclt past
+ the time we entered partner_down. */
+ if (lease -> pool -> failover_peer &&
+ lease -> pool -> failover_peer -> me.state == partner_down)
+ lease -> tsfp =
+ (lease -> pool -> failover_peer -> me.stos +
+ lease -> pool -> failover_peer -> mclt);
+#endif /* FAILOVER_PROTOCOL */
+ break;
+
+ case FTS_FREE:
+ case FTS_BACKUP:
+ lease -> next_binding_state = lease -> binding_state;
+ break;
+ }
+#if defined (DEBUG_LEASE_STATE_TRANSITIONS)
+ log_debug ("lease %s: next binding state %s",
+ piaddr (lease -> ip_addr),
+ binding_state_print (lease -> next_binding_state));
+#endif
+}
+
+/* Copy the contents of one lease into another, correctly maintaining
+ reference counts. */
+int lease_copy (struct lease **lp,
+ struct lease *lease, const char *file, int line)
+{
+ struct lease *lt = (struct lease *)0;
+ isc_result_t status;
+
+ status = lease_allocate (&lt, MDL);
+ if (status != ISC_R_SUCCESS)
+ return 0;
+
+ lt -> ip_addr = lease -> ip_addr;
+ lt -> starts = lease -> starts;
+ lt -> ends = lease -> ends;
+ lt -> uid_len = lease -> uid_len;
+ lt -> uid_max = lease -> uid_max;
+ if (lease -> uid == lease -> uid_buf) {
+ lt -> uid = lt -> uid_buf;
+ memcpy (lt -> uid_buf, lease -> uid_buf, sizeof lt -> uid_buf);
+ } else if (!lease -> uid_max) {
+ lt -> uid = (unsigned char *)0;
+ } else {
+ lt -> uid = dmalloc (lt -> uid_max, MDL);
+ if (!lt -> uid) {
+ lease_dereference (&lt, MDL);
+ return 0;
+ }
+ memcpy (lt -> uid, lease -> uid, lease -> uid_max);
+ }
+ if (lease -> client_hostname) {
+ lt -> client_hostname =
+ dmalloc (strlen (lease -> client_hostname) + 1, MDL);
+ if (!lt -> client_hostname) {
+ lease_dereference (&lt, MDL);
+ return 0;
+ }
+ strcpy (lt -> client_hostname, lease -> client_hostname);
+ }
+ if (lease -> scope)
+ binding_scope_reference (&lt -> scope, lease -> scope, MDL);
+ if (lease -> agent_options)
+ option_chain_head_reference (&lt -> agent_options,
+ lease -> agent_options, MDL);
+ host_reference (&lt -> host, lease -> host, file, line);
+ subnet_reference (&lt -> subnet, lease -> subnet, file, line);
+ pool_reference (&lt -> pool, lease -> pool, file, line);
+ class_reference (&lt -> billing_class,
+ lease -> billing_class, file, line);
+ lt -> hardware_addr = lease -> hardware_addr;
+ if (lease -> on_expiry)
+ executable_statement_reference (&lt -> on_expiry,
+ lease -> on_expiry,
+ file, line);
+ if (lease -> on_commit)
+ executable_statement_reference (&lt -> on_commit,
+ lease -> on_commit,
+ file, line);
+ if (lease -> on_release)
+ executable_statement_reference (&lt -> on_release,
+ lease -> on_release,
+ file, line);
+ lt->flags = lease->flags;
+ lt->tstp = lease->tstp;
+ lt->tsfp = lease->tsfp;
+ lt->atsfp = lease->atsfp;
+ lt->cltt = lease -> cltt;
+ lt->binding_state = lease->binding_state;
+ lt->next_binding_state = lease->next_binding_state;
+ lt->rewind_binding_state = lease->rewind_binding_state;
+ status = lease_reference(lp, lt, file, line);
+ lease_dereference(&lt, MDL);
+ return status == ISC_R_SUCCESS;
+}
+
+/* Release the specified lease and re-hash it as appropriate. */
+void release_lease (lease, packet)
+ struct lease *lease;
+ struct packet *packet;
+{
+ /* If there are statements to execute when the lease is
+ released, execute them. */
+#if defined (NSUPDATE)
+ ddns_removals(lease, NULL, NULL);
+#endif
+ if (lease -> on_release) {
+ execute_statements ((struct binding_value **)0,
+ packet, lease, (struct client_state *)0,
+ packet -> options,
+ (struct option_state *)0, /* XXX */
+ &lease -> scope, lease -> on_release);
+ if (lease -> on_release)
+ executable_statement_dereference (&lease -> on_release,
+ MDL);
+ }
+
+ /* We do either the on_release or the on_expiry events, but
+ not both (it's possible that they could be the same,
+ in any case). */
+ if (lease -> on_expiry)
+ executable_statement_dereference (&lease -> on_expiry, MDL);
+
+ if (lease -> binding_state != FTS_FREE &&
+ lease -> binding_state != FTS_BACKUP &&
+ lease -> binding_state != FTS_RELEASED &&
+ lease -> binding_state != FTS_EXPIRED &&
+ lease -> binding_state != FTS_RESET) {
+ if (lease -> on_commit)
+ executable_statement_dereference (&lease -> on_commit,
+ MDL);
+
+ /* Blow away any bindings. */
+ if (lease -> scope)
+ binding_scope_dereference (&lease -> scope, MDL);
+
+ /* Set sort times to the present. */
+ lease -> ends = cur_time;
+ /* Lower layers of muckery set tstp to ->ends. But we send
+ * protocol messages before this. So it is best to set
+ * tstp now anyway.
+ */
+ lease->tstp = cur_time;
+#if defined (FAILOVER_PROTOCOL)
+ if (lease -> pool && lease -> pool -> failover_peer) {
+ dhcp_failover_state_t *peer = NULL;
+
+ if (lease->pool != NULL)
+ peer = lease->pool->failover_peer;
+
+ if ((peer->service_state == not_cooperating) &&
+ (((peer->i_am == primary) &&
+ (lease->rewind_binding_state == FTS_FREE)) ||
+ ((peer->i_am == secondary) &&
+ (lease->rewind_binding_state == FTS_BACKUP)))) {
+ lease->next_binding_state =
+ lease->rewind_binding_state;
+ } else
+ lease -> next_binding_state = FTS_RELEASED;
+ } else {
+ lease -> next_binding_state = FTS_FREE;
+ }
+#else
+ lease -> next_binding_state = FTS_FREE;
+#endif
+ supersede_lease (lease, (struct lease *)0, 1, 1, 1);
+ }
+}
+
+/* Abandon the specified lease (set its timeout to infinity and its
+ particulars to zero, and re-hash it as appropriate. */
+
+void abandon_lease (lease, message)
+ struct lease *lease;
+ const char *message;
+{
+ struct lease *lt = (struct lease *)0;
+#if defined (NSUPDATE)
+ ddns_removals(lease, NULL, NULL);
+#endif
+
+ if (!lease_copy (&lt, lease, MDL))
+ return;
+
+ if (lt->scope)
+ binding_scope_dereference(&lt->scope, MDL);
+
+ lt -> ends = cur_time; /* XXX */
+ lt -> next_binding_state = FTS_ABANDONED;
+
+ log_error ("Abandoning IP address %s: %s",
+ piaddr (lease -> ip_addr), message);
+ lt -> hardware_addr.hlen = 0;
+ if (lt -> uid && lt -> uid != lt -> uid_buf)
+ dfree (lt -> uid, MDL);
+ lt -> uid = (unsigned char *)0;
+ lt -> uid_len = 0;
+ lt -> uid_max = 0;
+ supersede_lease (lease, lt, 1, 1, 1);
+ lease_dereference (&lt, MDL);
+}
+
+/* Abandon the specified lease (set its timeout to infinity and its
+ particulars to zero, and re-hash it as appropriate. */
+
+void dissociate_lease (lease)
+ struct lease *lease;
+{
+ struct lease *lt = (struct lease *)0;
+#if defined (NSUPDATE)
+ ddns_removals(lease, NULL, NULL);
+#endif
+
+ if (!lease_copy (&lt, lease, MDL))
+ return;
+
+#if defined (FAILOVER_PROTOCOL)
+ if (lease -> pool && lease -> pool -> failover_peer) {
+ lt -> next_binding_state = FTS_RESET;
+ } else {
+ lt -> next_binding_state = FTS_FREE;
+ }
+#else
+ lt -> next_binding_state = FTS_FREE;
+#endif
+ lt -> ends = cur_time; /* XXX */
+ lt -> hardware_addr.hlen = 0;
+ if (lt -> uid && lt -> uid != lt -> uid_buf)
+ dfree (lt -> uid, MDL);
+ lt -> uid = (unsigned char *)0;
+ lt -> uid_len = 0;
+ lt -> uid_max = 0;
+ supersede_lease (lease, lt, 1, 1, 1);
+ lease_dereference (&lt, MDL);
+}
+
+/* Timer called when a lease in a particular pool expires. */
+void pool_timer (vpool)
+ void *vpool;
+{
+ struct pool *pool;
+ struct lease *next = (struct lease *)0;
+ struct lease *lease = (struct lease *)0;
+#define FREE_LEASES 0
+#define ACTIVE_LEASES 1
+#define EXPIRED_LEASES 2
+#define ABANDONED_LEASES 3
+#define BACKUP_LEASES 4
+#define RESERVED_LEASES 5
+ struct lease **lptr[RESERVED_LEASES+1];
+ TIME next_expiry = MAX_TIME;
+ int i;
+ struct timeval tv;
+
+ pool = (struct pool *)vpool;
+
+ lptr [FREE_LEASES] = &pool -> free;
+ lptr [ACTIVE_LEASES] = &pool -> active;
+ lptr [EXPIRED_LEASES] = &pool -> expired;
+ lptr [ABANDONED_LEASES] = &pool -> abandoned;
+ lptr [BACKUP_LEASES] = &pool -> backup;
+ lptr[RESERVED_LEASES] = &pool->reserved;
+
+ for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+ /* If there's nothing on the queue, skip it. */
+ if (!*(lptr [i]))
+ continue;
+
+#if defined (FAILOVER_PROTOCOL)
+ if (pool->failover_peer &&
+ pool->failover_peer->me.state != partner_down) {
+ /*
+ * Normally the secondary doesn't initiate expiration
+ * events (unless in partner-down), but rather relies
+ * on the primary to expire the lease. However, when
+ * disconnected from its peer, the server is allowed to
+ * rewind a lease to the previous state that the peer
+ * would have recorded it. This means there may be
+ * opportunities for active->free or active->backup
+ * expirations while out of contact.
+ *
+ * Q: Should we limit this expiration to
+ * comms-interrupt rather than not-normal?
+ */
+ if ((i == ACTIVE_LEASES) &&
+ (pool->failover_peer->i_am == secondary) &&
+ (pool->failover_peer->me.state == normal))
+ continue;
+
+ /* Leases in an expired state don't move to
+ free because of a timeout unless we're in
+ partner_down. */
+ if (i == EXPIRED_LEASES)
+ continue;
+ }
+#endif
+ lease_reference (&lease, *(lptr [i]), MDL);
+
+ while (lease) {
+ /* Remember the next lease in the list. */
+ if (next)
+ lease_dereference (&next, MDL);
+ if (lease -> next)
+ lease_reference (&next, lease -> next, MDL);
+
+ /* If we've run out of things to expire on this list,
+ stop. */
+ if (lease -> sort_time > cur_time) {
+ if (lease -> sort_time < next_expiry)
+ next_expiry = lease -> sort_time;
+ break;
+ }
+
+ /* If there is a pending state change, and
+ this lease has gotten to the time when the
+ state change should happen, just call
+ supersede_lease on it to make the change
+ happen. */
+ if (lease->next_binding_state != lease->binding_state)
+ {
+#if defined(FAILOVER_PROTOCOL)
+ dhcp_failover_state_t *peer = NULL;
+
+ if (lease->pool != NULL)
+ peer = lease->pool->failover_peer;
+
+ /* Can we rewind the lease to a free state? */
+ if (peer != NULL &&
+ peer->service_state == not_cooperating &&
+ lease->next_binding_state == FTS_EXPIRED &&
+ ((peer->i_am == primary &&
+ lease->rewind_binding_state == FTS_FREE)
+ ||
+ (peer->i_am == secondary &&
+ lease->rewind_binding_state ==
+ FTS_BACKUP)))
+ lease->next_binding_state =
+ lease->rewind_binding_state;
+#endif
+ supersede_lease(lease, NULL, 1, 1, 1);
+ }
+
+ lease_dereference (&lease, MDL);
+ if (next)
+ lease_reference (&lease, next, MDL);
+ }
+ if (next)
+ lease_dereference (&next, MDL);
+ if (lease)
+ lease_dereference (&lease, MDL);
+ }
+ if (next_expiry != MAX_TIME) {
+ pool -> next_event_time = next_expiry;
+ tv . tv_sec = pool -> next_event_time;
+ tv . tv_usec = 0;
+ add_timeout (&tv, pool_timer, pool,
+ (tvref_t)pool_reference,
+ (tvunref_t)pool_dereference);
+ } else
+ pool -> next_event_time = MIN_TIME;
+
+}
+
+/* Locate the lease associated with a given IP address... */
+
+int find_lease_by_ip_addr (struct lease **lp, struct iaddr addr,
+ const char *file, int line)
+{
+ return lease_ip_hash_lookup(lp, lease_ip_addr_hash, addr.iabuf,
+ addr.len, file, line);
+}
+
+int find_lease_by_uid (struct lease **lp, const unsigned char *uid,
+ unsigned len, const char *file, int line)
+{
+ if (len == 0)
+ return 0;
+ return lease_id_hash_lookup (lp, lease_uid_hash, uid, len, file, line);
+}
+
+int find_lease_by_hw_addr (struct lease **lp,
+ const unsigned char *hwaddr, unsigned hwlen,
+ const char *file, int line)
+{
+ if (hwlen == 0)
+ return 0;
+ return lease_id_hash_lookup(lp, lease_hw_addr_hash, hwaddr, hwlen,
+ file, line);
+}
+
+/* If the lease is preferred over the candidate, return truth. The
+ * 'cand' and 'lease' names are retained to read more clearly against
+ * the 'uid_hash_add' and 'hw_hash_add' functions (this is common logic
+ * to those two functions).
+ *
+ * 1) ACTIVE leases are preferred. The active lease with
+ * the longest lifetime is preferred over shortest.
+ * 2) "transitional states" are next, this time with the
+ * most recent CLTT.
+ * 3) free/backup/etc states are next, again with CLTT. In truth we
+ * should never see reset leases for this.
+ * 4) Abandoned leases are always dead last.
+ */
+static isc_boolean_t
+client_lease_preferred(struct lease *cand, struct lease *lease)
+{
+ if (cand->binding_state == FTS_ACTIVE) {
+ if (lease->binding_state == FTS_ACTIVE &&
+ lease->ends >= cand->ends)
+ return ISC_TRUE;
+ } else if (cand->binding_state == FTS_EXPIRED ||
+ cand->binding_state == FTS_RELEASED) {
+ if (lease->binding_state == FTS_ACTIVE)
+ return ISC_TRUE;
+
+ if ((lease->binding_state == FTS_EXPIRED ||
+ lease->binding_state == FTS_RELEASED) &&
+ lease->cltt >= cand->cltt)
+ return ISC_TRUE;
+ } else if (cand->binding_state != FTS_ABANDONED) {
+ if (lease->binding_state == FTS_ACTIVE ||
+ lease->binding_state == FTS_EXPIRED ||
+ lease->binding_state == FTS_RELEASED)
+ return ISC_TRUE;
+
+ if (lease->binding_state != FTS_ABANDONED &&
+ lease->cltt >= cand->cltt)
+ return ISC_TRUE;
+ } else /* (cand->binding_state == FTS_ABANDONED) */ {
+ if (lease->binding_state != FTS_ABANDONED ||
+ lease->cltt >= cand->cltt)
+ return ISC_TRUE;
+ }
+
+ return ISC_FALSE;
+}
+
+/* Add the specified lease to the uid hash. */
+void
+uid_hash_add(struct lease *lease)
+{
+ struct lease *head = NULL;
+ struct lease *cand = NULL;
+ struct lease *prev = NULL;
+ struct lease *next = NULL;
+
+ /* If it's not in the hash, just add it. */
+ if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL))
+ lease_id_hash_add(lease_uid_hash, lease->uid, lease->uid_len,
+ lease, MDL);
+ else {
+ /* Otherwise, insert it into the list in order of its
+ * preference for "resuming allocation to the client."
+ *
+ * Because we don't have control of the hash bucket index
+ * directly, we have to remove and re-insert the client
+ * id into the hash if we're inserting onto the head.
+ */
+ lease_reference(&cand, head, MDL);
+ while (cand != NULL) {
+ if (client_lease_preferred(cand, lease))
+ break;
+
+ if (prev != NULL)
+ lease_dereference(&prev, MDL);
+ lease_reference(&prev, cand, MDL);
+
+ if (cand->n_uid != NULL)
+ lease_reference(&next, cand->n_uid, MDL);
+
+ lease_dereference(&cand, MDL);
+
+ if (next != NULL) {
+ lease_reference(&cand, next, MDL);
+ lease_dereference(&next, MDL);
+ }
+ }
+
+ /* If we want to insert 'before cand', and prev is NULL,
+ * then it was the head of the list. Assume that position.
+ */
+ if (prev == NULL) {
+ lease_reference(&lease->n_uid, head, MDL);
+ lease_id_hash_delete(lease_uid_hash, lease->uid,
+ lease->uid_len, MDL);
+ lease_id_hash_add(lease_uid_hash, lease->uid,
+ lease->uid_len, lease, MDL);
+ } else /* (prev != NULL) */ {
+ if(prev->n_uid != NULL) {
+ lease_reference(&lease->n_uid, prev->n_uid,
+ MDL);
+ lease_dereference(&prev->n_uid, MDL);
+ }
+ lease_reference(&prev->n_uid, lease, MDL);
+
+ lease_dereference(&prev, MDL);
+ }
+
+ if (cand != NULL)
+ lease_dereference(&cand, MDL);
+ lease_dereference(&head, MDL);
+ }
+}
+
+/* Delete the specified lease from the uid hash. */
+
+void uid_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head = (struct lease *)0;
+ struct lease *scan;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!find_lease_by_uid (&head, lease -> uid, lease -> uid_len, MDL)) {
+ if (lease -> n_uid)
+ lease_dereference (&lease -> n_uid, MDL);
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ lease_id_hash_delete(lease_uid_hash, lease->uid,
+ lease->uid_len, MDL);
+ if (lease -> n_uid) {
+ lease_id_hash_add(lease_uid_hash, lease->n_uid->uid,
+ lease->n_uid->uid_len, lease->n_uid,
+ MDL);
+ lease_dereference (&lease -> n_uid, MDL);
+ }
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ for (scan = head; scan -> n_uid; scan = scan -> n_uid) {
+ if (scan -> n_uid == lease) {
+ lease_dereference (&scan -> n_uid, MDL);
+ if (lease -> n_uid) {
+ lease_reference (&scan -> n_uid,
+ lease -> n_uid, MDL);
+ lease_dereference (&lease -> n_uid,
+ MDL);
+ }
+ break;
+ }
+ }
+ }
+ lease_dereference (&head, MDL);
+}
+
+/* Add the specified lease to the hardware address hash. */
+
+void
+hw_hash_add(struct lease *lease)
+{
+ struct lease *head = NULL;
+ struct lease *cand = NULL;
+ struct lease *prev = NULL;
+ struct lease *next = NULL;
+
+ /* If it's not in the hash, just add it. */
+ if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
+ lease -> hardware_addr.hlen, MDL))
+ lease_id_hash_add(lease_hw_addr_hash,
+ lease->hardware_addr.hbuf,
+ lease->hardware_addr.hlen, lease, MDL);
+ else {
+ /* Otherwise, insert it into the list in order of its
+ * preference for "resuming allocation to the client."
+ *
+ * Because we don't have control of the hash bucket index
+ * directly, we have to remove and re-insert the client
+ * id into the hash if we're inserting onto the head.
+ */
+ lease_reference(&cand, head, MDL);
+ while (cand != NULL) {
+ if (client_lease_preferred(cand, lease))
+ break;
+
+ if (prev != NULL)
+ lease_dereference(&prev, MDL);
+ lease_reference(&prev, cand, MDL);
+
+ if (cand->n_hw != NULL)
+ lease_reference(&next, cand->n_hw, MDL);
+
+ lease_dereference(&cand, MDL);
+
+ if (next != NULL) {
+ lease_reference(&cand, next, MDL);
+ lease_dereference(&next, MDL);
+ }
+ }
+
+ /* If we want to insert 'before cand', and prev is NULL,
+ * then it was the head of the list. Assume that position.
+ */
+ if (prev == NULL) {
+ lease_reference(&lease->n_hw, head, MDL);
+ lease_id_hash_delete(lease_hw_addr_hash,
+ lease->hardware_addr.hbuf,
+ lease->hardware_addr.hlen, MDL);
+ lease_id_hash_add(lease_hw_addr_hash,
+ lease->hardware_addr.hbuf,
+ lease->hardware_addr.hlen,
+ lease, MDL);
+ } else /* (prev != NULL) */ {
+ if(prev->n_hw != NULL) {
+ lease_reference(&lease->n_hw, prev->n_hw,
+ MDL);
+ lease_dereference(&prev->n_hw, MDL);
+ }
+ lease_reference(&prev->n_hw, lease, MDL);
+
+ lease_dereference(&prev, MDL);
+ }
+
+ if (cand != NULL)
+ lease_dereference(&cand, MDL);
+ lease_dereference(&head, MDL);
+ }
+}
+
+/* Delete the specified lease from the hardware address hash. */
+
+void hw_hash_delete (lease)
+ struct lease *lease;
+{
+ struct lease *head = (struct lease *)0;
+ struct lease *next = (struct lease *)0;
+
+ /* If it's not in the hash, we have no work to do. */
+ if (!find_lease_by_hw_addr (&head, lease -> hardware_addr.hbuf,
+ lease -> hardware_addr.hlen, MDL)) {
+ if (lease -> n_hw)
+ lease_dereference (&lease -> n_hw, MDL);
+ return;
+ }
+
+ /* If the lease we're freeing is at the head of the list,
+ remove the hash table entry and add a new one with the
+ next lease on the list (if there is one). */
+ if (head == lease) {
+ lease_id_hash_delete(lease_hw_addr_hash,
+ lease->hardware_addr.hbuf,
+ lease->hardware_addr.hlen, MDL);
+ if (lease->n_hw) {
+ lease_id_hash_add(lease_hw_addr_hash,
+ lease->n_hw->hardware_addr.hbuf,
+ lease->n_hw->hardware_addr.hlen,
+ lease->n_hw, MDL);
+ lease_dereference(&lease->n_hw, MDL);
+ }
+ } else {
+ /* Otherwise, look for the lease in the list of leases
+ attached to the hash table entry, and remove it if
+ we find it. */
+ while (head -> n_hw) {
+ if (head -> n_hw == lease) {
+ lease_dereference (&head -> n_hw, MDL);
+ if (lease -> n_hw) {
+ lease_reference (&head -> n_hw,
+ lease -> n_hw, MDL);
+ lease_dereference (&lease -> n_hw,
+ MDL);
+ }
+ break;
+ }
+ lease_reference (&next, head -> n_hw, MDL);
+ lease_dereference (&head, MDL);
+ lease_reference (&head, next, MDL);
+ lease_dereference (&next, MDL);
+ }
+ }
+ if (head)
+ lease_dereference (&head, MDL);
+}
+
+/* Write all interesting leases to permanent storage. */
+
+int write_leases ()
+{
+ struct lease *l;
+ struct shared_network *s;
+ struct pool *p;
+ struct host_decl *hp;
+ struct group_object *gp;
+ struct hash_bucket *hb;
+ struct class *cp;
+ struct collection *colp;
+ int i;
+ int num_written;
+ struct lease **lptr[RESERVED_LEASES+1];
+
+ /* write all the dynamically-created class declarations. */
+ if (collections->classes) {
+ numclasseswritten = 0;
+ for (colp = collections ; colp ; colp = colp->next) {
+ for (cp = colp->classes ; cp ; cp = cp->nic) {
+ write_named_billing_class(
+ (unsigned char *)cp->name,
+ 0, cp);
+ }
+ }
+
+ /* XXXJAB this number doesn't include subclasses... */
+ log_info ("Wrote %d class decls to leases file.",
+ numclasseswritten);
+ }
+
+
+ /* Write all the dynamically-created group declarations. */
+ if (group_name_hash) {
+ num_written = 0;
+ for (i = 0; i < group_name_hash -> hash_count; i++) {
+ for (hb = group_name_hash -> buckets [i];
+ hb; hb = hb -> next) {
+ gp = (struct group_object *)hb -> value;
+ if ((gp -> flags & GROUP_OBJECT_DYNAMIC) ||
+ ((gp -> flags & GROUP_OBJECT_STATIC) &&
+ (gp -> flags & GROUP_OBJECT_DELETED))) {
+ if (!write_group (gp))
+ return 0;
+ ++num_written;
+ }
+ }
+ }
+ log_info ("Wrote %d group decls to leases file.", num_written);
+ }
+
+ /* Write all the deleted host declarations. */
+ if (host_name_hash) {
+ num_written = 0;
+ for (i = 0; i < host_name_hash -> hash_count; i++) {
+ for (hb = host_name_hash -> buckets [i];
+ hb; hb = hb -> next) {
+ hp = (struct host_decl *)hb -> value;
+ if (((hp -> flags & HOST_DECL_STATIC) &&
+ (hp -> flags & HOST_DECL_DELETED))) {
+ if (!write_host (hp))
+ return 0;
+ ++num_written;
+ }
+ }
+ }
+ log_info ("Wrote %d deleted host decls to leases file.",
+ num_written);
+ }
+
+ /* Write all the new, dynamic host declarations. */
+ if (host_name_hash) {
+ num_written = 0;
+ for (i = 0; i < host_name_hash -> hash_count; i++) {
+ for (hb = host_name_hash -> buckets [i];
+ hb; hb = hb -> next) {
+ hp = (struct host_decl *)hb -> value;
+ if ((hp -> flags & HOST_DECL_DYNAMIC)) {
+ if (!write_host (hp))
+ ++num_written;
+ }
+ }
+ }
+ log_info ("Wrote %d new dynamic host decls to leases file.",
+ num_written);
+ }
+
+#if defined (FAILOVER_PROTOCOL)
+ /* Write all the failover states. */
+ if (!dhcp_failover_write_all_states ())
+ return 0;
+#endif
+
+ /* Write all the leases. */
+ num_written = 0;
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ lptr [FREE_LEASES] = &p -> free;
+ lptr [ACTIVE_LEASES] = &p -> active;
+ lptr [EXPIRED_LEASES] = &p -> expired;
+ lptr [ABANDONED_LEASES] = &p -> abandoned;
+ lptr [BACKUP_LEASES] = &p -> backup;
+ lptr [RESERVED_LEASES] = &p->reserved;
+
+ for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+ for (l = *(lptr [i]); l; l = l -> next) {
+#if !defined (DEBUG_DUMP_ALL_LEASES)
+ if (l->hardware_addr.hlen != 0 || l->uid_len != 0 ||
+ l->tsfp != 0 || l->binding_state != FTS_FREE)
+#endif
+ {
+ if (!write_lease (l))
+ return 0;
+ num_written++;
+ }
+ }
+ }
+ }
+ }
+ log_info ("Wrote %d leases to leases file.", num_written);
+#ifdef DHCPv6
+ if (!write_leases6()) {
+ return 0;
+ }
+#endif /* DHCPv6 */
+ if (!commit_leases ())
+ return 0;
+ return 1;
+}
+
+/* In addition to placing this lease upon a lease queue depending on its
+ * state, it also keeps track of the number of FREE and BACKUP leases in
+ * existence, and sets the sort_time on the lease.
+ *
+ * Sort_time is used in pool_timer() to determine when the lease will
+ * bubble to the top of the list and be supersede_lease()'d into its next
+ * state (possibly, if all goes well). Example, ACTIVE leases move to
+ * EXPIRED state when the 'ends' value is reached, so that is its sort
+ * time. Most queues are sorted by 'ends', since it is generally best
+ * practice to re-use the oldest lease, to reduce address collision
+ * chances.
+ */
+int lease_enqueue (struct lease *comp)
+{
+ struct lease **lq, *prev, *lp;
+ static struct lease **last_lq = NULL;
+ static struct lease *last_insert_point = NULL;
+
+ /* No queue to put it on? */
+ if (!comp -> pool)
+ return 0;
+
+ /* Figure out which queue it's going to. */
+ switch (comp -> binding_state) {
+ case FTS_FREE:
+ if (comp->flags & RESERVED_LEASE) {
+ lq = &comp->pool->reserved;
+ } else {
+ lq = &comp->pool->free;
+ comp->pool->free_leases++;
+ }
+ comp -> sort_time = comp -> ends;
+ break;
+
+ case FTS_ACTIVE:
+ lq = &comp -> pool -> active;
+ comp -> sort_time = comp -> ends;
+ break;
+
+ case FTS_EXPIRED:
+ case FTS_RELEASED:
+ case FTS_RESET:
+ lq = &comp -> pool -> expired;
+#if defined(FAILOVER_PROTOCOL)
+ /* In partner_down, tsfp is the time at which the lease
+ * may be reallocated (stos+mclt). We can do that with
+ * lease_mine_to_reallocate() anywhere between tsfp and
+ * ends. But we prefer to wait until ends before doing it
+ * automatically (choose the greater of the two). Note
+ * that 'ends' is usually a historic timestamp in the
+ * case of expired leases, is really only in the future
+ * on released leases, and if we know a lease to be released
+ * the peer might still know it to be active...in which case
+ * it's possible the peer has renewed this lease, so avoid
+ * doing that.
+ */
+ if (comp->pool->failover_peer &&
+ comp->pool->failover_peer->me.state == partner_down)
+ comp->sort_time = (comp->tsfp > comp->ends) ?
+ comp->tsfp : comp->ends;
+ else
+#endif
+ comp->sort_time = comp->ends;
+
+ break;
+
+ case FTS_ABANDONED:
+ lq = &comp -> pool -> abandoned;
+ comp -> sort_time = comp -> ends;
+ break;
+
+ case FTS_BACKUP:
+ if (comp->flags & RESERVED_LEASE) {
+ lq = &comp->pool->reserved;
+ } else {
+ lq = &comp->pool->backup;
+ comp->pool->backup_leases++;
+ }
+ comp -> sort_time = comp -> ends;
+ break;
+
+ default:
+ log_error ("Lease with bogus binding state: %d",
+ comp -> binding_state);
+#if defined (BINDING_STATE_DEBUG)
+ abort ();
+#endif
+ return 0;
+ }
+
+ /* This only works during server startup: during runtime, the last
+ * lease may be dequeued in between calls. If the queue is the same
+ * as was used previously, and the lease structure isn't (this is not
+ * a re-queue), use that as a starting point for the insertion-sort.
+ */
+ if ((server_starting & SS_QFOLLOW) && (lq == last_lq) &&
+ (comp != last_insert_point) &&
+ (last_insert_point->sort_time <= comp->sort_time)) {
+ prev = last_insert_point;
+ lp = prev->next;
+ } else {
+ prev = NULL;
+ lp = *lq;
+ }
+
+ /* Insertion sort the lease onto the appropriate queue. */
+ for (; lp ; lp = lp->next) {
+ if (lp -> sort_time >= comp -> sort_time)
+ break;
+ prev = lp;
+ }
+
+ if (prev) {
+ if (prev -> next) {
+ lease_reference (&comp -> next, prev -> next, MDL);
+ lease_dereference (&prev -> next, MDL);
+ }
+ lease_reference (&prev -> next, comp, MDL);
+ } else {
+ if (*lq) {
+ lease_reference (&comp -> next, *lq, MDL);
+ lease_dereference (lq, MDL);
+ }
+ lease_reference (lq, comp, MDL);
+ }
+ last_insert_point = comp;
+ last_lq = lq;
+ return 1;
+}
+
+/* For a given lease, sort it onto the right list in its pool and put it
+ in each appropriate hash, understanding that it's already by definition
+ in lease_ip_addr_hash. */
+
+isc_result_t
+lease_instantiate(const void *key, unsigned len, void *object)
+{
+ struct lease *lease = object;
+ struct class *class;
+ /* XXX If the lease doesn't have a pool at this point, it's an
+ XXX orphan, which we *should* keep around until it expires,
+ XXX but which right now we just forget. */
+ if (!lease -> pool) {
+ lease_ip_hash_delete(lease_ip_addr_hash, lease->ip_addr.iabuf,
+ lease->ip_addr.len, MDL);
+ return ISC_R_SUCCESS;
+ }
+
+ /* Put the lease on the right queue. Failure to queue is probably
+ * due to a bogus binding state. In such a case, we claim success,
+ * so that later leases in a hash_foreach are processed, but we
+ * return early as we really don't want hw address hash entries or
+ * other cruft to surround such a bogus entry.
+ */
+ if (!lease_enqueue(lease))
+ return ISC_R_SUCCESS;
+
+ /* Record the lease in the uid hash if possible. */
+ if (lease -> uid) {
+ uid_hash_add (lease);
+ }
+
+ /* Record it in the hardware address hash if possible. */
+ if (lease -> hardware_addr.hlen) {
+ hw_hash_add (lease);
+ }
+
+ /* If the lease has a billing class, set up the billing. */
+ if (lease -> billing_class) {
+ class = (struct class *)0;
+ class_reference (&class, lease -> billing_class, MDL);
+ class_dereference (&lease -> billing_class, MDL);
+ /* If the lease is available for allocation, the billing
+ is invalid, so we don't keep it. */
+ if (lease -> binding_state == FTS_ACTIVE ||
+ lease -> binding_state == FTS_EXPIRED ||
+ lease -> binding_state == FTS_RELEASED ||
+ lease -> binding_state == FTS_RESET)
+ bill_class (lease, class);
+ class_dereference (&class, MDL);
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* Run expiry events on every pool. This is called on startup so that
+ any expiry events that occurred after the server stopped and before it
+ was restarted can be run. At the same time, if failover support is
+ compiled in, we compute the balance of leases for the pool. */
+
+void expire_all_pools ()
+{
+ struct shared_network *s;
+ struct pool *p;
+ int i;
+ struct lease *l;
+ struct lease **lptr[RESERVED_LEASES+1];
+
+ /* Indicate that we are in the startup phase */
+ server_starting = SS_NOSYNC | SS_QFOLLOW;
+
+ /* First, go over the hash list and actually put all the leases
+ on the appropriate lists. */
+ lease_ip_hash_foreach(lease_ip_addr_hash, lease_instantiate);
+
+ /* Loop through each pool in each shared network and call the
+ * expiry routine on the pool. It is no longer safe to follow
+ * the queue insertion point, as expiration of a lease can move
+ * it between queues (and this may be the lease that function
+ * points at).
+ */
+ server_starting &= ~SS_QFOLLOW;
+ for (s = shared_networks; s; s = s -> next) {
+ for (p = s -> pools; p; p = p -> next) {
+ pool_timer (p);
+
+ p -> lease_count = 0;
+ p -> free_leases = 0;
+ p -> backup_leases = 0;
+
+ lptr [FREE_LEASES] = &p -> free;
+ lptr [ACTIVE_LEASES] = &p -> active;
+ lptr [EXPIRED_LEASES] = &p -> expired;
+ lptr [ABANDONED_LEASES] = &p -> abandoned;
+ lptr [BACKUP_LEASES] = &p -> backup;
+ lptr [RESERVED_LEASES] = &p->reserved;
+
+ for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+ for (l = *(lptr [i]); l; l = l -> next) {
+ p -> lease_count++;
+ if (l -> ends <= cur_time) {
+ if (l->binding_state == FTS_FREE) {
+ if (i == FREE_LEASES)
+ p->free_leases++;
+ else if (i != RESERVED_LEASES)
+ log_fatal("Impossible case "
+ "at %s:%d.", MDL);
+ } else if (l->binding_state == FTS_BACKUP) {
+ if (i == BACKUP_LEASES)
+ p->backup_leases++;
+ else if (i != RESERVED_LEASES)
+ log_fatal("Impossible case "
+ "at %s:%d.", MDL);
+ }
+ }
+#if defined (FAILOVER_PROTOCOL)
+ if (p -> failover_peer &&
+ l -> tstp > l -> atsfp &&
+ !(l -> flags & ON_UPDATE_QUEUE)) {
+ l -> desired_binding_state = l -> binding_state;
+ dhcp_failover_queue_update (l, 1);
+ }
+#endif
+ }
+ }
+ }
+ }
+
+ /* turn off startup phase */
+ server_starting = 0;
+}
+
+void dump_subnets ()
+{
+ struct lease *l;
+ struct shared_network *s;
+ struct subnet *n;
+ struct pool *p;
+ struct lease **lptr[RESERVED_LEASES+1];
+ int i;
+
+ log_info ("Subnets:");
+ for (n = subnets; n; n = n -> next_subnet) {
+ log_debug (" Subnet %s", piaddr (n -> net));
+ log_debug (" netmask %s",
+ piaddr (n -> netmask));
+ }
+ log_info ("Shared networks:");
+ for (s = shared_networks; s; s = s -> next) {
+ log_info (" %s", s -> name);
+ for (p = s -> pools; p; p = p -> next) {
+ lptr [FREE_LEASES] = &p -> free;
+ lptr [ACTIVE_LEASES] = &p -> active;
+ lptr [EXPIRED_LEASES] = &p -> expired;
+ lptr [ABANDONED_LEASES] = &p -> abandoned;
+ lptr [BACKUP_LEASES] = &p -> backup;
+ lptr [RESERVED_LEASES] = &p->reserved;
+
+ for (i = FREE_LEASES; i <= RESERVED_LEASES; i++) {
+ for (l = *(lptr [i]); l; l = l -> next) {
+ print_lease (l);
+ }
+ }
+ }
+ }
+}
+
+HASH_FUNCTIONS(lease_ip, const unsigned char *, struct lease, lease_ip_hash_t,
+ lease_reference, lease_dereference, do_ip4_hash)
+HASH_FUNCTIONS(lease_id, const unsigned char *, struct lease, lease_id_hash_t,
+ lease_reference, lease_dereference, do_id_hash)
+HASH_FUNCTIONS (host, const unsigned char *, struct host_decl, host_hash_t,
+ host_reference, host_dereference, do_string_hash)
+HASH_FUNCTIONS (class, const char *, struct class, class_hash_t,
+ class_reference, class_dereference, do_string_hash)
+
+#if defined (DEBUG_MEMORY_LEAKAGE) && \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+extern struct hash_table *dns_zone_hash;
+extern struct interface_info **interface_vector;
+extern int interface_count;
+dhcp_control_object_t *dhcp_control_object;
+extern struct hash_table *auth_key_hash;
+struct hash_table *universe_hash;
+struct universe **universes;
+int universe_count, universe_max;
+#if 0
+extern int end;
+#endif
+
+#if defined (COMPACT_LEASES)
+extern struct lease *lease_hunks;
+#endif
+
+void free_everything(void)
+{
+ struct subnet *sc = (struct subnet *)0, *sn = (struct subnet *)0;
+ struct shared_network *nc = (struct shared_network *)0,
+ *nn = (struct shared_network *)0;
+ struct pool *pc = (struct pool *)0, *pn = (struct pool *)0;
+ struct lease *lc = (struct lease *)0, *ln = (struct lease *)0;
+ struct interface_info *ic = (struct interface_info *)0,
+ *in = (struct interface_info *)0;
+ struct class *cc = (struct class *)0, *cn = (struct class *)0;
+ struct collection *lp;
+ int i;
+
+ /* Get rid of all the hash tables. */
+ if (host_hw_addr_hash)
+ host_free_hash_table (&host_hw_addr_hash, MDL);
+ host_hw_addr_hash = 0;
+ if (host_uid_hash)
+ host_free_hash_table (&host_uid_hash, MDL);
+ host_uid_hash = 0;
+ if (lease_uid_hash)
+ lease_id_free_hash_table (&lease_uid_hash, MDL);
+ lease_uid_hash = 0;
+ if (lease_ip_addr_hash)
+ lease_ip_free_hash_table (&lease_ip_addr_hash, MDL);
+ lease_ip_addr_hash = 0;
+ if (lease_hw_addr_hash)
+ lease_id_free_hash_table (&lease_hw_addr_hash, MDL);
+ lease_hw_addr_hash = 0;
+ if (host_name_hash)
+ host_free_hash_table (&host_name_hash, MDL);
+ host_name_hash = 0;
+ if (dns_zone_hash)
+ dns_zone_free_hash_table (&dns_zone_hash, MDL);
+ dns_zone_hash = 0;
+
+ while (host_id_info != NULL) {
+ host_id_info_t *tmp;
+ option_dereference(&host_id_info->option, MDL);
+ host_free_hash_table(&host_id_info->values_hash, MDL);
+ tmp = host_id_info->next;
+ dfree(host_id_info, MDL);
+ host_id_info = tmp;
+ }
+#if 0
+ if (auth_key_hash)
+ auth_key_free_hash_table (&auth_key_hash, MDL);
+#endif
+ auth_key_hash = 0;
+
+ omapi_object_dereference ((omapi_object_t **)&dhcp_control_object,
+ MDL);
+
+ for (lp = collections; lp; lp = lp -> next) {
+ if (lp -> classes) {
+ class_reference (&cn, lp -> classes, MDL);
+ do {
+ if (cn) {
+ class_reference (&cc, cn, MDL);
+ class_dereference (&cn, MDL);
+ }
+ if (cc -> nic) {
+ class_reference (&cn, cc -> nic, MDL);
+ class_dereference (&cc -> nic, MDL);
+ }
+ group_dereference (&cc -> group, MDL);
+ if (cc -> hash) {
+ class_free_hash_table (&cc -> hash, MDL);
+ cc -> hash = (struct hash_table *)0;
+ }
+ class_dereference (&cc, MDL);
+ } while (cn);
+ class_dereference (&lp -> classes, MDL);
+ }
+ }
+
+ if (interface_vector) {
+ for (i = 0; i < interface_count; i++) {
+ if (interface_vector [i])
+ interface_dereference (&interface_vector [i], MDL);
+ }
+ dfree (interface_vector, MDL);
+ interface_vector = 0;
+ }
+
+ if (interfaces) {
+ interface_reference (&in, interfaces, MDL);
+ do {
+ if (in) {
+ interface_reference (&ic, in, MDL);
+ interface_dereference (&in, MDL);
+ }
+ if (ic -> next) {
+ interface_reference (&in, ic -> next, MDL);
+ interface_dereference (&ic -> next, MDL);
+ }
+ omapi_unregister_io_object ((omapi_object_t *)ic);
+ if (ic -> shared_network) {
+ if (ic -> shared_network -> interface)
+ interface_dereference
+ (&ic -> shared_network -> interface, MDL);
+ shared_network_dereference (&ic -> shared_network, MDL);
+ }
+ interface_dereference (&ic, MDL);
+ } while (in);
+ interface_dereference (&interfaces, MDL);
+ }
+
+ /* Subnets are complicated because of the extra links. */
+ if (subnets) {
+ subnet_reference (&sn, subnets, MDL);
+ do {
+ if (sn) {
+ subnet_reference (&sc, sn, MDL);
+ subnet_dereference (&sn, MDL);
+ }
+ if (sc -> next_subnet) {
+ subnet_reference (&sn, sc -> next_subnet, MDL);
+ subnet_dereference (&sc -> next_subnet, MDL);
+ }
+ if (sc -> next_sibling)
+ subnet_dereference (&sc -> next_sibling, MDL);
+ if (sc -> shared_network)
+ shared_network_dereference (&sc -> shared_network, MDL);
+ group_dereference (&sc -> group, MDL);
+ if (sc -> interface)
+ interface_dereference (&sc -> interface, MDL);
+ subnet_dereference (&sc, MDL);
+ } while (sn);
+ subnet_dereference (&subnets, MDL);
+ }
+
+ /* So are shared networks. */
+ /* XXX: this doesn't work presently, but i'm ok just filtering
+ * it out of the noise (you get a bigger spike on the real leaks).
+ * It would be good to fix this, but it is not a "real bug," so not
+ * today. This hack is incomplete, it doesn't trim out sub-values.
+ */
+ if (shared_networks) {
+ shared_network_dereference (&shared_networks, MDL);
+ /* This is the old method (tries to free memory twice, broken) */
+ } else if (0) {
+ shared_network_reference (&nn, shared_networks, MDL);
+ do {
+ if (nn) {
+ shared_network_reference (&nc, nn, MDL);
+ shared_network_dereference (&nn, MDL);
+ }
+ if (nc -> next) {
+ shared_network_reference (&nn, nc -> next, MDL);
+ shared_network_dereference (&nc -> next, MDL);
+ }
+
+ /* As are pools. */
+ if (nc -> pools) {
+ pool_reference (&pn, nc -> pools, MDL);
+ do {
+ struct lease **lptr[RESERVED_LEASES+1];
+
+ if (pn) {
+ pool_reference (&pc, pn, MDL);
+ pool_dereference (&pn, MDL);
+ }
+ if (pc -> next) {
+ pool_reference (&pn, pc -> next, MDL);
+ pool_dereference (&pc -> next, MDL);
+ }
+
+ lptr [FREE_LEASES] = &pc -> free;
+ lptr [ACTIVE_LEASES] = &pc -> active;
+ lptr [EXPIRED_LEASES] = &pc -> expired;
+ lptr [ABANDONED_LEASES] = &pc -> abandoned;
+ lptr [BACKUP_LEASES] = &pc -> backup;
+ lptr [RESERVED_LEASES] = &pc->reserved;
+
+ /* As (sigh) are leases. */
+ for (i = FREE_LEASES ; i <= RESERVED_LEASES ; i++) {
+ if (*lptr [i]) {
+ lease_reference (&ln, *lptr [i], MDL);
+ do {
+ if (ln) {
+ lease_reference (&lc, ln, MDL);
+ lease_dereference (&ln, MDL);
+ }
+ if (lc -> next) {
+ lease_reference (&ln, lc -> next, MDL);
+ lease_dereference (&lc -> next, MDL);
+ }
+ if (lc -> billing_class)
+ class_dereference (&lc -> billing_class,
+ MDL);
+ if (lc -> state)
+ free_lease_state (lc -> state, MDL);
+ lc -> state = (struct lease_state *)0;
+ if (lc -> n_hw)
+ lease_dereference (&lc -> n_hw, MDL);
+ if (lc -> n_uid)
+ lease_dereference (&lc -> n_uid, MDL);
+ lease_dereference (&lc, MDL);
+ } while (ln);
+ lease_dereference (lptr [i], MDL);
+ }
+ }
+ if (pc -> group)
+ group_dereference (&pc -> group, MDL);
+ if (pc -> shared_network)
+ shared_network_dereference (&pc -> shared_network,
+ MDL);
+ pool_dereference (&pc, MDL);
+ } while (pn);
+ pool_dereference (&nc -> pools, MDL);
+ }
+ /* Because of a circular reference, we need to nuke this
+ manually. */
+ group_dereference (&nc -> group, MDL);
+ shared_network_dereference (&nc, MDL);
+ } while (nn);
+ shared_network_dereference (&shared_networks, MDL);
+ }
+
+ cancel_all_timeouts ();
+ relinquish_timeouts ();
+ relinquish_ackqueue();
+ trace_free_all ();
+ group_dereference (&root_group, MDL);
+ executable_statement_dereference (&default_classification_rules, MDL);
+
+ shutdown_state = shutdown_drop_omapi_connections;
+ omapi_io_state_foreach (dhcp_io_shutdown, 0);
+ shutdown_state = shutdown_listeners;
+ omapi_io_state_foreach (dhcp_io_shutdown, 0);
+ shutdown_state = shutdown_dhcp;
+ omapi_io_state_foreach (dhcp_io_shutdown, 0);
+
+ omapi_object_dereference ((omapi_object_t **)&icmp_state, MDL);
+
+ universe_free_hash_table (&universe_hash, MDL);
+ for (i = 0; i < universe_count; i++) {
+#if 0
+ union {
+ const char *c;
+ char *s;
+ } foo;
+#endif
+ if (universes [i]) {
+ if (universes[i]->name_hash)
+ option_name_free_hash_table(
+ &universes[i]->name_hash,
+ MDL);
+ if (universes[i]->code_hash)
+ option_code_free_hash_table(
+ &universes[i]->code_hash,
+ MDL);
+#if 0
+ if (universes [i] -> name > (char *)&end) {
+ foo.c = universes [i] -> name;
+ dfree (foo.s, MDL);
+ }
+ if (universes [i] > (struct universe *)&end)
+ dfree (universes [i], MDL);
+#endif
+ }
+ }
+ dfree (universes, MDL);
+
+ relinquish_free_lease_states ();
+ relinquish_free_pairs ();
+ relinquish_free_expressions ();
+ relinquish_free_binding_values ();
+ relinquish_free_option_caches ();
+ relinquish_free_packets ();
+#if defined(COMPACT_LEASES)
+ relinquish_lease_hunks ();
+#endif
+ relinquish_hash_bucket_hunks ();
+ omapi_type_relinquish ();
+}
+#endif /* DEBUG_MEMORY_LEAKAGE_ON_EXIT */
diff --git a/server/mdb6.c b/server/mdb6.c
new file mode 100644
index 0000000..9d410f5
--- /dev/null
+++ b/server/mdb6.c
@@ -0,0 +1,2555 @@
+/*
+ * Copyright (C) 2007-2011 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* TODO: assert() */
+/* TODO: simplify functions, as pool is now in iaaddr */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#include <stdarg.h>
+#include "dhcpd.h"
+#include "omapip/omapip.h"
+#include "omapip/hash.h"
+#include <isc/md5.h>
+
+HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t,
+ ia_reference, ia_dereference, do_string_hash)
+
+ia_hash_t *ia_na_active;
+ia_hash_t *ia_ta_active;
+ia_hash_t *ia_pd_active;
+
+HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t,
+ iasubopt_reference, iasubopt_dereference, do_string_hash)
+
+struct ipv6_pool **pools;
+int num_pools;
+
+/*
+ * Create a new IAADDR/PREFIX structure.
+ *
+ * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) {
+ struct iasubopt *tmp;
+
+ if (iasubopt == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*iasubopt != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->state = FTS_FREE;
+ tmp->heap_index = -1;
+ tmp->plen = 255;
+
+ *iasubopt = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IAADDR/PREFIX structure.
+ *
+ * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src,
+ const char *file, int line) {
+ if (iasubopt == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*iasubopt != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ *iasubopt = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Dereference an IAADDR/PREFIX structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) {
+ struct iasubopt *tmp;
+
+ if ((iasubopt == NULL) || (*iasubopt == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = *iasubopt;
+ *iasubopt = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ if (tmp->ia != NULL) {
+ ia_dereference(&(tmp->ia), file, line);
+ }
+ if (tmp->ipv6_pool != NULL) {
+ ipv6_pool_dereference(&(tmp->ipv6_pool), file, line);
+ }
+ if (tmp->scope != NULL) {
+ binding_scope_dereference(&tmp->scope, file, line);
+ }
+ dfree(tmp, file, line);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Make the key that we use for IA.
+ */
+isc_result_t
+ia_make_key(struct data_string *key, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line) {
+
+ memset(key, 0, sizeof(*key));
+ key->len = duid_len + sizeof(iaid);
+ if (!buffer_allocate(&(key->buffer), key->len, file, line)) {
+ return ISC_R_NOMEMORY;
+ }
+ key->data = key->buffer->data;
+ memcpy((char *)key->data, &iaid, sizeof(iaid));
+ memcpy((char *)key->data + sizeof(iaid), duid, duid_len);
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Create a new IA structure.
+ *
+ * - ia must be a pointer to a (struct ia_xx *) pointer previously
+ * initialized to NULL
+ * - iaid and duid are values from the client
+ *
+ * XXXsk: we don't concern ourself with the byte order of the IAID,
+ * which might be a problem if we transfer this structure
+ * between machines of different byte order
+ */
+isc_result_t
+ia_allocate(struct ia_xx **ia, u_int32_t iaid,
+ const char *duid, unsigned int duid_len,
+ const char *file, int line) {
+ struct ia_xx *tmp;
+
+ if (ia == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*ia != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ if (ia_make_key(&tmp->iaid_duid, iaid,
+ duid, duid_len, file, line) != ISC_R_SUCCESS) {
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+
+ *ia = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IA structure.
+ *
+ * - ia must be a pointer to a (struct ia_xx *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ia_reference(struct ia_xx **ia, struct ia_xx *src,
+ const char *file, int line) {
+ if (ia == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*ia != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ *ia = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Dereference an IA structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ia_dereference(struct ia_xx **ia, const char *file, int line) {
+ struct ia_xx *tmp;
+ int i;
+
+ if ((ia == NULL) || (*ia == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = *ia;
+ *ia = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ if (tmp->iasubopt != NULL) {
+ for (i=0; i<tmp->num_iasubopt; i++) {
+ iasubopt_dereference(&(tmp->iasubopt[i]),
+ file, line);
+ }
+ dfree(tmp->iasubopt, file, line);
+ }
+ data_string_forget(&(tmp->iaid_duid), file, line);
+ dfree(tmp, file, line);
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * Add an IAADDR/PREFIX entry to an IA structure.
+ */
+isc_result_t
+ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
+ const char *file, int line) {
+ int max;
+ struct iasubopt **new;
+
+ /*
+ * Grow our array if we need to.
+ *
+ * Note: we pick 4 as the increment, as that seems a reasonable
+ * guess as to how many addresses/prefixes we might expect
+ * on an interface.
+ */
+ if (ia->max_iasubopt <= ia->num_iasubopt) {
+ max = ia->max_iasubopt + 4;
+ new = dmalloc(max * sizeof(struct iasubopt *), file, line);
+ if (new == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+ memcpy(new, ia->iasubopt,
+ ia->num_iasubopt * sizeof(struct iasubopt *));
+ ia->iasubopt = new;
+ ia->max_iasubopt = max;
+ }
+
+ iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt,
+ file, line);
+ ia->num_iasubopt++;
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Remove an IAADDR/PREFIX entry to an IA structure.
+ *
+ * Note: if a suboption appears more than once, then only ONE will be removed.
+ */
+void
+ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
+ const char *file, int line) {
+ int i, j;
+
+ for (i=0; i<ia->num_iasubopt; i++) {
+ if (ia->iasubopt[i] == iasubopt) {
+ /* remove this sub option */
+ iasubopt_dereference(&(ia->iasubopt[i]), file, line);
+ /* move remaining suboption pointers down one */
+ for (j=i+1; j < ia->num_iasubopt; j++) {
+ ia->iasubopt[j-1] = ia->iasubopt[j];
+ }
+ /* decrease our total count */
+ /* remove the back-reference in the suboption itself */
+ ia_dereference(&iasubopt->ia, file, line);
+ ia->num_iasubopt--;
+ return;
+ }
+ }
+ log_error("%s(%d): IAADDR/PREFIX not in IA", file, line);
+}
+
+/*
+ * Remove all addresses/prefixes from an IA.
+ */
+void
+ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) {
+ int i;
+
+ for (i=0; i<ia->num_iasubopt; i++) {
+ ia_dereference(&(ia->iasubopt[i]->ia), file, line);
+ iasubopt_dereference(&(ia->iasubopt[i]), file, line);
+ }
+ ia->num_iasubopt = 0;
+}
+
+/*
+ * Compare two IA.
+ */
+isc_boolean_t
+ia_equal(const struct ia_xx *a, const struct ia_xx *b)
+{
+ isc_boolean_t found;
+ int i, j;
+
+ /*
+ * Handle cases where one or both of the inputs is NULL.
+ */
+ if (a == NULL) {
+ if (b == NULL) {
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * Check the type is the same.
+ */
+ if (a->ia_type != b->ia_type) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Check the DUID is the same.
+ */
+ if (a->iaid_duid.len != b->iaid_duid.len) {
+ return ISC_FALSE;
+ }
+ if (memcmp(a->iaid_duid.data,
+ b->iaid_duid.data, a->iaid_duid.len) != 0) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Make sure we have the same number of addresses/prefixes in each.
+ */
+ if (a->num_iasubopt != b->num_iasubopt) {
+ return ISC_FALSE;
+ }
+
+ /*
+ * Check that each address/prefix is present in both.
+ */
+ for (i=0; i<a->num_iasubopt; i++) {
+ found = ISC_FALSE;
+ for (j=0; j<a->num_iasubopt; j++) {
+ if (a->iasubopt[i]->plen != b->iasubopt[i]->plen)
+ continue;
+ if (memcmp(&(a->iasubopt[i]->addr),
+ &(b->iasubopt[j]->addr),
+ sizeof(struct in6_addr)) == 0) {
+ found = ISC_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ return ISC_FALSE;
+ }
+ }
+
+ /*
+ * These are the same in every way we care about.
+ */
+ return ISC_TRUE;
+}
+
+/*
+ * Helper function for lease heaps.
+ * Makes the top of the heap the oldest lease.
+ */
+static isc_boolean_t
+lease_older(void *a, void *b) {
+ struct iasubopt *la = (struct iasubopt *)a;
+ struct iasubopt *lb = (struct iasubopt *)b;
+
+ if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) {
+ return difftime(la->soft_lifetime_end_time,
+ lb->soft_lifetime_end_time) < 0;
+ } else {
+ return difftime(la->hard_lifetime_end_time,
+ lb->hard_lifetime_end_time) < 0;
+ }
+}
+
+/*
+ * Helper function for lease address/prefix heaps.
+ * Callback when an address's position in the heap changes.
+ */
+static void
+lease_index_changed(void *iasubopt, unsigned int new_heap_index) {
+ ((struct iasubopt *)iasubopt)-> heap_index = new_heap_index;
+}
+
+
+/*
+ * Create a new IPv6 lease pool structure.
+ *
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
+ const struct in6_addr *start_addr, int bits,
+ int units, const char *file, int line) {
+ struct ipv6_pool *tmp;
+
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = dmalloc(sizeof(*tmp), file, line);
+ if (tmp == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ tmp->refcnt = 1;
+ tmp->pool_type = type;
+ tmp->start_addr = *start_addr;
+ tmp->bits = bits;
+ tmp->units = units;
+ if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) {
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
+ 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
+ iasubopt_free_hash_table(&(tmp->leases), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+ if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
+ 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
+ isc_heap_destroy(&(tmp->active_timeouts));
+ iasubopt_free_hash_table(&(tmp->leases), file, line);
+ dfree(tmp, file, line);
+ return ISC_R_NOMEMORY;
+ }
+
+ *pool = tmp;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Reference an IPv6 pool structure.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
+ const char *file, int line) {
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ if (src == NULL) {
+ log_error("%s(%d): NULL pointer reference", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+ *pool = src;
+ src->refcnt++;
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed
+ * to prevent the lease from being garbage collected out from under the
+ * pool.
+ *
+ * The references are made from the hash and from the heap. The following
+ * helper functions dereference these when a pool is destroyed.
+ */
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the hash entries in a pool.
+ */
+static isc_result_t
+dereference_hash_entry(const void *name, unsigned len, void *value) {
+ struct iasubopt *iasubopt = (struct iasubopt *)value;
+
+ iasubopt_dereference(&iasubopt, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Helper function for pool cleanup.
+ * Dereference each of the heap entries in a pool.
+ */
+static void
+dereference_heap_entry(void *value, void *dummy) {
+ struct iasubopt *iasubopt = (struct iasubopt *)value;
+
+ iasubopt_dereference(&iasubopt, MDL);
+}
+
+
+/*
+ * Dereference an IPv6 pool structure.
+ *
+ * If it is the last reference, then the memory for the
+ * structure is freed.
+ */
+isc_result_t
+ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
+ struct ipv6_pool *tmp;
+
+ if ((pool == NULL) || (*pool == NULL)) {
+ log_error("%s(%d): NULL pointer", file, line);
+ return DHCP_R_INVALIDARG;
+ }
+
+ tmp = *pool;
+ *pool = NULL;
+
+ tmp->refcnt--;
+ if (tmp->refcnt < 0) {
+ log_error("%s(%d): negative refcnt", file, line);
+ tmp->refcnt = 0;
+ }
+ if (tmp->refcnt == 0) {
+ iasubopt_hash_foreach(tmp->leases, dereference_hash_entry);
+ iasubopt_free_hash_table(&(tmp->leases), file, line);
+ isc_heap_foreach(tmp->active_timeouts,
+ dereference_heap_entry, NULL);
+ isc_heap_destroy(&(tmp->active_timeouts));
+ isc_heap_foreach(tmp->inactive_timeouts,
+ dereference_heap_entry, NULL);
+ isc_heap_destroy(&(tmp->inactive_timeouts));
+ dfree(tmp, file, line);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Create an address by hashing the input, and using that for
+ * the non-network part.
+ */
+static void
+build_address6(struct in6_addr *addr,
+ const struct in6_addr *net_start_addr, int net_bits,
+ const struct data_string *input) {
+ isc_md5_t ctx;
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Use MD5 to get a nice 128 bit hash of the input.
+ * Yes, we know MD5 isn't cryptographically sound.
+ * No, we don't care.
+ */
+ isc_md5_init(&ctx);
+ isc_md5_update(&ctx, input->data, input->len);
+ isc_md5_final(&ctx, (unsigned char *)addr);
+
+ /*
+ * Copy the [0..128] network bits over.
+ */
+ str = (char *)addr;
+ net_str = (const char *)net_start_addr;
+ net_bytes = net_bits / 8;
+ for (i = 0; i < net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ switch (net_bits % 8) {
+ case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+ case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+
+ /*
+ * Set the universal/local bit ("u bit") to zero for /64s. The
+ * individual/group bit ("g bit") is unchanged, because the g-bit
+ * has no meaning when the u-bit is cleared.
+ */
+ if (net_bits == 64)
+ str[8] &= ~0x02;
+}
+
+/*
+ * Create a temporary address by a variant of RFC 4941 algo.
+ * Note: this should not be used for prefixes shorter than 64 bits.
+ */
+static void
+build_temporary6(struct in6_addr *addr,
+ const struct in6_addr *net_start_addr, int net_bits,
+ const struct data_string *input) {
+ static u_int32_t history[2];
+ static u_int32_t counter = 0;
+ isc_md5_t ctx;
+ unsigned char md[16];
+
+ /*
+ * First time/time to reseed.
+ * Please use a good pseudo-random generator here!
+ */
+ if (counter == 0) {
+ isc_random_get(&history[0]);
+ isc_random_get(&history[1]);
+ }
+
+ /*
+ * Use MD5 as recommended by RFC 4941.
+ */
+ isc_md5_init(&ctx);
+ isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL);
+ isc_md5_update(&ctx, input->data, input->len);
+ isc_md5_final(&ctx, md);
+
+ /*
+ * Build the address.
+ */
+ if (net_bits == 64) {
+ memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
+ memcpy(&addr->s6_addr[8], md, 8);
+ addr->s6_addr[8] &= ~0x02;
+ } else {
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Copy the [0..128] network bits over.
+ */
+ str = (char *)addr;
+ net_str = (const char *)net_start_addr;
+ net_bytes = net_bits / 8;
+ for (i = 0; i < net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ memcpy(str + net_bytes, md, 16 - net_bytes);
+ switch (net_bits % 8) {
+ case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+ case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+ }
+
+
+ /*
+ * Save history for the next call.
+ */
+ memcpy((unsigned char *)&history[0], md + 8, 8);
+ counter++;
+}
+
+/* Reserved Subnet Router Anycast ::0:0:0:0. */
+static struct in6_addr rtany;
+/* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
+static struct in6_addr resany;
+
+/*
+ * Create a lease for the given address and client duid.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ *
+ * Right now we simply hash the DUID, and if we get a collision, we hash
+ * again until we find a free address. We try this a fixed number of times,
+ * to avoid getting stuck in a loop (this is important on small pools
+ * where we can run out of space).
+ *
+ * We return the number of attempts that it took to find an available
+ * lease. This tells callers when a pool is are filling up, as
+ * well as an indication of how full the pool is; statistically the
+ * more full a pool is the more attempts must be made before finding
+ * a free lease. Realistically this will only happen in very full
+ * pools.
+ *
+ * We probably want different algorithms depending on the network size, in
+ * the long term.
+ */
+isc_result_t
+create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
+ unsigned int *attempts,
+ const struct data_string *uid, time_t soft_lifetime_end_time) {
+ struct data_string ds;
+ struct in6_addr tmp;
+ struct iasubopt *test_iaaddr;
+ struct data_string new_ds;
+ struct iasubopt *iaaddr;
+ isc_result_t result;
+ isc_boolean_t reserved_iid;
+ static isc_boolean_t init_resiid = ISC_FALSE;
+
+ /*
+ * Fill the reserved IIDs.
+ */
+ if (!init_resiid) {
+ memset(&rtany, 0, 16);
+ memset(&resany, 0, 8);
+ resany.s6_addr[8] = 0xfd;
+ memset(&resany.s6_addr[9], 0xff, 6);
+ init_resiid = ISC_TRUE;
+ }
+
+ /*
+ * Use the UID as our initial seed for the hash
+ */
+ memset(&ds, 0, sizeof(ds));
+ data_string_copy(&ds, (struct data_string *)uid, MDL);
+
+ *attempts = 0;
+ for (;;) {
+ /*
+ * Give up at some point.
+ */
+ if (++(*attempts) > 100) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * Build a resource.
+ */
+ switch (pool->pool_type) {
+ case D6O_IA_NA:
+ /* address */
+ build_address6(&tmp, &pool->start_addr,
+ pool->bits, &ds);
+ break;
+ case D6O_IA_TA:
+ /* temporary address */
+ build_temporary6(&tmp, &pool->start_addr,
+ pool->bits, &ds);
+ break;
+ case D6O_IA_PD:
+ /* prefix */
+ log_error("create_lease6: prefix pool.");
+ return DHCP_R_INVALIDARG;
+ default:
+ log_error("create_lease6: untyped pool.");
+ return DHCP_R_INVALIDARG;
+ }
+
+ /*
+ * Avoid reserved interface IDs.
+ * (cf. draft-krishnan-ipv6-reserved-iids-02.txt)
+ */
+ reserved_iid = ISC_FALSE;
+ if (memcmp(&tmp.s6_addr[8], &rtany, 8) == 0) {
+ reserved_iid = ISC_TRUE;
+ }
+ if (!reserved_iid &&
+ (memcmp(&tmp.s6_addr[8], &resany, 7) == 0) &&
+ ((tmp.s6_addr[15] & 0x80) == 0x80)) {
+ reserved_iid = ISC_TRUE;
+ }
+
+ /*
+ * If this address is not in use, we're happy with it
+ */
+ test_iaaddr = NULL;
+ if (!reserved_iid &&
+ (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
+ &tmp, sizeof(tmp), MDL) == 0)) {
+ break;
+ }
+ if (test_iaaddr != NULL)
+ iasubopt_dereference(&test_iaaddr, MDL);
+
+ /*
+ * Otherwise, we create a new input, adding the address
+ */
+ memset(&new_ds, 0, sizeof(new_ds));
+ new_ds.len = ds.len + sizeof(tmp);
+ if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ new_ds.data = new_ds.buffer->data;
+ memcpy(new_ds.buffer->data, ds.data, ds.len);
+ memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
+ data_string_forget(&ds, MDL);
+ data_string_copy(&ds, &new_ds, MDL);
+ data_string_forget(&new_ds, MDL);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /*
+ * We're happy with the address, create an IAADDR
+ * to hold it.
+ */
+ iaaddr = NULL;
+ result = iasubopt_allocate(&iaaddr, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ iaaddr->plen = 0;
+ memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
+
+ /*
+ * Add the lease to the pool (note state is free, not active?!).
+ */
+ result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
+ if (result == ISC_R_SUCCESS) {
+ iasubopt_reference(addr, iaaddr, MDL);
+ }
+ iasubopt_dereference(&iaaddr, MDL);
+ return result;
+}
+
+/*
+ * Put a lease in the pool directly. This is intended to be used when
+ * loading leases from the file.
+ */
+isc_result_t
+add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
+ time_t valid_lifetime_end_time) {
+ isc_result_t insert_result;
+ struct iasubopt *test_iasubopt;
+ struct iasubopt *tmp_iasubopt;
+
+ /* If a state was not assigned by the caller, assume active. */
+ if (lease->state == 0)
+ lease->state = FTS_ACTIVE;
+
+ ipv6_pool_reference(&lease->ipv6_pool, pool, MDL);
+
+ /*
+ * If this IAADDR/PREFIX is already in our structures, remove the
+ * old one.
+ */
+ test_iasubopt = NULL;
+ if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
+ &lease->addr, sizeof(lease->addr), MDL)) {
+ /* XXX: we should probably ask the lease what heap it is on
+ * (as a consistency check).
+ * XXX: we should probably have one function to "put this lease
+ * on its heap" rather than doing these if's everywhere. If
+ * you add more states to this list, don't.
+ */
+ if ((test_iasubopt->state == FTS_ACTIVE) ||
+ (test_iasubopt->state == FTS_ABANDONED)) {
+ isc_heap_delete(pool->active_timeouts,
+ test_iasubopt->heap_index);
+ pool->num_active--;
+ } else {
+ isc_heap_delete(pool->inactive_timeouts,
+ test_iasubopt->heap_index);
+ pool->num_inactive--;
+ }
+
+ iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
+ sizeof(test_iasubopt->addr), MDL);
+
+ /*
+ * We're going to do a bit of evil trickery here.
+ *
+ * We need to dereference the entry once to remove our
+ * current reference (in test_iasubopt), and then one
+ * more time to remove the reference left when the
+ * address was added to the pool before.
+ */
+ tmp_iasubopt = test_iasubopt;
+ iasubopt_dereference(&test_iasubopt, MDL);
+ iasubopt_dereference(&tmp_iasubopt, MDL);
+ }
+
+ /*
+ * Add IAADDR/PREFIX to our structures.
+ */
+ tmp_iasubopt = NULL;
+ iasubopt_reference(&tmp_iasubopt, lease, MDL);
+ if ((tmp_iasubopt->state == FTS_ACTIVE) ||
+ (tmp_iasubopt->state == FTS_ABANDONED)) {
+ tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time;
+ iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr,
+ sizeof(tmp_iasubopt->addr), lease, MDL);
+ insert_result = isc_heap_insert(pool->active_timeouts,
+ tmp_iasubopt);
+ if (insert_result == ISC_R_SUCCESS)
+ pool->num_active++;
+ } else {
+ tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time;
+ insert_result = isc_heap_insert(pool->inactive_timeouts,
+ tmp_iasubopt);
+ if (insert_result == ISC_R_SUCCESS)
+ pool->num_inactive++;
+ }
+ if (insert_result != ISC_R_SUCCESS) {
+ iasubopt_hash_delete(pool->leases, &lease->addr,
+ sizeof(lease->addr), MDL);
+ iasubopt_dereference(&tmp_iasubopt, MDL);
+ return insert_result;
+ }
+
+ /*
+ * Note: we intentionally leave tmp_iasubopt referenced; there
+ * is a reference in the heap/hash, after all.
+ */
+
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Determine if an address is present in a pool or not.
+ */
+isc_boolean_t
+lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
+ struct iasubopt *test_iaaddr;
+
+ test_iaaddr = NULL;
+ if (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
+ (void *)addr, sizeof(*addr), MDL)) {
+ iasubopt_dereference(&test_iaaddr, MDL);
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Put the lease on our active pool.
+ */
+static isc_result_t
+move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = lease->heap_index;
+ insert_result = isc_heap_insert(pool->active_timeouts, lease);
+ if (insert_result == ISC_R_SUCCESS) {
+ iasubopt_hash_add(pool->leases, &lease->addr,
+ sizeof(lease->addr), lease, MDL);
+ isc_heap_delete(pool->inactive_timeouts, old_heap_index);
+ pool->num_active++;
+ pool->num_inactive--;
+ lease->state = FTS_ACTIVE;
+ }
+ return insert_result;
+}
+
+/*
+ * Renew an lease in the pool.
+ *
+ * To do this, first set the new hard_lifetime_end_time for the resource,
+ * and then invoke renew_lease6() on it.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ */
+isc_result_t
+renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
+ /*
+ * If we're already active, then we can just move our expiration
+ * time down the heap.
+ *
+ * If we're abandoned then we are already on the active list
+ * but we need to retag the lease and move our expiration
+ * from infinite to the current value
+ *
+ * Otherwise, we have to move from the inactive heap to the
+ * active heap.
+ */
+ if (lease->state == FTS_ACTIVE) {
+ isc_heap_decreased(pool->active_timeouts, lease->heap_index);
+ return ISC_R_SUCCESS;
+ } else if (lease->state == FTS_ABANDONED) {
+ char tmp_addr[INET6_ADDRSTRLEN];
+ lease->state = FTS_ACTIVE;
+ isc_heap_increased(pool->active_timeouts, lease->heap_index);
+ log_info("Reclaiming previously abandoned address %s",
+ inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
+ sizeof(tmp_addr)));
+ return ISC_R_SUCCESS;
+ } else {
+ return move_lease_to_active(pool, lease);
+ }
+}
+
+/*
+ * Put the lease on our inactive pool, with the specified state.
+ */
+static isc_result_t
+move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease,
+ binding_state_t state) {
+ isc_result_t insert_result;
+ int old_heap_index;
+
+ old_heap_index = lease->heap_index;
+ insert_result = isc_heap_insert(pool->inactive_timeouts, lease);
+ if (insert_result == ISC_R_SUCCESS) {
+#if defined (NSUPDATE)
+ /* Process events upon expiration. */
+ if (pool->pool_type != D6O_IA_PD) {
+ ddns_removals(NULL, lease, NULL);
+ }
+#endif
+
+ /* Binding scopes are no longer valid after expiry or
+ * release.
+ */
+ if (lease->scope != NULL) {
+ binding_scope_dereference(&lease->scope, MDL);
+ }
+
+ iasubopt_hash_delete(pool->leases,
+ &lease->addr, sizeof(lease->addr), MDL);
+ isc_heap_delete(pool->active_timeouts, old_heap_index);
+ lease->state = state;
+ pool->num_active--;
+ pool->num_inactive++;
+ }
+ return insert_result;
+}
+
+/*
+ * Expire the oldest lease if it's lifetime_end_time is
+ * older than the given time.
+ *
+ * - leasep must be a pointer to a (struct iasubopt *) pointer previously
+ * initialized to NULL
+ *
+ * On return leasep has a reference to the removed entry. It is left
+ * pointing to NULL if the oldest lease has not expired.
+ */
+isc_result_t
+expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) {
+ struct iasubopt *tmp;
+ isc_result_t result;
+
+ if (leasep == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*leasep != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ if (pool->num_active > 0) {
+ tmp = (struct iasubopt *)
+ isc_heap_element(pool->active_timeouts, 1);
+ if (now > tmp->hard_lifetime_end_time) {
+ result = move_lease_to_inactive(pool, tmp,
+ FTS_EXPIRED);
+ if (result == ISC_R_SUCCESS) {
+ iasubopt_reference(leasep, tmp, MDL);
+ }
+ return result;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+
+/*
+ * For a declined lease, leave it on the "active" pool, but mark
+ * it as declined. Give it an infinite (well, really long) life.
+ */
+isc_result_t
+decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
+ isc_result_t result;
+
+ if ((lease->state != FTS_ACTIVE) &&
+ (lease->state != FTS_ABANDONED)) {
+ result = move_lease_to_active(pool, lease);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ }
+ lease->state = FTS_ABANDONED;
+ lease->hard_lifetime_end_time = MAX_TIME;
+ isc_heap_decreased(pool->active_timeouts, lease->heap_index);
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Put the returned lease on our inactive pool.
+ */
+isc_result_t
+release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
+ if (lease->state == FTS_ACTIVE) {
+ return move_lease_to_inactive(pool, lease, FTS_RELEASED);
+ } else {
+ return ISC_R_SUCCESS;
+ }
+}
+
+/*
+ * Create a prefix by hashing the input, and using that for
+ * the part subject to allocation.
+ */
+static void
+build_prefix6(struct in6_addr *pref,
+ const struct in6_addr *net_start_pref,
+ int pool_bits, int pref_bits,
+ const struct data_string *input) {
+ isc_md5_t ctx;
+ int net_bytes;
+ int i;
+ char *str;
+ const char *net_str;
+
+ /*
+ * Use MD5 to get a nice 128 bit hash of the input.
+ * Yes, we know MD5 isn't cryptographically sound.
+ * No, we don't care.
+ */
+ isc_md5_init(&ctx);
+ isc_md5_update(&ctx, input->data, input->len);
+ isc_md5_final(&ctx, (unsigned char *)pref);
+
+ /*
+ * Copy the network bits over.
+ */
+ str = (char *)pref;
+ net_str = (const char *)net_start_pref;
+ net_bytes = pool_bits / 8;
+ for (i = 0; i < net_bytes; i++) {
+ str[i] = net_str[i];
+ }
+ i = net_bytes;
+ switch (pool_bits % 8) {
+ case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
+ case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
+ case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
+ case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
+ case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
+ case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
+ case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
+ }
+ /*
+ * Zero the remaining bits.
+ */
+ net_bytes = pref_bits / 8;
+ for (i=net_bytes+1; i<16; i++) {
+ str[i] = 0;
+ }
+ i = net_bytes;
+ switch (pref_bits % 8) {
+ case 0: str[i] &= 0; break;
+ case 1: str[i] &= 0x80; break;
+ case 2: str[i] &= 0xC0; break;
+ case 3: str[i] &= 0xE0; break;
+ case 4: str[i] &= 0xF0; break;
+ case 5: str[i] &= 0xF8; break;
+ case 6: str[i] &= 0xFC; break;
+ case 7: str[i] &= 0xFE; break;
+ }
+}
+
+/*
+ * Create a lease for the given prefix and client duid.
+ *
+ * - pool must be a pointer to a (struct pool *) pointer previously
+ * initialized to NULL
+ *
+ * Right now we simply hash the DUID, and if we get a collision, we hash
+ * again until we find a free prefix. We try this a fixed number of times,
+ * to avoid getting stuck in a loop (this is important on small pools
+ * where we can run out of space).
+ *
+ * We return the number of attempts that it took to find an available
+ * prefix. This tells callers when a pool is are filling up, as
+ * well as an indication of how full the pool is; statistically the
+ * more full a pool is the more attempts must be made before finding
+ * a free prefix. Realistically this will only happen in very full
+ * pools.
+ *
+ * We probably want different algorithms depending on the network size, in
+ * the long term.
+ */
+isc_result_t
+create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref,
+ unsigned int *attempts,
+ const struct data_string *uid,
+ time_t soft_lifetime_end_time) {
+ struct data_string ds;
+ struct in6_addr tmp;
+ struct iasubopt *test_iapref;
+ struct data_string new_ds;
+ struct iasubopt *iapref;
+ isc_result_t result;
+
+ /*
+ * Use the UID as our initial seed for the hash
+ */
+ memset(&ds, 0, sizeof(ds));
+ data_string_copy(&ds, (struct data_string *)uid, MDL);
+
+ *attempts = 0;
+ for (;;) {
+ /*
+ * Give up at some point.
+ */
+ if (++(*attempts) > 10) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NORESOURCES;
+ }
+
+ /*
+ * Build a prefix
+ */
+ build_prefix6(&tmp, &pool->start_addr,
+ pool->bits, pool->units, &ds);
+
+ /*
+ * If this prefix is not in use, we're happy with it
+ */
+ test_iapref = NULL;
+ if (iasubopt_hash_lookup(&test_iapref, pool->leases,
+ &tmp, sizeof(tmp), MDL) == 0) {
+ break;
+ }
+ iasubopt_dereference(&test_iapref, MDL);
+
+ /*
+ * Otherwise, we create a new input, adding the prefix
+ */
+ memset(&new_ds, 0, sizeof(new_ds));
+ new_ds.len = ds.len + sizeof(tmp);
+ if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
+ data_string_forget(&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ new_ds.data = new_ds.buffer->data;
+ memcpy(new_ds.buffer->data, ds.data, ds.len);
+ memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
+ data_string_forget(&ds, MDL);
+ data_string_copy(&ds, &new_ds, MDL);
+ data_string_forget(&new_ds, MDL);
+ }
+
+ data_string_forget(&ds, MDL);
+
+ /*
+ * We're happy with the prefix, create an IAPREFIX
+ * to hold it.
+ */
+ iapref = NULL;
+ result = iasubopt_allocate(&iapref, MDL);
+ if (result != ISC_R_SUCCESS) {
+ return result;
+ }
+ iapref->plen = (u_int8_t)pool->units;
+ memcpy(&iapref->addr, &tmp, sizeof(iapref->addr));
+
+ /*
+ * Add the prefix to the pool (note state is free, not active?!).
+ */
+ result = add_lease6(pool, iapref, soft_lifetime_end_time);
+ if (result == ISC_R_SUCCESS) {
+ iasubopt_reference(pref, iapref, MDL);
+ }
+ iasubopt_dereference(&iapref, MDL);
+ return result;
+}
+
+/*
+ * Determine if a prefix is present in a pool or not.
+ */
+isc_boolean_t
+prefix6_exists(const struct ipv6_pool *pool,
+ const struct in6_addr *pref, u_int8_t plen) {
+ struct iasubopt *test_iapref;
+
+ if ((int)plen != pool->units)
+ return ISC_FALSE;
+
+ test_iapref = NULL;
+ if (iasubopt_hash_lookup(&test_iapref, pool->leases,
+ (void *)pref, sizeof(*pref), MDL)) {
+ iasubopt_dereference(&test_iapref, MDL);
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Mark an IPv6 address/prefix as unavailable from a pool.
+ *
+ * This is used for host entries and the addresses of the server itself.
+ */
+isc_result_t
+mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
+ struct iasubopt *dummy_iasubopt;
+ isc_result_t result;
+
+ dummy_iasubopt = NULL;
+ result = iasubopt_allocate(&dummy_iasubopt, MDL);
+ if (result == ISC_R_SUCCESS) {
+ dummy_iasubopt->addr = *addr;
+ iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr,
+ sizeof(*addr), dummy_iasubopt, MDL);
+ }
+ return result;
+}
+
+/*
+ * Add a pool.
+ */
+isc_result_t
+add_ipv6_pool(struct ipv6_pool *pool) {
+ struct ipv6_pool **new_pools;
+
+ new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL);
+ if (new_pools == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ if (num_pools > 0) {
+ memcpy(new_pools, pools,
+ sizeof(struct ipv6_pool *) * num_pools);
+ dfree(pools, MDL);
+ }
+ pools = new_pools;
+
+ pools[num_pools] = NULL;
+ ipv6_pool_reference(&pools[num_pools], pool, MDL);
+ num_pools++;
+ return ISC_R_SUCCESS;
+}
+
+static void
+cleanup_old_expired(struct ipv6_pool *pool) {
+ struct iasubopt *tmp;
+ struct ia_xx *ia;
+ struct ia_xx *ia_active;
+ unsigned char *tmpd;
+ time_t timeout;
+
+ while (pool->num_inactive > 0) {
+ tmp = (struct iasubopt *)
+ isc_heap_element(pool->inactive_timeouts, 1);
+ if (tmp->hard_lifetime_end_time != 0) {
+ timeout = tmp->hard_lifetime_end_time;
+ timeout += EXPIRED_IPV6_CLEANUP_TIME;
+ } else {
+ timeout = tmp->soft_lifetime_end_time;
+ }
+ if (cur_time < timeout) {
+ break;
+ }
+
+ isc_heap_delete(pool->inactive_timeouts, tmp->heap_index);
+ pool->num_inactive--;
+
+ if (tmp->ia != NULL) {
+ /*
+ * Check to see if this IA is in an active list,
+ * but has no remaining resources. If so, remove it
+ * from the active list.
+ */
+ ia = NULL;
+ ia_reference(&ia, tmp->ia, MDL);
+ ia_remove_iasubopt(ia, tmp, MDL);
+ ia_active = NULL;
+ tmpd = (unsigned char *)ia->iaid_duid.data;
+ if ((ia->ia_type == D6O_IA_NA) &&
+ (ia->num_iasubopt <= 0) &&
+ (ia_hash_lookup(&ia_active, ia_na_active, tmpd,
+ ia->iaid_duid.len, MDL) == 0) &&
+ (ia_active == ia)) {
+ ia_hash_delete(ia_na_active, tmpd,
+ ia->iaid_duid.len, MDL);
+ }
+ if ((ia->ia_type == D6O_IA_TA) &&
+ (ia->num_iasubopt <= 0) &&
+ (ia_hash_lookup(&ia_active, ia_ta_active, tmpd,
+ ia->iaid_duid.len, MDL) == 0) &&
+ (ia_active == ia)) {
+ ia_hash_delete(ia_ta_active, tmpd,
+ ia->iaid_duid.len, MDL);
+ }
+ if ((ia->ia_type == D6O_IA_PD) &&
+ (ia->num_iasubopt <= 0) &&
+ (ia_hash_lookup(&ia_active, ia_pd_active, tmpd,
+ ia->iaid_duid.len, MDL) == 0) &&
+ (ia_active == ia)) {
+ ia_hash_delete(ia_pd_active, tmpd,
+ ia->iaid_duid.len, MDL);
+ }
+ ia_dereference(&ia, MDL);
+ }
+ iasubopt_dereference(&tmp, MDL);
+ }
+}
+
+static void
+lease_timeout_support(void *vpool) {
+ struct ipv6_pool *pool;
+ struct iasubopt *lease;
+
+ pool = (struct ipv6_pool *)vpool;
+ for (;;) {
+ /*
+ * Get the next lease scheduled to expire.
+ *
+ * Note that if there are no leases in the pool,
+ * expire_lease6() will return ISC_R_SUCCESS with
+ * a NULL lease.
+ */
+ lease = NULL;
+ if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) {
+ break;
+ }
+ if (lease == NULL) {
+ break;
+ }
+
+ /* Look to see if there were ddns updates, and if
+ * so, drop them.
+ *
+ * DH: Do we want to do this on a special 'depref'
+ * timer rather than expiration timer?
+ */
+#if defined (NSUPDATE)
+ if (pool->pool_type != D6O_IA_PD) {
+ ddns_removals(NULL, lease, NULL);
+ }
+#endif
+
+ write_ia(lease->ia);
+
+ iasubopt_dereference(&lease, MDL);
+ }
+
+ /*
+ * Do some cleanup of our expired leases.
+ */
+ cleanup_old_expired(pool);
+
+ /*
+ * Schedule next round of expirations.
+ */
+ schedule_lease_timeout(pool);
+}
+
+/*
+ * For a given pool, add a timer that will remove the next
+ * lease to expire.
+ */
+void
+schedule_lease_timeout(struct ipv6_pool *pool) {
+ struct iasubopt *tmp;
+ time_t timeout;
+ time_t next_timeout;
+ struct timeval tv;
+
+ next_timeout = MAX_TIME;
+
+ if (pool->num_active > 0) {
+ tmp = (struct iasubopt *)
+ isc_heap_element(pool->active_timeouts, 1);
+ if (tmp->hard_lifetime_end_time < next_timeout) {
+ next_timeout = tmp->hard_lifetime_end_time + 1;
+ }
+ }
+
+ if (pool->num_inactive > 0) {
+ tmp = (struct iasubopt *)
+ isc_heap_element(pool->inactive_timeouts, 1);
+ if (tmp->hard_lifetime_end_time != 0) {
+ timeout = tmp->hard_lifetime_end_time;
+ timeout += EXPIRED_IPV6_CLEANUP_TIME;
+ } else {
+ timeout = tmp->soft_lifetime_end_time + 1;
+ }
+ if (timeout < next_timeout) {
+ next_timeout = timeout;
+ }
+ }
+
+ if (next_timeout < MAX_TIME) {
+ tv.tv_sec = next_timeout;
+ tv.tv_usec = 0;
+ add_timeout(&tv, lease_timeout_support, pool,
+ (tvref_t)ipv6_pool_reference,
+ (tvunref_t)ipv6_pool_dereference);
+ }
+}
+
+/*
+ * Schedule timeouts across all pools.
+ */
+void
+schedule_all_ipv6_lease_timeouts(void) {
+ int i;
+
+ for (i=0; i<num_pools; i++) {
+ schedule_lease_timeout(pools[i]);
+ }
+}
+
+/*
+ * Given an address and the length of the network mask, return
+ * only the network portion.
+ *
+ * Examples:
+ *
+ * "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
+ * "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
+ */
+static void
+ipv6_network_portion(struct in6_addr *result,
+ const struct in6_addr *addr, int bits) {
+ unsigned char *addrp;
+ int mask_bits;
+ int bytes;
+ int extra_bits;
+ int i;
+
+ static const unsigned char bitmasks[] = {
+ 0x00, 0xFE, 0xFC, 0xF8,
+ 0xF0, 0xE0, 0xC0, 0x80,
+ };
+
+ /*
+ * Sanity check our bits. ;)
+ */
+ if ((bits < 0) || (bits > 128)) {
+ log_fatal("ipv6_network_portion: bits %d not between 0 and 128",
+ bits);
+ }
+
+ /*
+ * Copy our address portion.
+ */
+ *result = *addr;
+ addrp = ((unsigned char *)result) + 15;
+
+ /*
+ * Zero out masked portion.
+ */
+ mask_bits = 128 - bits;
+ bytes = mask_bits / 8;
+ extra_bits = mask_bits % 8;
+
+ for (i=0; i<bytes; i++) {
+ *addrp = 0;
+ addrp--;
+ }
+ if (extra_bits) {
+ *addrp &= bitmasks[extra_bits];
+ }
+}
+
+/*
+ * Determine if the given address/prefix is in the pool.
+ */
+isc_boolean_t
+ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
+ struct in6_addr tmp;
+
+ ipv6_network_portion(&tmp, addr, pool->bits);
+ if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) {
+ return ISC_TRUE;
+ } else {
+ return ISC_FALSE;
+ }
+}
+
+/*
+ * Find the pool that contains the given address.
+ *
+ * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
+ * initialized to NULL
+ */
+isc_result_t
+find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
+ const struct in6_addr *addr) {
+ int i;
+
+ if (pool == NULL) {
+ log_error("%s(%d): NULL pointer reference", MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ if (*pool != NULL) {
+ log_error("%s(%d): non-NULL pointer", MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ for (i=0; i<num_pools; i++) {
+ if (pools[i]->pool_type != type)
+ continue;
+ if (ipv6_in_pool(addr, pools[i])) {
+ ipv6_pool_reference(pool, pools[i], MDL);
+ return ISC_R_SUCCESS;
+ }
+ }
+ return ISC_R_NOTFOUND;
+}
+
+/*
+ * Helper function for the various functions that act across all
+ * pools.
+ */
+static isc_result_t
+change_leases(struct ia_xx *ia,
+ isc_result_t (*change_func)(struct ipv6_pool *,
+ struct iasubopt *)) {
+ isc_result_t retval;
+ isc_result_t renew_retval;
+ struct ipv6_pool *pool;
+ struct in6_addr *addr;
+ int i;
+
+ retval = ISC_R_SUCCESS;
+ for (i=0; i<ia->num_iasubopt; i++) {
+ pool = NULL;
+ addr = &ia->iasubopt[i]->addr;
+ if (find_ipv6_pool(&pool, ia->ia_type,
+ addr) == ISC_R_SUCCESS) {
+ renew_retval = change_func(pool, ia->iasubopt[i]);
+ if (renew_retval != ISC_R_SUCCESS) {
+ retval = renew_retval;
+ }
+ }
+ /* XXXsk: should we warn if we don't find a pool? */
+ }
+ return retval;
+}
+
+/*
+ * Renew all leases in an IA from all pools.
+ *
+ * The new hard_lifetime_end_time should be updated for the addresses/prefixes.
+ *
+ * WARNING: lease times must only be extended, never reduced!!!
+ */
+isc_result_t
+renew_leases(struct ia_xx *ia) {
+ return change_leases(ia, renew_lease6);
+}
+
+/*
+ * Release all leases in an IA from all pools.
+ */
+isc_result_t
+release_leases(struct ia_xx *ia) {
+ return change_leases(ia, release_lease6);
+}
+
+/*
+ * Decline all leases in an IA from all pools.
+ */
+isc_result_t
+decline_leases(struct ia_xx *ia) {
+ return change_leases(ia, decline_lease6);
+}
+
+#ifdef DHCPv6
+/*
+ * Helper function to output leases.
+ */
+static int write_error;
+
+static isc_result_t
+write_ia_leases(const void *name, unsigned len, void *value) {
+ struct ia_xx *ia = (struct ia_xx *)value;
+
+ if (!write_error) {
+ if (!write_ia(ia)) {
+ write_error = 1;
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/*
+ * Write all DHCPv6 information.
+ */
+int
+write_leases6(void) {
+ write_error = 0;
+ write_server_duid();
+ ia_hash_foreach(ia_na_active, write_ia_leases);
+ if (write_error) {
+ return 0;
+ }
+ ia_hash_foreach(ia_ta_active, write_ia_leases);
+ if (write_error) {
+ return 0;
+ }
+ ia_hash_foreach(ia_pd_active, write_ia_leases);
+ if (write_error) {
+ return 0;
+ }
+ return 1;
+}
+#endif /* DHCPv6 */
+
+static isc_result_t
+mark_hosts_unavailable_support(const void *name, unsigned len, void *value) {
+ struct host_decl *h;
+ struct data_string fixed_addr;
+ struct in6_addr addr;
+ struct ipv6_pool *p;
+
+ h = (struct host_decl *)value;
+
+ /*
+ * If the host has no address, we don't need to mark anything.
+ */
+ if (h->fixed_addr == NULL) {
+ return ISC_R_SUCCESS;
+ }
+
+ /*
+ * Evaluate the fixed address.
+ */
+ memset(&fixed_addr, 0, sizeof(fixed_addr));
+ if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL,
+ &global_scope, h->fixed_addr, MDL)) {
+ log_error("mark_hosts_unavailable: "
+ "error evaluating host address.");
+ return ISC_R_SUCCESS;
+ }
+ if (fixed_addr.len != 16) {
+ log_error("mark_hosts_unavailable: "
+ "host address is not 128 bits.");
+ return ISC_R_SUCCESS;
+ }
+ memcpy(&addr, fixed_addr.data, 16);
+ data_string_forget(&fixed_addr, MDL);
+
+ /*
+ * Find the pool holding this host, and mark the address.
+ * (I suppose it is arguably valid to have a host that does not
+ * sit in any pool.)
+ */
+ p = NULL;
+ if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) {
+ mark_lease_unavailable(p, &addr);
+ ipv6_pool_dereference(&p, MDL);
+ }
+ if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) {
+ mark_lease_unavailable(p, &addr);
+ ipv6_pool_dereference(&p, MDL);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void
+mark_hosts_unavailable(void) {
+ hash_foreach(host_name_hash, mark_hosts_unavailable_support);
+}
+
+static isc_result_t
+mark_phosts_unavailable_support(const void *name, unsigned len, void *value) {
+ struct host_decl *h;
+ struct iaddrcidrnetlist *l;
+ struct in6_addr pref;
+ struct ipv6_pool *p;
+
+ h = (struct host_decl *)value;
+
+ /*
+ * If the host has no prefix, we don't need to mark anything.
+ */
+ if (h->fixed_prefix == NULL) {
+ return ISC_R_SUCCESS;
+ }
+
+ /*
+ * Get the fixed prefixes.
+ */
+ for (l = h->fixed_prefix; l != NULL; l = l->next) {
+ if (l->cidrnet.lo_addr.len != 16) {
+ continue;
+ }
+ memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16);
+
+ /*
+ * Find the pool holding this host, and mark the prefix.
+ * (I suppose it is arguably valid to have a host that does not
+ * sit in any pool.)
+ */
+ p = NULL;
+ if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) {
+ continue;
+ }
+ if (l->cidrnet.bits != p->units) {
+ ipv6_pool_dereference(&p, MDL);
+ continue;
+ }
+ mark_lease_unavailable(p, &pref);
+ ipv6_pool_dereference(&p, MDL);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+void
+mark_phosts_unavailable(void) {
+ hash_foreach(host_name_hash, mark_phosts_unavailable_support);
+}
+
+void
+mark_interfaces_unavailable(void) {
+ struct interface_info *ip;
+ int i;
+ struct ipv6_pool *p;
+
+ ip = interfaces;
+ while (ip != NULL) {
+ for (i=0; i<ip->v6address_count; i++) {
+ p = NULL;
+ if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i])
+ == ISC_R_SUCCESS) {
+ mark_lease_unavailable(p,
+ &ip->v6addresses[i]);
+ ipv6_pool_dereference(&p, MDL);
+ }
+ if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i])
+ == ISC_R_SUCCESS) {
+ mark_lease_unavailable(p,
+ &ip->v6addresses[i]);
+ ipv6_pool_dereference(&p, MDL);
+ }
+ }
+ ip = ip->next;
+ }
+}
+
+
+#ifdef UNIT_TEST
+#include <stdlib.h>
+
+int
+main(int argc, char *argv[]) {
+ struct iasubopt *iaaddr;
+ struct iasubopt *iaaddr_copy;
+ u_int32_t iaid;
+ struct ia_xx *ia_na;
+ struct ia_xx *ia_na_copy;
+ int i;
+ struct in6_addr addr;
+ struct ipv6_pool *pool;
+ struct ipv6_pool *pool_copy;
+ char addr_buf[INET6_ADDRSTRLEN];
+ char *uid;
+ struct data_string ds;
+ struct iasubopt *expired_iaaddr;
+ unsigned int attempts;
+
+ /*
+ * Test 0: Basic iaaddr manipulation.
+ */
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr->state != FTS_FREE) {
+ printf("ERROR: bad state %s:%d\n", MDL);
+ return 1;
+ }
+ if (iaaddr->heap_index != -1) {
+ printf("ERROR: bad heap_index %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = NULL;
+ if (iasubopt_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 1: Error iaaddr manipulation.
+ */
+ /* bogus allocate arguments */
+ if (iasubopt_allocate(NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = (struct iasubopt *)1;
+ if (iasubopt_allocate(&iaaddr, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus reference arguments */
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_reference(NULL, iaaddr, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = (struct iasubopt *)1;
+ if (iasubopt_reference(&iaaddr_copy, iaaddr,
+ MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr_copy = NULL;
+ if (iasubopt_reference(&iaaddr_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus dereference arguments */
+ if (iasubopt_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = NULL;
+ if (iasubopt_dereference(&iaaddr, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 2: Basic ia_na manipulation.
+ */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (memcmp(ia_na->iaid_duid.data, &iaid, sizeof(iaid)) != 0) {
+ printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
+ return 1;
+ }
+ if (memcmp(ia_na->iaid_duid.data+sizeof(iaid), "TestDUID", 8) != 0) {
+ printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_na->num_iasubopt != 0) {
+ printf("ERROR: bad num_iasubopt %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = NULL;
+ if (ia_reference(&ia_na_copy, ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_remove_iasubopt(ia_na, iaaddr, MDL);
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_dereference(&ia_na_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 3: lots of iaaddr in our ia_na
+ */
+
+ /* lots of iaaddr that we delete */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = ia_na->iasubopt[random() % ia_na->num_iasubopt];
+ ia_remove_iasubopt(ia_na, iaaddr, MDL);
+ }
+ if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* lots of iaaddr, let dereference cleanup */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=0; i<100; i++) {
+ iaaddr = NULL;
+ if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 4: Errors in ia_na.
+ */
+ /* bogus allocate arguments */
+ if (ia_allocate(NULL, 123, "", 0, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na = (struct ia_na *)1;
+ if (ia_allocate(&ia_na, 456, "", 0, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus reference arguments */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_reference(NULL, ia_na, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = (struct ia_na *)1;
+ if (ia_reference(&ia_na_copy, ia_na, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_na_copy = NULL;
+ if (ia_reference(&ia_na_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus dereference arguments */
+ if (ia_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* bogus remove */
+ iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ ia_remove_iasubopt(ia_na, NULL, MDL);
+ if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 5: Basic ipv6_pool manipulation.
+ */
+
+ /* allocate, reference */
+ inet_pton(AF_INET6, "1:2:3:4::", &addr);
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->bits != 64) {
+ printf("ERROR: bad bits %s:%d\n", MDL);
+ return 1;
+ }
+ inet_ntop(AF_INET6, &pool->start_addr, addr_buf, sizeof(addr_buf));
+ if (strcmp(inet_ntop(AF_INET6, &pool->start_addr, addr_buf,
+ sizeof(addr_buf)), "1:2:3:4::") != 0) {
+ printf("ERROR: bad start_addr %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = NULL;
+ if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* create_lease6, renew_lease6, expire_lease6 */
+ uid = "client0";
+ memset(&ds, 0, sizeof(ds));
+ ds.len = strlen(uid);
+ if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
+ printf("Out of memory\n");
+ return 1;
+ }
+ ds.data = ds.buffer->data;
+ memcpy((char *)ds.data, uid, ds.len);
+ if (create_lease6(pool, &iaaddr,
+ &attempts, &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_inactive != 1) {
+ printf("ERROR: bad num_inactive %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ expired_iaaddr = NULL;
+ if (expire_lease6(&expired_iaaddr, pool, 0) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr != NULL) {
+ printf("ERROR: should not have expired a lease %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr == NULL) {
+ printf("ERROR: should have expired a lease %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* release_lease6, decline_lease6 */
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (release_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: decline_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, 1) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (decline_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: decline_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != 1) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /* dereference */
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 6: Error ipv6_pool manipulation
+ */
+ if (ipv6_pool_allocate(NULL, 0, &addr,
+ 64, 128, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ pool = (struct ipv6_pool *)1;
+ if (ipv6_pool_allocate(&pool, 0, &addr,
+ 64, 128, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_reference(NULL, pool, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = (struct ipv6_pool *)1;
+ if (ipv6_pool_reference(&pool_copy, pool, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool_copy = NULL;
+ if (ipv6_pool_reference(&pool_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool_copy, MDL) != DHCP_R_INVALIDARG) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 7: order of expiration
+ */
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=10; i<100; i+=10) {
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, i) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (pool->num_active != (i / 10)) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (pool->num_active != 9) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ for (i=10; i<100; i+=10) {
+ if (expire_lease6(&expired_iaaddr,
+ pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr == NULL) {
+ printf("ERROR: should have expired a lease %s:%d\n",
+ MDL);
+ return 1;
+ }
+ if (pool->num_active != (9 - (i / 10))) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ if (expired_iaaddr->hard_lifetime_end_time != i) {
+ printf("ERROR: bad hard_lifetime_end_time %s:%d\n",
+ MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&expired_iaaddr, MDL) !=
+ ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ }
+ if (pool->num_active != 0) {
+ printf("ERROR: bad num_active %s:%d\n", MDL);
+ return 1;
+ }
+ expired_iaaddr = NULL;
+ if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
+ printf("ERROR: expire_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+
+ /*
+ * Test 8: small pool
+ */
+ pool = NULL;
+ addr.s6_addr[14] = 0x81;
+ if (ipv6_pool_allocate(&pool, 0, &addr, 127, 128, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, 42) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, 11) != ISC_R_SUCCESS) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
+ printf("ERROR: renew_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ if (create_lease6(pool, &iaaddr, &attempts,
+ &ds, 11) != ISC_R_NORESOURCES) {
+ printf("ERROR: create_lease6() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ addr.s6_addr[14] = 0;
+
+ /*
+ * Test 9: functions across all pools
+ */
+ pool = NULL;
+ if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
+ return 1;
+ }
+ if (add_ipv6_pool(pool) != ISC_R_SUCCESS) {
+ printf("ERROR: add_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ pool = NULL;
+ if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:4:ffff:ffff:ffff:ffff", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:5::", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+ inet_pton(AF_INET6, "1:2:3:3:ffff:ffff:ffff:ffff", &addr);
+ pool = NULL;
+ if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
+ printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
+ return 1;
+ }
+
+/* iaid = 666;
+ ia_na = NULL;
+ if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
+ printf("ERROR: ia_allocate() %s:%d\n", MDL);
+ return 1;
+ }*/
+
+ {
+ struct in6_addr r;
+ struct data_string ds;
+ u_char data[16];
+ char buf[64];
+ int i, j;
+
+ memset(&ds, 0, sizeof(ds));
+ memset(data, 0xaa, sizeof(data));
+ ds.len = 16;
+ ds.data = data;
+
+ inet_pton(AF_INET6, "3ffe:501:ffff:100::", &addr);
+ for (i = 32; i < 42; i++)
+ for (j = i + 1; j < 49; j++) {
+ memset(&r, 0, sizeof(r));
+ memset(buf, 0, 64);
+ build_prefix6(&r, &addr, i, j, &ds);
+ inet_ntop(AF_INET6, &r, buf, 64);
+ printf("%d,%d-> %s/%d\n", i, j, buf, j);
+ }
+ }
+
+ printf("SUCCESS: all tests passed (ignore any warning messages)\n");
+ return 0;
+}
+#endif
diff --git a/server/omapi.c b/server/omapi.c
new file mode 100644
index 0000000..bbddaf9
--- /dev/null
+++ b/server/omapi.c
@@ -0,0 +1,2584 @@
+/* omapi.c
+
+ OMAPI object interfaces for the DHCP server. */
+
+/*
+ * Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+/* Many, many thanks to Brian Murrell and BCtel for this code - BCtel
+ provided the funding that resulted in this code and the entire
+ OMAPI support library being written, and Brian helped brainstorm
+ and refine the requirements. To the extent that this code is
+ useful, you have Brian and BCtel to thank. Any limitations in the
+ code are a result of mistakes on my part. -- Ted Lemon */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+
+static isc_result_t class_lookup (omapi_object_t **,
+ omapi_object_t *, omapi_object_t *,
+ omapi_object_type_t *);
+
+omapi_object_type_t *dhcp_type_lease;
+omapi_object_type_t *dhcp_type_pool;
+omapi_object_type_t *dhcp_type_class;
+omapi_object_type_t *dhcp_type_subclass;
+omapi_object_type_t *dhcp_type_host;
+#if defined (FAILOVER_PROTOCOL)
+omapi_object_type_t *dhcp_type_failover_state;
+omapi_object_type_t *dhcp_type_failover_link;
+omapi_object_type_t *dhcp_type_failover_listener;
+#endif
+
+void dhcp_db_objects_setup ()
+{
+ isc_result_t status;
+
+ status = omapi_object_type_register (&dhcp_type_lease,
+ "lease",
+ dhcp_lease_set_value,
+ dhcp_lease_get_value,
+ dhcp_lease_destroy,
+ dhcp_lease_signal_handler,
+ dhcp_lease_stuff_values,
+ dhcp_lease_lookup,
+ dhcp_lease_create,
+ dhcp_lease_remove,
+#if defined (COMPACT_LEASES)
+ dhcp_lease_free,
+ dhcp_lease_get,
+#else
+ 0, 0,
+#endif
+ 0,
+ sizeof (struct lease),
+ 0, RC_LEASE);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register lease object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_class,
+ "class",
+ dhcp_class_set_value,
+ dhcp_class_get_value,
+ dhcp_class_destroy,
+ dhcp_class_signal_handler,
+ dhcp_class_stuff_values,
+ dhcp_class_lookup,
+ dhcp_class_create,
+ dhcp_class_remove, 0, 0, 0,
+ sizeof (struct class), 0,
+ RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register class object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_subclass,
+ "subclass",
+ dhcp_subclass_set_value,
+ dhcp_subclass_get_value,
+ dhcp_class_destroy,
+ dhcp_subclass_signal_handler,
+ dhcp_subclass_stuff_values,
+ dhcp_subclass_lookup,
+ dhcp_subclass_create,
+ dhcp_subclass_remove, 0, 0, 0,
+ sizeof (struct class), 0, RC_MISC);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register subclass object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_pool,
+ "pool",
+ dhcp_pool_set_value,
+ dhcp_pool_get_value,
+ dhcp_pool_destroy,
+ dhcp_pool_signal_handler,
+ dhcp_pool_stuff_values,
+ dhcp_pool_lookup,
+ dhcp_pool_create,
+ dhcp_pool_remove, 0, 0, 0,
+ sizeof (struct pool), 0, RC_MISC);
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register pool object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_host,
+ "host",
+ dhcp_host_set_value,
+ dhcp_host_get_value,
+ dhcp_host_destroy,
+ dhcp_host_signal_handler,
+ dhcp_host_stuff_values,
+ dhcp_host_lookup,
+ dhcp_host_create,
+ dhcp_host_remove, 0, 0, 0,
+ sizeof (struct host_decl),
+ 0, RC_MISC);
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register host object type: %s",
+ isc_result_totext (status));
+
+#if defined (FAILOVER_PROTOCOL)
+ status = omapi_object_type_register (&dhcp_type_failover_state,
+ "failover-state",
+ dhcp_failover_state_set_value,
+ dhcp_failover_state_get_value,
+ dhcp_failover_state_destroy,
+ dhcp_failover_state_signal,
+ dhcp_failover_state_stuff,
+ dhcp_failover_state_lookup,
+ dhcp_failover_state_create,
+ dhcp_failover_state_remove,
+ 0, 0, 0,
+ sizeof (dhcp_failover_state_t),
+ 0, RC_MISC);
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register failover state object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_failover_link,
+ "failover-link",
+ dhcp_failover_link_set_value,
+ dhcp_failover_link_get_value,
+ dhcp_failover_link_destroy,
+ dhcp_failover_link_signal,
+ dhcp_failover_link_stuff_values,
+ 0, 0, 0, 0, 0, 0,
+ sizeof (dhcp_failover_link_t), 0,
+ RC_MISC);
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register failover link object type: %s",
+ isc_result_totext (status));
+
+ status = omapi_object_type_register (&dhcp_type_failover_listener,
+ "failover-listener",
+ dhcp_failover_listener_set_value,
+ dhcp_failover_listener_get_value,
+ dhcp_failover_listener_destroy,
+ dhcp_failover_listener_signal,
+ dhcp_failover_listener_stuff,
+ 0, 0, 0, 0, 0, 0,
+ sizeof
+ (dhcp_failover_listener_t), 0,
+ RC_MISC);
+
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register failover listener object type: %s",
+ isc_result_totext (status));
+#endif /* FAILOVER_PROTOCOL */
+}
+
+isc_result_t dhcp_lease_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct lease *lease;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)h;
+
+ /* We're skipping a lot of things it might be interesting to
+ set - for now, we just make it possible to whack the state. */
+ if (!omapi_ds_strcmp (name, "state")) {
+ unsigned long bar;
+ const char *ols, *nls;
+ status = omapi_get_int_value (&bar, value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (bar < 1 || bar > FTS_LAST)
+ return DHCP_R_INVALIDARG;
+ nls = binding_state_names [bar - 1];
+ if (lease -> binding_state >= 1 &&
+ lease -> binding_state <= FTS_LAST)
+ ols = binding_state_names [lease -> binding_state - 1];
+ else
+ ols = "unknown state";
+
+ if (lease -> binding_state != bar) {
+ lease -> next_binding_state = bar;
+ if (supersede_lease (lease, 0, 1, 1, 1)) {
+ log_info ("lease %s state changed from %s to %s",
+ piaddr(lease->ip_addr), ols, nls);
+ return ISC_R_SUCCESS;
+ }
+ log_info ("lease %s state change from %s to %s failed.",
+ piaddr (lease -> ip_addr), ols, nls);
+ return ISC_R_IOERROR;
+ }
+ return DHCP_R_UNCHANGED;
+ } else if (!omapi_ds_strcmp (name, "ip-address")) {
+ return ISC_R_NOPERM;
+ } else if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (!omapi_ds_strcmp (name, "hostname")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (!omapi_ds_strcmp (name, "client-hostname")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (!omapi_ds_strcmp (name, "host")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (!omapi_ds_strcmp (name, "subnet")) {
+ return DHCP_R_INVALIDARG;
+ } else if (!omapi_ds_strcmp (name, "pool")) {
+ return ISC_R_NOPERM;
+ } else if (!omapi_ds_strcmp (name, "starts")) {
+ return ISC_R_NOPERM;
+ } else if (!omapi_ds_strcmp (name, "ends")) {
+ unsigned long lease_end, old_lease_end;
+ status = omapi_get_int_value (&lease_end, value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ old_lease_end = lease->ends;
+ lease->ends = lease_end;
+ if (supersede_lease (lease, 0, 1, 1, 1)) {
+ log_info ("lease %s end changed from %lu to %lu",
+ piaddr(lease->ip_addr), old_lease_end, lease_end);
+ return ISC_R_SUCCESS;
+ }
+ log_info ("lease %s end change from %lu to %lu failed",
+ piaddr(lease->ip_addr), old_lease_end, lease_end);
+ return ISC_R_IOERROR;
+ } else if (!omapi_ds_strcmp(name, "flags")) {
+ u_int8_t oldflags;
+
+ if (value->type != omapi_datatype_data)
+ return DHCP_R_INVALIDARG;
+
+ oldflags = lease->flags;
+ lease->flags = (value->u.buffer.value[0] & EPHEMERAL_FLAGS) |
+ (lease->flags & ~EPHEMERAL_FLAGS);
+ if(oldflags == lease->flags)
+ return ISC_R_SUCCESS;
+ if (!supersede_lease(lease, NULL, 1, 1, 1)) {
+ log_error("Failed to update flags for lease %s.",
+ piaddr(lease->ip_addr));
+ return ISC_R_IOERROR;
+ }
+ return ISC_R_SUCCESS;
+ } else if (!omapi_ds_strcmp (name, "billing-class")) {
+ return DHCP_R_UNCHANGED; /* XXX carefully allow change. */
+ } else if (!omapi_ds_strcmp (name, "hardware-address")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (!omapi_ds_strcmp (name, "hardware-type")) {
+ return DHCP_R_UNCHANGED; /* XXX take change. */
+ } else if (lease -> scope) {
+ status = binding_scope_set_value (lease -> scope, 0, name, value);
+ if (status == ISC_R_SUCCESS) {
+ if (write_lease (lease) && commit_leases ())
+ return ISC_R_SUCCESS;
+ return ISC_R_IOERROR;
+ }
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ if (!lease -> scope) {
+ if (!binding_scope_allocate (&lease -> scope, MDL))
+ return ISC_R_NOMEMORY;
+ }
+ status = binding_scope_set_value (lease -> scope, 1, name, value);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (write_lease (lease) && commit_leases ())
+ return ISC_R_SUCCESS;
+ return ISC_R_IOERROR;
+}
+
+
+isc_result_t dhcp_lease_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct lease *lease;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)h;
+
+ if (!omapi_ds_strcmp (name, "state"))
+ return omapi_make_int_value (value, name,
+ (int)lease -> binding_state, MDL);
+ else if (!omapi_ds_strcmp (name, "ip-address"))
+ return omapi_make_const_value (value, name,
+ lease -> ip_addr.iabuf,
+ lease -> ip_addr.len, MDL);
+ else if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) {
+ return omapi_make_const_value (value, name,
+ lease -> uid,
+ lease -> uid_len, MDL);
+ } else if (!omapi_ds_strcmp (name, "client-hostname")) {
+ if (lease -> client_hostname)
+ return omapi_make_string_value
+ (value, name, lease -> client_hostname, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "host")) {
+ if (lease -> host)
+ return omapi_make_handle_value
+ (value, name,
+ ((omapi_object_t *)lease -> host), MDL);
+ } else if (!omapi_ds_strcmp (name, "subnet"))
+ return omapi_make_handle_value (value, name,
+ ((omapi_object_t *)
+ lease -> subnet), MDL);
+ else if (!omapi_ds_strcmp (name, "pool"))
+ return omapi_make_handle_value (value, name,
+ ((omapi_object_t *)
+ lease -> pool), MDL);
+ else if (!omapi_ds_strcmp (name, "billing-class")) {
+ if (lease -> billing_class)
+ return omapi_make_handle_value
+ (value, name,
+ ((omapi_object_t *)lease -> billing_class),
+ MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "hardware-address")) {
+ if (lease -> hardware_addr.hlen)
+ return omapi_make_const_value
+ (value, name, &lease -> hardware_addr.hbuf [1],
+ (unsigned)(lease -> hardware_addr.hlen - 1),
+ MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!omapi_ds_strcmp (name, "hardware-type")) {
+ if (lease -> hardware_addr.hlen)
+ return omapi_make_int_value
+ (value, name, lease -> hardware_addr.hbuf [0],
+ MDL);
+ return ISC_R_NOTFOUND;
+ } else if (lease -> scope) {
+ status = binding_scope_get_value (value, lease -> scope, name);
+ if (status != ISC_R_NOTFOUND)
+ return status;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+isc_result_t dhcp_lease_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct lease *lease;
+
+ if (h -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)h;
+
+ if (lease -> uid)
+ uid_hash_delete (lease);
+ hw_hash_delete (lease);
+
+ if (lease -> on_release)
+ executable_statement_dereference (&lease -> on_release,
+ file, line);
+ if (lease -> on_expiry)
+ executable_statement_dereference (&lease -> on_expiry,
+ file, line);
+ if (lease -> on_commit)
+ executable_statement_dereference (&lease -> on_commit,
+ file, line);
+ if (lease -> scope)
+ binding_scope_dereference (&lease -> scope, file, line);
+
+ if (lease -> agent_options)
+ option_chain_head_dereference (&lease -> agent_options,
+ file, line);
+ if (lease -> uid && lease -> uid != lease -> uid_buf) {
+ dfree (lease -> uid, MDL);
+ lease -> uid = &lease -> uid_buf [0];
+ lease -> uid_len = 0;
+ }
+
+ if (lease -> client_hostname) {
+ dfree (lease -> client_hostname, MDL);
+ lease -> client_hostname = (char *)0;
+ }
+
+ if (lease -> host)
+ host_dereference (&lease -> host, file, line);
+ if (lease -> subnet)
+ subnet_dereference (&lease -> subnet, file, line);
+ if (lease -> pool)
+ pool_dereference (&lease -> pool, file, line);
+
+ if (lease -> state) {
+ free_lease_state (lease -> state, file, line);
+ lease -> state = (struct lease_state *)0;
+
+ cancel_timeout (lease_ping_timeout, lease);
+ --outstanding_pings; /* XXX */
+ }
+
+ if (lease -> billing_class)
+ class_dereference
+ (&lease -> billing_class, file, line);
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ /* XXX we should never be destroying a lease with a next
+ XXX pointer except on exit... */
+ if (lease -> next)
+ lease_dereference (&lease -> next, file, line);
+ if (lease -> n_hw)
+ lease_dereference (&lease -> n_hw, file, line);
+ if (lease -> n_uid)
+ lease_dereference (&lease -> n_uid, file, line);
+ if (lease -> next_pending)
+ lease_dereference (&lease -> next_pending, file, line);
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_lease_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct lease *lease;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)h;
+
+ if (!strcmp (name, "updated"))
+ return ISC_R_SUCCESS;
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> signal_handler) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_lease_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ u_int32_t bouncer;
+ struct lease *lease;
+ isc_result_t status;
+ u_int8_t flagbuf;
+
+ if (h -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)h;
+
+ /* Write out all the values. */
+
+ status = omapi_connection_put_name (c, "state");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (int));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, lease -> binding_state);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "ip-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, lease -> ip_addr.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c, lease -> ip_addr.iabuf,
+ lease -> ip_addr.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (lease -> uid_len) {
+ status = omapi_connection_put_name (c,
+ "dhcp-client-identifier");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, lease -> uid_len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (lease -> uid_len) {
+ status = omapi_connection_copyin (c, lease -> uid,
+ lease -> uid_len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ }
+
+ if (lease -> client_hostname) {
+ status = omapi_connection_put_name (c, "client-hostname");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status =
+ omapi_connection_put_string (c,
+ lease -> client_hostname);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (lease -> host) {
+ status = omapi_connection_put_name (c, "host");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_handle (c,
+ (omapi_object_t *)
+ lease -> host);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ status = omapi_connection_put_name (c, "subnet");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_handle
+ (c, (omapi_object_t *)lease -> subnet);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "pool");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_handle (c,
+ (omapi_object_t *)lease -> pool);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (lease -> billing_class) {
+ status = omapi_connection_put_name (c, "billing-class");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_handle
+ (c, (omapi_object_t *)lease -> billing_class);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (lease -> hardware_addr.hlen) {
+ status = omapi_connection_put_name (c, "hardware-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c,
+ (unsigned long)(lease -> hardware_addr.hlen - 1)));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_copyin
+ (c, &lease -> hardware_addr.hbuf [1],
+ (unsigned long)(lease -> hardware_addr.hlen - 1)));
+
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "hardware-type");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (int));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32
+ (c, lease -> hardware_addr.hbuf [0]);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* TIME values may be 64-bit, depending on system architecture.
+ * OMAPI must be system independent, both in terms of transmitting
+ * bytes on the wire in network byte order, and in terms of being
+ * readable and usable by both systems.
+ *
+ * XXX: In a future feature release, a put_int64() should be made
+ * to exist, and perhaps a put_time() wrapper that selects which
+ * to use based upon sizeof(TIME). In the meantime, use existing,
+ * 32-bit, code.
+ */
+ bouncer = (u_int32_t)lease->ends;
+ status = omapi_connection_put_name(c, "ends");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ bouncer = (u_int32_t)lease->starts;
+ status = omapi_connection_put_name(c, "starts");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ bouncer = (u_int32_t)lease->tstp;
+ status = omapi_connection_put_name(c, "tstp");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ bouncer = (u_int32_t)lease->tsfp;
+ status = omapi_connection_put_name(c, "tsfp");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ bouncer = (u_int32_t)lease->atsfp;
+ status = omapi_connection_put_name(c, "atsfp");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ bouncer = (u_int32_t)lease->cltt;
+ status = omapi_connection_put_name(c, "cltt");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(bouncer));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, bouncer);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "flags");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32(c, sizeof(flagbuf));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ flagbuf = lease->flags & EPHEMERAL_FLAGS;
+ status = omapi_connection_copyin(c, &flagbuf, sizeof(flagbuf));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ if (lease -> scope) {
+ status = binding_scope_stuff_values (c, lease -> scope);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_lease_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct lease *lease;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ /* First see if we were sent a handle. */
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (lp, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*lp) -> type != dhcp_type_lease) {
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ }
+
+ /* Now look for an IP address. */
+ status = omapi_get_value_str (ref, id, "ip-address", &tv);
+ if (status == ISC_R_SUCCESS) {
+ lease = (struct lease *)0;
+ lease_ip_hash_lookup(&lease, lease_ip_addr_hash,
+ tv->value->u.buffer.value,
+ tv->value->u.buffer.len, MDL);
+
+ omapi_value_dereference (&tv, MDL);
+
+ /* If we already have a lease, and it's not the same one,
+ then the query was invalid. */
+ if (*lp && *lp != (omapi_object_t *)lease) {
+ omapi_object_dereference (lp, MDL);
+ lease_dereference (&lease, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!lease) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)lease, MDL);
+ lease_dereference (&lease, MDL);
+ }
+ }
+
+ /* Now look for a client identifier. */
+ status = omapi_get_value_str (ref, id, "dhcp-client-identifier", &tv);
+ if (status == ISC_R_SUCCESS) {
+ lease = (struct lease *)0;
+ lease_id_hash_lookup(&lease, lease_uid_hash,
+ tv->value->u.buffer.value,
+ tv->value->u.buffer.len, MDL);
+ omapi_value_dereference (&tv, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)lease) {
+ omapi_object_dereference (lp, MDL);
+ lease_dereference (&lease, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!lease) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (lease -> n_uid) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_MULTIPLE;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)lease, MDL);
+ lease_dereference (&lease, MDL);
+ }
+ }
+
+ /* Now look for a hardware address. */
+ status = omapi_get_value_str (ref, id, "hardware-address", &tv);
+ if (status == ISC_R_SUCCESS) {
+ unsigned char *haddr;
+ unsigned int len;
+
+ len = tv -> value -> u.buffer.len + 1;
+ haddr = dmalloc (len, MDL);
+ if (!haddr) {
+ omapi_value_dereference (&tv, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ memcpy (haddr + 1, tv -> value -> u.buffer.value, len - 1);
+ omapi_value_dereference (&tv, MDL);
+
+ status = omapi_get_value_str (ref, id, "hardware-type", &tv);
+ if (status == ISC_R_SUCCESS) {
+ if (tv -> value -> type == omapi_datatype_data) {
+ if ((tv -> value -> u.buffer.len != 4) ||
+ (tv -> value -> u.buffer.value[0] != 0) ||
+ (tv -> value -> u.buffer.value[1] != 0) ||
+ (tv -> value -> u.buffer.value[2] != 0)) {
+ omapi_value_dereference (&tv, MDL);
+ dfree (haddr, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ haddr[0] = tv -> value -> u.buffer.value[3];
+ } else if (tv -> value -> type == omapi_datatype_int) {
+ haddr[0] = (unsigned char)
+ tv -> value -> u.integer;
+ } else {
+ omapi_value_dereference (&tv, MDL);
+ dfree (haddr, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ omapi_value_dereference (&tv, MDL);
+ } else {
+ /* If no hardware-type is specified, default to
+ ethernet. This may or may not be a good idea,
+ but Telus is currently relying on this behavior.
+ - DPN */
+ haddr[0] = HTYPE_ETHER;
+ }
+
+ lease = (struct lease *)0;
+ lease_id_hash_lookup(&lease, lease_hw_addr_hash, haddr, len,
+ MDL);
+ dfree (haddr, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)lease) {
+ omapi_object_dereference (lp, MDL);
+ lease_dereference (&lease, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!lease) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (lease -> n_hw) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ lease_dereference (&lease, MDL);
+ return DHCP_R_MULTIPLE;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)lease, MDL);
+ lease_dereference (&lease, MDL);
+ }
+ }
+
+ /* If we get to here without finding a lease, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_lease_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_lease_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_host_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct host_decl *host;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ host = (struct host_decl *)h;
+
+ /* XXX For now, we can only set these values on new host objects.
+ XXX Soon, we need to be able to update host objects. */
+ if (!omapi_ds_strcmp (name, "name")) {
+ if (host -> name)
+ return ISC_R_EXISTS;
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ host -> name = dmalloc (value -> u.buffer.len + 1,
+ MDL);
+ if (!host -> name)
+ return ISC_R_NOMEMORY;
+ memcpy (host -> name,
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ host -> name [value -> u.buffer.len] = 0;
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "group")) {
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ struct group_object *group;
+ group = (struct group_object *)0;
+ group_hash_lookup (&group, group_name_hash,
+ (char *)value -> u.buffer.value,
+ value -> u.buffer.len, MDL);
+ if (!group || (group -> flags & GROUP_OBJECT_DELETED))
+ return ISC_R_NOTFOUND;
+ if (host -> group)
+ group_dereference (&host -> group, MDL);
+ group_reference (&host -> group, group -> group, MDL);
+ if (host -> named_group)
+ group_object_dereference (&host -> named_group,
+ MDL);
+ group_object_reference (&host -> named_group,
+ group, MDL);
+ group_object_dereference (&group, MDL);
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "hardware-address")) {
+ if (host -> interface.hlen)
+ return ISC_R_EXISTS;
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ if (value -> u.buffer.len >
+ (sizeof host -> interface.hbuf) - 1)
+ return DHCP_R_INVALIDARG;
+ memcpy (&host -> interface.hbuf [1],
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ host -> interface.hlen = value -> u.buffer.len + 1;
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "hardware-type")) {
+ int type;
+ if (value && (value -> type == omapi_datatype_data &&
+ value -> u.buffer.len == sizeof type)) {
+ if (value -> u.buffer.len > sizeof type)
+ return DHCP_R_INVALIDARG;
+ memcpy (&type,
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ type = ntohl (type);
+ } else if (value -> type == omapi_datatype_int)
+ type = value -> u.integer;
+ else
+ return DHCP_R_INVALIDARG;
+ host -> interface.hbuf [0] = type;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) {
+ if (host -> client_identifier.data)
+ return ISC_R_EXISTS;
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ if (!buffer_allocate (&host -> client_identifier.buffer,
+ value -> u.buffer.len, MDL))
+ return ISC_R_NOMEMORY;
+ host -> client_identifier.data =
+ &host -> client_identifier.buffer -> data [0];
+ memcpy (host -> client_identifier.buffer -> data,
+ value -> u.buffer.value,
+ value -> u.buffer.len);
+ host -> client_identifier.len = value -> u.buffer.len;
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "ip-address")) {
+ if (host -> fixed_addr)
+ option_cache_dereference (&host -> fixed_addr, MDL);
+ if (!value)
+ return ISC_R_SUCCESS;
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ struct data_string ds;
+ memset (&ds, 0, sizeof ds);
+ ds.len = value -> u.buffer.len;
+ if (!buffer_allocate (&ds.buffer, ds.len, MDL))
+ return ISC_R_NOMEMORY;
+ ds.data = (&ds.buffer -> data [0]);
+ memcpy (ds.buffer -> data,
+ value -> u.buffer.value, ds.len);
+ if (!option_cache (&host -> fixed_addr,
+ &ds, (struct expression *)0,
+ (struct option *)0, MDL)) {
+ data_string_forget (&ds, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ data_string_forget (&ds, MDL);
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp (name, "statements")) {
+ if (!host -> group) {
+ if (!clone_group (&host -> group, root_group, MDL))
+ return ISC_R_NOMEMORY;
+ } else {
+ if (host -> group -> statements &&
+ (!host -> named_group ||
+ host -> group != host -> named_group -> group) &&
+ host -> group != root_group)
+ return ISC_R_EXISTS;
+ if (!clone_group (&host -> group, host -> group, MDL))
+ return ISC_R_NOMEMORY;
+ }
+ if (!host -> group)
+ return ISC_R_NOMEMORY;
+ if (value && (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string)) {
+ struct parse *parse;
+ int lose = 0;
+ parse = (struct parse *)0;
+ status = new_parse(&parse, -1,
+ (char *) value->u.buffer.value,
+ value->u.buffer.len,
+ "network client", 0);
+ if (status != ISC_R_SUCCESS || parse == NULL)
+ return status;
+
+ if (!(parse_executable_statements
+ (&host -> group -> statements, parse, &lose,
+ context_any))) {
+ end_parse (&parse);
+ return DHCP_R_BADPARSE;
+ }
+ end_parse (&parse);
+ } else
+ return DHCP_R_INVALIDARG;
+ return ISC_R_SUCCESS;
+ }
+
+ /* The "known" flag isn't supported in the database yet, but it's
+ legitimate. */
+ if (!omapi_ds_strcmp (name, "known")) {
+ return ISC_R_SUCCESS;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+
+isc_result_t dhcp_host_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct host_decl *host;
+ isc_result_t status;
+ struct data_string ip_addrs;
+
+ if (h -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ host = (struct host_decl *)h;
+
+ if (!omapi_ds_strcmp (name, "ip-addresses")) {
+ memset (&ip_addrs, 0, sizeof ip_addrs);
+ if (host -> fixed_addr &&
+ evaluate_option_cache (&ip_addrs, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ host -> fixed_addr, MDL)) {
+ status = omapi_make_const_value (value, name,
+ ip_addrs.data,
+ ip_addrs.len, MDL);
+ data_string_forget (&ip_addrs, MDL);
+ return status;
+ }
+ return ISC_R_NOTFOUND;
+ }
+
+ if (!omapi_ds_strcmp (name, "dhcp-client-identifier")) {
+ if (!host -> client_identifier.len)
+ return ISC_R_NOTFOUND;
+ return omapi_make_const_value (value, name,
+ host -> client_identifier.data,
+ host -> client_identifier.len,
+ MDL);
+ }
+
+ if (!omapi_ds_strcmp (name, "name"))
+ return omapi_make_string_value (value, name, host -> name,
+ MDL);
+
+ if (!omapi_ds_strcmp (name, "hardware-address")) {
+ if (!host -> interface.hlen)
+ return ISC_R_NOTFOUND;
+ return (omapi_make_const_value
+ (value, name, &host -> interface.hbuf [1],
+ (unsigned long)(host -> interface.hlen - 1), MDL));
+ }
+
+ if (!omapi_ds_strcmp (name, "hardware-type")) {
+ if (!host -> interface.hlen)
+ return ISC_R_NOTFOUND;
+ return omapi_make_int_value (value, name,
+ host -> interface.hbuf [0], MDL);
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+isc_result_t dhcp_host_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct host_decl *host;
+
+ if (h -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ host = (struct host_decl *)h;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ if (host -> n_ipaddr)
+ host_dereference (&host -> n_ipaddr, file, line);
+ if (host -> n_dynamic)
+ host_dereference (&host -> n_dynamic, file, line);
+ if (host -> name) {
+ dfree (host -> name, file, line);
+ host -> name = (char *)0;
+ }
+ data_string_forget (&host -> client_identifier, file, line);
+ if (host -> fixed_addr)
+ option_cache_dereference (&host -> fixed_addr, file, line);
+ if (host -> group)
+ group_dereference (&host -> group, file, line);
+ if (host -> named_group)
+ omapi_object_dereference ((omapi_object_t **)
+ &host -> named_group, file, line);
+ data_string_forget (&host -> auth_key_id, file, line);
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_host_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct host_decl *host;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ host = (struct host_decl *)h;
+
+ if (!strcmp (name, "updated")) {
+ /* There must be a client identifier of some sort. */
+ if (host -> interface.hlen == 0 &&
+ !host -> client_identifier.len)
+ return DHCP_R_INVALIDARG;
+
+ if (!host -> name) {
+ char hnbuf [64];
+ sprintf (hnbuf, "nh%08lx%08lx",
+ (unsigned long)cur_time, (unsigned long)host);
+ host -> name = dmalloc (strlen (hnbuf) + 1, MDL);
+ if (!host -> name)
+ return ISC_R_NOMEMORY;
+ strcpy (host -> name, hnbuf);
+ }
+
+#ifdef DEBUG_OMAPI
+ log_debug ("OMAPI added host %s", host -> name);
+#endif
+ status = enter_host (host, 1, 1);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ updatep = 1;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> signal_handler) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ if (updatep)
+ return ISC_R_SUCCESS;
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_host_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct host_decl *host;
+ isc_result_t status;
+ struct data_string ip_addrs;
+
+ if (h -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ host = (struct host_decl *)h;
+
+ /* Write out all the values. */
+
+ memset (&ip_addrs, 0, sizeof ip_addrs);
+ if (host -> fixed_addr &&
+ evaluate_option_cache (&ip_addrs, (struct packet *)0,
+ (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ (struct option_state *)0,
+ &global_scope,
+ host -> fixed_addr, MDL)) {
+ status = omapi_connection_put_name (c, "ip-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, ip_addrs.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c,
+ ip_addrs.data, ip_addrs.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (host -> client_identifier.len) {
+ status = omapi_connection_put_name (c,
+ "dhcp-client-identifier");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, host -> client_identifier.len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_copyin
+ (c,
+ host -> client_identifier.data,
+ host -> client_identifier.len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (host -> name) {
+ status = omapi_connection_put_name (c, "name");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_string (c, host -> name);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (host -> interface.hlen) {
+ status = omapi_connection_put_name (c, "hardware-address");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, (unsigned long)(host -> interface.hlen - 1)));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_copyin
+ (c, &host -> interface.hbuf [1],
+ (unsigned long)(host -> interface.hlen - 1)));
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_connection_put_name (c, "hardware-type");
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_put_uint32 (c, sizeof (int));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, host -> interface.hbuf [0]));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_host_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct host_decl *host;
+
+ if (!ref)
+ return DHCP_R_NOKEYS;
+
+ /* First see if we were sent a handle. */
+ status = omapi_get_value_str (ref, id, "handle", &tv);
+ if (status == ISC_R_SUCCESS) {
+ status = omapi_handle_td_lookup (lp, tv -> value);
+
+ omapi_value_dereference (&tv, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ /* Don't return the object if the type is wrong. */
+ if ((*lp) -> type != dhcp_type_host) {
+ omapi_object_dereference (lp, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+ if (((struct host_decl *)(*lp)) -> flags & HOST_DECL_DELETED) {
+ omapi_object_dereference (lp, MDL);
+ }
+ }
+
+ /* Now look for a client identifier. */
+ status = omapi_get_value_str (ref, id, "dhcp-client-identifier", &tv);
+ if (status == ISC_R_SUCCESS) {
+ host = (struct host_decl *)0;
+ host_hash_lookup (&host, host_uid_hash,
+ tv -> value -> u.buffer.value,
+ tv -> value -> u.buffer.len, MDL);
+ omapi_value_dereference (&tv, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)host) {
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!host || (host -> flags & HOST_DECL_DELETED)) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)host, MDL);
+ host_dereference (&host, MDL);
+ }
+ }
+
+ /* Now look for a hardware address. */
+ status = omapi_get_value_str (ref, id, "hardware-address", &tv);
+ if (status == ISC_R_SUCCESS) {
+ unsigned char *haddr;
+ unsigned int len;
+
+ len = tv -> value -> u.buffer.len + 1;
+ haddr = dmalloc (len, MDL);
+ if (!haddr) {
+ omapi_value_dereference (&tv, MDL);
+ return ISC_R_NOMEMORY;
+ }
+
+ memcpy (haddr + 1, tv -> value -> u.buffer.value, len - 1);
+ omapi_value_dereference (&tv, MDL);
+
+ status = omapi_get_value_str (ref, id, "hardware-type", &tv);
+ if (status == ISC_R_SUCCESS) {
+ if (tv -> value -> type == omapi_datatype_data) {
+ if ((tv -> value -> u.buffer.len != 4) ||
+ (tv -> value -> u.buffer.value[0] != 0) ||
+ (tv -> value -> u.buffer.value[1] != 0) ||
+ (tv -> value -> u.buffer.value[2] != 0)) {
+ omapi_value_dereference (&tv, MDL);
+ dfree (haddr, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ haddr[0] = tv -> value -> u.buffer.value[3];
+ } else if (tv -> value -> type == omapi_datatype_int) {
+ haddr[0] = (unsigned char)
+ tv -> value -> u.integer;
+ } else {
+ omapi_value_dereference (&tv, MDL);
+ dfree (haddr, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ omapi_value_dereference (&tv, MDL);
+ } else {
+ /* If no hardware-type is specified, default to
+ ethernet. This may or may not be a good idea,
+ but Telus is currently relying on this behavior.
+ - DPN */
+ haddr[0] = HTYPE_ETHER;
+ }
+
+ host = (struct host_decl *)0;
+ host_hash_lookup (&host, host_hw_addr_hash, haddr, len, MDL);
+ dfree (haddr, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)host) {
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!host || (host -> flags & HOST_DECL_DELETED)) {
+ if (*lp)
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)host, MDL);
+ host_dereference (&host, MDL);
+ }
+ }
+
+ /* Now look for an ip address. */
+ status = omapi_get_value_str (ref, id, "ip-address", &tv);
+ if (status == ISC_R_SUCCESS) {
+ struct lease *l;
+
+ /* first find the lease for this ip address */
+ l = (struct lease *)0;
+ lease_ip_hash_lookup(&l, lease_ip_addr_hash,
+ tv->value->u.buffer.value,
+ tv->value->u.buffer.len, MDL);
+ omapi_value_dereference (&tv, MDL);
+
+ if (!l && !*lp)
+ return ISC_R_NOTFOUND;
+
+ if (l) {
+ /* now use that to get a host */
+ host = (struct host_decl *)0;
+ host_hash_lookup (&host, host_hw_addr_hash,
+ l -> hardware_addr.hbuf,
+ l -> hardware_addr.hlen, MDL);
+
+ if (host && *lp && *lp != (omapi_object_t *)host) {
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!host || (host -> flags &
+ HOST_DECL_DELETED)) {
+ if (host)
+ host_dereference (&host, MDL);
+ if (!*lp)
+ return ISC_R_NOTFOUND;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp, (omapi_object_t *)host,
+ MDL);
+ host_dereference (&host, MDL);
+ }
+ lease_dereference (&l, MDL);
+ }
+ }
+
+ /* Now look for a name. */
+ status = omapi_get_value_str (ref, id, "name", &tv);
+ if (status == ISC_R_SUCCESS) {
+ host = (struct host_decl *)0;
+ host_hash_lookup (&host, host_name_hash,
+ tv -> value -> u.buffer.value,
+ tv -> value -> u.buffer.len, MDL);
+ omapi_value_dereference (&tv, MDL);
+
+ if (*lp && *lp != (omapi_object_t *)host) {
+ omapi_object_dereference (lp, MDL);
+ if (host)
+ host_dereference (&host, MDL);
+ return DHCP_R_KEYCONFLICT;
+ } else if (!host || (host -> flags & HOST_DECL_DELETED)) {
+ if (host)
+ host_dereference (&host, MDL);
+ return ISC_R_NOTFOUND;
+ } else if (!*lp) {
+ /* XXX fix so that hash lookup itself creates
+ XXX the reference. */
+ omapi_object_reference (lp,
+ (omapi_object_t *)host, MDL);
+ host_dereference (&host, MDL);
+ }
+ }
+
+ /* If we get to here without finding a host, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_host_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ struct host_decl *hp;
+ isc_result_t status;
+ hp = (struct host_decl *)0;
+ status = host_allocate (&hp, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ group_reference (&hp -> group, root_group, MDL);
+ hp -> flags = HOST_DECL_DYNAMIC;
+ status = omapi_object_reference (lp, (omapi_object_t *)hp, MDL);
+ host_dereference (&hp, MDL);
+ return status;
+}
+
+isc_result_t dhcp_host_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ struct host_decl *hp;
+ if (lp -> type != dhcp_type_host)
+ return DHCP_R_INVALIDARG;
+ hp = (struct host_decl *)lp;
+
+#ifdef DEBUG_OMAPI
+ log_debug ("OMAPI delete host %s", hp -> name);
+#endif
+ delete_host (hp, 1);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_pool_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct pool *pool;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_pool)
+ return DHCP_R_INVALIDARG;
+ pool = (struct pool *)h;
+
+ /* No values to set yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> set_value) {
+ status = ((*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+
+isc_result_t dhcp_pool_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct pool *pool;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_pool)
+ return DHCP_R_INVALIDARG;
+ pool = (struct pool *)h;
+
+ /* No values to get yet. */
+
+ /* Try to find some inner object that can provide the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+isc_result_t dhcp_pool_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct pool *pool;
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ struct permit *pc, *pn;
+#endif
+
+ if (h -> type != dhcp_type_pool)
+ return DHCP_R_INVALIDARG;
+ pool = (struct pool *)h;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ if (pool -> next)
+ pool_dereference (&pool -> next, file, line);
+ if (pool -> group)
+ group_dereference (&pool -> group, file, line);
+ if (pool -> shared_network)
+ shared_network_dereference (&pool -> shared_network, file, line);
+ if (pool -> active)
+ lease_dereference (&pool -> active, file, line);
+ if (pool -> expired)
+ lease_dereference (&pool -> expired, file, line);
+ if (pool -> free)
+ lease_dereference (&pool -> free, file, line);
+ if (pool -> backup)
+ lease_dereference (&pool -> backup, file, line);
+ if (pool -> abandoned)
+ lease_dereference (&pool -> abandoned, file, line);
+#if defined (FAILOVER_PROTOCOL)
+ if (pool -> failover_peer)
+ dhcp_failover_state_dereference (&pool -> failover_peer,
+ file, line);
+#endif
+ for (pc = pool -> permit_list; pc; pc = pn) {
+ pn = pc -> next;
+ free_permit (pc, file, line);
+ }
+ pool -> permit_list = (struct permit *)0;
+
+ for (pc = pool -> prohibit_list; pc; pc = pn) {
+ pn = pc -> next;
+ free_permit (pc, file, line);
+ }
+ pool -> prohibit_list = (struct permit *)0;
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_pool_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct pool *pool;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_pool)
+ return DHCP_R_INVALIDARG;
+ pool = (struct pool *)h;
+
+ /* Can't write pools yet. */
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> signal_handler) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ if (updatep)
+ return ISC_R_SUCCESS;
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t dhcp_pool_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct pool *pool;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_pool)
+ return DHCP_R_INVALIDARG;
+ pool = (struct pool *)h;
+
+ /* Can't stuff pool values yet. */
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_pool_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ /* Can't look up pools yet. */
+
+ /* If we get to here without finding a pool, no valid key was
+ specified. */
+ if (!*lp)
+ return DHCP_R_NOKEYS;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_pool_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t dhcp_pool_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+static isc_result_t
+class_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct class *class;
+ struct class *superclass = 0;
+ isc_result_t status;
+ int issubclass = (h -> type == dhcp_type_subclass);
+
+ class = (struct class *)h;
+
+ if (!omapi_ds_strcmp(name, "name")) {
+ char *tname;
+
+ if (class->name)
+ return ISC_R_EXISTS;
+
+ if ((tname = dmalloc(value->u.buffer.len + 1, MDL)) == NULL) {
+ return ISC_R_NOMEMORY;
+ }
+
+ /* tname is null terminated from dmalloc() */
+ memcpy(tname, value->u.buffer.value, value->u.buffer.len);
+
+ if (issubclass) {
+ status = find_class(&superclass, tname, MDL);
+ dfree(tname, MDL);
+
+ if (status == ISC_R_NOTFOUND)
+ return status;
+
+ if (class->superclass != NULL)
+ class_dereference(&class->superclass, MDL);
+
+ class_reference(&class->superclass, superclass, MDL);
+ } else if (value -> type == omapi_datatype_data ||
+ value -> type == omapi_datatype_string) {
+ class->name = dmalloc(value->u.buffer.len + 1, MDL);
+ if (!class->name)
+ return ISC_R_NOMEMORY;
+
+ /* class->name is null-terminated from dmalloc() */
+ memcpy(class->name, value->u.buffer.value,
+ value->u.buffer.len);
+ } else
+ return DHCP_R_INVALIDARG;
+
+ return ISC_R_SUCCESS;
+ }
+
+
+ if (issubclass && !omapi_ds_strcmp(name, "hashstring")) {
+ if (class->hash_string.data)
+ return ISC_R_EXISTS;
+
+ if (value->type == omapi_datatype_data ||
+ value->type == omapi_datatype_string) {
+ if (!buffer_allocate(&class->hash_string.buffer,
+ value->u.buffer.len, MDL))
+ return ISC_R_NOMEMORY;
+ class->hash_string.data =
+ class->hash_string.buffer->data;
+ memcpy(class->hash_string.buffer->data,
+ value->u.buffer.value, value->u.buffer.len);
+ class->hash_string.len = value->u.buffer.len;
+ } else
+ return DHCP_R_INVALIDARG;
+
+ return ISC_R_SUCCESS;
+ }
+
+ if (!omapi_ds_strcmp(name, "group")) {
+ if (value->type == omapi_datatype_data ||
+ value->type == omapi_datatype_string) {
+ struct group_object *group = NULL;
+
+ group_hash_lookup(&group, group_name_hash,
+ (char *)value->u.buffer.value,
+ value->u.buffer.len, MDL);
+ if (!group || (group->flags & GROUP_OBJECT_DELETED))
+ return ISC_R_NOTFOUND;
+ if (class->group)
+ group_dereference(&class->group, MDL);
+ group_reference(&class->group, group->group, MDL);
+ group_object_dereference(&group, MDL);
+ } else
+ return DHCP_R_INVALIDARG;
+
+ return ISC_R_SUCCESS;
+ }
+
+
+ /* note we do not support full expressions via omapi because the
+ expressions parser needs to be re-done to support parsing from
+ strings and not just files. */
+
+ if (!omapi_ds_strcmp(name, "match")) {
+ if (value->type == omapi_datatype_data ||
+ value->type == omapi_datatype_string) {
+ unsigned minlen = (value->u.buffer.len > 8 ?
+ 8 : value->u.buffer.len);
+
+ if (!strncmp("hardware",
+ (char *)value->u.buffer.value, minlen))
+ {
+ if (!expression_allocate(&class->submatch, MDL))
+ return ISC_R_NOMEMORY;
+
+ class->submatch->op = expr_hardware;
+ } else
+ return DHCP_R_INVALIDARG;
+ } else
+ return DHCP_R_INVALIDARG;
+
+ return ISC_R_SUCCESS;
+ }
+
+
+ if (!omapi_ds_strcmp(name, "option")) {
+ if (value->type == omapi_datatype_data ||
+ value->type == omapi_datatype_string) {
+ /* XXXJAB support 'options' here. */
+ /* XXXJAB specifically 'bootfile-name' */
+ return DHCP_R_INVALIDARG; /* XXX tmp */
+ } else
+ return DHCP_R_INVALIDARG;
+
+ /*
+ * Currently no way to get here, if we update the above
+ * code so that we do get here this return needs to be
+ * uncommented.
+ * return ISC_R_SUCCESS;
+ */
+ }
+
+
+ /* Try to find some inner object that can take the value. */
+ if (h->inner && h->inner->type->set_value) {
+ status = ((*(h->inner->type->set_value))
+ (h->inner, id, name, value));
+ if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
+ return status;
+ }
+
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+
+
+isc_result_t dhcp_class_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+
+ return class_set_value(h, id, name, value);
+}
+
+isc_result_t dhcp_class_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct class *class;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+ class = (struct class *)h;
+
+ if (!omapi_ds_strcmp (name, "name"))
+ return omapi_make_string_value (value, name, class -> name,
+ MDL);
+
+ /* Try to find some inner object that can provide the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+isc_result_t dhcp_class_destroy (omapi_object_t *h, const char *file, int line)
+{
+ struct class *class;
+
+ if (h -> type != dhcp_type_class && h -> type != dhcp_type_subclass)
+ return DHCP_R_INVALIDARG;
+ class = (struct class *)h;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ if (class -> nic)
+ class_dereference (&class -> nic, file, line);
+ if (class -> superclass)
+ class_dereference (&class -> superclass, file, line);
+ if (class -> name) {
+ dfree (class -> name, file, line);
+ class -> name = (char *)0;
+ }
+ if (class -> billed_leases) {
+ int i;
+ for (i = 0; i < class -> lease_limit; i++) {
+ if (class -> billed_leases [i]) {
+ lease_dereference (&class -> billed_leases [i],
+ file, line);
+ }
+ }
+ dfree (class -> billed_leases, file, line);
+ class -> billed_leases = (struct lease **)0;
+ }
+ if (class -> hash) {
+ class_free_hash_table (&class -> hash, file, line);
+ class -> hash = (class_hash_t *)0;
+ }
+ data_string_forget (&class -> hash_string, file, line);
+
+ if (class -> expr)
+ expression_dereference (&class -> expr, file, line);
+ if (class -> submatch)
+ expression_dereference (&class -> submatch, file, line);
+ if (class -> group)
+ group_dereference (&class -> group, file, line);
+ if (class -> statements)
+ executable_statement_dereference (&class -> statements,
+ file, line);
+ if (class -> superclass)
+ class_dereference (&class -> superclass, file, line);
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+class_signal_handler(omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ struct class *class = (struct class *)h;
+ isc_result_t status;
+ int updatep = 0;
+ int issubclass;
+
+ issubclass = (h -> type == dhcp_type_subclass);
+
+ if (!strcmp (name, "updated")) {
+
+ if (!issubclass) {
+ if (class -> name == 0 || strlen(class -> name) == 0) {
+ return DHCP_R_INVALIDARG;
+ }
+ } else {
+ if (class -> superclass == 0) {
+ return DHCP_R_INVALIDARG; /* didn't give name */
+ }
+
+ if (class -> hash_string.data == NULL) {
+ return DHCP_R_INVALIDARG;
+ }
+ }
+
+
+ if (issubclass) {
+ if (!class -> superclass -> hash)
+ class_new_hash(&class->superclass->hash,
+ SCLASS_HASH_SIZE, MDL);
+
+ add_hash (class -> superclass -> hash,
+ class -> hash_string.data,
+ class -> hash_string.len,
+ (void *)class, MDL);
+ }
+
+
+#ifdef DEBUG_OMAPI
+ if (issubclass) {
+ log_debug ("OMAPI added subclass %s",
+ class -> superclass -> name);
+ } else {
+ log_debug ("OMAPI added class %s", class -> name);
+ }
+#endif
+
+ status = enter_class (class, 1, 1);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ updatep = 1;
+ }
+
+ /* Try to find some inner object that can take the value. */
+ if (h -> inner && h -> inner -> type -> signal_handler) {
+ status = ((*(h -> inner -> type -> signal_handler))
+ (h -> inner, name, ap));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ if (updatep)
+ return ISC_R_SUCCESS;
+
+ return ISC_R_NOTFOUND;
+}
+
+
+isc_result_t dhcp_class_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+
+ return class_signal_handler(h, name, ap);
+}
+
+isc_result_t dhcp_class_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct class *class;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+ class = (struct class *)h;
+
+ /* Can't stuff class values yet. */
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t class_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref,
+ omapi_object_type_t *typewanted)
+{
+ omapi_value_t *nv = (omapi_value_t *)0;
+ omapi_value_t *hv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct class *class = 0;
+ struct class *subclass = 0;
+
+ *lp = NULL;
+
+ /* see if we have a name */
+ status = omapi_get_value_str (ref, id, "name", &nv);
+ if (status == ISC_R_SUCCESS) {
+ char *name = dmalloc(nv -> value -> u.buffer.len + 1, MDL);
+ memcpy (name,
+ nv -> value -> u.buffer.value,
+ nv -> value -> u.buffer.len);
+
+ omapi_value_dereference (&nv, MDL);
+
+ find_class(&class, name, MDL);
+
+ dfree(name, MDL);
+
+ if (class == NULL) {
+ return ISC_R_NOTFOUND;
+ }
+
+ if (typewanted == dhcp_type_subclass) {
+ status = omapi_get_value_str (ref, id,
+ "hashstring", &hv);
+ if (status != ISC_R_SUCCESS) {
+ class_dereference(&class, MDL);
+ return DHCP_R_NOKEYS;
+ }
+
+ if (hv -> value -> type != omapi_datatype_data &&
+ hv -> value -> type != omapi_datatype_string) {
+ class_dereference(&class, MDL);
+ omapi_value_dereference (&hv, MDL);
+ return DHCP_R_NOKEYS;
+ }
+
+ class_hash_lookup (&subclass, class -> hash,
+ (const char *)
+ hv -> value -> u.buffer.value,
+ hv -> value -> u.buffer.len, MDL);
+
+ omapi_value_dereference (&hv, MDL);
+
+ class_dereference(&class, MDL);
+
+ if (subclass == NULL) {
+ return ISC_R_NOTFOUND;
+ }
+
+ class_reference(&class, subclass, MDL);
+ class_dereference(&subclass, MDL);
+ }
+
+
+ /* Don't return the object if the type is wrong. */
+ if (class -> type != typewanted) {
+ class_dereference (&class, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ if (class -> flags & CLASS_DECL_DELETED) {
+ class_dereference (&class, MDL);
+ }
+
+ omapi_object_reference(lp, (omapi_object_t *)class, MDL);
+
+ return ISC_R_SUCCESS;
+ }
+
+ return DHCP_R_NOKEYS;
+}
+
+
+isc_result_t dhcp_class_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ return class_lookup(lp, id, ref, dhcp_type_class);
+}
+
+isc_result_t dhcp_class_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ struct class *cp = 0;
+ isc_result_t status;
+
+ status = class_allocate(&cp, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ group_reference (&cp -> group, root_group, MDL);
+ cp -> flags = CLASS_DECL_DYNAMIC;
+ status = omapi_object_reference (lp, (omapi_object_t *)cp, MDL);
+ class_dereference (&cp, MDL);
+ return status;
+}
+
+isc_result_t dhcp_class_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+ struct class *cp;
+ if (lp -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+ cp = (struct class *)lp;
+
+#ifdef DEBUG_OMAPI
+ log_debug ("OMAPI delete class %s", cp -> name);
+#endif
+
+ delete_class (cp, 1);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_subclass_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != dhcp_type_subclass)
+ return DHCP_R_INVALIDARG;
+
+ return class_set_value(h, id, name, value);
+}
+
+
+isc_result_t dhcp_subclass_get_value (omapi_object_t *h, omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ struct class *subclass;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+ subclass = (struct class *)h;
+ if (subclass -> name != 0)
+ return DHCP_R_INVALIDARG;
+
+ /* XXXJAB No values to get yet. */
+
+ /* Try to find some inner object that can provide the value. */
+ if (h -> inner && h -> inner -> type -> get_value) {
+ status = ((*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+ return DHCP_R_UNKNOWNATTRIBUTE;
+}
+
+isc_result_t dhcp_subclass_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != dhcp_type_subclass)
+ return DHCP_R_INVALIDARG;
+
+ return class_signal_handler(h, name, ap);
+}
+
+
+isc_result_t dhcp_subclass_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *h)
+{
+ struct class *subclass;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_class)
+ return DHCP_R_INVALIDARG;
+ subclass = (struct class *)h;
+ if (subclass -> name != 0)
+ return DHCP_R_INVALIDARG;
+
+
+ /* Can't stuff subclass values yet. */
+
+ /* Write out the inner object, if any. */
+ if (h -> inner && h -> inner -> type -> stuff_values) {
+ status = ((*(h -> inner -> type -> stuff_values))
+ (c, id, h -> inner));
+ if (status == ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_subclass_lookup (omapi_object_t **lp,
+ omapi_object_t *id, omapi_object_t *ref)
+{
+ return class_lookup(lp, id, ref, dhcp_type_subclass);
+}
+
+
+
+
+isc_result_t dhcp_subclass_create (omapi_object_t **lp,
+ omapi_object_t *id)
+{
+ struct class *cp = 0;
+ isc_result_t status;
+
+/*
+ * XXX
+ * NOTE: subclasses and classes have the same internal type, which makes it
+ * difficult to tell them apart. Specifically, in this function we need to
+ * create a class object (because there is no such thing as a subclass
+ * object), but one field of the class object is the type (which has the
+ * value dhcp_type_class), and it is from here that all the other omapi
+ * functions are accessed. So, even though there's a whole suite of
+ * subclass functions registered, they won't get used. Now we could change
+ * the type pointer after creating the class object, but I'm not certain
+ * that won't break something else.
+ */
+
+ status = subclass_allocate(&cp, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ group_reference (&cp -> group, root_group, MDL);
+
+ cp -> flags = CLASS_DECL_DYNAMIC;
+
+ status = omapi_object_reference (lp, (omapi_object_t *)cp, MDL);
+ subclass_dereference (&cp, MDL);
+ return status;
+}
+
+isc_result_t dhcp_subclass_remove (omapi_object_t *lp,
+ omapi_object_t *id)
+{
+#if 1
+
+ log_fatal("calling dhcp_subclass_set_value");
+ /* this should never be called see dhcp_subclass_create for why */
+
+#else
+
+ struct class *cp;
+ if (lp -> type != dhcp_type_subclass)
+ return DHCP_R_INVALIDARG;
+ cp = (struct class *)lp;
+
+#ifdef DEBUG_OMAPI
+ log_debug ("OMAPI delete subclass %s", cp -> name);
+#endif
+
+ delete_class (cp, 1);
+
+#endif
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t binding_scope_set_value (struct binding_scope *scope, int createp,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ struct binding *bp;
+ char *nname;
+ struct binding_value *nv;
+ nname = dmalloc (name -> len + 1, MDL);
+ if (!nname)
+ return ISC_R_NOMEMORY;
+ memcpy (nname, name -> value, name -> len);
+ nname [name -> len] = 0;
+ bp = find_binding (scope, nname);
+ if (!bp && !createp) {
+ dfree (nname, MDL);
+ return DHCP_R_UNKNOWNATTRIBUTE;
+ }
+ if (!value) {
+ dfree (nname, MDL);
+ if (!bp)
+ return DHCP_R_UNKNOWNATTRIBUTE;
+ binding_value_dereference (&bp -> value, MDL);
+ return ISC_R_SUCCESS;
+ }
+
+ nv = (struct binding_value *)0;
+ if (!binding_value_allocate (&nv, MDL)) {
+ dfree (nname, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ switch (value -> type) {
+ case omapi_datatype_int:
+ nv -> type = binding_numeric;
+ nv -> value.intval = value -> u.integer;
+ break;
+
+ case omapi_datatype_string:
+ case omapi_datatype_data:
+ if (!buffer_allocate (&nv -> value.data.buffer,
+ value -> u.buffer.len, MDL)) {
+ binding_value_dereference (&nv, MDL);
+ dfree (nname, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ memcpy (&nv -> value.data.buffer -> data [1],
+ value -> u.buffer.value, value -> u.buffer.len);
+ nv -> value.data.len = value -> u.buffer.len;
+ break;
+
+ case omapi_datatype_object:
+ binding_value_dereference (&nv, MDL);
+ dfree (nname, MDL);
+ return DHCP_R_INVALIDARG;
+ }
+
+ if (!bp) {
+ bp = dmalloc (sizeof *bp, MDL);
+ if (!bp) {
+ binding_value_dereference (&nv, MDL);
+ dfree (nname, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ memset (bp, 0, sizeof *bp);
+ bp -> name = nname;
+ nname = (char *)0;
+ bp -> next = scope -> bindings;
+ scope -> bindings = bp;
+ } else {
+ if (bp -> value)
+ binding_value_dereference (&bp -> value, MDL);
+ dfree (nname, MDL);
+ }
+ binding_value_reference (&bp -> value, nv, MDL);
+ binding_value_dereference (&nv, MDL);
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t binding_scope_get_value (omapi_value_t **value,
+ struct binding_scope *scope,
+ omapi_data_string_t *name)
+{
+ struct binding *bp;
+ omapi_typed_data_t *td;
+ isc_result_t status;
+ char *nname;
+ nname = dmalloc (name -> len + 1, MDL);
+ if (!nname)
+ return ISC_R_NOMEMORY;
+ memcpy (nname, name -> value, name -> len);
+ nname [name -> len] = 0;
+ bp = find_binding (scope, nname);
+ dfree (nname, MDL);
+ if (!bp)
+ return DHCP_R_UNKNOWNATTRIBUTE;
+ if (!bp -> value)
+ return DHCP_R_UNKNOWNATTRIBUTE;
+
+ switch (bp -> value -> type) {
+ case binding_boolean:
+ td = (omapi_typed_data_t *)0;
+ status = omapi_typed_data_new (MDL, &td, omapi_datatype_int,
+ bp -> value -> value.boolean);
+ break;
+
+ case binding_numeric:
+ td = (omapi_typed_data_t *)0;
+ status = omapi_typed_data_new (MDL, &td, omapi_datatype_int,
+ (int)
+ bp -> value -> value.intval);
+ break;
+
+ case binding_data:
+ td = (omapi_typed_data_t *)0;
+ status = omapi_typed_data_new (MDL, &td, omapi_datatype_data,
+ bp -> value -> value.data.len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ memcpy (&td -> u.buffer.value [0],
+ bp -> value -> value.data.data,
+ bp -> value -> value.data.len);
+ break;
+
+ /* Can't return values for these two (yet?). */
+ case binding_dns:
+ case binding_function:
+ return DHCP_R_INVALIDARG;
+
+ default:
+ log_fatal ("Impossible case at %s:%d.", MDL);
+ return ISC_R_FAILURE;
+ }
+
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_value_new (value, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_typed_data_dereference (&td, MDL);
+ return status;
+ }
+
+ omapi_data_string_reference (&(*value) -> name, name, MDL);
+ omapi_typed_data_reference (&(*value) -> value, td, MDL);
+ omapi_typed_data_dereference (&td, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t binding_scope_stuff_values (omapi_object_t *c,
+ struct binding_scope *scope)
+{
+ struct binding *bp;
+ unsigned len;
+ isc_result_t status;
+
+ for (bp = scope -> bindings; bp; bp = bp -> next) {
+ if (bp -> value) {
+ if (bp -> value -> type == binding_dns ||
+ bp -> value -> type == binding_function)
+ continue;
+
+ /* Stuff the name. */
+ len = strlen (bp -> name);
+ status = omapi_connection_put_uint16 (c, len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = omapi_connection_copyin (c,
+ (unsigned char *)bp -> name,
+ len);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ switch (bp -> value -> type) {
+ case binding_boolean:
+ status = omapi_connection_put_uint32 (c,
+ sizeof (u_int32_t));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c,
+ ((u_int32_t)(bp -> value -> value.boolean))));
+ break;
+
+ case binding_data:
+ status = (omapi_connection_put_uint32
+ (c, bp -> value -> value.data.len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ if (bp -> value -> value.data.len) {
+ status = (omapi_connection_copyin
+ (c, bp -> value -> value.data.data,
+ bp -> value -> value.data.len));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+ break;
+
+ case binding_numeric:
+ status = (omapi_connection_put_uint32
+ (c, sizeof (u_int32_t)));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ status = (omapi_connection_put_uint32
+ (c, ((u_int32_t)
+ (bp -> value -> value.intval))));
+ break;
+
+
+ /* NOTREACHED */
+ case binding_dns:
+ case binding_function:
+ break;
+ }
+ }
+ }
+ return ISC_R_SUCCESS;
+}
+
+/* vim: set tabstop=8: */
diff --git a/server/salloc.c b/server/salloc.c
new file mode 100644
index 0000000..17a23b5
--- /dev/null
+++ b/server/salloc.c
@@ -0,0 +1,254 @@
+/* salloc.c
+
+ Memory allocation for the DHCP server... */
+
+/*
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+
+#if defined (COMPACT_LEASES)
+struct lease *free_leases;
+
+# if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+struct lease *lease_hunks;
+
+void relinquish_lease_hunks ()
+{
+ struct lease *c, *n, **p, *f;
+ int i;
+
+ /* Account for all the leases on the free list. */
+ for (n = lease_hunks; n; n = n -> next) {
+ for (i = 1; i < n -> starts + 1; i++) {
+ p = &free_leases;
+ for (c = free_leases; c; c = c -> next) {
+ if (c == &n [i]) {
+ *p = c -> next;
+ n -> ends++;
+ break;
+ }
+ p = &c -> next;
+ }
+ if (!c) {
+ log_info ("lease %s refcnt %d",
+ piaddr (n [i].ip_addr), n [i].refcnt);
+ dump_rc_history (&n [i]);
+ }
+ }
+ }
+
+ for (c = lease_hunks; c; c = n) {
+ n = c -> next;
+ if (c -> ends != c -> starts) {
+ log_info ("lease hunk %lx leases %ld free %ld",
+ (unsigned long)c, (unsigned long)c -> starts,
+ (unsigned long)c -> ends);
+ }
+ dfree (c, MDL);
+ }
+
+ /* Free all the rogue leases. */
+ for (c = free_leases; c; c = n) {
+ n = c -> next;
+ dfree (c, MDL);
+ }
+}
+#endif
+
+struct lease *new_leases (n, file, line)
+ unsigned n;
+ const char *file;
+ int line;
+{
+ struct lease *rval;
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+ rval = dmalloc ((n + 1) * sizeof (struct lease), file, line);
+ memset (rval, 0, sizeof (struct lease));
+ rval -> starts = n;
+ rval -> next = lease_hunks;
+ lease_hunks = rval;
+ rval++;
+#else
+ rval = dmalloc (n * sizeof (struct lease), file, line);
+#endif
+ return rval;
+}
+
+/* If we are allocating leases in aggregations, there's really no way
+ to free one, although perhaps we can maintain a free list. */
+
+isc_result_t dhcp_lease_free (omapi_object_t *lo,
+ const char *file, int line)
+{
+ struct lease *lease;
+ if (lo -> type != dhcp_type_lease)
+ return DHCP_R_INVALIDARG;
+ lease = (struct lease *)lo;
+ memset (lease, 0, sizeof (struct lease));
+ lease -> next = free_leases;
+ free_leases = lease;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t dhcp_lease_get (omapi_object_t **lp,
+ const char *file, int line)
+{
+ struct lease **lease = (struct lease **)lp;
+ struct lease *lt;
+
+ if (free_leases) {
+ lt = free_leases;
+ free_leases = lt -> next;
+ *lease = lt;
+ return ISC_R_SUCCESS;
+ }
+ return ISC_R_NOMEMORY;
+}
+#endif /* COMPACT_LEASES */
+
+OMAPI_OBJECT_ALLOC (lease, struct lease, dhcp_type_lease)
+OMAPI_OBJECT_ALLOC (class, struct class, dhcp_type_class)
+OMAPI_OBJECT_ALLOC (subclass, struct class, dhcp_type_subclass)
+OMAPI_OBJECT_ALLOC (pool, struct pool, dhcp_type_pool)
+
+#if !defined (NO_HOST_FREES) /* Scary debugging mode - don't enable! */
+OMAPI_OBJECT_ALLOC (host, struct host_decl, dhcp_type_host)
+#else
+isc_result_t host_allocate (struct host_decl **p, const char *file, int line)
+{
+ return omapi_object_allocate ((omapi_object_t **)p,
+ dhcp_type_host, 0, file, line);
+}
+
+isc_result_t host_reference (struct host_decl **pptr, struct host_decl *ptr,
+ const char *file, int line)
+{
+ return omapi_object_reference ((omapi_object_t **)pptr,
+ (omapi_object_t *)ptr, file, line);
+}
+
+isc_result_t host_dereference (struct host_decl **ptr,
+ const char *file, int line)
+{
+ if ((*ptr) -> refcnt == 1) {
+ log_error ("host dereferenced with refcnt == 1.");
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history ();
+#endif
+ abort ();
+ }
+ return omapi_object_dereference ((omapi_object_t **)ptr, file, line);
+}
+#endif
+
+struct lease_state *free_lease_states;
+
+struct lease_state *new_lease_state (file, line)
+ const char *file;
+ int line;
+{
+ struct lease_state *rval;
+
+ if (free_lease_states) {
+ rval = free_lease_states;
+ free_lease_states =
+ (struct lease_state *)(free_lease_states -> next);
+ dmalloc_reuse (rval, file, line, 0);
+ } else {
+ rval = dmalloc (sizeof (struct lease_state), file, line);
+ if (!rval)
+ return rval;
+ }
+ memset (rval, 0, sizeof *rval);
+ if (!option_state_allocate (&rval -> options, file, line)) {
+ free_lease_state (rval, file, line);
+ return (struct lease_state *)0;
+ }
+ return rval;
+}
+
+void free_lease_state (ptr, file, line)
+ struct lease_state *ptr;
+ const char *file;
+ int line;
+{
+ if (ptr -> options)
+ option_state_dereference (&ptr -> options, file, line);
+ if (ptr -> packet)
+ packet_dereference (&ptr -> packet, file, line);
+ if (ptr -> shared_network)
+ shared_network_dereference (&ptr -> shared_network,
+ file, line);
+
+ data_string_forget (&ptr -> parameter_request_list, file, line);
+ data_string_forget (&ptr -> filename, file, line);
+ data_string_forget (&ptr -> server_name, file, line);
+ ptr -> next = free_lease_states;
+ free_lease_states = ptr;
+ dmalloc_reuse (free_lease_states, (char *)0, 0, 0);
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_lease_states ()
+{
+ struct lease_state *cs, *ns;
+
+ for (cs = free_lease_states; cs; cs = ns) {
+ ns = cs -> next;
+ dfree (cs, MDL);
+ }
+ free_lease_states = (struct lease_state *)0;
+}
+#endif
+
+struct permit *new_permit (file, line)
+ const char *file;
+ int line;
+{
+ struct permit *permit = ((struct permit *)
+ dmalloc (sizeof (struct permit), file, line));
+ if (!permit)
+ return permit;
+ memset (permit, 0, sizeof *permit);
+ return permit;
+}
+
+void free_permit (permit, file, line)
+ struct permit *permit;
+ const char *file;
+ int line;
+{
+ if (permit -> type == permit_class)
+ class_dereference (&permit -> class, MDL);
+ dfree (permit, file, line);
+}
diff --git a/server/stables.c b/server/stables.c
new file mode 100644
index 0000000..8294527
--- /dev/null
+++ b/server/stables.c
@@ -0,0 +1,510 @@
+/* stables.c
+
+ Tables of information only used by server... */
+
+/*
+ * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+#include <syslog.h>
+
+#if defined (FAILOVER_PROTOCOL)
+
+/* This is used to indicate some kind of failure when generating a
+ failover option. */
+failover_option_t null_failover_option = { 0, 0 };
+failover_option_t skip_failover_option = { 0, 0 };
+
+/* Information about failover options, for printing, encoding
+ and decoding. */
+struct failover_option_info ft_options [] =
+{
+ { 0, "unused", FT_UNDEF, 0, 0, 0 },
+ { FTO_ADDRESSES_TRANSFERRED, "addresses-transferred", FT_UINT32, 1,
+ FM_OFFSET(addresses_transferred), FTB_ADDRESSES_TRANSFERRED },
+ { FTO_ASSIGNED_IP_ADDRESS, "assigned-IP-address", FT_IPADDR, 1,
+ FM_OFFSET(assigned_addr), FTB_ASSIGNED_IP_ADDRESS },
+ { FTO_BINDING_STATUS, "binding-status", FT_UINT8, 1,
+ FM_OFFSET(binding_status), FTB_BINDING_STATUS },
+ { FTO_CLIENT_IDENTIFIER, "client-identifier", FT_BYTES, 0,
+ FM_OFFSET(client_identifier), FTB_CLIENT_IDENTIFIER },
+ { FTO_CHADDR, "client-hardware-address", FT_BYTES, 0,
+ FM_OFFSET(chaddr), FTB_CHADDR },
+ { FTO_CLTT, "client-last-transaction-time", FT_UINT32, 1,
+ FM_OFFSET(cltt), FTB_CLTT },
+ { FTO_REPLY_OPTIONS, "client-reply-options", FT_BYTES, 0,
+ FM_OFFSET(reply_options), FTB_REPLY_OPTIONS },
+ { FTO_REQUEST_OPTIONS, "client-request-options", FT_BYTES, 0,
+ FM_OFFSET(request_options), FTB_REQUEST_OPTIONS },
+ { FTO_DDNS, "DDNS", FT_DDNS, 1, FM_OFFSET(ddns), FTB_DDNS },
+ { FTO_DELAYED_SERVICE, "delayed-service", FT_UINT8, 1,
+ FM_OFFSET(delayed_service), FTB_DELAYED_SERVICE },
+ { FTO_HBA, "hash-bucket-assignment", FT_BYTES, 0,
+ FM_OFFSET(hba), FTB_HBA },
+ { FTO_IP_FLAGS, "IP-flags", FT_UINT16, 1,
+ FM_OFFSET(ip_flags), FTB_IP_FLAGS },
+ { FTO_LEASE_EXPIRY, "lease-expiration-time", FT_UINT32, 1,
+ FM_OFFSET(expiry), FTB_LEASE_EXPIRY },
+ { FTO_MAX_UNACKED, "max-unacked-bndupd", FT_UINT32, 1,
+ FM_OFFSET(max_unacked), FTB_MAX_UNACKED },
+ { FTO_MCLT, "MCLT", FT_UINT32, 1, FM_OFFSET(mclt), FTB_MCLT },
+ { FTO_MESSAGE, "message", FT_TEXT, 0,
+ FM_OFFSET(message), FTB_MESSAGE },
+ { FTO_MESSAGE_DIGEST, "message-digest", FT_BYTES, 0,
+ FM_OFFSET(message_digest), FTB_MESSAGE_DIGEST },
+ { FTO_POTENTIAL_EXPIRY, "potential-expiration-time", FT_UINT32, 1,
+ FM_OFFSET(potential_expiry), FTB_POTENTIAL_EXPIRY },
+ { FTO_RECEIVE_TIMER, "receive-timer", FT_UINT32, 1,
+ FM_OFFSET(receive_timer), FTB_RECEIVE_TIMER },
+ { FTO_PROTOCOL_VERSION, "protocol-version", FT_UINT8, 1,
+ FM_OFFSET(protocol_version), FTB_PROTOCOL_VERSION },
+ { FTO_REJECT_REASON, "reject-reason", FT_UINT8, 1,
+ FM_OFFSET(reject_reason), FTB_REJECT_REASON },
+ { FTO_RELATIONSHIP_NAME, "relationship-name", FT_BYTES, 0,
+ FM_OFFSET(relationship_name), FTB_RELATIONSHIP_NAME },
+ { FTO_SERVER_FLAGS, "server-flags", FT_UINT8, 1,
+ FM_OFFSET(server_flags), FTB_SERVER_FLAGS },
+ { FTO_SERVER_STATE, "server-state", FT_UINT8, 1,
+ FM_OFFSET(server_state), FTB_SERVER_STATE },
+ { FTO_STOS, "start-time-of-state", FT_UINT32, 1,
+ FM_OFFSET(stos), FTB_STOS },
+ { FTO_TLS_REPLY, "TLS-reply", FT_UINT8, 1,
+ FM_OFFSET(tls_reply), FTB_TLS_REPLY },
+ { FTO_TLS_REQUEST, "TLS-request", FT_UINT8, 1,
+ FM_OFFSET(tls_request), FTB_TLS_REQUEST },
+ { FTO_VENDOR_CLASS, "vendor-class-identifier", FT_BYTES, 0,
+ FM_OFFSET(vendor_class), FTB_VENDOR_CLASS },
+ { FTO_VENDOR_OPTIONS, "vendor-specific-options", FT_BYTES, 0,
+ FM_OFFSET(vendor_options), FTB_VENDOR_OPTIONS }
+};
+
+/* These are really options that make sense for a particular request - if
+ some other option comes in, we're not going to use it, so we can just
+ discard it. Note that the message-digest option is allowed for all
+ message types, but is not saved - it's just used to validate the message
+ and then discarded - so it's not mentioned here. */
+
+u_int32_t fto_allowed [] = {
+ 0, /* 0 unused */
+ 0, /* 1 POOLREQ */
+ FTB_ADDRESSES_TRANSFERRED, /* 2 POOLRESP */
+ (FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS | FTB_CLIENT_IDENTIFIER |
+ FTB_CHADDR | FTB_DDNS | FTB_IP_FLAGS | FTB_LEASE_EXPIRY |
+ FTB_POTENTIAL_EXPIRY | FTB_STOS | FTB_CLTT | FTB_REQUEST_OPTIONS |
+ FTB_REPLY_OPTIONS), /* 3 BNDUPD */
+ (FTB_ASSIGNED_IP_ADDRESS | FTB_BINDING_STATUS | FTB_CLIENT_IDENTIFIER |
+ FTB_CHADDR | FTB_DDNS | FTB_IP_FLAGS | FTB_LEASE_EXPIRY |
+ FTB_POTENTIAL_EXPIRY | FTB_STOS | FTB_CLTT | FTB_REQUEST_OPTIONS |
+ FTB_REPLY_OPTIONS | FTB_REJECT_REASON | FTB_MESSAGE), /* 4 BNDACK */
+ (FTB_RELATIONSHIP_NAME | FTB_MAX_UNACKED | FTB_RECEIVE_TIMER |
+ FTB_VENDOR_CLASS | FTB_PROTOCOL_VERSION | FTB_TLS_REQUEST |
+ FTB_MCLT | FTB_HBA), /* 5 CONNECT */
+ (FTB_RELATIONSHIP_NAME | FTB_MAX_UNACKED | FTB_RECEIVE_TIMER |
+ FTB_VENDOR_CLASS | FTB_PROTOCOL_VERSION | FTB_TLS_REPLY |
+ FTB_REJECT_REASON | FTB_MESSAGE), /* CONNECTACK */
+ 0, /* 7 UPDREQALL */
+ 0, /* 8 UPDDONE */
+ 0, /* 9 UPDREQ */
+ (FTB_SERVER_STATE | FTB_SERVER_FLAGS | FTB_STOS), /* 10 STATE */
+ 0, /* 11 CONTACT */
+ (FTB_REJECT_REASON | FTB_MESSAGE) /* 12 DISCONNECT */
+};
+
+/* Sizes of the various types. */
+int ft_sizes [] = {
+ 1, /* FT_UINT8 */
+ 4, /* FT_IPADDR */
+ 4, /* FT_UINT32 */
+ 1, /* FT_BYTES */
+ 1, /* FT_TEXT_OR_BYTES */
+ 0, /* FT_DDNS */
+ 0, /* FT_DDNS1 */
+ 2, /* FT_UINT16 */
+ 1, /* FT_TEXT */
+ 0, /* FT_UNDEF */
+ 0, /* FT_DIGEST */
+};
+
+/* Names of the various failover link states. */
+const char *dhcp_flink_state_names [] = {
+ "invalid state 0",
+ "startup",
+ "message length wait",
+ "message wait",
+ "disconnected"
+};
+#endif /* FAILOVER_PROTOCOL */
+
+/* Failover binding state names. These are used even if there is no
+ failover protocol support. */
+const char *binding_state_names [] = {
+ "free", "active", "expired", "released", "abandoned",
+ "reset", "backup" };
+
+struct universe agent_universe;
+static struct option agent_options[] = {
+ { "circuit-id", "X", &agent_universe, 1, 1 },
+ { "remote-id", "X", &agent_universe, 2, 1 },
+ { "agent-id", "I", &agent_universe, 3, 1 },
+ { "DOCSIS-device-class", "L", &agent_universe, 4, 1 },
+ { "link-selection", "I", &agent_universe, 5, 1 },
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+struct universe server_universe;
+static struct option server_options[] = {
+ { "default-lease-time", "T", &server_universe, 1, 1 },
+ { "max-lease-time", "T", &server_universe, 2, 1 },
+ { "min-lease-time", "T", &server_universe, 3, 1 },
+ { "dynamic-bootp-lease-cutoff", "T", &server_universe, 4, 1 },
+ { "dynamic-bootp-lease-length", "L", &server_universe, 5, 1 },
+ { "boot-unknown-clients", "f", &server_universe, 6, 1 },
+ { "dynamic-bootp", "f", &server_universe, 7, 1 },
+ { "allow-bootp", "f", &server_universe, 8, 1 },
+ { "allow-booting", "f", &server_universe, 9, 1 },
+ { "one-lease-per-client", "f", &server_universe, 10, 1 },
+ { "get-lease-hostnames", "f", &server_universe, 11, 1 },
+ { "use-host-decl-names", "f", &server_universe, 12, 1 },
+ { "use-lease-addr-for-default-route", "f",
+ &server_universe, 13, 1 },
+ { "min-secs", "B", &server_universe, 14, 1 },
+ { "filename", "t", &server_universe, 15, 1 },
+ { "server-name", "t", &server_universe, 16, 1 },
+ { "next-server", "I", &server_universe, 17, 1 },
+ { "authoritative", "f", &server_universe, 18, 1 },
+ { "vendor-option-space", "U", &server_universe, 19, 1 },
+ { "always-reply-rfc1048", "f", &server_universe, 20, 1 },
+ { "site-option-space", "X", &server_universe, 21, 1 },
+ { "always-broadcast", "f", &server_universe, 22, 1 },
+ { "ddns-domainname", "t", &server_universe, 23, 1 },
+ { "ddns-hostname", "t", &server_universe, 24, 1 },
+ { "ddns-rev-domainname", "t", &server_universe, 25, 1 },
+ { "lease-file-name", "t", &server_universe, 26, 1 },
+ { "pid-file-name", "t", &server_universe, 27, 1 },
+ { "duplicates", "f", &server_universe, 28, 1 },
+ { "declines", "f", &server_universe, 29, 1 },
+ { "ddns-updates", "f", &server_universe, 30, 1 },
+ { "omapi-port", "S", &server_universe, 31, 1 },
+ { "local-port", "S", &server_universe, 32, 1 },
+ { "limited-broadcast-address", "I", &server_universe, 33, 1 },
+ { "remote-port", "S", &server_universe, 34, 1 },
+ { "local-address", "I", &server_universe, 35, 1 },
+ { "omapi-key", "d", &server_universe, 36, 1 },
+ { "stash-agent-options", "f", &server_universe, 37, 1 },
+ { "ddns-ttl", "T", &server_universe, 38, 1 },
+ { "ddns-update-style", "Nddns-styles.", &server_universe, 39, 1 },
+ { "client-updates", "f", &server_universe, 40, 1 },
+ { "update-optimization", "f", &server_universe, 41, 1 },
+ { "ping-check", "f", &server_universe, 42, 1 },
+ { "update-static-leases", "f", &server_universe, 43, 1 },
+ { "log-facility", "Nsyslog-facilities.",
+ &server_universe, 44, 1 },
+ { "do-forward-updates", "f", &server_universe, 45, 1 },
+ { "ping-timeout", "T", &server_universe, 46, 1 },
+ { "infinite-is-reserved", "f", &server_universe, 47, 1 },
+ { "update-conflict-detection", "f", &server_universe, 48, 1 },
+ { "leasequery", "f", &server_universe, 49, 1 },
+ { "adaptive-lease-time-threshold", "B", &server_universe, 50, 1 },
+ { "do-reverse-updates", "f", &server_universe, 51, 1 },
+ { "fqdn-reply", "f", &server_universe, 52, 1 },
+ { "preferred-lifetime", "T", &server_universe, 53, 1 },
+ { "dhcpv6-lease-file-name", "t", &server_universe, 54, 1 },
+ { "dhcpv6-pid-file-name", "t", &server_universe, 55, 1 },
+ { "limit-addrs-per-ia", "L", &server_universe, 56, 1 },
+ { "limit-prefs-per-ia", "L", &server_universe, 57, 1 },
+/* Assert a configuration parsing error if delayed-ack isn't compiled in. */
+#if defined(DELAYED_ACK)
+ { "delayed-ack", "S", &server_universe, 58, 1 },
+ { "max-ack-delay", "L", &server_universe, 59, 1 },
+#endif
+#if defined(LDAP_CONFIGURATION)
+ { "ldap-server", "t", &server_universe, 60, 1 },
+ { "ldap-port", "d", &server_universe, 61, 1 },
+ { "ldap-username", "t", &server_universe, 62, 1 },
+ { "ldap-password", "t", &server_universe, 63, 1 },
+ { "ldap-base-dn", "t", &server_universe, 64, 1 },
+ { "ldap-method", "Nldap-methods.", &server_universe, 65, 1 },
+ { "ldap-debug-file", "t", &server_universe, 66, 1 },
+ { "ldap-dhcp-server-cn", "t", &server_universe, 67, 1 },
+ { "ldap-referrals", "f", &server_universe, 68, 1 },
+#if defined(LDAP_USE_SSL)
+ { "ldap-ssl", "Nldap-ssl-usage.", &server_universe, 69, 1 },
+ { "ldap-tls-reqcert", "Nldap-tls-reqcert.", &server_universe, 70, 1 },
+ { "ldap-tls-ca-file", "t", &server_universe, 71, 1 },
+ { "ldap-tls-ca-dir", "t", &server_universe, 72, 1 },
+ { "ldap-tls-cert", "t", &server_universe, 73, 1 },
+ { "ldap-tls-key", "t", &server_universe, 74, 1 },
+ { "ldap-tls-crlcheck", "Nldap-tls-crlcheck.", &server_universe, 75, 1 },
+ { "ldap-tls-ciphers", "t", &server_universe, 76, 1 },
+ { "ldap-tls-randfile", "t", &server_universe, 77, 1 },
+#endif /* LDAP_USE_SSL */
+#endif /* LDAP_CONFIGURATION */
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+#if defined(LDAP_CONFIGURATION)
+struct enumeration_value ldap_values [] = {
+ { "static", LDAP_METHOD_STATIC },
+ { "dynamic", LDAP_METHOD_DYNAMIC },
+ { (char *) 0, 0 }
+};
+
+struct enumeration ldap_methods = {
+ (struct enumeration *)0,
+ "ldap-methods", 1,
+ ldap_values
+};
+
+#if defined(LDAP_USE_SSL)
+struct enumeration_value ldap_ssl_usage_values [] = {
+ { "off", LDAP_SSL_OFF },
+ { "on",LDAP_SSL_ON },
+ { "ldaps", LDAP_SSL_LDAPS },
+ { "start_tls", LDAP_SSL_TLS },
+ { (char *) 0, 0 }
+};
+
+struct enumeration ldap_ssl_usage_enum = {
+ (struct enumeration *)0,
+ "ldap-ssl-usage", 1,
+ ldap_ssl_usage_values
+};
+
+struct enumeration_value ldap_tls_reqcert_values [] = {
+ { "never", LDAP_OPT_X_TLS_NEVER },
+ { "hard", LDAP_OPT_X_TLS_HARD },
+ { "demand", LDAP_OPT_X_TLS_DEMAND},
+ { "allow", LDAP_OPT_X_TLS_ALLOW },
+ { "try", LDAP_OPT_X_TLS_TRY },
+ { (char *) 0, 0 }
+};
+struct enumeration ldap_tls_reqcert_enum = {
+ (struct enumeration *)0,
+ "ldap-tls-reqcert", 1,
+ ldap_tls_reqcert_values
+};
+
+struct enumeration_value ldap_tls_crlcheck_values [] = {
+ { "none", LDAP_OPT_X_TLS_CRL_NONE},
+ { "peer", LDAP_OPT_X_TLS_CRL_PEER},
+ { "all", LDAP_OPT_X_TLS_CRL_ALL },
+ { (char *) 0, 0 }
+};
+struct enumeration ldap_tls_crlcheck_enum = {
+ (struct enumeration *)0,
+ "ldap-tls-crlcheck", 1,
+ ldap_tls_crlcheck_values
+};
+#endif
+#endif
+
+struct enumeration_value ddns_styles_values [] = {
+ { "none", 0 },
+ { "ad-hoc", 1 },
+ { "interim", 2 },
+ { (char *)0, 0 }
+};
+
+struct enumeration ddns_styles = {
+ (struct enumeration *)0,
+ "ddns-styles", 1,
+ ddns_styles_values
+};
+
+struct enumeration_value syslog_values [] = {
+#if defined (LOG_KERN)
+ { "kern", LOG_KERN },
+#endif
+#if defined (LOG_USER)
+ { "user", LOG_USER },
+#endif
+#if defined (LOG_MAIL)
+ { "mail", LOG_MAIL },
+#endif
+#if defined (LOG_DAEMON)
+ { "daemon", LOG_DAEMON },
+#endif
+#if defined (LOG_AUTH)
+ { "auth", LOG_AUTH },
+#endif
+#if defined (LOG_SYSLOG)
+ { "syslog", LOG_SYSLOG },
+#endif
+#if defined (LOG_LPR)
+ { "lpr", LOG_LPR },
+#endif
+#if defined (LOG_NEWS)
+ { "news", LOG_NEWS },
+#endif
+#if defined (LOG_UUCP)
+ { "uucp", LOG_UUCP },
+#endif
+#if defined (LOG_CRON)
+ { "cron", LOG_CRON },
+#endif
+#if defined (LOG_AUTHPRIV)
+ { "authpriv", LOG_AUTHPRIV },
+#endif
+#if defined (LOG_FTP)
+ { "ftp", LOG_FTP },
+#endif
+#if defined (LOG_LOCAL0)
+ { "local0", LOG_LOCAL0 },
+#endif
+#if defined (LOG_LOCAL1)
+ { "local1", LOG_LOCAL1 },
+#endif
+#if defined (LOG_LOCAL2)
+ { "local2", LOG_LOCAL2 },
+#endif
+#if defined (LOG_LOCAL3)
+ { "local3", LOG_LOCAL3 },
+#endif
+#if defined (LOG_LOCAL4)
+ { "local4", LOG_LOCAL4 },
+#endif
+#if defined (LOG_LOCAL5)
+ { "local5", LOG_LOCAL5 },
+#endif
+#if defined (LOG_LOCAL6)
+ { "local6", LOG_LOCAL6 },
+#endif
+#if defined (LOG_LOCAL7)
+ { "local7", LOG_LOCAL7 },
+#endif
+ { (char *)0, 0 }
+};
+
+struct enumeration syslog_enum = {
+ (struct enumeration *)0,
+ "syslog-facilities", 1,
+ syslog_values
+};
+
+void initialize_server_option_spaces()
+{
+ int i;
+ unsigned code;
+
+ /* Set up the Relay Agent Information Option suboption space... */
+ agent_universe.name = "agent";
+ agent_universe.concat_duplicates = 0;
+ agent_universe.option_state_dereference =
+ linked_option_state_dereference;
+ agent_universe.lookup_func = lookup_linked_option;
+ agent_universe.save_func = save_linked_option;
+ agent_universe.delete_func = delete_linked_option;
+ agent_universe.encapsulate = linked_option_space_encapsulate;
+ agent_universe.foreach = linked_option_space_foreach;
+ agent_universe.decode = parse_option_buffer;
+ agent_universe.index = universe_count++;
+ agent_universe.length_size = 1;
+ agent_universe.tag_size = 1;
+ agent_universe.get_tag = getUChar;
+ agent_universe.store_tag = putUChar;
+ agent_universe.get_length = getUChar;
+ agent_universe.store_length = putUChar;
+ agent_universe.site_code_min = 0;
+ agent_universe.end = 0;
+ universes [agent_universe.index] = &agent_universe;
+ if (!option_name_new_hash(&agent_universe.name_hash,
+ AGENT_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&agent_universe.code_hash,
+ AGENT_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate agent option hash table.");
+ for (i = 0 ; agent_options[i].name ; i++) {
+ option_code_hash_add(agent_universe.code_hash,
+ &agent_options[i].code, 0,
+ &agent_options[i], MDL);
+ option_name_hash_add(agent_universe.name_hash,
+ agent_options[i].name, 0,
+ &agent_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("Relay Agent name hash: %s",
+ option_name_hash_report(agent_universe.name_hash));
+ log_info("Relay Agent code hash: %s",
+ option_code_hash_report(agent_universe.code_hash));
+#endif
+ code = DHO_DHCP_AGENT_OPTIONS;
+ option_code_hash_lookup(&agent_universe.enc_opt,
+ dhcp_universe.code_hash, &code, 0, MDL);
+
+ /* Set up the server option universe... */
+ server_universe.name = "server";
+ server_universe.concat_duplicates = 0;
+ server_universe.lookup_func = lookup_hashed_option;
+ server_universe.option_state_dereference =
+ hashed_option_state_dereference;
+ server_universe.save_func = save_hashed_option;
+ server_universe.delete_func = delete_hashed_option;
+ server_universe.encapsulate = hashed_option_space_encapsulate;
+ server_universe.foreach = hashed_option_space_foreach;
+ server_universe.length_size = 1; /* Never used ... */
+ server_universe.tag_size = 4;
+ server_universe.store_tag = putUChar;
+ server_universe.store_length = putUChar;
+ server_universe.site_code_min = 0;
+ server_universe.end = 0;
+ server_universe.index = universe_count++;
+ universes [server_universe.index] = &server_universe;
+ if (!option_name_new_hash(&server_universe.name_hash,
+ SERVER_HASH_SIZE, MDL) ||
+ !option_code_new_hash(&server_universe.code_hash,
+ SERVER_HASH_SIZE, MDL))
+ log_fatal ("Can't allocate server option hash table.");
+ for (i = 0 ; server_options[i].name ; i++) {
+ option_code_hash_add(server_universe.code_hash,
+ &server_options[i].code, 0,
+ &server_options[i], MDL);
+ option_name_hash_add(server_universe.name_hash,
+ server_options[i].name, 0,
+ &server_options[i], MDL);
+ }
+#if defined(REPORT_HASH_PERFORMANCE)
+ log_info("Server-Config Option name hash: %s",
+ option_name_hash_report(server_universe.name_hash));
+ log_info("Server-Config Option code hash: %s",
+ option_code_hash_report(server_universe.code_hash));
+#endif
+
+ /* Add the server and agent option spaces to the option space hash. */
+ universe_hash_add (universe_hash,
+ agent_universe.name, 0, &agent_universe, MDL);
+ universe_hash_add (universe_hash,
+ server_universe.name, 0, &server_universe, MDL);
+
+ /* Make the server universe the configuration option universe. */
+ config_universe = &server_universe;
+
+ code = SV_VENDOR_OPTION_SPACE;
+ option_code_hash_lookup(&vendor_cfg_option, server_universe.code_hash,
+ &code, 0, MDL);
+}
diff --git a/tests/DHCPv6/000-badmsgtype.pl b/tests/DHCPv6/000-badmsgtype.pl
new file mode 100644
index 0000000..d0c35ee
--- /dev/null
+++ b/tests/DHCPv6/000-badmsgtype.pl
@@ -0,0 +1,164 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new(255);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+# timeout parameters
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our bogus packet
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/010-solicit-noclientid.pl b/tests/DHCPv6/010-solicit-noclientid.pl
new file mode 100644
index 0000000..6bb5d6e
--- /dev/null
+++ b/tests/DHCPv6/010-solicit-noclientid.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# do NOT add the Client Identifier (required by DOCSIS and RFC 3315)
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/011-solicit-serverid.pl b/tests/DHCPv6/011-solicit-serverid.pl
new file mode 100644
index 0000000..cee4ddb
--- /dev/null
+++ b/tests/DHCPv6/011-solicit-serverid.pl
@@ -0,0 +1,215 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server Identifier (NOT ALLOWED by DOCSIS and RFC 3315)
+$msg->add_option($OPT_SERVERID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/020-advertise-mcast.pl b/tests/DHCPv6/020-advertise-mcast.pl
new file mode 100644
index 0000000..c5c4ade
--- /dev/null
+++ b/tests/DHCPv6/020-advertise-mcast.pl
@@ -0,0 +1,164 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_ADVERTISE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+# timeout parameters
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our bogus packet
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/030-request-noclientid.pl b/tests/DHCPv6/030-request-noclientid.pl
new file mode 100644
index 0000000..d5af077
--- /dev/null
+++ b/tests/DHCPv6/030-request-noclientid.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_REQUEST);
+
+# NOT add the Client Identifier (required by DOCSIS and RFC 3315)
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/031-request-noserverid.pl b/tests/DHCPv6/031-request-noserverid.pl
new file mode 100644
index 0000000..1f73038
--- /dev/null
+++ b/tests/DHCPv6/031-request-noserverid.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_REQUEST);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/032-request-badduid.pl b/tests/DHCPv6/032-request-badduid.pl
new file mode 100644
index 0000000..30e5040
--- /dev/null
+++ b/tests/DHCPv6/032-request-badduid.pl
@@ -0,0 +1,216 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_REQUEST);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server Identifier (required by DOCSIS and RFC 3315)
+# but use *our* DUID
+$msg->add_option($OPT_SERVERID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/110-information-request-ia_na.pl b/tests/DHCPv6/110-information-request-ia_na.pl
new file mode 100644
index 0000000..5dc6889
--- /dev/null
+++ b/tests/DHCPv6/110-information-request-ia_na.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/111-information-request-ia_ta.pl b/tests/DHCPv6/111-information-request-ia_ta.pl
new file mode 100644
index 0000000..dc3cf1c
--- /dev/null
+++ b/tests/DHCPv6/111-information-request-ia_ta.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_TA for each interface (should be IA_NA, by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_TA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/112-badduid.pl b/tests/DHCPv6/112-badduid.pl
new file mode 100644
index 0000000..3be9261
--- /dev/null
+++ b/tests/DHCPv6/112-badduid.pl
@@ -0,0 +1,208 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_INFORMATION_REQUEST);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server Identifier (required by DOCSIS and RFC 3315)
+# but use *our* DUID
+$msg->add_option($OPT_SERVERID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 1; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/210-solicit-nohost.pl b/tests/DHCPv6/210-solicit-nohost.pl
new file mode 100644
index 0000000..be9538c
--- /dev/null
+++ b/tests/DHCPv6/210-solicit-nohost.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/211-solicit-opt-in-na.pl b/tests/DHCPv6/211-solicit-opt-in-na.pl
new file mode 100644
index 0000000..33f6951
--- /dev/null
+++ b/tests/DHCPv6/211-solicit-opt-in-na.pl
@@ -0,0 +1,218 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+$Data::Dumper::Useqq = 1;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6";
+#my $client_id = dhcp_client::duid(3);
+$msg->add_option($OPT_CLIENTID, $client_id);
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3));
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ my $enc_opt = dhcp_client::msg->new(0);
+ $enc_opt->add_option($OPT_CLIENTID, $client_id);
+ $option_data .= $enc_opt->packed_options();
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+#print Dumper($reply_msg), "\n";
+ exit(0);
+}
+
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl
new file mode 100644
index 0000000..a8d8149
--- /dev/null
+++ b/tests/DHCPv6/212-solicit-opt-in-na-norapidcommit.pl
@@ -0,0 +1,218 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+$Data::Dumper::Useqq = 1;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6";
+#my $client_id = dhcp_client::duid(3);
+$msg->add_option($OPT_CLIENTID, $client_id);
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3));
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ my $enc_opt = dhcp_client::msg->new(0);
+ $enc_opt->add_option($OPT_CLIENTID, $client_id);
+ $option_data .= $enc_opt->packed_options();
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+#$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+#print Dumper($reply_msg), "\n";
+ exit(0);
+}
+
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/280-release-nohost.pl b/tests/DHCPv6/280-release-nohost.pl
new file mode 100644
index 0000000..9758cd8
--- /dev/null
+++ b/tests/DHCPv6/280-release-nohost.pl
@@ -0,0 +1,175 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new message
+my $msg = dhcp_client::msg->new($MSG_RELEASE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our message
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/281-release-bad-address.pl b/tests/DHCPv6/281-release-bad-address.pl
new file mode 100644
index 0000000..d130812
--- /dev/null
+++ b/tests/DHCPv6/281-release-bad-address.pl
@@ -0,0 +1,187 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_RELEASE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::");
+ $iaaddr_option .= pack("NN", 0, 0);
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8);
+ $option_data .= $iaaddr_option;
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/282-release-no-address.pl b/tests/DHCPv6/282-release-no-address.pl
new file mode 100644
index 0000000..b3d0af5
--- /dev/null
+++ b/tests/DHCPv6/282-release-no-address.pl
@@ -0,0 +1,183 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_RELEASE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/283-release.pl b/tests/DHCPv6/283-release.pl
new file mode 100644
index 0000000..a1baae4
--- /dev/null
+++ b/tests/DHCPv6/283-release.pl
@@ -0,0 +1,189 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_RELEASE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6";
+$msg->add_option($OPT_CLIENTID, $client_id);
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3));
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff");
+ $iaaddr_option .= pack("NN", 0, 0);
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8);
+ $option_data .= $iaaddr_option;
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/290-decline-nohost.pl b/tests/DHCPv6/290-decline-nohost.pl
new file mode 100644
index 0000000..02a25e3
--- /dev/null
+++ b/tests/DHCPv6/290-decline-nohost.pl
@@ -0,0 +1,175 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_DECLINE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/291-decline-bad-address.pl b/tests/DHCPv6/291-decline-bad-address.pl
new file mode 100644
index 0000000..d9f0e1e
--- /dev/null
+++ b/tests/DHCPv6/291-decline-bad-address.pl
@@ -0,0 +1,187 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_DECLINE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $iaaddr_option = inet_pton(AF_INET6, "1:2:3:4::");
+ $iaaddr_option .= pack("NN", 0, 0);
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8);
+ $option_data .= $iaaddr_option;
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/292-decline-no-address.pl b/tests/DHCPv6/292-decline-no-address.pl
new file mode 100644
index 0000000..84c970f
--- /dev/null
+++ b/tests/DHCPv6/292-decline-no-address.pl
@@ -0,0 +1,183 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_DECLINE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/293-decline.pl b/tests/DHCPv6/293-decline.pl
new file mode 100644
index 0000000..960195a
--- /dev/null
+++ b/tests/DHCPv6/293-decline.pl
@@ -0,0 +1,189 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_DECLINE);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6";
+$msg->add_option($OPT_CLIENTID, $client_id);
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid(3));
+
+# add the Server identifier (required by RFC 3315)
+$msg->add_option($OPT_SERVERID, "InfiniteEntropy");
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $iaaddr_option = inet_pton(AF_INET6, "3ffe:aaaa:aaaa:aaaa::ffff");
+ $iaaddr_option .= pack("NN", 0, 0);
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $option_data .= pack("nn", $OPT_IAADDR, length($iaaddr_option)-8);
+ $option_data .= $iaaddr_option;
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/README b/tests/DHCPv6/README
new file mode 100644
index 0000000..c559de2
--- /dev/null
+++ b/tests/DHCPv6/README
@@ -0,0 +1,62 @@
+In order to test the DHCPv6 server, we have a configuration file with
+known values, and some Perl scripts designed to send and receive
+DHCPv6 packets to check various code paths.
+
+It is not complete test converage by any means, but it should be
+fairly easy to add additional tests as needed.
+
+The scripts themselves are not very well written. There is a lot of
+copied code, poor error handling, and so on. These should be rewritten
+at some point.
+
+To use, the DHCPv6 server must be running in test mode to send back to
+the originating port. (The scripts can be changed to bind to the
+appropriate client port, but they don't now, and have to run as root
+to do this). In server/dhcpv6.c, look for this comment:
+
+/* For testing, we reply to the sending port, so we don't need a root */
+/* client */
+ to_addr.sin6_port = remote_port;
+/* to_addr.sin6_port = packet->client_port;*/
+
+And change the code to use the client_port value.
+
+You will need to modify one of the test configuration files to use one
+of the physical subnets that your machine uses, in the subnet6
+statement.
+
+Then run the server as root, in debug mode:
+
+# touch /tmp/test.leases
+# dhcpd -cf test-a.conf -lf /tmp/test.leases -d
+
+You can invoke the scripts then:
+
+$ perl 000-badmsgtype.pl
+
+The expected results vary per script, depending on the behavior that
+is being tested.
+
+
+Notes about scripts:
+
+In order to manipulate IPv6 addresses, we need the Socket6 library,
+available from CPAN:
+
+http://search.cpan.org/~umemoto/Socket6-0.19/Socket6.pm
+
+The Perl that Sun issues for Solaris 10 is compiled with the Sun
+compiler. If you have the Sun compiler, then this will work fine.
+Otherwise you may need to install Perl from source.
+
+We need to get the hardware address in order to build DUID properly.
+The IO::Interface module reports hardware address, but not on Solaris
+10 it seems. Rather than do this the "right way", we do it the "Perl
+way", and hack it. "ifconfig" does return the Ethernet address, but
+only to the root user. However, we can look for files of the name
+/etc/hostname.*, get the IP address from "ifconfig", and then check
+for those addresses in the ARP table.
+
+Client DUID is supposed to be an opaque value to the server, but we go
+ahead and make a "real" type 1 or type 3 DUID.
+
diff --git a/tests/DHCPv6/dhcp_client.pm b/tests/DHCPv6/dhcp_client.pm
new file mode 100644
index 0000000..5caa377
--- /dev/null
+++ b/tests/DHCPv6/dhcp_client.pm
@@ -0,0 +1,454 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+package dhcp_client;
+
+require Exporter;
+
+@ISA = qw(Exporter);
+
+# message types
+$MSG_SOLICIT = 1;
+$MSG_ADVERTISE = 2;
+$MSG_REQUEST = 3;
+$MSG_CONFIRM = 4;
+$MSG_RENEW = 5;
+$MSG_REBIND = 6;
+$MSG_REPLY = 7;
+$MSG_RELEASE = 8;
+$MSG_DECLINE = 9;
+$MSG_RECONFIGURE = 10;
+$MSG_INFORMATION_REQUEST = 11;
+$MSG_RELAY_FORW = 12;
+$MSG_RELAY_REPL = 13;
+
+# option numbers
+$OPT_CLIENTID = 1;
+$OPT_SERVERID = 2;
+$OPT_IA_NA = 3;
+$OPT_IA_TA = 4;
+$OPT_IAADDR = 5;
+$OPT_ORO = 6;
+$OPT_PREFERENCE = 7;
+$OPT_ELAPSED_TIME = 8;
+$OPT_RELAY_MSG = 9;
+$OPT_AUTH = 11;
+$OPT_UNICAST = 12;
+$OPT_STATUS_CODE = 13;
+$OPT_RAPID_COMMIT = 14;
+$OPT_USER_CLASS = 15;
+$OPT_VENDOR_CLASS = 16;
+$OPT_VENDOR_OPTS = 17;
+$OPT_INTERFACE_ID = 18;
+$OPT_RECONF_MSG = 19;
+$OPT_RECONF_ACCEPT = 20;
+
+# timeouts
+$SOL_MAX_DELAY = 1;
+$SOL_TIMEOUT = 1;
+$SOL_MAX_RT = 120;
+$REQ_TIMEOUT = 1;
+$REQ_MAX_RT = 30;
+$REQ_MAX_RC = 10;
+$CNF_MAX_DELAY = 1;
+$CNF_MAX_RT = 4;
+$CNF_MAX_RD = 10;
+$REN_TIMEOUT = 10;
+$REN_MAX_RT = 600;
+$REB_TIMEOUT = 10;
+$REB_MAX_RT = 600;
+$INF_MAX_DELAY = 1;
+$INF_TIMEOUT = 1;
+$INF_MAX_RT = 120;
+$REL_TIMEOUT = 1;
+$REL_MAX_RC = 5;
+$DEC_TIMEOUT = 1;
+$DEC_MAX_RC = 5;
+$REC_TIMEOUT = 2;
+$REC_MAX_RC = 8;
+$HOP_COUNT_LIMIT = 32;
+
+@EXPORT = qw( $MSG_SOLICIT $MSG_ADVERTISE $MSG_REQUEST $MSG_CONFIRM
+ $MSG_RENEW $MSG_REBIND $MSG_REPLY $MSG_RELEASE $MSG_DECLINE
+ $MSG_RECONFIGURE $MSG_INFORMATION_REQUEST $MSG_RELAY_FORW
+ $MSG_RELAY_REPL
+ $OPT_CLIENTID $OPT_SERVERID $OPT_IA_NA $OPT_IA_TA $OPT_IAADDR
+ $OPT_ORO $OPT_PREFERENCE $OPT_ELAPSED_TIME $OPT_RELAY_MSG
+ $OPT_AUTH $OPT_UNICAST $OPT_STATUS_CODE $OPT_RAPID_COMMIT
+ $OPT_USER_CLASS $OPT_VENDOR_CLASS $OPT_VENDOR_OPTS
+ $OPT_INTERFACE_ID $OPT_RECONF_MSG $OPT_RECONF_ACCEPT
+ $SOL_MAX_DELAY $SOL_TIMEOUT $SOL_MAX_RT $REQ_TIMEOUT
+ $REQ_MAX_RT $REQ_MAX_RC $CNF_MAX_DELAY $CNF_MAX_RT
+ $CNF_MAX_RD $REN_TIMEOUT $REN_MAX_RT $REB_TIMEOUT $REB_MAX_RT
+ $INF_MAX_DELAY $INF_TIMEOUT $INF_MAX_RT $REL_TIMEOUT
+ $REL_MAX_RC $DEC_TIMEOUT $DEC_MAX_RC $REC_TIMEOUT $REC_MAX_RC
+ $HOP_COUNT_LIMIT );
+
+my %msg_type_num = (
+ MSG_SOLICIT => 1,
+ MSG_ADVERTISE => 2,
+ MSG_REQUEST => 3,
+ MSG_CONFIRM => 4,
+ MSG_RENEW => 5,
+ MSG_REBIND => 6,
+ MSG_REPLY => 7,
+ MSG_RELEASE => 8,
+ MSG_DECLINE => 9,
+ MSG_RECONFIGURE => 10,
+ MSG_INFORMATION_REQUEST => 11,
+ MSG_RELAY_FORW => 12,
+ MSG_RELAY_REPL => 13,
+);
+my %msg_num_type = reverse(%msg_type_num);
+
+my %opt_type_num = (
+ OPT_CLIENTID => 1,
+ OPT_SERVERID => 2,
+ OPT_IA_NA => 3,
+ OPT_IA_TA => 4,
+ OPT_IAADDR => 5,
+ OPT_ORO => 6,
+ OPT_PREFERENCE => 7,
+ OPT_ELAPSED_TIME => 8,
+ OPT_RELAY_MSG => 9,
+ OPT_AUTH => 11,
+ OPT_UNICAST => 12,
+ OPT_STATUS_CODE => 13,
+ OPT_RAPID_COMMIT => 14,
+ OPT_USER_CLASS => 15,
+ OPT_VENDOR_CLASS => 16,
+ OPT_VENDOR_OPTS => 17,
+ OPT_INTERFACE_ID => 18,
+ OPT_RECONF_MSG => 19,
+ OPT_RECONF_ACCEPT => 20,
+);
+my %opt_num_type = reverse(%opt_type_num);
+
+my %status_code_num = (
+ Success => 0,
+ UnspecFail => 1,
+ NoAddrsAvail => 2,
+ NoBinding => 3,
+ NotOnLink => 4,
+ UseMulticast => 5,
+);
+my %status_num_code = reverse(%status_code_num);
+
+my %docsis_type_num = (
+ CL_OPTION_ORO => 1,
+ CL_OPTION_TFTP_SERVERS => 32,
+ CL_OPTION_CONFIG_FILE_NAME => 33,
+ CL_OPTION_SYSLOG_SERVERS => 34,
+ CL_OPTION_TLV5 => 35,
+ CL_OPTION_DEVICE_ID => 36,
+ CL_OPTION_CCC => 37,
+ CL_OPTION_DOCSIS_VERS => 38,
+);
+my %docsis_num_type = reverse(%docsis_type_num);
+
+use strict;
+use English;
+use POSIX;
+
+# XXX: very Solaris-specific
+sub iface {
+ my @ifaces;
+ foreach my $fname (glob("/etc/hostname.*")) {
+ $fname =~ s[^/etc/hostname.][];
+ push(@ifaces, $fname);
+ }
+ return wantarray() ? @ifaces : $ifaces[0];
+}
+
+# XXX: very Solaris-specific
+sub mac_addr {
+ my @ip_addrs;
+ foreach my $iface (iface()) {
+ if (`ifconfig $iface 2>/dev/null` =~ /\sinet (\S+)\s/) {
+ push(@ip_addrs, $1);
+ }
+ }
+ my @mac_addrs;
+ foreach my $line (split(/\n/, `arp -an 2>/dev/null`)) {
+ my @parts = split(/\s+/, $line);
+ my $ip = $parts[1];
+ my $mac = $parts[-1];
+ if (grep { $ip eq $_ } @ip_addrs) {
+ $mac =~ s/://g;
+ push(@mac_addrs, $mac);
+ }
+ }
+ return wantarray() ? @mac_addrs : $mac_addrs[0];
+}
+
+sub mac_addr_binary {
+ my @mac_addr = split(//, mac_addr());
+ my $mac_addr = join("", map { chr(hex($_)) } @mac_addr);
+ return $mac_addr;
+}
+
+# DHCPv6 times start 2000-01-01 00:00:00
+my $dhcp_time_base = 946684800;
+#{
+# local $ENV{TZ} = "UTC";
+# POSIX::tzset();
+# $dhcp_time_base = POSIX::mktime(0, 0, 0, 1, 0, 100);
+#}
+
+sub dhcpv6_time {
+ return time() - $dhcp_time_base;
+}
+
+sub duid {
+ my ($type) = @_;
+
+ $type = 1 unless (defined $type);
+
+ if (($type == 1) || ($type == 3)) {
+ my $mac_addr = mac_addr_binary();
+ if ($type == 1) {
+ my $time = pack("N", dhcpv6_time());
+ return "\x00\x01\x00\x01${time}${mac_addr}";
+ } else {
+ return "\x00\x03\x00\x01${mac_addr}";
+ }
+ } else {
+ die "Unknown DUID type $type requested";
+ }
+}
+
+package dhcp_client::msg;
+
+use Socket;
+use Socket6;
+
+sub new {
+ my ($pkg, $msg_type, $trans_id) = @_;
+
+ my $this = {};
+ bless $this;
+
+ $this->{msg_type} = $msg_type+0;
+ if (defined $trans_id) {
+ $this->{trans_id} = $trans_id;
+ } else {
+ $this->{trans_id} = chr(rand(256)) .
+ chr(rand(256)) . chr(rand(256));
+ }
+ $this->{options} = [ ];
+
+ return $this;
+}
+
+
+sub add_option {
+ my ($this, $num, $data) = @_;
+
+ push(@{$this->{options}}, [ $num, $data ]);
+}
+
+sub get_option {
+ my ($this, $num) = @_;
+ my @options;
+ foreach my $option (@{$this->{options}}) {
+ if ($option->[0] == $num) {
+ push(@options, $option->[1]);
+ }
+ }
+ return wantarray() ? @options : $options[0];
+}
+
+sub packed_options {
+ my ($this) = @_;
+
+ my $options = "";
+ foreach my $option (@{$this->{options}}) {
+ $options .= pack("nn", $option->[0], length($option->[1]));
+ $options .= $option->[1];
+ }
+ return $options;
+}
+
+sub packet {
+ my ($this) = @_;
+
+ my $packet = "";
+ $packet .= chr($this->{msg_type});
+ $packet .= $this->{trans_id};
+ $packet .= $this->packed_options();
+ return $packet;
+}
+
+sub unpack_options {
+ my ($options) = @_;
+
+ my @parsed_options;
+ my $p = 0;
+ while ($p < length($options)) {
+ my ($id, $len) = unpack("nn", substr($options, $p, 4));
+ push(@parsed_options, [ $id, substr($options, $p + 4, $len) ]);
+ $p += 4 + $len;
+ }
+ return @parsed_options;
+}
+
+sub print_docsis_option {
+ my ($num, $data, $indent) = @_;
+
+ print "${indent}DOCSIS Option $num";
+ if ($docsis_num_type{$num}) {
+ print " ($docsis_num_type{$num})";
+ }
+ print ", length ", length($data), "\n";
+
+ return unless ($docsis_num_type{$num});
+
+ if ($docsis_num_type{$num} eq "CL_OPTION_ORO") {
+ my $num_oro = length($data) / 2;
+ for (my $i=0; $i<$num_oro; $i++) {
+ my $oro_num = unpack("n", substr($data, $i*2, 2));
+ print "${indent} $oro_num";
+ if ($docsis_num_type{$oro_num}) {
+ print " ($docsis_num_type{$oro_num})";
+ }
+ print "\n";
+ }
+ } elsif ($docsis_num_type{$num} eq "CL_OPTION_TFTP_SERVERS") {
+ my $num_servers = length($data) / 16;
+ for (my $i=0; $i<$num_servers; $i++) {
+ my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16));
+ print "$indent TFTP server ", ($i+1), ": ";
+ print uc($srv), "\n";
+ }
+ } elsif ($docsis_num_type{$num} eq "CL_OPTION_CONFIG_FILE_NAME") {
+ print "$indent Config file name: \"$data\"\n"
+ } elsif ($docsis_num_type{$num} eq "CL_OPTION_SYSLOG_SERVERS") {
+ my $num_servers = length($data) / 16;
+ for (my $i=0; $i<$num_servers; $i++) {
+ my $srv = inet_ntop(AF_INET6, substr($data, $i*16, 16));
+ print "$indent syslog server ", ($i+1), ": ";
+ print uc($srv), "\n";
+ }
+ }
+}
+
+sub print_option {
+ my ($num, $data, $indent) = @_;
+
+ print "${indent}Option $num";
+ if ($opt_num_type{$num}) {
+ print " ($opt_num_type{$num})";
+ }
+ print ", length ", length($data), "\n";
+ if ($num == $dhcp_client::OPT_ORO) {
+ my $num_oro = length($data) / 2;
+ for (my $i=0; $i<$num_oro; $i++) {
+ my $oro_num = unpack("n", substr($data, $i*2, 2));
+ print "${indent} $oro_num";
+ if ($opt_num_type{$oro_num}) {
+ print " ($opt_num_type{$oro_num})";
+ }
+ print "\n";
+ }
+ } elsif (($num == $dhcp_client::OPT_CLIENTID) ||
+ ($num == $dhcp_client::OPT_SERVERID)) {
+ print $indent, " ";
+ if (length($data) > 0) {
+ printf '%02X', ord(substr($data, 0, 1));
+ for (my $i=1; $i<length($data); $i++) {
+ printf ':%02X', ord(substr($data, $i, 1));
+ }
+ }
+ print "\n";
+ } elsif ($num == $dhcp_client::OPT_IA_NA) {
+ printf "${indent} IAID: 0x\%08X\n",
+ unpack("N", substr($data, 0, 4));
+ printf "${indent} T1: \%d\n", unpack("N", substr($data, 4, 4));
+ printf "${indent} T2: \%d\n", unpack("N", substr($data, 8, 4));
+ if (length($data) > 12) {
+ printf "${indent} IA_NA encapsulated options:\n";
+ foreach my $option (unpack_options(substr($data, 12))) {
+ print_option(@{$option}, $indent . " ");
+ }
+ }
+ } elsif ($num == $dhcp_client::OPT_IAADDR) {
+ printf "${indent} IPv6 address: \%s\n",
+ uc(inet_ntop(AF_INET6, substr($data, 0, 16)));
+ printf "${indent} Preferred lifetime: \%d\n",
+ unpack("N", substr($data, 16, 4));
+ printf "${indent} Valid lifetime: \%d\n",
+ unpack("N", substr($data, 20, 4));
+ if (length($data) > 24) {
+ printf "${indent} IAADDR encapsulated options:\n";
+ foreach my $option (unpack_options(substr($data, 24))) {
+ print_option(@{$option}, $indent . " ");
+ }
+ }
+ } elsif ($num == $dhcp_client::OPT_VENDOR_OPTS) {
+ my $enterprise_number = unpack("N", substr($data, 0, 4));
+ print "${indent} Enterprise number: $enterprise_number\n";
+
+ # DOCSIS
+ if ($enterprise_number == 4491) {
+ foreach my $option (unpack_options(substr($data, 4))) {
+ print_docsis_option(@{$option}, $indent . " ");
+ }
+ }
+ } elsif ($num == $dhcp_client::OPT_STATUS_CODE) {
+ my $code = ord(substr($data, 0, 1));
+ my $msg = substr($data, 1);
+ print "${indent} Code: $code";
+ if ($status_num_code{$code}) {
+ print " ($status_num_code{$code})";
+ }
+ print "\n";
+ print "${indent} Message: \"$msg\"\n";
+ }
+}
+
+# XXX: we aren't careful about packet boundaries and values...
+# DO NOT RUN ON PRODUCTION SYSTEMS!!!
+sub decode {
+ my ($packet, $print) = @_;
+
+ my $msg_type = ord(substr($packet, 0, 1));
+ my $trans_id = substr($packet, 1, 3);
+ my $msg = dhcp_client::msg->new($msg_type, $trans_id);
+
+ if ($print) {
+ print "DHCPv6 packet\n";
+ print " Message type: $msg_num_type{$msg_type}\n";
+ printf " Transaction id: 0x\%02X\%02X\%02X\n",
+ ord(substr($trans_id, 0, 1)),
+ ord(substr($trans_id, 1, 1)),
+ ord(substr($trans_id, 2, 1));
+ print " Options:\n";
+ }
+
+ foreach my $option (unpack_options(substr($packet, 4))) {
+ print_option(@{$option}, " ") if ($print);
+ $msg->add_option(@{$option});
+ }
+
+ return $msg;
+}
+
diff --git a/tests/DHCPv6/stubcli-opt-in-na.pl b/tests/DHCPv6/stubcli-opt-in-na.pl
new file mode 100644
index 0000000..d68ba09
--- /dev/null
+++ b/tests/DHCPv6/stubcli-opt-in-na.pl
@@ -0,0 +1,217 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+$Data::Dumper::Useqq = 1;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+my $client_id = "\x00\x01\x00\x01\x0c\x00\xa1\x41\x00\x06\x5b\x50\x99\xf6";
+$msg->add_option($OPT_CLIENTID, $client_id);
+#$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ my $enc_opt = dhcp_client::msg->new(0);
+ $enc_opt->add_option($OPT_CLIENTID, $client_id);
+ $option_data .= $enc_opt->packed_options();
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+#print Dumper($reply_msg), "\n";
+ exit(0);
+}
+
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/stubcli.pl b/tests/DHCPv6/stubcli.pl
new file mode 100644
index 0000000..be9538c
--- /dev/null
+++ b/tests/DHCPv6/stubcli.pl
@@ -0,0 +1,212 @@
+#! /usr/bin/perl -w
+
+# Copyright (c) 2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+# Internet Systems Consortium, Inc.
+# 950 Charter Street
+# Redwood City, CA 94063
+# <info@isc.org>
+# https://www.isc.org/
+
+use strict;
+use English;
+use Time::HiRes qw( sleep );
+use Socket;
+use Socket6;
+use IO::Select;
+
+use dhcp_client;
+
+# XXX: for debugging
+use Data::Dumper;
+
+# not-yet-standard options
+my $OPT_TIME_SERVERS = 40;
+my $OPT_TIME_OFFSET = 41;
+
+# DOCSIS sub-options
+my $DOCSIS_OPT_ORO = 1;
+# 2 to 31 are reserved
+my $DOCSIS_OPT_TFTP_SERVERS = 32;
+my $DOCSIS_OPT_CONFIG_FILE_NAME = 33;
+my $DOCSIS_OPT_SYSLOG_SERVERS = 34;
+my $DOCSIS_OPT_TLV5 = 35;
+my $DOCSIS_OPT_DEVICE_ID = 36;
+my $DOCSIS_OPT_CCC = 37;
+my $DOCSIS_OPT_VERS = 38;
+
+# well-known addresses
+my $All_DHCP_Relay_Agents_and_Servers = "ff02::1:2";
+my $All_DHCP_Servers = "ff05::1:3";
+
+# ports
+my $client_port = 546;
+my $server_port = 547;
+
+# create a new Solicit message
+my $msg = dhcp_client::msg->new($MSG_SOLICIT);
+
+# add the Client Identifier (required by DOCSIS and RFC 3315)
+$msg->add_option($OPT_CLIENTID, dhcp_client::duid());
+
+# add Elapsed Time, set to 0 on first packet (required by RFC 3315)
+$msg->add_option($OPT_ELAPSED_TIME, "\x00\x00");
+
+# add IA_NA for each interface (required by DOCSIS and RFC 3315)
+# XXX: should this be a single interface only?
+my $iaid = 0;
+foreach my $iface (dhcp_client::iface()) {
+ my $option_data = pack("NNN", ++$iaid, 0, 0);
+ $msg->add_option($OPT_IA_NA, $option_data);
+}
+
+# add Reconfigure Accept (required by DOCSIS)
+$msg->add_option($OPT_RECONF_ACCEPT, "");
+
+# add Options Request (required by DOCSIS, recommended by RFC 3315)
+my @oro = ( $OPT_TIME_SERVERS, $OPT_TIME_OFFSET );
+$msg->add_option($OPT_ORO, pack("n*", @oro));
+
+
+# add Vendor Class option (required by DOCSIS)
+$msg->add_option($OPT_VENDOR_CLASS, pack("N", 4491) . "docsis3.0");
+
+# add Vendor-specific Information Option option (required by DOCSIS)
+my $vsio = pack("N", 4491);
+
+# ORO (required by DOCSIS)
+my @docsis_oro = ( $DOCSIS_OPT_TFTP_SERVERS );
+$vsio .= pack("nnC*", $DOCSIS_OPT_ORO, 0+@docsis_oro, @docsis_oro);
+
+# TLV5 data: CMTS DOCSIS version number 3.0 (required by DOCSIS)
+my $tlv5_data = "\x01\x02\x03\x0";
+$vsio .= pack("nn", $DOCSIS_OPT_TLV5, length($tlv5_data)) . $tlv5_data;
+
+# DOCSIS Device (required by DOCSIS)
+my $docsis_device_id = dhcp_client::mac_addr_binary();
+$vsio .= pack("nn", $DOCSIS_OPT_DEVICE_ID, length($docsis_device_id));
+$vsio .= $docsis_device_id;
+
+$msg->add_option($OPT_VENDOR_OPTS, $vsio);
+
+# add Rapid Commit option (required by DOCSIS)
+$msg->add_option($OPT_RAPID_COMMIT, "");
+
+# timeout parameters, from DOCSIS
+my $IRT = $SOL_TIMEOUT;
+my $MRT = $SOL_MAX_RT;
+my $MRC = 4; # DOCSIS says 4, RFC 3315 says it SHOULD be 0
+my $MRD = 0;
+
+# sleep a random amount of time between 0 and 1 second, required by RFC 3315
+# XXX: this seems pretty stupid
+sleep(rand($SOL_MAX_DELAY));
+
+my $RT;
+my $count = 0;
+my $mrd_end_time;
+if ($MRD != 0) {
+ $mrd_end_time = time() + $MRD;
+}
+my $reply_msg;
+do {
+ # create our socket, and send our Solicit
+ socket(SOCK, PF_INET6, SOCK_DGRAM, getprotobyname('udp')) || die;
+ my $addr = inet_pton(AF_INET6, $All_DHCP_Servers);
+ my $packet = $msg->packet();
+ my $send_ret = send(SOCK, $packet, 0,
+ pack_sockaddr_in6($server_port, $addr));
+ if (not defined($send_ret)) {
+ printf STDERR
+ "Error \%d sending DHCPv6 Solicit message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ } elsif ($send_ret != length($packet)) {
+ print STDERR "Unable to send entire DHCPv6 Solicit message.\n";
+ exit(1);
+ }
+ $count++;
+
+ my $RAND = rand(0.2) - 0.1;
+ if (defined $RT) {
+ $RT = 2*$RT + $RAND*$RT;
+ if (($RT > $MRT) && ($MRT != 0)) {
+ $RT = $MRT + $RAND*$RT;
+ }
+ } else {
+ $RT = $IRT + $RAND*$IRT;
+ }
+
+ my $rt_end_time = time() + $RT;
+ if (defined($mrd_end_time) && ($mrd_end_time > $rt_end_time)) {
+ $rt_end_time = $mrd_end_time;
+ }
+
+ for (;;) {
+ my $timeout = $rt_end_time - time();
+ if ($timeout < 0) {
+# print STDERR "Timeout waiting for DHCPv6 Advertise ",
+# "or Reply message.\n";
+ last;
+ }
+
+ my @ready = IO::Select->new(\*SOCK)->can_read($timeout);
+
+ if (@ready) {
+ my $reply;
+ my $recv_ret;
+
+ $recv_ret = recv(SOCK, $reply, 1500, 0);
+ if (not defined $recv_ret) {
+ printf STDERR
+ "Error \%d receiving DHCPv6 " .
+ "message;\n\%s\n",
+ 0+$ERRNO, $ERRNO;
+ exit(1);
+ }
+
+ $reply_msg = dhcp_client::msg::decode($reply, 1);
+ if (($reply_msg->{msg_type} == $MSG_ADVERTISE) ||
+ ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ last;
+ }
+ }
+ }
+
+} until ($reply_msg ||
+ (($MRC != 0) && ($count > $MRC)) ||
+ (defined($mrd_end_time) && ($mrd_end_time > time())));
+
+unless ($reply_msg) {
+ if (($MRC != 0) && ($count >= $MRC)) {
+ print STDERR
+ "No reply after maximum retransmission count.\n";
+ } else {
+ print STDERR
+ "No reply after maximum retransmission duration.\n";
+ }
+}
+
+if ($reply_msg && ($reply_msg->{msg_type} == $MSG_REPLY)) {
+ print "Got DHCPv6 Reply message.\n";
+ exit(0);
+}
+
+#$Data::Dumper::Useqq = 1;
+#print Dumper($msg), "\n";
+#print Dumper($msg->packet()), "\n";
+#
+#print "packet length: ", length($msg->packet()), "\n";
+
diff --git a/tests/DHCPv6/test-a.conf b/tests/DHCPv6/test-a.conf
new file mode 100644
index 0000000..9fe4236
--- /dev/null
+++ b/tests/DHCPv6/test-a.conf
@@ -0,0 +1,67 @@
+#
+# Define the DHCPv6 option space.
+#
+# Option numbers are assigned by IANA:
+# http://www.iana.org/assignments/dhcpv6-parameters
+#
+option dhcp6.time-servers code 40 = array of ip6-address;
+option dhcp6.time-offset code 41 = signed integer 32;
+
+#
+# Define the DOCSIS option space.
+# TODO: DOCSIS oro definition
+#
+option space docsis code width 2 length width 2;
+option vsio.docsis code 4491 = encapsulate docsis;
+option docsis.tftp-servers code 32 = array of ip6-address;
+option docsis.cablelabs-configuration-file code 33 = text;
+option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
+option docsis.device-id code 36 = string;
+
+#
+# Declare some options.
+#
+option dhcp6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2;
+option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2;
+
+#
+# DNS server IP address to update dynamically
+#
+ddns-update-style interim;
+ddns-domainname "foo.com";
+
+#
+# Per-host settings.
+#
+host cablemodem-1 {
+ host-identifier option
+ dhcp6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6;
+ fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff;
+ ddns-domainname "bar.com";
+ option dhcp6.time-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option dhcp6.time-offset -14400; # -4 hours
+ option docsis.cablelabs-configuration-file "bootfile.cfg";
+ option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+}
+
+#host cablemodem-2 {
+# host-identifier option docsis.device-id 00:06:5B:50:99:F6;
+# option dhcp6.time-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1,
+# 3ffe:dddd:aaaa:aaaa::2;
+# option dhcp6.time-offset -14400; # -4 hours
+# option docsis.cablelabs-configuration-file "bootfile.cfg";
+# option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+# 3ffe:aaaa:aaaa:aaaa::2;
+#}
+
+# XXX: for testing
+subnet6 fe80::20c:29ff:fe42:820/128 {
+}
+
+
diff --git a/tests/DHCPv6/test-b.conf b/tests/DHCPv6/test-b.conf
new file mode 100644
index 0000000..4b67380
--- /dev/null
+++ b/tests/DHCPv6/test-b.conf
@@ -0,0 +1,60 @@
+#
+# Define the DHCPv6 option space.
+#
+# Option numbers are assigned by IANA:
+# http://www.iana.org/assignments/dhcpv6-parameters
+#
+option dhcpv6.time-servers code 40 = array of ip6-address;
+option dhcpv6.time-offset code 41 = signed integer 32;
+
+#
+# Define the DOCSIS option space.
+#
+option space docsis code width 2 length width 2;
+option docsis.tftp-servers code 32 = array of ip6-address;
+option docsis.cablelabs-configuration-file code 33 = text;
+option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
+option docsis.device-id code 36 = string;
+
+#
+# Declare some options.
+#
+option dhcpv6.time-servers 3ffe:bbbb:aaaa:aaaa::1, 3ffe:bbbb:aaaa:aaaa::2;
+option docsis.tftp-servers 3ffe:cccc:aaaa:aaaa::1, 3ffe:cccc:aaaa:aaaa::2;
+
+#
+# DNS server IP address to update dynamically
+#
+ddns-update-style interim;
+ddns-domainname "foo.com";
+
+#
+# Per-host settings.
+#
+host cablemodem-1 {
+ option dhcpv6.client-id 00:01:00:01:0c:00:a1:41:00:06:5b:50:99:f6;
+ fixed-address6 3ffe:aaaa:aaaa:aaaa::ffff;
+ ddns-domainname "bar.com";
+ option dhcpv6.time-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option docsis.tftp-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+ option dhcpv6.time-offset -14400; # -4 hours
+ option docsis.cablelabs-configuration-file "bootfile.cfg";
+ option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+}
+
+host cablemodem-2 {
+ option docsis.device-id 00:06:5B:50:99:F6;
+ option dhcpv6.time-servers 3ffe:dddd:aaaa:aaaa::1,
+ 3ffe:dddd:aaaa:aaaa::2;
+ option docsis.tftp-servers 3ffe:dddd:aaaa:aaaa::1,
+ 3ffe:dddd:aaaa:aaaa::2;
+ option dhcpv6.time-offset -14400; # -4 hours
+ option docsis.cablelabs-configuration-file "bootfile.cfg";
+ option docsis.cablelabs-syslog-servers 3ffe:aaaa:aaaa:aaaa::1,
+ 3ffe:aaaa:aaaa:aaaa::2;
+}
+
+
diff --git a/tests/HOWTO-unit-test b/tests/HOWTO-unit-test
new file mode 100644
index 0000000..6abd2b1
--- /dev/null
+++ b/tests/HOWTO-unit-test
@@ -0,0 +1,153 @@
+Introduction
+------------
+
+In DHCP, a unit test exercises a particular piece of code in
+isolation. There is a separate unit test per module or API. Each unit
+test lives in a directory beneath the code it is designed to exercise.
+So, we have:
+
+ client/tests/
+ common/tests/
+ dhcpctl/tests/
+
+And so on.
+
+Ideally each function would be invoked with every possible type of
+input, and each branch of every function would be checked. In practice
+we try to be a bit more pragmatic, and target the most basic
+operations, as well tricky code, and areas we have seen bugs in the
+past.
+
+
+Running Unit Tests
+------------------
+
+In order to run the unit tests for DHCP, use:
+
+$ make check
+
+This will run all of the unit tests.
+
+You can run a single test by going to the appropriate test directory
+and invoking the test directly:
+
+$ cd common/tests
+$ make test_alloc
+$ ./test_alloc
+
+There are also a number of options that you can use when running a
+test. To see these, use the "-u" flag on the program.
+
+
+Adding a New Unit Test
+----------------------
+
+To add an additional test to an existing test program, you must create
+a function for the new test in the C source file:
+
+static void
+mynewtest(void) {
+ static const char *test_desc = "describe the test";
+
+ t_assert("mynewtest", 1, T_REQUIRED, test_desc);
+
+ /* ... test code ... */
+
+ t_result(T_PASS);
+}
+
+Then add this function to the T_testlist[] array in the file:
+
+testspec_t T_testlist[] = {
+ ...
+ { mynewtest, "some new test" },
+ { NULL, NULL }
+};
+
+Then you should be able to compile and run your new test.
+
+
+Adding a New Unit Test Program
+------------------------------
+
+To add a new program, such as when a new module is added, you can copy
+the "unit_test_sample.c" file (in this directory) to a new name, add
+the new file as a target in Makefile.am, and begin adding tests. Do
+not forget to add it to CVS via "cvs add".
+
+If there is no "tests" directory for a given subdirectory, then one
+must be created. This can be done by:
+
+1. Creating the directory:
+
+ $ mkdir $subdir/tests
+ $ cvs add tests
+
+2. Adding the subdirectory to the build system:
+
+ Add to $subdir/Makefile.am:
+
+ SUBDIRS = tests
+
+ Add to the AC_OUTPUT macro in configure.ac:
+
+ $subdir/tests/Makefile
+
+3. Create a Makefile.am in the new directory, something like this:
+
+ AM_CPPFLAGS = -I../..
+
+ check_PROGRAMS = test_foo
+
+ TESTS = test_foo
+
+ test_foo_SOURCES = test_foo.c
+ test_foo_LDADD = ../../tests/libt_api.a # plus others...
+
+
+See existing Makefile.am for examples, and the Automake documentation:
+
+ http://www.gnu.org/software/automake/manual/html_node/Tests.html
+
+
+Support Functions
+-----------------
+
+Here are a few of the most useful functions defined in t_api that you
+can use in testing:
+
+ void
+ t_assert(const char *component, int anum, int class,
+ const char *what, ...);
+
+ The name of this function is slightly misleading. It
+ actually just prints out an error message in the test
+ output.
+
+ void
+ t_info(const char *format, ...);
+
+ Prints out a message in the test output. You should
+ include "\n" at the end.
+
+ void
+ t_result(int result);
+
+ Prints out the result in the test output. You should
+ use one of the constants for this:
+
+ T_PASS
+ T_FAIL
+ T_UNRESOLVED
+ T_UNSUPPORTED
+ T_UNTESTED
+ T_THREADONLY
+
+Additional Testing
+------------------
+
+Other static or runtime testing is always an option. For instance, you
+can use valgrind to check for memory leaks.
+
+
+$Id: HOWTO-unit-test,v 1.2 2007-11-16 11:04:12 shane Exp $
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..9c6c650
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,33 @@
+EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \
+ DHCPv6/000-badmsgtype.pl \
+ DHCPv6/010-solicit-noclientid.pl \
+ DHCPv6/011-solicit-serverid.pl \
+ DHCPv6/020-advertise-mcast.pl \
+ DHCPv6/030-request-noclientid.pl \
+ DHCPv6/031-request-noserverid.pl \
+ DHCPv6/032-request-badduid.pl \
+ DHCPv6/110-information-request-ia_na.pl \
+ DHCPv6/111-information-request-ia_ta.pl \
+ DHCPv6/112-badduid.pl \
+ DHCPv6/210-solicit-nohost.pl \
+ DHCPv6/211-solicit-opt-in-na.pl \
+ DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \
+ DHCPv6/280-release-nohost.pl \
+ DHCPv6/281-release-bad-address.pl \
+ DHCPv6/282-release-no-address.pl \
+ DHCPv6/283-release.pl \
+ DHCPv6/290-decline-nohost.pl \
+ DHCPv6/291-decline-bad-address.pl \
+ DHCPv6/292-decline-no-address.pl \
+ DHCPv6/293-decline.pl \
+ DHCPv6/README DHCPv6/dhcp_client.pm \
+ DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \
+ DHCPv6/test-a.conf DHCPv6/test-b.conf \
+ HOWTO-unit-test \
+ unit_test_sample.c
+
+AM_CPPFLAGS = -I..
+
+check_LIBRARIES = libt_api.a
+libt_api_a_SOURCES = t_api.c t_api_dhcp.c
+
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644
index 0000000..3b81ed9
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,420 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = tests
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/includes/config.h
+CONFIG_CLEAN_FILES =
+AR = ar
+ARFLAGS = cru
+libt_api_a_AR = $(AR) $(ARFLAGS)
+libt_api_a_LIBADD =
+am_libt_api_a_OBJECTS = t_api.$(OBJEXT) t_api_dhcp.$(OBJEXT)
+libt_api_a_OBJECTS = $(am_libt_api_a_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/includes
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libt_api_a_SOURCES)
+DIST_SOURCES = $(libt_api_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDAP_CFLAGS = @LDAP_CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_prefix_program = @ac_prefix_program@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+byte_order = @byte_order@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = failover/dhcp-1.cf failover/dhcp-2.cf failover/new-failover \
+ DHCPv6/000-badmsgtype.pl \
+ DHCPv6/010-solicit-noclientid.pl \
+ DHCPv6/011-solicit-serverid.pl \
+ DHCPv6/020-advertise-mcast.pl \
+ DHCPv6/030-request-noclientid.pl \
+ DHCPv6/031-request-noserverid.pl \
+ DHCPv6/032-request-badduid.pl \
+ DHCPv6/110-information-request-ia_na.pl \
+ DHCPv6/111-information-request-ia_ta.pl \
+ DHCPv6/112-badduid.pl \
+ DHCPv6/210-solicit-nohost.pl \
+ DHCPv6/211-solicit-opt-in-na.pl \
+ DHCPv6/212-solicit-opt-in-na-norapidcommit.pl \
+ DHCPv6/280-release-nohost.pl \
+ DHCPv6/281-release-bad-address.pl \
+ DHCPv6/282-release-no-address.pl \
+ DHCPv6/283-release.pl \
+ DHCPv6/290-decline-nohost.pl \
+ DHCPv6/291-decline-bad-address.pl \
+ DHCPv6/292-decline-no-address.pl \
+ DHCPv6/293-decline.pl \
+ DHCPv6/README DHCPv6/dhcp_client.pm \
+ DHCPv6/stubcli-opt-in-na.pl DHCPv6/stubcli.pl \
+ DHCPv6/test-a.conf DHCPv6/test-b.conf \
+ HOWTO-unit-test \
+ unit_test_sample.c
+
+AM_CPPFLAGS = -I..
+check_LIBRARIES = libt_api.a
+libt_api_a_SOURCES = t_api.c t_api_dhcp.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign tests/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-checkLIBRARIES:
+ -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES)
+libt_api.a: $(libt_api_a_OBJECTS) $(libt_api_a_DEPENDENCIES)
+ -rm -f libt_api.a
+ $(libt_api_a_AR) libt_api.a $(libt_api_a_OBJECTS) $(libt_api_a_LIBADD)
+ $(RANLIB) libt_api.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api_dhcp.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES)
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkLIBRARIES clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean \
+ clean-checkLIBRARIES clean-generic ctags distclean \
+ distclean-compile distclean-generic distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/tests/failover/dhcp-1.cf b/tests/failover/dhcp-1.cf
new file mode 100644
index 0000000..07842ab
--- /dev/null
+++ b/tests/failover/dhcp-1.cf
@@ -0,0 +1,154 @@
+authoritative;
+
+class "even" {
+ match if ((extract-int (suffix
+ (pick-first-value (option dhcp-client-identifier,
+ hardware), 1), 8) % 2) = 0);
+}
+class "odd" {
+ match if ((extract-int (suffix
+ (pick-first-value (option dhcp-client-identifier,
+ hardware), 1), 8) % 2) = 1);
+}
+
+lease-file-name "dhcp-1.leases";
+pid-file-name "dhcp-1.pid";
+ddns-update-style none;
+local-port 50002;
+remote-port 50003;
+omapi-port 50004;
+omapi-key FOO;
+
+default-lease-time 600;
+max-lease-time 600;
+
+failover peer "foo" {
+ primary;
+ address 10.0.0.1;
+ port 51000;
+ peer address 10.0.0.1;
+ peer port 51001;
+ max-response-delay 60;
+ max-unacked-updates 10;
+ mclt 100;
+ hba ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:
+ 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00;
+ load balance max seconds 2;
+}
+
+option space SUNW;
+option SUNW.root-mount-options code 1 = text;
+option SUNW.root-server-ip-address code 2 = ip-address;
+option SUNW.root-server-hostname code 3 = text;
+option SUNW.root-path-name code 4 = text;
+option SUNW.swap-server-ip-address code 5 = ip-address;
+option SUNW.swap-file-path code 6 = text;
+option SUNW.boot-file-path code 7 = text;
+option SUNW.posix-timezone-string code 8 = text;
+option SUNW.boot-read-size code 9 = unsigned integer 16;
+option SUNW.install-server-ip-address code 10 = ip-address;
+option SUNW.install-server-hostname code 11 = text;
+option SUNW.install-path code 12 = text;
+option SUNW.sysid-config-file-server code 13 = text;
+option SUNW.JumpStart-server code 14 = text;
+option SUNW.terminal-name code 15 = text;
+
+class "solaris-i86pc" {
+ match if option vendor-class-identifier = "SUNW.i86pc";
+ vendor-option-space SUNW;
+ option SUNW.boot-file-path "/platform/i86pc/kernel/unix";
+ option SUNW.root-path-name "/export/root/i86pc";
+}
+
+class "solaris-sun4u" {
+ match if option vendor-class-identifier = "SUNW.Ultra-5_10";
+ vendor-option-space SUNW;
+ option SUNW.install-path "/export/2/s581_sparc";
+ option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot";
+}
+
+option domain-name "connectathon.org.";
+option SUNW.root-server-ip-address 172.16.113.1;
+option SUNW.root-server-hostname "sundhcp-server17-1";
+
+class "sniffer" {
+ match if option host-name = "sniffer";
+}
+
+key FOO {
+ algorithm HMAC-MD5.SIG-ALG.REG.INT;
+ secret ABCD;
+}
+
+zone BISBEE.FUGUE.COM. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+zone 17.127.10.in-addr.arpa. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+zone 0.0.10.in-addr.arpa. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+subnet 204.152.186.128 netmask 255.255.255.192 {
+ not authoritative;
+}
+
+shared-network LOCAL {
+ subnet 127.0.0.0 netmask 255.255.255.0 {
+ }
+ subnet 10.0.2.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ failover peer "foo";
+ range 10.0.2.100 10.0.2.200;
+ }
+ }
+}
+
+shared-network NET-187 {
+ subnet 204.152.187.0 netmask 255.255.255.0 {
+ }
+ subnet 205.140.116.224 netmask 255.255.255.248 {
+ }
+ subnet 10.0.1.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ failover peer "foo";
+ range 10.0.1.10 10.0.1.200;
+ }
+ }
+}
+
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ allow members of "even";
+ option impress-servers 10.0.0.0;
+ failover peer "foo";
+ range 10.0.0.10 10.0.0.54;
+ range 10.0.0.100 10.0.0.149;
+ }
+ pool {
+ deny dynamic bootp clients;
+ allow members of "odd";
+ failover peer "foo";
+ option impress-servers 10.0.0.1;
+ range 10.0.0.55 10.0.0.99;
+ range 10.0.0.150 10.0.0.200;
+ }
+ pool {
+ deny dynamic bootp clients;
+ allow members of "sniffer";
+ failover peer "foo";
+ range 10.0.0.9 10.0.0.9;
+ }
+ option routers 10.0.0.1;
+ option domain-name "bisbee.fugue.com";
+ option domain-name-servers 10.0.0.1;
+}
diff --git a/tests/failover/dhcp-2.cf b/tests/failover/dhcp-2.cf
new file mode 100644
index 0000000..c9dfabe
--- /dev/null
+++ b/tests/failover/dhcp-2.cf
@@ -0,0 +1,152 @@
+authoritative;
+
+class "even" {
+ match if ((extract-int (suffix
+ (pick-first-value (option dhcp-client-identifier,
+ hardware), 1), 8) % 2) = 0);
+}
+class "odd" {
+ match if ((extract-int (suffix
+ (pick-first-value (option dhcp-client-identifier,
+ hardware), 1), 8) % 2) = 1);
+}
+
+lease-file-name "dhcp-2.leases";
+pid-file-name "dhcp-2.pid";
+local-port 50000;
+remote-port 50001;
+omapi-port 50005;
+
+ddns-update-style none;
+
+default-lease-time 600;
+max-lease-time 600;
+
+failover peer "foo" {
+ secondary;
+ address 10.0.0.1;
+ port 51001;
+ peer address 10.0.0.1;
+ peer port 51000;
+ max-response-delay 60;
+ max-unacked-updates 10;
+ mclt 100;
+ load balance max seconds 2;
+}
+
+option space SUNW;
+option SUNW.root-mount-options code 1 = text;
+option SUNW.root-server-ip-address code 2 = ip-address;
+option SUNW.root-server-hostname code 3 = text;
+option SUNW.root-path-name code 4 = text;
+option SUNW.swap-server-ip-address code 5 = ip-address;
+option SUNW.swap-file-path code 6 = text;
+option SUNW.boot-file-path code 7 = text;
+option SUNW.posix-timezone-string code 8 = text;
+option SUNW.boot-read-size code 9 = unsigned integer 16;
+option SUNW.install-server-ip-address code 10 = ip-address;
+option SUNW.install-server-hostname code 11 = text;
+option SUNW.install-path code 12 = text;
+option SUNW.sysid-config-file-server code 13 = text;
+option SUNW.JumpStart-server code 14 = text;
+option SUNW.terminal-name code 15 = text;
+
+class "solaris-i86pc" {
+ match if option vendor-class-identifier = "SUNW.i86pc";
+ vendor-option-space SUNW;
+ option SUNW.boot-file-path "/platform/i86pc/kernel/unix";
+ option SUNW.root-path-name "/export/root/i86pc";
+}
+
+class "solaris-sun4u" {
+ match if option vendor-class-identifier = "SUNW.Ultra-5_10";
+ vendor-option-space SUNW;
+ option SUNW.install-path "/export/2/s581_sparc";
+ option SUNW.root-path-name "/export/2/s581_sparc/Solaris_8/Tools/Boot";
+}
+
+option domain-name "connectathon.org.";
+option SUNW.root-server-ip-address 172.16.113.1;
+option SUNW.root-server-hostname "sundhcp-server17-1";
+
+class "sniffer" {
+ match if option host-name = "sniffer";
+}
+
+key FOO {
+ algorithm HMAC-MD5.SIG-ALG.REG.INT;
+ secret ABCD;
+}
+
+zone BISBEE.FUGUE.COM. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+zone 17.127.10.in-addr.arpa. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+zone 0.0.10.in-addr.arpa. {
+ primary 127.0.0.1;
+ key FOO;
+}
+
+subnet 204.152.186.128 netmask 255.255.255.192 {
+ not authoritative;
+}
+
+shared-network LOCAL {
+ subnet 127.0.0.0 netmask 255.255.255.0 {
+ }
+ subnet 10.0.2.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ failover peer "foo";
+ range 10.0.2.100 10.0.2.200;
+ }
+ }
+}
+
+shared-network 187-NET {
+ subnet 204.152.187.0 netmask 255.255.255.0 {
+ }
+ subnet 205.140.116.224 netmask 255.255.255.248 {
+ }
+ subnet 10.0.1.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ failover peer "foo";
+ range 10.0.1.10 10.0.1.200;
+ }
+ }
+}
+
+subnet 10.0.0.0 netmask 255.255.255.0 {
+ pool {
+ deny dynamic bootp clients;
+ allow members of "even";
+ option impress-servers 10.0.0.0;
+ failover peer "foo";
+ range 10.0.0.10 10.0.0.54;
+ range 10.0.0.100 10.0.0.149;
+ }
+ pool {
+ deny dynamic bootp clients;
+ allow members of "odd";
+ failover peer "foo";
+ option impress-servers 10.0.0.1;
+ range 10.0.0.55 10.0.0.99;
+ range 10.0.0.150 10.0.0.200;
+ }
+ pool {
+ deny dynamic bootp clients;
+ allow members of "sniffer";
+ failover peer "foo";
+ range 10.0.0.9 10.0.0.9;
+ }
+ option routers 10.0.0.1;
+ option domain-name "bisbee.fugue.com";
+ option domain-name-servers 10.0.0.1;
+}
diff --git a/tests/failover/new-failover b/tests/failover/new-failover
new file mode 100755
index 0000000..65d82e0
--- /dev/null
+++ b/tests/failover/new-failover
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+foo=10
+while [ $foo -lt 100 ]; do
+ cat >>dhcp-1.leases <<~
+lease 10.0.0.$foo {
+ starts 4 2001/05/01 02:19:16;
+ ends 5 2021/05/03 02:29:16;
+ binding state active;
+ next binding state free;
+ hardware ethernet 08:00:46:06:6c:23;
+ uid "test-$foo";
+}
+~
+ foo=`expr $foo + 1`
+ cat >>dhcp-2.leases <<~
+lease 10.0.0.$foo {
+ starts 4 2001/04/19 02:19:16;
+ ends 5 2021/04/21 02:29:16;
+ binding state active;
+ next binding state free;
+ hardware ethernet 08:00:46:06:6c:23;
+ uid "test-$foo";
+}
+~
+ foo=`expr $foo + 1`
+done
+
diff --git a/tests/t_api.c b/tests/t_api.c
new file mode 100644
index 0000000..ea37390
--- /dev/null
+++ b/tests/t_api.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: t_api.c,v 1.4 2009-10-28 04:12:30 sar Exp $ */
+
+/*! \file */
+
+/*
+ * This test API framework is taken from the BIND 9 code. It has been
+ * modified to remove the DNS-specific parts, and the BIND-specific
+ * parts.
+ *
+ * The DNS-specific parts are now wrapped with the DNS_SUPPORT macro,
+ * and the BIND-specific parts are now wrapped with the BIND_SUPPORT
+ * macro.
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+
+#include <isc/boolean.h>
+#include <isc/commandline.h>
+#include <isc/print.h>
+#include <isc/string.h>
+#include <isc/mem.h>
+
+#ifdef DNS_SUPPORT
+#include <dns/compress.h>
+#include <omapip/result.h>
+#endif /* DNS_SUPPORT */
+
+#ifndef BIND_SUPPORT
+#define isc_commandline_parse getopt
+#define isc_commandline_argument optarg
+#define isc_commandline_option optopt
+#endif /* BIND_SUPPORT */
+
+#include "t_api.h"
+#include "cdefs.h"
+
+static const char *Usage =
+ "\t-a : run all tests\n"
+ "\t-b <dir> : chdir to dir before running tests"
+ "\t-c <config_file> : use specified config file\n"
+ "\t-d <debug_level> : set debug level to debug_level\n"
+ "\t-h : print test info\n"
+ "\t-u : print usage info\n"
+ "\t-n <test_name> : run specified test name\n"
+ "\t-t <test_number> : run specified test number\n"
+ "\t-x : don't execute tests in a subproc\n"
+ "\t-q <timeout> : use 'timeout' as the timeout value\n";
+/*!<
+ * -a --> run all tests
+ * -b dir --> chdir to dir before running tests
+ * -c config --> use config file 'config'
+ * -d --> turn on api debugging
+ * -h --> print out available test names
+ * -u --> print usage info
+ * -n name --> run test named name
+ * -tn --> run test n
+ * -x --> don't execute testcases in a subproc
+ * -q timeout --> use 'timeout' as the timeout value
+ */
+
+#define T_MAXTESTS 256 /*% must be 0 mod 8 */
+#define T_MAXENV 256
+#define T_DEFAULT_CONFIG "t_config"
+#define T_BUFSIZ 256
+#define T_BIGBUF 4096
+
+#define T_TCTOUT 60
+
+int T_debug;
+int T_timeout;
+pid_t T_pid;
+static const char * T_config;
+static char T_tvec[T_MAXTESTS / 8];
+static char * T_env[T_MAXENV + 1];
+static char T_buf[T_BIGBUF];
+static char * T_dir;
+
+static int
+t_initconf(const char *path);
+
+static int
+t_dumpconf(const char *path);
+
+static int
+t_putinfo(const char *key, const char *info);
+
+static char *
+t_getdate(char *buf, size_t buflen);
+
+static void
+printhelp(void);
+
+static void
+printusage(void);
+
+static int T_int;
+
+static void
+t_sighandler(int sig) {
+ T_int = sig;
+}
+
+int
+main(int argc, char **argv) {
+ int c;
+ int tnum;
+ int subprocs;
+ pid_t deadpid;
+ int status;
+ int len;
+ isc_boolean_t first;
+ testspec_t *pts;
+ struct sigaction sa;
+
+#ifdef BIND_SUPPORT
+ isc_mem_debugging = ISC_MEM_DEBUGRECORD;
+#endif /* BIND_SUPPORT */
+ first = ISC_TRUE;
+ subprocs = 1;
+ T_timeout = T_TCTOUT;
+
+ /*
+ * -a option is now default.
+ */
+ memset(T_tvec, 0xffff, sizeof(T_tvec));
+
+ /*
+ * Parse args.
+ */
+ while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:"))
+ != -1) {
+ if (c == 'a') {
+ /*
+ * Flag all tests to be run.
+ */
+ memset(T_tvec, 0xffff, sizeof(T_tvec));
+ }
+ else if (c == 'b') {
+ T_dir = isc_commandline_argument;
+ }
+ else if (c == 't') {
+ tnum = atoi(isc_commandline_argument);
+ if ((tnum > 0) && (tnum < T_MAXTESTS)) {
+ if (first) {
+ /*
+ * Turn off effect of -a default
+ * and allow multiple -t and -n
+ * options.
+ */
+ memset(T_tvec, 0, sizeof(T_tvec));
+ first = ISC_FALSE;
+ }
+ /*
+ * Flag test tnum to be run.
+ */
+ tnum -= 1;
+ T_tvec[tnum / 8] |= (0x01 << (tnum % 8));
+ }
+ }
+ else if (c == 'c') {
+ T_config = isc_commandline_argument;
+ }
+ else if (c == 'd') {
+ T_debug = atoi(isc_commandline_argument);
+ }
+ else if (c == 'n') {
+ pts = &T_testlist[0];
+ tnum = 0;
+ while (pts->pfv != NULL) {
+ if (! strcmp(pts->func_name,
+ isc_commandline_argument)) {
+ if (first) {
+ memset(T_tvec, 0,
+ sizeof(T_tvec));
+ first = ISC_FALSE;
+ }
+ T_tvec[tnum/8] |= (0x01 << (tnum%8));
+ break;
+ }
+ ++pts;
+ ++tnum;
+ }
+ if (pts->pfv == NULL) {
+ fprintf(stderr, "no such test %s\n",
+ isc_commandline_argument);
+ exit(1);
+ }
+ }
+ else if (c == 'h') {
+ printhelp();
+ exit(0);
+ }
+ else if (c == 'u') {
+ printusage();
+ exit(0);
+ }
+ else if (c == 'x') {
+ subprocs = 0;
+ }
+ else if (c == 'q') {
+ T_timeout = atoi(isc_commandline_argument);
+ }
+ else if (c == ':') {
+ fprintf(stderr, "Option -%c requires an argument\n",
+ isc_commandline_option);
+ exit(1);
+ }
+ else if (c == '?') {
+ fprintf(stderr, "Unrecognized option -%c\n",
+ isc_commandline_option);
+ exit(1);
+ }
+ }
+
+ /*
+ * Set cwd.
+ */
+
+ if (T_dir != NULL)
+ IGNORE_RET (chdir(T_dir));
+
+ /*
+ * We don't want buffered output.
+ */
+
+ (void)setbuf(stdout, NULL);
+ (void)setbuf(stderr, NULL);
+
+ /*
+ * Setup signals.
+ */
+
+ sa.sa_flags = 0;
+ sigfillset(&sa.sa_mask);
+
+#ifdef SIGCHLD
+ /*
+ * This is mostly here for NetBSD's pthread implementation, until
+ * people catch up to the latest unproven-pthread package.
+ */
+ sa.sa_handler = SIG_DFL;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+#endif
+
+ sa.sa_handler = t_sighandler;
+ (void)sigaction(SIGINT, &sa, NULL);
+ (void)sigaction(SIGALRM, &sa, NULL);
+
+ /*
+ * Output start stanza to journal.
+ */
+
+ snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]);
+ len = strlen(T_buf);
+ (void) t_getdate(T_buf + len, T_BIGBUF - len);
+ t_putinfo("S", T_buf);
+
+ /*
+ * Setup the test environment using the config file.
+ */
+
+ if (T_config == NULL)
+ T_config = T_DEFAULT_CONFIG;
+
+ t_initconf(T_config);
+ if (T_debug)
+ t_dumpconf(T_config);
+
+ /*
+ * Now invoke all the test cases.
+ */
+
+ tnum = 0;
+ pts = &T_testlist[0];
+ while (*pts->pfv != NULL) {
+ if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) {
+ if (subprocs) {
+ T_pid = fork();
+ if (T_pid == 0) {
+ (*pts->pfv)();
+ exit(0);
+ } else if (T_pid > 0) {
+
+ T_int = 0;
+ sa.sa_handler = t_sighandler;
+ (void)sigaction(SIGALRM, &sa, NULL);
+ alarm(T_timeout);
+
+ deadpid = (pid_t) -1;
+ while (deadpid != T_pid) {
+ deadpid =
+ waitpid(T_pid, &status, 0);
+ if (deadpid == T_pid) {
+ if (WIFSIGNALED(status)) {
+ if (WTERMSIG(status) ==
+ SIGTERM)
+ t_info(
+ "the test case timed out\n");
+ else
+ t_info(
+ "the test case caused exception %d\n",
+ WTERMSIG(status));
+ t_result(T_UNRESOLVED);
+ }
+ } else if ((deadpid == -1) &&
+ (errno == EINTR) &&
+ T_int) {
+ kill(T_pid, SIGTERM);
+ T_int = 0;
+ }
+ else if ((deadpid == -1) &&
+ ((errno == ECHILD) ||
+ (errno == ESRCH)))
+ break;
+ }
+
+ alarm(0);
+ sa.sa_handler = SIG_IGN;
+ (void)sigaction(SIGALRM, &sa, NULL);
+ } else {
+ t_info("fork failed, errno == %d\n",
+ errno);
+ t_result(T_UNRESOLVED);
+ }
+ }
+ else {
+ (*pts->pfv)();
+ }
+ }
+ ++pts;
+ ++tnum;
+ }
+
+ snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]);
+ len = strlen(T_buf);
+ (void) t_getdate(T_buf + len, T_BIGBUF - len);
+ t_putinfo("E", T_buf);
+
+ return(0);
+}
+
+void
+t_assert(const char *component, int anum, int class, const char *what, ...) {
+ va_list args;
+
+ (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ?
+ "A" : "C");
+
+ /*
+ * Format text to a buffer.
+ */
+ va_start(args, what);
+ (void)vsnprintf(T_buf, sizeof(T_buf), what, args);
+ va_end(args);
+
+ (void)t_putinfo("A", T_buf);
+ (void)printf("\n");
+}
+
+void
+t_info(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ (void) vsnprintf(T_buf, sizeof(T_buf), format, args);
+ va_end(args);
+ (void) t_putinfo("I", T_buf);
+}
+
+void
+t_result(int result) {
+ const char *p;
+
+ switch (result) {
+ case T_PASS:
+ p = "PASS";
+ break;
+ case T_FAIL:
+ p = "FAIL";
+ break;
+ case T_UNRESOLVED:
+ p = "UNRESOLVED";
+ break;
+ case T_UNSUPPORTED:
+ p = "UNSUPPORTED";
+ break;
+ case T_UNTESTED:
+ p = "UNTESTED";
+ break;
+ case T_THREADONLY:
+ p = "THREADONLY";
+ break;
+ default:
+ p = "UNKNOWN";
+ break;
+ }
+ printf("R:%s\n", p);
+}
+
+char *
+t_getenv(const char *name) {
+ char *n;
+ char **p;
+ size_t len;
+
+ n = NULL;
+ if (name && *name) {
+
+ p = &T_env[0];
+ len = strlen(name);
+
+ while (*p != NULL) {
+ if (strncmp(*p, name, len) == 0) {
+ if ( *(*p + len) == '=') {
+ n = *p + len + 1;
+ break;
+ }
+ }
+ ++p;
+ }
+ }
+ return(n);
+}
+
+/*
+ *
+ * Read in the config file at path, initializing T_env.
+ *
+ * note: no format checking for now ...
+ *
+ */
+
+static int
+t_initconf(const char *path) {
+
+ int n;
+ int rval;
+ char **p;
+ FILE *fp;
+
+ rval = -1;
+
+ fp = fopen(path, "r");
+ if (fp != NULL) {
+ n = 0;
+ p = &T_env[0];
+ while (n < T_MAXENV) {
+ *p = t_fgetbs(fp);
+ if (*p == NULL)
+ break;
+ if ((**p == '#') || (strchr(*p, '=') == NULL)) {
+ /*
+ * Skip comments and other junk.
+ */
+ (void)free(*p);
+ continue;
+ }
+ ++p; ++n;
+ }
+ (void)fclose(fp);
+ rval = 0;
+ }
+
+ return (rval);
+}
+
+/*
+ *
+ * Dump T_env to stdout.
+ *
+ */
+
+static int
+t_dumpconf(const char *path) {
+ int rval;
+ char **p;
+ FILE *fp;
+
+ rval = -1;
+ fp = fopen(path, "r");
+ if (fp != NULL) {
+ p = &T_env[0];
+ while (*p != NULL) {
+ printf("C:%s\n", *p);
+ ++p;
+ }
+ (void) fclose(fp);
+ rval = 0;
+ }
+ return(rval);
+}
+
+/*
+ *
+ * Read a newline or EOF terminated string from fp.
+ * On success:
+ * return a malloc'd buf containing the string with
+ * the newline converted to a '\0'.
+ * On error:
+ * return NULL.
+ *
+ * Caller is responsible for freeing buf.
+ *
+ */
+
+char *
+t_fgetbs(FILE *fp) {
+ int c;
+ size_t n;
+ size_t size;
+ char *buf;
+ char *p;
+
+ n = 0;
+ size = T_BUFSIZ;
+ buf = (char *) malloc(T_BUFSIZ * sizeof(char));
+
+ if (buf != NULL) {
+ p = buf;
+ while ((c = fgetc(fp)) != EOF) {
+
+ if (c == '\n')
+ break;
+
+ *p++ = c;
+ ++n;
+ if ( n >= size ) {
+ size += T_BUFSIZ;
+ buf = (char *)realloc(buf,
+ size * sizeof(char));
+ if (buf == NULL)
+ break;
+ p = buf + n;
+ }
+ }
+ *p = '\0';
+ if (c == EOF && n == 0U) {
+ free(buf);
+ return (NULL);
+ }
+ return (buf);
+ } else {
+ fprintf(stderr, "malloc failed %d", errno);
+ return(NULL);
+ }
+}
+
+/*
+ *
+ * Put info to log, using key.
+ * For now, just dump it out.
+ * Later format into pretty lines.
+ *
+ */
+
+static int
+t_putinfo(const char *key, const char *info) {
+ int rval;
+
+ /*
+ * For now.
+ */
+ rval = printf("%s:%s", key, info);
+ return(rval);
+}
+
+static char *
+t_getdate(char *buf, size_t buflen) {
+ size_t n;
+ time_t t;
+ struct tm *p;
+
+ t = time(NULL);
+ p = localtime(&t);
+ n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p);
+ return(n != 0U ? buf : NULL);
+}
+
+/*
+ * Some generally used utilities.
+ */
+#ifdef DNS_SUPPORT
+struct dns_errormap {
+ isc_result_t result;
+ const char *text;
+} dns_errormap[] = {
+ { ISC_R_SUCCESS, "ISC_R_SUCCESS" },
+ { ISC_R_EXISTS, "ISC_R_EXISTS" },
+ { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" },
+ { ISC_R_NOSPACE, "ISC_R_NOSPACE" },
+ { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" },
+ { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" },
+ { ISC_R_RANGE, "ISC_R_RANGE" },
+ { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" },
+ { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" },
+ /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */
+ /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */
+ { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" },
+ { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" },
+ { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" },
+ { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" },
+ { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" },
+ { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" },
+ { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" },
+ { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" },
+ { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" },
+ { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" },
+ { DNS_R_SYNTAX, "DNS_R_SYNTAX" },
+ { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" },
+ { DNS_R_BADAAAA, "DNS_R_BADAAAA" },
+ { DNS_R_NOOWNER, "DNS_R_NOOWNER" },
+ { DNS_R_NOTTL, "DNS_R_NOTTL" },
+ { DNS_R_BADCLASS, "DNS_R_BADCLASS" },
+ { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" },
+ { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" },
+ { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" },
+ { DNS_R_BADTTL, "DNS_R_BADTTL" },
+ { DNS_R_NOREDATA, "DNS_R_NOREDATA" },
+ { DNS_R_CONTINUE, "DNS_R_CONTINUE" },
+ { DNS_R_DELEGATION, "DNS_R_DELEGATION" },
+ { DNS_R_GLUE, "DNS_R_GLUE" },
+ { DNS_R_DNAME, "DNS_R_DNAME" },
+ { DNS_R_CNAME, "DNS_R_CNAME" },
+ { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" },
+ { DNS_R_NXRRSET, "DNS_R_NXRRSET" },
+ { DNS_R_BADDB, "DNS_R_BADDB" },
+ { DNS_R_ZONECUT, "DNS_R_ZONECUT" },
+ { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" },
+ { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" },
+ { DNS_R_SINGLETON, "DNS_R_SINGLETON" },
+ { (isc_result_t)0, NULL }
+};
+
+isc_result_t
+t_dns_result_fromtext(char *name) {
+
+ isc_result_t result;
+ struct dns_errormap *pmap;
+
+ result = ISC_R_UNEXPECTED;
+
+ pmap = dns_errormap;
+ while (pmap->text != NULL) {
+ if (strcmp(name, pmap->text) == 0)
+ break;
+ ++pmap;
+ }
+
+ if (pmap->text != NULL)
+ result = pmap->result;
+
+ return (result);
+}
+
+struct dc_method_map {
+ unsigned int dc_method;
+ const char *text;
+} dc_method_map[] = {
+
+ { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" },
+ { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" },
+ { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" },
+ { 0, NULL }
+};
+
+unsigned int
+t_dc_method_fromtext(char *name) {
+ unsigned int dc_method;
+ struct dc_method_map *pmap;
+
+ dc_method = DNS_COMPRESS_NONE;
+
+ pmap = dc_method_map;
+ while (pmap->text != NULL) {
+ if (strcmp(name, pmap->text) == 0)
+ break;
+ ++pmap;
+ }
+
+ if (pmap->text != NULL)
+ dc_method = pmap->dc_method;
+
+ return(dc_method);
+}
+#endif /* DNS_SUPPORT */
+
+int
+t_bustline(char *line, char **toks) {
+ int cnt;
+ char *p;
+
+ cnt = 0;
+ if (line && *line) {
+ while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) {
+ *toks++ = p;
+ line = NULL;
+ ++cnt;
+ }
+ }
+ return(cnt);
+}
+
+static void
+printhelp(void) {
+ int cnt;
+ testspec_t *pts;
+
+ cnt = 1;
+ pts = &T_testlist[0];
+
+ printf("Available tests:\n");
+ while (pts->func_name) {
+ printf("\t%d\t%s\n", cnt, pts->func_name);
+ ++pts;
+ ++cnt;
+ }
+}
+
+static void
+printusage(void) {
+ printf("Usage:\n%s\n", Usage);
+}
+
+int
+t_eval(const char *filename, int (*func)(char **), int nargs) {
+ FILE *fp;
+ char *p;
+ int line;
+ int cnt;
+ int result;
+ int nfails;
+ int nprobs;
+ int npass;
+ char *tokens[T_MAXTOKS + 1];
+
+ npass = 0;
+ nfails = 0;
+ nprobs = 0;
+
+ fp = fopen(filename, "r");
+ if (fp != NULL) {
+ line = 0;
+ while ((p = t_fgetbs(fp)) != NULL) {
+
+ ++line;
+
+ /*
+ * Skip comment lines.
+ */
+ if ((isspace((unsigned char)*p)) || (*p == '#')) {
+ (void)free(p);
+ continue;
+ }
+
+ cnt = t_bustline(p, tokens);
+ if (cnt == nargs) {
+ result = func(tokens);
+ switch (result) {
+ case T_PASS:
+ ++npass;
+ break;
+ case T_FAIL:
+ ++nfails;
+ break;
+ case T_UNTESTED:
+ break;
+ default:
+ ++nprobs;
+ break;
+ }
+ } else {
+ t_info("bad format in %s at line %d\n",
+ filename, line);
+ ++nprobs;
+ }
+
+ (void)free(p);
+ }
+ (void)fclose(fp);
+ } else {
+ t_info("Missing datafile %s\n", filename);
+ ++nprobs;
+ }
+
+ result = T_UNRESOLVED;
+
+ if (nfails == 0 && nprobs == 0 && npass > 0)
+ result = T_PASS;
+ else if (nfails > 0)
+ result = T_FAIL;
+ else if (npass == 0)
+ result = T_UNTESTED;
+
+ return (result);
+}
diff --git a/tests/t_api_dhcp.c b/tests/t_api_dhcp.c
new file mode 100644
index 0000000..9ef221a
--- /dev/null
+++ b/tests/t_api_dhcp.c
@@ -0,0 +1,44 @@
+/*
+ * We have to have a number of symbols defined in order to build a
+ * DHCP program.
+ */
+
+#include <config.h>
+#include "dhcpd.h"
+
+void
+bootp(struct packet *packet) {
+}
+
+void
+dhcp(struct packet *packet) {
+}
+
+void
+dhcpv6(struct packet *packet) {
+}
+
+isc_result_t
+dhcp_set_control_state(control_object_state_t old, control_object_state_t new) {
+ return ISC_R_NOTIMPLEMENTED;
+}
+
+int
+check_collection(struct packet *p, struct lease *l, struct collection *c) {
+ return 0;
+}
+
+void
+classify (struct packet *p, struct class *c) {
+}
+
+isc_result_t
+find_class(struct class **class, const char *c1, const char *c2, int i) {
+ return ISC_R_NOTFOUND;
+}
+
+int
+parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
+ return 0;
+}
+
diff --git a/tests/unit_test_sample.c b/tests/unit_test_sample.c
new file mode 100644
index 0000000..86d6336
--- /dev/null
+++ b/tests/unit_test_sample.c
@@ -0,0 +1,25 @@
+#include "config.h"
+#include "t_api.h"
+
+static void foo(void);
+
+/*
+ * T_testlist is a list of tests that are invoked.
+ */
+testspec_t T_testlist[] = {
+ { foo, "sample test" },
+ { NULL, NULL }
+};
+
+static void
+foo(void) {
+ static const char *test_desc =
+ "this is an example test, for no actual module";
+
+ t_assert("sample", 1, T_REQUIRED, test_desc);
+
+ /* ... */ /* Test code would go here. */
+
+ t_result(T_PASS);
+}
+
diff --git a/util/bindvar.sh b/util/bindvar.sh
new file mode 100644
index 0000000..71170c4
--- /dev/null
+++ b/util/bindvar.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: bindvar.sh,v 1.2.2.1 2009-12-02 22:35:03 sar Exp $
+
+# Create a file with the base directory and gmake pat for
+# use by the bind/Makefile, we do this to minimize portability
+# concerns.
+
+binddir=`pwd`
+gmake=
+for x in gmake gnumake make; do
+ if $x --version 2>/dev/null | grep GNU > /dev/null; then
+ gmake=$x
+ break;
+ fi
+done
+
+cat <<EOF > bind/bindvar.tmp
+binddir=$binddir/bind
+GMAKE=$gmake
+EOF