diff options
author | murray <murray@FreeBSD.org> | 2002-02-19 11:04:34 +0000 |
---|---|---|
committer | murray <murray@FreeBSD.org> | 2002-02-19 11:04:34 +0000 |
commit | 57b30d23e7c11fa1a8c8c23f27de40971872952f (patch) | |
tree | 229464d9b3244ab78e2784c9a0a1f78de317089a /contrib/isc-dhcp/common | |
parent | 7acb11388cf5d680b16902b8ed6f46c46dc4d47b (diff) | |
download | FreeBSD-src-57b30d23e7c11fa1a8c8c23f27de40971872952f.zip FreeBSD-src-57b30d23e7c11fa1a8c8c23f27de40971872952f.tar.gz |
Import ISC DHCP 3.0.1 RC6 client.
Diffstat (limited to 'contrib/isc-dhcp/common')
31 files changed, 23007 insertions, 3904 deletions
diff --git a/contrib/isc-dhcp/common/Makefile.dist b/contrib/isc-dhcp/common/Makefile.dist index 1e3e062..3efa120 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 61d7572..5ed0204 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 e204dfe..49af907 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 0000000..a038903 --- /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 ddb78e6..ac73de5 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 0000000..bea933c --- /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 0000000..cbb7745 --- /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 5e7ca00..6f5d6f7 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 0000000..953bab0 --- /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 d44a987..7c8545b 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 0000000..4cbc332 --- /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 0000000..d529652 --- /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 41c0a30..309084a 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 0000000..ec4b22e --- /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 0000000..d73d450 --- /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 a9d7ec4..299e030 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 527afbb..158440e 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 14dd21a..da03c31 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 c293217..3c6d913 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, <, 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, <, 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 77f43b3..59197f2a4 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 *)𝔦 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 b840716..b7a5a04 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 6a15fcf..8fe3894 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 fe02689..a2d13f8 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 -> data.func -> statements, cfile, lose, + case_context))) { + if (*lose) + goto badx; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "expecting rigt brace."); + goto badx; + } + } else { + if (token != EQUAL) { + parse_warn (cfile, + "expecting '=' in %s statement.", + flag ? "define" : "set"); + goto badset; + } + + if (!parse_expression (&(*result) -> data.set.expr, + cfile, lose, context_any, + (struct expression **)0, + expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + break; + + case UNSET: + token = next_token (&val, (unsigned *)0, cfile); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "%s can't be a variable name", val); + badunset: + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for set statement."); + (*result) -> op = unset_statement; + (*result) -> data.unset = dmalloc (strlen (val) + 1, MDL); + if (!(*result)->data.unset) + log_fatal ("can't allocate variable name"); + strcpy ((*result) -> data.unset, val); + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + + case EVAL: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for eval statement."); + (*result) -> op = eval_statement; + + if (!parse_expression (&(*result) -> data.eval, + cfile, lose, context_data, /* XXX */ + (struct expression **)0, expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting data expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + } + break; + + case RETURN: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for return statement."); + (*result) -> op = return_statement; + + if (!parse_expression (&(*result) -> data.retval, + cfile, lose, context_data, + (struct expression **)0, expr_none)) { + if (!*lose) + parse_warn (cfile, + "expecting data expression."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + + case LOG: + token = next_token (&val, (unsigned *)0, cfile); + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for log statement."); + (*result) -> op = log_statement; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = peek_token (&val, (unsigned *)0, cfile); + i = 1; + if (token == FATAL) { + (*result) -> data.log.priority = log_priority_fatal; + } else if (token == ERROR) { + (*result) -> data.log.priority = log_priority_error; + } else if (token == TOKEN_DEBUG) { + (*result) -> data.log.priority = log_priority_debug; + } else if (token == INFO) { + (*result) -> data.log.priority = log_priority_info; + } else { + (*result) -> data.log.priority = log_priority_debug; + i = 0; + } + if (i) { + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + } + + if (!(parse_data_expression + (&(*result) -> data.log.expr, cfile, lose))) { + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != SEMI) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + break; + + /* Not really a statement, but we parse it here anyway + because it's appropriate for all DHCP agents with + parsers. */ + case ZONE: + token = next_token (&val, (unsigned *)0, cfile); + zone = (struct dns_zone *)0; + if (!dns_zone_allocate (&zone, MDL)) + log_fatal ("no memory for new zone."); + zone -> name = parse_host_name (cfile); + if (!zone -> name) { + badzone: + parse_warn (cfile, "expecting hostname."); + *lose = 1; + skip_to_semi (cfile); + dns_zone_dereference (&zone, MDL); + return 0; + } + i = strlen (zone -> name); + if (zone -> name [i - 1] != '.') { + s = dmalloc ((unsigned)i + 2, MDL); + if (!s) + goto badzone; + strcpy (s, zone -> name); + s [i] = '.'; + s [i + 1] = 0; + dfree (zone -> name, MDL); + zone -> name = s; + } + if (!parse_zone (zone, cfile)) + goto badzone; + status = enter_dns_zone (zone); + if (status != ISC_R_SUCCESS) { + if (parse_semi (cfile)) + parse_warn (cfile, "dns zone key %s: %s", + zone -> name, + isc_result_totext (status)); + dns_zone_dereference (&zone, MDL); + return 0; + } + dns_zone_dereference (&zone, MDL); + return 1; + + /* Also not really a statement, but same idea as above. */ + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_key (cfile)) { + *lose = 1; + return 0; + } + return 1; + + default: + if (config_universe && is_identifier (token)) { + option = (struct option *)0; + option_hash_lookup (&option, config_universe -> hash, + val, 0, MDL); + if (option) { + token = next_token (&val, + (unsigned *)0, cfile); + return parse_option_statement + (result, cfile, 1, option, + supersede_option_statement); + } + } + + if (token == NUMBER_OR_NAME || token == NAME) { + /* This is rather ugly. Since function calls are + data expressions, fake up an eval statement. */ + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for eval statement."); + (*result) -> op = eval_statement; + + if (!parse_expression (&(*result) -> data.eval, + cfile, lose, context_data, + (struct expression **)0, + expr_none)) { + if (!*lose) + parse_warn (cfile, "expecting " + "function call."); + else + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_semi (cfile)) { + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + break; + } + + *lose = 0; + return 0; + } + + return 1; +} + +/* zone-statements :== zone-statement | + zone-statement zone-statements + zone-statement :== + PRIMARY ip-addresses SEMI | + SECONDARY ip-addresses SEMI | + key-reference SEMI + ip-addresses :== ip-addr-or-hostname | + ip-addr-or-hostname COMMA ip-addresses + key-reference :== KEY STRING | + KEY identifier */ + +int parse_zone (struct dns_zone *zone, struct parse *cfile) +{ + int token; + const char *val; + char *key_name; + struct option_cache *oc; + int done = 0; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace"); + return 0; + } + + do { + token = peek_token (&val, (unsigned *)0, cfile); + switch (token) { + case PRIMARY: + if (zone -> primary) { + parse_warn (cfile, + "more than one primary."); + skip_to_semi (cfile); + return 0; + } + if (!option_cache_allocate (&zone -> primary, MDL)) + log_fatal ("can't allocate primary option cache."); + oc = zone -> primary; + goto consemup; + + case SECONDARY: + if (zone -> secondary) { + parse_warn (cfile, "more than one secondary."); + skip_to_semi (cfile); + return 0; + } + if (!option_cache_allocate (&zone -> secondary, MDL)) + log_fatal ("can't allocate secondary."); + oc = zone -> secondary; + consemup: + token = next_token (&val, (unsigned *)0, cfile); + do { + struct expression *expr = (struct expression *)0; + if (!parse_ip_addr_or_hostname (&expr, cfile, 0)) { + parse_warn (cfile, + "expecting IP addr or hostname."); + skip_to_semi (cfile); + return 0; + } + if (oc -> expression) { + struct expression *old = + (struct expression *)0; + expression_reference (&old, + oc -> expression, + MDL); + expression_dereference (&oc -> expression, + MDL); + if (!make_concat (&oc -> expression, + old, expr)) + log_fatal ("no memory for concat."); + expression_dereference (&expr, MDL); + expression_dereference (&old, MDL); + } else { + expression_reference (&oc -> expression, + expr, MDL); + expression_dereference (&expr, MDL); + } + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + if (token != SEMI) { + parse_warn (cfile, "expecting semicolon."); + skip_to_semi (cfile); + return 0; + } + break; + + case KEY: + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == STRING) { + token = next_token (&val, (unsigned *)0, cfile); + key_name = (char *)0; + } else { + key_name = parse_host_name (cfile); + if (!key_name) { + parse_warn (cfile, "expecting key name."); + skip_to_semi (cfile); + return 0; + } + val = key_name; + } + if (omapi_auth_key_lookup_name (&zone -> key, val) != + ISC_R_SUCCESS) + parse_warn (cfile, "unknown key %s", val); + if (key_name) + dfree (key_name, MDL); + if (!parse_semi (cfile)) + return 0; + break; + + default: + done = 1; + break; + } + } while (!done); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "expecting right brace."); + return 0; + } + return 1; +} + +/* key-statements :== key-statement | + key-statement key-statements + key-statement :== + ALGORITHM host-name SEMI | + secret-definition SEMI + secret-definition :== SECRET base64val | + SECRET STRING */ + +int parse_key (struct parse *cfile) +{ + int token; + const char *val; + int done = 0; + struct auth_key *key; + struct data_string ds; + isc_result_t status; + char *s; + + key = (struct auth_key *)0; + if (omapi_auth_key_new (&key, MDL) != ISC_R_SUCCESS) + log_fatal ("no memory for key"); + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == STRING) { + token = next_token (&val, (unsigned *)0, cfile); + key -> name = dmalloc (strlen (val) + 1, MDL); + if (!key -> name) + log_fatal ("no memory for key name."); + strcpy (key -> name, val); + + } else { + key -> name = parse_host_name (cfile); + if (!key -> name) { + parse_warn (cfile, "expecting key name."); + skip_to_semi (cfile); + goto bad; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "expecting left brace"); + goto bad; + } + + do { + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case ALGORITHM: + if (key -> algorithm) { + parse_warn (cfile, + "key %s: too many algorithms", + key -> name); + goto rbad; + } + key -> algorithm = parse_host_name (cfile); + if (!key -> algorithm) { + parse_warn (cfile, + "expecting key algorithm name."); + goto rbad; + } + if (!parse_semi (cfile)) + goto rbad; + /* If the algorithm name isn't an FQDN, tack on + the .SIG-ALG.REG.NET. domain. */ + s = strrchr (key -> algorithm, '.'); + if (!s) { + static char add [] = ".SIG-ALG.REG.INT."; + s = dmalloc (strlen (key -> algorithm) + + sizeof (add), MDL); + if (!s) { + log_error ("no memory for key %s.", + "algorithm"); + goto rbad; + } + strcpy (s, key -> algorithm); + strcat (s, add); + dfree (key -> algorithm, MDL); + key -> algorithm = s; + } else if (s [1]) { + /* If there is no trailing '.', hack one in. */ + s = dmalloc (strlen (key -> algorithm) + 2, MDL); + if (!s) { + log_error ("no memory for key %s.", + key -> algorithm); + goto rbad; + } + strcpy (s, key -> algorithm); + strcat (s, "."); + dfree (key -> algorithm, MDL); + key -> algorithm = s; + } + break; + + case SECRET: + if (key -> key) { + parse_warn (cfile, "key %s: too many secrets", + key -> name); + goto rbad; + } + + memset (&ds, 0, sizeof(ds)); + if (!parse_base64 (&ds, cfile)) + goto rbad; + status = omapi_data_string_new (&key -> key, ds.len, + MDL); + if (status != ISC_R_SUCCESS) + goto rbad; + memcpy (key -> key -> value, + ds.buffer -> data, ds.len); + data_string_forget (&ds, MDL); + + if (!parse_semi (cfile)) + goto rbad; + break; + + default: + done = 1; + break; + } + } while (!done); + if (token != RBRACE) { + parse_warn (cfile, "expecting right brace."); + goto rbad; + } + /* Allow the BIND 8 syntax, which has a semicolon after each + closing brace. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) + token = next_token (&val, (unsigned *)0, cfile); + + /* Remember the key. */ + status = omapi_auth_key_enter (key); + if (status != ISC_R_SUCCESS) { + parse_warn (cfile, "tsig key %s: %s", + key -> name, isc_result_totext (status)); + goto bad; + } + omapi_auth_key_dereference (&key, MDL); + return 1; + + rbad: + skip_to_rbrace (cfile, 1); + bad: + omapi_auth_key_dereference (&key, MDL); + return 0; +} + +/* + * on-statement :== event-types LBRACE executable-statements RBRACE + * event-types :== event-type OR event-types | + * event-type + * event-type :== EXPIRY | COMMIT | RELEASE + */ + +int parse_on_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = on_statement; + + do { + token = next_token (&val, (unsigned *)0, cfile); + switch (token) { + case EXPIRY: + (*result) -> data.on.evtypes |= ON_EXPIRY; + break; + + case COMMIT: + (*result) -> data.on.evtypes |= ON_COMMIT; + break; + + case RELEASE: + (*result) -> data.on.evtypes |= ON_RELEASE; + break; + + case TRANSMISSION: + (*result) -> data.on.evtypes |= ON_TRANSMISSION; + break; + + default: + parse_warn (cfile, "expecting a lease event type"); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + } while (token == OR); + + /* Semicolon means no statements. */ + if (token == SEMI) + return 1; + + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_executable_statements (&(*result) -> data.on.statements, + cfile, lose, context_any)) { + if (*lose) { + /* Try to even things up. */ + do { + token = next_token (&val, + (unsigned *)0, cfile); + } while (token != END_OF_FILE && token != RBRACE); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + return 1; +} + +/* + * switch-statement :== LPAREN expr RPAREN LBRACE executable-statements RBRACE + * + */ + +int parse_switch_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = switch_statement; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "expecting left brace."); + pfui: + *lose = 1; + skip_to_semi (cfile); + gnorf: + executable_statement_dereference (result, MDL); + return 0; + } + + if (!parse_expression (&(*result) -> data.s_switch.expr, + cfile, lose, context_data_or_numeric, + (struct expression **)0, expr_none)) { + if (!*lose) { + parse_warn (cfile, + "expecting data or numeric expression."); + goto pfui; + } + goto gnorf; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right paren expected."); + goto pfui; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + goto pfui; + } + if (!(parse_executable_statements + (&(*result) -> data.s_switch.statements, cfile, lose, + (is_data_expression ((*result) -> data.s_switch.expr) + ? context_data : context_numeric)))) { + if (*lose) { + skip_to_rbrace (cfile, 1); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + goto pfui; + } + return 1; +} + +/* + * case-statement :== CASE expr COLON + * + */ + +int parse_case_statement (result, cfile, lose, case_context) + struct executable_statement **result; + struct parse *cfile; + int *lose; + enum expression_context case_context; +{ + enum dhcp_token token; + const char *val; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for new statement."); + (*result) -> op = case_statement; + + if (!parse_expression (&(*result) -> data.c_case, + cfile, lose, case_context, + (struct expression **)0, expr_none)) + { + if (!*lose) { + parse_warn (cfile, "expecting %s expression.", + (case_context == context_data + ? "data" : "numeric")); + } + pfui: + *lose = 1; + skip_to_semi (cfile); + executable_statement_dereference (result, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COLON) { + parse_warn (cfile, "colon expected."); + goto pfui; + } + return 1; +} + +/* + * if-statement :== boolean-expression LBRACE executable-statements RBRACE + * else-statement + * + * else-statement :== <null> | + * ELSE LBRACE executable-statements RBRACE | + * ELSE IF if-statement | + * ELSIF if-statement + */ + +int parse_if_statement (result, cfile, lose) + struct executable_statement **result; + struct parse *cfile; + int *lose; +{ + enum dhcp_token token; + const char *val; + int parenp; + + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for if statement."); + + (*result) -> op = if_statement; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == LPAREN) { + parenp = 1; + next_token (&val, (unsigned *)0, cfile); + } else + parenp = 0; + + + if (!parse_boolean_expression (&(*result) -> data.ie.expr, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, "boolean expression expected."); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } +#if defined (DEBUG_EXPRESSION_PARSE) + print_expression ("if condition", (*result) -> data.ie.expr); +#endif + if (parenp) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "expecting right paren."); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != LBRACE) { + parse_warn (cfile, "left brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + if (!parse_executable_statements (&(*result) -> data.ie.tc, + cfile, lose, context_any)) { + if (*lose) { + /* Try to even things up. */ + do { + token = next_token (&val, + (unsigned *)0, cfile); + } while (token != END_OF_FILE && token != RBRACE); + executable_statement_dereference (result, MDL); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + token = peek_token (&val, (unsigned *)0, cfile); + if (token == ELSE) { + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token == IF) { + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_if_statement (&(*result) -> data.ie.fc, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, + "expecting if statement"); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } + } else if (token != LBRACE) { + parse_warn (cfile, "left brace or if expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } else { + token = next_token (&val, (unsigned *)0, cfile); + if (!(parse_executable_statements + (&(*result) -> data.ie.fc, + cfile, lose, context_any))) { + executable_statement_dereference (result, MDL); + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RBRACE) { + parse_warn (cfile, "right brace expected."); + skip_to_semi (cfile); + *lose = 1; + executable_statement_dereference (result, MDL); + return 0; + } + } + } else if (token == ELSIF) { + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_if_statement (&(*result) -> data.ie.fc, + cfile, lose)) { + if (!*lose) + parse_warn (cfile, + "expecting conditional."); + executable_statement_dereference (result, MDL); + *lose = 1; + return 0; + } + } else + (*result) -> data.ie.fc = (struct executable_statement *)0; + + return 1; +} + +/* + * boolean_expression :== CHECK STRING | + * NOT boolean-expression | + * data-expression EQUAL data-expression | + * data-expression BANG EQUAL data-expression | + * boolean-expression AND boolean-expression | + * boolean-expression OR boolean-expression + * EXISTS OPTION-NAME + */ + +int parse_boolean_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_boolean, + (struct expression **)0, expr_none)) + return 0; + + if (!is_boolean_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + parse_warn (cfile, "Expecting a boolean expression."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + return 1; +} + +/* + * data_expression :== SUBSTRING LPAREN data-expression COMMA + * numeric-expression COMMA + * numeric-expression RPAREN | + * CONCAT LPAREN data-expression COMMA + data-expression RPAREN + * SUFFIX LPAREN data_expression COMMA + * numeric-expression RPAREN | + * OPTION option_name | + * HARDWARE | + * PACKET LPAREN numeric-expression COMMA + * numeric-expression RPAREN | + * STRING | + * colon_seperated_hex_list + */ + +int parse_data_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_data, + (struct expression **)0, expr_none)) + return 0; + + if (!is_data_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + parse_warn (cfile, "Expecting a data expression."); + *lose = 1; + return 0; + } + return 1; +} + +/* + * numeric-expression :== EXTRACT_INT LPAREN data-expression + * COMMA number RPAREN | + * NUMBER + */ + +int parse_numeric_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_numeric, + (struct expression **)0, expr_none)) + return 0; + + if (!is_numeric_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + parse_warn (cfile, "Expecting a numeric expression."); + *lose = 1; + return 0; + } + return 1; +} + +/* + * dns-expression :== + * UPDATE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression COMMA numeric-expression RPAREN + * DELETE LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * NOT EXISTS LPAREN ns-class COMMA ns-type COMMA data-expression COMMA + * data-expression RPAREN + * ns-class :== IN | CHAOS | HS | NUMBER + * ns-type :== A | PTR | MX | TXT | NUMBER + */ + +int parse_dns_expression (expr, cfile, lose) + struct expression **expr; + struct parse *cfile; + int *lose; +{ + /* Parse an expression... */ + if (!parse_expression (expr, cfile, lose, context_dns, + (struct expression **)0, expr_none)) + return 0; + + if (!is_dns_expression (*expr) && + (*expr) -> op != expr_variable_reference && + (*expr) -> op != expr_funcall) { + parse_warn (cfile, "Expecting a dns update subexpression."); + *lose = 1; + return 0; + } + return 1; +} + +/* Parse a subexpression that does not contain a binary operator. */ + +int parse_non_binary (expr, cfile, lose, context) + struct expression **expr; + struct parse *cfile; + int *lose; + enum expression_context context; +{ + enum dhcp_token token; + const char *val; + struct collection *col; + struct option *option; + struct expression *nexp, **ep; + int known; + enum expr_op opcode; + const char *s; + char *cptr; + struct executable_statement *stmt; + int i; + unsigned long u; + isc_result_t status, code; + unsigned len; + + token = peek_token (&val, (unsigned *)0, cfile); + + /* Check for unary operators... */ + switch (token) { + case CHECK: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "string expected."); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + for (col = collections; col; col = col -> next) + if (!strcmp (col -> name, val)) + break; + if (!col) { + parse_warn (cfile, "unknown collection."); + *lose = 1; + return 0; + } + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_check; + (*expr) -> data.check = col; + break; + + case TOKEN_NOT: + token = next_token (&val, (unsigned *)0, cfile); + if (context == context_dns) { + token = peek_token (&val, (unsigned *)0, cfile); + goto not_exists; + } + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_not; + if (!parse_non_binary (&(*expr) -> data.not, + cfile, lose, context_boolean)) { + if (!*lose) { + parse_warn (cfile, "expression expected"); + skip_to_semi (cfile); + } + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + if (!is_boolean_expression ((*expr) -> data.not)) { + *lose = 1; + parse_warn (cfile, "boolean expression expected"); + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + break; + + case LPAREN: + token = next_token (&val, (unsigned *)0, cfile); + if (!parse_expression (expr, cfile, lose, context, + (struct expression **)0, expr_none)) { + if (!*lose) { + parse_warn (cfile, "expression expected"); + skip_to_semi (cfile); + } + *lose = 1; + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + *lose = 1; + parse_warn (cfile, "right paren expected"); + skip_to_semi (cfile); + return 0; + } + break; + + case EXISTS: + if (context == context_dns) + goto ns_exists; + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_exists; + known = 0; + (*expr) -> data.option = parse_option_name (cfile, 0, &known); + if (!(*expr) -> data.option) { + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case STATIC: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_static; + break; + + case KNOWN: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_known; + break; + + case SUBSTRING: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_substring; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + nolparen: + expression_dereference (expr, MDL); + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!parse_data_expression (&(*expr) -> data.substring.expr, + cfile, lose)) { + nodata: + expression_dereference (expr, MDL); + if (!*lose) { + parse_warn (cfile, + "expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + } + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + nocomma: + expression_dereference (expr, MDL); + parse_warn (cfile, "comma expected."); + *lose = 1; + + return 0; + } + + if (!parse_numeric_expression + (&(*expr) -> data.substring.offset,cfile, lose)) { + nonum: + if (!*lose) { + parse_warn (cfile, + "expecting numeric expression."); + skip_to_semi (cfile); + *lose = 1; + } + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression + (&(*expr) -> data.substring.len, cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + norparen: + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case SUFFIX: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_suffix; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_data_expression (&(*expr) -> data.suffix.expr, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.suffix.len, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case CONCAT: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_concat; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_data_expression (&(*expr) -> data.concat [0], + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + concat_another: + if (!parse_data_expression (&(*expr) -> data.concat [1], + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + + if (token == COMMA) { + nexp = (struct expression *)0; + if (!expression_allocate (&nexp, MDL)) + log_fatal ("can't allocate at CONCAT2"); + nexp -> op = expr_concat; + expression_reference (&nexp -> data.concat [0], + *expr, MDL); + expression_dereference (expr, MDL); + expression_reference (expr, nexp, MDL); + expression_dereference (&nexp, MDL); + goto concat_another; + } + + if (token != RPAREN) + goto norparen; + break; + + case BINARY_TO_ASCII: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_binary_to_ascii; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_numeric_expression (&(*expr) -> data.b2a.base, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.b2a.width, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_data_expression (&(*expr) -> data.b2a.seperator, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_data_expression (&(*expr) -> data.b2a.buffer, + cfile, lose)) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case REVERSE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_reverse; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!(parse_numeric_expression + (&(*expr) -> data.reverse.width, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.reverse.buffer, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case PICK: + /* pick (a, b, c) actually produces an internal representation + that looks like pick (a, pick (b, pick (c, nil))). */ + token = next_token (&val, (unsigned *)0, cfile); + if (!(expression_allocate (expr, MDL))) + log_fatal ("can't allocate expression"); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + nexp = (struct expression *)0; + expression_reference (&nexp, *expr, MDL); + do { + nexp -> op = expr_pick_first_value; + if (!(parse_data_expression + (&nexp -> data.pick_first_value.car, + cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token == COMMA) { + struct expression *foo = (struct expression *)0; + if (!expression_allocate (&foo, MDL)) + log_fatal ("can't allocate expr"); + expression_reference + (&nexp -> data.pick_first_value.cdr, foo, MDL); + expression_dereference (&nexp, MDL); + expression_reference (&nexp, foo, MDL); + expression_dereference (&foo, MDL); + } + } while (token == COMMA); + expression_dereference (&nexp, MDL); + + if (token != RPAREN) + goto norparen; + break; + + /* dns-update and dns-delete are present for historical + purposes, but are deprecated in favor of ns-update + in combination with update, delete, exists and not + exists. */ + case DNS_UPDATE: + case DNS_DELETE: +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + token = next_token (&val, (unsigned *)0, cfile); + if (token == DNS_UPDATE) + opcode = expr_ns_add; + else + opcode = expr_ns_delete; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, + "parse_expression: expecting string."); + badnsupdate: + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!strcasecmp (val, "a")) + u = T_A; + else if (!strcasecmp (val, "ptr")) + u = T_PTR; + else if (!strcasecmp (val, "mx")) + u = T_MX; + else if (!strcasecmp (val, "cname")) + u = T_CNAME; + else if (!strcasecmp (val, "TXT")) + u = T_TXT; + else { + parse_warn (cfile, "unexpected rrtype: %s", val); + goto badnsupdate; + } + + s = (opcode == expr_ns_add + ? "old-dns-update" + : "old-dns-delete"); + cptr = dmalloc (strlen (s) + 1, MDL); + if (!cptr) + log_fatal ("can't allocate name for %s", s); + strcpy (cptr, s); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_funcall; + (*expr) -> data.funcall.name = cptr; + + /* Fake up a function call. */ + ep = &(*expr) -> data.funcall.arglist; + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!make_const_int (&(*ep) -> data.arg.val, u)) + log_fatal ("can't allocate rrtype value."); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_data_expression (&(*ep) -> data.arg.val, + cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_data_expression (&(*ep) -> data.arg.val, + cfile, lose))) + goto nodata; + + if (opcode == expr_ns_add) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + ep = &((*ep) -> data.arg.next); + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!(parse_numeric_expression (&(*ep) -> data.arg.val, + cfile, lose))) { + parse_warn (cfile, + "expecting numeric expression."); + goto badnsupdate; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case NS_UPDATE: +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + nexp = *expr; + do { + nexp -> op = expr_dns_transaction; + if (!(parse_dns_expression + (&nexp -> data.dns_transaction.car, + cfile, lose))) + { + if (!*lose) + parse_warn + (cfile, + "expecting dns expression."); + badnstrans: + expression_dereference (expr, MDL); + *lose = 1; + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + + if (token == COMMA) { + if (!(expression_allocate + (&nexp -> data.dns_transaction.cdr, + MDL))) + log_fatal + ("can't allocate expression"); + nexp = nexp -> data.dns_transaction.cdr; + } + } while (token == COMMA); + + if (token != RPAREN) + goto norparen; + break; + + /* NOT EXISTS is special cased above... */ + not_exists: + token = peek_token (&val, (unsigned *)0, cfile); + if (token != EXISTS) { + parse_warn (cfile, "expecting DNS prerequisite."); + *lose = 1; + return 0; + } + opcode = expr_ns_not_exists; + goto nsupdatecode; + case TOKEN_ADD: + opcode = expr_ns_add; + goto nsupdatecode; + case TOKEN_DELETE: + opcode = expr_ns_delete; + goto nsupdatecode; + ns_exists: + opcode = expr_ns_exists; + nsupdatecode: + token = next_token (&val, (unsigned *)0, cfile); + +#if !defined (NSUPDATE) + parse_warn (cfile, + "Please rebuild dhcpd with --with-nsupdate."); +#endif + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = opcode; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token) && token != NUMBER) { + parse_warn (cfile, "expecting identifier or number."); + badnsop: + expression_dereference (expr, MDL); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (token == NUMBER) + (*expr) -> data.ns_add.rrclass = atoi (val); + else if (!strcasecmp (val, "in")) + (*expr) -> data.ns_add.rrclass = C_IN; + else if (!strcasecmp (val, "chaos")) + (*expr) -> data.ns_add.rrclass = C_CHAOS; + else if (!strcasecmp (val, "hs")) + (*expr) -> data.ns_add.rrclass = C_HS; + else { + parse_warn (cfile, "unexpected rrclass: %s", val); + goto badnsop; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token) && token != NUMBER) { + parse_warn (cfile, "expecting identifier or number."); + goto badnsop; + } + + if (token == NUMBER) + (*expr) -> data.ns_add.rrtype = atoi (val); + else if (!strcasecmp (val, "a")) + (*expr) -> data.ns_add.rrtype = T_A; + else if (!strcasecmp (val, "ptr")) + (*expr) -> data.ns_add.rrtype = T_PTR; + else if (!strcasecmp (val, "mx")) + (*expr) -> data.ns_add.rrtype = T_MX; + else if (!strcasecmp (val, "cname")) + (*expr) -> data.ns_add.rrtype = T_CNAME; + else if (!strcasecmp (val, "TXT")) + (*expr) -> data.ns_add.rrtype = T_TXT; + else { + parse_warn (cfile, "unexpected rrtype: %s", val); + goto badnsop; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.ns_add.rrname, cfile, lose))) + goto nodata; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_data_expression + (&(*expr) -> data.ns_add.rrdata, cfile, lose))) + goto nodata; + + if (opcode == expr_ns_add) { + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!(parse_numeric_expression + (&(*expr) -> data.ns_add.ttl, cfile, + lose))) { + if (!*lose) + parse_warn (cfile, + "expecting numeric expression."); + goto badnsupdate; + } + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case OPTION: + case CONFIG_OPTION: + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = (token == OPTION + ? expr_option + : expr_config_option); + token = next_token (&val, (unsigned *)0, cfile); + known = 0; + (*expr) -> data.option = parse_option_name (cfile, 0, &known); + if (!(*expr) -> data.option) { + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case HARDWARE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_hardware; + break; + + case LEASED_ADDRESS: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_leased_address; + break; + + case CLIENT_STATE: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_client_state; + break; + + case FILENAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_filename; + break; + + case SERVER_NAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_sname; + break; + + case LEASE_TIME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_lease_time; + break; + + case TOKEN_NULL: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_null; + break; + + case HOST_DECL_NAME: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_host_decl_name; + break; + + case UPDATED_DNS_RR: + token = next_token (&val, (unsigned *)0, cfile); + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != STRING) { + parse_warn (cfile, "expecting string."); + bad_rrtype: + *lose = 1; + return 0; + } + if (!strcasecmp (val, "a")) + s = "ddns-fwd-name"; + else if (!strcasecmp (val, "ptr")) + s = "ddns-rev-name"; + else { + parse_warn (cfile, "invalid DNS rrtype: %s", val); + goto bad_rrtype; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_reference; + (*expr) -> data.variable = + dmalloc (strlen (s) + 1, MDL); + if (!(*expr) -> data.variable) + log_fatal ("can't allocate variable name."); + strcpy ((*expr) -> data.variable, s); + break; + + case PACKET: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_packet; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + if (!parse_numeric_expression (&(*expr) -> data.packet.offset, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) + goto nocomma; + + if (!parse_numeric_expression (&(*expr) -> data.packet.len, + cfile, lose)) + goto nonum; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + case STRING: + token = next_token (&val, &len, cfile); + if (!make_const_data (expr, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("can't make constant string expression."); + break; + + case EXTRACT_INT: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + if (!parse_data_expression (&(*expr) -> data.extract_int, + cfile, lose)) { + if (!*lose) { + parse_warn (cfile, + "expecting data expression."); + skip_to_semi (cfile); + *lose = 1; + } + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "number expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + switch (atoi (val)) { + case 8: + (*expr) -> op = expr_extract_int8; + break; + + case 16: + (*expr) -> op = expr_extract_int16; + break; + + case 32: + (*expr) -> op = expr_extract_int32; + break; + + default: + parse_warn (cfile, + "unsupported integer size %d", atoi (val)); + *lose = 1; + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case ENCODE_INT: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + parse_warn (cfile, "left parenthesis expected."); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + if (!parse_numeric_expression (&(*expr) -> data.encode_int, + cfile, lose)) { + parse_warn (cfile, "expecting numeric expression."); + skip_to_semi (cfile); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != COMMA) { + parse_warn (cfile, "comma expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) { + parse_warn (cfile, "number expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + switch (atoi (val)) { + case 8: + (*expr) -> op = expr_encode_int8; + break; + + case 16: + (*expr) -> op = expr_encode_int16; + break; + + case 32: + (*expr) -> op = expr_encode_int32; + break; + + default: + parse_warn (cfile, + "unsupported integer size %d", atoi (val)); + *lose = 1; + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) { + parse_warn (cfile, "right parenthesis expected."); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + + case NUMBER: + /* If we're in a numeric context, this should just be a + number, by itself. */ + if (context == context_numeric || + context == context_data_or_numeric) { + next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = atoi (val); + break; + } + + case NUMBER_OR_NAME: + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + + (*expr) -> op = expr_const_data; + if (!parse_cshl (&(*expr) -> data.const_data, cfile)) { + expression_dereference (expr, MDL); + return 0; + } + break; + + case NS_FORMERR: + known = FORMERR; + goto ns_const; + ns_const: + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = known; + break; + + case NS_NOERROR: + known = ISC_R_SUCCESS; + goto ns_const; + + case NS_NOTAUTH: + known = ISC_R_NOTAUTH; + goto ns_const; + + case NS_NOTIMP: + known = ISC_R_NOTIMPLEMENTED; + goto ns_const; + + case NS_NOTZONE: + known = ISC_R_NOTZONE; + goto ns_const; + + case NS_NXDOMAIN: + known = ISC_R_NXDOMAIN; + goto ns_const; + + case NS_NXRRSET: + known = ISC_R_NXRRSET; + goto ns_const; + + case NS_REFUSED: + known = ISC_R_REFUSED; + goto ns_const; + + case NS_SERVFAIL: + known = ISC_R_SERVFAIL; + goto ns_const; + + case NS_YXDOMAIN: + known = ISC_R_YXDOMAIN; + goto ns_const; + + case NS_YXRRSET: + known = ISC_R_YXRRSET; + goto ns_const; + + case BOOTING: + known = S_INIT; + goto ns_const; + + case REBOOT: + known = S_REBOOTING; + goto ns_const; + + case SELECT: + known = S_SELECTING; + goto ns_const; + + case REQUEST: + known = S_REQUESTING; + goto ns_const; + + case BOUND: + known = S_BOUND; + goto ns_const; + + case RENEW: + known = S_RENEWING; + goto ns_const; + + case REBIND: + known = S_REBINDING; + goto ns_const; + + case DEFINED: + token = next_token (&val, (unsigned *)0, cfile); + token = next_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) + goto nolparen; + + token = next_token (&val, (unsigned *)0, cfile); + if (token != NAME && token != NUMBER_OR_NAME) { + parse_warn (cfile, "%s can't be a variable name", val); + skip_to_semi (cfile); + *lose = 1; + return 0; + } + + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_exists; + (*expr) -> data.variable = dmalloc (strlen (val) + 1, MDL); + if (!(*expr)->data.variable) + log_fatal ("can't allocate variable name"); + strcpy ((*expr) -> data.variable, val); + token = next_token (&val, (unsigned *)0, cfile); + if (token != RPAREN) + goto norparen; + break; + + /* Not a valid start to an expression... */ + default: + if (token != NAME && token != NUMBER_OR_NAME) + return 0; + + token = next_token (&val, (unsigned *)0, cfile); + + /* Save the name of the variable being referenced. */ + cptr = dmalloc (strlen (val) + 1, MDL); + if (!cptr) + log_fatal ("can't allocate variable name"); + strcpy (cptr, val); + + /* Simple variable reference, as far as we can tell. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != LPAREN) { + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_variable_reference; + (*expr) -> data.variable = cptr; + break; + } + + token = next_token (&val, (unsigned *)0, cfile); + if (!expression_allocate (expr, MDL)) + log_fatal ("can't allocate expression"); + (*expr) -> op = expr_funcall; + (*expr) -> data.funcall.name = cptr; + + /* Now parse the argument list. */ + ep = &(*expr) -> data.funcall.arglist; + do { + if (!expression_allocate (ep, MDL)) + log_fatal ("can't allocate expression"); + (*ep) -> op = expr_arg; + if (!parse_expression (&(*ep) -> data.arg.val, + cfile, lose, context_any, + (struct expression **)0, + expr_none)) { + if (!*lose) { + parse_warn (cfile, + "expecting expression."); + *lose = 1; + } + skip_to_semi (cfile); + expression_dereference (expr, MDL); + return 0; + } + ep = &((*ep) -> data.arg.next); + token = next_token (&val, (unsigned *)0, cfile); + } while (token == COMMA); + if (token != RPAREN) { + parse_warn (cfile, "Right parenthesis expected."); + skip_to_semi (cfile); + *lose = 1; + expression_dereference (expr, MDL); + return 0; + } + break; + } + return 1; +} + +/* Parse an expression. */ + +int parse_expression (expr, cfile, lose, context, plhs, binop) + struct expression **expr; + struct parse *cfile; + int *lose; + enum expression_context context; + struct expression **plhs; + enum expr_op binop; +{ + enum dhcp_token token; + const char *val; + struct expression *rhs = (struct expression *)0, *tmp; + struct expression *lhs = (struct expression *)0; + enum expr_op next_op; + enum expression_context + lhs_context = context_any, + rhs_context = context_any; + + /* Consume the left hand side we were passed. */ + if (plhs) { + expression_reference (&lhs, *plhs, MDL); + expression_dereference (plhs, MDL); + } + + new_rhs: + if (!parse_non_binary (&rhs, cfile, lose, context)) { + /* If we already have a left-hand side, then it's not + okay for there not to be a right-hand side here, so + we need to flag it as an error. */ + if (lhs) { + if (!*lose) { + parse_warn (cfile, + "expecting right-hand side."); + *lose = 1; + skip_to_semi (cfile); + } + expression_dereference (&lhs, MDL); + } + return 0; + } + + /* At this point, rhs contains either an entire subexpression, + or at least a left-hand-side. If we do not see a binary token + as the next token, we're done with the expression. */ + + token = peek_token (&val, (unsigned *)0, cfile); + switch (token) { + case BANG: + token = next_token (&val, (unsigned *)0, cfile); + token = peek_token (&val, (unsigned *)0, cfile); + if (token != EQUAL) { + parse_warn (cfile, "! in boolean context without ="); + *lose = 1; + skip_to_semi (cfile); + if (lhs) + expression_dereference (&lhs, MDL); + return 0; + } + next_op = expr_not_equal; + context = expression_context (rhs); + break; + + case EQUAL: + next_op = expr_equal; + context = expression_context (rhs); + break; + + case AND: + next_op = expr_and; + context = expression_context (rhs); + if (context != context_boolean) { + needbool: + parse_warn (cfile, "expecting boolean expressions"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + *lose = 1; + return 0; + } + break; + + case OR: + next_op = expr_or; + context = expression_context (rhs); + if (context != context_boolean) + goto needbool; + break; + + case PLUS: + next_op = expr_add; + context = expression_context (rhs); + if (context != context_numeric) { + neednum: + parse_warn (cfile, "expecting numeric expressions"); + skip_to_semi (cfile); + expression_dereference (&rhs, MDL); + *lose = 1; + return 0; + } + break; + + case MINUS: + next_op = expr_subtract; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case SLASH: + next_op = expr_divide; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case ASTERISK: + next_op = expr_multiply; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case PERCENT: + next_op = expr_remainder; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case AMPERSAND: + next_op = expr_binary_and; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case PIPE: + next_op = expr_binary_or; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + case CARET: + next_op = expr_binary_xor; + context = expression_context (rhs); + if (context != context_numeric) + goto neednum; + break; + + default: + next_op = expr_none; + } + + /* If we have no lhs yet, we just parsed it. */ + if (!lhs) { + /* If there was no operator following what we just parsed, + then we're done - return it. */ + if (next_op == expr_none) { + *expr = rhs; + return 1; + } + lhs = rhs; + rhs = (struct expression *)0; + binop = next_op; + next_token (&val, (unsigned *)0, cfile); + goto new_rhs; + } + + /* Now, if we didn't find a binary operator, we're done parsing + this subexpression, so combine it with the preceding binary + operator and return the result. */ + if (next_op == expr_none) { + if (!expression_allocate (expr, MDL)) + log_fatal ("Can't allocate expression!"); + + (*expr) -> op = binop; + /* All the binary operators' data union members + are the same, so we'll cheat and use the member + for the equals operator. */ + (*expr) -> data.equal [0] = lhs; + (*expr) -> data.equal [1] = rhs; + return 1; + } + + /* Eat the operator token - we now know it was a binary operator... */ + token = next_token (&val, (unsigned *)0, cfile); + + /* If the binary operator we saw previously has a lower precedence + than the next operator, then the rhs we just parsed for that + operator is actually the lhs of the operator with the higher + precedence - to get the real rhs, we need to recurse on the + new operator. */ + if (binop != expr_none && + op_precedence (binop, next_op) < 0) { + tmp = rhs; + rhs = (struct expression *)0; + if (!parse_expression (&rhs, cfile, lose, op_context (next_op), + &tmp, next_op)) { + if (!*lose) { + parse_warn (cfile, + "expecting a subexpression"); + *lose = 1; + } + return 0; + } + next_op = expr_none; + } + + /* Now combine the LHS and the RHS using binop. */ + tmp = (struct expression *)0; + if (!expression_allocate (&tmp, MDL)) + log_fatal ("No memory for equal precedence combination."); + + /* Store the LHS and RHS. */ + tmp -> data.equal [0] = lhs; + tmp -> data.equal [1] = rhs; + tmp -> op = binop; + + lhs = tmp; + tmp = (struct expression *)0; + rhs = (struct expression *)0; + + /* Recursions don't return until we have parsed the end of the + expression, so if we recursed earlier, we can now return what + we got. */ + if (next_op == expr_none) { + *expr = lhs; + return 1; + } + + binop = next_op; + goto new_rhs; +} + +/* option-statement :== identifier DOT identifier <syntax> SEMI + | identifier <syntax> SEMI + + Option syntax is handled specially through format strings, so it + would be painful to come up with BNF for it. However, it always + starts as above and ends in a SEMI. */ + +int parse_option_statement (result, cfile, lookups, option, op) + struct executable_statement **result; + struct parse *cfile; + int lookups; + struct option *option; + enum statement_op op; +{ + const char *val; + enum dhcp_token token; + const char *fmt = NULL; + struct expression *expr = (struct expression *)0; + struct expression *tmp; + int lose; + struct executable_statement *stmt; + int ftt = 1; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == SEMI) { + /* Eat the semicolon... */ + token = next_token (&val, (unsigned *)0, cfile); + goto done; + } + + if (token == EQUAL) { + /* Eat the equals sign. */ + token = next_token (&val, (unsigned *)0, cfile); + + /* Parse a data expression and use its value for the data. */ + if (!parse_data_expression (&expr, cfile, &lose)) { + /* In this context, we must have an executable + statement, so if we found something else, it's + still an error. */ + if (!lose) { + parse_warn (cfile, + "expecting a data expression."); + skip_to_semi (cfile); + } + return 0; + } + + /* We got a valid expression, so use it. */ + goto done; + } + + /* Parse the option data... */ + do { + /* Set a flag if this is an array of a simple type (i.e., + not an array of pairs of IP addresses, or something + like that. */ + int uniform = option -> format [1] == 'A'; + + and_again: + /* Set fmt to start of format for 'A' and one char back + for 'a' */ + if ((fmt != NULL) && + (fmt != option -> format) && (*fmt == 'a')) + fmt -= 1; + else + fmt = ((fmt == NULL) || + (*fmt == 'A')) ? option -> format : fmt; + + /* 'a' means always uniform */ + uniform |= (fmt [1] == 'a'); + + for ( ; *fmt; fmt++) { + if ((*fmt == 'A') || (*fmt == 'a')) + break; + if (*fmt == 'o') + continue; + tmp = expr; + expr = (struct expression *)0; + if (!parse_option_token (&expr, cfile, &fmt, + tmp, uniform, lookups)) { + if (fmt [1] != 'o') { + if (tmp) + expression_dereference (&tmp, + MDL); + return 0; + } + expr = tmp; + tmp = (struct expression *)0; + } + if (tmp) + expression_dereference (&tmp, MDL); + } + if ((*fmt == 'A') || (*fmt == 'a')) { + token = peek_token (&val, (unsigned *)0, cfile); + /* Comma means: continue with next element in array */ + if (token == COMMA) { + token = next_token (&val, + (unsigned *)0, cfile); + continue; + } + /* no comma: end of array. + 'A' or end of string means: leave the loop */ + if ((*fmt == 'A') || (fmt[1] == '\0')) + break; + /* 'a' means: go on with next char */ + if (*fmt == 'a') { + fmt++; + goto and_again; + } + } + } while ((*fmt == 'A') || (*fmt == 'a')); + + done: + if (!parse_semi (cfile)) + return 0; + if (!executable_statement_allocate (result, MDL)) + log_fatal ("no memory for option statement."); + (*result) -> op = op; + if (expr && !option_cache (&(*result) -> data.option, + (struct data_string *)0, expr, option, MDL)) + log_fatal ("no memory for option cache"); + if (expr) + expression_dereference (&expr, MDL); + return 1; +} + +int parse_option_token (rv, cfile, fmt, expr, uniform, lookups) + struct expression **rv; + struct parse *cfile; + const char **fmt; + struct expression *expr; + int uniform; + int lookups; +{ + const char *val; + enum dhcp_token token; + struct expression *t = (struct expression *)0; + unsigned char buf [4]; + unsigned len; + unsigned char *ob; + struct iaddr addr; + int num; + const char *f, *g; + struct enumeration_value *e; + + switch (**fmt) { + case 'U': + token = peek_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting identifier."); + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, &len, cfile); + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for %s", val); + break; + + case 'E': + g = strchr (*fmt, '.'); + if (!g) { + parse_warn (cfile, + "malformed encapsulation format (bug!)"); + skip_to_semi (cfile); + return 0; + } + *fmt = g; + case 'X': + token = peek_token (&val, (unsigned *)0, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + if (!expression_allocate (&t, MDL)) + return 0; + if (!parse_cshl (&t -> data.const_data, cfile)) { + expression_dereference (&t, MDL); + return 0; + } + t -> op = expr_const_data; + } else if (token == STRING) { + token = next_token (&val, &len, cfile); + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for \"%s\"", val); + } else { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting string %s.", + "or hexadecimal data"); + skip_to_semi (cfile); + } + return 0; + } + break; + + case 'd': /* Domain name... */ + val = parse_host_name (cfile); + if (!val) { + parse_warn (cfile, "not a valid domain name."); + skip_to_semi (cfile); + return 0; + } + len = strlen (val); + goto make_string; + + case 't': /* Text string... */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != STRING && !is_identifier (token)) { + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting string."); + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, &len, cfile); + make_string: + if (!make_const_data (&t, (const unsigned char *)val, + len, 1, 1, MDL)) + log_fatal ("No memory for concatenation"); + break; + + case 'N': + f = (*fmt) + 1; + g = strchr (*fmt, '.'); + if (!g) { + parse_warn (cfile, "malformed %s (bug!)", + "enumeration format"); + foo: + skip_to_semi (cfile); + return 0; + } + *fmt = g; + token = next_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "identifier expected"); + goto foo; + } + e = find_enumeration_value (f, (*fmt) - f, val); + if (!e) { + parse_warn (cfile, "unknown value"); + goto foo; + } + if (!make_const_data (&t, &e -> value, 1, 0, 1, MDL)) + return 0; + break; + + case 'I': /* IP address or hostname. */ + if (lookups) { + if (!parse_ip_addr_or_hostname (&t, cfile, uniform)) + return 0; + } else { + if (!parse_ip_addr (cfile, &addr)) + return 0; + if (!make_const_data (&t, addr.iabuf, addr.len, + 0, 1, MDL)) + return 0; + } + break; + + case 'T': /* Lease interval. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != INFINITE) + goto check_number; + token = next_token (&val, (unsigned *)0, cfile); + putLong (buf, -1); + if (!make_const_data (&t, buf, 4, 0, 1, MDL)) + return 0; + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = peek_token (&val, (unsigned *)0, cfile); + check_number: + if (token != NUMBER) { + need_number: + if ((*fmt) [1] != 'o') { + parse_warn (cfile, "expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 32); + if (!make_const_data (&t, buf, 4, 0, 1, MDL)) + return 0; + break; + + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 16); + if (!make_const_data (&t, buf, 2, 0, 1, MDL)) + return 0; + break; + + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + token = next_token (&val, (unsigned *)0, cfile); + convert_num (cfile, buf, val, 0, 8); + if (!make_const_data (&t, buf, 1, 0, 1, MDL)) + return 0; + break; + + case 'f': /* Boolean flag. */ + token = peek_token (&val, (unsigned *)0, cfile); + if (!is_identifier (token)) { + if ((*fmt) [1] != 'o') + parse_warn (cfile, "expecting identifier."); + bad_flag: + if ((*fmt) [1] != 'o') { + if (token != SEMI) + skip_to_semi (cfile); + } + return 0; + } + if (!strcasecmp (val, "true") + || !strcasecmp (val, "on")) + buf [0] = 1; + else if (!strcasecmp (val, "false") + || !strcasecmp (val, "off")) + buf [0] = 0; + else if (!strcasecmp (val, "ignore")) + buf [0] = 2; + else { + if ((*fmt) [1] != 'o') + parse_warn (cfile, "expecting boolean."); + goto bad_flag; + } + token = next_token (&val, (unsigned *)0, cfile); + if (!make_const_data (&t, buf, 1, 0, 1, MDL)) + return 0; + break; + + default: + parse_warn (cfile, "Bad format %c in parse_option_token.", + **fmt); + skip_to_semi (cfile); + return 0; + } + if (expr) { + if (!make_concat (rv, expr, t)) + return 0; + } else + expression_reference (rv, t, MDL); + expression_dereference (&t, MDL); + return 1; +} + +int parse_option_decl (oc, cfile) + struct option_cache **oc; + struct parse *cfile; +{ + const char *val; + int token; + u_int8_t buf [4]; + u_int8_t hunkbuf [1024]; + unsigned hunkix = 0; + const char *fmt, *f; + struct option *option; + struct iaddr ip_addr; + u_int8_t *dp; + unsigned len; + int nul_term = 0; + struct buffer *bp; + int known = 0; + struct enumeration_value *e; + + option = parse_option_name (cfile, 0, &known); + if (!option) + return 0; + + /* Parse the option data... */ + do { + /* Set a flag if this is an array of a simple type (i.e., + not an array of pairs of IP addresses, or something + like that. */ + int uniform = option -> format [1] == 'A'; + + for (fmt = option -> format; *fmt; fmt++) { + if (*fmt == 'A') + break; + switch (*fmt) { + case 'E': + fmt = strchr (fmt, '.'); + if (!fmt) { + parse_warn (cfile, + "malformed %s (bug!)", + "encapsulation format"); + skip_to_semi (cfile); + return 0; + } + case 'X': + len = parse_X (cfile, &hunkbuf [hunkix], + sizeof hunkbuf - hunkix); + hunkix += len; + break; + + case 't': /* Text string... */ + token = next_token (&val, + &len, cfile); + if (token != STRING) { + parse_warn (cfile, + "expecting string."); + skip_to_semi (cfile); + return 0; + } + if (hunkix + len + 1 > sizeof hunkbuf) { + parse_warn (cfile, + "option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return 0; + } + memcpy (&hunkbuf [hunkix], val, len + 1); + nul_term = 1; + hunkix += len; + break; + + case 'N': + f = fmt; + fmt = strchr (fmt, '.'); + if (!fmt) { + parse_warn (cfile, + "malformed %s (bug!)", + "enumeration format"); + foo: + skip_to_semi (cfile); + return 0; + } + token = next_token (&val, + (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "identifier expected"); + goto foo; + } + e = find_enumeration_value (f, fmt - f, val); + if (!e) { + parse_warn (cfile, + "unknown value"); + goto foo; + } + len = 1; + dp = &e -> value; + goto alloc; + + case 'I': /* IP address. */ + if (!parse_ip_addr (cfile, &ip_addr)) + return 0; + len = ip_addr.len; + dp = ip_addr.iabuf; + + alloc: + if (hunkix + len > sizeof hunkbuf) { + parse_warn (cfile, + "option data buffer %s", + "overflow"); + skip_to_semi (cfile); + return 0; + } + memcpy (&hunkbuf [hunkix], dp, len); + hunkix += len; + break; + + case 'L': /* Unsigned 32-bit integer... */ + case 'l': /* Signed 32-bit integer... */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) { + need_number: + parse_warn (cfile, + "expecting number."); + if (token != SEMI) + skip_to_semi (cfile); + return 0; + } + convert_num (cfile, buf, val, 0, 32); + len = 4; + dp = buf; + goto alloc; + + case 's': /* Signed 16-bit integer. */ + case 'S': /* Unsigned 16-bit integer. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + convert_num (cfile, buf, val, 0, 16); + len = 2; + dp = buf; + goto alloc; + + case 'b': /* Signed 8-bit integer. */ + case 'B': /* Unsigned 8-bit integer. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (token != NUMBER) + goto need_number; + convert_num (cfile, buf, val, 0, 8); + len = 1; + dp = buf; + goto alloc; + + case 'f': /* Boolean flag. */ + token = next_token (&val, + (unsigned *)0, cfile); + if (!is_identifier (token)) { + parse_warn (cfile, + "expecting identifier."); + bad_flag: + if (token != SEMI) + skip_to_semi (cfile); + return 0; + } + if (!strcasecmp (val, "true") + || !strcasecmp (val, "on")) + buf [0] = 1; + else if (!strcasecmp (val, "false") + || !strcasecmp (val, "off")) + buf [0] = 0; + else { + parse_warn (cfile, + "expecting boolean."); + goto bad_flag; + } + len = 1; + dp = buf; + goto alloc; + + default: + log_error ("parse_option_param: Bad format %c", + *fmt); + skip_to_semi (cfile); + return 0; + } + } + token = next_token (&val, (unsigned *)0, cfile); + } while (*fmt == 'A' && token == COMMA); + + if (token != SEMI) { + parse_warn (cfile, "semicolon expected."); + skip_to_semi (cfile); + return 0; + } + + bp = (struct buffer *)0; + if (!buffer_allocate (&bp, hunkix + nul_term, MDL)) + log_fatal ("no memory to store option declaration."); + if (!bp -> data) + log_fatal ("out of memory allocating option data."); + memcpy (bp -> data, hunkbuf, hunkix + nul_term); + + if (!option_cache_allocate (oc, MDL)) + log_fatal ("out of memory allocating option cache."); + + (*oc) -> data.buffer = bp; + (*oc) -> data.data = &bp -> data [0]; + (*oc) -> data.terminated = nul_term; + (*oc) -> data.len = hunkix; + (*oc) -> option = option; + return 1; +} + +/* Consider merging parse_cshl into this. */ + +int parse_X (cfile, buf, max) + struct parse *cfile; + u_int8_t *buf; + unsigned max; +{ + int token; + const char *val; + unsigned len; + u_int8_t *s; + + token = peek_token (&val, (unsigned *)0, cfile); + if (token == NUMBER_OR_NAME || token == NUMBER) { + len = 0; + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token != NUMBER && token != NUMBER_OR_NAME) { + parse_warn (cfile, + "expecting hexadecimal constant."); + skip_to_semi (cfile); + return 0; + } + convert_num (cfile, &buf [len], val, 16, 8); + if (len++ > max) { + parse_warn (cfile, + "hexadecimal constant too long."); + skip_to_semi (cfile); + return 0; + } + token = peek_token (&val, (unsigned *)0, cfile); + if (token == COLON) + token = next_token (&val, + (unsigned *)0, cfile); + } while (token == COLON); + val = (char *)buf; + } else if (token == STRING) { + token = next_token (&val, &len, cfile); + if (len + 1 > max) { + parse_warn (cfile, "string constant too long."); + skip_to_semi (cfile); + return 0; + } + memcpy (buf, val, len + 1); + } else { + parse_warn (cfile, "expecting string or hexadecimal data"); + skip_to_semi (cfile); + return 0; + } + return len; +} + +int parse_warn (struct parse *cfile, const char *fmt, ...) +{ + va_list list; + char lexbuf [256]; + char mbuf [1024]; + char fbuf [1024]; + unsigned i, lix; + + do_percentm (mbuf, fmt); +#ifndef NO_SNPRINTF + snprintf (fbuf, sizeof fbuf, "%s line %d: %s", + cfile -> tlname, cfile -> lexline, mbuf); +#else + sprintf (fbuf, "%s line %d: %s", + cfile -> tlname, cfile -> lexline, mbuf); +#endif + + va_start (list, fmt); + vsnprintf (mbuf, sizeof mbuf, fbuf, list); + va_end (list); + + lix = 0; + for (i = 0; + cfile -> token_line [i] && i < (cfile -> lexchar - 1); i++) { + if (lix < (sizeof lexbuf) - 1) + lexbuf [lix++] = ' '; + if (cfile -> token_line [i] == '\t') { + for (lix; + lix < (sizeof lexbuf) - 1 && (lix & 7); lix++) + lexbuf [lix] = ' '; + } + } + lexbuf [lix] = 0; + +#ifndef DEBUG + syslog (log_priority | LOG_ERR, "%s", mbuf); + syslog (log_priority | LOG_ERR, "%s", cfile -> token_line); + if (cfile -> lexchar < 81) + syslog (log_priority | LOG_ERR, "%s^", lexbuf); +#endif + + if (log_perror) { + write (2, mbuf, strlen (mbuf)); + write (2, "\n", 1); + write (2, cfile -> token_line, strlen (cfile -> token_line)); + write (2, "\n", 1); + if (cfile -> lexchar < 81) + write (2, lexbuf, lix); + write (2, "^\n", 2); + } + + cfile -> warnings_occurred = 1; + + return 0; +} diff --git a/contrib/isc-dhcp/common/print.c b/contrib/isc-dhcp/common/print.c index d05af8d..67e8ddd 100644 --- a/contrib/isc-dhcp/common/print.c +++ b/contrib/isc-dhcp/common/print.c @@ -3,7 +3,7 @@ Turn data structures into printable text. */ /* - * 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,146 @@ * 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: print.c,v 1.16.2.3 1999/02/13 19:19:03 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n"; +"$Id: print.c,v 1.53.2.4 2001/08/08 14:49:20 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" +char *quotify_string (const char *s, const char *file, int line) +{ + unsigned len = 0; + const char *sp; + char *buf, *nsp; + + for (sp = s; sp && *sp; sp++) { + if (*sp == ' ') + len++; + else if (!isascii (*sp) || !isprint (*sp)) + len += 4; + else if (*sp == '"' || *sp == '\\') + len += 2; + else + len++; + } + + buf = dmalloc (len + 1, file, line); + if (buf) { + nsp = buf; + for (sp = s; sp && *sp; sp++) { + if (*sp == ' ') + *nsp++ = ' '; + else if (!isascii (*sp) || !isprint (*sp)) { + sprintf (nsp, "\\%03o", + *(const unsigned char *)sp); + nsp += 4; + } else if (*sp == '"' || *sp == '\\') { + *nsp++ = '\\'; + *nsp++ = *sp; + } else + *nsp++ = *sp; + } + *nsp++ = 0; + } + return buf; +} + +char *quotify_buf (const unsigned char *s, unsigned len, + const char *file, int line) +{ + unsigned nulen = 0; + char *buf, *nsp; + int i; + + for (i = 0; i < len; i++) { + if (s [i] == ' ') + nulen++; + else if (!isascii (s [i]) || !isprint (s [i])) + nulen += 4; + else if (s [i] == '"' || s [i] == '\\') + nulen += 2; + else + nulen++; + } + + buf = dmalloc (nulen + 1, MDL); + if (buf) { + nsp = buf; + for (i = 0; i < len; i++) { + if (s [i] == ' ') + *nsp++ = ' '; + else if (!isascii (s [i]) || !isprint (s [i])) { + sprintf (nsp, "\\%03o", s [i]); + nsp += 4; + } else if (s [i] == '"' || s [i] == '\\') { + *nsp++ = '\\'; + *nsp++ = s [i]; + } else + *nsp++ = s [i]; + } + *nsp++ = 0; + } + return buf; +} + +char *print_base64 (const unsigned char *buf, unsigned len, + const char *file, int line) +{ + char *s, *b; + unsigned bl; + int i; + unsigned val, extra; + static char to64 [] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + bl = ((len * 4 + 2) / 3) + 1; + b = dmalloc (bl + 1, file, line); + if (!b) + return (char *)0; + + i = 0; + s = b; + while (i != len) { + val = buf [i++]; + extra = val & 3; + val = val >> 2; + *s++ = to64 [val]; + if (i == len) { + *s++ = to64 [extra << 4]; + *s++ = '='; + break; + } + val = (extra << 8) + buf [i++]; + extra = val & 15; + val = val >> 4; + *s++ = to64 [val]; + if (i == len) { + *s++ = to64 [extra << 2]; + *s++ = '='; + break; + } + val = (extra << 8) + buf [i++]; + extra = val & 0x3f; + val = val >> 6; + *s++ = to64 [val]; + *s++ = to64 [extra]; + } + if (!len) + *s++ = '='; + *s++ = 0; + if (s > b + bl + 1) + abort (); + return b; +} + char *print_hw_addr (htype, hlen, data) int htype; int hlen; @@ -56,9 +183,9 @@ char *print_hw_addr (htype, hlen, data) char *s; int i; - if (htype == 0 || hlen == 0) { - strcpy (habuf, "<null>"); - } else { + if (hlen <= 0) + habuf [0] = 0; + else { s = habuf; for (i = 0; i < hlen; i++) { sprintf (s, "%02x", data [i]); @@ -76,70 +203,102 @@ void print_lease (lease) struct tm *t; char tbuf [32]; - debug (" Lease %s", + log_debug (" Lease %s", piaddr (lease -> ip_addr)); t = gmtime (&lease -> starts); strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); - debug (" start %s", tbuf); + log_debug (" start %s", tbuf); t = gmtime (&lease -> ends); strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); - debug (" end %s", tbuf); - - t = gmtime (&lease -> timestamp); - strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t); - debug (" stamp %s", tbuf); + log_debug (" end %s", tbuf); - debug (" hardware addr = %s", - print_hw_addr (lease -> hardware_addr.htype, - lease -> hardware_addr.hlen, - lease -> hardware_addr.haddr)); - debug (" host %s ", + if (lease -> hardware_addr.hlen) + log_debug (" hardware addr = %s", + print_hw_addr (lease -> hardware_addr.hbuf [0], + lease -> hardware_addr.hlen - 1, + &lease -> hardware_addr.hbuf [1])); + log_debug (" host %s ", lease -> host ? lease -> host -> name : "<none>"); } +#if defined (DEBUG) +void dump_packet_option (struct option_cache *oc, + struct packet *packet, + struct lease *lease, + struct client_state *client, + struct option_state *in_options, + struct option_state *cfg_options, + struct binding_scope **scope, + struct universe *u, void *foo) +{ + const char *name, *dot; + struct data_string ds; + memset (&ds, 0, sizeof ds); + + if (u != &dhcp_universe) { + name = u -> name; + dot = "."; + } else { + name = ""; + dot = ""; + } + if (evaluate_option_cache (&ds, packet, lease, client, + in_options, cfg_options, scope, oc, MDL)) { + log_debug (" option %s%s%s %s;\n", + name, dot, oc -> option -> name, + pretty_print_option (oc -> option, + ds.data, ds.len, 1, 1)); + data_string_forget (&ds, MDL); + } +} + void dump_packet (tp) struct packet *tp; { struct dhcp_packet *tdp = tp -> raw; - debug ("packet length %d", tp -> packet_length); - debug ("op = %d htype = %d hlen = %d hops = %d", + log_debug ("packet length %d", tp -> packet_length); + log_debug ("op = %d htype = %d hlen = %d hops = %d", tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops); - debug ("xid = %x secs = %d flags = %x", - tdp -> xid, tdp -> secs, tdp -> flags); - debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr)); - debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr)); - debug ("siaddr = %s", inet_ntoa (tdp -> siaddr)); - debug ("giaddr = %s", inet_ntoa (tdp -> giaddr)); - debug ("chaddr = %02.2x:%02.2x:%02.2x:%02.2x:%02.2x:%02.2x", + log_debug ("xid = %x secs = %ld flags = %x", + tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags); + log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr)); + log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr)); + log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr)); + log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr)); + log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", ((unsigned char *)(tdp -> chaddr)) [0], ((unsigned char *)(tdp -> chaddr)) [1], ((unsigned char *)(tdp -> chaddr)) [2], ((unsigned char *)(tdp -> chaddr)) [3], ((unsigned char *)(tdp -> chaddr)) [4], ((unsigned char *)(tdp -> chaddr)) [5]); - debug ("filename = %s", tdp -> file); - debug ("server_name = %s", tdp -> sname); + log_debug ("filename = %s", tdp -> file); + log_debug ("server_name = %s", tdp -> sname); if (tp -> options_valid) { int i; - for (i = 0; i < 256; i++) { - if (tp -> options [i].data) - debug (" %s = %s", - dhcp_options [i].name, - pretty_print_option - (i, tp -> options [i].data, - tp -> options [i].len, 1, 1)); + for (i = 0; i < tp -> options -> universe_count; i++) { + if (tp -> options -> universes [i]) { + option_space_foreach (tp, (struct lease *)0, + (struct client_state *)0, + (struct option_state *)0, + tp -> options, + &global_scope, + universes [i], 0, + dump_packet_option); + } } } - debug (""); + log_debug ("%s", ""); } +#endif void dump_raw (buf, len) - unsigned char *buf; - int len; + const unsigned char *buf; + unsigned len; { int i; char lbuf [80]; @@ -150,7 +309,7 @@ void dump_raw (buf, len) for (i = 0; i < len; i++) { if ((i & 15) == 0) { if (lbix) - note (lbuf); + log_info (lbuf); sprintf (lbuf, "%03x:", i); lbix = 4; } else if ((i & 7) == 0) @@ -158,7 +317,7 @@ void dump_raw (buf, len) sprintf (&lbuf [lbix], " %02x", buf [i]); lbix += 3; } - note (lbuf); + log_info (lbuf); } void hash_dump (table) @@ -173,12 +332,1042 @@ void hash_dump (table) for (i = 0; i < table -> hash_count; i++) { if (!table -> buckets [i]) continue; - note ("hash bucket %d:", i); + log_info ("hash bucket %d:", i); for (bp = table -> buckets [i]; bp; bp = bp -> next) { if (bp -> len) dump_raw (bp -> name, bp -> len); else - note ((char *)bp -> name); + log_info ((const char *)bp -> name); + } + } +} + +#define HBLEN 60 + +#define DECLARE_HEX_PRINTER(x) \ +char *print_hex##x (len, data, limit) \ + unsigned len; \ + const u_int8_t *data; \ + unsigned limit; \ +{ \ + \ + static char hex_buf##x [HBLEN + 1]; \ + unsigned i; \ + \ + if (limit > HBLEN) \ + limit = HBLEN; \ + \ + for (i = 0; i < (limit - 2) && i < len; i++) { \ + if (!isascii (data [i]) || !isprint (data [i])) { \ + for (i = 0; i < limit / 3 && i < len; i++) { \ + sprintf (&hex_buf##x [i * 3], \ + "%02x:", data [i]); \ + } \ + hex_buf##x [i * 3 - 1] = 0; \ + return hex_buf##x; \ + } \ + } \ + hex_buf##x [0] = '"'; \ + i = len; \ + if (i > limit - 2) \ + i = limit - 2; \ + memcpy (&hex_buf##x [1], data, i); \ + hex_buf##x [i + 1] = '"'; \ + hex_buf##x [i + 2] = 0; \ + return hex_buf##x; \ +} + +DECLARE_HEX_PRINTER (_1) +DECLARE_HEX_PRINTER (_2) +DECLARE_HEX_PRINTER (_3) + +#define DQLEN 80 + +char *print_dotted_quads (len, data) + unsigned len; + const u_int8_t *data; +{ + static char dq_buf [DQLEN + 1]; + int i; + char *s, *last; + + s = &dq_buf [0]; + last = s; + + i = 0; + + do { + sprintf (s, "%d.%d.%d.%d, ", + data [i], data [i + 1], data [i + 2], data [i + 3]); + s += strlen (s); + i += 4; + } while ((s - &dq_buf [0] > DQLEN - 21) && + i + 3 < len); + if (i == len) + s [-2] = 0; + else + strcpy (s, "..."); + return dq_buf; +} + +char *print_dec_1 (val) + unsigned long val; +{ + static char vbuf [32]; + sprintf (vbuf, "%lu", val); + return vbuf; +} + +char *print_dec_2 (val) + unsigned long val; +{ + static char vbuf [32]; + sprintf (vbuf, "%lu", val); + return vbuf; +} + +static unsigned print_subexpression PROTO ((struct expression *, + char *, unsigned)); + +static unsigned print_subexpression (expr, buf, len) + struct expression *expr; + char *buf; + unsigned len; +{ + unsigned rv, left; + const char *s; + + switch (expr -> op) { + case expr_none: + if (len > 3) { + strcpy (buf, "nil"); + return 3; + } + break; + + case expr_match: + if (len > 7) { + strcpy (buf, "(match)"); + return 7; + } + break; + + case expr_check: + rv = 10 + strlen (expr -> data.check -> name); + if (len > rv) { + sprintf (buf, "(check %s)", + expr -> data.check -> name); + return rv; + } + break; + + case expr_equal: + if (len > 6) { + rv = 4; + strcpy (buf, "(eq "); + rv += print_subexpression (expr -> data.equal [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.equal [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_not_equal: + if (len > 7) { + rv = 5; + strcpy (buf, "(neq "); + rv += print_subexpression (expr -> data.equal [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.equal [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_substring: + if (len > 11) { + rv = 8; + strcpy (buf, "(substr "); + rv += print_subexpression (expr -> data.substring.expr, + buf + rv, len - rv - 3); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.substring.offset, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.substring.len, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_suffix: + if (len > 10) { + rv = 8; + strcpy (buf, "(suffix "); + rv += print_subexpression (expr -> data.suffix.expr, + buf + rv, len - rv - 2); + if (len > rv) + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.suffix.len, + buf + rv, len - rv - 1); + if (len > rv) + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_concat: + if (len > 10) { + rv = 8; + strcpy (buf, "(concat "); + rv += print_subexpression (expr -> data.concat [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.concat [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_pick_first_value: + if (len > 8) { + rv = 6; + strcpy (buf, "(pick1st "); + rv += print_subexpression + (expr -> data.pick_first_value.car, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.pick_first_value.cdr, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_host_lookup: + rv = 15 + strlen (expr -> data.host_lookup -> hostname); + if (len > rv) { + sprintf (buf, "(dns-lookup %s)", + expr -> data.host_lookup -> hostname); + return rv; + } + break; + + case expr_and: + s = "and"; + binop: + rv = strlen (s); + if (len > rv + 4) { + buf [0] = '('; + strcpy (&buf [1], s); + rv += 1; + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.and [0], + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.and [1], + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_or: + s = "or"; + goto binop; + + case expr_add: + s = "+"; + goto binop; + + case expr_subtract: + s = "-"; + goto binop; + + case expr_multiply: + s = "*"; + goto binop; + + case expr_divide: + s = "/"; + goto binop; + + case expr_remainder: + s = "%"; + goto binop; + + case expr_binary_and: + s = "&"; + goto binop; + + case expr_binary_or: + s = "|"; + goto binop; + + case expr_binary_xor: + s = "^"; + goto binop; + + case expr_not: + if (len > 6) { + rv = 5; + strcpy (buf, "(not "); + rv += print_subexpression (expr -> data.not, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_config_option: + s = "cfg-option"; + goto dooption; + + case expr_option: + s = "option"; + dooption: + rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) + + strlen (expr -> data.option -> universe -> name)); + if (len > rv) { + sprintf (buf, "(option %s.%s)", + expr -> data.option -> universe -> name, + expr -> data.option -> name); + return rv; + } + break; + + case expr_hardware: + if (len > 10) { + strcpy (buf, "(hardware)"); + return 10; + } + break; + + case expr_packet: + if (len > 10) { + rv = 8; + strcpy (buf, "(substr "); + rv += print_subexpression (expr -> data.packet.offset, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.packet.len, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_const_data: + s = print_hex_1 (expr -> data.const_data.len, + expr -> data.const_data.data, len); + rv = strlen (s); + if (rv >= len) + rv = len - 1; + strncpy (buf, s, rv); + buf [rv] = 0; + return rv; + + case expr_encapsulate: + rv = 13; + strcpy (buf, "(encapsulate "); + rv += expr -> data.encapsulate.len; + if (rv + 2 > len) + rv = len - 2; + strncpy (buf, + (const char *)expr -> data.encapsulate.data, rv - 13); + buf [rv++] = ')'; + buf [rv++] = 0; + break; + + case expr_extract_int8: + if (len > 7) { + rv = 6; + strcpy (buf, "(int8 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_extract_int16: + if (len > 8) { + rv = 7; + strcpy (buf, "(int16 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_extract_int32: + if (len > 8) { + rv = 7; + strcpy (buf, "(int32 "); + rv += print_subexpression (expr -> data.extract_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int8: + if (len > 7) { + rv = 6; + strcpy (buf, "(to-int8 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int16: + if (len > 8) { + rv = 7; + strcpy (buf, "(to-int16 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_encode_int32: + if (len > 8) { + rv = 7; + strcpy (buf, "(to-int32 "); + rv += print_subexpression (expr -> data.encode_int, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; } + break; + + case expr_const_int: + s = print_dec_1 (expr -> data.const_int); + rv = strlen (s); + if (len > rv) { + strcpy (buf, s); + return rv; + } + break; + + case expr_exists: + rv = 10 + (strlen (expr -> data.option -> name) + + strlen (expr -> data.option -> universe -> name)); + if (len > rv) { + sprintf (buf, "(exists %s.%s)", + expr -> data.option -> universe -> name, + expr -> data.option -> name); + return rv; + } + break; + + case expr_variable_exists: + rv = 10 + strlen (expr -> data.variable); + if (len > rv) { + sprintf (buf, "(defined %s)", expr -> data.variable); + return rv; + } + break; + + case expr_variable_reference: + rv = strlen (expr -> data.variable); + if (len > rv) { + sprintf (buf, "%s", expr -> data.variable); + return rv; + } + break; + + case expr_known: + s = "known"; + astring: + rv = strlen (s); + if (len > rv) { + strcpy (buf, s); + return rv; + } + break; + + case expr_leased_address: + s = "leased-address"; + goto astring; + + case expr_client_state: + s = "client-state"; + goto astring; + + case expr_host_decl_name: + s = "host-decl-name"; + goto astring; + + case expr_lease_time: + s = "lease-time"; + goto astring; + + case expr_static: + s = "static"; + goto astring; + + case expr_filename: + s = "filename"; + goto astring; + + case expr_sname: + s = "server-name"; + goto astring; + + case expr_reverse: + if (len > 11) { + rv = 13; + strcpy (buf, "(reverse "); + rv += print_subexpression (expr -> data.reverse.width, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.reverse.buffer, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_binary_to_ascii: + if (len > 5) { + rv = 9; + strcpy (buf, "(b2a "); + rv += print_subexpression (expr -> data.b2a.base, + buf + rv, len - rv - 4); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.width, + buf + rv, len - rv - 3); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.seperator, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.b2a.buffer, + buf + rv, len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_dns_transaction: + rv = 10; + if (len < rv + 2) { + buf [0] = '('; + strcpy (&buf [1], "ns-update "); + while (len < rv + 2) { + rv += print_subexpression + (expr -> data.dns_transaction.car, + buf + rv, len - rv - 2); + buf [rv++] = ' '; + expr = expr -> data.dns_transaction.cdr; + } + buf [rv - 1] = ')'; + buf [rv] = 0; + return rv; + } + return 0; + + case expr_ns_delete: + s = "delete"; + left = 4; + goto dodnsupd; + case expr_ns_exists: + s = "exists"; + left = 4; + goto dodnsupd; + case expr_ns_not_exists: + s = "not_exists"; + left = 4; + goto dodnsupd; + case expr_ns_add: + s = "update"; + left = 5; + dodnsupd: + rv = strlen (s); + if (len > strlen (s) + 1) { + buf [0] = '('; + strcpy (buf + 1, s); + rv++; + buf [rv++] = ' '; + s = print_dec_1 (expr -> data.ns_add.rrclass); + if (len > rv + strlen (s) + left) { + strcpy (&buf [rv], s); + rv += strlen (&buf [rv]); + } + buf [rv++] = ' '; + left--; + s = print_dec_1 (expr -> data.ns_add.rrtype); + if (len > rv + strlen (s) + left) { + strcpy (&buf [rv], s); + rv += strlen (&buf [rv]); + } + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.rrname, + buf + rv, len - rv - left); + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.rrdata, + buf + rv, len - rv - left); + buf [rv++] = ' '; + left--; + rv += print_subexpression + (expr -> data.ns_add.ttl, + buf + rv, len - rv - left); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_null: + if (len > 6) { + strcpy (buf, "(null)"); + return 6; + } + break; + case expr_funcall: + rv = 12 + strlen (expr -> data.funcall.name); + if (len > rv + 1) { + strcpy (buf, "(funcall "); + strcpy (buf + 9, expr -> data.funcall.name); + buf [rv++] = ' '; + rv += print_subexpression + (expr -> data.funcall.arglist, buf + rv, + len - rv - 1); + buf [rv++] = ')'; + buf [rv] = 0; + return rv; + } + break; + + case expr_arg: + rv = print_subexpression (expr -> data.arg.val, buf, len); + if (expr -> data.arg.next && rv + 2 < len) { + buf [rv++] = ' '; + rv += print_subexpression (expr -> data.arg.next, + buf, len); + if (rv + 1 < len) + buf [rv++] = 0; + return rv; + } + break; + case expr_function: + rv = 9; + if (len > rv + 1) { + struct string_list *foo; + strcpy (buf, "(function"); + for (foo = expr -> data.func -> args; + foo; foo = foo -> next) { + if (len > rv + 2 + strlen (foo -> string)) { + buf [rv - 1] = ' '; + strcpy (&buf [rv], foo -> string); + rv += strlen (foo -> string); + } + } + buf [rv] = ')'; + buf [rv++] = 0; + return rv; + } + } + return 0; +} + +void print_expression (name, expr) + const char *name; + struct expression *expr; +{ + char buf [1024]; + + print_subexpression (expr, buf, sizeof buf); + log_info ("%s: %s", name, buf); +} + +int token_print_indent_concat (FILE *file, int col, int indent, + const char *prefix, + const char *suffix, ...) +{ + va_list list; + char *buf; + unsigned len; + char *s, *t, *u; + + va_start (list, suffix); + s = va_arg (list, char *); + len = 0; + while (s) { + len += strlen (s); + s = va_arg (list, char *); + } + va_end (list); + + t = dmalloc (len + 1, MDL); + if (!t) + log_fatal ("token_print_indent: no memory for copy buffer"); + + va_start (list, suffix); + s = va_arg (list, char *); + u = t; + while (s) { + len = strlen (s); + strcpy (u, s); + u += len; + } + va_end (list); + + len = token_print_indent (file, col, indent, + prefix, suffix, t); + dfree (t, MDL); + return col; +} + +int token_indent_data_string (FILE *file, int col, int indent, + const char *prefix, const char *suffix, + struct data_string *data) +{ + int i; + char *buf; + char obuf [3]; + + /* See if this is just ASCII. */ + for (i = 0; i < data -> len; i++) + if (!isascii (data -> data [i]) || + !isprint (data -> data [i])) + break; + + /* If we have a purely ASCII string, output it as text. */ + if (i == data -> len) { + char *buf = dmalloc (data -> len + 3, MDL); + if (buf) { + buf [0] = '"'; + memcpy (buf + 1, data -> data, data -> len); + buf [data -> len + 1] = '"'; + buf [data -> len + 2] = 0; + i = token_print_indent (file, col, indent, + prefix, suffix, buf); + dfree (buf, MDL); + return i; + } + } + + for (i = 0; i < data -> len; i++) { + sprintf (obuf, "%2.2x", data -> data [i]); + col = token_print_indent (file, col, indent, + i == 0 ? prefix : "", + (i + 1 == data -> len + ? suffix + : ""), obuf); + if (i + 1 != data -> len) + col = token_print_indent (file, col, indent, + prefix, suffix, ":"); + } + return col; +} + +int token_print_indent (FILE *file, int col, int indent, + const char *prefix, + const char *suffix, const char *buf) +{ + int len = strlen (buf) + strlen (prefix); + if (col + len > 79) { + if (indent + len < 79) { + indent_spaces (file, indent); + col = indent; + } else { + indent_spaces (file, col); + col = len > 79 ? 0 : 79 - len - 1; + } + } else if (prefix && *prefix) { + fputs (prefix, file); + col += strlen (prefix); + } + fputs (buf, file); + col += len; + if (suffix && *suffix) { + if (col + strlen (suffix) > 79) { + indent_spaces (file, indent); + col = indent; + } else { + fputs (suffix, file); + col += strlen (suffix); + } + } + return col; +} + +void indent_spaces (FILE *file, int indent) +{ + int i; + fputc ('\n', file); + for (i = 0; i < indent; i++) + fputc (' ', file); +} + +#if defined (NSUPDATE) +void print_dns_status (int status, ns_updque *uq) +{ + char obuf [1024]; + char *s = &obuf [0], *end = &obuf [1022]; + ns_updrec *u; + int position; + int ttlp; + const char *predicate = "if", *en, *op; + int errorp; + + for (u = ISC_LIST_HEAD (*uq); u; u = ISC_LIST_NEXT (u, r_link)) { + ttlp = 0; + + switch (u -> r_opcode) + { + case NXRRSET: + op = "rrset doesn't exist"; + position = 1; + break; + case YXRRSET: + op = "rrset exists"; + position = 1; + break; + case NXDOMAIN: + op = "domain doesn't exist"; + position = 1; + break; + case YXDOMAIN: + op = "domain exists"; + position = 1; + break; + case ADD: + op = "add"; + position = 0; + ttlp = 1; + break; + case DELETE: + op = "delete"; + position = 0; + break; + default: + op = "unknown"; + position = 0; + break; + } + if (!position) { + if (s != &obuf [0] && s + 1 < end) + *s++ = ' '; + if (s + strlen (op) < end) { + strcpy (s, op); + s += strlen (s); + } + } else { + if (s != &obuf [0] && s + 1 < end) + *s++ = ' '; + if (s + strlen (predicate) < end) { + strcpy (s, predicate); + s += strlen (s); + } + predicate = "and"; + } + if (ttlp) { + if (s + 1 < end) + *s++ = ' '; + /* 27 is as big as a ttl can get. */ + if (s + 27 < end) { + sprintf (s, "%lu", + (unsigned long)(u -> r_ttl)); + s += strlen (s); + } + } + switch (u -> r_class) { + case C_IN: + en = "IN"; + break; + case C_CHAOS: + en = "CHAOS"; + break; + case C_HS: + en = "HS"; + break; + default: + en = "UNKNOWN"; + break; + } + if (s + strlen (en) < end) { + if (s + 1 < end) + *s++ = ' '; + strcpy (s, en); + s += strlen (en); + } + switch (u -> r_type) { + case T_A: + en = "A"; + break; + case T_PTR: + en = "PTR"; + break; + case T_MX: + en = "MX"; + break; + case T_TXT: + en = "TXT"; + break; + case T_CNAME: + en = "CNAME"; + break; + default: + en = "UNKNOWN"; + break; + } + if (s + strlen (en) < end) { + if (s + 1 < end) + *s++ = ' '; + strcpy (s, en); + s += strlen (en); + } + if (u -> r_dname) { + if (s + 1 < end) + *s++ = ' '; + if (s + strlen (u -> r_dname) < end) { + strcpy (s, u -> r_dname); + s += strlen (s); + } + } + if (u -> r_data) { + if (s + 1 < end) + *s++ = ' '; + if (u -> r_type == T_TXT) { + if (s + 1 < end) + *s++ = '"'; + } + if (s + u -> r_size < end) { + memcpy (s, u -> r_data, u -> r_size); + s += u -> r_size; + if (u -> r_type == T_TXT) { + if (s + 1 < end) + *s++ = '"'; + } + } + } + if (position) { + if (s + 1 < end) + *s++ = ' '; + if (s + strlen (op) < end) { + strcpy (s, op); + s += strlen (s); + } + } + if (u == ISC_LIST_TAIL (*uq)) + break; + } + if (s == &obuf [0]) { + strcpy (s, "empty update"); + s += strlen (s); + } + if (status == NOERROR) + errorp = 0; + else + errorp = 1; + en = isc_result_totext (status); +#if 0 + switch (status) { + case -1: + en = "resolver failed"; + break; + + case FORMERR: + en = "format error"; + break; + + case NOERROR: + en = "succeeded"; + errorp = 0; + break; + + case NOTAUTH: + en = "not authorized"; + break; + + case NOTIMP: + en = "not implemented"; + break; + + case NOTZONE: + en = "not a single valid zone"; + break; + + case NXDOMAIN: + en = "no such domain"; + break; + + case NXRRSET: + en = "no such record"; + break; + + case REFUSED: + en = "refused"; + break; + + case SERVFAIL: + en = "server failed"; + break; + + case YXDOMAIN: + en = "domain exists"; + break; + + case YXRRSET: + en = "record exists"; + break; + + default: + en = "unknown error"; + break; + } +#endif + + if (s + 2 < end) { + *s++ = ':'; + *s++ = ' '; + } + if (s + strlen (en) < end) { + strcpy (s, en); + s += strlen (en); } + if (s + 1 < end) + *s++ = '.'; + *s++ = 0; + if (errorp) + log_error (obuf); + else + log_info (obuf); } +#endif /* NSUPDATE */ diff --git a/contrib/isc-dhcp/common/raw.c b/contrib/isc-dhcp/common/raw.c index 0ef9289..c56b681 100644 --- a/contrib/isc-dhcp/common/raw.c +++ b/contrib/isc-dhcp/common/raw.c @@ -16,7 +16,7 @@ Sigh. */ /* - * Copyright (c) 1995, 1996, 1997, 1999 The Internet Software Consortium. + * Copyright (c) 1995-2000 Internet Software Consortium. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,14 +47,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: raw.c,v 1.11.2.4 1999/07/20 20:03:10 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1999 The Internet Software Consortium. All rights reserved.\n"; +"$Id: raw.c,v 1.17 2000/03/17 03:59:01 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -79,39 +81,46 @@ void if_register_send (info) /* List addresses on which we're listening. */ if (!quiet_interface_discovery) - note ("Sending on Raw Socket/%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 ("Sending on %s, port %d", + piaddr (info -> address), htons (local_port)); if ((sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) - error ("Can't create dhcp socket: %m"); + log_fatal ("Can't create dhcp socket: %m"); /* Set the BROADCAST option so that we can broadcast DHCP responses. */ flag = 1; if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof flag) < 0) - error ("Can't set SO_BROADCAST option on dhcp socket: %m"); + log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m"); /* Set the IP_HDRINCL flag so that we can supply our own IP headers... */ if (setsockopt (sock, IPPROTO_IP, IP_HDRINCL, &flag, sizeof flag) < 0) - error ("Can't set IP_HDRINCL flag: %m"); + log_fatal ("Can't set IP_HDRINCL flag: %m"); info -> wfdesc = sock; if (!quiet_interface_discovery) - note ("Sending on Raw/%s%s%s", + log_info ("Sending on Raw/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_send (info) + struct interface_info *info; +{ + close (info -> wfdesc); + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on Raw/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } -ssize_t send_packet (interface, packet, raw, len, from, to, hto) +size_t send_packet (interface, packet, raw, len, from, to, hto) struct interface_info *interface; struct packet *packet; struct dhcp_packet *raw; @@ -120,7 +129,7 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto) struct sockaddr_in *to; struct hardware *hto; { - unsigned char buf [1500]; + unsigned char buf [256]; int bufp = 0; struct iovec iov [2]; int result; @@ -129,37 +138,16 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto) assemble_udp_ip_header (interface, buf, &bufp, from.s_addr, to -> sin_addr.s_addr, to -> sin_port, (unsigned char *)raw, len); - if (len + bufp > sizeof buf) { - warn ("send_packet: packet too large (%s)", len + bufp); - return; - } - memcpy (buf + bufp, raw, len); - bufp += len; - result = sendto (interface -> wfdesc, (char *)buf, bufp, 0, - (struct sockaddr *)to, sizeof *to); - if (result < 0) - warn ("send_packet: %m"); - return result; -} - -int can_unicast_without_arp () -{ - return 1; -} -int can_receive_unicast_unconfigured (ip) - struct interface_info *ip; -{ - return 1; -} + /* 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; -void maybe_setup_fallback () -{ -} - -void if_reinitialize_send (info) - struct interface_info *info; -{ + result = writev(interface -> wfdesc, iov, 2); + if (result < 0) + log_error ("send_packet: %m"); + return result; } - -#endif /* USE_RAW_SEND */ +#endif /* USE_SOCKET_SEND */ diff --git a/contrib/isc-dhcp/common/resolv.c b/contrib/isc-dhcp/common/resolv.c new file mode 100644 index 0000000..396f9ef --- /dev/null +++ b/contrib/isc-dhcp/common/resolv.c @@ -0,0 +1,212 @@ +/* resolv.c + + Parser for /etc/resolv.conf file. */ + +/* + * 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 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: resolv.c,v 1.16 2001/05/02 06:39:43 mellon Exp $ Copyright (c) 1996-2001 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + +#include "dhcpd.h" + +struct name_server *name_servers; +struct domain_search_list *domains; +char path_resolv_conf [] = _PATH_RESOLV_CONF; + +void read_resolv_conf (parse_time) + TIME parse_time; +{ + int file; + struct parse *cfile; + const char *val; + int token; + int declaration = 0; + struct name_server *sp, *sl, *ns; + struct domain_search_list *dp, *dl, *nd; + struct iaddr *iaddr; + + if ((file = open (path_resolv_conf, O_RDONLY)) < 0) { + log_error ("Can't open %s: %m", path_resolv_conf); + return; + } + + cfile = (struct parse *)0; + new_parse (&cfile, file, (char *)0, 0, path_resolv_conf, 1); + + do { + token = next_token (&val, (unsigned *)0, cfile); + if (token == END_OF_FILE) + break; + else if (token == EOL) + continue; + else if (token == DOMAIN || token == SEARCH) { + do { + struct domain_search_list *nd, **dp; + char *dn; + + dn = parse_host_name (cfile); + if (!dn) + break; + + dp = &domains; + for (nd = domains; nd; nd = nd -> next) { + dp = &nd -> next; + if (!strcmp (nd -> domain, dn)) + break; + } + if (!nd) { + nd = new_domain_search_list (MDL); + if (!nd) + log_fatal ("No memory for %s", + dn); + nd -> next = + (struct domain_search_list *)0; + *dp = nd; + nd -> domain = dn; + dn = (char *)0; + } + nd -> rcdate = parse_time; + token = peek_token (&val, + (unsigned *)0, cfile); + } while (token != EOL); + if (token != EOL) { + parse_warn (cfile, + "junk after domain declaration"); + skip_to_semi (cfile); + } + token = next_token (&val, (unsigned *)0, cfile); + } else if (token == NAMESERVER) { + struct name_server *ns, **sp; + struct iaddr iaddr; + + parse_ip_addr (cfile, &iaddr); + + sp = &name_servers; + for (ns = name_servers; ns; ns = ns -> next) { + sp = &ns -> next; + if (!memcmp (&ns -> addr.sin_addr, + iaddr.iabuf, iaddr.len)) + break; + } + if (!ns) { + ns = new_name_server (MDL); + if (!ns) + log_fatal ("No memory for nameserver %s", + piaddr (iaddr)); + ns -> next = (struct name_server *)0; + *sp = ns; + memcpy (&ns -> addr.sin_addr, + iaddr.iabuf, iaddr.len); +#ifdef HAVE_SA_LEN + ns -> addr.sin_len = sizeof ns -> addr; +#endif + ns -> addr.sin_family = AF_INET; + ns -> addr.sin_port = htons (53); + memset (ns -> addr.sin_zero, 0, + sizeof ns -> addr.sin_zero); + } + ns -> rcdate = parse_time; + skip_to_semi (cfile); + } else + skip_to_semi (cfile); /* Ignore what we don't grok. */ + } while (1); + token = next_token (&val, (unsigned *)0, cfile); + + /* Lose servers that are no longer in /etc/resolv.conf. */ + sl = (struct name_server *)0; + for (sp = name_servers; sp; sp = ns) { + ns = sp -> next; + if (sp -> rcdate != parse_time) { + if (sl) + sl -> next = sp -> next; + else + name_servers = sp -> next; + /* We can't actually free the name server structure, + because somebody might be hanging on to it. If + your /etc/resolv.conf file changes a lot, this + could be a noticable memory leak. */ + } else + sl = sp; + } + + /* Lose domains that are no longer in /etc/resolv.conf. */ + dl = (struct domain_search_list *)0; + for (dp = domains; dp; dp = nd) { + nd = dp -> next; + if (dp -> rcdate != parse_time) { + if (dl) + dl -> next = dp -> next; + else + domains = dp -> next; + free_domain_search_list (dp, MDL); + } else + dl = dp; + } + close (file); + end_parse (&cfile); +} + +/* Pick a name server from the /etc/resolv.conf file. */ + +struct name_server *first_name_server () +{ + FILE *rc; + static TIME rcdate; + struct stat st; + + /* Check /etc/resolv.conf and reload it if it's changed. */ + if (cur_time > rcdate) { + if (stat (path_resolv_conf, &st) < 0) { + log_error ("Can't stat %s", path_resolv_conf); + return (struct name_server *)0; + } + if (st.st_mtime > rcdate) { + char rcbuf [512]; + char *s, *t, *u; + rcdate = cur_time + 1; + + read_resolv_conf (rcdate); + } + } + + return name_servers; +} diff --git a/contrib/isc-dhcp/common/socket.c b/contrib/isc-dhcp/common/socket.c index 0a6b0b8..b4ecd14 100644 --- a/contrib/isc-dhcp/common/socket.c +++ b/contrib/isc-dhcp/common/socket.c @@ -3,8 +3,8 @@ BSD socket interface code... */ /* - * Copyright (c) 1995, 1996, 1997, 1998, 1999 - * The Internet Software Consortium. All rights reserved. + * Copyright (c) 1995-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 @@ -34,10 +34,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.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''. */ /* SO_BINDTODEVICE support added by Elliot Poger (poger@leland.stanford.edu). @@ -50,7 +51,7 @@ #ifndef lint static char copyright[] = -"$Id: socket.c,v 1.26.2.12 1999/10/25 15:39:55 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; +"$Id: socket.c,v 1.55.2.1 2002/01/17 19:42:55 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -108,7 +109,7 @@ int if_register_socket (info) #if !defined (HAVE_SO_BINDTODEVICE) && !defined (USE_FALLBACK) /* Make sure only one interface is registered. */ if (once) - error ("The standard socket API can only support %s", + log_fatal ("The standard socket API can only support %s", "hosts with a single network interface."); once = 1; #endif @@ -116,35 +117,44 @@ int if_register_socket (info) /* Set up the address we're going to bind to. */ name.sin_family = AF_INET; name.sin_port = local_port; - name.sin_addr.s_addr = INADDR_ANY; + name.sin_addr = local_address; memset (name.sin_zero, 0, sizeof (name.sin_zero)); /* Make a socket... */ if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) - error ("Can't create dhcp socket: %m"); + log_fatal ("Can't create dhcp socket: %m"); /* Set the REUSEADDR option so that we don't fail to start if we're being restarted. */ flag = 1; if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof flag) < 0) - error ("Can't set SO_REUSEADDR option on dhcp socket: %m"); + log_fatal ("Can't set SO_REUSEADDR option on dhcp socket: %m"); - /* Set the BROADCAST option so that we can broadcast DHCP responses. */ - if (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, - (char *)&flag, sizeof flag) < 0) - error ("Can't set SO_BROADCAST option on dhcp socket: %m"); + /* Set the BROADCAST option so that we can broadcast DHCP responses. + We shouldn't do this for fallback devices, and we can detect that + a device is a fallback because it has no ifp structure. */ + if (info -> ifp && + (setsockopt (sock, SOL_SOCKET, SO_BROADCAST, + (char *)&flag, sizeof flag) < 0)) + log_fatal ("Can't set SO_BROADCAST option on dhcp socket: %m"); /* Bind the socket to this interface's IP address. */ - if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) - error ("Can't bind to dhcp address: %m"); + if (bind (sock, (struct sockaddr *)&name, sizeof name) < 0) { + log_error ("Can't bind to dhcp address: %m"); + log_error ("Please make sure there is no other dhcp server"); + log_error ("running and that there's no entry for dhcp or"); + log_error ("bootp in /etc/inetd.conf. Also make sure you"); + log_error ("are not running HP JetAdmin software, which"); + log_fatal ("includes a bootp server."); + } #if defined (HAVE_SO_BINDTODEVICE) /* Bind this socket to this interface. */ if (info -> ifp && setsockopt (sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)(info -> ifp), sizeof *(info -> ifp)) < 0) { - error("setsockopt: SO_BINDTODEVICE: %m"); + log_fatal ("setsockopt: SO_BINDTODEVICE: %m"); } #endif @@ -158,16 +168,39 @@ void if_register_send (info) { #ifndef USE_SOCKET_RECEIVE info -> wfdesc = if_register_socket (info); +#if defined (USE_SOCKET_FALLBACK) + /* Fallback only registers for send, but may need to receive as + well. */ + info -> rfdesc = info -> wfdesc; +#endif #else info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) - note ("Sending on Socket/%s%s%s", + log_info ("Sending on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +#if defined (USE_SOCKET_SEND) +void if_deregister_send (info) + struct interface_info *info; +{ +#ifndef USE_SOCKET_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling output on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); } +#endif /* USE_SOCKET_SEND */ #endif /* USE_SOCKET_SEND || USE_SOCKET_FALLBACK */ #ifdef USE_SOCKET_RECEIVE @@ -178,7 +211,21 @@ void if_register_receive (info) we don't need to register this interface twice. */ info -> rfdesc = if_register_socket (info); if (!quiet_interface_discovery) - note ("Listening on Socket/%s%s%s", + log_info ("Listening on Socket/%s%s%s", + info -> name, + (info -> shared_network ? "/" : ""), + (info -> shared_network ? + info -> shared_network -> name : "")); +} + +void if_deregister_receive (info) + struct interface_info *info; +{ + close (info -> rfdesc); + info -> rfdesc = -1; + + if (!quiet_interface_discovery) + log_info ("Disabling input on Socket/%s%s%s", info -> name, (info -> shared_network ? "/" : ""), (info -> shared_network ? @@ -211,10 +258,10 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto) retry++ < 10); #endif if (result < 0) { - warn ("send_packet: %m"); + log_error ("send_packet: %m"); if (errno == ENETUNREACH) - warn ("send_packet: please consult README file %s", - "regarding broadcast address."); + log_error ("send_packet: please consult README file%s", + " regarding broadcast address."); } return result; } @@ -228,7 +275,7 @@ ssize_t receive_packet (interface, buf, len, from, hfrom) struct sockaddr_in *from; struct hardware *hfrom; { - int flen = sizeof *from; + SOCKLEN_T flen = sizeof *from; int result; #ifdef IGNORE_HOSTUNREACH @@ -250,24 +297,35 @@ ssize_t receive_packet (interface, buf, len, from, hfrom) #if defined (USE_SOCKET_FALLBACK) /* This just reads in a packet and silently discards it. */ -void fallback_discard (protocol) - struct protocol *protocol; +isc_result_t fallback_discard (object) + omapi_object_t *object; { char buf [1540]; struct sockaddr_in from; - int flen = sizeof from; + SOCKLEN_T flen = sizeof from; int status; - struct interface_info *interface = protocol -> local; + struct interface_info *interface; + + if (object -> type != dhcp_type_interface) + return ISC_R_INVALIDARG; + interface = (struct interface_info *)object; status = recvfrom (interface -> wfdesc, buf, sizeof buf, 0, (struct sockaddr *)&from, &flen); - if (status < 0) - warn ("fallback_discard: %m"); +#if defined (DEBUG) + /* Only report fallback discard errors if we're debugging. */ + if (status < 0) { + log_error ("fallback_discard: %m"); + return ISC_R_UNEXPECTED; + } +#endif + return ISC_R_SUCCESS; } #endif /* USE_SOCKET_FALLBACK */ #if defined (USE_SOCKET_SEND) -int can_unicast_without_arp () +int can_unicast_without_arp (ip) + struct interface_info *ip; { return 0; } @@ -282,18 +340,40 @@ int can_receive_unicast_unconfigured (ip) #endif } +int supports_multiple_interfaces (ip) + struct interface_info *ip; +{ +#if defined (SO_BINDTODEVICE) + return 1; +#else + return 0; +#endif +} + /* If we have SO_BINDTODEVICE, set up a fallback interface; otherwise, do not. */ void maybe_setup_fallback () { #if defined (USE_SOCKET_FALLBACK) - 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)) { fbi -> wfdesc = if_register_socket (fbi); - add_protocol ("fallback", - fbi -> wfdesc, fallback_discard, fbi); + fbi -> rfdesc = fbi -> wfdesc; + log_info ("Sending on Socket/%s%s%s", + fbi -> name, + (fbi -> shared_network ? "/" : ""), + (fbi -> shared_network ? + fbi -> shared_network -> name : "")); + + status = omapi_register_io_object ((omapi_object_t *)fbi, + if_readsocket, 0, + fallback_discard, 0, 0); + if (status != ISC_R_SUCCESS) + log_fatal ("Can't register I/O handle for %s: %s", + fbi -> name, isc_result_totext (status)); + interface_dereference (&fbi, MDL); } #endif } diff --git a/contrib/isc-dhcp/common/tables.c b/contrib/isc-dhcp/common/tables.c index 9274e38..d3e2379 100644 --- a/contrib/isc-dhcp/common/tables.c +++ b/contrib/isc-dhcp/common/tables.c @@ -3,7 +3,7 @@ Tables of information... */ /* - * Copyright (c) 1995, 1996 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,24 +34,31 @@ * 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: tables.c,v 1.13.2.4 1999/04/24 16:46:44 mellon Exp $ Copyright (c) 1995, 1996 The Internet Software Consortium. All rights reserved.\n"; +"$Id: tables.c,v 1.51.2.4 2001/10/17 03:26:26 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" +/* XXXDPN: Moved here from hash.c, when it moved to libomapi. Not sure + where these really belong. */ +HASH_FUNCTIONS (group, const char *, struct group_object, group_hash_t, + group_reference, group_dereference) +HASH_FUNCTIONS (universe, const char *, struct universe, universe_hash_t, 0, 0) +HASH_FUNCTIONS (option, const char *, struct option, option_hash_t, 0, 0) + /* DHCP Option names, formats and codes, from RFC1533. Format codes: - e - end of data I - IP address l - 32-bit signed integer L - 32-bit unsigned integer @@ -62,6 +69,32 @@ static char copyright[] = t - ASCII text f - flag (true or false) A - array of whatever precedes (e.g., IA means array of IP addresses) + a - array of the preceding character (e.g., IIa means two or more IP + addresses) + U - name of an option space (universe) + F - implicit flag - the presence of the option indicates that the + flag is true. + o - the preceding value is optional. + E - encapsulation, string or colon-seperated hex list (the latter + two for parsing). E is followed by a text string containing + the name of the option space to encapsulate, followed by a '.'. + If the E is immediately followed by '.', the applicable vendor + option space is used if one is defined. + e - If an encapsulation directive is not the first thing in the string, + the option scanner requires an efficient way to find the encapsulation. + This is done by placing a 'e' at the beginning of the option. The + 'e' has no other purpose, and is not required if 'E' is the first + thing in the option. + X - either an ASCII string or binary data. On output, the string is + scanned to see if it's printable ASCII and, if so, output as a + quoted string. If not, it's output as colon-seperated hex. On + input, the option can be specified either as a quoted string or as + a colon-seperated hex list. + N - enumeration. N is followed by a text string containing + the name of the set of enumeration values to parse or emit, + followed by a '.'. The width of the data is specified in the + named enumeration. Named enumerations are tracked in parse.c. + d - Domain name (i.e., FOO or FOO.BAR). */ struct universe dhcp_universe; @@ -109,7 +142,7 @@ struct option dhcp_options [256] = { { "nis-domain", "t", &dhcp_universe, 40 }, { "nis-servers", "IA", &dhcp_universe, 41 }, { "ntp-servers", "IA", &dhcp_universe, 42 }, - { "vendor-encapsulated-options", "X", &dhcp_universe, 43 }, + { "vendor-encapsulated-options", "E.", &dhcp_universe, 43 }, { "netbios-name-servers", "IA", &dhcp_universe, 44 }, { "netbios-dd-server", "IA", &dhcp_universe, 45 }, { "netbios-node-type", "B", &dhcp_universe, 46 }, @@ -126,10 +159,10 @@ struct option dhcp_options [256] = { { "dhcp-max-message-size", "S", &dhcp_universe, 57 }, { "dhcp-renewal-time", "L", &dhcp_universe, 58 }, { "dhcp-rebinding-time", "L", &dhcp_universe, 59 }, - { "dhcp-class-identifier", "t", &dhcp_universe, 60 }, + { "vendor-class-identifier", "X", &dhcp_universe, 60 }, { "dhcp-client-identifier", "X", &dhcp_universe, 61 }, - { "option-62", "X", &dhcp_universe, 62 }, - { "option-63", "X", &dhcp_universe, 63 }, + { "nwip-domain", "X", &dhcp_universe, 62 }, + { "nwip-suboptions", "Enwip.", &dhcp_universe, 63 }, { "nisplus-domain", "t", &dhcp_universe, 64 }, { "nisplus-servers", "IA", &dhcp_universe, 65 }, { "tftp-server-name", "t", &dhcp_universe, 66 }, @@ -144,272 +177,707 @@ struct option dhcp_options [256] = { { "streettalk-server", "IA", &dhcp_universe, 75 }, { "streettalk-directory-assistance-server", "IA", &dhcp_universe, 76 }, { "user-class", "t", &dhcp_universe, 77 }, - { "option-78", "X", &dhcp_universe, 78 }, - { "option-79", "X", &dhcp_universe, 79 }, - { "option-80", "X", &dhcp_universe, 80 }, - { "option-81", "X", &dhcp_universe, 81 }, - { "option-82", "X", &dhcp_universe, 82 }, - { "option-83", "X", &dhcp_universe, 83 }, - { "option-84", "X", &dhcp_universe, 84 }, + { "slp-directory-agent", "fIa", &dhcp_universe, 78 }, + { "slp-service-scope", "fto", &dhcp_universe, 79 }, + { "#80", "X", &dhcp_universe, 80 }, + { "fqdn", "Efqdn.", &dhcp_universe, 81 }, + { "relay-agent-information", "Eagent.", &dhcp_universe, 82 }, + { "#83", "X", &dhcp_universe, 83 }, + { "#84", "X", &dhcp_universe, 84 }, { "nds-servers", "IA", &dhcp_universe, 85 }, { "nds-tree-name", "X", &dhcp_universe, 86 }, { "nds-context", "X", &dhcp_universe, 87 }, - { "option-88", "X", &dhcp_universe, 88 }, - { "option-89", "X", &dhcp_universe, 89 }, - { "option-90", "X", &dhcp_universe, 90 }, - { "option-91", "X", &dhcp_universe, 91 }, - { "option-92", "X", &dhcp_universe, 92 }, - { "option-93", "X", &dhcp_universe, 93 }, - { "option-94", "X", &dhcp_universe, 94 }, - { "option-95", "X", &dhcp_universe, 95 }, - { "option-96", "X", &dhcp_universe, 96 }, - { "option-97", "X", &dhcp_universe, 97 }, - { "option-98", "X", &dhcp_universe, 98 }, - { "option-99", "X", &dhcp_universe, 99 }, - { "option-100", "X", &dhcp_universe, 100 }, - { "option-101", "X", &dhcp_universe, 101 }, - { "option-102", "X", &dhcp_universe, 102 }, - { "option-103", "X", &dhcp_universe, 103 }, - { "option-104", "X", &dhcp_universe, 104 }, - { "option-105", "X", &dhcp_universe, 105 }, - { "option-106", "X", &dhcp_universe, 106 }, - { "option-107", "X", &dhcp_universe, 107 }, - { "option-108", "X", &dhcp_universe, 108 }, - { "option-109", "X", &dhcp_universe, 109 }, - { "option-110", "X", &dhcp_universe, 110 }, - { "option-111", "X", &dhcp_universe, 111 }, - { "option-112", "X", &dhcp_universe, 112 }, - { "option-113", "X", &dhcp_universe, 113 }, - { "option-114", "X", &dhcp_universe, 114 }, - { "option-115", "X", &dhcp_universe, 115 }, - { "option-116", "X", &dhcp_universe, 116 }, - { "option-117", "X", &dhcp_universe, 117 }, - { "option-118", "X", &dhcp_universe, 118 }, - { "option-119", "X", &dhcp_universe, 119 }, - { "option-120", "X", &dhcp_universe, 120 }, - { "option-121", "X", &dhcp_universe, 121 }, - { "option-122", "X", &dhcp_universe, 122 }, - { "option-123", "X", &dhcp_universe, 123 }, - { "option-124", "X", &dhcp_universe, 124 }, - { "option-125", "X", &dhcp_universe, 125 }, - { "option-126", "X", &dhcp_universe, 126 }, - { "option-127", "X", &dhcp_universe, 127 }, - { "option-128", "X", &dhcp_universe, 128 }, - { "option-129", "X", &dhcp_universe, 129 }, - { "option-130", "X", &dhcp_universe, 130 }, - { "option-131", "X", &dhcp_universe, 131 }, - { "option-132", "X", &dhcp_universe, 132 }, - { "option-133", "X", &dhcp_universe, 133 }, - { "option-134", "X", &dhcp_universe, 134 }, - { "option-135", "X", &dhcp_universe, 135 }, - { "option-136", "X", &dhcp_universe, 136 }, - { "option-137", "X", &dhcp_universe, 137 }, - { "option-138", "X", &dhcp_universe, 138 }, - { "option-139", "X", &dhcp_universe, 139 }, - { "option-140", "X", &dhcp_universe, 140 }, - { "option-141", "X", &dhcp_universe, 141 }, - { "option-142", "X", &dhcp_universe, 142 }, - { "option-143", "X", &dhcp_universe, 143 }, - { "option-144", "X", &dhcp_universe, 144 }, - { "option-145", "X", &dhcp_universe, 145 }, - { "option-146", "X", &dhcp_universe, 146 }, - { "option-147", "X", &dhcp_universe, 147 }, - { "option-148", "X", &dhcp_universe, 148 }, - { "option-149", "X", &dhcp_universe, 149 }, - { "option-150", "X", &dhcp_universe, 150 }, - { "option-151", "X", &dhcp_universe, 151 }, - { "option-152", "X", &dhcp_universe, 152 }, - { "option-153", "X", &dhcp_universe, 153 }, - { "option-154", "X", &dhcp_universe, 154 }, - { "option-155", "X", &dhcp_universe, 155 }, - { "option-156", "X", &dhcp_universe, 156 }, - { "option-157", "X", &dhcp_universe, 157 }, - { "option-158", "X", &dhcp_universe, 158 }, - { "option-159", "X", &dhcp_universe, 159 }, - { "option-160", "X", &dhcp_universe, 160 }, - { "option-161", "X", &dhcp_universe, 161 }, - { "option-162", "X", &dhcp_universe, 162 }, - { "option-163", "X", &dhcp_universe, 163 }, - { "option-164", "X", &dhcp_universe, 164 }, - { "option-165", "X", &dhcp_universe, 165 }, - { "option-166", "X", &dhcp_universe, 166 }, - { "option-167", "X", &dhcp_universe, 167 }, - { "option-168", "X", &dhcp_universe, 168 }, - { "option-169", "X", &dhcp_universe, 169 }, - { "option-170", "X", &dhcp_universe, 170 }, - { "option-171", "X", &dhcp_universe, 171 }, - { "option-172", "X", &dhcp_universe, 172 }, - { "option-173", "X", &dhcp_universe, 173 }, - { "option-174", "X", &dhcp_universe, 174 }, - { "option-175", "X", &dhcp_universe, 175 }, - { "option-176", "X", &dhcp_universe, 176 }, - { "option-177", "X", &dhcp_universe, 177 }, - { "option-178", "X", &dhcp_universe, 178 }, - { "option-179", "X", &dhcp_universe, 179 }, - { "option-180", "X", &dhcp_universe, 180 }, - { "option-181", "X", &dhcp_universe, 181 }, - { "option-182", "X", &dhcp_universe, 182 }, - { "option-183", "X", &dhcp_universe, 183 }, - { "option-184", "X", &dhcp_universe, 184 }, - { "option-185", "X", &dhcp_universe, 185 }, - { "option-186", "X", &dhcp_universe, 186 }, - { "option-187", "X", &dhcp_universe, 187 }, - { "option-188", "X", &dhcp_universe, 188 }, - { "option-189", "X", &dhcp_universe, 189 }, - { "option-190", "X", &dhcp_universe, 190 }, - { "option-191", "X", &dhcp_universe, 191 }, - { "option-192", "X", &dhcp_universe, 192 }, - { "option-193", "X", &dhcp_universe, 193 }, - { "option-194", "X", &dhcp_universe, 194 }, - { "option-195", "X", &dhcp_universe, 195 }, - { "option-196", "X", &dhcp_universe, 196 }, - { "option-197", "X", &dhcp_universe, 197 }, - { "option-198", "X", &dhcp_universe, 198 }, - { "option-199", "X", &dhcp_universe, 199 }, - { "option-200", "X", &dhcp_universe, 200 }, - { "option-201", "X", &dhcp_universe, 201 }, - { "option-202", "X", &dhcp_universe, 202 }, - { "option-203", "X", &dhcp_universe, 203 }, - { "option-204", "X", &dhcp_universe, 204 }, - { "option-205", "X", &dhcp_universe, 205 }, - { "option-206", "X", &dhcp_universe, 206 }, - { "option-207", "X", &dhcp_universe, 207 }, - { "option-208", "X", &dhcp_universe, 208 }, - { "option-209", "X", &dhcp_universe, 209 }, - { "option-210", "X", &dhcp_universe, 210 }, - { "option-211", "X", &dhcp_universe, 211 }, - { "option-212", "X", &dhcp_universe, 212 }, - { "option-213", "X", &dhcp_universe, 213 }, - { "option-214", "X", &dhcp_universe, 214 }, - { "option-215", "X", &dhcp_universe, 215 }, - { "option-216", "X", &dhcp_universe, 216 }, - { "option-217", "X", &dhcp_universe, 217 }, - { "option-218", "X", &dhcp_universe, 218 }, - { "option-219", "X", &dhcp_universe, 219 }, - { "option-220", "X", &dhcp_universe, 220 }, - { "option-221", "X", &dhcp_universe, 221 }, - { "option-222", "X", &dhcp_universe, 222 }, - { "option-223", "X", &dhcp_universe, 223 }, - { "option-224", "X", &dhcp_universe, 224 }, - { "option-225", "X", &dhcp_universe, 225 }, - { "option-226", "X", &dhcp_universe, 226 }, - { "option-227", "X", &dhcp_universe, 227 }, - { "option-228", "X", &dhcp_universe, 228 }, - { "option-229", "X", &dhcp_universe, 229 }, - { "option-230", "X", &dhcp_universe, 230 }, - { "option-231", "X", &dhcp_universe, 231 }, - { "option-232", "X", &dhcp_universe, 232 }, - { "option-233", "X", &dhcp_universe, 233 }, - { "option-234", "X", &dhcp_universe, 234 }, - { "option-235", "X", &dhcp_universe, 235 }, - { "option-236", "X", &dhcp_universe, 236 }, - { "option-237", "X", &dhcp_universe, 237 }, - { "option-238", "X", &dhcp_universe, 238 }, - { "option-239", "X", &dhcp_universe, 239 }, - { "option-240", "X", &dhcp_universe, 240 }, - { "option-241", "X", &dhcp_universe, 241 }, - { "option-242", "X", &dhcp_universe, 242 }, - { "option-243", "X", &dhcp_universe, 243 }, - { "option-244", "X", &dhcp_universe, 244 }, - { "option-245", "X", &dhcp_universe, 245 }, - { "option-246", "X", &dhcp_universe, 246 }, - { "option-247", "X", &dhcp_universe, 247 }, - { "option-248", "X", &dhcp_universe, 248 }, - { "option-249", "X", &dhcp_universe, 249 }, - { "option-250", "X", &dhcp_universe, 250 }, - { "option-251", "X", &dhcp_universe, 251 }, - { "option-252", "X", &dhcp_universe, 252 }, - { "option-253", "X", &dhcp_universe, 253 }, - { "option-254", "X", &dhcp_universe, 254 }, + { "#88", "X", &dhcp_universe, 88 }, + { "#89", "X", &dhcp_universe, 89 }, + { "#90", "X", &dhcp_universe, 90 }, + { "#91", "X", &dhcp_universe, 91 }, + { "#92", "X", &dhcp_universe, 92 }, + { "#93", "X", &dhcp_universe, 93 }, + { "#94", "X", &dhcp_universe, 94 }, + { "#95", "X", &dhcp_universe, 95 }, + { "#96", "X", &dhcp_universe, 96 }, + { "#97", "X", &dhcp_universe, 97 }, + { "uap-servers", "t", &dhcp_universe, 98 }, + { "#99", "X", &dhcp_universe, 99 }, + { "#100", "X", &dhcp_universe, 100 }, + { "#101", "X", &dhcp_universe, 101 }, + { "#102", "X", &dhcp_universe, 102 }, + { "#103", "X", &dhcp_universe, 103 }, + { "#104", "X", &dhcp_universe, 104 }, + { "#105", "X", &dhcp_universe, 105 }, + { "#106", "X", &dhcp_universe, 106 }, + { "#107", "X", &dhcp_universe, 107 }, + { "#108", "X", &dhcp_universe, 108 }, + { "#109", "X", &dhcp_universe, 109 }, + { "#110", "X", &dhcp_universe, 110 }, + { "#111", "X", &dhcp_universe, 111 }, + { "#112", "X", &dhcp_universe, 112 }, + { "#113", "X", &dhcp_universe, 113 }, + { "#114", "X", &dhcp_universe, 114 }, + { "#115", "X", &dhcp_universe, 115 }, + { "#116", "X", &dhcp_universe, 116 }, + { "#117", "X", &dhcp_universe, 117 }, + { "subnet-selection", "X", &dhcp_universe, 118 }, + { "#119", "X", &dhcp_universe, 119 }, + { "#120", "X", &dhcp_universe, 120 }, + { "#121", "X", &dhcp_universe, 121 }, + { "#122", "X", &dhcp_universe, 122 }, + { "#123", "X", &dhcp_universe, 123 }, + { "#124", "X", &dhcp_universe, 124 }, + { "#125", "X", &dhcp_universe, 125 }, + { "#126", "X", &dhcp_universe, 126 }, + { "#127", "X", &dhcp_universe, 127 }, + { "#128", "X", &dhcp_universe, 128 }, + { "#129", "X", &dhcp_universe, 129 }, + { "#130", "X", &dhcp_universe, 130 }, + { "#131", "X", &dhcp_universe, 131 }, + { "#132", "X", &dhcp_universe, 132 }, + { "#133", "X", &dhcp_universe, 133 }, + { "#134", "X", &dhcp_universe, 134 }, + { "#135", "X", &dhcp_universe, 135 }, + { "#136", "X", &dhcp_universe, 136 }, + { "#137", "X", &dhcp_universe, 137 }, + { "#138", "X", &dhcp_universe, 138 }, + { "#139", "X", &dhcp_universe, 139 }, + { "#140", "X", &dhcp_universe, 140 }, + { "#141", "X", &dhcp_universe, 141 }, + { "#142", "X", &dhcp_universe, 142 }, + { "#143", "X", &dhcp_universe, 143 }, + { "#144", "X", &dhcp_universe, 144 }, + { "#145", "X", &dhcp_universe, 145 }, + { "#146", "X", &dhcp_universe, 146 }, + { "#147", "X", &dhcp_universe, 147 }, + { "#148", "X", &dhcp_universe, 148 }, + { "#149", "X", &dhcp_universe, 149 }, + { "#150", "X", &dhcp_universe, 150 }, + { "#151", "X", &dhcp_universe, 151 }, + { "#152", "X", &dhcp_universe, 152 }, + { "#153", "X", &dhcp_universe, 153 }, + { "#154", "X", &dhcp_universe, 154 }, + { "#155", "X", &dhcp_universe, 155 }, + { "#156", "X", &dhcp_universe, 156 }, + { "#157", "X", &dhcp_universe, 157 }, + { "#158", "X", &dhcp_universe, 158 }, + { "#159", "X", &dhcp_universe, 159 }, + { "#160", "X", &dhcp_universe, 160 }, + { "#161", "X", &dhcp_universe, 161 }, + { "#162", "X", &dhcp_universe, 162 }, + { "#163", "X", &dhcp_universe, 163 }, + { "#164", "X", &dhcp_universe, 164 }, + { "#165", "X", &dhcp_universe, 165 }, + { "#166", "X", &dhcp_universe, 166 }, + { "#167", "X", &dhcp_universe, 167 }, + { "#168", "X", &dhcp_universe, 168 }, + { "#169", "X", &dhcp_universe, 169 }, + { "#170", "X", &dhcp_universe, 170 }, + { "#171", "X", &dhcp_universe, 171 }, + { "#172", "X", &dhcp_universe, 172 }, + { "#173", "X", &dhcp_universe, 173 }, + { "#174", "X", &dhcp_universe, 174 }, + { "#175", "X", &dhcp_universe, 175 }, + { "#176", "X", &dhcp_universe, 176 }, + { "#177", "X", &dhcp_universe, 177 }, + { "#178", "X", &dhcp_universe, 178 }, + { "#179", "X", &dhcp_universe, 179 }, + { "#180", "X", &dhcp_universe, 180 }, + { "#181", "X", &dhcp_universe, 181 }, + { "#182", "X", &dhcp_universe, 182 }, + { "#183", "X", &dhcp_universe, 183 }, + { "#184", "X", &dhcp_universe, 184 }, + { "#185", "X", &dhcp_universe, 185 }, + { "#186", "X", &dhcp_universe, 186 }, + { "#187", "X", &dhcp_universe, 187 }, + { "#188", "X", &dhcp_universe, 188 }, + { "#189", "X", &dhcp_universe, 189 }, + { "#190", "X", &dhcp_universe, 190 }, + { "#191", "X", &dhcp_universe, 191 }, + { "#192", "X", &dhcp_universe, 192 }, + { "#193", "X", &dhcp_universe, 193 }, + { "#194", "X", &dhcp_universe, 194 }, + { "#195", "X", &dhcp_universe, 195 }, + { "#196", "X", &dhcp_universe, 196 }, + { "#197", "X", &dhcp_universe, 197 }, + { "#198", "X", &dhcp_universe, 198 }, + { "#199", "X", &dhcp_universe, 199 }, + { "#200", "X", &dhcp_universe, 200 }, + { "#201", "X", &dhcp_universe, 201 }, + { "#202", "X", &dhcp_universe, 202 }, + { "#203", "X", &dhcp_universe, 203 }, + { "#204", "X", &dhcp_universe, 204 }, + { "#205", "X", &dhcp_universe, 205 }, + { "#206", "X", &dhcp_universe, 206 }, + { "#207", "X", &dhcp_universe, 207 }, + { "#208", "X", &dhcp_universe, 208 }, + { "#209", "X", &dhcp_universe, 209 }, + { "authenticate", "X", &dhcp_universe, 210 }, + { "#211", "X", &dhcp_universe, 211 }, + { "#212", "X", &dhcp_universe, 212 }, + { "#213", "X", &dhcp_universe, 213 }, + { "#214", "X", &dhcp_universe, 214 }, + { "#215", "X", &dhcp_universe, 215 }, + { "#216", "X", &dhcp_universe, 216 }, + { "#217", "X", &dhcp_universe, 217 }, + { "#218", "X", &dhcp_universe, 218 }, + { "#219", "X", &dhcp_universe, 219 }, + { "#220", "X", &dhcp_universe, 220 }, + { "#221", "X", &dhcp_universe, 221 }, + { "#222", "X", &dhcp_universe, 222 }, + { "#223", "X", &dhcp_universe, 223 }, + { "#224", "X", &dhcp_universe, 224 }, + { "#225", "X", &dhcp_universe, 225 }, + { "#226", "X", &dhcp_universe, 226 }, + { "#227", "X", &dhcp_universe, 227 }, + { "#228", "X", &dhcp_universe, 228 }, + { "#229", "X", &dhcp_universe, 229 }, + { "#230", "X", &dhcp_universe, 230 }, + { "#231", "X", &dhcp_universe, 231 }, + { "#232", "X", &dhcp_universe, 232 }, + { "#233", "X", &dhcp_universe, 233 }, + { "#234", "X", &dhcp_universe, 234 }, + { "#235", "X", &dhcp_universe, 235 }, + { "#236", "X", &dhcp_universe, 236 }, + { "#237", "X", &dhcp_universe, 237 }, + { "#238", "X", &dhcp_universe, 238 }, + { "#239", "X", &dhcp_universe, 239 }, + { "#240", "X", &dhcp_universe, 240 }, + { "#241", "X", &dhcp_universe, 241 }, + { "#242", "X", &dhcp_universe, 242 }, + { "#243", "X", &dhcp_universe, 243 }, + { "#244", "X", &dhcp_universe, 244 }, + { "#245", "X", &dhcp_universe, 245 }, + { "#246", "X", &dhcp_universe, 246 }, + { "#247", "X", &dhcp_universe, 247 }, + { "#248", "X", &dhcp_universe, 248 }, + { "#249", "X", &dhcp_universe, 249 }, + { "#250", "X", &dhcp_universe, 250 }, + { "#251", "X", &dhcp_universe, 251 }, + { "#252", "X", &dhcp_universe, 252 }, + { "#253", "X", &dhcp_universe, 253 }, + { "#254", "X", &dhcp_universe, 254 }, { "option-end", "e", &dhcp_universe, 255 }, }; -/* Default dhcp option priority list (this is ad hoc and should not be - mistaken for a carefully crafted and optimized list). */ -unsigned char dhcp_option_default_priority_list [] = { - DHO_DHCP_REQUESTED_ADDRESS, - DHO_DHCP_OPTION_OVERLOAD, - DHO_DHCP_MAX_MESSAGE_SIZE, - DHO_DHCP_RENEWAL_TIME, - DHO_DHCP_REBINDING_TIME, - DHO_DHCP_CLASS_IDENTIFIER, - DHO_DHCP_CLIENT_IDENTIFIER, - DHO_SUBNET_MASK, - DHO_TIME_OFFSET, - DHO_ROUTERS, - DHO_TIME_SERVERS, - DHO_NAME_SERVERS, - DHO_DOMAIN_NAME_SERVERS, - DHO_HOST_NAME, - DHO_LOG_SERVERS, - DHO_COOKIE_SERVERS, - DHO_LPR_SERVERS, - DHO_IMPRESS_SERVERS, - DHO_RESOURCE_LOCATION_SERVERS, - DHO_HOST_NAME, - DHO_BOOT_SIZE, - DHO_MERIT_DUMP, - DHO_DOMAIN_NAME, - DHO_SWAP_SERVER, - DHO_ROOT_PATH, - DHO_EXTENSIONS_PATH, - DHO_IP_FORWARDING, - DHO_NON_LOCAL_SOURCE_ROUTING, - DHO_POLICY_FILTER, - DHO_MAX_DGRAM_REASSEMBLY, - DHO_DEFAULT_IP_TTL, - DHO_PATH_MTU_AGING_TIMEOUT, - DHO_PATH_MTU_PLATEAU_TABLE, - DHO_INTERFACE_MTU, - DHO_ALL_SUBNETS_LOCAL, - DHO_BROADCAST_ADDRESS, - DHO_PERFORM_MASK_DISCOVERY, - DHO_MASK_SUPPLIER, - DHO_ROUTER_DISCOVERY, - DHO_ROUTER_SOLICITATION_ADDRESS, - DHO_STATIC_ROUTES, - DHO_TRAILER_ENCAPSULATION, - DHO_ARP_CACHE_TIMEOUT, - DHO_IEEE802_3_ENCAPSULATION, - DHO_DEFAULT_TCP_TTL, - DHO_TCP_KEEPALIVE_INTERVAL, - DHO_TCP_KEEPALIVE_GARBAGE, - DHO_NIS_DOMAIN, - DHO_NIS_SERVERS, - DHO_NTP_SERVERS, - DHO_VENDOR_ENCAPSULATED_OPTIONS, - DHO_NETBIOS_NAME_SERVERS, - DHO_NETBIOS_DD_SERVER, - DHO_NETBIOS_NODE_TYPE, - DHO_NETBIOS_SCOPE, - DHO_FONT_SERVERS, - DHO_X_DISPLAY_MANAGER, - DHO_DHCP_PARAMETER_REQUEST_LIST, - - /* Presently-undefined options... */ - 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, - 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, - 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, - 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, - 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, - 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, - 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, - 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, - 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, - 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, - 251, 252, 253, 254, +struct universe nwip_universe; +struct option nwip_options [256] = { + { "pad", "", &nwip_universe, 0 }, + { "illegal-1", "", &nwip_universe, 1 }, + { "illegal-2", "", &nwip_universe, 2 }, + { "illegal-3", "", &nwip_universe, 3 }, + { "illegal-4", "", &nwip_universe, 4 }, + { "nsq-broadcast", "f", &nwip_universe, 5 }, + { "preferred-dss", "IA", &nwip_universe, 6 }, + { "nearest-nwip-server", "IA", &nwip_universe, 7 }, + { "autoretries", "B", &nwip_universe, 8 }, + { "autoretry-secs", "B", &nwip_universe, 9 }, + { "nwip-1-1", "f", &nwip_universe, 10 }, + { "primary-dss", "I", &nwip_universe, 11 }, + { "#12", "X", &nwip_universe, 12 }, + { "#13", "X", &nwip_universe, 13 }, + { "#14", "X", &nwip_universe, 14 }, + { "#15", "X", &nwip_universe, 15 }, + { "#16", "X", &nwip_universe, 16 }, + { "#17", "X", &nwip_universe, 17 }, + { "#18", "X", &nwip_universe, 18 }, + { "#19", "X", &nwip_universe, 19 }, + { "#20", "X", &nwip_universe, 20 }, + { "#21", "X", &nwip_universe, 21 }, + { "#22", "X", &nwip_universe, 22 }, + { "#23", "X", &nwip_universe, 23 }, + { "#24", "X", &nwip_universe, 24 }, + { "#25", "X", &nwip_universe, 25 }, + { "#26", "X", &nwip_universe, 26 }, + { "#27", "X", &nwip_universe, 27 }, + { "#28", "X", &nwip_universe, 28 }, + { "#29", "X", &nwip_universe, 29 }, + { "#30", "X", &nwip_universe, 30 }, + { "#31", "X", &nwip_universe, 31 }, + { "#32", "X", &nwip_universe, 32 }, + { "#33", "X", &nwip_universe, 33 }, + { "#34", "X", &nwip_universe, 34 }, + { "#35", "X", &nwip_universe, 35 }, + { "#36", "X", &nwip_universe, 36 }, + { "#37", "X", &nwip_universe, 37 }, + { "#38", "X", &nwip_universe, 38 }, + { "#39", "X", &nwip_universe, 39 }, + { "#40", "X", &nwip_universe, 40 }, + { "#41", "X", &nwip_universe, 41 }, + { "#42", "X", &nwip_universe, 42 }, + { "#43", "X", &nwip_universe, 43 }, + { "#44", "X", &nwip_universe, 44 }, + { "#45", "X", &nwip_universe, 45 }, + { "#46", "X", &nwip_universe, 46 }, + { "#47", "X", &nwip_universe, 47 }, + { "#48", "X", &nwip_universe, 48 }, + { "#49", "X", &nwip_universe, 49 }, + { "#50", "X", &nwip_universe, 50 }, + { "#51", "X", &nwip_universe, 51 }, + { "#52", "X", &nwip_universe, 52 }, + { "#53", "X", &nwip_universe, 53 }, + { "#54", "X", &nwip_universe, 54 }, + { "#55", "X", &nwip_universe, 55 }, + { "#56", "X", &nwip_universe, 56 }, + { "#57", "X", &nwip_universe, 57 }, + { "#58", "X", &nwip_universe, 58 }, + { "#59", "X", &nwip_universe, 59 }, + { "#60", "X", &nwip_universe, 60 }, + { "#61", "X", &nwip_universe, 61 }, + { "#62", "X", &nwip_universe, 62 }, + { "#63", "X", &nwip_universe, 63 }, + { "#64", "X", &nwip_universe, 64 }, + { "#65", "X", &nwip_universe, 65 }, + { "#66", "X", &nwip_universe, 66 }, + { "#67", "X", &nwip_universe, 67 }, + { "#68", "X", &nwip_universe, 68 }, + { "#69", "X", &nwip_universe, 69 }, + { "#70", "X", &nwip_universe, 70 }, + { "#71", "X", &nwip_universe, 71 }, + { "#72", "X", &nwip_universe, 72 }, + { "#73", "X", &nwip_universe, 73 }, + { "#74", "X", &nwip_universe, 74 }, + { "#75", "X", &nwip_universe, 75 }, + { "#76", "X", &nwip_universe, 76 }, + { "#77", "X", &nwip_universe, 77 }, + { "#78", "X", &nwip_universe, 78 }, + { "#79", "X", &nwip_universe, 79 }, + { "#80", "X", &nwip_universe, 80 }, + { "#81", "X", &nwip_universe, 81 }, + { "#82", "X", &nwip_universe, 82 }, + { "#83", "X", &nwip_universe, 83 }, + { "#84", "X", &nwip_universe, 84 }, + { "#85", "X", &nwip_universe, 85 }, + { "#86", "X", &nwip_universe, 86 }, + { "#87", "X", &nwip_universe, 87 }, + { "#88", "X", &nwip_universe, 88 }, + { "#89", "X", &nwip_universe, 89 }, + { "#90", "X", &nwip_universe, 90 }, + { "#91", "X", &nwip_universe, 91 }, + { "#92", "X", &nwip_universe, 92 }, + { "#93", "X", &nwip_universe, 93 }, + { "#94", "X", &nwip_universe, 94 }, + { "#95", "X", &nwip_universe, 95 }, + { "#96", "X", &nwip_universe, 96 }, + { "#97", "X", &nwip_universe, 97 }, + { "#98", "X", &nwip_universe, 98 }, + { "#99", "X", &nwip_universe, 99 }, + { "#100", "X", &nwip_universe, 100 }, + { "#101", "X", &nwip_universe, 101 }, + { "#102", "X", &nwip_universe, 102 }, + { "#103", "X", &nwip_universe, 103 }, + { "#104", "X", &nwip_universe, 104 }, + { "#105", "X", &nwip_universe, 105 }, + { "#106", "X", &nwip_universe, 106 }, + { "#107", "X", &nwip_universe, 107 }, + { "#108", "X", &nwip_universe, 108 }, + { "#109", "X", &nwip_universe, 109 }, + { "#110", "X", &nwip_universe, 110 }, + { "#111", "X", &nwip_universe, 111 }, + { "#112", "X", &nwip_universe, 112 }, + { "#113", "X", &nwip_universe, 113 }, + { "#114", "X", &nwip_universe, 114 }, + { "#115", "X", &nwip_universe, 115 }, + { "#116", "X", &nwip_universe, 116 }, + { "#117", "X", &nwip_universe, 117 }, + { "#118", "X", &nwip_universe, 118 }, + { "#119", "X", &nwip_universe, 119 }, + { "#120", "X", &nwip_universe, 120 }, + { "#121", "X", &nwip_universe, 121 }, + { "#122", "X", &nwip_universe, 122 }, + { "#123", "X", &nwip_universe, 123 }, + { "#124", "X", &nwip_universe, 124 }, + { "#125", "X", &nwip_universe, 125 }, + { "#126", "X", &nwip_universe, 126 }, + { "#127", "X", &nwip_universe, 127 }, + { "#128", "X", &nwip_universe, 128 }, + { "#129", "X", &nwip_universe, 129 }, + { "#130", "X", &nwip_universe, 130 }, + { "#131", "X", &nwip_universe, 131 }, + { "#132", "X", &nwip_universe, 132 }, + { "#133", "X", &nwip_universe, 133 }, + { "#134", "X", &nwip_universe, 134 }, + { "#135", "X", &nwip_universe, 135 }, + { "#136", "X", &nwip_universe, 136 }, + { "#137", "X", &nwip_universe, 137 }, + { "#138", "X", &nwip_universe, 138 }, + { "#139", "X", &nwip_universe, 139 }, + { "#140", "X", &nwip_universe, 140 }, + { "#141", "X", &nwip_universe, 141 }, + { "#142", "X", &nwip_universe, 142 }, + { "#143", "X", &nwip_universe, 143 }, + { "#144", "X", &nwip_universe, 144 }, + { "#145", "X", &nwip_universe, 145 }, + { "#146", "X", &nwip_universe, 146 }, + { "#147", "X", &nwip_universe, 147 }, + { "#148", "X", &nwip_universe, 148 }, + { "#149", "X", &nwip_universe, 149 }, + { "#150", "X", &nwip_universe, 150 }, + { "#151", "X", &nwip_universe, 151 }, + { "#152", "X", &nwip_universe, 152 }, + { "#153", "X", &nwip_universe, 153 }, + { "#154", "X", &nwip_universe, 154 }, + { "#155", "X", &nwip_universe, 155 }, + { "#156", "X", &nwip_universe, 156 }, + { "#157", "X", &nwip_universe, 157 }, + { "#158", "X", &nwip_universe, 158 }, + { "#159", "X", &nwip_universe, 159 }, + { "#160", "X", &nwip_universe, 160 }, + { "#161", "X", &nwip_universe, 161 }, + { "#162", "X", &nwip_universe, 162 }, + { "#163", "X", &nwip_universe, 163 }, + { "#164", "X", &nwip_universe, 164 }, + { "#165", "X", &nwip_universe, 165 }, + { "#166", "X", &nwip_universe, 166 }, + { "#167", "X", &nwip_universe, 167 }, + { "#168", "X", &nwip_universe, 168 }, + { "#169", "X", &nwip_universe, 169 }, + { "#170", "X", &nwip_universe, 170 }, + { "#171", "X", &nwip_universe, 171 }, + { "#172", "X", &nwip_universe, 172 }, + { "#173", "X", &nwip_universe, 173 }, + { "#174", "X", &nwip_universe, 174 }, + { "#175", "X", &nwip_universe, 175 }, + { "#176", "X", &nwip_universe, 176 }, + { "#177", "X", &nwip_universe, 177 }, + { "#178", "X", &nwip_universe, 178 }, + { "#179", "X", &nwip_universe, 179 }, + { "#180", "X", &nwip_universe, 180 }, + { "#181", "X", &nwip_universe, 181 }, + { "#182", "X", &nwip_universe, 182 }, + { "#183", "X", &nwip_universe, 183 }, + { "#184", "X", &nwip_universe, 184 }, + { "#185", "X", &nwip_universe, 185 }, + { "#186", "X", &nwip_universe, 186 }, + { "#187", "X", &nwip_universe, 187 }, + { "#188", "X", &nwip_universe, 188 }, + { "#189", "X", &nwip_universe, 189 }, + { "#190", "X", &nwip_universe, 190 }, + { "#191", "X", &nwip_universe, 191 }, + { "#192", "X", &nwip_universe, 192 }, + { "#193", "X", &nwip_universe, 193 }, + { "#194", "X", &nwip_universe, 194 }, + { "#195", "X", &nwip_universe, 195 }, + { "#196", "X", &nwip_universe, 196 }, + { "#197", "X", &nwip_universe, 197 }, + { "#198", "X", &nwip_universe, 198 }, + { "#199", "X", &nwip_universe, 199 }, + { "#200", "X", &nwip_universe, 200 }, + { "#201", "X", &nwip_universe, 201 }, + { "#202", "X", &nwip_universe, 202 }, + { "#203", "X", &nwip_universe, 203 }, + { "#204", "X", &nwip_universe, 204 }, + { "#205", "X", &nwip_universe, 205 }, + { "#206", "X", &nwip_universe, 206 }, + { "#207", "X", &nwip_universe, 207 }, + { "#208", "X", &nwip_universe, 208 }, + { "#209", "X", &nwip_universe, 209 }, + { "#210", "X", &nwip_universe, 210 }, + { "#211", "X", &nwip_universe, 211 }, + { "#212", "X", &nwip_universe, 212 }, + { "#213", "X", &nwip_universe, 213 }, + { "#214", "X", &nwip_universe, 214 }, + { "#215", "X", &nwip_universe, 215 }, + { "#216", "X", &nwip_universe, 216 }, + { "#217", "X", &nwip_universe, 217 }, + { "#218", "X", &nwip_universe, 218 }, + { "#219", "X", &nwip_universe, 219 }, + { "#220", "X", &nwip_universe, 220 }, + { "#221", "X", &nwip_universe, 221 }, + { "#222", "X", &nwip_universe, 222 }, + { "#223", "X", &nwip_universe, 223 }, + { "#224", "X", &nwip_universe, 224 }, + { "#225", "X", &nwip_universe, 225 }, + { "#226", "X", &nwip_universe, 226 }, + { "#227", "X", &nwip_universe, 227 }, + { "#228", "X", &nwip_universe, 228 }, + { "#229", "X", &nwip_universe, 229 }, + { "#230", "X", &nwip_universe, 230 }, + { "#231", "X", &nwip_universe, 231 }, + { "#232", "X", &nwip_universe, 232 }, + { "#233", "X", &nwip_universe, 233 }, + { "#234", "X", &nwip_universe, 234 }, + { "#235", "X", &nwip_universe, 235 }, + { "#236", "X", &nwip_universe, 236 }, + { "#237", "X", &nwip_universe, 237 }, + { "#238", "X", &nwip_universe, 238 }, + { "#239", "X", &nwip_universe, 239 }, + { "#240", "X", &nwip_universe, 240 }, + { "#241", "X", &nwip_universe, 241 }, + { "#242", "X", &nwip_universe, 242 }, + { "#243", "X", &nwip_universe, 243 }, + { "#244", "X", &nwip_universe, 244 }, + { "#245", "X", &nwip_universe, 245 }, + { "#246", "X", &nwip_universe, 246 }, + { "#247", "X", &nwip_universe, 247 }, + { "#248", "X", &nwip_universe, 248 }, + { "#249", "X", &nwip_universe, 249 }, + { "#250", "X", &nwip_universe, 250 }, + { "#251", "X", &nwip_universe, 251 }, + { "#252", "X", &nwip_universe, 252 }, + { "#253", "X", &nwip_universe, 253 }, + { "#254", "X", &nwip_universe, 254 }, + { "#end", "e", &nwip_universe, 255 }, }; -int sizeof_dhcp_option_default_priority_list = - sizeof dhcp_option_default_priority_list; - +struct universe fqdn_universe; +struct option fqdn_options [256] = { + { "pad", "", &fqdn_universe, 0 }, + { "no-client-update", "f", &fqdn_universe, 1 }, + { "server-update", "f", &fqdn_universe, 2 }, + { "encoded", "f", &fqdn_universe, 3 }, + { "rcode1", "B", &fqdn_universe, 4 }, + { "rcode2", "B", &fqdn_universe, 5 }, + { "hostname", "t", &fqdn_universe, 6 }, + { "domainname", "t", &fqdn_universe, 7 }, + { "fqdn", "t", &fqdn_universe, 8 }, + { "#9", "X", &fqdn_universe, 9 }, + { "#10", "X", &fqdn_universe, 10 }, + { "#11", "X", &fqdn_universe, 11 }, + { "#12", "X", &fqdn_universe, 12 }, + { "#13", "X", &fqdn_universe, 13 }, + { "#14", "X", &fqdn_universe, 14 }, + { "#15", "X", &fqdn_universe, 15 }, + { "#16", "X", &fqdn_universe, 16 }, + { "#17", "X", &fqdn_universe, 17 }, + { "#18", "X", &fqdn_universe, 18 }, + { "#19", "X", &fqdn_universe, 19 }, + { "#20", "X", &fqdn_universe, 20 }, + { "#21", "X", &fqdn_universe, 21 }, + { "#22", "X", &fqdn_universe, 22 }, + { "#23", "X", &fqdn_universe, 23 }, + { "#24", "X", &fqdn_universe, 24 }, + { "#25", "X", &fqdn_universe, 25 }, + { "#26", "X", &fqdn_universe, 26 }, + { "#27", "X", &fqdn_universe, 27 }, + { "#28", "X", &fqdn_universe, 28 }, + { "#29", "X", &fqdn_universe, 29 }, + { "#30", "X", &fqdn_universe, 30 }, + { "#31", "X", &fqdn_universe, 31 }, + { "#32", "X", &fqdn_universe, 32 }, + { "#33", "X", &fqdn_universe, 33 }, + { "#34", "X", &fqdn_universe, 34 }, + { "#35", "X", &fqdn_universe, 35 }, + { "#36", "X", &fqdn_universe, 36 }, + { "#37", "X", &fqdn_universe, 37 }, + { "#38", "X", &fqdn_universe, 38 }, + { "#39", "X", &fqdn_universe, 39 }, + { "#40", "X", &fqdn_universe, 40 }, + { "#41", "X", &fqdn_universe, 41 }, + { "#42", "X", &fqdn_universe, 42 }, + { "#43", "X", &fqdn_universe, 43 }, + { "#44", "X", &fqdn_universe, 44 }, + { "#45", "X", &fqdn_universe, 45 }, + { "#46", "X", &fqdn_universe, 46 }, + { "#47", "X", &fqdn_universe, 47 }, + { "#48", "X", &fqdn_universe, 48 }, + { "#49", "X", &fqdn_universe, 49 }, + { "#50", "X", &fqdn_universe, 50 }, + { "#51", "X", &fqdn_universe, 51 }, + { "#52", "X", &fqdn_universe, 52 }, + { "#53", "X", &fqdn_universe, 53 }, + { "#54", "X", &fqdn_universe, 54 }, + { "#55", "X", &fqdn_universe, 55 }, + { "#56", "X", &fqdn_universe, 56 }, + { "#57", "X", &fqdn_universe, 57 }, + { "#58", "X", &fqdn_universe, 58 }, + { "#59", "X", &fqdn_universe, 59 }, + { "#60", "X", &fqdn_universe, 60 }, + { "#61", "X", &fqdn_universe, 61 }, + { "#62", "X", &fqdn_universe, 62 }, + { "#63", "X", &fqdn_universe, 63 }, + { "#64", "X", &fqdn_universe, 64 }, + { "#65", "X", &fqdn_universe, 65 }, + { "#66", "X", &fqdn_universe, 66 }, + { "#67", "X", &fqdn_universe, 67 }, + { "#68", "X", &fqdn_universe, 68 }, + { "#69", "X", &fqdn_universe, 69 }, + { "#70", "X", &fqdn_universe, 70 }, + { "#71", "X", &fqdn_universe, 71 }, + { "#72", "X", &fqdn_universe, 72 }, + { "#73", "X", &fqdn_universe, 73 }, + { "#74", "X", &fqdn_universe, 74 }, + { "#75", "X", &fqdn_universe, 75 }, + { "#76", "X", &fqdn_universe, 76 }, + { "#77", "X", &fqdn_universe, 77 }, + { "#78", "X", &fqdn_universe, 78 }, + { "#79", "X", &fqdn_universe, 79 }, + { "#80", "X", &fqdn_universe, 80 }, + { "#81", "X", &fqdn_universe, 81 }, + { "#82", "X", &fqdn_universe, 82 }, + { "#83", "X", &fqdn_universe, 83 }, + { "#84", "X", &fqdn_universe, 84 }, + { "#85", "X", &fqdn_universe, 85 }, + { "#86", "X", &fqdn_universe, 86 }, + { "#87", "X", &fqdn_universe, 87 }, + { "#88", "X", &fqdn_universe, 88 }, + { "#89", "X", &fqdn_universe, 89 }, + { "#90", "X", &fqdn_universe, 90 }, + { "#91", "X", &fqdn_universe, 91 }, + { "#92", "X", &fqdn_universe, 92 }, + { "#93", "X", &fqdn_universe, 93 }, + { "#94", "X", &fqdn_universe, 94 }, + { "#95", "X", &fqdn_universe, 95 }, + { "#96", "X", &fqdn_universe, 96 }, + { "#97", "X", &fqdn_universe, 97 }, + { "#98", "X", &fqdn_universe, 98 }, + { "#99", "X", &fqdn_universe, 99 }, + { "#100", "X", &fqdn_universe, 100 }, + { "#101", "X", &fqdn_universe, 101 }, + { "#102", "X", &fqdn_universe, 102 }, + { "#103", "X", &fqdn_universe, 103 }, + { "#104", "X", &fqdn_universe, 104 }, + { "#105", "X", &fqdn_universe, 105 }, + { "#106", "X", &fqdn_universe, 106 }, + { "#107", "X", &fqdn_universe, 107 }, + { "#108", "X", &fqdn_universe, 108 }, + { "#109", "X", &fqdn_universe, 109 }, + { "#110", "X", &fqdn_universe, 110 }, + { "#111", "X", &fqdn_universe, 111 }, + { "#112", "X", &fqdn_universe, 112 }, + { "#113", "X", &fqdn_universe, 113 }, + { "#114", "X", &fqdn_universe, 114 }, + { "#115", "X", &fqdn_universe, 115 }, + { "#116", "X", &fqdn_universe, 116 }, + { "#117", "X", &fqdn_universe, 117 }, + { "#118", "X", &fqdn_universe, 118 }, + { "#119", "X", &fqdn_universe, 119 }, + { "#120", "X", &fqdn_universe, 120 }, + { "#121", "X", &fqdn_universe, 121 }, + { "#122", "X", &fqdn_universe, 122 }, + { "#123", "X", &fqdn_universe, 123 }, + { "#124", "X", &fqdn_universe, 124 }, + { "#125", "X", &fqdn_universe, 125 }, + { "#126", "X", &fqdn_universe, 126 }, + { "#127", "X", &fqdn_universe, 127 }, + { "#128", "X", &fqdn_universe, 128 }, + { "#129", "X", &fqdn_universe, 129 }, + { "#130", "X", &fqdn_universe, 130 }, + { "#131", "X", &fqdn_universe, 131 }, + { "#132", "X", &fqdn_universe, 132 }, + { "#133", "X", &fqdn_universe, 133 }, + { "#134", "X", &fqdn_universe, 134 }, + { "#135", "X", &fqdn_universe, 135 }, + { "#136", "X", &fqdn_universe, 136 }, + { "#137", "X", &fqdn_universe, 137 }, + { "#138", "X", &fqdn_universe, 138 }, + { "#139", "X", &fqdn_universe, 139 }, + { "#140", "X", &fqdn_universe, 140 }, + { "#141", "X", &fqdn_universe, 141 }, + { "#142", "X", &fqdn_universe, 142 }, + { "#143", "X", &fqdn_universe, 143 }, + { "#144", "X", &fqdn_universe, 144 }, + { "#145", "X", &fqdn_universe, 145 }, + { "#146", "X", &fqdn_universe, 146 }, + { "#147", "X", &fqdn_universe, 147 }, + { "#148", "X", &fqdn_universe, 148 }, + { "#149", "X", &fqdn_universe, 149 }, + { "#150", "X", &fqdn_universe, 150 }, + { "#151", "X", &fqdn_universe, 151 }, + { "#152", "X", &fqdn_universe, 152 }, + { "#153", "X", &fqdn_universe, 153 }, + { "#154", "X", &fqdn_universe, 154 }, + { "#155", "X", &fqdn_universe, 155 }, + { "#156", "X", &fqdn_universe, 156 }, + { "#157", "X", &fqdn_universe, 157 }, + { "#158", "X", &fqdn_universe, 158 }, + { "#159", "X", &fqdn_universe, 159 }, + { "#160", "X", &fqdn_universe, 160 }, + { "#161", "X", &fqdn_universe, 161 }, + { "#162", "X", &fqdn_universe, 162 }, + { "#163", "X", &fqdn_universe, 163 }, + { "#164", "X", &fqdn_universe, 164 }, + { "#165", "X", &fqdn_universe, 165 }, + { "#166", "X", &fqdn_universe, 166 }, + { "#167", "X", &fqdn_universe, 167 }, + { "#168", "X", &fqdn_universe, 168 }, + { "#169", "X", &fqdn_universe, 169 }, + { "#170", "X", &fqdn_universe, 170 }, + { "#171", "X", &fqdn_universe, 171 }, + { "#172", "X", &fqdn_universe, 172 }, + { "#173", "X", &fqdn_universe, 173 }, + { "#174", "X", &fqdn_universe, 174 }, + { "#175", "X", &fqdn_universe, 175 }, + { "#176", "X", &fqdn_universe, 176 }, + { "#177", "X", &fqdn_universe, 177 }, + { "#178", "X", &fqdn_universe, 178 }, + { "#179", "X", &fqdn_universe, 179 }, + { "#180", "X", &fqdn_universe, 180 }, + { "#181", "X", &fqdn_universe, 181 }, + { "#182", "X", &fqdn_universe, 182 }, + { "#183", "X", &fqdn_universe, 183 }, + { "#184", "X", &fqdn_universe, 184 }, + { "#185", "X", &fqdn_universe, 185 }, + { "#186", "X", &fqdn_universe, 186 }, + { "#187", "X", &fqdn_universe, 187 }, + { "#188", "X", &fqdn_universe, 188 }, + { "#189", "X", &fqdn_universe, 189 }, + { "#190", "X", &fqdn_universe, 190 }, + { "#191", "X", &fqdn_universe, 191 }, + { "#192", "X", &fqdn_universe, 192 }, + { "#193", "X", &fqdn_universe, 193 }, + { "#194", "X", &fqdn_universe, 194 }, + { "#195", "X", &fqdn_universe, 195 }, + { "#196", "X", &fqdn_universe, 196 }, + { "#197", "X", &fqdn_universe, 197 }, + { "#198", "X", &fqdn_universe, 198 }, + { "#199", "X", &fqdn_universe, 199 }, + { "#200", "X", &fqdn_universe, 200 }, + { "#201", "X", &fqdn_universe, 201 }, + { "#202", "X", &fqdn_universe, 202 }, + { "#203", "X", &fqdn_universe, 203 }, + { "#204", "X", &fqdn_universe, 204 }, + { "#205", "X", &fqdn_universe, 205 }, + { "#206", "X", &fqdn_universe, 206 }, + { "#207", "X", &fqdn_universe, 207 }, + { "#208", "X", &fqdn_universe, 208 }, + { "#209", "X", &fqdn_universe, 209 }, + { "#210", "X", &fqdn_universe, 210 }, + { "#211", "X", &fqdn_universe, 211 }, + { "#212", "X", &fqdn_universe, 212 }, + { "#213", "X", &fqdn_universe, 213 }, + { "#214", "X", &fqdn_universe, 214 }, + { "#215", "X", &fqdn_universe, 215 }, + { "#216", "X", &fqdn_universe, 216 }, + { "#217", "X", &fqdn_universe, 217 }, + { "#218", "X", &fqdn_universe, 218 }, + { "#219", "X", &fqdn_universe, 219 }, + { "#220", "X", &fqdn_universe, 220 }, + { "#221", "X", &fqdn_universe, 221 }, + { "#222", "X", &fqdn_universe, 222 }, + { "#223", "X", &fqdn_universe, 223 }, + { "#224", "X", &fqdn_universe, 224 }, + { "#225", "X", &fqdn_universe, 225 }, + { "#226", "X", &fqdn_universe, 226 }, + { "#227", "X", &fqdn_universe, 227 }, + { "#228", "X", &fqdn_universe, 228 }, + { "#229", "X", &fqdn_universe, 229 }, + { "#230", "X", &fqdn_universe, 230 }, + { "#231", "X", &fqdn_universe, 231 }, + { "#232", "X", &fqdn_universe, 232 }, + { "#233", "X", &fqdn_universe, 233 }, + { "#234", "X", &fqdn_universe, 234 }, + { "#235", "X", &fqdn_universe, 235 }, + { "#236", "X", &fqdn_universe, 236 }, + { "#237", "X", &fqdn_universe, 237 }, + { "#238", "X", &fqdn_universe, 238 }, + { "#239", "X", &fqdn_universe, 239 }, + { "#240", "X", &fqdn_universe, 240 }, + { "#241", "X", &fqdn_universe, 241 }, + { "#242", "X", &fqdn_universe, 242 }, + { "#243", "X", &fqdn_universe, 243 }, + { "#244", "X", &fqdn_universe, 244 }, + { "#245", "X", &fqdn_universe, 245 }, + { "#246", "X", &fqdn_universe, 246 }, + { "#247", "X", &fqdn_universe, 247 }, + { "#248", "X", &fqdn_universe, 248 }, + { "#249", "X", &fqdn_universe, 249 }, + { "#250", "X", &fqdn_universe, 250 }, + { "#251", "X", &fqdn_universe, 251 }, + { "#252", "X", &fqdn_universe, 252 }, + { "#253", "X", &fqdn_universe, 253 }, + { "#254", "X", &fqdn_universe, 254 }, + { "#end", "e", &fqdn_universe, 255 }, +}; -char *hardware_types [] = { +const char *hardware_types [] = { "unknown-0", "ethernet", "unknown-2", @@ -667,26 +1135,115 @@ char *hardware_types [] = { "unknown-254", "unknown-255" }; +universe_hash_t *universe_hash; +struct universe **universes; +int universe_count, universe_max; +/* Universe containing names of configuration options, which, rather than + writing "option universe-name.option-name ...;", can be set by writing + "option-name ...;". */ -struct hash_table universe_hash; +struct universe *config_universe; -void initialize_universes() +void initialize_common_option_spaces() { int i; + universe_max = 10; + universes = ((struct universe **) + dmalloc (universe_max * sizeof (struct universe *), MDL)); + if (!universes) + log_fatal ("Can't allocate option space table."); + memset (universes, 0, universe_max * sizeof (struct universe *)); + + /* Set up the DHCP option universe... */ dhcp_universe.name = "dhcp"; - dhcp_universe.hash = new_hash (); - if (!dhcp_universe.hash) - error ("Can't allocate dhcp option hash table."); + dhcp_universe.lookup_func = lookup_hashed_option; + dhcp_universe.option_state_dereference = + hashed_option_state_dereference; + dhcp_universe.save_func = save_hashed_option; + dhcp_universe.delete_func = delete_hashed_option; + dhcp_universe.encapsulate = hashed_option_space_encapsulate; + dhcp_universe.foreach = hashed_option_space_foreach; + dhcp_universe.decode = parse_option_buffer; + dhcp_universe.length_size = 1; + dhcp_universe.tag_size = 1; + dhcp_universe.store_tag = putUChar; + dhcp_universe.store_length = putUChar; + dhcp_universe.index = universe_count++; + universes [dhcp_universe.index] = &dhcp_universe; + if (!option_new_hash (&dhcp_universe.hash, 1, MDL)) + log_fatal ("Can't allocate dhcp option hash table."); for (i = 0; i < 256; i++) { dhcp_universe.options [i] = &dhcp_options [i]; - add_hash (dhcp_universe.hash, - (unsigned char *)dhcp_options [i].name, 0, - (unsigned char *)&dhcp_options [i]); + option_hash_add (dhcp_universe.hash, + dhcp_options [i].name, 0, + &dhcp_options [i], MDL); + } + + /* Set up the Novell option universe (for option 63)... */ + nwip_universe.name = "nwip"; + nwip_universe.lookup_func = lookup_linked_option; + nwip_universe.option_state_dereference = + linked_option_state_dereference; + nwip_universe.save_func = save_linked_option; + nwip_universe.delete_func = delete_linked_option; + nwip_universe.encapsulate = nwip_option_space_encapsulate; + nwip_universe.foreach = linked_option_space_foreach; + nwip_universe.decode = parse_option_buffer; + nwip_universe.length_size = 1; + nwip_universe.tag_size = 1; + nwip_universe.store_tag = putUChar; + nwip_universe.store_length = putUChar; + fqdn_universe.enc_opt = &dhcp_options [DHO_NWIP_SUBOPTIONS]; + nwip_universe.index = universe_count++; + universes [nwip_universe.index] = &nwip_universe; + option_new_hash (&nwip_universe.hash, 1, MDL); + if (!nwip_universe.hash) + log_fatal ("Can't allocate nwip option hash table."); + for (i = 0; i < 256; i++) { + nwip_universe.options [i] = &nwip_options [i]; + option_hash_add (nwip_universe.hash, + nwip_options [i].name, 0, + &nwip_options [i], MDL); + } + + /* Set up the FQDN option universe... */ + fqdn_universe.name = "fqdn"; + fqdn_universe.lookup_func = lookup_linked_option; + fqdn_universe.option_state_dereference = + linked_option_state_dereference; + fqdn_universe.save_func = save_linked_option; + fqdn_universe.delete_func = delete_linked_option; + fqdn_universe.encapsulate = fqdn_option_space_encapsulate; + fqdn_universe.foreach = linked_option_space_foreach; + fqdn_universe.decode = fqdn_universe_decode; + fqdn_universe.length_size = 1; + fqdn_universe.tag_size = 1; + fqdn_universe.store_tag = putUChar; + fqdn_universe.store_length = putUChar; + fqdn_universe.index = universe_count++; + fqdn_universe.enc_opt = &dhcp_options [DHO_FQDN]; + universes [fqdn_universe.index] = &fqdn_universe; + option_new_hash (&fqdn_universe.hash, 1, MDL); + if (!fqdn_universe.hash) + log_fatal ("Can't allocate fqdn option hash table."); + for (i = 0; i < 256; i++) { + fqdn_universe.options [i] = &fqdn_options [i]; + option_hash_add (fqdn_universe.hash, + fqdn_options [i].name, 0, + &fqdn_options [i], MDL); } - universe_hash.hash_count = DEFAULT_HASH_SIZE; - add_hash (&universe_hash, - (unsigned char *)dhcp_universe.name, 0, - (unsigned char *)&dhcp_universe); + + /* Set up the hash of universes. */ + universe_new_hash (&universe_hash, 1, MDL); + universe_hash_add (universe_hash, + dhcp_universe.name, 0, + &dhcp_universe, MDL); + universe_hash_add (universe_hash, + nwip_universe.name, 0, + &nwip_universe, MDL); + universe_hash_add (universe_hash, + fqdn_universe.name, 0, + &fqdn_universe, MDL); } diff --git a/contrib/isc-dhcp/common/tr.c b/contrib/isc-dhcp/common/tr.c index b19c183..3352d0a 100644 --- a/contrib/isc-dhcp/common/tr.c +++ b/contrib/isc-dhcp/common/tr.c @@ -1,8 +1,45 @@ +/* tr.c + + token ring interface support + Contributed in May of 1999 by Andrew Chittenden */ + /* - * packet_tr.c - token ring interface code, contributed in May of 1999 - * by Andrew Chittenden + * 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. */ +#ifndef lint +static char copyright[] = +"$Id: tr.c,v 1.7 2001/04/27 22:23:02 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n"; +#endif /* not lint */ + #include "dhcpd.h" #if defined (HAVE_TR_SUPPORT) && \ @@ -44,7 +81,7 @@ static struct timeval routing_timer; void assemble_tr_header (interface, buf, bufix, to) struct interface_info *interface; unsigned char *buf; - int *bufix; + unsigned *bufix; struct hardware *to; { struct trh_hdr *trh; @@ -54,14 +91,14 @@ void assemble_tr_header (interface, buf, bufix, to) /* set up the token header */ trh = (struct trh_hdr *) &buf[*bufix]; - if (interface -> hw_address.hlen == sizeof (trh->saddr)) - memcpy (trh->saddr, interface -> hw_address.haddr, + if (interface -> hw_address.hlen - 1 == sizeof (trh->saddr)) + memcpy (trh->saddr, &interface -> hw_address.hbuf [1], sizeof (trh->saddr)); else memset (trh->saddr, 0x00, sizeof (trh->saddr)); - if (to && to -> hlen == 6) /* XXX */ - memcpy (trh->daddr, to -> haddr, sizeof trh->daddr); + if (to && to -> hlen == 7) /* XXX */ + memcpy (trh->daddr, &to -> hbuf [1], sizeof trh->daddr); else memset (trh->daddr, 0xff, sizeof (trh->daddr)); @@ -97,7 +134,7 @@ static unsigned char tr_broadcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; ssize_t decode_tr_header (interface, buf, bufix, from) struct interface_info *interface; unsigned char *buf; - int bufix; + unsigned bufix; struct hardware *from; { struct trh_hdr *trh = (struct trh_hdr *) buf + bufix; @@ -143,14 +180,14 @@ ssize_t decode_tr_header (interface, buf, bufix, from) * filter code in bpf.c */ llc = (struct trllc *)(buf + bufix + hdr_len); ip = (struct ip *) (llc + 1); - udp = (struct udphdr *) ((unsigned char*) ip + ip->ip_hl * 4); + udp = (struct udphdr *) ((unsigned char*) ip + IP_HL (ip)); /* make sure it is a snap encoded, IP, UDP, unfragmented packet sent * to our port */ if (llc->dsap != EXTENDED_SAP || ntohs(llc->ethertype) != ETHERTYPE_IP || ip->ip_p != IPPROTO_UDP - || (ip->ip_off & IP_OFFMASK) != 0 + || (ntohs (ip->ip_off) & IP_OFFMASK) != 0 || udp->uh_dport != local_port) return -1; @@ -241,8 +278,8 @@ static void save_source_routing(trh, interface) /* found an entry so update it with fresh information */ if (rover != NULL) { - if ((trh->saddr[0] & TR_RII) - && (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2)) { + if ((trh->saddr[0] & TR_RII) && + ((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) { rcf = ntohs(trh->rcf); rcf &= ~TR_RCF_BROADCAST_MASK; memcpy(rover->rseg, trh->rseg, sizeof(rover->rseg)); @@ -253,7 +290,7 @@ static void save_source_routing(trh, interface) } /* no entry found, so create one */ - rover = malloc(sizeof(struct routing_entry)); + rover = dmalloc (sizeof (struct routing_entry), MDL); if (rover == NULL) { fprintf(stderr, "%s: unable to save source routing information\n", @@ -294,7 +331,7 @@ static void expire_routes() while((rover = *prover) != NULL) { if ((now.tv_sec - rover->access_time) > routing_timeout) { *prover = rover->next; - free(rover); + dfree (rover, MDL); } else prover = &rover->next; } diff --git a/contrib/isc-dhcp/common/tree.c b/contrib/isc-dhcp/common/tree.c index ac83d67..a9254cc 100644 --- a/contrib/isc-dhcp/common/tree.c +++ b/contrib/isc-dhcp/common/tree.c @@ -3,7 +3,7 @@ Routines for manipulating parse trees... */ /* - * 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,281 +34,317 @@ * 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: tree.c,v 1.10 1997/05/09 08:14:57 mellon Exp $ Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. All rights reserved.\n"; +"$Id: tree.c,v 1.101.2.6 2001/10/18 20:12:16 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" +#include <omapip/omapip_p.h> -static TIME tree_evaluate_recurse PROTO ((int *, unsigned char **, int *, - struct tree *)); -static TIME do_host_lookup PROTO ((int *, unsigned char **, int *, - struct dns_host_entry *)); -static void do_data_copy PROTO ((int *, unsigned char **, int *, - unsigned char *, int)); +struct binding_scope *global_scope; + +static int do_host_lookup PROTO ((struct data_string *, + struct dns_host_entry *)); + +#ifdef NSUPDATE +struct __res_state resolver_state; +int resolver_inited = 0; +#endif pair cons (car, cdr) caddr_t car; pair cdr; { - pair foo = (pair)dmalloc (sizeof *foo, "cons"); + pair foo = (pair)dmalloc (sizeof *foo, MDL); if (!foo) - error ("no memory for cons."); + log_fatal ("no memory for cons."); foo -> car = car; foo -> cdr = cdr; return foo; } -struct tree_cache *tree_cache (tree) - struct tree *tree; +int make_const_option_cache (oc, buffer, data, len, option, file, line) + struct option_cache **oc; + struct buffer **buffer; + u_int8_t *data; + unsigned len; + struct option *option; + const char *file; + int line; { - struct tree_cache *tc; + struct buffer *bp; + + if (buffer) { + bp = *buffer; + *buffer = 0; + } else { + bp = (struct buffer *)0; + if (!buffer_allocate (&bp, len, file, line)) { + log_error ("%s(%d): can't allocate buffer.", + file, line); + return 0; + } + } - tc = new_tree_cache ("tree_cache"); - if (!tc) + if (!option_cache_allocate (oc, file, line)) { + log_error ("%s(%d): can't allocate option cache.", file, line); + buffer_dereference (&bp, file, line); return 0; - tc -> value = (unsigned char *)0; - tc -> len = tc -> buf_size = 0; - tc -> timeout = 0; - tc -> tree = tree; - return tc; + } + + (*oc) -> data.len = len; + (*oc) -> data.buffer = bp; + (*oc) -> data.data = &bp -> data [0]; + (*oc) -> data.terminated = 0; + if (data) + memcpy (&bp -> data [0], data, len); + (*oc) -> option = option; + return 1; } -struct tree *tree_host_lookup (name) - char *name; +int make_host_lookup (expr, name) + struct expression **expr; + const char *name; { - struct tree *nt; - nt = new_tree ("tree_host_lookup"); - if (!nt) - error ("No memory for host lookup tree node."); - nt -> op = TREE_HOST_LOOKUP; - nt -> data.host_lookup.host = enter_dns_host (name); - return nt; + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for host lookup tree node."); + return 0; + } + (*expr) -> op = expr_host_lookup; + if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) { + expression_dereference (expr, MDL); + return 0; + } + return 1; } -struct dns_host_entry *enter_dns_host (name) - char *name; +int enter_dns_host (dh, name) + struct dns_host_entry **dh; + const char *name; { - struct dns_host_entry *dh; - - if (!(dh = (struct dns_host_entry *)dmalloc - (sizeof (struct dns_host_entry), "enter_dns_host")) - || !(dh -> hostname = dmalloc (strlen (name) + 1, - "enter_dns_host"))) - error ("Can't allocate space for new host."); - strcpy (dh -> hostname, name); - dh -> data = (unsigned char *)0; - dh -> data_len = 0; - dh -> buf_len = 0; - dh -> timeout = 0; - return dh; + /* XXX This should really keep a hash table of hostnames + XXX and just add a new reference to a hostname that + XXX already exists, if possible, rather than creating + XXX a new structure. */ + if (!dns_host_entry_allocate (dh, name, MDL)) { + log_error ("Can't allocate space for new host."); + return 0; + } + return 1; } -struct tree *tree_const (data, len) - unsigned char *data; - int len; +int make_const_data (struct expression **expr, const unsigned char *data, + unsigned len, int terminated, int allocate, + const char *file, int line) { - struct tree *nt; - if (!(nt = new_tree ("tree_const")) - || !(nt -> data.const_val.data = - (unsigned char *)dmalloc (len, "tree_const"))) - error ("No memory for constant data tree node."); - nt -> op = TREE_CONST; - memcpy (nt -> data.const_val.data, data, len); - nt -> data.const_val.len = len; - return nt; + struct expression *nt; + + if (!expression_allocate (expr, file, line)) { + log_error ("No memory for make_const_data tree node."); + return 0; + } + nt = *expr; + + if (len) { + if (allocate) { + if (!buffer_allocate (&nt -> data.const_data.buffer, + len + terminated, file, line)) { + log_error ("Can't allocate const_data buffer"); + expression_dereference (expr, file, line); + return 0; + } + nt -> data.const_data.data = + &nt -> data.const_data.buffer -> data [0]; + memcpy (nt -> data.const_data.buffer -> data, + data, len + terminated); + } else + nt -> data.const_data.data = data; + nt -> data.const_data.terminated = terminated; + } else + nt -> data.const_data.data = 0; + + nt -> op = expr_const_data; + nt -> data.const_data.len = len; + return 1; } -struct tree *tree_concat (left, right) - struct tree *left, *right; +int make_const_int (expr, val) + struct expression **expr; + unsigned long val; { - struct tree *nt; + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for make_const_int tree node."); + return 0; + } + + (*expr) -> op = expr_const_int; + (*expr) -> data.const_int = val; + return 1; +} +int make_concat (expr, left, right) + struct expression **expr; + struct expression *left, *right; +{ /* If we're concatenating a null tree to a non-null tree, just return the non-null tree; if both trees are null, return a null tree. */ - if (!left) - return right; - if (!right) - return left; - - /* If both trees are constant, combine them. */ - if (left -> op == TREE_CONST && right -> op == TREE_CONST) { - unsigned char *buf = dmalloc (left -> data.const_val.len - + right -> data.const_val.len, - "tree_concat"); - if (!buf) - error ("No memory to concatenate constants."); - memcpy (buf, left -> data.const_val.data, - left -> data.const_val.len); - memcpy (buf + left -> data.const_val.len, - right -> data.const_val.data, - right -> data.const_val.len); - dfree (left -> data.const_val.data, "tree_concat"); - dfree (right -> data.const_val.data, "tree_concat"); - left -> data.const_val.data = buf; - left -> data.const_val.len += right -> data.const_val.len; - free_tree (right, "tree_concat"); - return left; + if (!left) { + if (!right) + return 0; + expression_reference (expr, right, MDL); + return 1; + } + if (!right) { + expression_reference (expr, left, MDL); + return 1; } /* Otherwise, allocate a new node to concatenate the two. */ - if (!(nt = new_tree ("tree_concat"))) - error ("No memory for data tree concatenation node."); - nt -> op = TREE_CONCAT; - nt -> data.concat.left = left; - nt -> data.concat.right = right; - return nt; + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for concatenation expression node."); + return 0; + } + + (*expr) -> op = expr_concat; + expression_reference (&(*expr) -> data.concat [0], left, MDL); + expression_reference (&(*expr) -> data.concat [1], right, MDL); + return 1; } -struct tree *tree_limit (tree, limit) - struct tree *tree; - int limit; +int make_encapsulation (expr, name) + struct expression **expr; + struct data_string *name; { - struct tree *rv; - - /* If the tree we're limiting is constant, limit it now. */ - if (tree -> op == TREE_CONST) { - if (tree -> data.const_val.len > limit) - tree -> data.const_val.len = limit; - return tree; + /* Allocate a new node to store the encapsulation. */ + if (!expression_allocate (expr, MDL)) { + log_error ("No memory for encapsulation expression node."); + return 0; } - - /* Otherwise, put in a node which enforces the limit on evaluation. */ - rv = new_tree ("tree_limit"); - if (!rv) - return (struct tree *)0; - rv -> op = TREE_LIMIT; - rv -> data.limit.tree = tree; - rv -> data.limit.limit = limit; - return rv; + + (*expr) -> op = expr_encapsulate; + data_string_copy (&(*expr) -> data.encapsulate, name, MDL); + return 1; } -int tree_evaluate (tree_cache) - struct tree_cache *tree_cache; +int make_substring (new, expr, offset, length) + struct expression **new; + struct expression *expr; + struct expression *offset; + struct expression *length; { - unsigned char *bp = tree_cache -> value; - int bc = tree_cache -> buf_size; - int bufix = 0; + /* Allocate an expression node to compute the substring. */ + if (!expression_allocate (new, MDL)) { + log_error ("no memory for substring expression."); + return 0; + } + (*new) -> op = expr_substring; + expression_reference (&(*new) -> data.substring.expr, expr, MDL); + expression_reference (&(*new) -> data.substring.offset, offset, MDL); + expression_reference (&(*new) -> data.substring.len, length, MDL); + return 1; +} - /* If there's no tree associated with this cache, it evaluates - to a constant and that was detected at startup. */ - if (!tree_cache -> tree) - return 1; +int make_limit (new, expr, limit) + struct expression **new; + struct expression *expr; + int limit; +{ + struct expression *rv; - /* Try to evaluate the tree without allocating more memory... */ - tree_cache -> timeout = tree_evaluate_recurse (&bufix, &bp, &bc, - tree_cache -> tree); + /* Allocate a node to enforce a limit on evaluation. */ + if (!expression_allocate (new, MDL)) + log_error ("no memory for limit expression"); + (*new) -> op = expr_substring; + expression_reference (&(*new) -> data.substring.expr, expr, MDL); - /* No additional allocation needed? */ - if (bufix <= bc) { - tree_cache -> len = bufix; - return 1; + /* Offset is a constant 0. */ + if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) { + log_error ("no memory for limit offset expression"); + expression_dereference (new, MDL); + return 0; } + (*new) -> data.substring.offset -> op = expr_const_int; + (*new) -> data.substring.offset -> data.const_int = 0; - /* If we can't allocate more memory, return with what we - have (maybe nothing). */ - if (!(bp = (unsigned char *)dmalloc (bufix, "tree_evaluate"))) + /* Length is a constant: the specified limit. */ + if (!expression_allocate (&(*new) -> data.substring.len, MDL)) { + log_error ("no memory for limit length expression"); + expression_dereference (new, MDL); return 0; + } + (*new) -> data.substring.len -> op = expr_const_int; + (*new) -> data.substring.len -> data.const_int = limit; - /* Record the change in conditions... */ - bc = bufix; - bufix = 0; - - /* Note that the size of the result shouldn't change on the - second call to tree_evaluate_recurse, since we haven't - changed the ``current'' time. */ - tree_evaluate_recurse (&bufix, &bp, &bc, tree_cache -> tree); - - /* Free the old buffer if needed, then store the new buffer - location and size and return. */ - if (tree_cache -> value) - dfree (tree_cache -> value, "tree_evaluate"); - tree_cache -> value = bp; - tree_cache -> len = bufix; - tree_cache -> buf_size = bc; return 1; } -static TIME tree_evaluate_recurse (bufix, bufp, bufcount, tree) - int *bufix; - unsigned char **bufp; - int *bufcount; - struct tree *tree; +int option_cache (struct option_cache **oc, struct data_string *dp, + struct expression *expr, struct option *option, + const char *file, int line) { - int limit; - TIME t1, t2; - - switch (tree -> op) { - case TREE_CONCAT: - t1 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.concat.left); - t2 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.concat.right); - if (t1 > t2) - return t2; - return t1; - - case TREE_HOST_LOOKUP: - return do_host_lookup (bufix, bufp, bufcount, - tree -> data.host_lookup.host); - - case TREE_CONST: - do_data_copy (bufix, bufp, bufcount, - tree -> data.const_val.data, - tree -> data.const_val.len); - t1 = MAX_TIME; - return t1; - - case TREE_LIMIT: - limit = *bufix + tree -> data.limit.limit; - t1 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.limit.tree); - *bufix = limit; - return t1; + if (!option_cache_allocate (oc, file, line)) + return 0; + if (dp) + data_string_copy (&(*oc) -> data, dp, file, line); + if (expr) + expression_reference (&(*oc) -> expression, expr, file, line); + (*oc) -> option = option; + return 1; +} - default: - warn ("Bad node id in tree: %d."); - t1 = MAX_TIME; - return t1; +int make_let (result, name) + struct executable_statement **result; + const char *name; +{ + if (!(executable_statement_allocate (result, MDL))) + return 0; + + (*result) -> op = let_statement; + (*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL); + if (!(*result) -> data.let.name) { + executable_statement_dereference (result, MDL); + return 0; } + strcpy ((*result) -> data.let.name, name); + return 1; } - -static TIME do_host_lookup (bufix, bufp, bufcount, dns) - int *bufix; - unsigned char **bufp; - int *bufcount; + +static int do_host_lookup (result, dns) + struct data_string *result; struct dns_host_entry *dns; { struct hostent *h; - int i; - int new_len; + unsigned i, count; + unsigned new_len; #ifdef DEBUG_EVAL - debug ("time: now = %d dns = %d %d diff = %d", + log_debug ("time: now = %d dns = %d diff = %d", cur_time, dns -> timeout, cur_time - dns -> timeout); #endif /* If the record hasn't timed out, just copy the data and return. */ if (cur_time <= dns -> timeout) { #ifdef DEBUG_EVAL - debug ("easy copy: %x %d %x", - dns -> data, dns -> data_len, - dns -> data ? *(int *)(dns -> data) : 0); + log_debug ("easy copy: %d %s", + dns -> data.len, + (dns -> data.len > 4 + ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) + : 0)); #endif - do_data_copy (bufix, bufp, bufcount, - dns -> data, dns -> data_len); - return dns -> timeout; + data_string_copy (result, &dns -> data, MDL); + return 1; } #ifdef DEBUG_EVAL - debug ("Looking up %s", dns -> hostname); + log_debug ("Looking up %s", dns -> hostname); #endif /* Otherwise, look it up... */ @@ -318,95 +354,3748 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) switch (h_errno) { case HOST_NOT_FOUND: #endif - warn ("%s: host unknown.", dns -> hostname); + log_error ("%s: host unknown.", dns -> hostname); #ifndef NO_H_ERRNO break; case TRY_AGAIN: - warn ("%s: temporary name server failure", - dns -> hostname); + log_error ("%s: temporary name server failure", + dns -> hostname); break; case NO_RECOVERY: - warn ("%s: name server failed", dns -> hostname); + log_error ("%s: name server failed", dns -> hostname); break; case NO_DATA: - warn ("%s: no A record associated with address", - dns -> hostname); + log_error ("%s: no A record associated with address", + dns -> hostname); } #endif /* !NO_H_ERRNO */ /* Okay to try again after a minute. */ - return cur_time + 60; + dns -> timeout = cur_time + 60; + data_string_forget (&dns -> data, MDL); + return 0; } #ifdef DEBUG_EVAL - debug ("Lookup succeeded; first address is %x", - h -> h_addr_list [0]); + log_debug ("Lookup succeeded; first address is %s", + inet_ntoa (h -> h_addr_list [0])); #endif /* Count the number of addresses we got... */ - for (i = 0; h -> h_addr_list [i]; i++) + for (count = 0; h -> h_addr_list [count]; count++) ; + /* Dereference the old data, if any. */ + data_string_forget (&dns -> data, MDL); + /* Do we need to allocate more memory? */ - new_len = i * h -> h_length; - if (dns -> buf_len < i) { - unsigned char *buf = - (unsigned char *)dmalloc (new_len, "do_host_lookup"); - /* If we didn't get more memory, use what we have. */ - if (!buf) { - new_len = dns -> buf_len; - if (!dns -> buf_len) { - dns -> timeout = cur_time + 60; - return dns -> timeout; - } - } else { - if (dns -> data) - dfree (dns -> data, "do_host_lookup"); - dns -> data = buf; - dns -> buf_len = new_len; - } + new_len = count * h -> h_length; + if (!buffer_allocate (&dns -> data.buffer, new_len, MDL)) + { + log_error ("No memory for %s.", dns -> hostname); + return 0; } + dns -> data.data = &dns -> data.buffer -> data [0]; + dns -> data.len = new_len; + dns -> data.terminated = 0; + /* Addresses are conveniently stored one to the buffer, so we have to copy them out one at a time... :'( */ - for (i = 0; i < new_len / h -> h_length; i++) { - memcpy (dns -> data + h -> h_length * i, - h -> h_addr_list [i], h -> h_length); + for (i = 0; i < count; i++) { + memcpy (&dns -> data.buffer -> data [h -> h_length * i], + h -> h_addr_list [i], (unsigned)(h -> h_length)); } #ifdef DEBUG_EVAL - debug ("dns -> data: %x h -> h_addr_list [0]: %x", - *(int *)(dns -> data), h -> h_addr_list [0]); + log_debug ("dns -> data: %x h -> h_addr_list [0]: %x", + *(int *)(dns -> buffer), h -> h_addr_list [0]); #endif - dns -> data_len = new_len; - /* Set the timeout for an hour from now. + /* XXX Set the timeout for an hour from now. XXX This should really use the time on the DNS reply. */ dns -> timeout = cur_time + 3600; #ifdef DEBUG_EVAL - debug ("hard copy: %x %d %x", - dns -> data, dns -> data_len, *(int *)(dns -> data)); + log_debug ("hard copy: %d %s", dns -> data.len, + (dns -> data.len > 4 + ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0)); +#endif + data_string_copy (result, &dns -> data, MDL); + return 1; +} + +int evaluate_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr, file, line) + struct binding_value **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; + const char *file; + int line; +{ + struct binding_value *bv; + int status; + struct binding *binding; + + bv = (struct binding_value *)0; + + if (expr -> op == expr_variable_reference) { + if (!scope || !*scope) + return 0; + + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (result) + binding_value_reference (result, + binding -> value, + file, line); + return 1; + } else + return 0; + } else if (expr -> op == expr_funcall) { + struct string_list *s; + struct expression *arg; + struct binding_scope *ns; + struct binding *nb; + + if (!scope || !*scope) { + log_error ("%s: no such function.", + expr -> data.funcall.name); + return 0; + } + + binding = find_binding (*scope, expr -> data.funcall.name); + + if (!binding || !binding -> value) { + log_error ("%s: no such function.", + expr -> data.funcall.name); + return 0; + } + if (binding -> value -> type != binding_function) { + log_error ("%s: not a function.", + expr -> data.funcall.name); + return 0; + } + + /* Create a new binding scope in which to define + the arguments to the function. */ + ns = (struct binding_scope *)0; + if (!binding_scope_allocate (&ns, MDL)) { + log_error ("%s: can't allocate argument scope.", + expr -> data.funcall.name); + return 0; + } + + arg = expr -> data.funcall.arglist; + s = binding -> value -> value.fundef -> args; + while (arg && s) { + nb = dmalloc (sizeof *nb, MDL); + if (!nb) { + blb: + binding_scope_dereference (&ns, MDL); + return 0; + } else { + memset (nb, 0, sizeof *nb); + nb -> name = dmalloc (strlen (s -> string) + 1, + MDL); + if (nb -> name) + strcpy (nb -> name, s -> string); + else { + dfree (nb, MDL); + nb = (struct binding *)0; + goto blb; + } + } + evaluate_expression (&nb -> value, packet, lease, + client_state, + in_options, cfg_options, scope, + arg -> data.arg.val, file, line); + nb -> next = ns -> bindings; + ns -> bindings = nb; + arg = arg -> data.arg.next; + s = s -> next; + } + if (arg) { + log_error ("%s: too many arguments.", + expr -> data.funcall.name); + binding_scope_dereference (&ns, MDL); + return 0; + } + if (s) { + log_error ("%s: too few arguments.", + expr -> data.funcall.name); + binding_scope_dereference (&ns, MDL); + return 0; + } + + if (scope && *scope) + binding_scope_reference (&ns -> outer, *scope, MDL); + + status = (execute_statements + (&bv, packet, + lease, client_state, in_options, cfg_options, &ns, + binding -> value -> value.fundef -> statements)); + binding_scope_dereference (&ns, MDL); + + if (!bv) + return 1; + } else if (is_boolean_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_boolean; + status = (evaluate_boolean_expression + (&bv -> value.boolean, packet, lease, client_state, + in_options, cfg_options, scope, expr)); + } else if (is_numeric_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_numeric; + status = (evaluate_numeric_expression + (&bv -> value.intval, packet, lease, client_state, + in_options, cfg_options, scope, expr)); + } else if (is_data_expression (expr)) { + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_data; + status = (evaluate_data_expression + (&bv -> value.data, packet, lease, client_state, + in_options, cfg_options, scope, expr, MDL)); + } else if (is_dns_expression (expr)) { +#if defined (NSUPDATE) + if (!binding_value_allocate (&bv, MDL)) + return 0; + bv -> type = binding_dns; + status = (evaluate_dns_expression + (&bv -> value.dns, packet, lease, client_state, + in_options, cfg_options, scope, expr)); +#endif + } else { + log_error ("%s: invalid expression type: %d", + "evaluate_expression", expr -> op); + return 0; + } + if (result && status) + binding_value_reference (result, bv, file, line); + binding_value_dereference (&bv, MDL); + + return status; +} + +int binding_value_dereference (struct binding_value **v, + const char *file, int line) +{ + struct binding_value *bv = *v; + + *v = (struct binding_value *)0; + + /* Decrement the reference count. If it's nonzero, we're + done. */ + --(bv -> refcnt); + rc_register (file, line, v, bv, bv -> refcnt, 1, RC_MISC); + if (bv -> refcnt > 0) + return 1; + if (bv -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (bv); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + switch (bv -> type) { + case binding_boolean: + case binding_numeric: + break; + case binding_data: + if (bv -> value.data.buffer) + data_string_forget (&bv -> value.data, file, line); + break; + case binding_dns: +#if defined (NSUPDATE) + if (bv -> value.dns) { + if (bv -> value.dns -> r_data) { + dfree (bv -> value.dns -> r_data_ephem, MDL); + bv -> value.dns -> r_data = (unsigned char *)0; + bv -> value.dns -> r_data_ephem = + (unsigned char *)0; + } + minires_freeupdrec (bv -> value.dns); + } + break; +#endif + default: + log_error ("%s(%d): invalid binding type: %d", + file, line, bv -> type); + return 0; + } + dfree (bv, file, line); + return 1; +} + +#if defined (NSUPDATE) +int evaluate_dns_expression (result, packet, lease, client_state, in_options, + cfg_options, scope, expr) + ns_updrec **result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + ns_updrec *foo; + unsigned long ttl = 0; + char *tname; + struct data_string name, data; + int r0, r1, r2, r3; + + if (!result || *result) { + log_error ("evaluate_dns_expression called with non-null %s", + "result pointer"); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + switch (expr -> op) { +#if defined (NSUPDATE) + case expr_ns_add: + r0 = evaluate_numeric_expression (&ttl, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.ns_add.ttl); + goto nsfinish; + + case expr_ns_exists: + ttl = 1; + + case expr_ns_delete: + case expr_ns_not_exists: + r0 = 1; + nsfinish: + memset (&name, 0, sizeof name); + r1 = evaluate_data_expression (&name, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.ns_add.rrname, + MDL); + if (r1) { + /* The result of the evaluation may or may not + be NUL-terminated, but we need it + terminated for sure, so we have to allocate + a buffer and terminate it. */ + tname = dmalloc (name.len + 1, MDL); + if (!tname) { + r2 = 0; + r1 = 0; + data_string_forget (&name, MDL); + } else { + memcpy (tname, name.data, name.len); + tname [name.len] = 0; + memset (&data, 0, sizeof data); + r2 = evaluate_data_expression + (&data, packet, lease, client_state, + in_options, cfg_options, scope, + expr -> data.ns_add.rrdata, MDL); + } + } else + r2 = 0; + if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) { + *result = minires_mkupdrec (((expr -> op == expr_ns_add || + expr -> op == expr_ns_delete) + ? S_UPDATE : S_PREREQ), + tname, + expr -> data.ns_add.rrclass, + expr -> data.ns_add.rrtype, + ttl); + if (!*result) { + ngood: + if (r2) { + data_string_forget (&data, MDL); + r2 = 0; + } + } else { + if (data.len) { + /* As a special case, if we get exactly + four bytes of data, it's an IP address + represented as a 32-bit quantity, which + is actually what we *should* be getting + here. Because res_mkupdrec is currently + broken and expects a dotted quad, convert + it. This should be fixed when the new + resolver is merged. */ + if (data.len == 4) { + (*result) -> r_data_ephem = + dmalloc (16, MDL); + if (!(*result) -> r_data_ephem) + goto dpngood; + (*result) -> r_data = + (*result) -> r_data_ephem; + sprintf ((char *)(*result) -> r_data_ephem, + "%d.%d.%d.%d", + data.data [0], data.data [1], + data.data [2], data.data [3]); + (*result) -> r_size = + strlen ((const char *) + (*result) -> r_data); + } else { + (*result) -> r_size = data.len; + (*result) -> r_data_ephem = + dmalloc (data.len, MDL); + if (!(*result) -> r_data_ephem) { + dpngood: /* double plus ungood. */ + minires_freeupdrec (*result); + *result = 0; + goto ngood; + } + (*result) -> r_data = + (*result) -> r_data_ephem; + memcpy ((*result) -> r_data_ephem, + data.data, data.len); + } + } else { + (*result) -> r_data = 0; + (*result) -> r_size = 0; + } + switch (expr -> op) { + case expr_ns_add: + (*result) -> r_opcode = ADD; + break; + case expr_ns_delete: + (*result) -> r_opcode = DELETE; + break; + case expr_ns_exists: + (*result) -> r_opcode = YXRRSET; + break; + case expr_ns_not_exists: + (*result) -> r_opcode = NXRRSET; + break; + + /* Can't happen, but satisfy gcc. */ + default: + break; + } + } + } + if (r1) { + data_string_forget (&name, MDL); + dfree (tname, MDL); + } + if (r2) + data_string_forget (&data, MDL); + /* One flaw in the thinking here: an IP address and an + ASCII string both look like data expressions, but + for A records, we want an ASCII string, not a + binary IP address. Do I need to turn binary IP + addresses into a seperate type? */ + return (r0 && r1 && + (r2 || expr -> op != expr_ns_add) && *result); + +#else + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + return 0; +#endif + case expr_funcall: + log_error ("%s: dns values for functions not supported.", + expr -> data.funcall.name); + break; + + case expr_variable_reference: + log_error ("%s: dns values for variables not supported.", + expr -> data.variable); + break; + + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_none: + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + log_error ("Data opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("Function opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_dns_expression: %d", + expr -> op); + return 0; +} +#endif /* defined (NSUPDATE) */ + +int evaluate_boolean_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr) + int *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + struct data_string left, right; + struct data_string rrtype, rrname, rrdata; + unsigned long ttl; + int srrtype, srrname, srrdata, sttl; + int bleft, bright; + int sleft, sright; + struct binding *binding; + struct binding_value *bv, *obv; + + switch (expr -> op) { + case expr_check: + *result = check_collection (packet, lease, + expr -> data.check); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: check (%s) returns %s", + expr -> data.check -> name, + *result ? "true" : "false"); +#endif + return 1; + + case expr_equal: + case expr_not_equal: + bv = obv = (struct binding_value *)0; + sleft = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, scope, + expr -> data.equal [0], MDL); + sright = evaluate_expression (&obv, packet, lease, + client_state, in_options, + cfg_options, scope, + expr -> data.equal [1], MDL); + if (sleft && sright) { + if (bv -> type != obv -> type) + *result = expr -> op == expr_not_equal; + else { + switch (obv -> type) { + case binding_boolean: + if (bv -> value.boolean == obv -> value.boolean) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_data: + if ((bv -> value.data.len == + obv -> value.data.len) && + !memcmp (bv -> value.data.data, + obv -> value.data.data, + obv -> value.data.len)) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_numeric: + if (bv -> value.intval == obv -> value.intval) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + + case binding_dns: +#if defined (NSUPDATE) + /* XXX This should be a comparison for equal + XXX values, not for identity. */ + if (bv -> value.dns == obv -> value.dns) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; +#else + *result = expr -> op == expr_not_equal; +#endif + break; + + case binding_function: + if (bv -> value.fundef == obv -> value.fundef) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + break; + default: + *result = expr -> op == expr_not_equal; + break; + } + } + } else if (!sleft && !sright) + *result = expr -> op == expr_equal; + else + *result = expr -> op == expr_not_equal; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: %sequal = %s", + expr -> op == expr_not_equal ? "not" : "", + (*result ? "true" : "false")); +#endif + if (sleft) + binding_value_dereference (&bv, MDL); + if (sright) + binding_value_dereference (&obv, MDL); + return 1; + + case expr_and: + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + if (sleft && bleft) + sright = evaluate_boolean_expression + (&bright, packet, lease, client_state, + in_options, cfg_options, + scope, expr -> data.and [1]); + else + sright = bright = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: and (%s, %s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + sright ? (bright ? "true" : "false") : "NULL", + ((sleft && sright) + ? (bleft && bright ? "true" : "false") : "NULL")); +#endif + if (sleft && sright) { + *result = bleft && bright; + return 1; + } + return 0; + + case expr_or: + bleft = bright = 0; + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.or [0]); + if (!sleft || !bleft) + sright = evaluate_boolean_expression + (&bright, packet, lease, client_state, + in_options, cfg_options, + scope, expr -> data.or [1]); + else + sright = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: or (%s, %s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + sright ? (bright ? "true" : "false") : "NULL", + ((sleft || sright) + ? (bleft || bright ? "true" : "false") : "NULL")); +#endif + if (sleft || sright) { + *result = bleft || bright; + return 1; + } + return 0; + + case expr_not: + sleft = evaluate_boolean_expression (&bleft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.not); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: not (%s) = %s", + sleft ? (bleft ? "true" : "false") : "NULL", + ((sleft && sright) + ? (!bleft ? "true" : "false") : "NULL")); + +#endif + if (sleft) { + *result = !bleft; + return 1; + } + return 0; + + case expr_exists: + memset (&left, 0, sizeof left); + if (!in_options || + !get_option (&left, expr -> data.exists -> universe, + packet, lease, client_state, + in_options, cfg_options, in_options, + scope, expr -> data.exists -> code, MDL)) + *result = 0; + else { + *result = 1; + data_string_forget (&left, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: exists %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + *result ? "true" : "false"); +#endif + return 1; + + case expr_known: + if (!packet) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: known = NULL"); +#endif + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: known = %s", + packet -> known ? "true" : "false"); +#endif + *result = packet -> known; + return 1; + + case expr_static: + if (!lease || !(lease -> flags & STATIC_LEASE)) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: static = false (%s %s %s %d)", + lease ? "y" : "n", + (lease && (lease -> flags & STATIC_LEASE) + ? "y" : "n"), + piaddr (lease -> ip_addr), + lease ? lease -> flags : 0); +#endif + *result = 0; + return 1; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("bool: static = true"); +#endif + *result = 1; + return 1; + + case expr_variable_exists: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding) { + if (binding -> value) + *result = 1; + else + *result = 0; + } else + *result = 0; + } else + *result = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s? = %s", expr -> data.variable, + *result ? "true" : "false"); +#endif + return 1; + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == + binding_boolean) { + *result = binding -> value -> value.boolean; + sleft = 1; + } else { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_boolean_expression"); + sleft = 0; + } + } else + sleft = 0; + } else + sleft = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s = %s", expr -> data.variable, + sleft ? (*result ? "true" : "false") : "NULL"); +#endif + return sleft; + + case expr_funcall: + bv = (struct binding_value *)0; + sleft = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, + scope, expr, MDL); + if (sleft) { + if (bv -> type != binding_boolean) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_boolean_expression"); + else + *result = bv -> value.boolean; + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("boolean: %s() = %s", expr -> data.funcall.name, + sleft ? (*result ? "true" : "false") : "NULL"); +#endif + break; + + case expr_none: + case expr_match: + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + case expr_filename: + case expr_sname: + log_error ("Data opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_boolean_expr"); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; +} + +int evaluate_data_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr, file, line) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; + const char *file; + int line; +{ + struct data_string data, other; + unsigned long offset, len, i; + int s0, s1, s2, s3; + int status; + struct binding *binding; + char *s; + struct binding_value *bv; + + switch (expr -> op) { + /* Extract N bytes starting at byte M of a data string. */ + case expr_substring: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.substring.expr, + MDL); + + /* Evaluate the offset and length. */ + s1 = evaluate_numeric_expression + (&offset, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.substring.offset); + s2 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.substring.len); + + if (s0 && s1 && s2) { + /* If the offset is after end of the string, + return an empty string. Otherwise, do the + adjustments and return what's left. */ + if (data.len > offset) { + data_string_copy (result, &data, file, line); + result -> len -= offset; + if (result -> len > len) { + result -> len = len; + result -> terminated = 0; + } + result -> data += offset; + } + s3 = 1; + } else + s3 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: substring (%s, %s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s1 ? print_dec_1 (offset) : "NULL", + s2 ? print_dec_2 (len) : "NULL", + (s3 ? print_hex_2 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s0) + data_string_forget (&data, MDL); + if (s3) + return 1; + return 0; + + + /* Extract the last N bytes of a data string. */ + case expr_suffix: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.suffix.expr, MDL); + /* Evaluate the length. */ + s1 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.suffix.len); + if (s0 && s1) { + data_string_copy (result, &data, file, line); + + /* If we are returning the last N bytes of a + string whose length is <= N, just return + the string - otherwise, compute a new + starting address and decrease the + length. */ + if (data.len > len) { + result -> data += data.len - len; + result -> len = len; + } + data_string_forget (&data, MDL); + } + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: suffix (%s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s1 ? print_dec_1 (len) : "NULL", + ((s0 && s1) + ? print_hex_2 (result -> len, result -> data, 30) + : "NULL")); +#endif + return s0 && s1; + + /* Extract an option. */ + case expr_option: + if (in_options) + s0 = get_option (result, + expr -> data.option -> universe, + packet, lease, client_state, + in_options, cfg_options, in_options, + scope, expr -> data.option -> code, + file, line); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: option %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + s0 ? print_hex_1 (result -> len, result -> data, 60) + : "NULL"); +#endif + return s0; + + case expr_config_option: + if (cfg_options) + s0 = get_option (result, + expr -> data.option -> universe, + packet, lease, client_state, + in_options, cfg_options, cfg_options, + scope, expr -> data.option -> code, + file, line); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: config-option %s.%s = %s", + expr -> data.option -> universe -> name, + expr -> data.option -> name, + s0 ? print_hex_1 (result -> len, result -> data, 60) + : "NULL"); +#endif + return s0; + + /* Combine the hardware type and address. */ + case expr_hardware: + /* On the client, hardware is our hardware. */ + if (client_state) { + memset (result, 0, sizeof *result); + result -> data = + client_state -> interface -> hw_address.hbuf; + result -> len = + client_state -> interface -> hw_address.hlen; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: hardware = %s", + print_hex_1 (result -> len, + result -> data, 60)); +#endif + return 1; + } + + /* The server cares about the client's hardware address, + so only in the case where we are examining a packet can + we return anything. */ + if (!packet || !packet -> raw) { + log_error ("data: hardware: raw packet not available"); + return 0; + } + if (packet -> raw -> hlen > sizeof packet -> raw -> chaddr) { + log_error ("data: hardware: invalid hlen (%d)\n", + packet -> raw -> hlen); + return 0; + } + result -> len = packet -> raw -> hlen + 1; + if (buffer_allocate (&result -> buffer, result -> len, + file, line)) { + result -> data = &result -> buffer -> data [0]; + result -> buffer -> data [0] = packet -> raw -> htype; + memcpy (&result -> buffer -> data [1], + packet -> raw -> chaddr, + packet -> raw -> hlen); + result -> terminated = 0; + } else { + log_error ("data: hardware: no memory for buffer."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: hardware = %s", + print_hex_1 (result -> len, result -> data, 60)); +#endif + return 1; + + /* Extract part of the raw packet. */ + case expr_packet: + if (!packet || !packet -> raw) { + log_error ("data: packet: raw packet not available"); + return 0; + } + + s0 = evaluate_numeric_expression (&offset, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.packet.offset); + s1 = evaluate_numeric_expression (&len, + packet, lease, client_state, + in_options, cfg_options, + scope, + expr -> data.packet.len); + if (s0 && s1 && offset < packet -> packet_length) { + if (offset + len > packet -> packet_length) + result -> len = + packet -> packet_length - offset; + else + result -> len = len; + if (buffer_allocate (&result -> buffer, + result -> len, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (result -> buffer -> data, + (((unsigned char *)(packet -> raw)) + + offset), result -> len); + result -> terminated = 0; + } else { + log_error ("data: packet: no buffer memory."); + return 0; + } + s2 = 1; + } else + s2 = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: packet (%ld, %ld) = %s", + offset, len, + s2 ? print_hex_1 (result -> len, + result -> data, 60) : NULL); +#endif + return s2; + + /* The encapsulation of all defined options in an + option space... */ + case expr_encapsulate: + if (cfg_options) + s0 = option_space_encapsulate + (result, packet, lease, client_state, + in_options, cfg_options, scope, + &expr -> data.encapsulate); + else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: encapsulate (%s) = %s", + expr -> data.encapsulate.data, + s0 ? print_hex_1 (result -> len, + result -> data, 60) : "NULL"); +#endif + return s0; + + /* Some constant data... */ + case expr_const_data: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: const = %s", + print_hex_1 (expr -> data.const_data.len, + expr -> data.const_data.data, 60)); +#endif + data_string_copy (result, + &expr -> data.const_data, file, line); + return 1; + + /* Hostname lookup... */ + case expr_host_lookup: + s0 = do_host_lookup (result, expr -> data.host_lookup); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: DNS lookup (%s) = %s", + expr -> data.host_lookup -> hostname, + (s0 + ? print_dotted_quads (result -> len, result -> data) + : "NULL")); +#endif + return s0; + + /* Concatenation... */ + case expr_concat: + memset (&data, 0, sizeof data); + s0 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.concat [0], MDL); + memset (&other, 0, sizeof other); + s1 = evaluate_data_expression (&other, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.concat [1], MDL); + + if (s0 && s1) { + result -> len = data.len + other.len; + if (!buffer_allocate (&result -> buffer, + (result -> len + other.terminated), + file, line)) { + log_error ("data: concat: no memory"); + result -> len = 0; + data_string_forget (&data, MDL); + data_string_forget (&other, MDL); + return 0; + } + result -> data = &result -> buffer -> data [0]; + memcpy (result -> buffer -> data, data.data, data.len); + memcpy (&result -> buffer -> data [data.len], + other.data, other.len + other.terminated); + } + + if (s0) + data_string_forget (&data, MDL); + if (s1) + data_string_forget (&other, MDL); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: concat (%s, %s) = %s", + s0 ? print_hex_1 (data.len, data.data, 20) : "NULL", + s1 ? print_hex_2 (other.len, other.data, 20) : "NULL", + ((s0 && s1) + ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + return s0 && s1; + + case expr_encode_int8: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 1; + if (!buffer_allocate (&result -> buffer, + 1, file, line)) { + log_error ("data: encode_int8: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + result -> buffer -> data [0] = len; + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int8 (NULL) = NULL"); + else + log_debug ("data: encode_int8 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + + case expr_encode_int16: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 2; + if (!buffer_allocate (&result -> buffer, 2, + file, line)) { + log_error ("data: encode_int16: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + putUShort (result -> buffer -> data, len); + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int16 (NULL) = NULL"); + else + log_debug ("data: encode_int16 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + case expr_encode_int32: + s0 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.encode_int); + if (s0) { + result -> len = 4; + if (!buffer_allocate (&result -> buffer, 4, + file, line)) { + log_error ("data: encode_int32: no memory"); + result -> len = 0; + s0 = 0; + } else { + result -> data = &result -> buffer -> data [0]; + putULong (result -> buffer -> data, len); + } + } else + result -> len = 0; + +#if defined (DEBUG_EXPRESSIONS) + if (!s0) + log_debug ("data: encode_int32 (NULL) = NULL"); + else + log_debug ("data: encode_int32 (%ld) = %s", len, + print_hex_2 (result -> len, + result -> data, 20)); +#endif + return s0; + + case expr_binary_to_ascii: + /* Evaluate the base (offset) and width (len): */ + s0 = evaluate_numeric_expression + (&offset, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.b2a.base); + s1 = evaluate_numeric_expression (&len, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.b2a.width); + + /* Evaluate the seperator string. */ + memset (&data, 0, sizeof data); + s2 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.b2a.seperator, + MDL); + + /* Evaluate the data to be converted. */ + memset (&other, 0, sizeof other); + s3 = evaluate_data_expression (&other, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.b2a.buffer, MDL); + + if (s0 && s1 && s2 && s3) { + unsigned buflen, i; + + if (len != 8 && len != 16 && len != 32) { + log_info ("binary_to_ascii: %s %ld!", + "invalid width", len); + goto b2a_out; + } + len /= 8; + + /* The buffer must be a multiple of the number's + width. */ + if (other.len % len) { + log_info ("binary-to-ascii: %s %d %s %ld!", + "length of buffer", other.len, + "not a multiple of width", len); + status = 0; + goto b2a_out; + } + + /* Count the width of the output. */ + buflen = 0; + for (i = 0; i < other.len; i += len) { + if (len == 1) { + if (offset == 8) { + if (other.data [i] < 8) + buflen++; + else if (other.data [i] < 64) + buflen += 2; + else + buflen += 3; + } else if (offset == 10) { + if (other.data [i] < 10) + buflen++; + else if (other.data [i] < 100) + buflen += 2; + else + buflen += 3; + } else if (offset == 16) { + if (other.data [i] < 16) + buflen++; + else + buflen += 2; + } else + buflen += (converted_length + (&other.data [i], + offset, 1)); + } else + buflen += (converted_length + (&other.data [i], + offset, len)); + if (i + len != other.len) + buflen += data.len; + } + + if (!buffer_allocate (&result -> buffer, + buflen + 1, file, line)) { + log_error ("data: binary-to-ascii: no memory"); + status = 0; + goto b2a_out; + } + result -> data = &result -> buffer -> data [0]; + result -> len = buflen; + result -> terminated = 1; + + buflen = 0; + for (i = 0; i < other.len; i += len) { + buflen += (binary_to_ascii + (&result -> buffer -> data [buflen], + &other.data [i], offset, len)); + if (i + len != other.len) { + memcpy (&result -> + buffer -> data [buflen], + data.data, data.len); + buflen += data.len; + } + } + /* NUL terminate. */ + result -> buffer -> data [buflen] = 0; + status = 1; + } else + status = 0; + + b2a_out: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s", + s0 ? print_dec_1 (offset) : "NULL", + s1 ? print_dec_2 (len) : "NULL", + s2 ? print_hex_1 (data.len, data.data, 30) : "NULL", + s3 ? print_hex_2 (other.len, other.data, 30) : "NULL", + (status ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s2) + data_string_forget (&data, MDL); + if (s3) + data_string_forget (&other, MDL); + if (status) + return 1; + return 0; + + case expr_reverse: + /* Evaluate the width (len): */ + s0 = evaluate_numeric_expression + (&len, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.reverse.width); + + /* Evaluate the data. */ + memset (&data, 0, sizeof data); + s1 = evaluate_data_expression (&data, packet, lease, + client_state, + in_options, cfg_options, scope, + expr -> data.reverse.buffer, + MDL); + + if (s0 && s1) { + char *upper; + int i; + + /* The buffer must be a multiple of the number's + width. */ + if (data.len % len) { + log_info ("reverse: %s %d %s %ld!", + "length of buffer", data.len, + "not a multiple of width", len); + status = 0; + goto reverse_out; + } + + /* XXX reverse in place? I don't think we can. */ + if (!buffer_allocate (&result -> buffer, + data.len, file, line)) { + log_error ("data: reverse: no memory"); + status = 0; + goto reverse_out; + } + result -> data = &result -> buffer -> data [0]; + result -> len = data.len; + result -> terminated = 0; + + for (i = 0; i < data.len; i += len) { + memcpy (&result -> buffer -> data [i], + &data.data [data.len - i - len], len); + } + status = 1; + } else + status = 0; + + reverse_out: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: reverse (%s, %s) = %s", + s0 ? print_dec_1 (len) : "NULL", + s1 ? print_hex_1 (data.len, data.data, 30) : "NULL", + (status ? print_hex_3 (result -> len, result -> data, 30) + : "NULL")); +#endif + if (s0) + data_string_forget (&data, MDL); + if (status) + return 1; + return 0; + + case expr_leased_address: + if (!lease) { + log_error ("data: leased_address: not available"); + return 0; + } + result -> len = lease -> ip_addr.len; + if (buffer_allocate (&result -> buffer, result -> len, + file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + lease -> ip_addr.iabuf, lease -> ip_addr.len); + result -> terminated = 0; + } else { + log_error ("data: leased-address: no memory."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: leased-address = %s", + print_hex_1 (result -> len, result -> data, 60)); +#endif + return 1; + + case expr_pick_first_value: + memset (&data, 0, sizeof data); + if ((evaluate_data_expression + (result, packet, + lease, client_state, in_options, cfg_options, + scope, expr -> data.pick_first_value.car, MDL))) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (%s, xxx)", + print_hex_1 (result -> len, + result -> data, 40)); +#endif + return 1; + } + + if (expr -> data.pick_first_value.cdr && + (evaluate_data_expression + (result, packet, + lease, client_state, in_options, cfg_options, + scope, expr -> data.pick_first_value.cdr, MDL))) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (NULL, %s)", + print_hex_1 (result -> len, + result -> data, 40)); +#endif + return 1; + } + +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: pick_first_value (NULL, NULL) = NULL"); +#endif + return 0; + + case expr_host_decl_name: + if (!lease || !lease -> host) { + log_error ("data: host_decl_name: not available"); + return 0; + } + result -> len = strlen (lease -> host -> name); + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + strcpy ((char *)&result -> buffer -> data [0], + lease -> host -> name); + result -> terminated = 1; + } else { + log_error ("data: host-decl-name: no memory."); + return 0; + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: host-decl-name = %s", lease -> host -> name); +#endif + return 1; + + case expr_null: +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: null = NULL"); +#endif + return 0; + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == binding_data) { + data_string_copy (result, + &binding -> value -> value.data, + file, line); + s0 = 1; + } else if (binding -> value -> type != binding_data) { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_data_expression"); + s0 = 0; + } else + s0 = 0; + } else + s0 = 0; + } else + s0 = 0; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %s", expr -> data.variable, + s0 ? print_hex_1 (result -> len, + result -> data, 50) : "NULL"); +#endif + return s0; + + case expr_funcall: + bv = (struct binding_value *)0; + s0 = evaluate_expression (&bv, packet, lease, client_state, + in_options, cfg_options, + scope, expr, MDL); + if (s0) { + if (bv -> type != binding_data) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_data_expression"); + else + data_string_copy (result, &bv -> value.data, + file, line); + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %s", expr -> data.funcall.name, + s0 ? print_hex_1 (result -> len, + result -> data, 50) : "NULL"); +#endif + break; + + /* Extract the filename. */ + case expr_filename: + if (packet && packet -> raw -> file [0]) { + char *fn = + memchr (packet -> raw -> file, 0, + sizeof packet -> raw -> file); + if (!fn) + fn = ((char *)packet -> raw -> file + + sizeof packet -> raw -> file); + result -> len = fn - &(packet -> raw -> file [0]); + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + packet -> raw -> file, + result -> len); + result -> buffer -> data [result -> len] = 0; + result -> terminated = 1; + s0 = 1; + } else { + log_error ("data: filename: no memory."); + s0 = 0; + } + } else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_info ("data: filename = \"%s\"", + s0 ? (const char *)(result -> data) : "NULL"); +#endif + return s0; + + /* Extract the server name. */ + case expr_sname: + if (packet && packet -> raw -> sname [0]) { + char *fn = + memchr (packet -> raw -> sname, 0, + sizeof packet -> raw -> sname); + if (!fn) + fn = ((char *)packet -> raw -> sname + + sizeof packet -> raw -> sname); + result -> len = fn - &packet -> raw -> sname [0]; + if (buffer_allocate (&result -> buffer, + result -> len + 1, file, line)) { + result -> data = &result -> buffer -> data [0]; + memcpy (&result -> buffer -> data [0], + packet -> raw -> sname, + result -> len); + result -> buffer -> data [result -> len] = 0; + result -> terminated = 1; + s0 = 1; + } else { + log_error ("data: sname: no memory."); + s0 = 0; + } + } else + s0 = 0; + +#if defined (DEBUG_EXPRESSIONS) + log_info ("data: sname = \"%s\"", + s0 ? (const char *)(result -> data) : "NULL"); +#endif + return s0; + + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_none: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + case expr_lease_time: + case expr_dns_transaction: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + log_error ("Numeric opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns update opcode in evaluate_data_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_data_expression"); + return 0; + + case expr_arg: + break; + } + + log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op); + return 0; +} + +int evaluate_numeric_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, expr) + unsigned long *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + struct data_string data; + int status, sleft, sright; +#if defined (NSUPDATE) + ns_updrec *nut; + ns_updque uq; +#endif + struct expression *cur, *next; + struct binding *binding; + struct binding_value *bv; + unsigned long ileft, iright; + + switch (expr -> op) { + case expr_check: + case expr_equal: + case expr_not_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_match: + case expr_static: + case expr_known: + case expr_none: + case expr_exists: + case expr_variable_exists: + log_error ("Boolean opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_null: + log_error ("Data opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + memset (&data, 0, sizeof data); + status = evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL); + if (status) + *result = data.data [0]; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int8 (%s) = %s", + status ? print_hex_1 (data.len, data.data, 60) : "NULL", + status ? print_dec_1 (*result) : "NULL" ); +#endif + if (status) data_string_forget (&data, MDL); + return status; + + case expr_extract_int16: + memset (&data, 0, sizeof data); + status = (evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL)); + if (status && data.len >= 2) + *result = getUShort (data.data); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int16 (%s) = %ld", + ((status && data.len >= 2) ? + print_hex_1 (data.len, data.data, 60) : "NULL"), + *result); +#endif + if (status) data_string_forget (&data, MDL); + return (status && data.len >= 2); + + case expr_extract_int32: + memset (&data, 0, sizeof data); + status = (evaluate_data_expression + (&data, packet, lease, client_state, in_options, + cfg_options, scope, expr -> data.extract_int, MDL)); + if (status && data.len >= 4) + *result = getULong (data.data); +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: extract_int32 (%s) = %ld", + ((status && data.len >= 4) ? + print_hex_1 (data.len, data.data, 60) : "NULL"), + *result); +#endif + if (status) data_string_forget (&data, MDL); + return (status && data.len >= 4); + + case expr_const_int: + *result = expr -> data.const_int; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("number: CONSTANT = %ld", *result); +#endif + return 1; + + case expr_lease_time: + if (!lease) { + log_error ("data: leased_lease: not available"); + return 0; + } + if (lease -> ends < cur_time) { + log_error ("%s %lu when it is now %lu", + "data: lease_time: lease ends at", + (long)(lease -> ends), (long)cur_time); + return 0; + } + *result = lease -> ends - cur_time; +#if defined (DEBUG_EXPRESSIONS) + log_debug ("number: lease-time = (%lu - %lu) = %ld", + lease -> ends, + cur_time, *result); +#endif + return 1; + + case expr_dns_transaction: +#if !defined (NSUPDATE) + return 0; +#else + if (!resolver_inited) { + minires_ninit (&resolver_state); + resolver_inited = 1; + resolver_state.retrans = 1; + resolver_state.retry = 1; + } + ISC_LIST_INIT (uq); + cur = expr; + do { + next = cur -> data.dns_transaction.cdr; + nut = 0; + status = (evaluate_dns_expression + (&nut, packet, + lease, client_state, in_options, cfg_options, + scope, cur -> data.dns_transaction.car)); + if (!status) + goto dns_bad; + ISC_LIST_APPEND (uq, nut, r_link); + cur = next; + } while (next); + + /* Do the update and record the error code, if there was + an error; otherwise set it to NOERROR. */ + *result = minires_nupdate (&resolver_state, + ISC_LIST_HEAD (uq)); + status = 1; + + print_dns_status ((int)*result, &uq); + + dns_bad: + while (!ISC_LIST_EMPTY (uq)) { + ns_updrec *tmp = ISC_LIST_HEAD (uq); + ISC_LIST_UNLINK (uq, tmp, r_link); + if (tmp -> r_data_ephem) { + dfree (tmp -> r_data_ephem, MDL); + tmp -> r_data = (unsigned char *)0; + tmp -> r_data_ephem = (unsigned char *)0; + } + minires_freeupdrec (tmp); + } + return status; +#endif /* NSUPDATE */ + + case expr_variable_reference: + if (scope && *scope) { + binding = find_binding (*scope, expr -> data.variable); + + if (binding && binding -> value) { + if (binding -> value -> type == binding_numeric) { + *result = binding -> value -> value.intval; + status = 1; + } else { + log_error ("binding type %d in %s.", + binding -> value -> type, + "evaluate_numeric_expression"); + status = 0; + } + } else + status = 0; + } else + status = 0; +#if defined (DEBUG_EXPRESSIONS) + if (status) + log_debug ("numeric: %s = %ld", + expr -> data.variable, *result); + else + log_debug ("numeric: %s = NULL", + expr -> data.variable); +#endif + return status; + + case expr_funcall: + bv = (struct binding_value *)0; + status = evaluate_expression (&bv, packet, lease, + client_state, + in_options, cfg_options, + scope, expr, MDL); + if (status) { + if (bv -> type != binding_numeric) + log_error ("%s() returned type %d in %s.", + expr -> data.funcall.name, + bv -> type, + "evaluate_numeric_expression"); + else + *result = bv -> value.intval; + binding_value_dereference (&bv, MDL); + } +#if defined (DEBUG_EXPRESSIONS) + log_debug ("data: %s = %ld", expr -> data.funcall.name, + status ? *result : 0); +#endif + break; + + case expr_add: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld + %ld = %ld", + ileft, iright, ileft + iright); + else if (sleft) + log_debug ("num: %ld + NULL = NULL", ileft); + else + log_debug ("num: NULL + %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft + iright; + return 1; + } + return 0; + + case expr_subtract: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld - %ld = %ld", + ileft, iright, ileft - iright); + else if (sleft) + log_debug ("num: %ld - NULL = NULL", ileft); + else + log_debug ("num: NULL - %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft - iright; + return 1; + } + return 0; + + case expr_multiply: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld * %ld = %ld", + ileft, iright, ileft * iright); + else if (sleft) + log_debug ("num: %ld * NULL = NULL", ileft); + else + log_debug ("num: NULL * %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft * iright; + return 1; + } + return 0; + + case expr_divide: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) { + if (iright != 0) + log_debug ("num: %ld / %ld = %ld", + ileft, iright, ileft / iright); + else + log_debug ("num: %ld / %ld = NULL", + ileft, iright); + } else if (sleft) + log_debug ("num: %ld / NULL = NULL", ileft); + else + log_debug ("num: NULL / %ld = NULL", iright); #endif - do_data_copy (bufix, bufp, bufcount, dns -> data, dns -> data_len); - return dns -> timeout; + if (sleft && sright && iright) { + *result = ileft / iright; + return 1; + } + return 0; + + case expr_remainder: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) { + if (iright != 0) + log_debug ("num: %ld %% %ld = %ld", + ileft, iright, ileft % iright); + else + log_debug ("num: %ld %% %ld = NULL", + ileft, iright); + } else if (sleft) + log_debug ("num: %ld %% NULL = NULL", ileft); + else + log_debug ("num: NULL %% %ld = NULL", iright); +#endif + if (sleft && sright && iright) { + *result = ileft % iright; + return 1; + } + return 0; + + case expr_binary_and: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld | %ld = %ld", + ileft, iright, ileft & iright); + else if (sleft) + log_debug ("num: %ld & NULL = NULL", ileft); + else + log_debug ("num: NULL & %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft & iright; + return 1; + } + return 0; + + case expr_binary_or: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld | %ld = %ld", + ileft, iright, ileft | iright); + else if (sleft) + log_debug ("num: %ld | NULL = NULL", ileft); + else + log_debug ("num: NULL | %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft | iright; + return 1; + } + return 0; + + case expr_binary_xor: + sleft = evaluate_numeric_expression (&ileft, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [0]); + sright = evaluate_numeric_expression (&iright, packet, lease, + client_state, + in_options, cfg_options, + scope, + expr -> data.and [1]); + +#if defined (DEBUG_EXPRESSIONS) + if (sleft && sright) + log_debug ("num: %ld ^ %ld = %ld", + ileft, iright, ileft ^ iright); + else if (sleft) + log_debug ("num: %ld ^ NULL = NULL", ileft); + else + log_debug ("num: NULL ^ %ld = NULL", iright); +#endif + if (sleft && sright) { + *result = ileft ^ iright; + return 1; + } + return 0; + + case expr_client_state: + if (client_state) { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: client-state = %d", + client_state -> state); +#endif + *result = client_state -> state; + return 1; + } else { +#if defined (DEBUG_EXPRESSIONS) + log_debug ("num: client-state = NULL"); +#endif + return 0; + } + + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + log_error ("dns opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_function: + log_error ("function definition in evaluate_numeric_expr"); + return 0; + + case expr_arg: + break; + } + + log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op); + return 0; } -static void do_data_copy (bufix, bufp, bufcount, data, len) - int *bufix; - unsigned char **bufp; - int *bufcount; - unsigned char *data; - int len; +/* Return data hanging off of an option cache structure, or if there + isn't any, evaluate the expression hanging off of it and return the + result of that evaluation. There should never be both an expression + and a valid data_string. */ + +int evaluate_option_cache (result, packet, lease, client_state, + in_options, cfg_options, scope, oc, file, line) + struct data_string *result; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct option_cache *oc; + const char *file; + int line; { - int space = *bufcount - *bufix; + if (oc -> data.len) { + data_string_copy (result, &oc -> data, file, line); + return 1; + } + if (!oc -> expression) + return 0; + return evaluate_data_expression (result, packet, lease, client_state, + in_options, cfg_options, scope, + oc -> expression, file, line); +} + +/* Evaluate an option cache and extract a boolean from the result, + returning the boolean. Return false if there is no data. */ - /* If there's more space than we need, use only what we need. */ - if (space > len) - space = len; +int evaluate_boolean_option_cache (ignorep, packet, + lease, client_state, in_options, + cfg_options, scope, oc, file, line) + int *ignorep; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct option_cache *oc; + const char *file; + int line; +{ + struct data_string ds; + int result; - /* Copy as much data as will fit, then increment the buffer index - by the amount we actually had to copy, which could be more. */ - if (space > 0) - memcpy (*bufp + *bufix, data, space); - *bufix += len; + /* So that we can be called with option_lookup as an argument. */ + if (!oc || !in_options) + return 0; + + memset (&ds, 0, sizeof ds); + if (!evaluate_option_cache (&ds, packet, + lease, client_state, in_options, + cfg_options, scope, oc, file, line)) + return 0; + + if (ds.len) { + result = ds.data [0]; + if (result == 2) { + result = 0; + *ignorep = 1; + } else + *ignorep = 0; + } else + result = 0; + data_string_forget (&ds, MDL); + return result; } + + +/* Evaluate a boolean expression and return the result of the evaluation, + or FALSE if it failed. */ + +int evaluate_boolean_expression_result (ignorep, packet, lease, client_state, + in_options, cfg_options, scope, expr) + int *ignorep; + struct packet *packet; + struct lease *lease; + struct client_state *client_state; + struct option_state *in_options; + struct option_state *cfg_options; + struct binding_scope **scope; + struct expression *expr; +{ + int result; + + /* So that we can be called with option_lookup as an argument. */ + if (!expr) + return 0; + + if (!evaluate_boolean_expression (&result, packet, lease, client_state, + in_options, cfg_options, + scope, expr)) + return 0; + + if (result == 2) { + *ignorep = 1; + result = 0; + } else + *ignorep = 0; + return result; +} + + +/* Dereference an expression node, and if the reference count goes to zero, + dereference any data it refers to, and then free it. */ +void expression_dereference (eptr, file, line) + struct expression **eptr; + const char *file; + int line; +{ + struct expression *expr = *eptr; + + /* Zero the pointer. */ + *eptr = (struct expression *)0; + + /* Decrement the reference count. If it's nonzero, we're + done. */ + --(expr -> refcnt); + rc_register (file, line, eptr, expr, expr -> refcnt, 1, RC_MISC); + if (expr -> refcnt > 0) + return; + if (expr -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (expr); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return; +#endif + } + + /* Dereference subexpressions. */ + switch (expr -> op) { + /* All the binary operators can be handled the same way. */ + case expr_equal: + case expr_not_equal: + case expr_concat: + case expr_and: + case expr_or: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + if (expr -> data.equal [0]) + expression_dereference (&expr -> data.equal [0], + file, line); + if (expr -> data.equal [1]) + expression_dereference (&expr -> data.equal [1], + file, line); + break; + + case expr_substring: + if (expr -> data.substring.expr) + expression_dereference (&expr -> data.substring.expr, + file, line); + if (expr -> data.substring.offset) + expression_dereference (&expr -> data.substring.offset, + file, line); + if (expr -> data.substring.len) + expression_dereference (&expr -> data.substring.len, + file, line); + break; + + case expr_suffix: + if (expr -> data.suffix.expr) + expression_dereference (&expr -> data.suffix.expr, + file, line); + if (expr -> data.suffix.len) + expression_dereference (&expr -> data.suffix.len, + file, line); + break; + + case expr_not: + if (expr -> data.not) + expression_dereference (&expr -> data.not, file, line); + break; + + case expr_packet: + if (expr -> data.packet.offset) + expression_dereference (&expr -> data.packet.offset, + file, line); + if (expr -> data.packet.len) + expression_dereference (&expr -> data.packet.len, + file, line); + break; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + if (expr -> data.extract_int) + expression_dereference (&expr -> data.extract_int, + file, line); + break; + + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + if (expr -> data.encode_int) + expression_dereference (&expr -> data.encode_int, + file, line); + break; + + case expr_encapsulate: + case expr_const_data: + data_string_forget (&expr -> data.const_data, file, line); + break; + + case expr_host_lookup: + if (expr -> data.host_lookup) + dns_host_entry_dereference (&expr -> data.host_lookup, + file, line); + break; + + case expr_binary_to_ascii: + if (expr -> data.b2a.base) + expression_dereference (&expr -> data.b2a.base, + file, line); + if (expr -> data.b2a.width) + expression_dereference (&expr -> data.b2a.width, + file, line); + if (expr -> data.b2a.seperator) + expression_dereference (&expr -> data.b2a.seperator, + file, line); + if (expr -> data.b2a.buffer) + expression_dereference (&expr -> data.b2a.buffer, + file, line); + break; + + case expr_pick_first_value: + if (expr -> data.pick_first_value.car) + expression_dereference (&expr -> data.pick_first_value.car, + file, line); + if (expr -> data.pick_first_value.cdr) + expression_dereference (&expr -> data.pick_first_value.cdr, + file, line); + break; + + case expr_reverse: + if (expr -> data.reverse.width) + expression_dereference (&expr -> data.reverse.width, + file, line); + if (expr -> data.reverse.buffer) + expression_dereference + (&expr -> data.reverse.buffer, file, line); + break; + + case expr_dns_transaction: + if (expr -> data.dns_transaction.car) + expression_dereference (&expr -> data.dns_transaction.car, + file, line); + if (expr -> data.dns_transaction.cdr) + expression_dereference (&expr -> data.dns_transaction.cdr, + file, line); + break; + + case expr_ns_add: + if (expr -> data.ns_add.rrname) + expression_dereference (&expr -> data.ns_add.rrname, + file, line); + if (expr -> data.ns_add.rrdata) + expression_dereference (&expr -> data.ns_add.rrdata, + file, line); + if (expr -> data.ns_add.ttl) + expression_dereference (&expr -> data.ns_add.ttl, + file, line); + break; + + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + if (expr -> data.ns_delete.rrname) + expression_dereference (&expr -> data.ns_delete.rrname, + file, line); + if (expr -> data.ns_delete.rrdata) + expression_dereference (&expr -> data.ns_delete.rrdata, + file, line); + break; + + case expr_variable_reference: + case expr_variable_exists: + if (expr -> data.variable) + dfree (expr -> data.variable, file, line); + break; + + case expr_funcall: + if (expr -> data.funcall.name) + dfree (expr -> data.funcall.name, file, line); + if (expr -> data.funcall.arglist) + expression_dereference (&expr -> data.funcall.arglist, + file, line); + break; + + case expr_arg: + if (expr -> data.arg.val) + expression_dereference (&expr -> data.arg.val, + file, line); + if (expr -> data.arg.next) + expression_dereference (&expr -> data.arg.next, + file, line); + break; + + case expr_function: + fundef_dereference (&expr -> data.func, file, line); + break; + + /* No subexpressions. */ + case expr_leased_address: + case expr_lease_time: + case expr_filename: + case expr_sname: + case expr_const_int: + case expr_check: + case expr_option: + case expr_hardware: + case expr_exists: + case expr_known: + case expr_null: + break; + + default: + break; + } + free_expression (expr, MDL); +} + +int is_dns_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_ns_add || + expr -> op == expr_ns_delete || + expr -> op == expr_ns_exists || + expr -> op == expr_ns_not_exists); +} + +int is_boolean_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_check || + expr -> op == expr_exists || + expr -> op == expr_variable_exists || + expr -> op == expr_equal || + expr -> op == expr_not_equal || + expr -> op == expr_and || + expr -> op == expr_or || + expr -> op == expr_not || + expr -> op == expr_known || + expr -> op == expr_static); +} + +int is_data_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_substring || + expr -> op == expr_suffix || + expr -> op == expr_option || + expr -> op == expr_hardware || + expr -> op == expr_const_data || + expr -> op == expr_packet || + expr -> op == expr_concat || + expr -> op == expr_encapsulate || + expr -> op == expr_encode_int8 || + expr -> op == expr_encode_int16 || + expr -> op == expr_encode_int32 || + expr -> op == expr_host_lookup || + expr -> op == expr_binary_to_ascii || + expr -> op == expr_filename || + expr -> op == expr_sname || + expr -> op == expr_reverse || + expr -> op == expr_pick_first_value || + expr -> op == expr_host_decl_name || + expr -> op == expr_leased_address || + expr -> op == expr_config_option || + expr -> op == expr_null); +} + +int is_numeric_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_extract_int8 || + expr -> op == expr_extract_int16 || + expr -> op == expr_extract_int32 || + expr -> op == expr_const_int || + expr -> op == expr_lease_time || + expr -> op == expr_dns_transaction || + expr -> op == expr_add || + expr -> op == expr_subtract || + expr -> op == expr_multiply || + expr -> op == expr_divide || + expr -> op == expr_remainder || + expr -> op == expr_binary_and || + expr -> op == expr_binary_or || + expr -> op == expr_binary_xor || + expr -> op == expr_client_state); +} + +int is_compound_expression (expr) + struct expression *expr; +{ + return (expr -> op == expr_ns_add || + expr -> op == expr_ns_delete || + expr -> op == expr_ns_exists || + expr -> op == expr_ns_not_exists || + expr -> op == expr_substring || + expr -> op == expr_suffix || + expr -> op == expr_option || + expr -> op == expr_concat || + expr -> op == expr_encode_int8 || + expr -> op == expr_encode_int16 || + expr -> op == expr_encode_int32 || + expr -> op == expr_binary_to_ascii || + expr -> op == expr_reverse || + expr -> op == expr_pick_first_value || + expr -> op == expr_config_option || + expr -> op == expr_extract_int8 || + expr -> op == expr_extract_int16 || + expr -> op == expr_extract_int32 || + expr -> op == expr_dns_transaction); +} + +static int op_val PROTO ((enum expr_op)); + +static int op_val (op) + enum expr_op op; +{ + switch (op) { + case expr_none: + case expr_match: + case expr_static: + case expr_check: + case expr_substring: + case expr_suffix: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_not: + case expr_option: + case expr_hardware: + case expr_packet: + case expr_const_data: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_variable_exists: + case expr_known: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_lease_time: + case expr_dns_transaction: + case expr_null: + case expr_variable_reference: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_arg: + case expr_funcall: + case expr_function: + /* XXXDPN: Need to assign sane precedences to these. */ + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return 100; + + case expr_equal: + case expr_not_equal: + return 3; + + case expr_and: + case expr_multiply: + case expr_divide: + case expr_remainder: + return 1; + + case expr_or: + case expr_add: + case expr_subtract: + return 2; + } + return 100; +} + +int op_precedence (op1, op2) + enum expr_op op1, op2; +{ + int ov1, ov2; + + return op_val (op1) - op_val (op2); +} + +enum expression_context expression_context (struct expression *expr) +{ + if (is_data_expression (expr)) + return context_data; + if (is_numeric_expression (expr)) + return context_numeric; + if (is_boolean_expression (expr)) + return context_boolean; + if (is_dns_expression (expr)) + return context_dns; + return context_any; +} + +enum expression_context op_context (op) + enum expr_op op; +{ + switch (op) { +/* XXX Why aren't these specific? */ + case expr_none: + case expr_match: + case expr_static: + case expr_check: + case expr_substring: + case expr_suffix: + case expr_concat: + case expr_encapsulate: + case expr_host_lookup: + case expr_not: + case expr_option: + case expr_hardware: + case expr_packet: + case expr_const_data: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_variable_exists: + case expr_known: + case expr_binary_to_ascii: + case expr_reverse: + case expr_filename: + case expr_sname: + case expr_pick_first_value: + case expr_host_decl_name: + case expr_config_option: + case expr_leased_address: + case expr_lease_time: + case expr_null: + case expr_variable_reference: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_dns_transaction: + case expr_arg: + case expr_funcall: + case expr_function: + return context_any; + + case expr_equal: + case expr_not_equal: + return context_data; + + case expr_and: + return context_boolean; + + case expr_or: + return context_boolean; + + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return context_numeric; + } + return context_any; +} + +int write_expression (file, expr, col, indent, firstp) + FILE *file; + struct expression *expr; + int col; + int indent; + int firstp; +{ + struct expression *e; + const char *s; + char obuf [65]; + int scol; + int width; + + /* If this promises to be a fat expression, start a new line. */ + if (!firstp && is_compound_expression (expr)) { + indent_spaces (file, indent); + col = indent; + } + + switch (expr -> op) { + case expr_none: + col = token_print_indent (file, col, indent, "", "", "null"); + break; + + case expr_check: + col = token_print_indent (file, col, indent, "", "", "check"); + col = token_print_indent_concat (file, col, indent, + " ", "", "\"", + expr -> data.check -> name, + "\"", (char *)0); + break; + + case expr_not_equal: + s = "!="; + goto binary; + + case expr_equal: + s = "="; + binary: + col = write_expression (file, expr -> data.equal [0], + col, indent, 1); + col = token_print_indent (file, col, indent, " ", " ", s); + col = write_expression (file, expr -> data.equal [1], + col, indent + 2, 0); + break; + + case expr_substring: + col = token_print_indent (file, col, indent, "", "", + "substring"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.substring.expr, + col, scol, 1); + col = token_print_indent (file, col, indent, "", " ", ","); + col = write_expression (file, expr -> data.substring.offset, + col, indent, 0); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.substring.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_suffix: + col = token_print_indent (file, col, indent, "", "", "suffix"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.suffix.expr, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.suffix.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_concat: + e = expr; + col = token_print_indent (file, col, indent, "", "", + "concat"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + firstp = 1; + concat_again: + col = write_expression (file, e -> data.concat [0], + col, scol, firstp); + firstp = 0; + if (!e -> data.concat [1]) + goto no_concat_cdr; + col = token_print_indent (file, col, scol, "", " ", ","); + if (e -> data.concat [1] -> op == expr_concat) { + e = e -> data.concat [1]; + goto concat_again; + } + col = write_expression (file, e -> data.concat [1], + col, scol, 0); + no_concat_cdr: + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_host_lookup: + col = token_print_indent (file, col, indent, "", "", + "gethostbyname"); + col = token_print_indent (file, col, indent, " ", "", "("); + col = token_print_indent_concat + (file, col, indent, "", "", + "\"", expr -> data.host_lookup -> hostname, "\"", + (char *)0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_add: + s = "+"; + goto binary; + + case expr_subtract: + s = "-"; + goto binary; + + case expr_multiply: + s = "*"; + goto binary; + + case expr_divide: + s = "/"; + goto binary; + + case expr_remainder: + s = "%"; + goto binary; + + case expr_binary_and: + s = "&"; + goto binary; + + case expr_binary_or: + s = "|"; + goto binary; + + case expr_binary_xor: + s = "^"; + goto binary; + + case expr_and: + s = "and"; + goto binary; + + case expr_or: + s = "or"; + goto binary; + + case expr_not: + col = token_print_indent (file, col, indent, "", " ", "not"); + col = write_expression (file, + expr -> data.not, col, indent + 2, 1); + break; + + case expr_option: + s = "option"; + + print_option_name: + col = token_print_indent (file, col, indent, "", "", s); + + if (expr -> data.option -> universe != &dhcp_universe) { + col = token_print_indent (file, col, indent, + " ", "", + (expr -> data.option -> + universe -> name)); + col = token_print_indent (file, col, indent, "", "", + "."); + col = token_print_indent (file, col, indent, "", "", + expr -> data.option -> name); + } else { + col = token_print_indent (file, col, indent, " ", "", + expr -> data.option -> name); + } + break; + + case expr_hardware: + col = token_print_indent (file, col, indent, "", "", + "hardware"); + break; + + case expr_packet: + col = token_print_indent (file, col, indent, "", "", + "packet"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.packet.offset, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.packet.len, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_const_data: + col = token_indent_data_string (file, col, indent, "", "", + &expr -> data.const_data); + break; + + case expr_extract_int8: + width = 8; + extract_int: + col = token_print_indent (file, col, indent, "", "", + "extract-int"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.extract_int, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + sprintf (obuf, "%d", width); + col = token_print_indent (file, col, scol, " ", "", obuf); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_extract_int16: + width = 16; + goto extract_int; + + case expr_extract_int32: + width = 32; + goto extract_int; + + case expr_encode_int8: + width = 8; + encode_int: + col = token_print_indent (file, col, indent, "", "", + "encode-int"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.extract_int, + col, indent, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + sprintf (obuf, "%d", width); + col = token_print_indent (file, col, scol, " ", "", obuf); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_encode_int16: + width = 16; + goto encode_int; + + case expr_encode_int32: + width = 32; + goto encode_int; + + case expr_const_int: + sprintf (obuf, "%lu", expr -> data.const_int); + col = token_print_indent (file, col, indent, "", "", obuf); + break; + + case expr_exists: + s = "exists"; + goto print_option_name; + + case expr_encapsulate: + col = token_print_indent (file, col, indent, "", "", + "encapsulate"); + col = token_indent_data_string (file, col, indent, " ", "", + &expr -> data.encapsulate); + break; + + case expr_known: + col = token_print_indent (file, col, indent, "", "", "known"); + break; + + case expr_reverse: + col = token_print_indent (file, col, indent, "", "", + "reverse"); + col = token_print_indent (file, col, indent, " ", "", "("); + scol = col; + col = write_expression (file, expr -> data.reverse.width, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", ","); + col = write_expression (file, expr -> data.reverse.buffer, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_leased_address: + col = token_print_indent (file, col, indent, "", "", + "leased-address"); + break; + + case expr_client_state: + col = token_print_indent (file, col, indent, "", "", + "client-state"); + break; + + case expr_binary_to_ascii: + col = token_print_indent (file, col, indent, "", "", + "binary-to-ascii"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + col = write_expression (file, expr -> data.b2a.base, + col, scol, 1); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.width, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.seperator, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.b2a.buffer, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_config_option: + s = "config-option"; + goto print_option_name; + + case expr_host_decl_name: + col = token_print_indent (file, col, indent, "", "", + "host-decl-name"); + break; + + case expr_pick_first_value: + e = expr; + col = token_print_indent (file, col, indent, "", "", + "concat"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + firstp = 1; + pick_again: + col = write_expression (file, + e -> data.pick_first_value.car, + col, scol, firstp); + firstp = 0; + /* We're being very lisp-like right now - instead of + representing this expression as (first middle . last) we're + representing it as (first middle last), which means that the + tail cdr is always nil. Apologies to non-wisp-lizards - may + this obscure way of describing the problem motivate you to + learn more about the one true computing language. */ + if (!e -> data.pick_first_value.cdr) + goto no_pick_cdr; + col = token_print_indent (file, col, scol, "", " ", + ","); + if (e -> data.pick_first_value.cdr -> op == + expr_pick_first_value) { + e = e -> data.pick_first_value.cdr; + goto pick_again; + } + col = write_expression (file, + e -> data.pick_first_value.cdr, + col, scol, 0); + no_pick_cdr: + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_lease_time: + col = token_print_indent (file, col, indent, "", "", + "lease-time"); + break; + + case expr_dns_transaction: + col = token_print_indent (file, col, indent, "", "", + "ns-update"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = 0; + for (e = expr; + e && e -> op == expr_dns_transaction; + e = e -> data.dns_transaction.cdr) { + if (!scol) { + scol = col; + firstp = 1; + } else + firstp = 0; + col = write_expression (file, + e -> data.dns_transaction.car, + col, scol, firstp); + if (e -> data.dns_transaction.cdr) + col = token_print_indent (file, col, scol, + "", " ", ","); + } + if (e) + col = write_expression (file, e, col, scol, 0); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + case expr_ns_add: + col = token_print_indent (file, col, indent, "", "", + "update"); + col = token_print_indent (file, col, indent, " ", "", + "("); + scol = col; + sprintf (obuf, "%d", expr -> data.ns_add.rrclass); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + sprintf (obuf, "%d", expr -> data.ns_add.rrtype); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrname, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrdata, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.ttl, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_ns_delete: + col = token_print_indent (file, col, indent, "", "", + "delete"); + col = token_print_indent (file, col, indent, " ", "", + "("); + finish_ns_small: + scol = col; + sprintf (obuf, "%d", expr -> data.ns_add.rrclass); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + sprintf (obuf, "%d", expr -> data.ns_add.rrtype); + col = token_print_indent (file, col, scol, "", "", obuf); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrname, + col, scol, 0); + col = token_print_indent (file, col, scol, "", " ", + ","); + col = write_expression (file, expr -> data.ns_add.rrdata, + col, scol, 0); + col = token_print_indent (file, col, indent, "", "", + ")"); + break; + + case expr_ns_exists: + col = token_print_indent (file, col, indent, "", "", + "exists"); + col = token_print_indent (file, col, indent, " ", "", + "("); + goto finish_ns_small; + + case expr_ns_not_exists: + col = token_print_indent (file, col, indent, "", "", + "not exists"); + col = token_print_indent (file, col, indent, " ", "", + "("); + goto finish_ns_small; + + case expr_static: + col = token_print_indent (file, col, indent, "", "", + "static"); + break; + + case expr_null: + col = token_print_indent (file, col, indent, "", "", "null"); + break; + + case expr_variable_reference: + col = token_print_indent (file, indent, indent, "", "", + expr -> data.variable); + break; + + case expr_variable_exists: + col = token_print_indent (file, indent, indent, "", "", + "defined"); + col = token_print_indent (file, col, indent, " ", "", "("); + col = token_print_indent (file, col, indent, "", "", + expr -> data.variable); + col = token_print_indent (file, col, indent, "", "", ")"); + break; + + default: + log_fatal ("invalid expression type in print_expression: %d", + expr -> op); + } + return col; +} + +struct binding *find_binding (struct binding_scope *scope, const char *name) +{ + struct binding *bp; + struct binding_scope *s; + + for (s = scope; s; s = s -> outer) { + for (bp = s -> bindings; bp; bp = bp -> next) { + if (!strcasecmp (name, bp -> name)) { + return bp; + } + } + } + return (struct binding *)0; +} + +int free_bindings (struct binding_scope *scope, const char *file, int line) +{ + struct binding *bp, *next; + + for (bp = scope -> bindings; bp; bp = next) { + next = bp -> next; + if (bp -> name) + dfree (bp -> name, file, line); + if (bp -> value) + binding_value_dereference (&bp -> value, file, line); + dfree (bp, file, line); + } + scope -> bindings = (struct binding *)0; + return 1; +} + +int binding_scope_dereference (ptr, file, line) + struct binding_scope **ptr; + const char *file; + int line; +{ + int i; + struct binding_scope *binding_scope; + + if (!ptr || !*ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + binding_scope = *ptr; + *ptr = (struct binding_scope *)0; + --binding_scope -> refcnt; + rc_register (file, line, ptr, + binding_scope, binding_scope -> refcnt, 1, RC_MISC); + if (binding_scope -> refcnt > 0) + return 1; + + if (binding_scope -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (binding_scope); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + free_bindings (binding_scope, file, line); + if (binding_scope -> outer) + binding_scope_dereference (&binding_scope -> outer, MDL); + dfree (binding_scope, file, line); + return 1; +} + +int fundef_dereference (ptr, file, line) + struct fundef **ptr; + const char *file; + int line; +{ + struct fundef *bp = *ptr; + struct string_list *sp, *next; + + if (!ptr) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + if (!bp) { + log_error ("%s(%d): null pointer", file, line); +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + + bp -> refcnt--; + rc_register (file, line, ptr, bp, bp -> refcnt, 1, RC_MISC); + if (bp -> refcnt < 0) { + log_error ("%s(%d): negative refcnt!", file, line); +#if defined (DEBUG_RC_HISTORY) + dump_rc_history (bp); +#endif +#if defined (POINTER_DEBUG) + abort (); +#else + return 0; +#endif + } + if (!bp -> refcnt) { + for (sp = bp -> args; sp; sp = next) { + next = sp -> next; + dfree (sp, file, line); + } + if (bp -> statements) + executable_statement_dereference (&bp -> statements, + file, line); + dfree (bp, file, line); + } + *ptr = (struct fundef *)0; + return 1; +} + +#if defined (NOTYET) /* Post 3.0 final. */ +int data_subexpression_length (int *rv, + struct expression *expr) +{ + int crhs, clhs, llhs, lrhs; + switch (expr -> op) { + case expr_substring: + if (expr -> data.substring.len && + expr -> data.substring.len -> op == expr_const_int) { + (*rv = + (int)expr -> data.substring.len -> data.const_int); + return 1; + } + return 0; + + case expr_packet: + case expr_suffix: + if (expr -> data.suffix.len && + expr -> data.suffix.len -> op == expr_const_int) { + (*rv = + (int)expr -> data.suffix.len -> data.const_int); + return 1; + } + return 0; + + case expr_concat: + clhs = data_subexpression_length (&llhs, + expr -> data.concat [0]); + crhs = data_subexpression_length (&lrhs, + expr -> data.concat [1]); + if (crhs == 0 || clhs == 0) + return 0; + *rv = llhs + lrhs; + return 1; + break; + + case expr_hardware: + return 0; + + case expr_const_data: + *rv = expr -> data.const_data.len; + return 2; + + case expr_reverse: + return data_subexpression_length (rv, + expr -> data.reverse.buffer); + + case expr_leased_address: + case expr_lease_time: + *rv = 4; + return 2; + + case expr_pick_first_value: + clhs = data_subexpression_length (&llhs, + expr -> data.concat [0]); + crhs = data_subexpression_length (&lrhs, + expr -> data.concat [1]); + if (crhs == 0 || clhs == 0) + return 0; + if (llhs > lrhs) + *rv = llhs; + else + *rv = lrhs; + return 1; + + case expr_binary_to_ascii: + case expr_config_option: + case expr_host_decl_name: + case expr_encapsulate: + case expr_filename: + case expr_sname: + case expr_host_lookup: + case expr_option: + case expr_none: + case expr_match: + case expr_check: + case expr_equal: + case expr_and: + case expr_or: + case expr_not: + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_encode_int8: + case expr_encode_int16: + case expr_encode_int32: + case expr_const_int: + case expr_exists: + case expr_known: + case expr_dns_transaction: + case expr_static: + case expr_ns_add: + case expr_ns_delete: + case expr_ns_exists: + case expr_ns_not_exists: + case expr_not_equal: + case expr_null: + case expr_variable_exists: + case expr_variable_reference: + case expr_arg: + case expr_funcall: + case expr_function: + case expr_add: + case expr_subtract: + case expr_multiply: + case expr_divide: + case expr_remainder: + case expr_binary_and: + case expr_binary_or: + case expr_binary_xor: + case expr_client_state: + return 0; + } + return 0; +} + +int expr_valid_for_context (struct expression *expr, + enum expression_context context) +{ + /* We don't know at parse time what type of value a function may + return, so we can't flag an error on it. */ + if (expr -> op == expr_funcall || + expr -> op == expr_variable_reference) + return 1; + + switch (context) { + case context_any: + return 1; + + case context_boolean: + if (is_boolean_expression (expr)) + return 1; + return 0; + + case context_data: + if (is_data_expression (expr)) + return 1; + return 0; + + case context_numeric: + if (is_numeric_expression (expr)) + return 1; + return 0; + + case context_dns: + if (is_dns_expression (expr)) { + return 1; + } + return 0; + + case context_data_or_numeric: + if (is_numeric_expression (expr) || + is_data_expression (expr)) { + return 1; + } + return 0; + + case context_function: + if (expr -> op == expr_function) + return 1; + return 0; + } + return 0; +} +#endif /* NOTYET */ + +struct binding *create_binding (struct binding_scope **scope, const char *name) +{ + struct binding *binding; + + if (!*scope) { + if (!binding_scope_allocate (scope, MDL)) + return (struct binding *)0; + } + + binding = find_binding (*scope, name); + if (!binding) { + binding = dmalloc (sizeof *binding, MDL); + if (!binding) + return (struct binding *)0; + + memset (binding, 0, sizeof *binding); + binding -> name = dmalloc (strlen (name) + 1, MDL); + if (!binding -> name) { + dfree (binding, MDL); + return (struct binding *)0; + } + strcpy (binding -> name, name); + + binding -> next = (*scope) -> bindings; + (*scope) -> bindings = binding; + } + + return binding; +} + + +int bind_ds_value (struct binding_scope **scope, + const char *name, + struct data_string *value) +{ + struct binding *binding; + + binding = create_binding (scope, name); + if (!binding) + return 0; + + if (binding -> value) + binding_value_dereference (&binding -> value, MDL); + + if (!binding_value_allocate (&binding -> value, MDL)) + return 0; + + data_string_copy (&binding -> value -> value.data, value, MDL); + binding -> value -> type = binding_data; + + return 1; +} + + +int find_bound_string (struct data_string *value, + struct binding_scope *scope, + const char *name) +{ + struct binding *binding; + + binding = find_binding (scope, name); + if (!binding || + !binding -> value || + binding -> value -> type != binding_data) + return 0; + + if (binding -> value -> value.data.terminated) { + data_string_copy (value, &binding -> value -> value.data, MDL); + } else { + buffer_allocate (&value -> buffer, + binding -> value -> value.data.len, + MDL); + if (!value -> buffer) + return 0; + + memcpy (value -> buffer -> data, + binding -> value -> value.data.data, + binding -> value -> value.data.len); + value -> data = value -> buffer -> data; + value -> len = binding -> value -> value.data.len; + } + + return 1; +} + +int unset (struct binding_scope *scope, const char *name) +{ + struct binding *binding; + + binding = find_binding (scope, name); + if (binding) { + if (binding -> value) + binding_value_dereference + (&binding -> value, MDL); + return 1; + } + return 0; +} + +/* vim: set tabstop=8: */ diff --git a/contrib/isc-dhcp/common/upf.c b/contrib/isc-dhcp/common/upf.c index 70f8eff..a737d57 100644 --- a/contrib/isc-dhcp/common/upf.c +++ b/contrib/isc-dhcp/common/upf.c @@ -3,8 +3,8 @@ Ultrix PacketFilter interface code. */ /* - * Copyright (c) 1995, 1996, 1997, 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 @@ -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: upf.c,v 1.3.2.5 1999/03/29 22:07:13 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; +"$Id: upf.c,v 1.21 2000/09/01 23:03:39 mellon Exp $ Copyright (c) 1996-2000 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" @@ -97,7 +98,7 @@ int if_register_upf (info) if (errno == EBUSY) { continue; } else { - error ("Can't find free upf: %m"); + log_fatal ("Can't find free upf: %m"); } } else { break; @@ -106,26 +107,26 @@ int if_register_upf (info) /* Set the UPF device to point at this interface. */ if (ioctl (sock, EIOCSETIF, info -> ifp) < 0) - error ("Can't attach interface %s to upf device %s: %m", + log_fatal ("Can't attach interface %s to upf device %s: %m", info -> name, filename); /* Get the hardware address. */ if (ioctl (sock, EIOCDEVP, ¶m) < 0) - error ("Can't get interface %s hardware address: %m", + log_fatal ("Can't get interface %s hardware address: %m", info -> name); /* We only know how to do ethernet. */ if (param.end_dev_type != ENDT_10MB) - error ("Invalid device type on network interface %s: %d", + log_fatal ("Invalid device type on network interface %s: %d", info -> name, param.end_dev_type); if (param.end_addr_len != 6) - error ("Invalid hardware address length on %s: %d", + log_fatal ("Invalid hardware address length on %s: %d", info -> name, param.end_addr_len); - info -> hw_address.hlen = 6; - info -> hw_address.htype = ARPHRD_ETHER; - memcpy (&info -> hw_address.haddr [0], param.end_addr, 6); + info -> hw_address.hlen = 7; + info -> hw_address.hbuf [0] = ARPHRD_ETHER; + memcpy (&info -> hw_address.hbuf [1], param.end_addr, 6); return sock; } @@ -143,11 +144,29 @@ void if_register_send (info) info -> wfdesc = info -> rfdesc; #endif if (!quiet_interface_discovery) - note ("Sending on UPF/%s/%s%s%s", + log_info ("Sending on UPF/%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_send (info) + struct interface_info *info; +{ +#ifndef USE_UPF_RECEIVE + close (info -> wfdesc); +#endif + info -> wfdesc = -1; + if (!quiet_interface_discovery) + log_info ("Disabling output on UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); @@ -173,18 +192,18 @@ void if_register_receive (info) /* 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... */ flag = (ENHOLDSIG | ENBATCH | ENTSTAMP | ENPROMISC | ENNONEXCL | ENCOPYALL); if (ioctl (info -> rfdesc, EIOCMBIC, &flag) < 0) - error ("Can't clear pfilt bits: %m"); + log_fatal ("Can't clear pfilt bits: %m"); /* Set the ENBATCH and ENCOPYALL bits... */ bits = ENBATCH | ENCOPYALL; if (ioctl (info -> rfdesc, EIOCMBIS, &bits) < 0) - error ("Can't set ENBATCH|ENCOPYALL: %m"); + log_fatal ("Can't set ENBATCH|ENCOPYALL: %m"); /* Set up the UPF filter program. */ /* XXX Unlike the BPF filter program, this one won't work if the @@ -207,13 +226,29 @@ void if_register_receive (info) pf.enf_Filter [pf.enf_FilterLen++] = local_port; if (ioctl (info -> rfdesc, EIOCSETF, &pf) < 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 UPF/%s/%s%s%s", + log_info ("Listening on UPF/%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 UPF/%s/%s%s%s", + info -> name, + print_hw_addr (info -> hw_address.hbuf [0], + info -> hw_address.hlen - 1, + &info -> hw_address.hbuf [1]), (info -> shared_network ? "/" : ""), (info -> shared_network ? info -> shared_network -> name : "")); @@ -230,30 +265,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_UPF_SEND */ @@ -306,7 +346,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; } @@ -317,14 +358,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 |