aboutsummaryrefslogtreecommitdiff
path: root/contrib/isc-dhcp/common
diff options
context:
space:
mode:
authorMurray Stokely <murray@FreeBSD.org>2002-02-19 11:04:34 +0000
committerMurray Stokely <murray@FreeBSD.org>2002-02-19 11:04:34 +0000
commitce99b771f886a2c842db7aa803c9a5a5918f42c8 (patch)
tree229464d9b3244ab78e2784c9a0a1f78de317089a /contrib/isc-dhcp/common
parent7657fb140fbd218ea326d55bd3c43c4077f03d9a (diff)
downloadsrc-ce99b771f886a2c842db7aa803c9a5a5918f42c8.tar.gz
src-ce99b771f886a2c842db7aa803c9a5a5918f42c8.zip
Import ISC DHCP 3.0.1 RC6 client.
Notes
Notes: svn path=/vendor/isc-dhcp/dist/; revision=90908
Diffstat (limited to 'contrib/isc-dhcp/common')
-rw-r--r--contrib/isc-dhcp/common/Makefile.dist195
-rw-r--r--contrib/isc-dhcp/common/alloc.c1338
-rw-r--r--contrib/isc-dhcp/common/bpf.c227
-rw-r--r--contrib/isc-dhcp/common/comapi.c958
-rw-r--r--contrib/isc-dhcp/common/conflex.c876
-rw-r--r--contrib/isc-dhcp/common/ctrace.c297
-rw-r--r--contrib/isc-dhcp/common/dhcp-eval.5484
-rw-r--r--contrib/isc-dhcp/common/dhcp-options.51359
-rw-r--r--contrib/isc-dhcp/common/discover.c1138
-rw-r--r--contrib/isc-dhcp/common/dispatch.c796
-rw-r--r--contrib/isc-dhcp/common/dlpi.c1345
-rw-r--r--contrib/isc-dhcp/common/dns.c943
-rw-r--r--contrib/isc-dhcp/common/ethernet.c45
-rw-r--r--contrib/isc-dhcp/common/execute.c1061
-rw-r--r--contrib/isc-dhcp/common/fddi.c106
-rw-r--r--contrib/isc-dhcp/common/icmp.c270
-rw-r--r--contrib/isc-dhcp/common/inet.c73
-rw-r--r--contrib/isc-dhcp/common/lpf.c203
-rw-r--r--contrib/isc-dhcp/common/memory.c996
-rw-r--r--contrib/isc-dhcp/common/nit.c160
-rw-r--r--contrib/isc-dhcp/common/options.c2106
-rw-r--r--contrib/isc-dhcp/common/packet.c121
-rw-r--r--contrib/isc-dhcp/common/parse.c4558
-rw-r--r--contrib/isc-dhcp/common/print.c1281
-rw-r--r--contrib/isc-dhcp/common/raw.c92
-rw-r--r--contrib/isc-dhcp/common/resolv.c212
-rw-r--r--contrib/isc-dhcp/common/socket.c152
-rw-r--r--contrib/isc-dhcp/common/tables.c1117
-rw-r--r--contrib/isc-dhcp/common/tr.c65
-rw-r--r--contrib/isc-dhcp/common/tree.c4195
-rw-r--r--contrib/isc-dhcp/common/upf.c142
31 files changed, 23007 insertions, 3904 deletions
diff --git a/contrib/isc-dhcp/common/Makefile.dist b/contrib/isc-dhcp/common/Makefile.dist
index 1e3e062cd775..3efa1205071a 100644
--- a/contrib/isc-dhcp/common/Makefile.dist
+++ b/contrib/isc-dhcp/common/Makefile.dist
@@ -1,49 +1,18 @@
# Makefile.dist
-#
-# Copyright (c) 1996, 1999 The Internet Software Consortium.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-# 3. Neither the name of The Internet Software Consortium nor the names of its
-# contributors may be used to endorse or promote products derived
-# from this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
-# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
-# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-# THE INTERNET SOFTWARE CONSORTIUM OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-# OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-CATMANPAGES = dhcp-options.cat5
-SEDMANPAGES = dhcp-options.man5
+CATMANPAGES = dhcp-options.cat5 dhcp-eval.cat5
+SEDMANPAGES = dhcp-options.man5 dhcp-eval.man5
SRC = raw.c parse.c nit.c icmp.c dispatch.c conflex.c upf.c bpf.c socket.c \
- lpf.c packet.c memory.c print.c options.c inet.c convert.c \
- tree.c tables.c hash.c alloc.c errwarn.c inet_addr.c dlpi.c \
- tr.c ethernet.c
+ lpf.c dlpi.c packet.c tr.c ethernet.c memory.c print.c options.c \
+ inet.c tree.c tables.c alloc.c fddi.c ctrace.c \
+ dns.c resolv.c execute.c discover.c comapi.c
OBJ = raw.o parse.o nit.o icmp.o dispatch.o conflex.o upf.o bpf.o socket.o \
- lpf.o packet.o memory.o print.o options.o inet.o convert.o \
- tree.o tables.o hash.o alloc.o errwarn.o inet_addr.o dlpi.o \
- tr.o ethernet.o
-MAN = dhcp-options.5
+ lpf.o dlpi.o packet.o tr.o ethernet.o memory.o print.o options.o \
+ inet.o tree.o tables.o alloc.o fddi.o ctrace.o \
+ dns.o resolv.o execute.o discover.o comapi.o
+MAN = dhcp-options.5 dhcp-eval.5
-DEBUG = -g
-INCLUDES = -I.. -I../includes
+INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes
CFLAGS = $(DEBUG) $(PREDEFINES) $(INCLUDES) $(COPTS)
all: libdhcp.a $(CATMANPAGES)
@@ -51,7 +20,7 @@ all: libdhcp.a $(CATMANPAGES)
libdhcp.a: $(OBJ)
rm -f libdhcp.a
ar cruv libdhcp.a $(OBJ)
- ranlib libdhcp.a
+ $(RANLIB) libdhcp.a
install: all
for dir in $(FFMANDIR); do \
@@ -66,17 +35,29 @@ install: all
done
$(MANINSTALL) $(MANFROM) dhcp-options.$(MANCAT)5 $(MANTO) \
$(DESTDIR)$(FFMANDIR)/dhcp-options$(FFMANEXT)
+ $(MANINSTALL) $(MANFROM) dhcp-eval.$(MANCAT)5 $(MANTO) \
+ $(DESTDIR)$(FFMANDIR)/dhcp-eval$(FFMANEXT)
+depend:
+ $(MKDEP) $(INCLUDES) $(PREDEFINES) $(SRC)
clean:
-rm -f $(OBJ)
-
+
realclean: clean
- -rm -f libdhcp.a *~ #* $(CATMANPAGES) $(SEDMANPAGES)
+ -rm -f libdhcp.a $(CATMANPAGES) $(SEDMANPAGES) *~ #*
distclean: realclean
-rm -f Makefile
+links:
+ @for foo in $(SRC) $(MAN); do \
+ if [ ! -b $$foo ]; then \
+ rm -f $$foo; \
+ fi; \
+ ln -s $(TOP)/common/$$foo $$foo; \
+ done
+
dhcp-options.cat5: dhcp-options.man5
nroff -man dhcp-options.man5 >dhcp-options.cat5
@@ -84,123 +65,11 @@ dhcp-options.man5: dhcp-options.5
sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
-e "s#RUNDIR#$(VARRUN)#g" < dhcp-options.5 >dhcp-options.man5
-# Dependencies (semi-automatically-generated)
+dhcp-eval.cat5: dhcp-eval.man5
+ nroff -man dhcp-eval.man5 >dhcp-eval.cat5
-raw.o: raw.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-parse.o: parse.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h \
- ../includes/dhcp.h ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h ../includes/dhctoken.h
-nit.o: nit.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-icmp.o: icmp.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h ../includes/netinet/ip.h \
- ../includes/netinet/ip_icmp.h
-dispatch.o: dispatch.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h \
- conflex.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h ../includes/dhctoken.h
-upf.o: upf.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-bpf.o: bpf.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h ../includes/netinet/ip.h ../includes/netinet/udp.h \
- ../includes/netinet/if_ether.h
-socket.o: socket.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-lpf.o: lpf.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-packet.o: packet.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h ../includes/netinet/ip.h \
- ../includes/netinet/udp.h ../includes/netinet/if_ether.h
-memory.o: memory.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-print.o: print.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-options.o: options.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-inet.o: inet.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-convert.o: convert.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-tree.o: tree.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-tables.o: tables.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-hash.o: hash.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-alloc.o: alloc.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-errwarn.o: errwarn.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-inet_addr.o: inet_addr.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
-dlpi.o: dlpi.c ../includes/dhcpd.h \
- ../includes/cdefs.h ../includes/osdep.h ../includes/site.h \
- ../includes/cf/netbsd.h ../includes/dhcp.h \
- ../includes/tree.h ../includes/hash.h ../includes/inet.h \
- ../includes/sysconf.h
+dhcp-eval.man5: dhcp-eval.5
+ sed -e "s#ETCDIR#$(ETC)#g" -e "s#DBDIR#$(VARDB)#g" \
+ -e "s#RUNDIR#$(VARRUN)#g" < dhcp-eval.5 >dhcp-eval.man5
+
+# Dependencies (semi-automatically-generated)
diff --git a/contrib/isc-dhcp/common/alloc.c b/contrib/isc-dhcp/common/alloc.c
index 61d7572626ab..5ed0204e596c 100644
--- a/contrib/isc-dhcp/common/alloc.c
+++ b/contrib/isc-dhcp/common/alloc.c
@@ -3,7 +3,7 @@
Memory allocation... */
/*
- * Copyright (c) 1995, 1996, 1998 The Internet Software Consortium.
+ * Copyright (c) 1996-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,299 +34,1293 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: alloc.c,v 1.13.2.2 1999/03/26 16:39:36 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: alloc.c,v 1.53.2.8 2001/10/18 20:30:02 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
+#include <omapip/omapip_p.h>
struct dhcp_packet *dhcp_free_list;
struct packet *packet_free_list;
-VOIDPTR dmalloc (size, name)
+int option_chain_head_allocate (ptr, file, line)
+ struct option_chain_head **ptr;
+ const char *file;
+ int line;
+{
int size;
- char *name;
+ 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;
{
- VOIDPTR foo = (VOIDPTR)malloc (size);
- if (!foo)
- warn ("No memory for %s.", name);
- else
- memset (foo, 0, size);
- return foo;
+ 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;
+{
+ int i;
+ 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;
}
-void dfree (ptr, name)
- VOIDPTR ptr;
- char *name;
+int group_allocate (ptr, file, line)
+ struct group **ptr;
+ const char *file;
+ int line;
{
+ int size;
+ struct group *g;
+
if (!ptr) {
- warn ("dfree %s: free on null pointer.", name);
- return;
+ 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
}
- free (ptr);
+
+ g = dmalloc (sizeof *g, file, line);
+ if (g) {
+ memset (g, 0, sizeof *g);
+ return group_reference (ptr, g, file, line);
+ }
+ return 0;
}
-struct packet *new_packet (name)
- char *name;
+int group_reference (ptr, bp, file, line)
+ struct group **ptr;
+ struct group *bp;
+ const char *file;
+ int line;
{
- struct packet *rval;
- rval = (struct packet *)dmalloc (sizeof (struct packet), name);
- return rval;
+ 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;
+{
+ int i;
+ 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 (name)
- char *name;
+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),
- name);
+ 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 ((VOIDPTR)ptr, file, line);
+}
+
+struct option *new_option (file, line)
+ const char *file;
+ int line;
+{
+ struct option *rval =
+ dmalloc (sizeof (struct option), file, line);
+ if (rval)
+ memset (rval, 0, sizeof *rval);
return rval;
}
-struct tree *new_tree (name)
- char *name;
+void free_option (ptr, file, line)
+ struct option *ptr;
+ const char *file;
+ int line;
+{
+/* XXX have to put all options on heap before this is possible. */
+#if 0
+ if (ptr -> name)
+ dfree ((VOIDPTR)option -> name, file, line);
+ dfree ((VOIDPTR)ptr, file, line);
+#endif
+}
+
+struct universe *new_universe (file, line)
+ const char *file;
+ int line;
{
- struct tree *rval = dmalloc (sizeof (struct tree), name);
+ struct universe *rval =
+ dmalloc (sizeof (struct universe), file, line);
return rval;
}
-struct tree_cache *free_tree_caches;
+void free_universe (ptr, file, line)
+ struct universe *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((VOIDPTR)ptr, file, line);
+}
+
+void free_domain_search_list (ptr, file, line)
+ struct domain_search_list *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((VOIDPTR)ptr, file, line);
+}
+
+void free_protocol (ptr, file, line)
+ struct protocol *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((VOIDPTR)ptr, file, line);
+}
+
+void free_dhcp_packet (ptr, file, line)
+ struct dhcp_packet *ptr;
+ const char *file;
+ int line;
+{
+ dfree ((VOIDPTR)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;
-struct tree_cache *new_tree_cache (name)
- char *name;
+pair new_pair (file, line)
+ const char *file;
+ int line;
{
- struct tree_cache *rval;
+ 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;
+}
- if (free_tree_caches) {
- rval = free_tree_caches;
- free_tree_caches =
- (struct tree_cache *)(rval -> value);
+void free_pair (foo, file, line)
+ pair foo;
+ const char *file;
+ int line;
+{
+ foo -> cdr = free_pairs;
+ free_pairs = foo;
+ dmalloc_reuse (free_pairs, (char *)0, 0, 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 tree_cache), name);
+ rval = dmalloc (sizeof (struct expression), file, line);
if (!rval)
- error ("unable to allocate tree cache for %s.", name);
+ return 0;
}
- return rval;
+ memset (rval, 0, sizeof *rval);
+ return expression_reference (cptr, rval, file, line);
}
-struct hash_table *new_hash_table (count, name)
- int count;
- char *name;
+int expression_reference (ptr, src, file, line)
+ struct expression **ptr;
+ struct expression *src;
+ const char *file;
+ int line;
{
- struct hash_table *rval = dmalloc (sizeof (struct hash_table)
- - (DEFAULT_HASH_SIZE
- * sizeof (struct hash_bucket *))
- + (count
- * sizeof (struct hash_bucket *)),
- name);
- rval -> hash_count = count;
- return rval;
+ 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;
}
-struct hash_bucket *new_hash_bucket (name)
- char *name;
+void free_expression (expr, file, line)
+ struct expression *expr;
+ const char *file;
+ int line;
{
- struct hash_bucket *rval = dmalloc (sizeof (struct hash_bucket), name);
- return rval;
+ expr -> data.not = free_expressions;
+ free_expressions = expr;
+ dmalloc_reuse (free_expressions, (char *)0, 0, 0);
}
-struct lease *new_leases (n, name)
- int n;
- char *name;
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_expressions ()
{
- struct lease *rval = dmalloc (n * sizeof (struct lease), name);
- return rval;
+ 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 lease *new_lease (name)
- char *name;
+struct binding_value *free_binding_values;
+
+int binding_value_allocate (cptr, file, line)
+ struct binding_value **cptr;
+ const char *file;
+ int line;
{
- struct lease *rval = dmalloc (sizeof (struct lease), name);
- return rval;
+ 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);
}
-struct subnet *new_subnet (name)
- char *name;
+int binding_value_reference (ptr, src, file, line)
+ struct binding_value **ptr;
+ struct binding_value *src;
+ const char *file;
+ int line;
{
- struct subnet *rval = dmalloc (sizeof (struct subnet), name);
- return rval;
+ 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;
}
-struct class *new_class (name)
- char *name;
+void free_binding_value (bv, file, line)
+ struct binding_value *bv;
+ const char *file;
+ int line;
{
- struct class *rval = dmalloc (sizeof (struct class), name);
- return rval;
+ bv -> value.bv = free_binding_values;
+ free_binding_values = bv;
+ dmalloc_reuse (free_binding_values, (char *)0, 0, 0);
}
-struct shared_network *new_shared_network (name)
- char *name;
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_binding_values ()
{
- struct shared_network *rval =
- dmalloc (sizeof (struct shared_network), name);
- return rval;
+ 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
-struct group *new_group (name)
- char *name;
+int fundef_allocate (cptr, file, line)
+ struct fundef **cptr;
+ const char *file;
+ int line;
{
- struct group *rval =
- dmalloc (sizeof (struct group), name);
- return rval;
+ 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);
}
-struct protocol *new_protocol (name)
- char *name;
+int fundef_reference (ptr, src, file, line)
+ struct fundef **ptr;
+ struct fundef *src;
+ const char *file;
+ int line;
{
- struct protocol *rval = dmalloc (sizeof (struct protocol), name);
- return rval;
+ 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 lease_state *free_lease_states;
+struct option_cache *free_option_caches;
-struct lease_state *new_lease_state (name)
- char *name;
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_option_caches ()
{
- struct lease_state *rval;
+ struct option_cache *o, *n;
- if (free_lease_states) {
- rval = free_lease_states;
- free_lease_states =
- (struct lease_state *)(free_lease_states -> next);
+ 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 lease_state), name);
+ rval = dmalloc (sizeof (struct option_cache), file, line);
+ if (!rval)
+ return 0;
}
- return rval;
+ memset (rval, 0, sizeof *rval);
+ return option_cache_reference (cptr, rval, file, line);
}
-struct domain_search_list *new_domain_search_list (name)
- char *name;
+int option_cache_reference (ptr, src, file, line)
+ struct option_cache **ptr;
+ struct option_cache *src;
+ const char *file;
+ int line;
{
- struct domain_search_list *rval =
- dmalloc (sizeof (struct domain_search_list), name);
- return rval;
+ 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;
}
-struct name_server *new_name_server (name)
- char *name;
+int buffer_allocate (ptr, len, file, line)
+ struct buffer **ptr;
+ unsigned len;
+ const char *file;
+ int line;
{
- struct name_server *rval =
- dmalloc (sizeof (struct name_server), name);
- return rval;
+ struct buffer *bp;
+
+ bp = dmalloc (len + sizeof *bp, file, line);
+ if (!bp)
+ return 0;
+ memset (bp, 0, sizeof *bp);
+ bp -> refcnt = 0;
+ return buffer_reference (ptr, bp, file, line);
}
-void free_name_server (ptr, name)
- struct name_server *ptr;
- char *name;
+int buffer_reference (ptr, bp, file, line)
+ struct buffer **ptr;
+ struct buffer *bp;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_domain_search_list (ptr, name)
- struct domain_search_list *ptr;
- char *name;
+int buffer_dereference (ptr, file, line)
+ struct buffer **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ struct buffer *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): 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;
}
-void free_lease_state (ptr, name)
- struct lease_state *ptr;
- char *name;
+int dns_host_entry_allocate (ptr, hostname, file, line)
+ struct dns_host_entry **ptr;
+ const char *hostname;
+ const char *file;
+ int line;
{
- if (ptr -> prl)
- dfree (ptr -> prl, name);
- ptr -> next = free_lease_states;
- free_lease_states = ptr;
+ 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);
}
-void free_protocol (ptr, name)
- struct protocol *ptr;
- char *name;
+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;
+{
+ struct dns_host_entry *bp;
+
+ 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 (VOIDPTR);
+ *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;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_group (ptr, name)
- struct group *ptr;
- char *name;
+int executable_statement_allocate (ptr, file, line)
+ struct executable_statement **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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);
}
-void free_shared_network (ptr, name)
- struct shared_network *ptr;
- char *name;
+int executable_statement_reference (ptr, bp, file, line)
+ struct executable_statement **ptr;
+ struct executable_statement *bp;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_class (ptr, name)
- struct class *ptr;
- char *name;
+static struct packet *free_packets;
+
+#if defined (DEBUG_MEMORY_LEAKAGE) || \
+ defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void relinquish_free_packets ()
{
- dfree ((VOIDPTR)ptr, name);
+ 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
-void free_subnet (ptr, name)
- struct subnet *ptr;
- char *name;
+int packet_allocate (ptr, file, line)
+ struct packet **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ int size;
+ 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;
}
-void free_lease (ptr, name)
- struct lease *ptr;
- char *name;
+int packet_reference (ptr, bp, file, line)
+ struct packet **ptr;
+ struct packet *bp;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_hash_bucket (ptr, name)
- struct hash_bucket *ptr;
- char *name;
+int packet_dereference (ptr, file, line)
+ struct packet **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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, (char *)0, 0, 0);
+ return 1;
}
-void free_hash_table (ptr, name)
- struct hash_table *ptr;
- char *name;
+int dns_zone_allocate (ptr, file, line)
+ struct dns_zone **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ int size;
+ 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;
}
-void free_tree_cache (ptr, name)
- struct tree_cache *ptr;
- char *name;
+int dns_zone_reference (ptr, bp, file, line)
+ struct dns_zone **ptr;
+ struct dns_zone *bp;
+ const char *file;
+ int line;
{
- ptr -> value = (unsigned char *)free_tree_caches;
- free_tree_caches = ptr;
+ 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;
}
-void free_packet (ptr, name)
- struct packet *ptr;
- char *name;
+int binding_scope_allocate (ptr, file, line)
+ struct binding_scope **ptr;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_dhcp_packet (ptr, name)
- struct dhcp_packet *ptr;
- char *name;
+int binding_scope_reference (ptr, bp, file, line)
+ struct binding_scope **ptr;
+ struct binding_scope *bp;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ 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;
}
-void free_tree (ptr, name)
- struct tree *ptr;
- char *name;
+/* Make a copy of the data in data_string, upping the buffer reference
+ count if there's a buffer. */
+
+void data_string_copy (dest, src, file, line)
+ struct data_string *dest;
+ struct data_string *src;
+ const char *file;
+ int line;
{
- dfree ((VOIDPTR)ptr, name);
+ if (src -> buffer)
+ buffer_reference (&dest -> buffer, src -> buffer, file, line);
+ 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);
+}
+
+/* Make a copy of the data in data_string, upping the buffer reference
+ count if there's a buffer. */
+
+void data_string_truncate (dp, len)
+ struct data_string *dp;
+ int len;
+{
+ if (len < dp -> len) {
+ dp -> terminated = 0;
+ dp -> len = len;
+ }
}
diff --git a/contrib/isc-dhcp/common/bpf.c b/contrib/isc-dhcp/common/bpf.c
index e204dfe2e0f1..49af907dcadb 100644
--- a/contrib/isc-dhcp/common/bpf.c
+++ b/contrib/isc-dhcp/common/bpf.c
@@ -3,8 +3,8 @@
BPF socket interface code, originally contributed by Archie Cobbs. */
/*
- * Copyright (c) 1995, 1996, 1998, 1999
- * The Internet Software Consortium. All rights reserved.
+ * Copyright (c) 1996-2000 Internet Software Consortium.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,16 +33,21 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * This software was contributed to the Internet Software Consortium
+ * by Archie Cobbs, and is now maintained by Ted Lemon in cooperation
+ * with Nominum, Inc. To learn more about the Internet Software
+ * Consortium, see ``http://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''.
+ *
+ * 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.
*/
#ifndef lint
static char copyright[] =
-"$Id: bpf.c,v 1.19.2.10 1999/05/27 17:44:51 mellon Exp $ Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
+"$Id: bpf.c,v 1.48 2001/04/08 21:12:49 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -55,7 +60,6 @@ static char copyright[] =
# else
# include <sys/ioctl.h>
# include <sys/uio.h>
-
# include <net/bpf.h>
# if defined (NEED_OSF_PFILT_HACKS)
# include <net/pfilt.h>
@@ -110,11 +114,11 @@ int if_register_bpf (info)
continue;
} else {
if (!b)
- error ("No bpf devices.%s%s%s",
+ log_fatal ("No bpf devices.%s%s%s",
" Please read the README",
" section for your operating",
" system.");
- error ("Can't find free bpf: %m");
+ log_fatal ("Can't find free bpf: %m");
}
} else {
break;
@@ -123,7 +127,7 @@ int if_register_bpf (info)
/* Set the BPF device to point at this interface. */
if (ioctl (sock, BIOCSETIF, info -> ifp) < 0)
- error ("Can't attach interface %s to bpf device %s: %m",
+ log_fatal ("Can't attach interface %s to bpf device %s: %m",
info -> name, filename);
return sock;
@@ -142,11 +146,32 @@ void if_register_send (info)
info -> wfdesc = info -> rfdesc;
#endif
if (!quiet_interface_discovery)
- note ("Sending on BPF/%s/%s%s%s",
+ 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.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
+ 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 : ""));
@@ -185,8 +210,12 @@ struct bpf_insn dhcp_bpf_filter [] = {
BPF_STMT(BPF_RET+BPF_K, 0),
};
-int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
+#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 */
@@ -200,7 +229,8 @@ struct bpf_insn dhcp_bpf_tr_filter [] = {
int dhcp_bpf_tr_filter_len = (sizeof dhcp_bpf_tr_filter /
sizeof (struct bpf_insn));
-#endif
+#endif /* HAVE_TR_SUPPORT */
+#endif /* USE_LPF_RECEIVE || USE_BPF_RECEIVE */
#if defined (USE_BPF_RECEIVE)
void if_register_receive (info)
@@ -211,50 +241,80 @@ void if_register_receive (info)
u_int32_t addr;
struct bpf_program p;
u_int32_t bits;
+#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)
- error ("Can't get BPF version: %m");
+ log_fatal ("Can't get BPF version: %m");
if (v.bv_major != BPF_MAJOR_VERSION ||
v.bv_minor < BPF_MINOR_VERSION)
- error ("Kernel BPF version out of range - recompile dhcpd!");
+ 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)
- error ("Can't set immediate mode on bpf device: %m");
+ 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)
- error ("Can't set ALLOWCOPYALL: %m");
+ log_fatal ("Can't set ALLOWCOPYALL: %m");
/* Clear all the packet filter mode bits first... */
bits = 0;
if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0)
- error ("Can't clear pfilt bits: %m");
+ 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)
- error ("Can't set ENBATCH|ENCOPYALL|ENBPFHDR: %m");
+ 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)
- error ("Can't get bpf buffer length: %m");
- info -> rbuf = malloc (info -> rbuf_max);
+ log_fatal ("Can't get bpf buffer length: %m");
+ info -> rbuf = dmalloc (info -> rbuf_max, MDL);
if (!info -> rbuf)
- error ("Can't allocate %d bytes for bpf input buffer.");
+ 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...
@@ -263,13 +323,30 @@ void if_register_receive (info)
dhcp_bpf_filter [8].k = ntohs (local_port);
if (ioctl (info -> rfdesc, BIOCSETF, &p) < 0)
- error ("Can't install packet filter program: %m");
+ log_fatal ("Can't install packet filter program: %m");
if (!quiet_interface_discovery)
- note ("Listening on BPF/%s/%s%s%s",
+ log_info ("Listening on BPF/%s/%s%s%s",
info -> name,
- print_hw_addr (info -> hw_address.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
+ 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 : ""));
@@ -286,30 +363,35 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
struct sockaddr_in *to;
struct hardware *hto;
{
- int bufp = 0;
- unsigned char buf [256];
- struct iovec iov [2];
+ 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);
/* Assemble the headers... */
- assemble_hw_header (interface, buf, &bufp, hto);
- assemble_udp_ip_header (interface, buf, &bufp, from.s_addr,
+ 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 *)buf;
- iov [0].iov_len = bufp;
- iov [1].iov_base = (char *)raw;
- iov [1].iov_len = len;
-
- result = writev(interface -> wfdesc, iov, 2);
+ 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)
- warn ("send_packet: %m");
+ log_error ("send_packet: %m");
return result;
}
#endif /* USE_BPF_SEND */
@@ -340,10 +422,16 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
length = read (interface -> rfdesc,
interface -> rbuf,
interface -> rbuf_max);
- if (length <= 0)
+ if (length <= 0) {
+ if (errno == EIO) {
+ dhcp_interface_remove
+ ((omapi_object_t *)interface,
+ (omapi_object_t *)0);
+ }
return length;
+ }
interface -> rbuf_offset = 0;
- interface -> rbuf_len = length;
+ interface -> rbuf_len = BPF_WORDALIGN (length);
}
/* If there isn't room for a whole bpf header, something went
@@ -370,8 +458,9 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
the packet won't fit in the input buffer, all we
can do is drop it. */
if (hdr.bh_caplen != hdr.bh_datalen) {
- interface -> rbuf_offset +=
- hdr.bh_hdrlen = hdr.bh_caplen;
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_hdrlen + hdr.bh_caplen);
continue;
}
@@ -388,7 +477,9 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
physical layer that supports this, but WTH), skip this
packet. */
if (offset < 0) {
- interface -> rbuf_offset += hdr.bh_caplen;
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
continue;
}
interface -> rbuf_offset += offset;
@@ -404,30 +495,37 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
/* If the IP or UDP checksum was bad, skip the packet... */
if (offset < 0) {
- interface -> rbuf_offset += hdr.bh_caplen;
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
continue;
}
- interface -> rbuf_offset += offset;
+ 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 += hdr.bh_caplen;
+ 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,
hdr.bh_caplen);
- interface -> rbuf_offset += hdr.bh_caplen;
+ interface -> rbuf_offset =
+ BPF_WORDALIGN (interface -> rbuf_offset +
+ hdr.bh_caplen);
return hdr.bh_caplen;
} while (!length);
return 0;
}
-int can_unicast_without_arp ()
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
{
return 1;
}
@@ -438,14 +536,25 @@ int can_receive_unicast_unconfigured (ip)
return 1;
}
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
void maybe_setup_fallback ()
{
- struct interface_info *fbi;
- fbi = setup_fallback ();
- if (fbi) {
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
if_register_fallback (fbi);
- add_protocol ("fallback", fallback_interface -> wfdesc,
- fallback_discard, fallback_interface);
+ 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/contrib/isc-dhcp/common/comapi.c b/contrib/isc-dhcp/common/comapi.c
new file mode 100644
index 000000000000..a038903fb283
--- /dev/null
+++ b/contrib/isc-dhcp/common/comapi.c
@@ -0,0 +1,958 @@
+/* omapi.c
+
+ OMAPI object interfaces for the DHCP server. */
+
+/*
+ * Copyright (c) 1999-2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 */
+
+#ifndef lint
+static char copyright[] =
+"$Id: comapi.c,v 1.9.2.5 2001/10/18 20:09:59 mellon Exp $ Copyright (c) 1999-2001 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#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;
+ int foo;
+
+ if (h -> type != dhcp_type_group)
+ return ISC_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 ISC_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 = (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)
+ return status;
+ if (!(parse_executable_statements
+ (&group -> group -> statements, parse, &lose,
+ context_any))) {
+ end_parse (&parse);
+ return ISC_R_BADPARSE;
+ }
+ end_parse (&parse);
+ return ISC_R_SUCCESS;
+ } else
+ return ISC_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 == ISC_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;
+ struct data_string ip_addrs;
+
+ if (h -> type != dhcp_type_group)
+ return ISC_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;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_group)
+ return ISC_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, *t;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_group)
+ return ISC_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 ISC_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 ISC_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 ISC_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 ISC_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 ISC_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 ISC_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 ISC_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;
+ int foo;
+ unsigned long newstate;
+
+ if (h -> type != dhcp_type_control)
+ return ISC_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 == ISC_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;
+ struct data_string ip_addrs;
+
+ if (h -> type != dhcp_type_control)
+ return ISC_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)
+{
+ dhcp_control_object_t *control, *t;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_control)
+ return ISC_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, *t;
+ isc_result_t status;
+ int updatep = 0;
+
+ if (h -> type != dhcp_type_control)
+ return ISC_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 ISC_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;
+ dhcp_control_object_t *control;
+
+ /* 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 ISC_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;
+ int foo;
+
+ if (h -> type != dhcp_type_subnet)
+ return ISC_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 == ISC_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 ISC_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;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_subnet)
+ return ISC_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 ISC_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 ISC_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)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct subnet *subnet;
+
+ /* Can't look up subnets yet. */
+
+ /* If we get to here without finding a subnet, no valid key was
+ specified. */
+ if (!*lp)
+ return ISC_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;
+ int foo;
+
+ if (h -> type != dhcp_type_shared_network)
+ return ISC_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 == ISC_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 ISC_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;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_shared_network)
+ return ISC_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 ISC_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 ISC_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)
+{
+ omapi_value_t *tv = (omapi_value_t *)0;
+ isc_result_t status;
+ struct shared_network *shared_network;
+
+ /* 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 ISC_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/contrib/isc-dhcp/common/conflex.c b/contrib/isc-dhcp/common/conflex.c
index ddb78e60d41e..ac73de54966f 100644
--- a/contrib/isc-dhcp/common/conflex.c
+++ b/contrib/isc-dhcp/common/conflex.c
@@ -3,7 +3,7 @@
Lexical scanner for dhcpd config file... */
/*
- * Copyright (c) 1995, 1996, 1997 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,146 +34,193 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: conflex.c,v 1.29.2.4 1999/04/06 14:58:55 mellon Exp $ Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. All rights reserved.\n";
+"$Id: conflex.c,v 1.92.2.4 2002/01/10 19:35:59 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-#include "dhctoken.h"
#include <ctype.h>
-int lexline;
-int lexchar;
-char *token_line;
-char *prev_line;
-char *cur_line;
-char *tlname;
-int eol_token;
-
-static char line1 [81];
-static char line2 [81];
-static int lpos;
-static int line;
-static int tlpos;
-static int tline;
-static int token;
-static int ugflag;
-static char *tval;
-static char tokbuf [1500];
+static int get_char PROTO ((struct parse *));
+static enum dhcp_token get_token PROTO ((struct parse *));
+static void skip_to_eol PROTO ((struct parse *));
+static enum dhcp_token read_string PROTO ((struct parse *));
+static enum dhcp_token read_number PROTO ((int, struct parse *));
+static enum dhcp_token read_num_or_name PROTO ((int, struct parse *));
+static enum dhcp_token intern PROTO ((char *, enum dhcp_token));
-#ifdef OLD_LEXER
-char comments [4096];
-int comment_index;
-#endif
+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;
+{
+ struct parse *tmp;
+
+ tmp = dmalloc (sizeof (struct parse), MDL);
+ if (!tmp)
+ return ISC_R_NOMEMORY;
+ memset (tmp, 0, sizeof *tmp);
+ tmp -> token = 0;
+ 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 -> warnings_occurred = 0;
+ tmp -> file = file;
+ tmp -> eol_token = eolp;
-static int get_char PROTO ((FILE *));
-static int get_token PROTO ((FILE *));
-static void skip_to_eol PROTO ((FILE *));
-static int read_string PROTO ((FILE *));
-static int read_number PROTO ((int, FILE *));
-static int read_num_or_name PROTO ((int, FILE *));
-static int intern PROTO ((char *, int));
+ tmp -> bufix = 0;
+ tmp -> buflen = buflen;
+ if (inbuf) {
+ tmp -> bufsiz = 0;
+ tmp -> inbuf = inbuf;
+ } else {
+ tmp -> inbuf = dmalloc (8192, MDL);
+ if (!tmp -> inbuf) {
+ dfree (tmp, MDL);
+ return ISC_R_NOMEMORY;
+ }
+ tmp -> bufsiz = 8192;
+ }
-void new_parse (name)
- char *name;
+ *cfile = tmp;
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t end_parse (cfile)
+ struct parse **cfile;
{
- tlname = name;
- lpos = line = 1;
- cur_line = line1;
- prev_line = line2;
- token_line = cur_line;
- cur_line [0] = prev_line [0] = 0;
- warnings_occurred = 0;
+ if ((*cfile) -> bufsiz)
+ dfree ((*cfile) -> inbuf, MDL);
+ dfree (*cfile, MDL);
+ *cfile = (struct parse *)0;
+ return ISC_R_SUCCESS;
}
static int get_char (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- int c = getc (cfile);
- if (!ugflag) {
+ /* My kingdom for WITH... */
+ int c;
+
+ if (cfile -> bufix == cfile -> buflen) {
+ if (cfile -> file != -1) {
+ cfile -> buflen =
+ read (cfile -> file,
+ cfile -> inbuf, cfile -> bufsiz);
+ if (cfile -> buflen == 0) {
+ c = EOF;
+ cfile -> bufix = 0;
+ } else if (cfile -> buflen < 0) {
+ c = EOF;
+ cfile -> bufix = cfile -> buflen = 0;
+ } else {
+ c = cfile -> inbuf [0];
+ cfile -> bufix = 1;
+ }
+ } else
+ c = EOF;
+ } else {
+ c = cfile -> inbuf [cfile -> bufix];
+ cfile -> bufix++;
+ }
+
+ if (!cfile -> ugflag) {
if (c == EOL) {
- if (cur_line == line1) {
- cur_line = line2;
- prev_line = line1;
+ if (cfile -> cur_line == cfile -> line1) {
+ cfile -> cur_line = cfile -> line2;
+ cfile -> prev_line = cfile -> line1;
} else {
- cur_line = line2;
- prev_line = line1;
+ cfile -> cur_line = cfile -> line1;
+ cfile -> prev_line = cfile -> line2;
}
- line++;
- lpos = 1;
- cur_line [0] = 0;
+ cfile -> line++;
+ cfile -> lpos = 1;
+ cfile -> cur_line [0] = 0;
} else if (c != EOF) {
- if (lpos <= 81) {
- cur_line [lpos - 1] = c;
- cur_line [lpos] = 0;
+ if (cfile -> lpos <= 80) {
+ cfile -> cur_line [cfile -> lpos - 1] = c;
+ cfile -> cur_line [cfile -> lpos] = 0;
}
- lpos++;
+ cfile -> lpos++;
}
} else
- ugflag = 0;
+ cfile -> ugflag = 0;
return c;
}
-static int get_token (cfile)
- FILE *cfile;
+static enum dhcp_token get_token (cfile)
+ struct parse *cfile;
{
int c;
- int ttok;
+ enum dhcp_token ttok;
static char tb [2];
int l, p, u;
do {
- l = line;
- p = lpos;
- u = ugflag;
+ l = cfile -> line;
+ p = cfile -> lpos;
+ u = cfile -> ugflag;
c = get_char (cfile);
#ifdef OLD_LEXER
if (c == '\n' && p == 1 && !u
- && comment_index < sizeof comments)
- comments [comment_index++] = '\n';
+ && cfile -> comment_index < sizeof cfile -> comments)
+ cfile -> comments [cfile -> comment_index++] = '\n';
#endif
- if (!(c == '\n' && eol_token) && isascii (c) && isspace (c))
+ if (!(c == '\n' && cfile -> eol_token)
+ && isascii (c) && isspace (c))
continue;
if (c == '#') {
#ifdef OLD_LEXER
- if (comment_index < sizeof comments)
- comments [comment_index++] = '#';
+ if (cfile -> comment_index < sizeof cfile -> comments)
+ cfile -> comments [cfile -> comment_index++] = '#';
#endif
skip_to_eol (cfile);
continue;
}
if (c == '"') {
- lexline = l;
- lexchar = p;
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
ttok = read_string (cfile);
break;
}
if ((isascii (c) && isdigit (c)) || c == '-') {
- lexline = l;
- lexchar = p;
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
ttok = read_number (c, cfile);
break;
} else if (isascii (c) && isalpha (c)) {
- lexline = l;
- lexchar = p;
+ 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 {
- lexline = l;
- lexchar = p;
+ cfile -> lexline = l;
+ cfile -> lexchar = p;
tb [0] = c;
tb [1] = 0;
- tval = tb;
+ cfile -> tval = tb;
+ cfile -> tlen = 1;
ttok = c;
break;
}
@@ -181,56 +228,68 @@ static int get_token (cfile)
return ttok;
}
-int next_token (rval, cfile)
- char **rval;
- FILE *cfile;
+enum dhcp_token next_token (rval, rlen, cfile)
+ const char **rval;
+ unsigned *rlen;
+ struct parse *cfile;
{
int rv;
- if (token) {
- if (lexline != tline)
- token_line = cur_line;
- lexchar = tlpos;
- lexline = tline;
- rv = token;
- token = 0;
+ 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_token (cfile);
- token_line = cur_line;
+ cfile -> token_line = cfile -> cur_line;
}
if (rval)
- *rval = tval;
+ *rval = cfile -> tval;
+ if (rlen)
+ *rlen = cfile -> tlen;
#ifdef DEBUG_TOKENS
- fprintf (stderr, "%s:%d ", tval, rv);
+ fprintf (stderr, "%s:%d ", cfile -> tval, rv);
#endif
return rv;
}
-int peek_token (rval, cfile)
- char **rval;
- FILE *cfile;
+enum dhcp_token peek_token (rval, rlen, cfile)
+ const char **rval;
+ unsigned int *rlen;
+ struct parse *cfile;
{
int x;
- if (!token) {
- tlpos = lexchar;
- tline = lexline;
- token = get_token (cfile);
- if (lexline != tline)
- token_line = prev_line;
- x = lexchar; lexchar = tlpos; tlpos = x;
- x = lexline; lexline = tline; tline = x;
+ if (!cfile -> token) {
+ cfile -> tlpos = cfile -> lexchar;
+ cfile -> tline = cfile -> lexline;
+ cfile -> token = get_token (cfile);
+ 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 = tval;
+ *rval = cfile -> tval;
+ if (rlen)
+ *rlen = cfile -> tlen;
#ifdef DEBUG_TOKENS
- fprintf (stderr, "(%s:%d) ", tval, token);
+ fprintf (stderr, "(%s:%d) ", cfile -> tval, cfile -> token);
#endif
- return token;
+ return cfile -> token;
}
static void skip_to_eol (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
int c;
do {
@@ -238,8 +297,8 @@ static void skip_to_eol (cfile)
if (c == EOF)
return;
#ifdef OLD_LEXER
- if (comment_index < sizeof (comments))
- comments [comment_index++] = c;
+ if (cfile -> comment_index < sizeof (cfile -> comments))
+ comments [cfile -> comment_index++] = c;
#endif
if (c == EOL) {
return;
@@ -247,50 +306,125 @@ static void skip_to_eol (cfile)
} while (1);
}
-static int read_string (cfile)
- FILE *cfile;
+static enum dhcp_token read_string (cfile)
+ struct parse *cfile;
{
int i;
int bs = 0;
int c;
+ int value;
+ int hex;
- for (i = 0; i < sizeof tokbuf; i++) {
+ for (i = 0; i < sizeof cfile -> tokbuf; i++) {
+ again:
c = get_char (cfile);
if (c == EOF) {
- parse_warn ("eof in string constant");
+ parse_warn (cfile, "eof in string constant");
break;
}
- if (bs) {
+ 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;
- tokbuf [i] = c;
- } else if (c == '\\')
+ } 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 <= '9') {
+ 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;
- else if (c == '"')
+ goto again;
+ } else if (c == '"')
break;
else
- tokbuf [i] = c;
+ 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 tokbuf) {
- parse_warn ("string constant larger than internal buffer");
+ if (i == sizeof cfile -> tokbuf) {
+ parse_warn (cfile,
+ "string constant larger than internal buffer");
--i;
}
- tokbuf [i] = 0;
- tval = tokbuf;
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
return STRING;
}
-static int read_number (c, cfile)
+static enum dhcp_token read_number (c, cfile)
int c;
- FILE *cfile;
+ struct parse *cfile;
{
int seenx = 0;
int i = 0;
int token = NUMBER;
- tokbuf [i++] = c;
- for (; i < sizeof tokbuf; i++) {
+ cfile -> tokbuf [i++] = c;
+ for (; i < sizeof cfile -> tokbuf; i++) {
c = get_char (cfile);
if (!seenx && c == 'x') {
seenx = 1;
@@ -302,95 +436,190 @@ static int read_number (c, cfile)
token = NUMBER_OR_NAME;
#endif
} else if (!isascii (c) || !isxdigit (c)) {
- ungetc (c, cfile);
- ugflag = 1;
+ if (c != EOF) {
+ cfile -> bufix--;
+ cfile -> ugflag = 1;
+ }
break;
}
- tokbuf [i] = c;
+ cfile -> tokbuf [i] = c;
}
- if (i == sizeof tokbuf) {
- parse_warn ("numeric token larger than internal buffer");
+ if (i == sizeof cfile -> tokbuf) {
+ parse_warn (cfile,
+ "numeric token larger than internal buffer");
--i;
}
- tokbuf [i] = 0;
- tval = tokbuf;
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
return token;
}
-static int read_num_or_name (c, cfile)
+static enum dhcp_token read_num_or_name (c, cfile)
int c;
- FILE *cfile;
+ struct parse *cfile;
{
int i = 0;
- int rv = NUMBER_OR_NAME;
- tokbuf [i++] = c;
- for (; i < sizeof tokbuf; i++) {
+ 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))) {
- ungetc (c, cfile);
- ugflag = 1;
+ if (c != EOF) {
+ cfile -> bufix--;
+ cfile -> ugflag = 1;
+ }
break;
}
if (!isxdigit (c))
rv = NAME;
- tokbuf [i] = c;
+ cfile -> tokbuf [i] = c;
}
- if (i == sizeof tokbuf) {
- parse_warn ("token larger than internal buffer");
+ if (i == sizeof cfile -> tokbuf) {
+ parse_warn (cfile, "token larger than internal buffer");
--i;
}
- tokbuf [i] = 0;
- tval = tokbuf;
- return intern (tval, rv);
+ cfile -> tokbuf [i] = 0;
+ cfile -> tlen = i;
+ cfile -> tval = cfile -> tokbuf;
+ return intern (cfile -> tval, rv);
}
-static int intern (atom, dfv)
+static enum dhcp_token intern (atom, dfv)
char *atom;
- int dfv;
+ enum dhcp_token dfv;
{
if (!isascii (atom [0]))
return dfv;
switch (tolower (atom [0])) {
+ case '-':
+ if (atom [1] == 0)
+ return MINUS;
+ break;
+
case 'a':
- if (!strcasecmp (atom + 1, "lways-reply-rfc1048"))
- return ALWAYS_REPLY_RFC1048;
+ if (!strncasecmp (atom + 1, "uth", 3)) {
+ if (!strncasecmp (atom + 3, "uthenticat", 10)) {
+ if (!strcasecmp (atom + 13, "ed"))
+ return AUTHENTICATED;
+ if (!strcasecmp (atom + 13, "ion"))
+ return AUTHENTICATION;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "uthoritative"))
+ return AUTHORITATIVE;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "nd"))
+ return AND;
if (!strcasecmp (atom + 1, "ppend"))
return APPEND;
if (!strcasecmp (atom + 1, "llow"))
return ALLOW;
if (!strcasecmp (atom + 1, "lias"))
return ALIAS;
+ if (!strcasecmp (atom + 1, "lgorithm"))
+ return ALGORITHM;
if (!strcasecmp (atom + 1, "bandoned"))
- return ABANDONED;
- if (!strcasecmp (atom + 1, "uthoritative"))
- return AUTHORITATIVE;
+ return TOKEN_ABANDONED;
+ if (!strcasecmp (atom + 1, "dd"))
+ return TOKEN_ADD;
+ if (!strcasecmp (atom + 1, "ll"))
+ return ALL;
+ if (!strcasecmp (atom + 1, "t"))
+ return AT;
+ if (!strcasecmp (atom + 1, "rray"))
+ return ARRAY;
+ if (!strcasecmp (atom + 1, "ddress"))
+ return ADDRESS;
+ if (!strcasecmp (atom + 1, "ctive"))
+ return TOKEN_ACTIVE;
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, "ootp"))
- return BOOTP;
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, "ommit"))
+ return COMMIT;
+ if (!strcasecmp (atom + 1, "ode"))
+ return CODE;
+ if (!strcasecmp (atom + 1, "onfig-option"))
+ return CONFIG_OPTION;
+ if (!strcasecmp (atom + 1, "heck"))
+ return CHECK;
if (!strcasecmp (atom + 1, "lass"))
return CLASS;
+ if (!strcasecmp (atom + 1, "lose"))
+ return TOKEN_CLOSE;
+ if (!strcasecmp (atom + 1, "reate"))
+ return TOKEN_CREATE;
if (!strcasecmp (atom + 1, "iaddr"))
return CIADDR;
- if (!strcasecmp (atom + 1, "lient-identifier"))
- return CLIENT_IDENTIFIER;
- if (!strcasecmp (atom + 1, "lient-hostname"))
- return CLIENT_HOSTNAME;
+ if (!strncasecmp (atom + 1, "lient", 5)) {
+ if (!strcasecmp (atom + 6, "-identifier"))
+ return CLIENT_IDENTIFIER;
+ if (!strcasecmp (atom + 6, "-hostname"))
+ return CLIENT_HOSTNAME;
+ if (!strcasecmp (atom + 6, "-state"))
+ return CLIENT_STATE;
+ if (!strcasecmp (atom + 6, "-updates"))
+ return CLIENT_UPDATES;
+ if (!strcasecmp (atom + 6, "s"))
+ return CLIENTS;
+ }
+ if (!strcasecmp (atom + 1, "oncat"))
+ return CONCAT;
+ if (!strcasecmp (atom + 1, "onnect"))
+ return CONNECT;
+ if (!strcasecmp (atom + 1, "ommunications-interrupted"))
+ return COMMUNICATIONS_INTERRUPTED;
+ if (!strcasecmp (atom + 1, "ltt"))
+ return CLTT;
break;
case 'd':
+ 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 (!strcasecmp (atom + 1, "omain-name"))
+ return DOMAIN_NAME;
+ 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;
@@ -398,31 +627,80 @@ static int intern (atom, dfv)
return DEFAULT_LEASE_TIME;
break;
}
- if (!strncasecmp (atom + 1, "ynamic-bootp", 12)) {
- if (!atom [13])
- return DYNAMIC_BOOTP;
- if (!strcasecmp (atom + 13, "-lease-cutoff"))
- return DYNAMIC_BOOTP_LEASE_CUTOFF;
- if (!strcasecmp (atom + 13, "-lease-length"))
- return DYNAMIC_BOOTP_LEASE_LENGTH;
- break;
+ 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 (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, "thernet"))
return ETHERNET;
if (!strcasecmp (atom + 1, "nds"))
return ENDS;
- if (!strcasecmp (atom + 1, "xpire"))
- return EXPIRE;
+ 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;
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, "ddi"))
return 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 (!strcasecmp (atom + 1, "iaddr"))
@@ -433,26 +711,85 @@ static int intern (atom, dfv)
return GET_LEASE_HOSTNAMES;
break;
case 'h':
+ 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, "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, "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, "nitial-interval"))
return INITIAL_INTERVAL;
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 (!strcasecmp (atom + 1, "nown"))
+ return KNOWN;
+ if (!strcasecmp (atom + 1, "ey"))
+ return KEY;
break;
case 'l':
if (!strcasecmp (atom + 1, "ease"))
return LEASE;
+ if (!strcasecmp (atom + 1, "eased-address"))
+ return LEASED_ADDRESS;
+ if (!strcasecmp (atom + 1, "ease-time"))
+ return LEASE_TIME;
+ 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, "og"))
+ return LOG;
break;
case 'm':
- if (!strcasecmp (atom + 1, "ax-lease-time"))
- return MAX_LEASE_TIME;
+ if (!strncasecmp (atom + 1, "ax", 2)) {
+ if (!atom [3])
+ return TOKEN_MAX;
+ if (!strcasecmp (atom + 3, "-lease-time"))
+ return MAX_LEASE_TIME;
+ 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, "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;
@@ -460,38 +797,121 @@ static int intern (atom, dfv)
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 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, "acket"))
return PACKET;
+ if (!strcasecmp (atom + 1, "ool"))
+ return POOL;
+ 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, "esolution-interrupted"))
+ return RESOLUTION_INTERRUPTED;
if (!strcasecmp (atom + 1, "ange"))
return RANGE;
+ if (!strcasecmp (atom + 1, "ecover"))
+ return RECOVER;
+ if (!strcasecmp (atom + 1, "ecover-done"))
+ return RECOVER_DONE;
+ if (!strcasecmp (atom + 1, "ecover-wait"))
+ return RECOVER_WAIT;
+ if (!strcasecmp (atom + 1, "econtact-interval"))
+ return RECONTACT_INTERVAL;
if (!strcasecmp (atom + 1, "equest"))
return REQUEST;
if (!strcasecmp (atom + 1, "equire"))
return REQUIRE;
+ if (!strcasecmp (atom + 1, "equire"))
+ return REQUIRE;
if (!strcasecmp (atom + 1, "etry"))
return RETRY;
+ if (!strcasecmp (atom + 1, "eturn"))
+ return RETURN;
if (!strcasecmp (atom + 1, "enew"))
return RENEW;
if (!strcasecmp (atom + 1, "ebind"))
@@ -500,30 +920,89 @@ static int intern (atom, dfv)
return REBOOT;
if (!strcasecmp (atom + 1, "eject"))
return REJECT;
+ if (!strcasecmp (atom + 1, "everse"))
+ return REVERSE;
+ if (!strcasecmp (atom + 1, "elease"))
+ return RELEASE;
+ if (!strcasecmp (atom + 1, "efused"))
+ return NS_REFUSED;
+ if (!strcasecmp (atom + 1, "eleased"))
+ return TOKEN_RELEASED;
+ if (!strcasecmp (atom + 1, "eset"))
+ return TOKEN_RESET;
+ if (!strcasecmp (atom + 1, "eserved"))
+ return TOKEN_RESERVED;
+ if (!strcasecmp (atom + 1, "emove"))
+ return REMOVE;
+ if (!strcasecmp (atom + 1, "efresh"))
+ return REFRESH;
break;
case 's':
+ if (!strcasecmp (atom + 1, "tate"))
+ return STATE;
+ if (!strcasecmp (atom + 1, "ecret"))
+ return SECRET;
+ if (!strcasecmp (atom + 1, "ervfail"))
+ return NS_SERVFAIL;
+ if (!strcasecmp (atom + 1, "witch"))
+ return SWITCH;
+ if (!strcasecmp (atom + 1, "igned"))
+ return SIGNED;
+ if (!strcasecmp (atom + 1, "tring"))
+ return STRING_TOKEN;
+ if (!strcasecmp (atom + 1, "uffix"))
+ return SUFFIX;
if (!strcasecmp (atom + 1, "earch"))
return SEARCH;
if (!strcasecmp (atom + 1, "tarts"))
return STARTS;
if (!strcasecmp (atom + 1, "iaddr"))
return SIADDR;
- if (!strcasecmp (atom + 1, "ubnet"))
- return SUBNET;
if (!strcasecmp (atom + 1, "hared-network"))
return SHARED_NETWORK;
+ if (!strcasecmp (atom + 1, "econdary"))
+ return SECONDARY;
if (!strcasecmp (atom + 1, "erver-name"))
return SERVER_NAME;
if (!strcasecmp (atom + 1, "erver-identifier"))
return SERVER_IDENTIFIER;
+ if (!strcasecmp (atom + 1, "erver"))
+ return SERVER;
if (!strcasecmp (atom + 1, "elect-timeout"))
return SELECT_TIMEOUT;
+ if (!strcasecmp (atom + 1, "elect"))
+ return SELECT;
if (!strcasecmp (atom + 1, "end"))
return SEND;
if (!strcasecmp (atom + 1, "cript"))
return SCRIPT;
if (!strcasecmp (atom + 1, "upersede"))
return SUPERSEDE;
+ if (!strncasecmp (atom + 1, "ub", 2)) {
+ if (!strcasecmp (atom + 3, "string"))
+ return SUBSTRING;
+ if (!strcasecmp (atom + 3, "net"))
+ return SUBNET;
+ if (!strcasecmp (atom + 3, "class"))
+ return SUBCLASS;
+ break;
+ }
+ if (!strcasecmp (atom + 1, "pawn"))
+ return SPAWN;
+ if (!strcasecmp (atom + 1, "pace"))
+ return SPACE;
+ if (!strcasecmp (atom + 1, "tatic"))
+ return STATIC;
+ if (!strcasecmp (atom + 1, "plit"))
+ return SPLIT;
+ if (!strcasecmp (atom + 1, "et"))
+ return TOKEN_SET;
+ if (!strcasecmp (atom + 1, "econds"))
+ return SECONDS;
+ if (!strcasecmp (atom + 1, "hutdown"))
+ return SHUTDOWN;
+ if (!strcasecmp (atom + 1, "tartup"))
+ return STARTUP;
break;
case 't':
if (!strcasecmp (atom + 1, "imestamp"))
@@ -532,8 +1011,22 @@ static int intern (atom, dfv)
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;
break;
case 'u':
+ 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;
@@ -544,18 +1037,43 @@ static int intern (atom, dfv)
return USE_LEASE_ADDR_FOR_DEFAULT_ROUTE;
break;
}
- if (!strcasecmp (atom + 1, "id"))
- return UID;
- if (!strcasecmp (atom + 1, "nknown-clients"))
- return UNKNOWN_CLIENTS;
+ 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 AUTHENTICATED;
+ 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;
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, "one"))
+ return ZONE;
break;
}
return dfv;
diff --git a/contrib/isc-dhcp/common/ctrace.c b/contrib/isc-dhcp/common/ctrace.c
new file mode 100644
index 000000000000..bea933cb433c
--- /dev/null
+++ b/contrib/isc-dhcp/common/ctrace.c
@@ -0,0 +1,297 @@
+/* trace.c
+
+ Subroutines that support dhcp tracing... */
+
+/*
+ * Copyright (c) 2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon, as part of a project for Nominum, Inc. To learn more
+ * about the Internet Software Consortium, see http://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);
+ memcpy (&tipkt.primary_address,
+ &ip -> primary_address, sizeof ip -> primary_address);
+ 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);
+ memcpy (&ip -> primary_address, &tipkt -> primary_address,
+ sizeof ip -> primary_address);
+ 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 -> primary_address;
+
+ 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;
+ 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);
+
+ 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/contrib/isc-dhcp/common/dhcp-eval.5 b/contrib/isc-dhcp/common/dhcp-eval.5
new file mode 100644
index 000000000000..cbb77452da2b
--- /dev/null
+++ b/contrib/isc-dhcp/common/dhcp-eval.5
@@ -0,0 +1,484 @@
+.\" dhcp-eval.5
+.\"
+.\" Copyright (c) 1996-2001 Internet Software Consortium.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of The Internet Software Consortium nor the names
+.\" of its contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" This software has been written for the Internet Software Consortium
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about the Internet Software Consortium, see
+.\" ``http://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 dhcp-eval 5
+.SH NAME
+dhcp-eval - ISC DHCP conditional evaluation
+.SH DESCRIPTION
+The Internet Software 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=\fI data-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 boolean-expression-1 \fBand\fI boolean-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\fI boolean-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 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 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 0xff.
+.RE
+.PP
+.I colon-seperated hexadecimal list
+.PP
+.RS 0.25i
+A list of hexadecimal octet values, seperated 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.
+.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 seperated 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
+.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.
+.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: LOGGING
+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
+.B log (\fIpriority\fB, \fIdata-expr\FB)\fR
+.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
+.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\f\R.
+.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-eval(5), dhcpd(8),
+dhclient(8), RFC2132, RFC2131.
+.SH AUTHOR
+The Internet Software Consortium DHCP Distribution was written by Ted
+Lemon under a contract with Vixie Labs. Funding for
+this project was provided through the Internet Software Consortium.
+Information about the Internet Software Consortium can be found at
+.B http://www.isc.org.
diff --git a/contrib/isc-dhcp/common/dhcp-options.5 b/contrib/isc-dhcp/common/dhcp-options.5
index 5e7ca0002493..6f5d6f715234 100644
--- a/contrib/isc-dhcp/common/dhcp-options.5
+++ b/contrib/isc-dhcp/common/dhcp-options.5
@@ -1,8 +1,6 @@
.\" dhcp-options.5
.\"
-.\" Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
-.\" All rights reserved.
-.\"
+.\" Copyright (c) 1996-2001 Internet Software Consortium.
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
@@ -31,10 +29,11 @@
.\" SUCH DAMAGE.
.\"
.\" This software has been written for the Internet Software Consortium
-.\" by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
-.\" Enterprises. To learn more about the Internet Software Consortium,
-.\" see ``http://www.isc.org/isc''. To learn more about Vixie
-.\" Enterprises, see ``http://www.vix.com''.
+.\" by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+.\" To learn more about the Internet Software Consortium, see
+.\" ``http://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 dhcpd-options 5
.SH NAME
dhcp-options - Dynamic Host Configuration Protocol options
@@ -82,94 +81,310 @@ data types specify signed and unsigned 8-bit integers.
Unsigned 8-bit integers are also sometimes referred to as octets.
.PP
The
-.B string
+.B text
data type specifies an NVT ASCII string, which must be
-enclosed in double quotes - for example, to specify a domain-name
+enclosed in double quotes - for example, to specify a root-path
option, the syntax would be
.nf
.sp 1
- option domain-name "isc.org";
+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
+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 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 data-string
+.B string
data type specifies either an NVT ASCII string
enclosed in double quotes, or a series of octets specified in
hexadecimal, seperated by colons. For example:
.nf
.sp 1
- option dhcp-client-identifier "CLIENT-FOO";
+ option dhcp-client-identifier "CLIENT-FOO";
or
- option dhcp-client-identifier 43:4c:49:45:54:2d:46:4f:4f;
+ 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 DHCP OPTIONS
The documentation for the various options mentioned below is taken
-from the latest IETF draft document on DHCP options. Options which
-are not listed by name may be defined by the name option-\fInnn\fR,
-where \fInnn\fI is the decimal number of the option code. These
-options may be followed either by a string, enclosed in quotes, or by
-a series of octets, expressed as two-digit hexadecimal numbers seperated
-by colons. For example:
+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 \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 \fBdhcp-client-identifier\fR \fIstring\fR\fB;\fR
+.RS 0.25i
+.PP
+This option can be used to specify the 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 option-133 "my-option-133-text";
- option option-129 1:54:c9:2b:47;
+
+ option dhcp-client-identifier "\\0foo";
+
+rather than:
+
+ option dhcp-client-identifier "foo";
.fi
+.RE
.PP
-Because dhcpd does not know the format of these undefined option codes,
-no checking is done to ensure the correctness of the entered data.
+.B option \fBdhcp-lease-time\fR \fIuint32\fR\fB;\fR
+.RS 0.25i
.PP
-The standard options are:
+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
-.B option subnet-mask \fIip-address\fR\fB;\fR
+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
-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.
+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 time-offset \fIint32\fR\fB;\fR
+.B option \fBdhcp-message\fR \fItext\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).
+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 routers \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBdhcp-message-type\fR \fIuint8\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.
+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
-.B option time-servers \fIip-address\fR [, \fIip-address\fR...
-]\fB;\fR
+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
.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.
+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 \fBien116-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-];
+.B option \fBdhcp-rebinding-time\fR \fIuint32\fR\fB;\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.
+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 not user configurable.
+.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 not user configurable.
+.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...
@@ -181,68 +396,126 @@ The domain-name-servers option specifies a list of Domain Name System
should be listed in order of preference.
.RE
.PP
-.B option \fBlog-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+.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 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
-The log-server option specifies a list of MIT-LCS UDP log servers
+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.
+.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 \fBcookie-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+.B option \fBimpress-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.
+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 \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBinterface-mtu\fR \fIuint16\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.
+This option specifies the MTU to use on this interface. The minimum
+legal value for the MTU is 68.
.RE
.PP
-.B option \fBimpress-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+.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 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 impress-server option specifies a list of Imagen Impress servers
+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 \fBresource-location-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+.B option \fBlpr-servers\fR \fIip-address \fR [\fB,\fR \fIip-address\fR...
]\fB;\fR
.RS 0.25i
.PP
-This option specifies a list of RFC 887 Resource Location
+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 \fBhost-name\fR \fIstring\fR\fB;\fR
+.B option \fBmask-supplier\fR \fIflag\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 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 \fBboot-size\fR \fIuint16\fR\fB;\fR
+.B option \fBmax-dgram-reassembly\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.
+This option specifies the maximum size datagram that the client
+should be prepared to reassemble. The minimum value legal value is
+576.
.RE
.PP
-.B option \fBmerit-dump\fR \fIstring\fR\fB;\fR
+.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
@@ -251,33 +524,123 @@ path is formatted as a character string consisting of characters from
the NVT ASCII character set.
.RE
.PP
-.B option \fBdomain-name\fR \fIstring\fR\fB;\fR
+.B option \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR \fIip-address\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.
+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 \fBswap-server\fR \fIip-address\fR\fB;\fR
+.B option \fBnds-context\fR \fIstring\fR\fB;\fR
.RS 0.25i
.PP
-This specifies the IP address of the client's swap server.
+The nds-context option specifies the name of the initial Netware
+Directory Service for an NDS client.
.RE
.PP
-.B option \fBroot-path\fR \fIstring\fB;\fR\fR
+.B option \fBnds-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\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.
+The nds-servers option specifies a list of IP addresses of NDS servers.
.RE
.PP
-.B option \fBip-forwarding\fR \fIflag\fR\fB;\fR
+.B option \fBnds-tree-name\fR \fIstring\fR\fB;\fR
.RS 0.25i
.PP
-This option specifies whether the client should configure its IP
-layer for packet forwarding. A value of 0 means disable IP
-forwarding, and a value of 1 means enable IP forwarding.
+The nds-context 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 \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 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
@@ -286,37 +649,33 @@ forwarding, and a value of 1 means enable IP forwarding.
This option specifies whether the client should configure its IP
layer to allow forwarding of datagrams with non-local source routes
(see Section 3.3.5 of [4] for a discussion of this topic). A value
-of 0 means disallow forwarding of such datagrams, and a value of 1
+of 0 means disallow forwarding of such datagrams, and a value of true
means allow forwarding.
.RE
.PP
-.B option \fBpolicy-filter\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...
+.B option \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
]\fB;\fR
.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.
+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 \fBmax-dgram-reassembly\fR \fIuint16\fR\fB;\fR
+.B option \fBnwip-domain\fR \fIstring\fR\fB;\fR
.RS 0.25i
.PP
-This option specifies the maximum size datagram that the client
-should be prepared to reassemble. The minimum value legal value is
-576.
+The name of the NetWare/IP domain that a NetWare/IP client should
+use.
.RE
.PP
-.B option \fBdefault-ip-ttl\fR \fIuint8;\fR
+.B option \fBnwip-suboptions\fR \fIstring\fR\fB;\fR
.RS 0.25i
.PP
-This option specifies the default time-to-live that the client should
-use on outgoing datagrams.
+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
@@ -336,48 +695,55 @@ 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 \fBinterface-mtu\fR \fIuint16\fR\fB;\fR
+.B option \fBperform-mask-discovery\fR \fIflag\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.
+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
-.B option \fBall-subnets-local\fR \fIflag\fR\fB;\fR
+.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 whether or not the client may assume that all
-subnets of the IP network to which the client is connected use the
-same MTU as the subnet of that network to which the client is
-directly connected. A value of 1 indicates that all subnets share
-the same MTU. A value of 0 means that the client should assume that
-some subnets of the directly connected network may have smaller MTUs.
+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 \fBbroadcast-address\fR \fIip-address\fR\fB;\fR
+.B option \fBpop-server\fR \fIip-address\fR [\fB,\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).
+The POP3 server option specifies a list of POP3 available to the
+client. Servers should be listed in order of preference.
.RE
.PP
-.B option \fBperform-mask-discovery\fR \fIflag\fR\fB;\fR
+.B option \fBresource-location-servers\fR \fIip-address\fR
+ [\fB, \fR\fIip-address\fR...]\fB;\fR
+.fi
.RS 0.25i
.PP
-This option specifies whether or not the client should perform subnet
-mask discovery using ICMP. A value of 0 indicates that the client
-should not perform mask discovery. A value of 1 means that the
-client should perform mask discovery.
+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 \fBmask-supplier\fR \fIflag\fR\fB;\fR
+.B option \fBroot-path\fR \fItext\fB;\fR\fR
.RS 0.25i
.PP
-This option specifies whether or not the client should respond to
-subnet mask requests using ICMP. A value of 0 indicates that the
-client should not respond. A value of 1 means that the client should
-respond.
+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
@@ -385,8 +751,8 @@ respond.
.PP
This option specifies whether or not the client should solicit
routers using the Router Discovery mechanism defined in RFC 1256.
-A value of 0 indicates that the client should not perform
-router discovery. A value of 1 means that the client should perform
+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
@@ -397,10 +763,65 @@ This option specifies the address to which the client should transmit
router solicitation requests.
.RE
.PP
-.B option \fBstatic-routes\fR \fIip-address ip-address\fR [\fB,\fR \fIip-address ip-address\fR...
+.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-seperated 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
@@ -413,39 +834,73 @@ the destination.
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.
+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
-.B option \fBtrailer-encapsulation\fR \fIflag\fR\fB;\fR
+.nf
+.B option \fBstreettalk-directory-assistance-server\fR \fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fB;\fR
+.fi
.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 0 indicates that the client should not attempt to use trailers. A
-value of 1 means that the client should attempt to use trailers.
+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 \fBarp-cache-timeout\fR \fIuint32\fR\fB;\fR
+.B option \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fB;\fR
.RS 0.25i
.PP
-This option specifies the timeout in seconds for ARP cache entries.
+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 \fBieee802-3-encapsulation\fR \fIflag\fR\fB;\fR
+.B option subnet-mask \fIip-address\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 0 indicates that the client
-should use RFC 894 encapsulation. A value of 1 means that the client
-should use RFC 1042 encapsulation.
+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 \fBdefault-tcp-ttl\fR \fIuint8\fR\fB;\fR
+.B option \fBsubnet-selection\fR \fIstring\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.
+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 the whether or not the client should send TCP
+keepalive messages with a 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
@@ -458,99 +913,117 @@ indicates that the client should not generate keepalive messages on
connections unless specifically requested by an application.
.RE
.PP
-.B option \fBtcp-keepalive-garbage\fR \fIflag\fR\fB;\fR
+.B option \fBtftp-server-name\fR \fItext\fR\fB;\fR
.RS 0.25i
.PP
-This option specifies the whether or not the client should send TCP
-keepalive messages with a octet of garbage for compatibility with
-older implementations. A value of 0 indicates that a garbage octet
-should not be sent. A value of 1 indicates that a garbage octet
-should be sent.
+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 \fBnis-domain\fR \fIstring\fR\fB;\fR
+.B option time-offset \fIint32\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.
+The time-offset option specifies the offset of the client's subnet in
+seconds from Coordinated Universal Time (UTC).
.RE
.PP
-.B option \fBnis-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
+.B option time-servers \fIip-address\fR [, \fIip-address\fR...
]\fB;\fR
.RS 0.25i
.PP
-This option specifies a list of IP addresses indicating NIS servers
+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 \fBntp-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBtrailer-encapsulation\fR \fIflag\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.
+This option specifies whether or not the client should negotiate the
+use of trailers (RFC 893 [14]) when using the ARP protocol. A value
+of 0 indicates that the client should not attempt to use trailers. A
+value of true means that the client should attempt to use trailers.
.RE
.PP
-.B option \fBnetbios-name-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBuap-servers\fR \fItext\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.
+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 seperated by spaces.
.RE
.PP
-.B option \fBnetbios-dd-server\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBuser-class\fR \fIstring\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
+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.
.PP
-.B option \fBnetbios-node-type\fR \fIuint8\fR\fB;\fR
+.B option \fBvendor-class-identifier\fR \fIstring\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.
+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 a
+clients are sending, you can write the following in your DHCP server
+configuration file:
+.nf
.PP
-Possible node types are:
+set vendor-class option vendor-class-identifier;
+.fi
.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
+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-class "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 of the
+dhcpd.conf manual page for further information.
.RE
.PP
-.B option
-.B netbios-scope
-.I string\fB;\fR
+.B option \fBvendor-encapsulated-options\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.
+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 of the \fIdhcpd.conf\fI manual page for
+details.
.RE
.PP
-.B option \fBfont-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.B option \fBwww-server\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.
+The WWW server option specifies a list of WWW 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...
@@ -561,130 +1034,466 @@ 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
-.PP
-.B option \fBdhcp-client-identifier\fR \fIdata-string\fR\fB;\fR
+.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
-This option can be used to specify the a DHCP client identifier in a
-host declaration, so that dhcpd can find the host record by matching
-against the client identifier.
+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
-.B option \fBnisplus-domain\fR \fIstring\fR\fB;\fR
+.PP
+.B option \fBagent.remote-id\fR \fIstring\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.
+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
-.B option \fBnisplus-servers\fR \fIip-address\fR [\fB,\fR \fIip-address\fR...
-]\fB;\fR
+.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
-This option specifies a list of IP addresses indicating NIS+ servers
-available to the client. Servers should be listed in order of
-preference.
+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 \fBtftp-server-name\fR \fIstring\fR\fB;\fR
+.B option fqdn.server-update \fIflag\fB;
.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.
+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 \fBbootfile-name\fR \fIstring\fR\fB;\fR
+.B option fqdn.encoded \fIflag\fB;
.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.
+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 \fBmobile-ip-home-agent\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option fqdn.rcode1 \fIflag\fB;
+.PP
+.B option fqdn.rcode1 \fIflag\fB;
.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.
+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 \fBsmtp-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option fqdn.fqdn \fItext\fB;
.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.
+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 \fBpop-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+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
-The POP3 server option specifies a list of POP3 available to the
-client. Servers should be listed in order of preference.
-.RE
+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
-.B option \fBnntp-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.RE
+.B option \fBnwip.preferred-dss\fR \fIip-address\fR [\fB,\fR \fIip-address\fR... ]\fR\fB;\fR
.RS 0.25i
.PP
-The NNTP server option specifies a list of NNTP available to the
-client. Servers should be listed in order of preference.
+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 \fBwww-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option \fBnwip.nearest-nwip-server\fR \fI\fIip-address\fR
+ [\fB,\fR \fIip-address\fR...]\fR\fB;\fR
.RS 0.25i
.PP
-The WWW server option specifies a list of WWW available to the
-client. Servers should be listed in order of preference.
+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 \fBfinger-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option \fBnwip.autoretries\fR \fIuint8\fR\fB;\fR
.RS 0.25i
.PP
-The Finger server option specifies a list of Finger available to the
-client. Servers should be listed in order of preference.
+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 \fBirc-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option \fBnwip.autoretry-secs\fR \fIuint8\fR\fB;\fR
.RS 0.25i
.PP
-The IRC server option specifies a list of IRC available to the
-client. Servers should be listed in order of preference.
+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 \fBstreettalk-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option \fBnwip.nwip-1-1\fR \fIuint8\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.
+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 \fBstreetalk-directory-assistance-server\fR \fIip-address\fR [\fB,\fR
-\fIip-address\fR... ]\fB;\fR
+.B option \fBnwip.primary-dss\fR \fIip-address\fR\fB;\fR
.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.
+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 DEFINING NEW OPTIONS
+The Internet Software 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. For site-local
+options, all codes between 128 and 254 are reserved for DHCP options,
+so you can pick any one of these. 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, but it's not likely to cause too much trouble in
+practice.
+.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
+.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 seperated 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 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 \fB vendor-encapsulated-options\fR
+option, which allows vendors to define their own options that will be
+sent encapsulated in a standard DHCP option. The format of the
+.B vendor-encapsulated-options
+option is either a series of bytes whose format is not specified, or
+a sequence of options, each of which consists of a single-byte
+vendor-specific option code, followed by a single-byte length,
+followed by as many bytes of data as are specified in the length (the
+length does not include itself or the option code).
+.PP
+The value of this option 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-seperated list of hexadecimal values. 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;
+.fi
+.PP
+The second way of setting the value of this option 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
+.B vendor-encapsulated-options
+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 ;
+.PP
+The name can then be used in option definitions, as described earlier in
+this document. For example:
+.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
+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;
+}
+
+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
+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
+.B vendor-encapsulated-options
+option.
.SH SEE ALSO
-dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcpd(8),
-dhclient(8), RFC2132, RFC2131.
+dhcpd.conf(5), dhcpd.leases(5), dhclient.conf(5), dhcp-eval(5), dhcpd(8),
+dhclient(8), RFC2132, RFC2131, draft-ietf-dhc-agent-options-??.txt.
.SH AUTHOR
-.B dhcpd(8)
-was written by Ted Lemon <mellon@vix.com>
-under a contract with Vixie Labs. Funding
-for this project was provided by the Internet Software Corporation.
+The Internet Software Consortium DHCP Distribution was written by Ted
+Lemon under a contract with Vixie Labs. Funding for
+this project was provided through the Internet Software Consortium.
Information about the Internet Software Consortium can be found at
-.B http://www.isc.org/isc.
+.B http://www.isc.org.
diff --git a/contrib/isc-dhcp/common/discover.c b/contrib/isc-dhcp/common/discover.c
new file mode 100644
index 000000000000..953bab0a1961
--- /dev/null
+++ b/contrib/isc-dhcp/common/discover.c
@@ -0,0 +1,1138 @@
+/* dispatch.c
+
+ Network input dispatcher... */
+
+/*
+ * Copyright (c) 1995-2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
+static char copyright[] =
+"$Id: discover.c,v 1.42.2.8 2001/10/18 20:10:26 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+#include <sys/ioctl.h>
+
+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;
+struct in_addr local_address;
+
+void (*bootp_packet_handler) PROTO ((struct interface_info *,
+ struct dhcp_packet *, unsigned,
+ unsigned int,
+ struct iaddr, struct hardware *));
+
+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;
+}
+
+/* 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 (state)
+ int state;
+{
+ struct interface_info *tmp, *ip;
+ struct interface_info *last, *next;
+ char buf [2048];
+ struct ifconf ic;
+ struct ifreq ifr;
+ int i;
+ int sock;
+ int address_count = 0;
+ struct subnet *subnet;
+ struct shared_network *share;
+ struct sockaddr_in foo;
+ int ir;
+ struct ifreq *tif;
+#ifdef ALIAS_NAMES_PERMUTED
+ char *s;
+#endif
+ isc_result_t status;
+ static int setup_fallback = 0;
+ int wifcount = 0;
+
+ /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
+ if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ log_fatal ("Can't create addrlist socket");
+
+ /* Get the interface configuration information... */
+
+#ifdef SIOCGIFCONF_ZERO_PROBE
+ /* linux will only tell us how long a buffer it wants if we give it
+ * a null buffer first. So, do a dry run to figure out the length.
+ *
+ * XXX this code is duplicated from below because trying to fold
+ * the logic into the if statement and goto resulted in excesssive
+ * obfuscation. The intent is that unless you run Linux you shouldn't
+ * have to deal with this. */
+
+ ic.ifc_len = 0;
+ ic.ifc_ifcu.ifcu_buf = (caddr_t)NULL;
+#else
+ /* otherwise, we just feed it a starting size, and it'll tell us if
+ * it needs more */
+
+ ic.ifc_len = sizeof buf;
+ ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
+#endif
+
+ gifconf_again:
+ i = ioctl(sock, SIOCGIFCONF, &ic);
+
+ if (i < 0)
+ log_fatal ("ioctl: SIOCGIFCONF: %m");
+
+#ifdef SIOCGIFCONF_ZERO_PROBE
+ /* Workaround for SIOCGIFCONF bug on some Linux versions. */
+ if (ic.ifc_ifcu.ifcu_buf == 0 && ic.ifc_len == 0) {
+ ic.ifc_len = sizeof buf;
+ ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
+ goto gifconf_again;
+ }
+#endif
+
+ /* If the SIOCGIFCONF resulted in more data than would fit in
+ a buffer, allocate a bigger buffer. */
+ if ((ic.ifc_ifcu.ifcu_buf == buf
+#ifdef SIOCGIFCONF_ZERO_PROBE
+ || ic.ifc_ifcu.ifcu_buf == 0
+#endif
+ ) && ic.ifc_len > sizeof buf) {
+ ic.ifc_ifcu.ifcu_buf = dmalloc ((size_t)ic.ifc_len, MDL);
+ if (!ic.ifc_ifcu.ifcu_buf)
+ log_fatal ("Can't allocate SIOCGIFCONF buffer.");
+ goto gifconf_again;
+#ifdef SIOCGIFCONF_ZERO_PROBE
+ } else if (ic.ifc_ifcu.ifcu_buf == 0) {
+ ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
+ ic.ifc_len = sizeof buf;
+ goto gifconf_again;
+#endif
+ }
+
+
+ /* 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. */
+ for (i = 0; i < ic.ifc_len;) {
+ struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i);
+#ifdef HAVE_SA_LEN
+ if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr))
+ i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len;
+ else
+#endif
+ i += sizeof *ifp;
+
+#ifdef ALIAS_NAMES_PERMUTED
+ if ((s = strrchr (ifp -> ifr_name, ':'))) {
+ *s = 0;
+ }
+#endif
+
+#ifdef SKIP_DUMMY_INTERFACES
+ if (!strncmp (ifp -> ifr_name, "dummy", 5))
+ continue;
+#endif
+
+
+ /* See if this is the sort of interface we want to
+ deal with. */
+ strcpy (ifr.ifr_name, ifp -> ifr_name);
+ if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
+ log_fatal ("Can't get interface flags for %s: %m",
+ ifr.ifr_name);
+
+ /* See if we've seen an interface that matches this one. */
+ for (tmp = interfaces; tmp; tmp = tmp -> next)
+ if (!strcmp (tmp -> name, ifp -> ifr_name))
+ break;
+
+ /* Skip loopback, point-to-point and down interfaces,
+ except don't skip down interfaces if we're trying to
+ get a list of configurable interfaces. */
+ if (((ifr.ifr_flags & IFF_LOOPBACK ||
+ ifr.ifr_flags & IFF_POINTOPOINT) && !tmp) ||
+ (!(ifr.ifr_flags & IFF_UP) &&
+ state != DISCOVER_UNCONFIGURED))
+ continue;
+
+ /* If there isn't already an interface by this name,
+ allocate one. */
+ if (!tmp) {
+ tmp = (struct interface_info *)0;
+ status = interface_allocate (&tmp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Error allocating interface %s: %s",
+ ifp -> ifr_name,
+ isc_result_totext (status));
+ strcpy (tmp -> name, ifp -> ifr_name);
+ interface_snorf (tmp, ir);
+ interface_dereference (&tmp, MDL);
+ tmp = interfaces; /* XXX */
+ }
+
+ if (dhcp_interface_discovery_hook)
+ (*dhcp_interface_discovery_hook) (tmp);
+
+ /* If we have the capability, extract link information
+ and record it in a linked list. */
+#ifdef HAVE_AF_LINK
+ if (ifp -> ifr_addr.sa_family == AF_LINK) {
+ struct sockaddr_dl *foo = ((struct sockaddr_dl *)
+ (&ifp -> ifr_addr));
+#if defined (HAVE_SIN_LEN)
+ tmp -> hw_address.hlen = foo -> sdl_alen;
+#else
+ tmp -> hw_address.hlen = 6; /* XXX!!! */
+#endif
+ tmp -> hw_address.hbuf [0] = HTYPE_ETHER; /* XXX */
+ memcpy (&tmp -> hw_address.hbuf [1],
+ LLADDR (foo), tmp -> hw_address.hlen);
+ tmp -> hw_address.hlen++; /* for type. */
+ } else
+#endif /* AF_LINK */
+
+ if (ifp -> ifr_addr.sa_family == AF_INET) {
+ struct iaddr addr;
+
+ /* Get a pointer to the address... */
+ memcpy (&foo, &ifp -> ifr_addr,
+ sizeof ifp -> ifr_addr);
+
+ /* We don't want the loopback interface. */
+ if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK) &&
+ ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_SERVER))
+ continue;
+
+
+ /* If this is the first real IP address we've
+ found, keep a pointer to ifreq structure in
+ which we found it. */
+ if (!tmp -> ifp) {
+#ifdef HAVE_SA_LEN
+ unsigned len = ((sizeof ifp -> ifr_name) +
+ ifp -> ifr_addr.sa_len);
+#else
+ unsigned len = sizeof *ifp;
+#endif
+ tif = (struct ifreq *)dmalloc (len, MDL);
+ if (!tif)
+ log_fatal ("no space for ifp.");
+ memcpy (tif, ifp, len);
+ tmp -> ifp = tif;
+ tmp -> primary_address = foo.sin_addr;
+ }
+
+ /* Grab the address... */
+ addr.len = 4;
+ memcpy (addr.iabuf, &foo.sin_addr.s_addr,
+ addr.len);
+ if (dhcp_interface_setup_hook)
+ (*dhcp_interface_setup_hook) (tmp, &addr);
+ }
+ }
+
+ /* If we allocated a buffer, free it. */
+ if (ic.ifc_ifcu.ifcu_buf != buf)
+ dfree (ic.ifc_ifcu.ifcu_buf, MDL);
+
+#if defined (LINUX_SLASHPROC_DISCOVERY)
+ /* On Linux, interfaces that don't have IP addresses don't
+ show up in the SIOCGIFCONF syscall. This only matters for
+ the DHCP client, of course - the relay agent and server
+ should only care about interfaces that are configured with
+ IP addresses anyway.
+
+ The PROCDEV_DEVICE (/proc/net/dev) is a kernel-supplied file
+ that, when read, prints a human readable network status. We
+ extract the names of the network devices by skipping the first
+ two lines (which are header) and then parsing off everything
+ up to the colon in each subsequent line - these lines start
+ with the interface name, then a colon, then a bunch of
+ statistics. */
+
+ if (state == DISCOVER_UNCONFIGURED) {
+ FILE *proc_dev;
+ char buffer [256];
+ int skip = 2;
+
+ proc_dev = fopen (PROCDEV_DEVICE, "r");
+ if (!proc_dev)
+ log_fatal ("%s: %m", PROCDEV_DEVICE);
+
+ while (fgets (buffer, sizeof buffer, proc_dev)) {
+ char *name = buffer;
+ char *sep;
+
+ /* Skip the first two blocks, which are header
+ lines. */
+ if (skip) {
+ --skip;
+ continue;
+ }
+
+ sep = strrchr (buffer, ':');
+ if (sep)
+ *sep = '\0';
+ while (*name == ' ')
+ name++;
+
+ /* See if we've seen an interface that matches
+ this one. */
+ for (tmp = interfaces; tmp; tmp = tmp -> next)
+ if (!strcmp (tmp -> name, name))
+ break;
+
+ /* If we found one, nothing more to do.. */
+ if (tmp)
+ continue;
+
+ /* Otherwise, allocate one. */
+ tmp = (struct interface_info *)0;
+ status = interface_allocate (&tmp, MDL);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't allocate interface %s: %s",
+ name, isc_result_totext (status));
+ tmp -> flags = ir;
+ strncpy (tmp -> name, name, IFNAMSIZ);
+ interface_reference (&tmp -> next, interfaces, MDL);
+ interface_dereference (&interfaces, MDL);
+ interface_reference (&interfaces, tmp, MDL);
+ interface_dereference (&tmp, MDL);
+ tmp = interfaces;
+
+ if (dhcp_interface_discovery_hook)
+ (*dhcp_interface_discovery_hook) (tmp);
+
+ }
+ fclose (proc_dev);
+ }
+#endif
+
+ /* Now cycle through all the interfaces we found, looking for
+ hardware addresses. */
+#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK)
+ for (tmp = interfaces; tmp; tmp = tmp -> next) {
+ struct ifreq ifr;
+ struct sockaddr sa;
+ int b, sk;
+
+ if (!tmp -> ifp) {
+ /* Make up an ifreq structure. */
+ tif = (struct ifreq *)dmalloc (sizeof (struct ifreq),
+ MDL);
+ if (!tif)
+ log_fatal ("no space to remember ifp.");
+ memset (tif, 0, sizeof (struct ifreq));
+ strcpy (tif -> ifr_name, tmp -> name);
+ tmp -> ifp = tif;
+ }
+
+ /* Read the hardware address from this interface. */
+ ifr = *tmp -> ifp;
+ if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+
+ sa = *(struct sockaddr *)&ifr.ifr_hwaddr;
+
+ switch (sa.sa_family) {
+#ifdef HAVE_ARPHRD_TUNNEL
+ case ARPHRD_TUNNEL:
+ /* ignore tunnel interfaces. */
+#endif
+#ifdef HAVE_ARPHRD_ROSE
+ case ARPHRD_ROSE:
+#endif
+#ifdef HAVE_ARPHRD_LOOPBACK
+ case ARPHRD_LOOPBACK:
+ /* ignore loopback interface */
+ break;
+#endif
+
+ case ARPHRD_ETHER:
+ tmp -> hw_address.hlen = 7;
+ tmp -> hw_address.hbuf [0] = ARPHRD_ETHER;
+ memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
+ break;
+
+#ifndef HAVE_ARPHRD_IEEE802
+# define ARPHRD_IEEE802 HTYPE_IEEE802
+#endif
+#if defined (HAVE_ARPHRD_IEEE802_TR)
+ case ARPHRD_IEEE802_TR:
+#endif
+ case ARPHRD_IEEE802:
+ tmp -> hw_address.hlen = 7;
+ tmp -> hw_address.hbuf [0] = ARPHRD_IEEE802;
+ memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
+ break;
+
+#ifndef HAVE_ARPHRD_FDDI
+# define ARPHRD_FDDI HTYPE_FDDI
+#endif
+ case ARPHRD_FDDI:
+ tmp -> hw_address.hlen = 17;
+ tmp -> hw_address.hbuf [0] = HTYPE_FDDI; /* XXX */
+ memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 16);
+ break;
+
+#ifdef HAVE_ARPHRD_METRICOM
+ case ARPHRD_METRICOM:
+ tmp -> hw_address.hlen = 7;
+ tmp -> hw_address.hbuf [0] = ARPHRD_METRICOM;
+ memcpy (&tmp -> hw_address.hbuf [0], sa.sa_data, 6);
+ break;
+#endif
+
+#ifdef HAVE_ARPHRD_AX25
+ case ARPHRD_AX25:
+ tmp -> hw_address.hlen = 7;
+ tmp -> hw_address.hbuf [0] = ARPHRD_AX25;
+ memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
+ break;
+#endif
+
+#ifdef HAVE_ARPHRD_NETROM
+ case ARPHRD_NETROM:
+ tmp -> hw_address.hlen = 7;
+ tmp -> hw_address.hbuf [0] = ARPHRD_NETROM;
+ memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
+ break;
+#endif
+
+ default:
+ log_error ("%s: unknown hardware address type %d",
+ ifr.ifr_name, sa.sa_family);
+ break;
+ }
+ }
+#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */
+
+ /* 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) {
+ close (sock);
+ return;
+ }
+
+ /* Weed out the interfaces that did not have IP addresses. */
+ tmp = last = next = (struct interface_info *)0;
+ 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)
+ continue;
+ if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_REQUESTED)
+ tmp -> flags &= ~(INTERFACE_AUTOMATIC |
+ INTERFACE_REQUESTED);
+ if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
+ 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;
+
+ memcpy (&foo, &tmp -> ifp -> ifr_addr,
+ sizeof tmp -> ifp -> ifr_addr);
+
+ /* We must have a subnet declaration for each interface. */
+ if (!tmp -> shared_network && (state == DISCOVER_SERVER)) {
+ log_error ("%s", "");
+ log_error ("No subnet declaration for %s (%s).",
+ tmp -> name, inet_ntoa (foo.sin_addr));
+ 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",
+ "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 subnet %s",
+ " 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) {
+ if (!subnet -> interface_address.len) {
+ /* Set the interface address for this subnet
+ to the first address we found. */
+ subnet -> interface_address.len = 4;
+ memcpy (subnet -> interface_address.iabuf,
+ &foo.sin_addr.s_addr, 4);
+ }
+ }
+
+ /* 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_register_receive (tmp);
+ if_register_send (tmp);
+
+ interface_stash (tmp);
+ wifcount++;
+#if defined (HAVE_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. */
+ 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;
+ status = omapi_register_io_object ((omapi_object_t *)tmp,
+ if_readsocket, 0,
+ got_one, 0, 0);
+ if (status != ISC_R_SUCCESS)
+ log_fatal ("Can't register I/O handle for %s: %s",
+ tmp -> name, isc_result_totext (status));
+ }
+
+ close (sock);
+
+ if (state == DISCOVER_SERVER && wifcount == 0) {
+ log_info ("%s", "");
+ log_fatal ("Not configured to listen on any interfaces!");
+ }
+
+ if (!setup_fallback) {
+ setup_fallback = 1;
+ maybe_setup_fallback ();
+ }
+
+#if defined (HAVE_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
+}
+
+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 ISC_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. We're allowing packets with no
+ sname or filename, because we're aware of at least one
+ client that sends such packets, but this definitely falls
+ into the category of being forgiving. */
+ if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN)
+ return ISC_R_UNEXPECTED;
+
+ 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;
+}
+
+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;
+ int foo;
+
+ if (h -> type != dhcp_type_interface)
+ return ISC_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 ISC_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 == ISC_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;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_interface)
+ return ISC_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;
+ struct client_config *config;
+ struct client_state *client;
+ isc_result_t status;
+
+ if (h -> type != dhcp_type_interface)
+ return ISC_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 -> 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_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 ISC_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)
+ 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 ISC_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 ISC_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 ISC_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 ISC_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);
+
+ if_deregister_send (interface);
+ if_deregister_receive (interface);
+
+ 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/contrib/isc-dhcp/common/dispatch.c b/contrib/isc-dhcp/common/dispatch.c
index d44a98740e45..7c8545be6468 100644
--- a/contrib/isc-dhcp/common/dispatch.c
+++ b/contrib/isc-dhcp/common/dispatch.c
@@ -3,8 +3,8 @@
Network input dispatcher... */
/*
- * Copyright (c) 1995, 1996, 1997, 1998, 1999
- * The Internet Software Consortium. All rights reserved.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,587 +34,59 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: dispatch.c,v 1.47.2.15 1999/07/13 12:51:55 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
+"$Id: dispatch.c,v 1.63.2.2 2001/06/21 16:47:15 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-#include <sys/ioctl.h>
-struct interface_info *interfaces, *dummy_interfaces, *fallback_interface;
-struct protocol *protocols;
struct timeout *timeouts;
static struct timeout *free_timeouts;
-static int interfaces_invalidated;
-void (*bootp_packet_handler) PROTO ((struct interface_info *,
- struct dhcp_packet *, int, unsigned int,
- struct iaddr, struct hardware *));
-int quiet_interface_discovery;
-
-/* 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 (state)
- int state;
+void set_time (u_int32_t t)
{
- struct interface_info *tmp;
- struct interface_info *last, *next;
- char buf [8192];
- struct ifconf ic;
- struct ifreq ifr;
- int i;
- int sock;
- struct subnet *subnet;
- struct shared_network *share;
- struct sockaddr_in foo;
- int ir;
- struct ifreq *tif;
-#ifdef ALIAS_NAMES_PERMUTED
- char *s;
-#endif
-
- /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
- if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- error ("Can't create addrlist socket");
-
- /* Get the interface configuration information... */
- ic.ifc_len = sizeof buf;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
- i = ioctl(sock, SIOCGIFCONF, &ic);
-
- if (i < 0)
- error ("ioctl: SIOCGIFCONF: %m");
-
- /* 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. */
- for (i = 0; i < ic.ifc_len;) {
- struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i);
-#ifdef HAVE_SA_LEN
- if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr))
- i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len;
- else
-#endif
- i += sizeof *ifp;
-
-#ifdef ALIAS_NAMES_PERMUTED
- if ((s = strrchr (ifp -> ifr_name, ':'))) {
- *s = 0;
- }
-#endif
-
-#ifdef SKIP_DUMMY_INTERFACES
- if (!strncmp (ifp -> ifr_name, "dummy", 5))
- continue;
-#endif
-
-
- /* See if this is the sort of interface we want to
- deal with. */
- strcpy (ifr.ifr_name, ifp -> ifr_name);
- if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
- error ("Can't get interface flags for %s: %m",
- ifr.ifr_name);
-
- /* Skip loopback, point-to-point and down interfaces,
- except don't skip down interfaces if we're trying to
- get a list of configurable interfaces. */
- if ((ifr.ifr_flags & IFF_LOOPBACK) ||
-#ifdef HAVE_IFF_POINTOPOINT
- (ifr.ifr_flags & IFF_POINTOPOINT) ||
-#endif
- (!(ifr.ifr_flags & IFF_UP) &&
- state != DISCOVER_UNCONFIGURED))
- continue;
-
- /* See if we've seen an interface that matches this one. */
- for (tmp = interfaces; tmp; tmp = tmp -> next)
- if (!strcmp (tmp -> name, ifp -> ifr_name))
- break;
-
- /* If there isn't already an interface by this name,
- allocate one. */
- if (!tmp) {
- tmp = ((struct interface_info *)
- dmalloc (sizeof *tmp, "discover_interfaces"));
- if (!tmp)
- error ("Insufficient memory to %s %s",
- "record interface", ifp -> ifr_name);
- strcpy (tmp -> name, ifp -> ifr_name);
- tmp -> next = interfaces;
- tmp -> flags = ir;
- interfaces = tmp;
- }
-
- /* If we have the capability, extract link information
- and record it in a linked list. */
-#ifdef HAVE_AF_LINK
- if (ifp -> ifr_addr.sa_family == AF_LINK) {
- struct sockaddr_dl *foo = ((struct sockaddr_dl *)
- (&ifp -> ifr_addr));
- tmp -> hw_address.hlen = foo -> sdl_alen;
- tmp -> hw_address.htype = HTYPE_ETHER; /* XXX */
- memcpy (tmp -> hw_address.haddr,
- LLADDR (foo), foo -> sdl_alen);
- } else
-#endif /* AF_LINK */
-
- if (ifp -> ifr_addr.sa_family == AF_INET) {
- struct iaddr addr;
-
- /* Get a pointer to the address... */
- memcpy (&foo, &ifp -> ifr_addr,
- sizeof ifp -> ifr_addr);
-
- /* We don't want the loopback interface. */
- if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
- continue;
-
-
- /* If this is the first real IP address we've
- found, keep a pointer to ifreq structure in
- which we found it. */
- if (!tmp -> ifp) {
-#ifdef HAVE_SA_LEN
- int len = ((sizeof ifp -> ifr_name) +
- ifp -> ifr_addr.sa_len);
-#else
- int len = sizeof *ifp;
-#endif
- tif = (struct ifreq *)malloc (len);
- if (!tif)
- error ("no space to remember ifp.");
- memcpy (tif, ifp, len);
- tmp -> ifp = tif;
- tmp -> primary_address = foo.sin_addr;
- }
-
- /* Grab the address... */
- addr.len = 4;
- memcpy (addr.iabuf, &foo.sin_addr.s_addr,
- addr.len);
-
- /* If there's a registered subnet for this address,
- connect it together... */
- if ((subnet = find_subnet (addr))) {
- /* If this interface has multiple aliases
- on the same subnet, ignore all but the
- first we encounter. */
- if (!subnet -> interface) {
- subnet -> interface = tmp;
- subnet -> interface_address = addr;
- } else if (subnet -> interface != tmp) {
- warn ("Multiple %s %s: %s %s",
- "interfaces match the",
- "same subnet",
- subnet -> interface -> name,
- tmp -> name);
- }
- share = subnet -> shared_network;
- if (tmp -> shared_network &&
- tmp -> shared_network != share) {
- warn ("Interface %s matches %s",
- tmp -> name,
- "multiple shared networks");
- } else {
- tmp -> shared_network = share;
- }
-
- if (!share -> interface) {
- share -> interface = tmp;
- } else if (share -> interface != tmp) {
- warn ("Multiple %s %s: %s %s",
- "interfaces match the",
- "same shared network",
- share -> interface -> name,
- tmp -> name);
- }
- }
- }
+ /* Do any outstanding timeouts. */
+ if (cur_time != t) {
+ cur_time = t;
+ process_outstanding_timeouts ((struct timeval *)0);
}
-
-#if defined (LINUX_SLASHPROC_DISCOVERY)
- /* On Linux, interfaces that don't have IP addresses don't show up
- in the SIOCGIFCONF syscall. We got away with this prior to
- Linux 2.1 because we would give each interface an IP address of
- 0.0.0.0 before trying to boot, but that doesn't work after 2.1
- because we're using LPF, because we can't configure interfaces
- with IP addresses of 0.0.0.0 anymore (grumble). This only
- matters for the DHCP client, of course - the relay agent and
- server should only care about interfaces that are configured
- with IP addresses anyway.
-
- The PROCDEV_DEVICE (/proc/net/dev) is a kernel-supplied file
- that, when read, prints a human readable network status. We
- extract the names of the network devices by skipping the first
- two lines (which are header) and then parsing off everything
- up to the colon in each subsequent line - these lines start
- with the interface name, then a colon, then a bunch of
- statistics. Yes, Virgina, this is a kludge, but you work
- with what you have. */
-
- if (state == DISCOVER_UNCONFIGURED) {
- FILE *proc_dev;
- char buffer [256];
- int skip = 2;
-
- proc_dev = fopen (PROCDEV_DEVICE, "r");
- if (!proc_dev)
- error ("%s: %m", PROCDEV_DEVICE);
-
- while (fgets (buffer, sizeof buffer, proc_dev)) {
- char *name = buffer;
- char *sep;
-
- /* Skip the first two blocks, which are header
- lines. */
- if (skip) {
- --skip;
- continue;
- }
-
- sep = strrchr (buffer, ':');
- if (sep)
- *sep = '\0';
- while (*name == ' ')
- name++;
-
- /* See if we've seen an interface that matches
- this one. */
- for (tmp = interfaces; tmp; tmp = tmp -> next)
- if (!strcmp (tmp -> name, name))
- break;
-
- /* If we found one, nothing more to do.. */
- if (tmp)
- continue;
-
- /* Otherwise, allocate one. */
- tmp = ((struct interface_info *)
- dmalloc (sizeof *tmp, "discover_interfaces"));
- if (!tmp)
- error ("Insufficient memory to %s %s",
- "record interface", name);
- memset (tmp, 0, sizeof *tmp);
- strcpy (tmp -> name, name);
-
- tmp -> flags = ir;
- tmp -> next = interfaces;
- interfaces = tmp;
- }
- fclose (proc_dev);
- }
-#endif
-
- /* Now cycle through all the interfaces we found, looking for
- hardware addresses. */
-#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK)
- for (tmp = interfaces; tmp; tmp = tmp -> next) {
- struct ifreq ifr;
- struct sockaddr sa;
- int b, sk;
-
- if (!tmp -> ifp) {
- /* Make up an ifreq structure. */
- tif = (struct ifreq *)malloc (sizeof (struct ifreq));
- if (!tif)
- error ("no space to remember ifp.");
- memset (tif, 0, sizeof (struct ifreq));
- strcpy (tif -> ifr_name, tmp -> name);
- tmp -> ifp = tif;
- }
-
- /* Read the hardware address from this interface. */
- ifr = *tmp -> ifp;
- if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
- continue;
-
- sa = *(struct sockaddr *)&ifr.ifr_hwaddr;
-
- switch (sa.sa_family) {
-#ifdef HAVE_ARPHRD_TUNNEL
- case ARPHRD_TUNNEL:
- /* ignore tunnel interfaces. */
-#endif
-#ifdef HAVE_ARPHRD_ROSE
- case ARPHRD_ROSE:
-#endif
-#ifdef HAVE_ARPHRD_LOOPBACK
- case ARPHRD_LOOPBACK:
- /* ignore loopback interface */
- break;
-#endif
-
- case ARPHRD_ETHER:
- tmp -> hw_address.hlen = 6;
- tmp -> hw_address.htype = ARPHRD_ETHER;
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 6);
- break;
-
-#ifndef HAVE_ARPHRD_IEEE802
-# define ARPHRD_IEEE802 HTYPE_IEEE802
-#endif
- case ARPHRD_IEEE802:
- tmp -> hw_address.hlen = 6;
- tmp -> hw_address.htype = ARPHRD_IEEE802;
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 6);
- break;
-
-#ifndef HAVE_ARPHRD_FDDI
-# define ARPHRD_FDDI HTYPE_FDDI
-#endif
- case ARPHRD_FDDI:
- tmp -> hw_address.hlen = 16;
- tmp -> hw_address.htype = HTYPE_FDDI; /* XXX */
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 16);
- break;
-
-#ifdef HAVE_ARPHRD_METRICOM
- case ARPHRD_METRICOM:
- tmp -> hw_address.hlen = 6;
- tmp -> hw_address.htype = ARPHRD_METRICOM;
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 6);
- break;
-#endif
-
-#ifdef HAVE_ARPHRD_AX25
- case ARPHRD_AX25:
- tmp -> hw_address.hlen = 6;
- tmp -> hw_address.htype = ARPHRD_AX25;
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 6);
- break;
-#endif
-
-#ifdef HAVE_ARPHRD_NETROM
- case ARPHRD_NETROM:
- tmp -> hw_address.hlen = 6;
- tmp -> hw_address.htype = ARPHRD_NETROM;
- memcpy (tmp -> hw_address.haddr, sa.sa_data, 6);
- break;
-#endif
-
- default:
- warn ("%s: unknown hardware address type %d",
- ifr.ifr_name, sa.sa_family);
- break;
- }
- }
-#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */
-
- /* 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. */
- last = (struct interface_info *)0;
- for (tmp = interfaces; tmp; tmp = next) {
- next = tmp -> next;
- if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
- state == DISCOVER_REQUESTED)
- tmp -> flags &= ~(INTERFACE_AUTOMATIC |
- INTERFACE_REQUESTED);
- if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
- if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
- error ("%s: not found", tmp -> name);
- if (!last)
- interfaces = interfaces -> next;
- else
- last -> next = tmp -> next;
-
- /* Remember the interface in case we need to know
- about it later. */
- tmp -> next = dummy_interfaces;
- dummy_interfaces = tmp;
- continue;
- }
- last = tmp;
-
- memcpy (&foo, &tmp -> ifp -> ifr_addr,
- sizeof tmp -> ifp -> ifr_addr);
-
- /* We must have a subnet declaration for each interface. */
- if (!tmp -> shared_network && (state == DISCOVER_SERVER)) {
- warn ("No subnet declaration for %s (%s).",
- tmp -> name, inet_ntoa (foo.sin_addr));
- warn ("Please write a subnet declaration in your %s",
- "dhcpd.conf file for the");
- error ("network segment to which interface %s %s",
- tmp -> name, "is attached.");
- }
-
- /* 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) {
- if (!subnet -> interface_address.len) {
- /* Set the interface address for this subnet
- to the first address we found. */
- subnet -> interface_address.len = 4;
- memcpy (subnet -> interface_address.iabuf,
- &foo.sin_addr.s_addr, 4);
- }
- }
-
- /* Register the interface... */
- if_register_receive (tmp);
- if_register_send (tmp);
- }
-
- /* Now register all the remaining interfaces as protocols. */
- for (tmp = interfaces; tmp; tmp = tmp -> next)
- add_protocol (tmp -> name, tmp -> rfdesc, got_one, tmp);
-
- close (sock);
-
- maybe_setup_fallback ();
}
-struct interface_info *setup_fallback ()
+struct timeval *process_outstanding_timeouts (struct timeval *tvp)
{
- fallback_interface =
- ((struct interface_info *)
- dmalloc (sizeof *fallback_interface, "discover_interfaces"));
- if (!fallback_interface)
- error ("Insufficient memory to record fallback interface.");
- memset (fallback_interface, 0, sizeof *fallback_interface);
- strcpy (fallback_interface -> name, "fallback");
- fallback_interface -> shared_network =
- new_shared_network ("parse_statement");
- if (!fallback_interface -> shared_network)
- error ("No memory for shared subnet");
- memset (fallback_interface -> shared_network, 0,
- sizeof (struct shared_network));
- fallback_interface -> shared_network -> name = "fallback-net";
- return fallback_interface;
-}
-
-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;
+ /* Call any expired timeouts, and then if there's
+ still a timeout registered, time out the select
+ call then. */
+ another:
+ if (timeouts) {
+ struct timeout *t;
+ if (timeouts -> when <= cur_time) {
+ t = timeouts;
+ timeouts = timeouts -> next;
+ (*(t -> func)) (t -> what);
+ if (t -> unref)
+ (*t -> unref) (&t -> what, MDL);
+ t -> next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+ if (tvp) {
+ tvp -> tv_sec = timeouts -> when;
+ tvp -> tv_usec = 0;
+ }
+ return tvp;
+ } else
+ return (struct timeval *)0;
}
-#ifdef USE_POLL
-/* Wait for packets to come in using poll(). When a packet comes in,
- call receive_packet to receive the packet and possibly strip hardware
- addressing information from it, and then call through the
- bootp_packet_handler hook to try to do something with it. */
-
-void dispatch ()
-{
- struct protocol *l;
- int nfds = 0;
- struct pollfd *fds;
- int count;
- int i;
- int to_msec;
-
- nfds = 0;
- for (l = protocols; l; l = l -> next) {
- ++nfds;
- }
- fds = (struct pollfd *)malloc ((nfds) * sizeof (struct pollfd));
- if (!fds)
- error ("Can't allocate poll structures.");
-
- do {
- /* Call any expired timeouts, and then if there's
- still a timeout registered, time out the select
- call then. */
- another:
- if (timeouts) {
- struct timeout *t;
- if (timeouts -> when <= cur_time) {
- t = timeouts;
- timeouts = timeouts -> next;
- (*(t -> func)) (t -> what);
- t -> next = free_timeouts;
- free_timeouts = t;
- goto another;
- }
- /* Figure timeout in milliseconds, and check for
- potential overflow. We assume that integers
- are 32 bits, which is harmless if they're 64
- bits - we'll just get extra timeouts in that
- case. Lease times would have to be quite
- long in order for a 32-bit integer to overflow,
- anyway. */
- to_msec = timeouts -> when - cur_time;
- if (to_msec > 2147483)
- to_msec = 2147483;
- to_msec *= 1000;
- } else
- to_msec = -1;
-
- /* Set up the descriptors to be polled. */
- i = 0;
- for (l = protocols; l; l = l -> next) {
- fds [i].fd = l -> fd;
- fds [i].events = POLLIN;
- fds [i].revents = 0;
- ++i;
- }
-
- /* Wait for a packet or a timeout... XXX */
- count = poll (fds, nfds, to_msec);
-
- /* Get the current time... */
- GET_TIME (&cur_time);
-
- /* Not likely to be transitory... */
- if (count < 0) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- else
- error ("poll: %m");
- }
-
- i = 0;
- for (l = protocols; l; l = l -> next) {
- if ((fds [i].revents & POLLIN)) {
- fds [i].revents = 0;
- if (l -> handler)
- (*(l -> handler)) (l);
- if (interfaces_invalidated)
- break;
- }
- ++i;
- }
- interfaces_invalidated = 0;
- } while (1);
-}
-#else
/* 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
@@ -622,135 +94,32 @@ void dispatch ()
void dispatch ()
{
- fd_set r, w, x;
- struct protocol *l;
- int max = 0;
- int count;
struct timeval tv, *tvp;
+ isc_result_t status;
- FD_ZERO (&w);
- FD_ZERO (&x);
-
+ /* Wait for a packet or a timeout... XXX */
do {
- /* Call any expired timeouts, and then if there's
- still a timeout registered, time out the select
- call then. */
- another:
- if (timeouts) {
- struct timeout *t;
- if (timeouts -> when <= cur_time) {
- t = timeouts;
- timeouts = timeouts -> next;
- (*(t -> func)) (t -> what);
- t -> next = free_timeouts;
- free_timeouts = t;
- goto another;
- }
- tv.tv_sec = timeouts -> when - cur_time;
- tv.tv_usec = 0;
- tvp = &tv;
- } else
- tvp = (struct timeval *)0;
-
- /* Set up the read mask. */
- FD_ZERO (&r);
-
- for (l = protocols; l; l = l -> next) {
- FD_SET (l -> fd, &r);
- if (l -> fd > max)
- max = l -> fd;
- }
-
- /* Wait for a packet or a timeout... XXX */
- count = select (max + 1, &r, &w, &x, tvp);
-
- /* Get the current time... */
- GET_TIME (&cur_time);
-
- /* Not likely to be transitory... */
- if (count < 0)
- error ("select: %m");
-
- for (l = protocols; l; l = l -> next) {
- if (!FD_ISSET (l -> fd, &r))
- continue;
- if (l -> handler)
- (*(l -> handler)) (l);
- if (interfaces_invalidated)
- break;
- }
- interfaces_invalidated = 0;
- } while (1);
-}
-#endif /* USE_POLL */
-
-void got_one (l)
- struct protocol *l;
-{
- 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 = l -> local;
-
- if ((result =
- receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) {
- warn ("receive_packet failed on %s: %m", ip -> name);
- return;
- }
- if (result == 0)
- return;
-
- if (bootp_packet_handler) {
- ifrom.len = 4;
- memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
-
- (*bootp_packet_handler) (ip, &u.packet, result,
- from.sin_port, ifrom, &hfrom);
- }
-}
-
-int locate_network (packet)
- struct packet *packet;
-{
- struct iaddr ia;
-
- /* If this came through a gateway, find the corresponding subnet... */
- if (packet -> raw -> giaddr.s_addr) {
- struct subnet *subnet;
- ia.len = 4;
- memcpy (ia.iabuf, &packet -> raw -> giaddr, 4);
- subnet = find_subnet (ia);
- if (subnet)
- packet -> shared_network = subnet -> shared_network;
- else
- packet -> shared_network = (struct shared_network *)0;
- } else {
- packet -> shared_network =
- packet -> interface -> shared_network;
- }
- if (packet -> shared_network)
- return 1;
- return 0;
+ tvp = process_outstanding_timeouts (&tv);
+ status = omapi_one_dispatch (0, tvp);
+ } while (status == ISC_R_TIMEDOUT || status == ISC_R_SUCCESS);
+ log_fatal ("omapi_one_dispatch failed: %s -- exiting.",
+ isc_result_totext (status));
}
-void add_timeout (when, where, what)
+void add_timeout (when, where, what, ref, unref)
TIME when;
void (*where) PROTO ((void *));
void *what;
+ tvref_t ref;
+ tvunref_t unref;
{
struct timeout *t, *q;
/* See if this timeout supersedes an existing timeout. */
t = (struct timeout *)0;
for (q = timeouts; q; q = q -> next) {
- if (q -> func == where && q -> what == what) {
+ if ((where == NULL || q -> func == where) &&
+ q -> what == what) {
if (t)
t -> next = q -> next;
else
@@ -766,15 +135,20 @@ void add_timeout (when, where, what)
if (free_timeouts) {
q = free_timeouts;
free_timeouts = q -> next;
- q -> func = where;
- q -> what = what;
} else {
- q = (struct timeout *)malloc (sizeof (struct timeout));
+ q = ((struct timeout *)
+ dmalloc (sizeof (struct timeout), MDL));
if (!q)
- error ("Can't allocate timeout structure!");
- q -> func = where;
+ 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;
- }
}
q -> when = when;
@@ -823,46 +197,32 @@ void cancel_timeout (where, what)
/* If we found the timeout, put it on the free list. */
if (q) {
+ if (q -> unref)
+ (*q -> unref) (&q -> what, MDL);
q -> next = free_timeouts;
free_timeouts = q;
}
}
-/* Add a protocol to the list of protocols... */
-void add_protocol (name, fd, handler, local)
- char *name;
- int fd;
- void (*handler) PROTO ((struct protocol *));
- void *local;
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void cancel_all_timeouts ()
{
- struct protocol *p;
-
- p = (struct protocol *)malloc (sizeof *p);
- if (!p)
- error ("can't allocate protocol struct for %s", name);
-
- p -> fd = fd;
- p -> handler = handler;
- p -> local = local;
-
- p -> next = protocols;
- protocols = p;
+ struct timeout *t, *n;
+ for (t = timeouts; t; t = n) {
+ n = t -> next;
+ if (t -> unref && t -> what)
+ (*t -> unref) (&t -> what, MDL);
+ t -> next = free_timeouts;
+ free_timeouts = t;
+ }
}
-void remove_protocol (proto)
- struct protocol *proto;
+void relinquish_timeouts ()
{
- struct protocol *p, *next, *prev;
-
- prev = (struct protocol *)0;
- for (p = protocols; p; p = next) {
- next = p -> next;
- if (p == proto) {
- if (prev)
- prev -> next = p -> next;
- else
- protocols = p -> next;
- free (p);
- }
+ struct timeout *t, *n;
+ for (t = free_timeouts; t; t = n) {
+ n = t -> next;
+ dfree (t, MDL);
}
}
+#endif
diff --git a/contrib/isc-dhcp/common/dlpi.c b/contrib/isc-dhcp/common/dlpi.c
new file mode 100644
index 000000000000..4cbc332ac2df
--- /dev/null
+++ b/contrib/isc-dhcp/common/dlpi.c
@@ -0,0 +1,1345 @@
+/* dlpi.c
+
+ Data Link Provider Interface (DLPI) network interface code. */
+
+/*
+ * Copyright (c) 1996-2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software was written for the Internet Software Consortium
+ * by Eric James Negaard, <lmdejn@lmd.ericsson.se>. To learn more about
+ * the Internet Software Consortium, see ``http://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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"$Id: dlpi.c,v 1.28 2001/04/05 20:53:01 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+
+#if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE)
+
+# include <sys/ioctl.h>
+# include <sys/time.h>
+# include <sys/dlpi.h>
+# include <stropts.h>
+# ifdef USE_DLPI_PFMOD
+# include <sys/pfmod.h>
+# endif
+# ifdef USE_POLL
+# include <poll.h>
+# endif
+
+# 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
+
+static int strioctl PROTO ((int fd, int cmd, int timeout, int len, char *dp));
+
+#define DLPI_MAXDLBUF 8192 /* Buffer size */
+#define DLPI_MAXDLADDR 1024 /* Max address size */
+#define DLPI_DEVDIR "/dev/" /* Device directory */
+
+static int dlpiopen PROTO ((char *ifname));
+static int dlpiunit PROTO ((char *ifname));
+static int dlpiinforeq PROTO ((int fd));
+static int dlpiphysaddrreq PROTO ((int fd, unsigned long addrtype));
+static int dlpiattachreq PROTO ((int fd, unsigned long ppa));
+static int dlpibindreq PROTO ((int fd, unsigned long sap, unsigned long max_conind,
+ unsigned long service_mode, unsigned long conn_mgmt,
+ unsigned long xidtest));
+static int dlpidetachreq PROTO ((int fd));
+static int dlpiunbindreq PROTO ((int fd));
+static int dlpiokack PROTO ((int fd, char *bufp));
+static int dlpiinfoack PROTO ((int fd, char *bufp));
+static int dlpiphysaddrack PROTO ((int fd, char *bufp));
+static int dlpibindack PROTO ((int fd, char *bufp));
+static int dlpiunitdatareq PROTO ((int fd, unsigned char *addr,
+ int addrlen, unsigned long minpri,
+ unsigned long maxpri, unsigned char *data,
+ int datalen));
+static int dlpiunitdataind PROTO ((int fd,
+ unsigned char *dstaddr,
+ unsigned long *dstaddrlen,
+ unsigned char *srcaddr,
+ unsigned long *srcaddrlen,
+ unsigned long *grpaddr,
+ unsigned char *data,
+ int datalen));
+
+# ifndef USE_POLL
+static void sigalrm PROTO ((int sig));
+# endif
+static int expected PROTO ((unsigned long prim, union DL_primitives *dlp,
+ int msgflags));
+static int strgetmsg PROTO ((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 %ld",
+ info -> name, 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;
+}
+
+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;
+ }
+}
+
+#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;
+{
+ unsigned hbufp = 0;
+ double hh [32];
+ 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);
+
+ 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 flags = 0;
+ int length = 0;
+ int offset = 0;
+ int rslt;
+ int bufix = 0;
+
+#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) {
+ 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], (char *) &srcaddr [phys_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, (unsigned char *)0, length);
+
+ /* If the IP or UDP checksum was bad, skip the packet... */
+ if (offset < 0) {
+ return 0;
+ }
+
+ bufix += offset;
+ length -= offset;
+
+ /* Copy out the data in the packet... */
+ memcpy (buf, &dbuf [bufix], length);
+ return length;
+}
+#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;
+{
+ int fd;
+ char *cp, *dp, *ep;
+ 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 (ifname)
+ char *ifname;
+{
+ char devname [50];
+ char *cp, *dp, *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);
+}
+
+/*
+ * dlpiunbindreq - send a request to unbind.
+ */
+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.
+ */
+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);
+}
+
+
+/*
+ * 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) < 0) {
+ 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) < 0) {
+ 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) < 0) {
+ 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) < 0) {
+ return -1;
+ }
+
+ if (ctl.len < sizeof (dl_phys_addr_ack_t)) {
+ /* Returned structure is too short */
+ return -1;
+ }
+
+ return 0;
+}
+
+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) {
+ 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;
+}
+
+/*
+ * 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 0;
+ }
+
+ if (dlp -> dl_primitive != prim) {
+ /* Incorrect/unexpected return message */
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * 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;
+#ifdef USE_POLL
+ struct pollfd pfd;
+ int count;
+ time_t now;
+ time_t starttime;
+ int to_msec;
+#endif
+
+#ifdef USE_POLL
+ 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;
+ }
+ }
+#else /* defined (USE_POLL) */
+ /*
+ * Start timer. Can't use select, since it might return true if there
+ * were non High-Priority data available on the stream.
+ */
+ (void) sigset (SIGALRM, sigalrm);
+
+ if (alarm (DLPI_MAXWAIT) < 0) {
+ /* log_fatal ("alarm: %m"); */
+ return -1;
+ }
+#endif /* !defined (USE_POLL) */
+
+ /*
+ * Set flags argument and issue getmsg ().
+ */
+ *flagsp = 0;
+ if ((result = getmsg (fd, ctlp, datap, flagsp)) < 0) {
+ return result;
+ }
+
+#ifndef USE_POLL
+ /*
+ * Stop timer.
+ */
+ if (alarm (0) < 0) {
+ /* log_fatal ("alarm: %m"); */
+ return -1;
+ }
+#endif
+
+ /*
+ * 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;
+}
+
+#ifndef USE_POLL
+/*
+ * sigalrm - handle alarms.
+ */
+static void sigalrm (sig)
+ int sig;
+{
+ fprintf (stderr, "strgetmsg: timeout");
+ exit (1);
+}
+#endif /* !defined (USE_POLL) */
+
+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 */
diff --git a/contrib/isc-dhcp/common/dns.c b/contrib/isc-dhcp/common/dns.c
new file mode 100644
index 000000000000..d529652730c5
--- /dev/null
+++ b/contrib/isc-dhcp/common/dns.c
@@ -0,0 +1,943 @@
+/* dns.c
+
+ Domain Name Service subroutines. */
+
+/*
+ * Copyright (c) 2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://www.isc.org/''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#ifndef lint
+static char copyright[] =
+"$Id: dns.c,v 1.35.2.10 2001/10/26 21:26:39 mellon Exp $ Copyright (c) 2001 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+#include "arpa/nameser.h"
+#include "dst/md5.h"
+
+/* This file is kind of a crutch for the BIND 8 nsupdate code, which has
+ * itself been cruelly hacked from its original state. What this code
+ * does is twofold: first, 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. Secondly, it maintains a set of named TSIG keys,
+ * and associates those keys with zones. When an update is requested for
+ * a particular zone, the key associated with that zone is used for the
+ * update.
+ *
+ * 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 or cached zone (we'll get to
+ * those in a second), then it tries to find a more specific zone. This
+ * is done by looking first for an SOA for GAZANGA.TOPANGA.FOO.COM. Then
+ * an SOA for TOPANGA.FOO.COM is sought. If during this search a predefined
+ * or cached zone is found, the update fails - there's something wrong
+ * somewhere.
+ *
+ * If a more specific zone _is_ found, that zone is cached for the length of
+ * its TTL in the same database as that described above. TSIG updates are
+ * never done for cached zones - 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 predefined or cached zone to update.
+ *
+ * When an update is attempted and no predefined or cached 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 or cached 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. When it finds one, it creates a cached
+ * zone and attempts an update, and gives up if the update fails.
+ *
+ * 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.
+ */
+
+dns_zone_hash_t *dns_zone_hash;
+
+#if defined (NSUPDATE)
+isc_result_t find_tsig_key (ns_tsig_key **key, const char *zname,
+ struct dns_zone *zone)
+{
+ isc_result_t status;
+ ns_tsig_key *tkey;
+
+ if (!zone)
+ return ISC_R_NOTFOUND;
+
+ if (!zone -> key) {
+ return ISC_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 ISC_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, 1, 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)
+{
+ struct dns_zone *tz = (struct dns_zone *)0;
+ 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;
+{
+ int i;
+ 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 (const char *dname, ns_class class,
+ char *zname, size_t zsize,
+ struct in_addr *addrs,
+ int naddrs, int *naddrout,
+ struct dns_zone **zcookie)
+{
+ isc_result_t status = ISC_R_NOTFOUND;
+ const char *np;
+ struct dns_zone *zone = (struct dns_zone *)0;
+ struct data_string nsaddrs;
+ int ix;
+
+ /* The absence of the zcookie pointer indicates that we
+ succeeded previously, but the update itself failed, meaning
+ that we shouldn't use the cached zone. */
+ if (!zcookie)
+ return ISC_R_NOTFOUND;
+
+ /* We can't look up a null zone. */
+ if (!dname || !*dname)
+ return ISC_R_INVALIDARG;
+
+ /* For each subzone, try to find a cached zone. */
+ for (np = dname; np; np = strchr (np, '.')) {
+ np++;
+ status = dns_zone_lookup (&zone, np);
+ if (status == ISC_R_SUCCESS)
+ break;
+ }
+
+ 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) > zsize) {
+ dns_zone_dereference (&zone, MDL);
+ return ISC_R_NOSPACE;
+ }
+ strcpy (zname, 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 < naddrs) {
+ if (ip + 4 > nsaddrs.len)
+ break;
+ memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
+ 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 < naddrs) {
+ if (ip + 4 > nsaddrs.len)
+ break;
+ memcpy (&addrs [ix], &nsaddrs.data [ip], 4);
+ ip += 4;
+ ix++;
+ }
+ data_string_forget (&nsaddrs, MDL);
+ }
+ }
+
+ /* It's not an error for zcookie to have a value here - actually,
+ it's quite likely, because res_nupdate cycles through all the
+ names in the update looking for their zones. */
+ if (!*zcookie)
+ dns_zone_reference (zcookie, zone, MDL);
+ dns_zone_dereference (&zone, MDL);
+ if (naddrout)
+ *naddrout = ix;
+ 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);
+}
+
+void cache_found_zone (ns_class class,
+ char *zname, struct in_addr *addrs, int naddrs)
+{
+ isc_result_t status = ISC_R_NOTFOUND;
+ struct dns_zone *zone = (struct dns_zone *)0;
+ struct data_string nsaddrs;
+ int ix = strlen (zname);
+
+ if (zname [ix - 1] == '.')
+ ix = 0;
+
+ /* See if there's already such a zone. */
+ if (dns_zone_lookup (&zone, zname) == ISC_R_SUCCESS) {
+ /* If it's not a dynamic zone, leave it alone. */
+ if (!zone -> timeout)
+ return;
+ /* Address may have changed, so just blow it away. */
+ if (zone -> primary)
+ option_cache_dereference (&zone -> primary, MDL);
+ if (zone -> secondary)
+ option_cache_dereference (&zone -> secondary, MDL);
+ } else if (!dns_zone_allocate (&zone, MDL))
+ return;
+
+ if (!zone -> name) {
+ zone -> name =
+ dmalloc (strlen (zname) + 1 + (ix != 0), MDL);
+ if (!zone -> name) {
+ dns_zone_dereference (&zone, MDL);
+ return;
+ }
+ strcpy (zone -> name, zname);
+ /* Add a trailing '.' if it was missing. */
+ if (ix) {
+ zone -> name [ix] = '.';
+ zone -> name [ix + 1] = 0;
+ }
+ }
+
+ /* XXX Need to get the lower-level code to push the actual zone
+ XXX TTL up to us. */
+ zone -> timeout = cur_time + 1800;
+
+ if (!option_cache_allocate (&zone -> primary, MDL)) {
+ dns_zone_dereference (&zone, MDL);
+ return;
+ }
+ if (!buffer_allocate (&zone -> primary -> data.buffer,
+ naddrs * sizeof (struct in_addr), MDL)) {
+ dns_zone_dereference (&zone, MDL);
+ return;
+ }
+ memcpy (zone -> primary -> data.buffer -> data,
+ addrs, naddrs * sizeof *addrs);
+ zone -> primary -> data.data =
+ &zone -> primary -> data.buffer -> data [0];
+ zone -> primary -> data.len = naddrs * sizeof *addrs;
+
+ enter_dns_zone (zone);
+}
+
+/* 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[MD5_DIGEST_LENGTH];
+ MD5_CTX 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 and NUL. */
+ if (!buffer_allocate (&id -> buffer,
+ (MD5_DIGEST_LENGTH * 2) + 3, 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
+ */
+
+ /* Put the type in the first two bytes. */
+ id -> buffer -> data [0] = "0123456789abcdef" [type >> 4];
+ id -> buffer -> data [1] = "0123456789abcdef" [type % 15];
+
+ /* Mash together an MD5 hash of the identifier. */
+ MD5_Init (&md5);
+ MD5_Update (&md5, data, len);
+ MD5_Final (buf, &md5);
+
+ /* Convert into ASCII. */
+ for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
+ id -> buffer -> data [i * 2 + 2] =
+ "0123456789abcdef" [(buf [i] >> 4) & 0xf];
+ id -> buffer -> data [i * 2 + 3] =
+ "0123456789abcdef" [buf [i] & 0xf];
+ }
+ id -> len = MD5_DIGEST_LENGTH * 2 + 2;
+ id -> buffer -> data [id -> len] = 0;
+ id -> terminated = 1;
+
+ return 1;
+}
+
+/* Now for the DDNS update code that is shared between client and
+ server... */
+
+isc_result_t ddns_update_a (struct data_string *ddns_fwd_name,
+ struct iaddr ddns_addr,
+ struct data_string *ddns_dhcid,
+ unsigned long ttl, int rrsetp)
+{
+ ns_updque updqueue;
+ ns_updrec *updrec;
+ isc_result_t result;
+ char ddns_address [16];
+
+ if (ddns_addr.len != 4)
+ return ISC_R_INVALIDARG;
+#ifndef NO_SNPRINTF
+ snprintf (ddns_address, 16, "%d.%d.%d.%d",
+ ddns_addr.iabuf[0], ddns_addr.iabuf[1],
+ ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+#else
+ sprintf (ddns_address, "%d.%d.%d.%d",
+ ddns_addr.iabuf[0], ddns_addr.iabuf[1],
+ ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+#endif
+
+ /*
+ * 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"
+ */
+
+ ISC_LIST_INIT (updqueue);
+
+ /*
+ * A RR does not exist.
+ */
+ updrec = minires_mkupdrec (S_PREREQ,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)0;
+ updrec -> r_size = 0;
+ updrec -> r_opcode = rrsetp ? NXRRSET : NXDOMAIN;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Add A RR.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, ttl);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)ddns_address;
+ updrec -> r_size = strlen (ddns_address);
+ updrec -> r_opcode = ADD;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Add DHCID RR.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_DHCID, ttl);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = ddns_dhcid -> data;
+ updrec -> r_size = ddns_dhcid -> len;
+ updrec -> r_opcode = ADD;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Attempt to perform the update.
+ */
+ result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
+
+ print_dns_status ((int)result, &updqueue);
+
+ while (!ISC_LIST_EMPTY (updqueue)) {
+ updrec = ISC_LIST_HEAD (updqueue);
+ ISC_LIST_UNLINK (updqueue, updrec, r_link);
+ minires_freeupdrec (updrec);
+ }
+
+
+ /*
+ * If this update operation 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 (result == ISC_R_SUCCESS)
+ return result;
+
+
+ /*
+ * 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"
+ */
+
+ if (result != (rrsetp ? ISC_R_YXRRSET : ISC_R_YXDOMAIN))
+ return result;
+
+
+ /*
+ * DHCID RR exists, and matches client identity.
+ */
+ updrec = minires_mkupdrec (S_PREREQ,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_DHCID, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = ddns_dhcid -> data;
+ updrec -> r_size = ddns_dhcid -> len;
+ updrec -> r_opcode = YXRRSET;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Delete A RRset.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)0;
+ updrec -> r_size = 0;
+ updrec -> r_opcode = DELETE;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Add A RR.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, ttl);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)ddns_address;
+ updrec -> r_size = strlen (ddns_address);
+ updrec -> r_opcode = ADD;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Attempt to perform the update.
+ */
+ result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
+
+ print_dns_status ((int)result, &updqueue);
+
+ /*
+ * If this 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"
+ */
+
+ error:
+ while (!ISC_LIST_EMPTY (updqueue)) {
+ updrec = ISC_LIST_HEAD (updqueue);
+ ISC_LIST_UNLINK (updqueue, updrec, r_link);
+ minires_freeupdrec (updrec);
+ }
+
+ return result;
+}
+
+isc_result_t ddns_remove_a (struct data_string *ddns_fwd_name,
+ struct iaddr ddns_addr,
+ struct data_string *ddns_dhcid)
+{
+ ns_updque updqueue;
+ ns_updrec *updrec;
+ isc_result_t result = SERVFAIL;
+ char ddns_address [16];
+
+ if (ddns_addr.len != 4)
+ return ISC_R_INVALIDARG;
+
+#ifndef NO_SNPRINTF
+ snprintf (ddns_address, 16, "%d.%d.%d.%d",
+ ddns_addr.iabuf[0], ddns_addr.iabuf[1],
+ ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+#else
+ sprintf (ddns_address, "%d.%d.%d.%d",
+ ddns_addr.iabuf[0], ddns_addr.iabuf[1],
+ ddns_addr.iabuf[2], ddns_addr.iabuf[3]);
+#endif
+
+
+ /*
+ * 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"
+ */
+
+ ISC_LIST_INIT (updqueue);
+
+ /*
+ * DHCID RR exists, and matches client identity.
+ */
+ updrec = minires_mkupdrec (S_PREREQ,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_DHCID,0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = ddns_dhcid -> data;
+ updrec -> r_size = ddns_dhcid -> len;
+ updrec -> r_opcode = YXRRSET;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * A RR matches the expiring lease.
+ */
+ updrec = minires_mkupdrec (S_PREREQ,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)ddns_address;
+ updrec -> r_size = strlen (ddns_address);
+ updrec -> r_opcode = YXRRSET;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+
+ /*
+ * Delete appropriate A RR.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)ddns_address;
+ updrec -> r_size = strlen (ddns_address);
+ updrec -> r_opcode = DELETE;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+ /*
+ * Attempt to perform the update.
+ */
+ result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
+ print_dns_status ((int)result, &updqueue);
+
+ /*
+ * If the 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"
+ */
+
+ if (result != ISC_R_SUCCESS) {
+ /* If the rrset isn't there, we didn't need to do the
+ delete, which is success. */
+ if (result == ISC_R_NXRRSET || result == ISC_R_NXDOMAIN)
+ result = ISC_R_SUCCESS;
+ goto error;
+ }
+
+ while (!ISC_LIST_EMPTY (updqueue)) {
+ updrec = ISC_LIST_HEAD (updqueue);
+ ISC_LIST_UNLINK (updqueue, updrec, r_link);
+ minires_freeupdrec (updrec);
+ }
+
+ /* If the deletion of the A succeeded, and there are no A 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 A has been added to this
+ domain name. */
+ ISC_LIST_INIT (updqueue);
+
+ /*
+ * A RR does not exist.
+ */
+ updrec = minires_mkupdrec (S_PREREQ,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_A, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = (unsigned char *)0;
+ updrec -> r_size = 0;
+ updrec -> r_opcode = NXRRSET;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+ /*
+ * Delete appropriate DHCID RR.
+ */
+ updrec = minires_mkupdrec (S_UPDATE,
+ (const char *)ddns_fwd_name -> data,
+ C_IN, T_DHCID, 0);
+ if (!updrec) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ updrec -> r_data = ddns_dhcid -> data;
+ updrec -> r_size = ddns_dhcid -> len;
+ updrec -> r_opcode = DELETE;
+
+ ISC_LIST_APPEND (updqueue, updrec, r_link);
+
+ /*
+ * Attempt to perform the update.
+ */
+ result = minires_nupdate (&resolver_state, ISC_LIST_HEAD (updqueue));
+ print_dns_status ((int)result, &updqueue);
+
+ /* Fall through. */
+ error:
+
+ while (!ISC_LIST_EMPTY (updqueue)) {
+ updrec = ISC_LIST_HEAD (updqueue);
+ ISC_LIST_UNLINK (updqueue, updrec, r_link);
+ minires_freeupdrec (updrec);
+ }
+
+ return result;
+}
+
+
+#endif /* NSUPDATE */
+
+HASH_FUNCTIONS (dns_zone, const char *, struct dns_zone, dns_zone_hash_t,
+ dns_zone_reference, dns_zone_dereference)
diff --git a/contrib/isc-dhcp/common/ethernet.c b/contrib/isc-dhcp/common/ethernet.c
index 41c0a309e03d..309084a8766a 100644
--- a/contrib/isc-dhcp/common/ethernet.c
+++ b/contrib/isc-dhcp/common/ethernet.c
@@ -1,9 +1,9 @@
-/* packet.c
+/* ethernet.c
Packet assembly code, originally contributed by Archie Cobbs. */
/*
- * Copyright (c) 1995, 1996 The Internet Software Consortium.
+ * Copyright (c) 1996-2000 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,15 +34,16 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: ethernet.c,v 1.1.2.2 1999/11/11 16:10:41 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: ethernet.c,v 1.6.2.1 2001/06/14 19:15:27 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -53,31 +54,27 @@ static char copyright[] =
#if defined (PACKET_ASSEMBLY)
/* Assemble an hardware header... */
-/* XXX currently only supports ethernet; doesn't check for other types. */
void assemble_ethernet_header (interface, buf, bufix, to)
struct interface_info *interface;
unsigned char *buf;
- int *bufix;
+ unsigned *bufix;
struct hardware *to;
{
- struct ether_header eh;
+ struct isc_ether_header eh;
- if (to && to -> hlen == 6) /* XXX */
- memcpy (eh.ether_dhost, to -> haddr, sizeof eh.ether_dhost);
+ 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 == sizeof (eh.ether_shost))
- memcpy (eh.ether_shost, interface -> hw_address.haddr,
+ 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));
-#ifdef BROKEN_FREEBSD_BPF /* Fixed in FreeBSD 2.2 */
- eh.ether_type = ETHERTYPE_IP;
-#else
eh.ether_type = htons (ETHERTYPE_IP);
-#endif
memcpy (&buf [*bufix], &eh, ETHER_HEADER_SIZE);
*bufix += ETHER_HEADER_SIZE;
@@ -90,10 +87,10 @@ void assemble_ethernet_header (interface, buf, bufix, to)
ssize_t decode_ethernet_header (interface, buf, bufix, from)
struct interface_info *interface;
unsigned char *buf;
- int bufix;
+ unsigned bufix;
struct hardware *from;
{
- struct ether_header eh;
+ struct isc_ether_header eh;
memcpy (&eh, buf + bufix, ETHER_HEADER_SIZE);
@@ -101,10 +98,10 @@ ssize_t decode_ethernet_header (interface, buf, bufix, from)
if (ntohs (eh.ether_type) != ETHERTYPE_IP)
return -1;
#endif
- memcpy (from -> haddr, eh.ether_shost, sizeof (eh.ether_shost));
- from -> htype = ARPHRD_ETHER;
- from -> hlen = sizeof eh.ether_shost;
+ memcpy (&from -> hbuf [1], eh.ether_shost, sizeof (eh.ether_shost));
+ from -> hbuf [0] = ARPHRD_ETHER;
+ from -> hlen = (sizeof eh.ether_shost) + 1;
- return sizeof eh;
+ return ETHER_HEADER_SIZE;
}
#endif /* PACKET_DECODING */
diff --git a/contrib/isc-dhcp/common/execute.c b/contrib/isc-dhcp/common/execute.c
new file mode 100644
index 000000000000..ec4b22ee5a3a
--- /dev/null
+++ b/contrib/isc-dhcp/common/execute.c
@@ -0,0 +1,1061 @@
+/* execute.c
+
+ Support for executable statements. */
+
+/*
+ * Copyright (c) 1998-2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
+static char copyright[] =
+"$Id: execute.c,v 1.44.2.8 2001/10/18 20:10:58 mellon Exp $ Copyright (c) 1998-2000 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.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;
+ unsigned long num;
+ struct binding_scope *outer;
+ 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 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 {
+ badalloc:
+ 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;
+ }
+ }
+ }
+ 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.buffer -> data);
+ break;
+ case log_priority_error:
+ log_error ("%.*s", (int)ds.len,
+ ds.buffer -> data);
+ break;
+ case log_priority_debug:
+ log_debug ("%.*s", (int)ds.len,
+ ds.buffer -> data);
+ break;
+ case log_priority_info:
+ log_info ("%.*s", (int)ds.len,
+ ds.buffer -> 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;
+{
+ struct executable_statement *bp;
+
+ 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 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;
+{
+ struct executable_statement *r, *x;
+ int result;
+ int status;
+ 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, col, 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;
+
+ 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;
+ unsigned long foo;
+
+ if (is_data_expression (expr)) {
+ struct executable_statement *e;
+ 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;
+ int result;
+
+ 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:
+ break;
+ }
+ }
+ return ok;
+}
diff --git a/contrib/isc-dhcp/common/fddi.c b/contrib/isc-dhcp/common/fddi.c
new file mode 100644
index 000000000000..d73d4509f271
--- /dev/null
+++ b/contrib/isc-dhcp/common/fddi.c
@@ -0,0 +1,106 @@
+/* fddi.c
+
+ Packet assembly code, originally contributed by Archie Cobbs. */
+
+/*
+ * Copyright (c) 1996-2000 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
+static char copyright[] =
+"$Id: fddi.c,v 1.3 2000/04/18 23:02:09 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
+#endif /* not lint */
+
+#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/contrib/isc-dhcp/common/icmp.c b/contrib/isc-dhcp/common/icmp.c
index a9d7ec410553..299e03079ebb 100644
--- a/contrib/isc-dhcp/common/icmp.c
+++ b/contrib/isc-dhcp/common/icmp.c
@@ -1,10 +1,10 @@
-/* icmp.c
+/* dhcp.c
ICMP Protocol engine - for sending out pings and receiving
responses. */
/*
- * Copyright (c) 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1996-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,23 +35,32 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: icmp.c,v 1.7.2.2 1999/03/29 23:20:00 mellon Exp $ Copyright (c) 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: icmp.c,v 1.30.2.3 2001/10/18 20:11:24 mellon Exp $ Copyright (c) 1996-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
#include "netinet/ip.h"
#include "netinet/ip_icmp.h"
-static int icmp_protocol_initialized;
-static int icmp_protocol_fd;
+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. */
@@ -61,31 +70,83 @@ void icmp_startup (routep, handler)
{
struct protoent *proto;
int protocol = 1;
+ struct sockaddr_in from;
+ int fd;
int state;
+ struct icmp_state *new;
+ omapi_object_t *h;
+ isc_result_t result;
/* Only initialize icmp once. */
- if (icmp_protocol_initialized)
- error ("attempted to reinitialize icmp protocol");
- icmp_protocol_initialized = 1;
-
- /* 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_protocol_fd = socket (AF_INET, SOCK_RAW, protocol);
- if (icmp_protocol_fd < 0)
- error ("unable to create icmp socket: %m");
-
- /* Make sure it does routing... */
- state = 0;
- if (setsockopt (icmp_protocol_fd, SOL_SOCKET, SO_DONTROUTE,
- (char *)&state, sizeof state) < 0)
- error ("Unable to disable SO_DONTROUTE on ICMP socket: %m");
-
- add_protocol ("icmp", icmp_protocol_fd, icmp_echoreply,
- (void *)handler);
+ 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)
@@ -94,9 +155,14 @@ int icmp_echorequest (addr)
struct sockaddr_in to;
struct icmp icmp;
int status;
+#if defined (TRACING)
+ trace_iov_t iov [2];
+#endif
- if (!icmp_protocol_initialized)
- error ("attempt to use ICMP protocol before initialization.");
+ if (no_icmp)
+ return 1;
+ if (!icmp_state)
+ log_fatal ("ICMP protocol used before initialization.");
#ifdef HAVE_SA_LEN
to.sin_len = sizeof to;
@@ -115,61 +181,145 @@ int icmp_echorequest (addr)
#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));
- /* Send the ICMP packet... */
- status = sendto (icmp_protocol_fd, (char *)&icmp, sizeof icmp, 0,
- (struct sockaddr *)&to, sizeof to);
- if (status < 0)
- warn ("icmp_echorequest %s: %m", inet_ntoa(to.sin_addr));
+#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 (status != sizeof icmp)
+ return 0;
+#if defined (TRACING)
+ }
+#endif
return 1;
}
-void icmp_echoreply (protocol)
- struct protocol *protocol;
+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;
- int len;
+ SOCKLEN_T sl;
+ int hlen, len;
struct iaddr ia;
- void (*handler) PROTO ((struct iaddr, u_int8_t *, int));
+ struct icmp_state *state;
+#if defined (TRACING)
+ trace_iov_t iov [2];
+#endif
+
+ state = (struct icmp_state *)h;
- len = sizeof from;
- status = recvfrom (protocol -> fd, (char *)icbuf, sizeof icbuf, 0,
- (struct sockaddr *)&from, &len);
+ sl = sizeof from;
+ status = recvfrom (state -> socket, (char *)icbuf, sizeof icbuf, 0,
+ (struct sockaddr *)&from, &sl);
if (status < 0) {
- warn ("icmp_echoreply: %m");
- return;
+ log_error ("icmp_echoreply: %m");
+ return ISC_R_UNEXPECTED;
}
- /* Probably not for us. */
- if (status < (sizeof (struct ip)) + (sizeof *icfrom)) {
- return;
+ /* 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 - sizeof (struct ip);
- icfrom = (struct icmp *)(icbuf + sizeof (struct ip));
+ len = status - hlen;
+ icfrom = (struct icmp *)(icbuf + hlen);
/* Silently discard ICMP packets that aren't echoreplies. */
if (icfrom -> icmp_type != ICMP_ECHOREPLY) {
- return;
+ return ISC_R_SUCCESS;
}
/* If we were given a second-stage handler, call it. */
- if (protocol -> local) {
- handler = ((void (*) PROTO ((struct iaddr,
- u_int8_t *, int)))
- protocol -> local);
+ if (state -> icmp_handler) {
memcpy (ia.iabuf, &from.sin_addr, sizeof from.sin_addr);
ia.len = sizeof from.sin_addr;
- (*handler) (ia, icbuf, len);
+#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;
+ unsigned len;
+ 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/contrib/isc-dhcp/common/inet.c b/contrib/isc-dhcp/common/inet.c
index 527afbb8b708..158440e35e7e 100644
--- a/contrib/isc-dhcp/common/inet.c
+++ b/contrib/isc-dhcp/common/inet.c
@@ -4,7 +4,8 @@
way... */
/*
- * Copyright (c) 1996 The Internet Software Consortium. All rights reserved.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,15 +35,16 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: inet.c,v 1.5.2.2 1999/04/24 16:48:10 mellon Exp $ Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
+"$Id: inet.c,v 1.8.2.3 2001/06/21 16:59:00 mellon Exp $ Copyright (c) 1995-1999 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -119,7 +121,7 @@ struct iaddr broadcast_addr (subnet, mask)
struct iaddr subnet;
struct iaddr mask;
{
- int i;
+ int i, j, k;
struct iaddr rv;
if (subnet.len != mask.len) {
@@ -181,3 +183,60 @@ char *piaddr (addr)
}
return pbuf;
}
+
+char *piaddr1 (addr)
+ struct iaddr addr;
+{
+ static char pbuf [4 * 16];
+ char *s = pbuf;
+ int i;
+
+ if (addr.len == 0) {
+ strcpy (s, "<null address>");
+ }
+ for (i = 0; i < addr.len; i++) {
+ sprintf (s, "%s%d", i ? "." : "", addr.iabuf [i]);
+ s += strlen (s);
+ }
+ return pbuf;
+}
+
+char *piaddrmask (struct iaddr addr, struct iaddr mask,
+ const char *file, int line)
+{
+ char *s, *t;
+ int i, mw;
+ unsigned len;
+
+ for (i = 0; i < 32; i++) {
+ if (!mask.iabuf [3 - i / 8])
+ i += 7;
+ else if (mask.iabuf [3 - i / 8] & (1 << (i % 8)))
+ break;
+ }
+ mw = 32 - i;
+ len = mw > 9 ? 2 : 1;
+ len += 4; /* three dots and a slash. */
+ for (i = 0; i < (mw / 8) + 1; i++) {
+ if (addr.iabuf [i] > 99)
+ len += 3;
+ else if (addr.iabuf [i] > 9)
+ len += 2;
+ else
+ len++;
+ }
+ s = dmalloc (len + 1, file, line);
+ if (!s)
+ return s;
+ t = s;
+ sprintf (t, "%d", addr.iabuf [0]);
+ t += strlen (t);
+ for (i = 1; i < (mw / 8) + 1; i++) {
+ sprintf (t, ".%d", addr.iabuf [i]);
+ t += strlen (t);
+ }
+ *t++ = '/';
+ sprintf (t, "%d", mw);
+ return s;
+}
+
diff --git a/contrib/isc-dhcp/common/lpf.c b/contrib/isc-dhcp/common/lpf.c
index 14dd21af7adb..da03c31115b1 100644
--- a/contrib/isc-dhcp/common/lpf.c
+++ b/contrib/isc-dhcp/common/lpf.c
@@ -4,8 +4,8 @@
Support Services in Vancouver, B.C. */
/*
- * Copyright (c) 1995, 1996, 1998, 1999
- * The Internet Software Consortium. All rights reserved.
+ * Copyright (c) 1996-2000 Internet Software Consortium.
+ * All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,17 +33,11 @@
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
*/
#ifndef lint
static char copyright[] =
-"$Id: lpf.c,v 1.1.2.10 1999/10/25 15:39:02 mellon Exp $ Copyright (c) 1995, 1996, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
+"$Id: lpf.c,v 1.29 2001/04/24 00:36:00 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -59,9 +53,6 @@ static char copyright[] =
#include "includes/netinet/udp.h"
#include "includes/netinet/if_ether.h"
-static void lpf_gen_filter_setup PROTO ((struct interface_info *));
-static void lpf_tr_filter_setup PROTO ((struct interface_info *));
-
/* Reinitializes the specified interface after an address change. This
is not required for packet-filter APIs. */
@@ -92,16 +83,19 @@ int if_register_lpf (info)
struct sockaddr sa;
/* Make an LPF socket. */
- if ((sock = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) {
+ 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) {
- warn ("socket: %m");
- error ("Make sure to set %s %s!",
- "CONFIG_PACKET=y and CONFIG_FILTER=y",
- "in your kernel configuration");
+ 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!");
}
- error("Open a socket for LPF: %m");
+ log_fatal ("Open a socket for LPF: %m");
}
/* Bind to the interface name */
@@ -112,12 +106,14 @@ int if_register_lpf (info)
if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
errno == EAFNOSUPPORT || errno == EINVAL) {
- warn ("bind: %m");
- error ("Set %s %s!",
- "CONFIG_PACKET=y and CONFIG_FILTER=y",
- "in your kernel configuration");
+ 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!");
}
- error("Bind socket to interface: %m");
+ log_fatal ("Bind socket to interface: %m");
}
return sock;
@@ -131,16 +127,38 @@ void if_register_send (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, interface);
+ info -> wfdesc = if_register_lpf (info);
#else
info -> wfdesc = info -> rfdesc;
#endif
if (!quiet_interface_discovery)
- note ("Sending on LPF/%s/%s%s%s",
+ 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.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
+ 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 : ""));
@@ -152,11 +170,14 @@ void if_register_send (info)
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 *);
-static void lpf_tr_filter_setup (struct interface_info *);
void if_register_receive (info)
struct interface_info *info;
@@ -164,20 +185,40 @@ void if_register_receive (info)
/* Open a LPF device and hang it on this interface... */
info -> rfdesc = if_register_lpf (info);
- if (info -> hw_address.htype == HTYPE_IEEE802)
+#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)
- note ("Listening on LPF/%s/%s%s%s",
- info -> name,
- print_hw_addr (info -> hw_address.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
- (info -> shared_network ? "/" : ""),
- (info -> shared_network ?
- info -> shared_network -> name : ""));
+ 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)
@@ -193,20 +234,25 @@ static void lpf_gen_filter_setup (info)
/* 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 (local_port);
+ 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)
- error ("socket: %m - make sure %s %s!",
- "CONFIG_PACKET and CONFIG_FILTER are defined",
- "in your kernel configuration");
- error ("Can't install packet filter program: %m");
+ 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;
{
@@ -228,13 +274,18 @@ static void lpf_tr_filter_setup (info)
sizeof p) < 0) {
if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
- errno == EAFNOSUPPORT)
- error ("socket: %m - make sure %s %s!",
- "CONFIG_PACKET and CONFIG_FILTER are defined",
- "in your kernel configuration");
- error ("Can't install packet filter program: %m");
+ 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
@@ -247,21 +298,27 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
struct sockaddr_in *to;
struct hardware *hto;
{
- int bufp = 0;
- unsigned char buf [1500];
+ unsigned hbufp = 0, ibufp = 0;
+ double hh [16];
+ double ih [1536 / sizeof (double)];
+ unsigned char *buf = (unsigned char *)ih;
struct sockaddr sa;
int result;
+ int fudge;
if (!strcmp (interface -> name, "fallback"))
return send_fallback (interface, packet, raw,
len, from, to, hto);
/* Assemble the headers... */
- assemble_hw_header (interface, buf, &bufp, hto);
- assemble_udp_ip_header (interface, buf, &bufp, from.s_addr,
+ 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 + bufp, 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. */
@@ -270,10 +327,10 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
strncpy (sa.sa_data,
(const char *)interface -> ifp, sizeof sa.sa_data);
- result = sendto (interface -> wfdesc, buf, bufp + len, 0,
- &sa, sizeof sa);
+ result = sendto (interface -> wfdesc,
+ buf + fudge, ibufp + len - fudge, 0, &sa, sizeof sa);
if (result < 0)
- warn ("send_packet: %m");
+ log_error ("send_packet: %m");
return result;
}
#endif /* USE_LPF_SEND */
@@ -289,8 +346,8 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int nread;
int length = 0;
int offset = 0;
- unsigned char ibuf [1500];
- int bufix = 0;
+ unsigned char ibuf [1536];
+ unsigned bufix = 0;
length = read (interface -> rfdesc, ibuf, sizeof ibuf);
if (length <= 0)
@@ -311,8 +368,8 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
length -= offset;
/* Decode the IP and UDP headers... */
- offset = decode_udp_ip_header (interface, ibuf, bufix,
- from, (unsigned char *)0, length);
+ offset = decode_udp_ip_header (interface, ibuf, bufix, from,
+ (unsigned char *)0, (unsigned)length);
/* If the IP or UDP checksum was bad, skip the packet... */
if (offset < 0)
@@ -326,7 +383,8 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
return length;
}
-int can_unicast_without_arp ()
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
{
return 1;
}
@@ -337,14 +395,25 @@ int can_receive_unicast_unconfigured (ip)
return 1;
}
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
void maybe_setup_fallback ()
{
- struct interface_info *fbi;
- fbi = setup_fallback ();
- if (fbi) {
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
if_register_fallback (fbi);
- add_protocol ("fallback", fallback_interface -> wfdesc,
- fallback_discard, fallback_interface);
+ 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/contrib/isc-dhcp/common/memory.c b/contrib/isc-dhcp/common/memory.c
index c293217eb272..3c6d913dcb43 100644
--- a/contrib/isc-dhcp/common/memory.c
+++ b/contrib/isc-dhcp/common/memory.c
@@ -3,7 +3,7 @@
Memory-resident database... */
/*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,907 +34,137 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: memory.c,v 1.35.2.4 1999/05/27 17:47:43 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: memory.c,v 1.66.2.3 2001/10/17 03:25:10 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-static struct subnet *subnets;
-static struct shared_network *shared_networks;
-static struct hash_table *host_hw_addr_hash;
-static struct hash_table *host_uid_hash;
-static struct hash_table *lease_uid_hash;
-static struct hash_table *lease_ip_addr_hash;
-static struct hash_table *lease_hw_addr_hash;
-static struct lease *dangling_leases;
-
-static struct hash_table *vendor_class_hash;
-static struct hash_table *user_class_hash;
-
-void enter_host (hd)
- struct host_decl *hd;
-{
- struct host_decl *hp = (struct host_decl *)0;
- struct host_decl *np = (struct host_decl *)0;
-
- hd -> n_ipaddr = (struct host_decl *)0;
-
- if (hd -> interface.hlen) {
- if (!host_hw_addr_hash)
- host_hw_addr_hash = new_hash ();
- else
- hp = (struct host_decl *)
- hash_lookup (host_hw_addr_hash,
- hd -> interface.haddr,
- hd -> interface.hlen);
-
- /* If there isn't already a host decl matching this
- address, add it to the hash table. */
- if (!hp)
- add_hash (host_hw_addr_hash,
- hd -> interface.haddr, hd -> interface.hlen,
- (unsigned char *)hd);
- }
-
- /* If there was already a host declaration for this hardware
- address, add this one to the end of the list. */
-
- if (hp) {
- for (np = hp; np -> n_ipaddr; np = np -> n_ipaddr)
- ;
- np -> n_ipaddr = hd;
- }
-
- if (hd -> group -> options [DHO_DHCP_CLIENT_IDENTIFIER]) {
- if (!tree_evaluate (hd -> group -> options
- [DHO_DHCP_CLIENT_IDENTIFIER]))
- return;
-
- /* 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) {
- host_uid_hash = new_hash ();
- hp = (struct host_decl *)0;
- } else
- hp = (struct host_decl *) hash_lookup
- (host_uid_hash,
- hd -> group -> options
- [DHO_DHCP_CLIENT_IDENTIFIER] -> value,
- hd -> group -> options
- [DHO_DHCP_CLIENT_IDENTIFIER] -> len);
-
- /* 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 (hp) {
- /* Don't link it in twice... */
- if (!np) {
- for (np = hp; np -> n_ipaddr;
- np = np -> n_ipaddr)
- ;
- np -> n_ipaddr = hd;
- }
- } else {
- add_hash (host_uid_hash,
- hd -> group -> options
- [DHO_DHCP_CLIENT_IDENTIFIER] -> value,
- hd -> group -> options
- [DHO_DHCP_CLIENT_IDENTIFIER] -> len,
- (unsigned char *)hd);
- }
- }
-}
-
-struct host_decl *find_hosts_by_haddr (htype, haddr, hlen)
- int htype;
- unsigned char *haddr;
- int hlen;
-{
- struct host_decl *foo;
-
- foo = (struct host_decl *)hash_lookup (host_hw_addr_hash,
- haddr, hlen);
- return foo;
-}
-
-struct host_decl *find_hosts_by_uid (data, len)
- unsigned char *data;
- int len;
-{
- struct host_decl *foo;
-
- foo = (struct host_decl *)hash_lookup (host_uid_hash, data, len);
- return foo;
-}
-
-/* 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. */
-
-struct subnet *find_host_for_network (host, addr, share)
- struct host_decl **host;
- struct iaddr *addr;
- struct shared_network *share;
-{
- int i;
- struct subnet *subnet;
- struct iaddr ip_address;
- struct host_decl *hp;
-
- for (hp = *host; hp; hp = hp -> n_ipaddr) {
- if (!hp -> fixed_addr || !tree_evaluate (hp -> fixed_addr))
- continue;
- for (i = 0; i < hp -> fixed_addr -> len; i += 4) {
- ip_address.len = 4;
- memcpy (ip_address.iabuf,
- hp -> fixed_addr -> value + i, 4);
- subnet = find_grouped_subnet (share, ip_address);
- if (subnet) {
- *addr = ip_address;
- *host = hp;
- return subnet;
- }
- }
- }
- return (struct subnet *)0;
-}
-
-void new_address_range (low, high, subnet, dynamic)
- struct iaddr low, high;
- struct subnet *subnet;
- int dynamic;
-{
- struct lease *address_range, *lp, *plp;
- struct iaddr net;
- int min, max, i;
- char lowbuf [16], highbuf [16], netbuf [16];
- struct shared_network *share = subnet -> shared_network;
- struct hostent *h;
- struct in_addr ia;
-
- /* All subnets should have attached shared network structures. */
- if (!share) {
- strcpy (netbuf, piaddr (subnet -> net));
- error ("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)
- lease_uid_hash = new_hash ();
- if (!lease_ip_addr_hash)
- lease_ip_addr_hash = new_hash ();
- if (!lease_hw_addr_hash)
- lease_hw_addr_hash = new_hash ();
-
- /* Make sure that high and low addresses are in same subnet. */
- net = subnet_number (low, subnet -> netmask);
- if (!addr_eq (net, subnet_number (high, subnet -> netmask))) {
- strcpy (lowbuf, piaddr (low));
- strcpy (highbuf, piaddr (high));
- strcpy (netbuf, piaddr (subnet -> netmask));
- error ("Address range %s to %s, netmask %s spans %s!",
- lowbuf, highbuf, netbuf, "multiple subnets");
- }
-
- /* Make sure that the addresses are on the correct subnet. */
- if (!addr_eq (net, subnet -> net)) {
- strcpy (lowbuf, piaddr (low));
- strcpy (highbuf, piaddr (high));
- strcpy (netbuf, piaddr (subnet -> netmask));
- error ("Address range %s to %s not on net %s/%s!",
- lowbuf, highbuf, piaddr (subnet -> net), netbuf);
- }
-
- /* 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. */
- address_range = new_leases (max - min + 1, "new_address_range");
- if (!address_range) {
- strcpy (lowbuf, piaddr (low));
- strcpy (highbuf, piaddr (high));
- error ("No memory for address range %s-%s.", lowbuf, highbuf);
- }
- memset (address_range, 0, (sizeof *address_range) * (max - min + 1));
-
- /* Fill in the last lease if it hasn't been already... */
- if (!share -> last_lease) {
- share -> last_lease = &address_range [0];
- }
-
- /* Fill out the lease structures with some minimal information. */
- for (i = 0; i < max - min + 1; i++) {
- address_range [i].ip_addr =
- ip_addr (subnet -> net, subnet -> netmask, i + min);
- address_range [i].starts =
- address_range [i].timestamp = MIN_TIME;
- address_range [i].ends = MIN_TIME;
- address_range [i].subnet = subnet;
- address_range [i].shared_network = share;
- address_range [i].flags = dynamic ? DYNAMIC_BOOTP_OK : 0;
-
- memcpy (&ia, address_range [i].ip_addr.iabuf, 4);
-
- if (subnet -> group -> get_lease_hostnames) {
- h = gethostbyaddr ((char *)&ia, sizeof ia, AF_INET);
- if (!h)
- warn ("No hostname for %s", inet_ntoa (ia));
- else {
- address_range [i].hostname =
- malloc (strlen (h -> h_name) + 1);
- if (!address_range [i].hostname)
- error ("no memory for hostname %s.",
- h -> h_name);
- strcpy (address_range [i].hostname,
- h -> h_name);
- }
- }
-
- /* Link this entry into the list. */
- address_range [i].next = share -> leases;
- address_range [i].prev = (struct lease *)0;
- share -> leases = &address_range [i];
- if (address_range [i].next)
- address_range [i].next -> prev = share -> leases;
- add_hash (lease_ip_addr_hash,
- address_range [i].ip_addr.iabuf,
- address_range [i].ip_addr.len,
- (unsigned char *)&address_range [i]);
- }
-
- /* Find out if any dangling leases are in range... */
- plp = (struct lease *)0;
- for (lp = dangling_leases; lp; lp = lp -> next) {
- struct iaddr lnet;
- int lhost;
-
- lnet = subnet_number (lp -> ip_addr, subnet -> netmask);
- lhost = host_addr (lp -> ip_addr, subnet -> netmask);
-
- /* If it's in range, fill in the real lease structure with
- the dangling lease's values, and remove the lease from
- the list of dangling leases. */
- if (addr_eq (lnet, subnet -> net) &&
- lhost >= i && lhost <= max) {
- if (plp) {
- plp -> next = lp -> next;
- } else {
- dangling_leases = lp -> next;
- }
- lp -> next = (struct lease *)0;
- address_range [lhost - i].hostname = lp -> hostname;
- address_range [lhost - i].client_hostname =
- lp -> client_hostname;
- supersede_lease (&address_range [lhost - i], lp, 0);
- free_lease (lp, "new_address_range");
- } else
- plp = lp;
- }
-}
-
-struct subnet *find_subnet (addr)
- struct iaddr addr;
-{
- struct subnet *rv;
-
- for (rv = subnets; rv; rv = rv -> next_subnet) {
- if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
- return rv;
- }
- return (struct subnet *)0;
-}
-
-struct subnet *find_grouped_subnet (share, addr)
- struct shared_network *share;
- struct iaddr addr;
-{
- struct subnet *rv;
-
- for (rv = share -> subnets; rv; rv = rv -> next_sibling) {
- if (addr_eq (subnet_number (addr, rv -> netmask), rv -> net))
- return rv;
- }
- return (struct subnet *)0;
-}
-
-int subnet_inner_than (subnet, scan, warnp)
- struct subnet *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 [16];
- int i, j;
- for (i = 0; i < 32; i++)
- if (subnet -> netmask.iabuf [3 - (i >> 3)]
- & (1 << (i & 7)))
- break;
- for (j = 0; j < 32; j++)
- if (scan -> netmask.iabuf [3 - (j >> 3)] &
- (1 << (j & 7)))
- break;
- strcpy (n1buf, piaddr (subnet -> net));
- if (warnp)
- warn ("%ssubnet %s/%d conflicts with subnet %s/%d",
- "Warning: ", 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, *prev = (struct subnet *)0;
-
- /* Check for duplicates... */
- for (scan = subnets; scan; scan = scan -> next_subnet) {
- /* 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) {
- prev -> next_subnet = subnet;
- } else
- subnets = subnet;
- subnet -> next_subnet = scan;
- return;
- }
- prev = scan;
- }
-
- /* XXX use the BSD radix tree code instead of a linked list. */
- subnet -> next_subnet = subnets;
- subnets = subnet;
-}
-
-/* Enter a new shared network into the shared network list. */
-
-void enter_shared_network (share)
- struct shared_network *share;
-{
- /* XXX Sort the nets into a balanced tree to make searching quicker. */
- share -> next = shared_networks;
- shared_networks = share;
-}
-
-/* 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 = find_lease_by_ip_addr (lease -> ip_addr);
-
- /* If we don't have a place for this lease yet, save it for
- later. */
- if (!comp) {
- comp = new_lease ("enter_lease");
- if (!comp) {
- error ("No memory for lease %s\n",
- piaddr (lease -> ip_addr));
- }
- *comp = *lease;
- comp -> next = dangling_leases;
- comp -> prev = (struct lease *)0;
- dangling_leases = comp;
+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 ISC_R_INVALIDARG;
+ if (!d)
+ return ISC_R_INVALIDARG;
+
+ /* Also not okay to delete a group that's not the one in
+ the hash table. */
+ if (d != group)
+ return ISC_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 {
- /* Record the hostname information in the lease. */
- comp -> hostname = lease -> hostname;
- comp -> client_hostname = lease -> client_hostname;
- supersede_lease (comp, lease, 0);
- }
-}
-
-/* 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)
- struct lease *comp, *lease;
- int commit;
-{
- int enter_uid = 0;
- int enter_hwaddr = 0;
- struct lease *lp;
-
- /* 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 -> flags & ABANDONED_LEASE) &&
- comp -> ends > cur_time &&
- (((comp -> uid && lease -> uid) &&
- (comp -> uid_len != lease -> uid_len ||
- memcmp (comp -> uid, lease -> uid, comp -> uid_len))) ||
- (!comp -> uid &&
- ((comp -> hardware_addr.htype !=
- lease -> hardware_addr.htype) ||
- (comp -> hardware_addr.hlen !=
- lease -> hardware_addr.hlen) ||
- memcmp (comp -> hardware_addr.haddr,
- lease -> hardware_addr.haddr,
- comp -> hardware_addr.hlen))))) {
- warn ("Lease conflict at %s",
- piaddr (comp -> ip_addr));
- return 0;
- } else {
- /* If there's a Unique ID, dissociate it from the hash
- table and free it if necessary. */
- if (comp -> uid) {
- uid_hash_delete (comp);
- enter_uid = 1;
- if (comp -> uid != &comp -> uid_buf [0]) {
- free (comp -> uid);
- comp -> uid_max = 0;
- comp -> uid_len = 0;
- }
- comp -> uid = (unsigned char *)0;
- } else
- enter_uid = 1;
-
- if (comp -> hardware_addr.htype &&
- ((comp -> hardware_addr.hlen !=
- lease -> hardware_addr.hlen) ||
- (comp -> hardware_addr.htype !=
- lease -> hardware_addr.htype) ||
- memcmp (comp -> hardware_addr.haddr,
- lease -> hardware_addr.haddr,
- comp -> hardware_addr.hlen))) {
- hw_hash_delete (comp);
- enter_hwaddr = 1;
- } else if (!comp -> hardware_addr.htype)
- enter_hwaddr = 1;
-
- /* 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;
- } 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;
- } else {
- error ("corrupt lease uid."); /* XXX */
- }
- } else {
- comp -> uid = (unsigned char *)0;
- comp -> uid_max = 0;
- }
- comp -> uid_len = lease -> uid_len;
- comp -> host = lease -> host;
- comp -> hardware_addr = lease -> hardware_addr;
- comp -> flags = ((lease -> flags & ~PERSISTENT_FLAGS) |
- (comp -> flags & ~EPHEMERAL_FLAGS));
-
- /* Record the lease in the uid hash if necessary. */
- if (enter_uid && lease -> uid) {
- uid_hash_add (comp);
- }
-
- /* Record it in the hardware address hash if necessary. */
- if (enter_hwaddr && lease -> hardware_addr.htype) {
- hw_hash_add (comp);
- }
-
- /* Remove the lease from its current place in the
- timeout sequence. */
- if (comp -> prev) {
- comp -> prev -> next = comp -> next;
- } else {
- comp -> shared_network -> leases = comp -> next;
- }
- if (comp -> next) {
- comp -> next -> prev = comp -> prev;
- }
- if (comp -> shared_network -> last_lease == comp) {
- comp -> shared_network -> last_lease = comp -> prev;
- }
-
- /* Find the last insertion point... */
- if (comp == comp -> shared_network -> insertion_point ||
- !comp -> shared_network -> insertion_point) {
- lp = comp -> shared_network -> leases;
- } else {
- lp = comp -> shared_network -> insertion_point;
- }
-
- if (!lp) {
- /* Nothing on the list yet? Just make comp the
- head of the list. */
- comp -> shared_network -> leases = comp;
- comp -> shared_network -> last_lease = comp;
- } else if (lp -> ends > lease -> ends) {
- /* Skip down the list until we run out of list
- or find a place for comp. */
- while (lp -> next && lp -> ends > lease -> ends) {
- lp = lp -> next;
- }
- if (lp -> ends > lease -> ends) {
- /* If we ran out of list, put comp
- at the end. */
- lp -> next = comp;
- comp -> prev = lp;
- comp -> next = (struct lease *)0;
- comp -> shared_network -> last_lease = comp;
- } else {
- /* If we didn't, put it between lp and
- the previous item on the list. */
- if ((comp -> prev = lp -> prev))
- comp -> prev -> next = comp;
- comp -> next = lp;
- lp -> prev = comp;
- }
- } else {
- /* Skip up the list until we run out of list
- or find a place for comp. */
- while (lp -> prev && lp -> ends < lease -> ends) {
- lp = lp -> prev;
- }
- if (lp -> ends < lease -> ends) {
- /* If we ran out of list, put comp
- at the beginning. */
- lp -> prev = comp;
- comp -> next = lp;
- comp -> prev = (struct lease *)0;
- comp -> shared_network -> leases = comp;
- } else {
- /* If we didn't, put it between lp and
- the next item on the list. */
- if ((comp -> next = lp -> next))
- comp -> next -> prev = comp;
- comp -> prev = lp;
- lp -> next = comp;
+ 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, *u;
+ isc_result_t status;
+
+ /* 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);
}
}
- comp -> shared_network -> insertion_point = comp;
- comp -> ends = lease -> ends;
- }
-
- /* Return zero if we didn't commit the lease to permanent storage;
- nonzero if we did. */
- return commit && write_lease (comp) && commit_leases ();
-}
-
-/* Release the specified lease and re-hash it as appropriate. */
-
-void release_lease (lease)
- struct lease *lease;
-{
- struct lease lt;
-
- lt = *lease;
- if (lt.ends > cur_time) {
- lt.ends = cur_time;
- supersede_lease (lease, &lt, 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;
- char *message;
-{
- struct lease lt;
-
- lease -> flags |= ABANDONED_LEASE;
- lt = *lease;
- lt.ends = cur_time;
- warn ("Abandoning IP address %s: %s",
- piaddr (lease -> ip_addr), message);
- lt.hardware_addr.htype = 0;
- lt.hardware_addr.hlen = 0;
- lt.uid = (unsigned char *)0;
- lt.uid_len = 0;
- supersede_lease (lease, &lt, 1);
-}
-
-/* Locate the lease associated with a given IP address... */
-
-struct lease *find_lease_by_ip_addr (addr)
- struct iaddr addr;
-{
- struct lease *lease = (struct lease *)hash_lookup (lease_ip_addr_hash,
- addr.iabuf,
- addr.len);
- return lease;
-}
-
-struct lease *find_lease_by_uid (uid, len)
- unsigned char *uid;
- int len;
-{
- struct lease *lease = (struct lease *)hash_lookup (lease_uid_hash,
- uid, len);
- return lease;
-}
-
-struct lease *find_lease_by_hw_addr (hwaddr, hwlen)
- unsigned char *hwaddr;
- int hwlen;
-{
- struct lease *lease = (struct lease *)hash_lookup (lease_hw_addr_hash,
- hwaddr, hwlen);
- return lease;
-}
-
-/* Add the specified lease to the uid hash. */
-
-void uid_hash_add (lease)
- struct lease *lease;
-{
- struct lease *head =
- find_lease_by_uid (lease -> uid, lease -> uid_len);
- struct lease *scan;
-
-#ifdef DEBUG
- if (lease -> n_uid)
- abort ();
-#endif
-
- /* If it's not in the hash, just add it. */
- if (!head)
- add_hash (lease_uid_hash, lease -> uid,
- lease -> uid_len, (unsigned char *)lease);
- else {
- /* Otherwise, attach it to the end of the list. */
- for (scan = head; scan -> n_uid; scan = scan -> n_uid)
-#ifdef DEBUG
- if (scan == lease)
- abort ()
-#endif
- ;
- scan -> n_uid = lease;
- }
-}
-
-/* Delete the specified lease from the uid hash. */
-
-void uid_hash_delete (lease)
- struct lease *lease;
-{
- struct lease *head =
- find_lease_by_uid (lease -> uid, lease -> uid_len);
- struct lease *scan;
-
- /* If it's not in the hash, we have no work to do. */
- if (!head) {
- lease -> n_uid = (struct lease *)0;
- 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) {
- delete_hash_entry (lease_uid_hash,
- lease -> uid, lease -> uid_len);
- if (lease -> n_uid)
- add_hash (lease_uid_hash,
- lease -> n_uid -> uid,
- lease -> n_uid -> uid_len,
- (unsigned char *)(lease -> n_uid));
} 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) {
- scan -> n_uid = scan -> n_uid -> n_uid;
- break;
- }
- }
+ group_new_hash (&group_name_hash, 0, MDL);
+ t = (struct group_object *)0;
}
- lease -> n_uid = (struct lease *)0;
-}
-
-/* Add the specified lease to the hardware address hash. */
-void hw_hash_add (lease)
- struct lease *lease;
-{
- struct lease *head =
- find_lease_by_hw_addr (lease -> hardware_addr.haddr,
- lease -> hardware_addr.hlen);
- struct lease *scan;
-
- /* If it's not in the hash, just add it. */
- if (!head)
- add_hash (lease_hw_addr_hash,
- lease -> hardware_addr.haddr,
- lease -> hardware_addr.hlen,
- (unsigned char *)lease);
- else {
- /* Otherwise, attach it to the end of the list. */
- for (scan = head; scan -> n_hw; scan = scan -> n_hw)
- ;
- scan -> n_hw = lease;
- }
-}
-
-/* Delete the specified lease from the hardware address hash. */
-
-void hw_hash_delete (lease)
- struct lease *lease;
-{
- struct lease *head =
- find_lease_by_hw_addr (lease -> hardware_addr.haddr,
- lease -> hardware_addr.hlen);
- struct lease *scan;
-
- /* If it's not in the hash, we have no work to do. */
- if (!head) {
- lease -> n_hw = (struct lease *)0;
- return;
+ /* 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);
}
- /* 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) {
- delete_hash_entry (lease_hw_addr_hash,
- lease -> hardware_addr.haddr,
- lease -> hardware_addr.hlen);
- if (lease -> n_hw)
- add_hash (lease_hw_addr_hash,
- lease -> n_hw -> hardware_addr.haddr,
- lease -> n_hw -> hardware_addr.hlen,
- (unsigned char *)(lease -> n_hw));
- } 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_hw; scan = scan -> n_hw) {
- if (scan -> n_hw == lease) {
- scan -> n_hw = scan -> n_hw -> n_hw;
- break;
- }
- }
+ /* Store the group declaration in the lease file. */
+ if (writep && group_write_hook) {
+ if (!(*group_write_hook) (group))
+ return ISC_R_IOERROR;
}
- lease -> n_hw = (struct lease *)0;
+ return ISC_R_SUCCESS;
}
-
-struct class *add_class (type, name)
- int type;
- char *name;
+int clone_group (struct group **gp, struct group *group,
+ const char *file, int line)
{
- struct class *class = new_class ("add_class");
- char *tname = (char *)malloc (strlen (name) + 1);
-
- if (!vendor_class_hash)
- vendor_class_hash = new_hash ();
- if (!user_class_hash)
- user_class_hash = new_hash ();
+ isc_result_t status;
+ struct group *g = (struct group *)0;
- if (!tname || !class || !vendor_class_hash || !user_class_hash)
- return (struct class *)0;
-
- memset (class, 0, sizeof *class);
- strcpy (tname, name);
- class -> name = tname;
-
- if (type)
- add_hash (user_class_hash,
- (unsigned char *)tname, strlen (tname),
- (unsigned char *)class);
- else
- add_hash (vendor_class_hash,
- (unsigned char *)tname, strlen (tname),
- (unsigned char *)class);
- return class;
-}
-
-struct class *find_class (type, name, len)
- int type;
- unsigned char *name;
- int len;
-{
- struct class *class =
- (struct class *)hash_lookup (type
- ? user_class_hash
- : vendor_class_hash, name, len);
- return class;
-}
-
-struct group *clone_group (group, caller)
- struct group *group;
- char *caller;
-{
- struct group *g = new_group (caller);
- if (!g)
- error ("%s: can't allocate new group", caller);
- *g = *group;
- return g;
-}
-
-/* Write all interesting leases to permanent storage. */
-
-void write_leases ()
-{
- struct lease *l;
- struct shared_network *s;
-
- for (s = shared_networks; s; s = s -> next) {
- for (l = s -> leases; l; l = l -> next) {
- if (l -> hardware_addr.hlen ||
- l -> uid_len ||
- (l -> flags & ABANDONED_LEASE))
- if (!write_lease (l))
- error ("Can't rewrite lease database");
- }
- }
- if (!commit_leases ())
- error ("Can't commit leases to new database: %m");
-}
-
-void dump_subnets ()
-{
- struct lease *l;
- struct shared_network *s;
- struct subnet *n;
-
- note ("Subnets:");
- for (n = subnets; n; n = n -> next_subnet) {
- debug (" Subnet %s", piaddr (n -> net));
- debug (" netmask %s",
- piaddr (n -> netmask));
- }
- note ("Shared networks:");
- for (s = shared_networks; s; s = s -> next) {
- note (" %s", s -> name);
- for (l = s -> leases; l; l = l -> next) {
- print_lease (l);
- }
- if (s -> last_lease) {
- debug (" Last Lease:");
- print_lease (s -> last_lease);
- }
- }
+ /* 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/contrib/isc-dhcp/common/nit.c b/contrib/isc-dhcp/common/nit.c
index 77f43b38d98d..59197f2a46f3 100644
--- a/contrib/isc-dhcp/common/nit.c
+++ b/contrib/isc-dhcp/common/nit.c
@@ -4,7 +4,7 @@
with one crucial tidbit of help from Stu Grossmen. */
/*
- * Copyright (c) 1996, 1998, 1999 The Internet Software Consortium.
+ * Copyright (c) 1996-2000 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,14 +35,16 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''. */
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: nit.c,v 1.15.2.4 1999/03/29 22:07:14 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: nit.c,v 1.34 2001/02/17 21:17:25 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -95,7 +97,7 @@ int if_register_nit (info)
/* Open a NIT device */
sock = open ("/dev/nit", O_RDWR);
if (sock < 0)
- error ("Can't open NIT device for %s: %m", info -> name);
+ 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;
@@ -103,7 +105,7 @@ int if_register_nit (info)
sio.ic_dp = (char *)(info -> ifp);
sio.ic_timout = INFTIM;
if (ioctl (sock, I_STR, &sio) < 0)
- error ("Can't attach interface %s to nit device: %m",
+ log_fatal ("Can't attach interface %s to nit device: %m",
info -> name);
/* Get the low-level address... */
@@ -112,16 +114,17 @@ int if_register_nit (info)
sio.ic_dp = (char *)&ifr;
sio.ic_timout = INFTIM;
if (ioctl (sock, I_STR, &sio) < 0)
- error ("Can't get physical layer address for %s: %m",
+ log_fatal ("Can't get physical layer address for %s: %m",
info -> name);
/* XXX code below assumes ethernet interface! */
- info -> hw_address.hlen = 6;
- info -> hw_address.htype = ARPHRD_ETHER;
- memcpy (info -> hw_address.haddr, ifr.ifr_ifru.ifru_addr.sa_data, 6);
+ 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)
- error ("Can't push packet filter onto NIT for %s: %m",
+ log_fatal ("Can't push packet filter onto NIT for %s: %m",
info -> name);
return sock;
@@ -150,15 +153,34 @@ void if_register_send (info)
sio.ic_dp = (char *)&pf;
sio.ic_timout = INFTIM;
if (ioctl (info -> wfdesc, I_STR, &sio) < 0)
- error ("Can't set NIT filter: %m");
+ log_fatal ("Can't set NIT filter: %m");
#else
info -> wfdesc = info -> rfdesc;
#endif
if (!quiet_interface_discovery)
- note ("Sending on NIT/%s%s%s",
- print_hw_addr (info -> hw_address.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
+ 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 : ""));
@@ -187,28 +209,28 @@ void if_register_receive (info)
packet. */
x = 0;
if (ioctl (info -> rfdesc, NIOCSSNAP, &x) < 0)
- error ("Can't set NIT snap length on %s: %m", info -> name);
+ 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)
- note ("I_SRDOPT failed on %s: %m", info -> name);
+ log_info ("I_SRDOPT failed on %s: %m", info -> name);
#if 0
/* Push on the chunker... */
if (ioctl (info -> rfdesc, I_PUSH, "nbuf") < 0)
- error ("Can't push chunker onto NIT STREAM: %m");
+ 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)
- error ("Can't set chunk timeout: %m");
+ log_fatal ("Can't set chunk timeout: %m");
#endif
/* Ask for no header... */
x = 0;
if (ioctl (info -> rfdesc, NIOCSFLAGS, &x) < 0)
- error ("Can't set NIT flags on %s: %m", info -> name);
+ 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
@@ -236,13 +258,31 @@ void if_register_receive (info)
sio.ic_dp = (char *)&pf;
sio.ic_timout = INFTIM;
if (ioctl (info -> rfdesc, I_STR, &sio) < 0)
- error ("Can't set NIT filter on %s: %m", info -> name);
+ log_fatal ("Can't set NIT filter on %s: %m", info -> name);
if (!quiet_interface_discovery)
- note ("Listening on NIT/%s%s%s",
- print_hw_addr (info -> hw_address.htype,
- info -> hw_address.hlen,
- info -> hw_address.haddr),
+ 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 : ""));
@@ -259,11 +299,12 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
struct sockaddr_in *to;
struct hardware *hto;
{
- int bufp;
- unsigned char buf [1536 + sizeof (struct sockaddr)];
+ unsigned hbufp, ibufp;
+ double hh [16];
+ double ih [1536 / sizeof (double)];
+ unsigned char *buf = (unsigned char *)ih;
struct sockaddr *junk;
struct strbuf ctl, data;
- int hw_end;
struct sockaddr_in foo;
int result;
@@ -272,38 +313,35 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
len, from, to, hto);
/* Start with the sockaddr struct... */
- junk = (struct sockaddr *)&buf [0];
- bufp = ((unsigned char *)&junk -> sa_data [0]) - &buf [0];
+ 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, buf, &bufp, hto);
- hw_end = bufp;
- assemble_udp_ip_header (interface, buf, &bufp, from.s_addr,
- to -> sin_addr.s_addr, to -> sin_port,
- raw, len);
+ 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 + bufp, raw, len);
+ memcpy (buf + ibufp, raw, len);
/* Set up the sockaddr structure... */
#if USE_SIN_LEN
- junk -> sa_len = hw_end - 2; /* XXX */
+ junk -> sa_len = hbufp - 2; /* XXX */
#endif
junk -> sa_family = AF_UNSPEC;
-#if 0 /* Already done. */
- memcpy (junk.sa_data, buf, hw_len);
-#endif
-
/* Set up the msg_buf structure... */
- ctl.buf = (char *)&buf [0];
- ctl.maxlen = ctl.len = hw_end;
- data.buf = (char *)&buf [hw_end];
- data.maxlen = data.len = bufp + len - hw_end;
+ 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)
- warn ("send_packet: %m");
+ log_error ("send_packet: %m");
return result;
}
#endif /* USE_NIT_SEND */
@@ -355,7 +393,8 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
return length;
}
-int can_unicast_without_arp ()
+int can_unicast_without_arp (ip)
+ struct interface_info *ip;
{
return 1;
}
@@ -366,14 +405,25 @@ int can_receive_unicast_unconfigured (ip)
return 1;
}
+int supports_multiple_interfaces (ip)
+ struct interface_info *ip;
+{
+ return 1;
+}
+
void maybe_setup_fallback ()
{
- struct interface_info *fbi;
- fbi = setup_fallback ();
- if (fbi) {
+ isc_result_t status;
+ struct interface_info *fbi = (struct interface_info *)0;
+ if (setup_fallback (&fbi, MDL)) {
if_register_fallback (fbi);
- add_protocol ("fallback", fallback_interface -> wfdesc,
- fallback_discard, fallback_interface);
+ 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/contrib/isc-dhcp/common/options.c b/contrib/isc-dhcp/common/options.c
index b840716f345a..b7a5a0480807 100644
--- a/contrib/isc-dhcp/common/options.c
+++ b/contrib/isc-dhcp/common/options.c
@@ -3,7 +3,7 @@
DHCP options parsing and reassembly. */
/*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,176 +34,470 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: options.c,v 1.26.2.11 2000/06/24 07:24:02 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.85.2.6 2001/10/18 20:11:38 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#define DHCP_OPTION_DATA
#include "dhcpd.h"
-#include <ctype.h>
+#include <omapip/omapip_p.h>
+
+struct option *vendor_cfg_option;
+
+static void do_option_set PROTO ((pair *,
+ struct option_cache *,
+ enum statement_op));
/* Parse all available options out of the specified packet. */
-void parse_options (packet)
+int parse_options (packet)
struct packet *packet;
{
- /* Initially, zero all option pointers. */
- memset (packet -> options, 0, sizeof (packet -> options));
+ int i;
+ 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;
+ return 1;
}
/* Go through the options field, up to the end of the packet
or the End field. */
- parse_option_buffer (packet, &packet -> raw -> options [4],
- packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
+ if (!parse_option_buffer (packet -> options,
+ &packet -> raw -> options [4],
+ (packet -> packet_length -
+ DHCP_FIXED_NON_UDP - 4),
+ &dhcp_universe))
+ return 0;
+
/* If we parsed a DHCP Option Overload option, parse more
options out of the buffer(s) containing them. */
- if (packet -> options_valid
- && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
- parse_option_buffer (packet,
- (unsigned char *)
- packet -> raw -> file,
- sizeof packet -> raw -> file);
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
- parse_option_buffer (packet,
- (unsigned char *)
- packet -> raw -> sname,
- sizeof packet -> raw -> sname);
+ if (packet -> options_valid &&
+ (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 and setting packet -> options_valid if no
errors are encountered. */
-void parse_option_buffer (packet, buffer, length)
- struct packet *packet;
- unsigned char *buffer;
- int length;
+int parse_option_buffer (options, buffer, length, universe)
+ struct option_state *options;
+ const unsigned char *buffer;
+ unsigned length;
+ struct universe *universe;
{
- unsigned char *s, *t;
- unsigned char *end = buffer + length;
- int len;
+ unsigned char *t;
+ const unsigned char *end = buffer + length;
+ unsigned len, offset;
int code;
+ struct option_cache *op = (struct option_cache *)0;
+ struct buffer *bp = (struct buffer *)0;
- for (s = buffer; *s != DHO_END && s < end; ) {
- code = s [0];
+ if (!buffer_allocate (&bp, length, MDL)) {
+ log_error ("no memory for option buffer.");
+ return 0;
+ }
+ memcpy (bp -> data, buffer, length);
+
+ for (offset = 0; buffer [offset] != DHO_END && offset < length; ) {
+ code = buffer [offset];
/* Pad options don't have a length - just skip them. */
if (code == DHO_PAD) {
- ++s;
+ ++offset;
continue;
}
+
+ /* Don't look for length if the buffer isn't that big. */
+ if (offset + 2 > length) {
+ len = 65536;
+ goto bogus;
+ }
+
/* All other fields (except end, see above) have a
one-byte length. */
- len = s [1];
+ len = buffer [offset + 1];
/* If the length is outrageous, the options are bad. */
- if (s + len + 2 > end) {
- warn ("Option %s length %d overflows input buffer.",
- dhcp_options [code].name,
- len);
- packet -> options_valid = 0;
- return;
+ if (offset + len + 2 > length) {
+ bogus:
+ log_error ("parse_option_buffer: option %s (%d) %s.",
+ dhcp_options [code].name, len,
+ "larger than buffer");
+ buffer_dereference (&bp, MDL);
+ return 0;
}
- /* If we haven't seen this option before, just make
- space for it and copy it there. */
- if (!packet -> options [code].data) {
- if (!(t = ((unsigned char *)
- dmalloc (len + 1, "parse_option_buffer"))))
- error ("Can't allocate storage for option %s.",
- dhcp_options [code].name);
- /* Copy and NUL-terminate the option (in case it's an
- ASCII string. */
- memcpy (t, &s [2], len);
- t [len] = 0;
- packet -> options [code].len = len;
- packet -> options [code].data = t;
- } else {
- /* If it's a repeat, concatenate it to whatever
- we last saw. This is really only required
- for clients, but what the heck... */
- t = ((unsigned char *)
- dmalloc (len + packet -> options [code].len + 1,
- "parse_option_buffer"));
- if (!t)
- error ("Can't expand storage for option %s.",
- dhcp_options [code].name);
- memcpy (t, packet -> options [code].data,
- packet -> options [code].len);
- memcpy (t + packet -> options [code].len,
- &s [2], len);
- packet -> options [code].len += len;
- t [packet -> options [code].len] = 0;
- dfree (packet -> options [code].data,
- "parse_option_buffer");
- packet -> options [code].data = t;
- }
- s += len + 2;
+
+ /* 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 (!((universe -> options [code] -> format [0] == 'e' ||
+ universe -> options [code] -> format [0] == 'E') &&
+ (parse_encapsulated_suboptions
+ (options, universe -> options [code],
+ buffer + offset + 2, len,
+ universe, (const char *)0)))) {
+ save_option_buffer (universe, options, bp,
+ &bp -> data [offset + 2], len,
+ universe -> options [code], 1);
+ }
+ offset += len + 2;
}
- packet -> options_valid = 1;
+ 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)
+{
+ char *name;
+ 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 [0], 1,
+ &fqdn_options [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)
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data[5], i,
+ &fqdn_options [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_options [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_options [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;
+ }
+
+ if (!terminated) {
+ first_len = total_len;
+ }
+
+ if (first_len > 0 &&
+ !save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data[6], first_len,
+ &fqdn_options [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_options [FQDN_DOMAINNAME], 1))
+ goto bad;
+ }
+ if (total_len > 0)
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [6], total_len,
+ &fqdn_options [FQDN_FQDN], 1))
+ goto bad;
+ }
+
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [1], 1,
+ &fqdn_options [FQDN_NO_CLIENT_UPDATE], 0))
+ goto bad;
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [2], 1,
+ &fqdn_options [FQDN_SERVER_UPDATE], 0))
+ goto bad;
+
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [3], 1,
+ &fqdn_options [FQDN_RCODE1], 0))
+ goto bad;
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [4], 1,
+ &fqdn_options [FQDN_RCODE2], 0))
+ goto bad;
+
+ buffer_dereference (&bp, MDL);
+ return 1;
}
/* cons options into a big buffer, and then split them out into the
three seperate buffers if needed. This allows us to cons up a set
of vendor options using the same routine. */
-int cons_options (inpacket, outpacket, mms,
- options, overload, terminate, bootpp, prl, prl_len)
+int cons_options (inpacket, outpacket, lease, client_state,
+ mms, in_options, cfg_options,
+ scope, overload, terminate, bootpp, prl, vuname)
struct packet *inpacket;
struct dhcp_packet *outpacket;
+ struct lease *lease;
+ struct client_state *client_state;
int mms;
- struct tree_cache **options;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
int overload; /* Overload flags that may be set. */
int terminate;
int bootpp;
- u_int8_t *prl;
- int prl_len;
+ struct data_string *prl;
+ const char *vuname;
{
- unsigned char priority_list [300];
+#define PRIORITY_COUNT 300
+ unsigned priority_list [PRIORITY_COUNT];
int priority_len;
unsigned char buffer [4096]; /* Really big buffer... */
- int main_buffer_size;
- int mainbufix, bufix;
- int option_size;
- int length;
+ unsigned main_buffer_size;
+ unsigned mainbufix, bufix, agentix;
+ unsigned option_size;
+ unsigned length;
+ int i;
+ struct option_cache *op;
+ struct data_string ds;
+ pair pp, *hash;
+ int need_endopt = 0;
+ int have_sso = 0;
- /* If the client has provided a maximum DHCP message size,
- use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
- use up to the minimum IP MTU size (576 bytes). */
- /* XXX if a BOOTP client specifies a max message size, we will
- honor it. */
- if (!mms &&
- inpacket &&
- inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data &&
- (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >=
- sizeof (u_int16_t)))
- mms = getUShort (inpacket -> options
- [DHO_DHCP_MAX_MESSAGE_SIZE].data);
+ 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, take the
+ one in the packet. */
+
+ if (!mms && 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))
+ mms = getUShort (ds.data);
+ data_string_forget (&ds, MDL);
+ }
/* If the client has provided a maximum DHCP message size,
use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
use up to the minimum IP MTU size (576 bytes). */
/* XXX if a BOOTP client specifies a max message size, we will
honor it. */
- if (mms)
+
+ if (mms) {
main_buffer_size = mms - DHCP_FIXED_LEN;
- else if (bootpp)
- main_buffer_size = 64;
- else
+
+ /* Enforce a minimum packet size... */
+ if (main_buffer_size < (576 - DHCP_FIXED_LEN))
+ main_buffer_size = 576 - DHCP_FIXED_LEN;
+ } else if (bootpp) {
+ if (inpacket) {
+ main_buffer_size =
+ inpacket -> packet_length - DHCP_FIXED_LEN;
+ if (main_buffer_size < 64)
+ main_buffer_size = 64;
+ } else
+ main_buffer_size = 64;
+ } else
main_buffer_size = 576 - DHCP_FIXED_LEN;
+ /* Set a hard limit at the size of the output buffer. */
if (main_buffer_size > sizeof buffer)
main_buffer_size = sizeof buffer;
@@ -213,35 +507,96 @@ int cons_options (inpacket, outpacket, mms,
priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
priority_list [priority_len++] = DHO_DHCP_MESSAGE;
+ priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
- /* If the client has provided a list of options that it wishes
- returned, use it to prioritize. Otherwise, prioritize
- based on the default priority list. */
-
- if (inpacket &&
- inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
- int prlen = (inpacket ->
- options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
- if (prlen + priority_len > sizeof priority_list)
- prlen = (sizeof priority_list) - priority_len;
-
- memcpy (&priority_list [priority_len],
- (inpacket -> options
- [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen);
- priority_len += prlen;
- prl = priority_list;
- } else if (prl) {
- if (prl_len + priority_len > sizeof priority_list)
- prl_len = (sizeof priority_list) - priority_len;
-
- memcpy (&priority_list [priority_len], prl, prl_len);
- priority_len += prl_len;
- prl = priority_list;
+ if (prl && 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));
+
+ 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];
+ }
} else {
- memcpy (&priority_list [priority_len],
- dhcp_option_default_priority_list,
- sizeof_dhcp_option_default_priority_list);
- priority_len += sizeof_dhcp_option_default_priority_list;
+ /* First, hardcode some more options that ought to be
+ sent first... */
+ 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;
+
+ /* 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];
+ 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]);
+ 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 go through all the universes for which options
+ were set and see if there are encapsulations for
+ them; if there are, put the encapsulation options
+ on the priority list as well. */
+ for (i = 0; i < cfg_options -> universe_count; i++) {
+ if (cfg_options -> universes [i] &&
+ 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;
}
/* Copy the options into the big buffer... */
@@ -249,11 +604,13 @@ int cons_options (inpacket, outpacket, mms,
(main_buffer_size - 7 +
((overload & 1) ? DHCP_FILE_LEN : 0) +
((overload & 2) ? DHCP_SNAME_LEN : 0)),
- options, priority_list, priority_len,
+ inpacket, lease, client_state,
+ in_options, cfg_options, scope,
+ priority_list, priority_len,
main_buffer_size,
(main_buffer_size +
((overload & 1) ? DHCP_FILE_LEN : 0)),
- terminate);
+ terminate, vuname);
/* Put the cookie up front... */
memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
@@ -267,13 +624,12 @@ int cons_options (inpacket, outpacket, mms,
memcpy (&outpacket -> options [mainbufix],
buffer, option_size);
mainbufix += option_size;
+ agentix = mainbufix;
if (mainbufix < main_buffer_size)
- outpacket -> options [mainbufix++]
- = DHO_END;
+ need_endopt = 1;
length = DHCP_FIXED_NON_UDP + mainbufix;
} else {
- outpacket -> options [mainbufix++] =
- DHO_DHCP_OPTION_OVERLOAD;
+ outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
outpacket -> options [mainbufix++] = 1;
if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
outpacket -> options [mainbufix++] = 3;
@@ -282,8 +638,10 @@ int cons_options (inpacket, outpacket, mms,
memcpy (&outpacket -> options [mainbufix],
buffer, main_buffer_size - mainbufix);
+ length = DHCP_FIXED_NON_UDP + main_buffer_size;
+ agentix = main_buffer_size;
+
bufix = main_buffer_size - mainbufix;
- length = DHCP_FIXED_NON_UDP + mainbufix;
if (overload & 1) {
if (option_size - bufix <= DHCP_FILE_LEN) {
memcpy (outpacket -> file,
@@ -314,220 +672,400 @@ int cons_options (inpacket, outpacket, mms,
= DHO_PAD;
}
}
+
+ /* Now hack in the agent options if there are any. */
+ priority_list [0] = DHO_DHCP_AGENT_OPTIONS;
+ priority_len = 1;
+ agentix +=
+ store_options (&outpacket -> options [agentix],
+ 1500 - DHCP_FIXED_LEN - agentix,
+ inpacket, lease, client_state,
+ in_options, cfg_options, scope,
+ priority_list, priority_len,
+ 1500 - DHCP_FIXED_LEN - agentix,
+ 1500 - DHCP_FIXED_LEN - agentix, 0, (char *)0);
+
+ /* Tack a DHO_END option onto the packet if we need to. */
+ if (agentix < 1500 - DHCP_FIXED_LEN && need_endopt)
+ outpacket -> options [agentix++] = DHO_END;
+
+ /* Figure out the length. */
+ length = DHCP_FIXED_NON_UDP + agentix;
return length;
}
/* Store all the requested options into the requested buffer. */
-int store_options (buffer, buflen, options, priority_list, priority_len,
- first_cutoff, second_cutoff, terminate)
+int store_options (buffer, buflen, packet, lease, client_state,
+ in_options, cfg_options, scope, priority_list, priority_len,
+ first_cutoff, second_cutoff, terminate, vuname)
unsigned char *buffer;
- int buflen;
- struct tree_cache **options;
- unsigned char *priority_list;
+ 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;
- int first_cutoff, second_cutoff;
+ unsigned first_cutoff, second_cutoff;
int terminate;
+ const char *vuname;
{
int bufix = 0;
- int option_stored [256];
int i;
int ix;
int tto;
+ struct data_string od;
+ struct option_cache *oc;
+ unsigned code;
+ int optstart;
+
+ memset (&od, 0, sizeof od);
- /* Zero out the stored-lengths array. */
- memset (option_stored, 0, sizeof option_stored);
+ /* Eliminate duplicate options in the parameter request list.
+ There's got to be some clever knuthian way to do this:
+ Eliminate all but the first occurance of a value in an array
+ of values without otherwise disturbing the order of the array. */
+ for (i = 0; i < priority_len - 1; i++) {
+ 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--;
+ }
+ }
+ }
/* Copy out the options in the order that they appear in the
priority list... */
for (i = 0; i < priority_len; i++) {
- /* Code for next option to try to store. */
- int code = priority_list [i];
- int optstart;
+ /* Number of bytes left to store (some may already
+ have been stored by a previous pass). */
+ unsigned length;
+ int optstart;
+ struct universe *u;
+ int have_encapsulation = 0;
+ struct data_string encapsulation;
- /* Number of bytes left to store (some may already
- have been stored by a previous pass). */
- int length;
+ memset (&encapsulation, 0, sizeof encapsulation);
- /* If no data is available for this option, skip it. */
- if (!options [code]) {
- continue;
- }
+ /* 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;
- /* The client could ask for things that are mandatory,
- in which case we should avoid storing them twice... */
- if (option_stored [code])
- continue;
- option_stored [code] = 1;
+ oc = lookup_option (u, cfg_options, code);
- /* Find the value of the option... */
- if (!tree_evaluate (options [code])) {
- continue;
- }
+ /* It's an encapsulation, try to find the universe
+ to be encapsulated first, except that if it's a straight
+ encapsulation and the user has provided a value for the
+ encapsulation option, use the user-provided value. */
+ if ((u -> options [code] -> format [0] == 'E' && !oc) ||
+ u -> options [code] -> format [0] == 'e') {
+ int uix;
+ static char *s, *t;
+ struct option_cache *tmp;
+ struct data_string name;
- /* We should now have a constant length for the option. */
- length = options [code] -> len;
+ s = strchr (u -> options [code] -> format, 'E');
+ if (s)
+ t = strchr (++s, '.');
+ if (s && t) {
+ memset (&name, 0, sizeof name);
- /* Do we add a NUL? */
- if (terminate && dhcp_options [code].format [0] == 't') {
- length++;
- tto = 1;
- } else {
- tto = 0;
+ /* 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);
+ }
}
+ }
- /* 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. */
+ /* 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. */
- ix = 0;
+ /* If no data is available for this option, skip it. */
+ if (!oc && !have_encapsulation) {
+ continue;
+ }
+
+ /* Find the value of the option... */
+ if (oc) {
+ evaluate_option_cache (&od, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, MDL);
+ if (!od.len) {
+ data_string_forget (&encapsulation, MDL);
+ data_string_forget (&od, MDL);
+ have_encapsulation = 0;
+ continue;
+ }
+ }
- optstart = bufix;
- while (length) {
- unsigned char incr = length > 255 ? 255 : length;
+ /* We should now have a constant length for the option. */
+ length = od.len;
+ if (have_encapsulation) {
+ length += encapsulation.len;
+ 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;
+ }
+ }
- /* If this hunk of the buffer will cross a
- boundary, only go up to the boundary in this
- pass. */
- if (bufix < first_cutoff &&
- bufix + incr > first_cutoff)
- incr = first_cutoff - bufix;
- else if (bufix < second_cutoff &&
- bufix + incr > second_cutoff)
- incr = second_cutoff - bufix;
+ /* Do we add a NUL? */
+ if (terminate && dhcp_options [code].format [0] == 't') {
+ length++;
+ tto = 1;
+ } else {
+ tto = 0;
+ }
- /* If this option is going to overflow the buffer,
- skip it. */
- if (bufix + 2 + incr > buflen) {
- bufix = optstart;
- break;
- }
+ /* 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. */
- /* Everything looks good - copy it in! */
- buffer [bufix] = code;
- buffer [bufix + 1] = incr;
- if (tto && incr == length) {
- memcpy (buffer + bufix + 2,
- options [code] -> value + ix,
- incr - 1);
- buffer [bufix + 2 + incr - 1] = 0;
- } else {
- memcpy (buffer + bufix + 2,
- options [code] -> value + ix, incr);
- }
- length -= incr;
- ix += incr;
- bufix += 2 + incr;
- }
+ ix = 0;
+ optstart = bufix;
+ while (length) {
+ unsigned char incr = length > 255 ? 255 : length;
+ int consumed = 0;
+
+ /* If this hunk of the buffer will cross a
+ boundary, only go up to the boundary in this
+ pass. */
+ if (bufix < first_cutoff &&
+ bufix + incr > first_cutoff)
+ incr = first_cutoff - bufix;
+ else if (bufix < second_cutoff &&
+ bufix + incr > second_cutoff)
+ incr = second_cutoff - bufix;
+
+ /* If this option is going to overflow the buffer,
+ skip it. */
+ if (bufix + 2 + incr > buflen) {
+ bufix = optstart;
+ break;
+ }
+
+ /* Everything looks good - copy it in! */
+ buffer [bufix] = code;
+ buffer [bufix + 1] = incr;
+ if (tto && incr == length) {
+ memcpy (buffer + bufix + 2,
+ od.data + ix, (unsigned)(incr - 1));
+ buffer [bufix + 2 + incr - 1] = 0;
+ } else {
+ memcpy (buffer + bufix + 2,
+ od.data + ix, (unsigned)incr);
+ }
+ length -= incr;
+ ix += incr;
+ bufix += 2 + incr;
+ }
+ data_string_forget (&od, MDL);
}
+
return bufix;
}
/* Format the specified option so that a human can easily read it. */
-char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
- unsigned int code;
- unsigned char *data;
- int len;
+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 */
int hunksize = 0;
+ int opthunk = 0;
+ int hunkinc = 0;
int numhunk = -1;
int numelem = 0;
char fmtbuf [32];
- int i, j, k;
+ struct enumeration *enumbuf [32];
+ int i, j, k, l;
char *op = optbuf;
- unsigned char *dp = data;
+ const unsigned char *dp = data;
struct in_addr foo;
char comma;
-
- /* Code should be between 0 and 255. */
- if (code > 255)
- error ("pretty_print_option: bad code %d\n", code);
+ unsigned long tval;
if (emit_commas)
comma = ',';
else
comma = ' ';
+ memset (enumbuf, 0, sizeof enumbuf);
+
/* Figure out the size of the data. */
- for (i = 0; dhcp_options [code].format [i]; i++) {
+ for (l = i = 0; option -> format [i]; i++, l++) {
if (!numhunk) {
- warn ("%s: Excess information in format string: %s\n",
- dhcp_options [code].name,
- &(dhcp_options [code].format [i]));
+ log_error ("%s: Extra codes in format string: %s",
+ option -> name,
+ &(option -> format [i]));
break;
}
numelem++;
- fmtbuf [i] = dhcp_options [code].format [i];
- switch (dhcp_options [code].format [i]) {
+ fmtbuf [l] = option -> format [i];
+ switch (option -> format [i]) {
+ case 'a':
+ --numelem;
+ fmtbuf [l] = 0;
+ numhunk = 0;
+ break;
case 'A':
--numelem;
- fmtbuf [i] = 0;
+ fmtbuf [l] = 0;
numhunk = 0;
break;
+ case 'E':
+ /* Skip the universe name. */
+ while (option -> format [i] &&
+ option -> format [i] != '.')
+ i++;
case 'X':
for (k = 0; k < len; k++) {
if (!isascii (data [k]) ||
!isprint (data [k]))
break;
}
- if (k == len) {
- fmtbuf [i] = 't';
+ /* 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 [i] = 'x';
+ fmtbuf [l] = 'x';
hunksize++;
comma = ':';
numhunk = 0;
}
- fmtbuf [i + 1] = 0;
+ fmtbuf [l + 1] = 0;
break;
+ case 'd':
case 't':
- fmtbuf [i] = 't';
- fmtbuf [i + 1] = 0;
+ fmtbuf [l] = '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);
+ hunksize += 1;
+ hunkinc = 1;
+ 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':
hunksize++;
+ hunkinc = 1;
break;
case 'e':
break;
+ case 'o':
+ opthunk += hunkinc;
+ break;
default:
- warn ("%s: garbage in format string: %s\n",
- dhcp_options [code].name,
- &(dhcp_options [code].format [i]));
+ log_error ("%s: garbage in format string: %s",
+ option -> name,
+ &(option -> format [i]));
break;
}
}
/* Check for too few bytes... */
- if (hunksize > len) {
- warn ("%s: expecting at least %d bytes; got %d",
- dhcp_options [code].name,
+ 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)
- warn ("%s: %d extra bytes",
- dhcp_options [code].name,
+ log_error ("%s: %d extra bytes",
+ option -> name,
len - hunksize);
/* If this is an array, compute its size. */
@@ -535,8 +1073,8 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
numhunk = len / hunksize;
/* See if we got an exact number of hunks. */
if (numhunk > 0 && numhunk * hunksize < len)
- warn ("%s: %d extra bytes at end of array\n",
- dhcp_options [code].name,
+ 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. */
@@ -574,6 +1112,21 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
*op++ = '"';
*op = 0;
break;
+ /* pretty-printing an array of enums is
+ going to get ugly. */
+ case 'N':
+ if (!enumbuf [j])
+ goto enum_as_num;
+ for (i = 0; ;i++) {
+ if (!enumbuf [j] -> values [i].name)
+ goto enum_as_num;
+ if (enumbuf [j] -> values [i].value ==
+ *dp)
+ break;
+ }
+ strcpy (op, enumbuf [j] -> values [i].name);
+ op += strlen (op);
+ break;
case 'I':
foo.s_addr = htonl (getULong (dp));
strcpy (op, inet_ntoa (foo));
@@ -583,23 +1136,31 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
sprintf (op, "%ld", (long)getLong (dp));
dp += 4;
break;
+ case 'T':
+ tval = getULong (dp);
+ if (tval == -1)
+ sprintf (op, "%s", "infinite");
+ else
+ sprintf (op, "%ld", tval);
+ break;
case 'L':
sprintf (op, "%ld",
(unsigned long)getULong (dp));
dp += 4;
break;
case 's':
- sprintf (op, "%d", getShort (dp));
+ sprintf (op, "%d", (int)getShort (dp));
dp += 2;
break;
case 'S':
- sprintf (op, "%d", getUShort (dp));
+ sprintf (op, "%d", (unsigned)getUShort (dp));
dp += 2;
break;
case 'b':
- sprintf (op, "%d", *(char *)dp++);
+ sprintf (op, "%d", *(const char *)dp++);
break;
case 'B':
+ enum_as_num:
sprintf (op, "%d", *dp++);
break;
case 'x':
@@ -609,58 +1170,1051 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
strcpy (op, *dp++ ? "true" : "false");
break;
default:
- warn ("Unexpected format code %c", fmtbuf [j]);
+ 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 do_option_set.");
+ 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;
+ }
+ }
+ noc -> option = oc -> option;
+ 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;
+}
+
+int save_option_buffer (struct universe *universe,
+ struct option_state *options,
+ struct buffer *bp,
+ unsigned char *buffer, unsigned length,
+ struct option *option, int tp)
+{
+ struct buffer *lbp = (struct buffer *)0;
+ struct option_cache *op = (struct option_cache *)0;
+
+ if (!option_cache_allocate (&op, MDL)) {
+ log_error ("No memory for option %s.%s.",
+ universe -> name,
+ option -> name);
+ return 0;
+ }
+
+ /* 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, MDL)) {
+ log_error ("no memory for option buffer.");
+
+ option_cache_dereference (&op, MDL);
+ return 0;
+ }
+ memcpy (lbp -> data, buffer, length + tp);
+ 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 (tp) {
+ /* 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;
+
+ op -> option = option;
+
+ /* Now store the option. */
+ save_option (universe, options, op);
+
+ /* And let go of our reference. */
+ option_cache_dereference (&op, MDL);
+
+ return 1;
+}
+
+void save_option (struct universe *universe,
+ struct option_state *options, struct option_cache *oc)
+{
+ if (universe -> save_func)
+ (*universe -> save_func) (universe, options, oc);
+ else
+ log_error ("can't store options in %s space.",
+ universe -> name);
+}
+
+void save_hashed_option (universe, options, oc)
+ struct universe *universe;
+ struct option_state *options;
+ struct option_cache *oc;
+{
+ int hashix;
+ pair bptr;
+ pair *hash = options -> universes [universe -> index];
+
+ 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] = (VOIDPTR)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;
+ }
+
+ /* If we find one, dereference it and put the new one
+ in its place. */
+ if (bptr) {
+ option_cache_dereference
+ ((struct option_cache **)&bptr -> car, MDL);
+ option_cache_reference
+ ((struct option_cache **)&bptr -> car,
+ 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) -> 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;
+}
+
+int store_option (result, universe, packet, lease, client_state,
+ in_options, cfg_options, scope, oc)
+ 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 d1, d2;
+
+ memset (&d1, 0, sizeof d1);
+ memset (&d2, 0, sizeof d2);
+
+ if (evaluate_option_cache (&d2, packet, lease, client_state,
+ in_options, cfg_options, scope, oc, MDL)) {
+ if (!buffer_allocate (&d1.buffer,
+ (result -> len +
+ universe -> length_size +
+ universe -> tag_size + d2.len), MDL)) {
+ data_string_forget (result, MDL);
+ data_string_forget (&d2, MDL);
+ return 0;
+ }
+ d1.data = &d1.buffer -> data [0];
+ if (result -> len)
+ memcpy (d1.buffer -> data,
+ result -> data, result -> len);
+ d1.len = result -> len;
+ (*universe -> store_tag) (&d1.buffer -> data [d1.len],
+ oc -> option -> code);
+ d1.len += universe -> tag_size;
+ (*universe -> store_length) (&d1.buffer -> data [d1.len],
+ d2.len);
+ d1.len += universe -> length_size;
+ memcpy (&d1.buffer -> data [d1.len], d2.data, d2.len);
+ d1.len += d2.len;
+ data_string_forget (&d2, MDL);
+ data_string_forget (result, MDL);
+ data_string_copy (result, &d1, MDL);
+ data_string_forget (&d1, MDL);
+ return 1;
+ }
+ 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;
+
+ u = (struct universe *)0;
+ universe_hash_lookup (&u, universe_hash,
+ (const char *)name -> data, name -> len, MDL);
+ if (!u)
+ return 0;
+
+ if (u -> encapsulate)
+ return (*u -> encapsulate) (result, packet, lease,
+ client_state,
+ in_options, cfg_options, scope, u);
+ log_error ("encapsulation requested for %s with no support.",
+ name -> data);
+ return 0;
+}
+
+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;
+
+ 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;
+ }
+ }
+
+ 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;
+ int i;
+ 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 [fqdn_universe.index]);
+ if (!head)
+ return 0;
+
+ status = 0;
+ for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
+ struct option_cache *oc = (struct option_cache *)(ocp -> car);
+ 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) {
+ 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);
+ no_nwip -> option = nwip_universe.options [1];
+ }
+ 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;
+}
+
+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];
+ unsigned 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);
+ }
+ len = 4 + 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.");
+ return 0;
+ }
+ buffer_reference (&result -> buffer, bp, MDL);
+ result -> len = 3;
+ result -> data = &bp -> data [0];
+
+ memset (&bp -> data [0], 0, len);
+ 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]) {
+ unsigned char *out;
+ int i;
+ bp -> data [0] |= 4;
+ out = &bp -> data [3];
+ if (results [FQDN_FQDN].len) {
+ i = 0;
+ while (i < results [FQDN_FQDN].len) {
+ int j;
+ for (j = i; ('.' !=
+ results [FQDN_FQDN].data [j]) &&
+ j < results [FQDN_FQDN].len; j++)
+ ;
+ *out++ = j - i;
+ memcpy (out, &results [FQDN_FQDN].data [i],
+ (unsigned)(j - i));
+ out += j - i;
+ i = j;
+ if (results [FQDN_FQDN].data [j] == '.')
+ i++;
+ }
+ if ((results [FQDN_FQDN].data
+ [results [FQDN_FQDN].len - 1] == '.'))
+ *out++ = 0;
+ result -> len = out - result -> data;
+ 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;
+ }
+ }
+ for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
+ if (results [i].len)
+ data_string_forget (&results [i], MDL);
+ }
+ buffer_dereference (&bp, MDL);
+ return 1;
+}
+
+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);
+ int i;
+
+ 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 (universe, options, oc)
+ struct universe *universe;
+ struct option_state *options;
+ struct option_cache *oc;
+{
+ pair *tail;
+ pair np = (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) {
+ 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)) {
+ if (oc -> option ==
+ ((struct option_cache *)((*tail) -> car)) -> option) {
+ option_cache_dereference ((struct option_cache **)
+ (&(*tail) -> car), MDL);
+ option_cache_reference ((struct option_cache **)
+ (&(*tail) -> car), 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;
+ pair oc;
+ struct option_chain_head *head;
+
+ if (universe -> index >= cfg_options -> universe_count)
+ return 0;
+ head = ((struct option_chain_head *)
+ cfg_options -> universes [universe -> index]);
+ if (!head)
+ return 0;
+
+ status = 0;
+ 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;
+ }
+
+ 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;
- int len;
+ unsigned len;
unsigned int from_port;
struct iaddr from;
struct hardware *hfrom;
{
- struct packet tp;
int i;
+ 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) {
- note ("Discarding packet with invalid hlen.");
+ packet_dereference (&decoded_packet, MDL);
+ log_info ("Discarding packet with bogus hlen.");
return;
}
- memset (&tp, 0, sizeof tp);
- tp.raw = packet;
- tp.packet_length = len;
- tp.client_port = from_port;
- tp.client_addr = from;
- tp.interface = interface;
- tp.haddr = hfrom;
-
- parse_options (&tp);
- if (tp.options_valid &&
- tp.options [DHO_DHCP_MESSAGE_TYPE].data)
- tp.packet_type =
- tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
- if (tp.packet_type)
- dhcp (&tp);
- else
- bootp (&tp);
+ /* 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;
+ }
- /* Free the data associated with the options. */
- for (i = 0; i < 256; i++) {
- if (tp.options [i].len && tp.options [i].data)
- dfree (tp.options [i].data, "do_packet");
+ 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
}
diff --git a/contrib/isc-dhcp/common/packet.c b/contrib/isc-dhcp/common/packet.c
index 6a15fcf1e4df..8fe389412922 100644
--- a/contrib/isc-dhcp/common/packet.c
+++ b/contrib/isc-dhcp/common/packet.c
@@ -3,7 +3,7 @@
Packet assembly code, originally contributed by Archie Cobbs. */
/*
- * Copyright (c) 1995, 1996, 1999 The Internet Software Consortium.
+ * Copyright (c) 1996-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,16 +33,16 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * This 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.
*/
#ifndef lint
static char copyright[] =
-"$Id: packet.c,v 1.18.2.6 1999/06/10 00:58:16 mellon Exp $ Copyright (c) 1996 The Internet Software Consortium. All rights reserved.\n";
+"$Id: packet.c,v 1.40.2.1 2001/05/31 19:28:51 mellon Exp $ Copyright (c) 1996-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -50,25 +50,26 @@ static char copyright[] =
#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;
- int nbytes;
+ unsigned nbytes;
u_int32_t sum;
{
- int i;
+ unsigned i;
#ifdef DEBUG_CHECKSUM
- debug ("checksum (%x %d %x)", buf, nbytes, sum);
+ log_debug ("checksum (%x %d %x)", buf, nbytes, sum);
#endif
/* Checksum all the pairs of bytes first... */
- for (i = 0; i < (nbytes & ~1); i += 2) {
+ for (i = 0; i < (nbytes & ~1U); i += 2) {
#ifdef DEBUG_CHECKSUM_VERBOSE
- debug ("sum = %x", sum);
+ log_debug ("sum = %x", sum);
#endif
sum += (u_int16_t) ntohs(*((u_int16_t *)(buf + i)));
/* Add carry. */
@@ -80,7 +81,7 @@ u_int32_t checksum (buf, nbytes, sum)
byte order is big-endian, so the remaining byte is the high byte. */
if (i < nbytes) {
#ifdef DEBUG_CHECKSUM_VERBOSE
- debug ("sum = %x", sum);
+ log_debug ("sum = %x", sum);
#endif
sum += buf [i] << 8;
/* Add carry. */
@@ -91,41 +92,43 @@ u_int32_t checksum (buf, nbytes, sum)
return sum;
}
-/* Finish computing the sum, and then put it into network byte order. */
+/* Finish computing the checksum, and then put it into network byte order. */
u_int32_t wrapsum (sum)
u_int32_t sum;
{
#ifdef DEBUG_CHECKSUM
- debug ("wrapsum (%x)", sum);
+ log_debug ("wrapsum (%x)", sum);
#endif
sum = ~sum & 0xFFFF;
#ifdef DEBUG_CHECKSUM_VERBOSE
- debug ("sum = %x", sum);
+ log_debug ("sum = %x", sum);
#endif
#ifdef DEBUG_CHECKSUM
- debug ("wrapsum returns %x", htons (sum));
+ log_debug ("wrapsum returns %x", htons (sum));
#endif
return htons(sum);
}
#ifdef PACKET_ASSEMBLY
-/* Assemble an hardware header... */
-/* XXX currently only supports ethernet; doesn't check for other types. */
-
void assemble_hw_header (interface, buf, bufix, to)
struct interface_info *interface;
unsigned char *buf;
- int *bufix;
+ unsigned *bufix;
struct hardware *to;
{
#if defined (HAVE_TR_SUPPORT)
- if (interface -> hw_address.htype == HTYPE_IEEE802)
+ 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);
}
@@ -136,19 +139,19 @@ void assemble_udp_ip_header (interface, buf, bufix,
from, to, port, data, len)
struct interface_info *interface;
unsigned char *buf;
- int *bufix;
+ unsigned *bufix;
u_int32_t from;
u_int32_t to;
- unsigned int port;
+ u_int32_t port;
unsigned char *data;
- int len;
+ unsigned len;
{
struct ip ip;
struct udphdr udp;
/* Fill out the IP header */
- ip.ip_v = 4;
- ip.ip_hl = 5;
+ 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;
@@ -193,18 +196,24 @@ void assemble_udp_ip_header (interface, buf, bufix,
#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;
- int bufix;
+ unsigned bufix;
struct hardware *from;
{
#if defined (HAVE_TR_SUPPORT)
- if (interface -> hw_address.htype == HTYPE_IEEE802)
+ 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);
}
@@ -213,10 +222,10 @@ ssize_t decode_hw_header (interface, buf, bufix, from)
ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
struct interface_info *interface;
unsigned char *buf;
- int bufix;
+ unsigned bufix;
struct sockaddr_in *from;
unsigned char *data;
- int buflen;
+ unsigned buflen;
{
struct ip *ip;
struct udphdr *udp;
@@ -228,7 +237,9 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
static int udp_packets_bad_checksum;
static int udp_packets_length_checked;
static int udp_packets_length_overflow;
- int len;
+ unsigned len;
+ unsigned ulen;
+ int ignore = 0;
ip = (struct ip *)(buf + bufix);
udp = (struct udphdr *)(buf + bufix + ip_len);
@@ -243,23 +254,34 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
return -1;
#endif /* USERLAND_FILTER */
+ ulen = ntohs (udp -> uh_ulen);
+ if (ulen < sizeof *udp ||
+ ((unsigned char *)udp) + ulen > buf + bufix + buflen) {
+ log_info ("bogus UDP packet length: %d", ulen);
+ 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) {
- note ("%d bad IP checksums seen in %d packets",
- ip_packets_bad_checksum, ip_packets_seen);
+ 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;
}
/* Check the IP packet length. */
- if (ntohs (ip -> ip_len) != buflen)
- debug ("ip length %d disagrees with bytes received %d.",
- ntohs (ip -> ip_len), buflen);
+ if (ntohs (ip -> ip_len) != buflen) {
+ if ((ntohs (ip -> ip_len + 2) & ~1) == buflen)
+ ignore = 1;
+ else
+ log_debug ("ip length %d disagrees with bytes received %d.",
+ ntohs (ip -> ip_len), buflen);
+ }
/* Copy out the IP source address... */
memcpy (&from -> sin_addr, &ip -> ip_src, 4);
@@ -270,23 +292,29 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
if (!data) {
data = buf + bufix + ip_len + sizeof *udp;
- len = ntohs (udp -> uh_ulen) - sizeof *udp;
+ len = ulen - sizeof *udp;
++udp_packets_length_checked;
if (len + data > buf + bufix + buflen) {
++udp_packets_length_overflow;
if (udp_packets_length_checked > 4 &&
(udp_packets_length_checked /
udp_packets_length_overflow) < 2) {
- note ("%d udp packets in %d too long - dropped",
- udp_packets_length_overflow,
- udp_packets_length_checked);
+ log_info ("%d udp packets in %d too long - dropped",
+ udp_packets_length_overflow,
+ udp_packets_length_checked);
udp_packets_length_overflow =
udp_packets_length_checked = 0;
}
return -1;
}
- if (len + data != buf + bufix + buflen)
- debug ("accepting packet with data after udp payload.");
+ if (len + data < buf + bufix + buflen &&
+ len + data != buf + bufix + buflen && !ignore)
+ log_debug ("accepting packet with data after udp payload.");
+ if (len + data > buf + bufix + buflen) {
+ log_debug ("dropping packet with bogus uh_ulen %ld",
+ (long)(len + sizeof *udp));
+ return -1;
+ }
}
usum = udp -> uh_sum;
@@ -298,16 +326,15 @@ ssize_t decode_udp_ip_header (interface, buf, bufix, from, data, buflen)
&ip -> ip_src,
2 * sizeof ip -> ip_src,
IPPROTO_UDP +
- (u_int32_t)
- ntohs (udp -> uh_ulen)))));
+ (u_int32_t)ulen))));
udp_packets_seen++;
if (usum && usum != sum) {
udp_packets_bad_checksum++;
if (udp_packets_seen > 4 &&
(udp_packets_seen / udp_packets_bad_checksum) < 2) {
- note ("%d bad udp checksums in %d packets",
- udp_packets_bad_checksum, udp_packets_seen);
+ 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;
diff --git a/contrib/isc-dhcp/common/parse.c b/contrib/isc-dhcp/common/parse.c
index fe02689032e0..a2d13f868f7e 100644
--- a/contrib/isc-dhcp/common/parse.c
+++ b/contrib/isc-dhcp/common/parse.c
@@ -3,7 +3,7 @@
Common parser code for dhcpd and dhclient. */
/*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,19 +34,58 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://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 lint
static char copyright[] =
-"$Id: parse.c,v 1.2.2.4 1999/03/29 22:18:53 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: parse.c,v 1.104.2.8 2002/01/10 19:37:51 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-#include "dhctoken.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,
+ const char *value)
+{
+ struct enumeration *e;
+ int i;
+
+ e = find_enumeration (name, length);
+ if (e) {
+ 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
@@ -63,17 +102,26 @@ static char copyright[] =
...et cetera. */
void skip_to_semi (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- int token;
- char *val;
- int brace_count = 0;
+ 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, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
if (token == RBRACE) {
+ token = next_token (&val, (unsigned *)0, cfile);
if (brace_count) {
- token = next_token (&val, cfile);
if (!--brace_count)
return;
} else
@@ -81,28 +129,28 @@ void skip_to_semi (cfile)
} else if (token == LBRACE) {
brace_count++;
} else if (token == SEMI && !brace_count) {
- token = next_token (&val, cfile);
+ 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, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
return;
}
- token = next_token (&val, cfile);
- } while (token != EOF);
+ token = next_token (&val, (unsigned *)0, cfile);
+ } while (token != END_OF_FILE);
}
int parse_semi (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- int token;
- char *val;
+ enum dhcp_token token;
+ const char *val;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SEMI) {
- parse_warn ("semicolon expected.");
+ parse_warn (cfile, "semicolon expected.");
skip_to_semi (cfile);
return 0;
}
@@ -111,76 +159,100 @@ int parse_semi (cfile)
/* string-parameter :== STRING SEMI */
-char *parse_string (cfile)
- FILE *cfile;
+int parse_string (cfile, sptr, lptr)
+ struct parse *cfile;
+ char **sptr;
+ unsigned *lptr;
{
- char *val;
- int token;
+ const char *val;
+ enum dhcp_token token;
char *s;
+ unsigned len;
- token = next_token (&val, cfile);
+ token = next_token (&val, &len, cfile);
if (token != STRING) {
- parse_warn ("filename must be a string");
+ parse_warn (cfile, "expecting a string");
skip_to_semi (cfile);
- return (char *)0;
+ return 0;
}
- s = (char *)malloc (strlen (val) + 1);
+ s = (char *)dmalloc (len + 1, MDL);
if (!s)
- error ("no memory for string %s.", val);
- strcpy (s, val);
+ log_fatal ("no memory for string %s.", val);
+ memcpy (s, val, len + 1);
- if (!parse_semi (cfile))
- return (char *)0;
- return s;
+ 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 | hostname DOT identifier */
+/*
+ * hostname :== IDENTIFIER
+ * | IDENTIFIER DOT
+ * | hostname DOT IDENTIFIER
+ */
char *parse_host_name (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
- char *val;
- int token;
- int len = 0;
+ 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 = next_token (&val, cfile);
- if (!is_identifier (token) && token != NUMBER) {
- parse_warn ("expecting an identifier in hostname");
- skip_to_semi (cfile);
- return (char *)0;
- }
+ 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 *)malloc (strlen (val) + 1)))
- error ("can't allocate temp space for hostname.");
+ 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, cfile);
- if (token == DOT)
- token = next_token (&val, cfile);
+ 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 *)malloc (len)))
- error ("can't allocate space for hostname.");
- t = s + len;
+ 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;
- int l = strlen ((char *)(c -> car));
+ unsigned l = strlen ((char *)(c -> car));
t -= l;
memcpy (t, (char *)(c -> car), l);
/* Free up temp space. */
- free (c -> car);
- free (c);
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
c = cdr;
if (t != s)
*--t = '.';
@@ -188,10 +260,66 @@ char *parse_host_name (cfile)
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. */
+
+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;
+
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (is_identifier (token)) {
+ name = parse_host_name (cfile);
+ if (!name)
+ return 0;
+ if (!make_host_lookup (expr, name))
+ return 0;
+ if (!uniform) {
+ if (!make_limit (&x, *expr, 4))
+ return 0;
+ expression_dereference (expr, MDL);
+ *expr = x;
+ }
+ } else if (token == NUMBER) {
+ if (!parse_numeric_aggregate (cfile, addr, &len, DOT, 10, 8))
+ return 0;
+ return make_const_data (expr, addr, len, 0, 1, MDL);
+ } 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)
- FILE *cfile;
+ struct parse *cfile;
struct iaddr *addr;
{
+ const char *val;
+ enum dhcp_token token;
+
addr -> len = 4;
if (parse_numeric_aggregate (cfile, addr -> iabuf,
&addr -> len, DOT, 10, 8))
@@ -199,33 +327,41 @@ int parse_ip_addr (cfile, addr)
return 0;
}
-/* hardware-parameter :== HARDWARE ETHERNET csns SEMI
- csns :== NUMBER | csns COLON NUMBER */
+/*
+ * hardware-parameter :== HARDWARE hardware-type colon-seperated-hex-list SEMI
+ * hardware-type :== ETHERNET | TOKEN_RING
+ */
void parse_hardware_param (cfile, hardware)
- FILE *cfile;
+ struct parse *cfile;
struct hardware *hardware;
{
- char *val;
- int token;
- int hlen;
+ const char *val;
+ enum dhcp_token token;
+ unsigned hlen;
unsigned char *t;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
switch (token) {
case ETHERNET:
- hardware -> htype = HTYPE_ETHER;
+ hardware -> hbuf [0] = HTYPE_ETHER;
break;
case TOKEN_RING:
- hardware -> htype = HTYPE_IEEE802;
+ hardware -> hbuf [0] = HTYPE_IEEE802;
break;
case FDDI:
- hardware -> htype = HTYPE_FDDI;
+ hardware -> hbuf [0] = HTYPE_FDDI;
break;
default:
- parse_warn ("expecting a network hardware type");
- skip_to_semi (cfile);
- return;
+ 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,
@@ -236,26 +372,33 @@ void parse_hardware_param (cfile, hardware)
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)
+ if (!t) {
+ hardware -> hlen = 1;
return;
- if (hlen > sizeof hardware -> haddr) {
- free (t);
- parse_warn ("hardware address too long");
+ }
+ if (hlen + 1 > sizeof hardware -> hbuf) {
+ dfree (t, MDL);
+ parse_warn (cfile, "hardware address too long");
} else {
- hardware -> hlen = hlen;
- memcpy ((unsigned char *)&hardware -> haddr [0],
- t, hardware -> hlen);
- if (hlen < sizeof hardware -> haddr)
- memset (&hardware -> haddr [hlen], 0,
- (sizeof hardware -> haddr) - hlen);
- free (t);
+ 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);
}
- token = next_token (&val, cfile);
+ out:
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SEMI) {
- parse_warn ("expecting semicolon.");
+ parse_warn (cfile, "expecting semicolon.");
skip_to_semi (cfile);
}
}
@@ -263,19 +406,19 @@ void parse_hardware_param (cfile, hardware)
/* lease-time :== NUMBER SEMI */
void parse_lease_time (cfile, timep)
- FILE *cfile;
+ struct parse *cfile;
TIME *timep;
{
- char *val;
- int token;
+ const char *val;
+ enum dhcp_token token;
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("Expecting numeric lease time");
+ parse_warn (cfile, "Expecting numeric lease time");
skip_to_semi (cfile);
return;
}
- convert_num ((unsigned char *)timep, val, 10, 32);
+ convert_num (cfile, (unsigned char *)timep, val, 10, 32);
/* Unswap the number - convert_num returns stuff in NBO. */
*timep = ntohl (*timep); /* XXX */
@@ -291,97 +434,100 @@ void parse_lease_time (cfile, timep)
unsigned char *parse_numeric_aggregate (cfile, buf,
max, seperator, base, size)
- FILE *cfile;
+ struct parse *cfile;
unsigned char *buf;
- int *max;
+ unsigned *max;
int seperator;
int base;
- int size;
+ unsigned size;
{
- char *val;
- int token;
- unsigned char *bufp = buf, *s;
- char *t;
- int count = 0;
+ 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 *)malloc (*max * size / 8);
+ bufp = (unsigned char *)dmalloc (*max * size / 8, MDL);
if (!bufp)
- error ("can't allocate space for numeric aggregate");
+ log_fatal ("no space for numeric aggregate");
+ s = 0;
} else
s = bufp;
do {
if (count) {
- token = peek_token (&val, cfile);
+ token = peek_token (&val, (unsigned *)0, cfile);
if (token != seperator) {
if (!*max)
break;
if (token != RBRACE && token != LBRACE)
- token = next_token (&val, cfile);
- parse_warn ("too few numbers.");
+ 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, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
}
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
- if (token == EOF) {
- parse_warn ("unexpected end of file");
+ 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 ("expecting numeric value.");
+ 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 (s, val, base, size);
+ convert_num (cfile, s, val, base, size);
s += size / 8;
} else {
- t = (char *)malloc (strlen (val) + 1);
+ t = (unsigned char *)dmalloc (strlen (val) + 1, MDL);
if (!t)
- error ("no temp space for number.");
- strcpy (t, val);
- c = cons (t, c);
+ 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 *)malloc (count * size / 8);
+ bufp = (unsigned char *)dmalloc (count * size / 8, MDL);
if (!bufp)
- error ("can't allocate space for numeric aggregate.");
+ log_fatal ("no space for numeric aggregate.");
s = bufp + count - size / 8;
*max = count;
}
while (c) {
pair cdr = c -> cdr;
- convert_num (s, (char *)(c -> car), base, size);
+ convert_num (cfile, s, (char *)(c -> car), base, size);
s -= size / 8;
/* Free up temp space. */
- free (c -> car);
- free (c);
+ dfree (c -> car, MDL);
+ dfree (c, MDL);
c = cdr;
}
return bufp;
}
-void convert_num (buf, str, base, size)
+void convert_num (cfile, buf, str, base, size)
+ struct parse *cfile;
unsigned char *buf;
- char *str;
+ const char *str;
int base;
- int size;
+ unsigned size;
{
- char *ptr = str;
+ const char *ptr = str;
int negative = 0;
u_int32_t val = 0;
int tval;
@@ -419,12 +565,13 @@ void convert_num (buf, str, base, size)
else if (tval >= '0')
tval -= '0';
else {
- warn ("Bogus number: %s.", str);
+ parse_warn (cfile, "Bogus number: %s.", str);
break;
}
if (tval >= base) {
- warn ("Bogus number: %s: digit %d not in base %d\n",
- str, tval, base);
+ parse_warn (cfile,
+ "Bogus number %s: digit %d not in base %d",
+ str, tval, base);
break;
}
val = val * base + tval;
@@ -437,16 +584,22 @@ void convert_num (buf, str, base, size)
if (val > max) {
switch (base) {
case 8:
- warn ("value %s%o exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lo exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
case 16:
- warn ("value %s%x exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lx exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
default:
- warn ("value %s%u exceeds max (%d) for precision.",
- negative ? "-" : "", val, max);
+ parse_warn (cfile,
+ "%s%lu exceeds max (%d) for precision.",
+ negative ? "-" : "",
+ (unsigned long)val, max);
break;
}
}
@@ -457,13 +610,14 @@ void convert_num (buf, str, base, size)
*buf = -(unsigned long)val;
break;
case 16:
- putShort (buf, -(unsigned long)val);
+ putShort (buf, -(long)val);
break;
case 32:
- putLong (buf, -(unsigned long)val);
+ putLong (buf, -(long)val);
break;
default:
- warn ("Unexpected integer size: %d\n", size);
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", size);
break;
}
} else {
@@ -478,160 +632,182 @@ void convert_num (buf, str, base, size)
putULong (buf, val);
break;
default:
- warn ("Unexpected integer size: %d\n", size);
+ parse_warn (cfile,
+ "Unexpected integer size: %d\n", size);
break;
}
}
}
-/* date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
- NUMBER COLON NUMBER COLON NUMBER SEMI
-
- Dates are always in GMT; first number is day of week; next is
- year/month/day; next is hours:minutes:seconds on a 24-hour
- clock. */
+/*
+ * date :== NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER SEMI |
+ * NUMBER NUMBER SLASH NUMBER SLASH NUMBER
+ * NUMBER COLON NUMBER COLON NUMBER NUMBER SEMI |
+ * NEVER
+ *
+ * Dates are stored in GMT 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.
+ */
TIME parse_date (cfile)
- FILE *cfile;
+ struct parse *cfile;
{
struct tm tm;
int guess;
- char *val;
- int token;
+ 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 };
- /* Day of week... */
- token = next_token (&val, cfile);
+ /* Day of week, or "never"... */
+ token = next_token (&val, (unsigned *)0, cfile);
+ if (token == NEVER) {
+ if (!parse_semi (cfile))
+ return 0;
+ return MAX_TIME;
+ }
+
if (token != NUMBER) {
- parse_warn ("numeric day of week expected.");
+ parse_warn (cfile, "numeric day of week expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_wday = atoi (val);
+ wday = atoi (val);
/* Year... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric year expected.");
+ parse_warn (cfile, "numeric year expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_year = atoi (val);
- if (tm.tm_year > 1900)
- tm.tm_year -= 1900;
+
+ /* 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 seperating year from month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SLASH) {
- parse_warn ("expected slash seperating year from month.");
+ parse_warn (cfile,
+ "expected slash seperating year from month.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric month expected.");
+ parse_warn (cfile, "numeric month expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_mon = atoi (val) - 1;
+ mon = atoi (val) - 1;
/* Slash seperating month from day... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != SLASH) {
- parse_warn ("expected slash seperating month from day.");
+ parse_warn (cfile,
+ "expected slash seperating month from day.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Month... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric day of month expected.");
+ parse_warn (cfile, "numeric day of month expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_mday = atoi (val);
+ mday = atoi (val);
/* Hour... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric hour expected.");
+ parse_warn (cfile, "numeric hour expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_hour = atoi (val);
+ hour = atoi (val);
/* Colon seperating hour from minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != COLON) {
- parse_warn ("expected colon seperating hour from minute.");
+ parse_warn (cfile,
+ "expected colon seperating hour from minute.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric minute expected.");
+ parse_warn (cfile, "numeric minute expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_min = atoi (val);
+ min = atoi (val);
/* Colon seperating minute from second... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != COLON) {
- parse_warn ("expected colon seperating hour from minute.");
+ parse_warn (cfile,
+ "expected colon seperating hour from minute.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
/* Minute... */
- token = next_token (&val, cfile);
+ token = next_token (&val, (unsigned *)0, cfile);
if (token != NUMBER) {
- parse_warn ("numeric minute expected.");
+ parse_warn (cfile, "numeric minute expected.");
if (token != SEMI)
skip_to_semi (cfile);
return (TIME)0;
}
- tm.tm_sec = atoi (val);
- tm.tm_isdst = 0;
+ sec = atoi (val);
- /* XXX */ /* We assume that mktime does not use tm_yday. */
- tm.tm_yday = 0;
+ token = peek_token (&val, (unsigned *)0, cfile);
+ if (token == NUMBER) {
+ token = next_token (&val, (unsigned *)0, cfile);
+ tzoff = atoi (val);
+ } else
+ tzoff = 0;
/* Make sure the date ends in a semicolon... */
- token = next_token (&val, cfile);
- if (token != SEMI) {
- parse_warn ("semicolon expected.");
- skip_to_semi (cfile);
+ if (!parse_semi (cfile))
return 0;
- }
/* Guess the time value... */
- guess = ((((((365 * (tm.tm_year - 70) + /* Days in years since '70 */
- (tm.tm_year - 69) / 4 + /* Leap days since '70 */
- (tm.tm_mon /* Days in months this year */
- ? months [tm.tm_mon - 1]
+ 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) +
- (tm.tm_mon > 1 && /* Leap day this year */
- !((tm.tm_year - 72) & 3)) +
- tm.tm_mday - 1) * 24) + /* Day of month */
- tm.tm_hour) * 60) +
- tm.tm_min) * 60) + tm.tm_sec;
+ (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
@@ -644,3 +820,3999 @@ TIME parse_date (cfile)
return guess;
}
+
+/*
+ * option-name :== IDENTIFIER |
+ IDENTIFIER . IDENTIFIER
+ */
+
+struct option *parse_option_name (cfile, allocate, known)
+ struct parse *cfile;
+ int allocate;
+ int *known;
+{
+ const char *val;
+ enum dhcp_token token;
+ char *uname;
+ struct universe *universe;
+ struct option *option;
+
+ 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 (struct option *)0;
+ }
+ 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 (struct option *)0;
+ }
+
+ /* 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 (struct option *)0;
+ }
+ } 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 = (struct option *)0;
+ option_hash_lookup (&option, universe -> hash, val, 0, MDL);
+
+ /* If we didn't get an option structure, it's an undefined option. */
+ if (option) {
+ if (known)
+ *known = 1;
+ } else {
+ /* If we've been told to allocate, that means that this
+ (might) be an option code definition, so we'll create
+ an option structure just in case. */
+ if (allocate) {
+ option = new_option (MDL);
+ if (val == uname)
+ option -> name = val;
+ else {
+ char *s;
+ dfree (uname, MDL);
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for option %s.%s",
+ universe -> name, val);
+ strcpy (s, val);
+ option -> name = s;
+ }
+ option -> universe = universe;
+ option -> code = 0;
+ return option;
+ }
+ if (val == uname)
+ parse_warn (cfile, "no option named %s", val);
+ else
+ parse_warn (cfile, "no option named %s in space %s",
+ val, uname);
+ skip_to_semi (cfile);
+ return (struct option *)0;
+ }
+
+ /* Free the initial identifier token. */
+ dfree (uname, MDL);
+ return option;
+}
+
+/* IDENTIFIER SEMI */
+
+void parse_option_space_decl (cfile)
+ struct parse *cfile;
+{
+ int token;
+ const char *val;
+ struct universe **ua, *nu;
+ char *s;
+
+ 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... */
+ s = dmalloc (strlen (val) + 1, MDL);
+ if (!s)
+ log_fatal ("No memory for new option space name.");
+ strcpy (s, val);
+ nu -> name = s;
+ 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 = 1;
+ nu -> tag_size = 1;
+ nu -> store_tag = putUChar;
+ nu -> store_length = putUChar;
+ 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;
+ option_new_hash (&nu -> hash, 1, MDL);
+ if (!nu -> hash)
+ log_fatal ("Can't allocate %s option hash table.", nu -> name);
+ universe_hash_add (universe_hash, nu -> name, 0, nu, MDL);
+ parse_semi (cfile);
+}
+
+/* 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;
+ unsigned arrayp = 0;
+ int recordp = 0;
+ int no_more_in_record = 0;
+ char tokbuf [128];
+ unsigned tokix = 0;
+ char type;
+ int code;
+ int is_signed;
+ char *s;
+ int has_encapsulation = 0;
+
+ /* 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 DOMAIN_NAME:
+ 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;
+ }
+ if (strlen (val) + tokix + 2 > sizeof (tokbuf))
+ goto toobig;
+ tokbuf [tokix++] = 'E';
+ strcpy (&tokbuf [tokix], val);
+ tokix += strlen (val);
+ type = '.';
+ has_encapsulation = 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;
+ }
+ if (has_encapsulation && tokbuf [0] == 'E')
+ has_encapsulation = 0;
+ s = dmalloc (tokix +
+ (arrayp ? 1 : 0) +
+ (has_encapsulation ? 1 : 0) + 1, MDL);
+ if (!s)
+ log_fatal ("no memory for option format.");
+ if (has_encapsulation)
+ s [0] = 'e';
+ memcpy (s + has_encapsulation, tokbuf, tokix);
+ tokix += has_encapsulation;
+ if (arrayp)
+ s [tokix++] = (arrayp > recordp) ? 'a' : 'A';
+ s [tokix] = 0;
+ option -> format = s;
+ if (option -> universe -> options [option -> code]) {
+ /* XXX Free the option, but we can't do that now because they
+ XXX may start out static. */
+ }
+ option -> universe -> options [option -> code] = option;
+ option_hash_add (option -> universe -> hash,
+ (const char *)option -> name,
+ 0, option, 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-seperated-hex-list :== NUMBER |
+ * NUMBER COLON colon-seperated-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;
+{
+ enum dhcp_token token;
+ const char *val;
+ struct executable_statement base;
+ struct class *cta;
+ struct option *option;
+ 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 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;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ send_option_statement);
+
+ case SUPERSEDE:
+ case OPTION:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ supersede_option_statement);
+
+ 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;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ default_option_statement);
+
+ case PREPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ prepend_option_statement);
+
+ case APPEND:
+ token = next_token (&val, (unsigned *)0, cfile);
+ known = 0;
+ option = parse_option_name (cfile, 0, &known);
+ if (!option) {
+ *lose = 1;
+ return 0;
+ }
+ return parse_option_statement (result, cfile, 1, option,
+ append_option_statement);
+
+ 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 ->