summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdhcputil
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libdhcputil
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libdhcputil')
-rw-r--r--usr/src/lib/libdhcputil/Makefile74
-rw-r--r--usr/src/lib/libdhcputil/Makefile.com64
-rw-r--r--usr/src/lib/libdhcputil/README.inittab294
-rw-r--r--usr/src/lib/libdhcputil/common/dhcp_inittab.c1223
-rw-r--r--usr/src/lib/libdhcputil/common/dhcp_inittab.h117
-rw-r--r--usr/src/lib/libdhcputil/common/dhcp_symbol.c884
-rw-r--r--usr/src/lib/libdhcputil/common/dhcp_symbol.h131
-rw-r--r--usr/src/lib/libdhcputil/common/dhcpmsg.c216
-rw-r--r--usr/src/lib/libdhcputil/common/dhcpmsg.h74
-rw-r--r--usr/src/lib/libdhcputil/common/llib-ldhcputil35
-rw-r--r--usr/src/lib/libdhcputil/i386/Makefile32
-rw-r--r--usr/src/lib/libdhcputil/libdhcputil.xcl25
-rw-r--r--usr/src/lib/libdhcputil/req.flg30
-rw-r--r--usr/src/lib/libdhcputil/sparc/Makefile32
-rw-r--r--usr/src/lib/libdhcputil/spec/Makefile30
-rw-r--r--usr/src/lib/libdhcputil/spec/Makefile.targ33
-rw-r--r--usr/src/lib/libdhcputil/spec/amd64/Makefile32
-rw-r--r--usr/src/lib/libdhcputil/spec/dhcputil.spec178
-rw-r--r--usr/src/lib/libdhcputil/spec/i386/Makefile36
-rw-r--r--usr/src/lib/libdhcputil/spec/sparc/Makefile36
-rw-r--r--usr/src/lib/libdhcputil/spec/sparcv9/Makefile33
-rw-r--r--usr/src/lib/libdhcputil/spec/versions43
22 files changed, 3652 insertions, 0 deletions
diff --git a/usr/src/lib/libdhcputil/Makefile b/usr/src/lib/libdhcputil/Makefile
new file mode 100644
index 0000000000..eebf93f7df
--- /dev/null
+++ b/usr/src/lib/libdhcputil/Makefile
@@ -0,0 +1,74 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include $(SRC)/lib/Makefile.lib
+
+LOCHDRS = dhcp_inittab.h dhcp_symbol.h dhcpmsg.h
+COMHDRS = dhcp_impl.h dhcp_symbol_common.h
+HDRS = $(LOCHDRS) $(COMHDRS)
+LOCHDRDIR = common
+COMHDRDIR = $(SRC)/common/net/dhcp
+HDRDIR = $(LOCHDRDIR)
+SUBDIRS = $(MACH)
+POFILE = libdhcputil.po
+MSGFILES = common/dhcpmsg.c
+XGETFLAGS = -a -x libdhcputil.xcl
+
+# Override HDRSRCS so check only local headers
+HDRSRCS = $(LOCHDRS:%=$(HDRDIR)/%)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install: spec .WAIT $(SUBDIRS)
+
+lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+$(ROOTHDRDIR)/%: $(COMHDRDIR)/%
+ $(INS.file)
+
+check: $(CHECKHDRS)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+spec $(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libdhcputil/Makefile.com b/usr/src/lib/libdhcputil/Makefile.com
new file mode 100644
index 0000000000..c6e3f163e4
--- /dev/null
+++ b/usr/src/lib/libdhcputil/Makefile.com
@@ -0,0 +1,64 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY = libdhcputil.a
+VERS = .1
+LOCOBJS = dhcp_inittab.o dhcp_symbol.o dhcpmsg.o
+COMDIR = $(SRC)/common/net/dhcp
+COMOBJS = scan.o
+OBJECTS = $(LOCOBJS) $(COMOBJS)
+
+include ../../Makefile.lib
+
+# install this library in the root filesystem
+include ../../Makefile.rootfs
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+LDLIBS += -lc -lnsl -lgen -linetutil
+SRCS = $(LOCOBJS:%.o=$(SRCDIR)/%.c) $(COMOBJS:%.o=$(COMDIR)/%.c)
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+SRCDIR = ../common
+MAPDIR = ../spec/$(TRANSMACH)
+SPECMAPFILE = $(MAPDIR)/mapfile
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(COMDIR)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+pics/%.o: $(COMDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libdhcputil/README.inittab b/usr/src/lib/libdhcputil/README.inittab
new file mode 100644
index 0000000000..b6ec2763e6
--- /dev/null
+++ b/usr/src/lib/libdhcputil/README.inittab
@@ -0,0 +1,294 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+Copyright (c) 2001 by Sun Microsystems, Inc.
+All rights reserved.
+
+Inittab Purpose, Goals, and Functionality
+Peter Memishian
+ident "%Z%%M% %I% %E% SMI"
+
+PROBLEM STATEMENT
+=================
+
+Currently, each DHCP-related utility that needs to handle DHCP options
+uses ad-hoc methods for learning and using them, ranging from using
+hard-coded internal tables to providing published (but distinct)
+configuration files describing these options.
+
+Originally, when only the DHCP server needed to be concerned with DHCP
+options, not having a standard API for managing and parsing DHCP
+options was understandable. Now, with four consumers of DHCP options
+in core Solaris (in.dhcpd, dhcpinfo, snoop, and dhcpmgr), the
+situation has spiraled out of control. In addition to the obvious
+maintenance headache caused by the redundant code, it has also become
+a burden to our customers, who already have to cope with multiple
+places where DHCP option information is stored (dhcptags(4),
+dhcptab(4)).
+
+The inittab API is designed to reduce the confusion, both for the
+customer and the application developer. Its goal is to provide a
+single configuration for applications to receive their DHCP option
+knowledge from and general routines for encoding and decoding DHCP
+options.
+
+INITTAB
+=======
+
+The inittab file contains information regarding the syntax and (to
+some degree) the semantics of DHCP options. It is primarily a
+read-only file (like /etc/termcap) and should not need to be changed
+by users. Experienced sysadmins may need to update this file to add
+new DHCP options, but this should be rare.
+
+The inittab file consists of inittab records, each being one line long
+and describing a particular option. The format is based heavily on
+the format for defining symbols in dhcptab(4). Each line has the
+following syntax:
+
+ option_name category, code, type, granularity, maximum, consumers
+
+where:
+
+ `option_name' is user-interpretable name of the option (for use with
+ dhcpinfo(1M) for instance). This field should at least be per-
+ category unique and ideally should be unique across all categories.
+ Of particular note is that options names in the STANDARD, SITE, and
+ VENDOR spaces should not overlap, or the behavior is undefined.
+
+ `category' is one of STANDARD, SITE, VENDOR, FIELD, or INTERNAL and
+ identifies the namespace in which the option falls.
+
+ `code' is the code of this option when it is sent over the
+ wire. (note: in most cases, `code' uniquely identifies the
+ option, without a category. however, in the case of internal
+ categories like FIELD or INTERNAL, `code' may be used for
+ other purposes and thus may not be globally unique). This field
+ should be per-category unique and the STANDARD and SITE fields
+ should not have overlapping code fields or the behavior is
+ undefined.
+
+ `type' describes the payload associated with this option. Valid
+ types are IP, ASCII, OCTET, NUMBER, BOOL, UNUMBER8, UNUMBER16,
+ UNUMBER32, SNUMBER8, SNUMBER16, and SNUMBER32. For numbers,
+ a preceding `U' or `S' indicates whether the number is unsigned
+ or signed, and the trailing number indicates the number of bits
+ in the number.
+
+ `granularity' describes how many units of `type' payload make
+ up a whole value for this option. In the case of `NUMBER',
+ granularity describes the number of bytes in the number. Note
+ that `NUMBER' is preserved for compatibility, but the more
+ descriptive [SU]NUMBER{8,16,32,64} types should preferred.
+
+ `maximum' describes how many whole values are allowed for this
+ option. 0 indicates an infinite number.
+
+ `consumers' describe which programs make use of this information.
+ (`i' for dhcpinfo, `s' for snoop, `d' for in.dhcpd, and
+ `m' for dhcpmgr).
+
+A sample entry would be
+
+ StaticRt STANDARD, 33, IP, 2, 0, isdm
+
+which describes an option named `StaticRt', that is in the STANDARD
+category (i.e., defined by the DHCP standard), and is option code
+33, which is of type `IP Address', consisting of a potentially
+infinite number of pairs of IP addresses. Lastly, the consumers of
+option are dhcpinfo, snoop, in.dhcpd and dhcpmgr.
+
+Comments in the inittab file begin with `#', and end with a newline.
+Comments need not start at the beginning of a line. Lines cannot be
+continued (with `\' for instance).
+
+The inittab file becomes the authoritative source for all DHCP options
+for all DHCP option consumers, with the following exceptions and notes:
+
+ o The DHCP agent and DHCP server both have their core protocol-
+ related functionality hardcoded into them, so changes to the
+ inittab file do not generally affect their inner workings.
+
+ o A program can specify which entries it wants from the inittab.
+ This means that some DHCP options will never be used by some
+ programs, even if they are listed as a `consumer' of the given
+ option. An example of this is that the DHCP server never
+ requests any fields with the VENDOR category. (VENDOR information
+ for the DHCP server comes from dhcptab(4) instead).
+
+ o In general, changing provided information in a released inittab
+ file is ill-advised. Adding new entries should be the extent
+ of the modifications that are performed.
+
+ o The inittab C API also provides functions which allow programs
+ to verify that a given entry in the inittab file is correct
+ (which it does by consulting a compiled-in database of current
+ options). In general, this functionality is only used where
+ absolutely necessary, since it nullifies some of the advantages
+ of having an inittab.
+
+ o Where a symbol is defined both in the inittab and in dhcptab(4),
+ inittab is authoritative. EXTEND symbol definitions in
+ dhcptab(4) will be deprecated in a future release of Solaris.
+
+C-LEVEL API
+===========
+
+Each inittab entry describes a specific DHCP option and is defined as
+a dhcp_symbol_t (as defined in usr/src/lib/libdhcputil/common/dhcp_symbol.h).
+
+In general, it is expected that inittab entries are acquired via
+inittab_load(), inittab_getbyname(), or inittab_getbycode() and passed
+as needed to the remaining inittab_XXX functions. If consumers need
+to convert the inittab entries into a different format, then the
+fields inside the inittab entry may be read directly. Some inittab
+functions return dynamically allocated parameters; all such parameters
+can be freed with free(3c).
+
+To get an inittab entry, one of the following API's must be used:
+
+ dhcp_symbol_t *
+ inittab_load(uchar_t categories, char consumer, size_t *n_entries);
+
+ dhcp_symbol_t *
+ inittab_getbyname(uchar_t categories, char consumer, const char *name);
+
+ dhcp_symbol_t *
+ inittab_getbycode(uchar_t categories, char consumer, unsigned int code);
+
+where the `categories' parameter consists of the following values OR'd
+together:
+
+ #define ITAB_CAT_STANDARD 0x01
+ #define ITAB_CAT_FIELD 0x02
+ #define ITAB_CAT_INTERNAL 0x04
+ #define ITAB_CAT_VENDOR 0x08
+ #define ITAB_CAT_SITE 0x10
+
+and the `consumer' field consists of one of the following:
+
+ #define ITAB_CONS_INFO 'i'
+ #define ITAB_CONS_SERVER 'd'
+ #define ITAB_CONS_SNOOP 's'
+ #define ITAB_CONS_MANAGER 'm'
+
+inittab_load() creates and returns an array of dhcp_symbol_t's made
+up of all the entries of the specified categories that are available
+to the provided consumer. Note that there is no specified order to
+the entries returned. The array is dynamically allocated, and the
+number of items in the array is returned in the `n_entries' parameter.
+
+inittab_getbyname()/inittab_getbycode() return an dhcp_symbol_t
+matching the given name or code for the provided category and the
+provided consumer. The dhcp_symbol_t is dynamically allocated.
+
+Some inittab consumers may need to make sure that a given inittab
+entry has not been corrupted in the inittab file. For those cases,
+inittab_verify() can be used to validate an inittab_entry against an
+internal table compiled into the inittab API:
+
+ int
+ inittab_verify(dhcp_symbol_t *inittab_ent,
+ dhcp_symbol_t *internal_ent);
+
+where `inittab_ent' is an dhcp_symbol_t previously returned from
+inittab_load() or inittab_getbyX(). inittab_verify() returns
+ITAB_SUCCESS if `inittab_ent' is verified to be correct, ITAB_FAILURE
+if `inittab_ent' is incorrect, and ITAB_UNKNOWN if inittab_verify()
+doesn't know. If `internal_ent' is non-NULL, it is filled in with the
+value of the option known internally to the inittab API. Entries are
+verified using the `ds_category' and `ds_code' fields from the
+dhcp_symbol_t. For ITAB_SUCCESS to be returned, the entry passed in
+and the internal entry both must have the same ds_gran, ds_max, and
+ds_type values.
+
+To perform encoding and decoding of DHCP options, the following
+routines are provided:
+
+ uchar_t *
+ inittab_encode(dhcp_symbol_t *inittab_ent, const char *data,
+ uint16_t *lengthp, boolean_t just_payload);
+
+ const char *
+ inittab_decode(dhcp_symbol_t *inittab_ent, uchar_t *data,
+ uint16_t length, boolean_t just_payload);
+
+Both of these routines take an `inittab_ent' that was previously
+returned from inittab_load() or inittab_getbyX().
+
+For inittab_encode(), `data' is an ASCII string to encode, and a
+pointer to a dynamically allocated byte-array representing the encoded
+option is returned. The size of the resulting data returned is stored
+in `lengthp'. Note that if the `just_payload' option is set, then
+only the payload of the option is returned (i.e., the option code and
+option length is left off the returned data). To encode multiple
+items of a given type, separate the items by spaces, such as
+"109.108.21.1 148.232.2.1". Octal data should be of the form "0xNN"
+where NN is a hexadecimal digit representing the byte.
+
+For inittab_decode(), `data' is a byte-array representing an encoded
+option, which is `length' bytes long. A pointer to a dynamically
+allocated string representing the option's value in ASCII is returned.
+Note that if the `data' byte-array consists of just the payload of the
+option, then the `just_payload' option should be set.
+
+In addition, the following routines return extended error information
+for reporting parsing errors:
+
+ uchar_t *
+ inittab_encode_e(dhcp_symbol_t *inittab_ent, const char *data,
+ uint16_t *lengthp, boolean_t just_payload, int *eerrno);
+
+ const char *
+ inittab_decode_e(dhcp_symbol_t *inittab_ent, uchar_t *data,
+ uint16_t length, boolean_t just_payload, int *eerrno);
+
+
+The extended codes:
+
+/*
+ * DHCP Extended error codes
+ */
+#define ITAB_SYNTAX_ERROR (-1)
+#define ITAB_BAD_IPADDR (-2)
+#define ITAB_BAD_STRING (-3)
+#define ITAB_BAD_OCTET (-4)
+#define ITAB_BAD_NUMBER (-5)
+#define ITAB_BAD_BOOLEAN (-6)
+#define ITAB_NOT_ENOUGH_IP (-7)
+#define ITAB_BAD_GRAN (-8)
+
+
+ENVIRONMENT VARIABLES
+=====================
+
+In order to aid in debugging inittab-related problems, two environment
+variables, DHCP_INITTAB_DEBUG, and DHCP_INITTAB_PATH, can be set
+before starting a program which uses the inittab API.
+
+If DHCP_INITTAB_DEBUG is an exported environment variable, then the
+inittab API will print useful diagnostic messages handy in tracking
+down problems in the inittab file. If DHCP_INITTAB_PATH is an
+exported environment variable, then its value is used as the location
+of the inittab file, instead of /etc/dhcp/inittab.
+
+--
+Peter Memishian, Internet Engineering, Solaris Software (meem@east.sun.com)
diff --git a/usr/src/lib/libdhcputil/common/dhcp_inittab.c b/usr/src/lib/libdhcputil/common/dhcp_inittab.c
new file mode 100644
index 0000000000..4adca5a652
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcp_inittab.c
@@ -0,0 +1,1223 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <sys/isa_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/sysmacros.h>
+#include <libinetutil.h>
+
+#include "dhcp_symbol.h"
+#include "dhcp_inittab.h"
+
+static uint64_t dhcp_htonll(uint64_t);
+static uint64_t dhcp_ntohll(uint64_t);
+static void inittab_msg(const char *, ...);
+static uchar_t category_to_code(const char *);
+static boolean_t encode_number(uint8_t, uint8_t, boolean_t, uint8_t,
+ const char *, uint8_t *, int *);
+static boolean_t decode_number(uint8_t, uint8_t, boolean_t, uint8_t,
+ const uint8_t *, char *, int *);
+static dhcp_symbol_t *inittab_lookup(uchar_t, char, const char *, int32_t,
+ size_t *);
+static dsym_category_t itabcode_to_dsymcode(uchar_t);
+static boolean_t parse_entry(char *, char **);
+
+/*
+ * forward declaration of our internal inittab_table[]. too bulky to put
+ * up front -- check the end of this file for its definition.
+ */
+static dhcp_symbol_t inittab_table[];
+
+/*
+ * the number of fields in the inittab and names for the fields. note that
+ * this order is meaningful to parse_entry(); other functions should just
+ * use them as indexes into the array returned from parse_entry().
+ */
+#define ITAB_FIELDS 7
+enum { ITAB_NAME, ITAB_CODE, ITAB_TYPE, ITAB_GRAN, ITAB_MAX, ITAB_CONS,
+ ITAB_CAT };
+
+/*
+ * the category_map_entry_t is used to map the inittab category codes to
+ * the dsym codes. the reason the codes are different is that the inittab
+ * needs to have the codes be ORable such that queries can retrieve more
+ * than one category at a time. this map is also used to map the inittab
+ * string representation of a category to its numerical code.
+ */
+typedef struct category_map_entry {
+ dsym_category_t cme_dsymcode;
+ char *cme_name;
+ uchar_t cme_itabcode;
+} category_map_entry_t;
+
+static category_map_entry_t category_map[] = {
+ { DSYM_STANDARD, "STANDARD", ITAB_CAT_STANDARD },
+ { DSYM_FIELD, "FIELD", ITAB_CAT_FIELD },
+ { DSYM_INTERNAL, "INTERNAL", ITAB_CAT_INTERNAL },
+ { DSYM_VENDOR, "VENDOR", ITAB_CAT_VENDOR },
+ { DSYM_SITE, "SITE", ITAB_CAT_SITE }
+};
+
+/*
+ * inittab_load(): returns all inittab entries with the specified criteria
+ *
+ * input: uchar_t: the categories the consumer is interested in
+ * char: the consumer type of the caller
+ * size_t *: set to the number of entries returned
+ * output: dhcp_symbol_t *: an array of dynamically allocated entries
+ * on success, NULL upon failure
+ */
+dhcp_symbol_t *
+inittab_load(uchar_t categories, char consumer, size_t *n_entries)
+{
+ return (inittab_lookup(categories, consumer, NULL, -1, n_entries));
+}
+
+/*
+ * inittab_getbyname(): returns an inittab entry with the specified criteria
+ *
+ * input: int: the categories the consumer is interested in
+ * char: the consumer type of the caller
+ * char *: the name of the inittab entry the consumer wants
+ * output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
+ * on success, NULL upon failure
+ */
+dhcp_symbol_t *
+inittab_getbyname(uchar_t categories, char consumer, const char *name)
+{
+ return (inittab_lookup(categories, consumer, name, -1, NULL));
+}
+
+/*
+ * inittab_getbycode(): returns an inittab entry with the specified criteria
+ *
+ * input: uchar_t: the categories the consumer is interested in
+ * char: the consumer type of the caller
+ * uint16_t: the code of the inittab entry the consumer wants
+ * output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
+ * on success, NULL upon failure
+ */
+dhcp_symbol_t *
+inittab_getbycode(uchar_t categories, char consumer, uint16_t code)
+{
+ return (inittab_lookup(categories, consumer, NULL, code, NULL));
+}
+
+/*
+ * inittab_lookup(): returns inittab entries with the specified criteria
+ *
+ * input: uchar_t: the categories the consumer is interested in
+ * char: the consumer type of the caller
+ * const char *: the name of the entry the caller is interested
+ * in, or NULL if the caller doesn't care
+ * int32_t: the code the caller is interested in, or -1 if the
+ * caller doesn't care
+ * size_t *: set to the number of entries returned
+ * output: dhcp_symbol_t *: dynamically allocated dhcp_symbol structures
+ * on success, NULL upon failure
+ */
+static dhcp_symbol_t *
+inittab_lookup(uchar_t categories, char consumer, const char *name,
+ int32_t code, size_t *n_entriesp)
+{
+ FILE *inittab_fp;
+ dhcp_symbol_t *new_entries, *entries = NULL;
+ dhcp_symbol_t entry;
+ char buffer[ITAB_MAX_LINE_LEN];
+ char *fields[ITAB_FIELDS];
+ unsigned long line = 0;
+ size_t i, n_entries = 0;
+ char *inittab_path;
+ uchar_t category_code;
+ dsym_cdtype_t type;
+
+ inittab_path = getenv("DHCP_INITTAB_PATH");
+ if (inittab_path == NULL)
+ inittab_path = ITAB_INITTAB_PATH;
+
+ inittab_fp = fopen(inittab_path, "r");
+ if (inittab_fp == NULL) {
+ inittab_msg("inittab_lookup: fopen: %s: %s",
+ ITAB_INITTAB_PATH, strerror(errno));
+ return (NULL);
+ }
+
+ (void) bufsplit(",\n", 0, NULL);
+ while (fgets(buffer, sizeof (buffer), inittab_fp) != NULL) {
+
+ line++;
+
+ /*
+ * make sure the string didn't overflow our buffer
+ */
+ if (strchr(buffer, '\n') == NULL) {
+ inittab_msg("inittab_lookup: line %li: too long, "
+ "skipping", line);
+ continue;
+ }
+
+ /*
+ * skip `pure comment' lines
+ */
+ for (i = 0; buffer[i] != '\0'; i++)
+ if (isspace(buffer[i]) == 0)
+ break;
+
+ if (buffer[i] == ITAB_COMMENT_CHAR || buffer[i] == '\0')
+ continue;
+
+ /*
+ * parse the entry out into fields.
+ */
+ if (parse_entry(buffer, fields) == B_FALSE) {
+ inittab_msg("inittab_lookup: line %li: syntax error, "
+ "skipping", line);
+ continue;
+ }
+
+ /*
+ * validate the values in the entries; skip if invalid.
+ */
+ if (atoi(fields[ITAB_GRAN]) > ITAB_GRAN_MAX) {
+ inittab_msg("inittab_lookup: line %li: granularity `%s'"
+ " out of range, skipping", line, fields[ITAB_GRAN]);
+ continue;
+ }
+
+ if (atoi(fields[ITAB_MAX]) > ITAB_MAX_MAX) {
+ inittab_msg("inittab_lookup: line %li: maximum `%s' "
+ "out of range, skipping", line, fields[ITAB_MAX]);
+ continue;
+ }
+
+ if (dsym_get_type_id(fields[ITAB_TYPE], &type, B_FALSE) !=
+ DSYM_SUCCESS) {
+ inittab_msg("inittab_lookup: line %li: type `%s' "
+ "is invalid, skipping", line, fields[ITAB_TYPE]);
+ continue;
+ }
+
+ /*
+ * find out whether this entry of interest to our consumer,
+ * and if so, throw it onto the set of entries we'll return.
+ * check categories last since it's the most expensive check.
+ */
+ if (strchr(fields[ITAB_CONS], consumer) == NULL)
+ continue;
+
+ if (code != -1 && atoi(fields[ITAB_CODE]) != code)
+ continue;
+
+ if (name != NULL && strcasecmp(fields[ITAB_NAME], name) != 0)
+ continue;
+
+ category_code = category_to_code(fields[ITAB_CAT]);
+ if ((category_code & categories) == 0)
+ continue;
+
+ /*
+ * looks like a match. allocate an entry and fill it in
+ */
+ new_entries = realloc(entries, (n_entries + 1) *
+ sizeof (dhcp_symbol_t));
+
+ /*
+ * if we run out of memory, might as well return what we can
+ */
+ if (new_entries == NULL) {
+ inittab_msg("inittab_lookup: ran out of memory "
+ "allocating dhcp_symbol_t's");
+ break;
+ }
+
+ entry.ds_max = atoi(fields[ITAB_MAX]);
+ entry.ds_code = atoi(fields[ITAB_CODE]);
+ entry.ds_type = type;
+ entry.ds_gran = atoi(fields[ITAB_GRAN]);
+ entry.ds_category = itabcode_to_dsymcode(category_code);
+ entry.ds_classes.dc_cnt = 0;
+ entry.ds_classes.dc_names = NULL;
+ (void) strlcpy(entry.ds_name, fields[ITAB_NAME],
+ sizeof (entry.ds_name));
+
+ entries = new_entries;
+ entries[n_entries++] = entry;
+ }
+
+ if (ferror(inittab_fp) != 0) {
+ inittab_msg("inittab_lookup: error on inittab stream");
+ clearerr(inittab_fp);
+ }
+
+ (void) fclose(inittab_fp);
+
+ if (n_entriesp != NULL)
+ *n_entriesp = n_entries;
+
+ return (entries);
+}
+
+/*
+ * parse_entry(): parses an entry out into its constituent fields
+ *
+ * input: char *: the entry
+ * char **: an array of ITAB_FIELDS length which contains
+ * pointers into the entry on upon return
+ * output: boolean_t: B_TRUE on success, B_FALSE on failure
+ */
+static boolean_t
+parse_entry(char *entry, char **fields)
+{
+ char *category, *spacep;
+ size_t n_fields, i;
+
+ /*
+ * due to a mistake made long ago, the first and second fields of
+ * each entry are not separated by a comma, but rather by
+ * whitespace -- have bufsplit() treat the two fields as one, then
+ * pull them apart afterwards.
+ */
+ n_fields = bufsplit(entry, ITAB_FIELDS - 1, fields);
+ if (n_fields != (ITAB_FIELDS - 1))
+ return (B_FALSE);
+
+ /*
+ * pull the first and second fields apart. this is complicated
+ * since the first field can contain embedded whitespace (so we
+ * must separate the two fields by the last span of whitespace).
+ *
+ * first, find the initial span of whitespace. if there isn't one,
+ * then the entry is malformed.
+ */
+ category = strpbrk(fields[ITAB_NAME], " \t");
+ if (category == NULL)
+ return (B_FALSE);
+
+ /*
+ * find the last span of whitespace.
+ */
+ do {
+ while (isspace(*category))
+ category++;
+
+ spacep = strpbrk(category, " \t");
+ if (spacep != NULL)
+ category = spacep;
+ } while (spacep != NULL);
+
+ /*
+ * NUL-terminate the first byte of the last span of whitespace, so
+ * that the first field doesn't have any residual trailing
+ * whitespace.
+ */
+ spacep = category - 1;
+ while (isspace(*spacep))
+ spacep--;
+
+ if (spacep <= fields[0])
+ return (B_FALSE);
+
+ *++spacep = '\0';
+
+ /*
+ * remove any whitespace from the fields.
+ */
+ for (i = 0; i < n_fields; i++) {
+ while (isspace(*fields[i]))
+ fields[i]++;
+ }
+ fields[ITAB_CAT] = category;
+
+ return (B_TRUE);
+}
+
+/*
+ * inittab_verify(): verifies that a given inittab entry matches an internal
+ * definition
+ *
+ * input: dhcp_symbol_t *: the inittab entry to verify
+ * dhcp_symbol_t *: if non-NULL, a place to store the internal
+ * inittab entry upon return
+ * output: int: ITAB_FAILURE, ITAB_SUCCESS, or ITAB_UNKNOWN
+ */
+int
+inittab_verify(dhcp_symbol_t *inittab_ent, dhcp_symbol_t *internal_ent)
+{
+ unsigned int i;
+
+ for (i = 0; inittab_table[i].ds_name[0] != '\0'; i++) {
+
+ if (inittab_ent->ds_category != inittab_table[i].ds_category)
+ continue;
+
+ if (inittab_ent->ds_code == inittab_table[i].ds_code) {
+ if (internal_ent != NULL)
+ *internal_ent = inittab_table[i];
+
+ if (inittab_table[i].ds_type != inittab_ent->ds_type ||
+ inittab_table[i].ds_gran != inittab_ent->ds_gran ||
+ inittab_table[i].ds_max != inittab_ent->ds_max)
+ return (ITAB_FAILURE);
+
+ return (ITAB_SUCCESS);
+ }
+ }
+
+ return (ITAB_UNKNOWN);
+}
+
+/*
+ * inittab_encode_e(): converts a string representation of a given datatype into
+ * binary; used for encoding ascii values into a form that
+ * can be put in DHCP packets to be sent on the wire.
+ *
+ * input: dhcp_symbol_t *: the entry describing the value option
+ * const char *: the value to convert
+ * uint16_t *: set to the length of the binary data returned
+ * boolean_t: if false, return a full DHCP option
+ * output: uchar_t *: a dynamically allocated byte array with converted data
+ */
+uchar_t *
+inittab_encode_e(dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
+ boolean_t just_payload, int *ierrnop)
+{
+ uint16_t length = 0;
+ uchar_t n_entries = 0;
+ const char *valuep;
+ char *currp;
+ uchar_t *result = NULL;
+ unsigned int i;
+ uint8_t type_size = inittab_type_to_size(ie);
+ boolean_t is_signed;
+ uint_t vallen, reslen;
+
+ *ierrnop = 0;
+ if (type_size == 0) {
+ *ierrnop = ITAB_SYNTAX_ERROR;
+ return (NULL);
+ }
+
+ if (ie->ds_type == DSYM_ASCII)
+ n_entries = strlen(value); /* no NUL */
+ else if (ie->ds_type == DSYM_OCTET) {
+ vallen = strlen(value);
+ n_entries = vallen / 2;
+ n_entries += vallen % 2;
+ } else {
+ /*
+ * figure out the number of entries by counting the spaces
+ * in the value string
+ */
+ for (valuep = value; valuep++ != NULL; n_entries++)
+ valuep = strchr(valuep, ' ');
+ }
+
+ /*
+ * if we're gonna return a complete option, then include the
+ * option length and code in the size of the packet we allocate
+ */
+ if (just_payload == B_FALSE)
+ length += 2;
+
+ length += n_entries * type_size;
+ if (length > 0)
+ result = malloc(length);
+
+ switch (ie->ds_type) {
+
+ case DSYM_ASCII:
+
+ if (result == NULL) {
+ *ierrnop = ITAB_NOMEM;
+ return (NULL);
+ }
+
+ if (strlen(value) > length) {
+ free(result);
+ *ierrnop = ITAB_BAD_STRING;
+ return (NULL);
+ }
+
+ (void) memcpy(result, value, length);
+ break;
+
+ case DSYM_OCTET:
+
+ if (result == NULL) {
+ *ierrnop = ITAB_BAD_OCTET;
+ return (NULL);
+ }
+
+ reslen = length;
+ /* Call libinetutil function to decode */
+ if (hexascii_to_octet(value, vallen, result, &reslen) != 0) {
+ free(result);
+ *ierrnop = ITAB_BAD_OCTET;
+ return (NULL);
+ }
+ break;
+
+ case DSYM_IP:
+
+ if (result == NULL) {
+ *ierrnop = ITAB_BAD_IPADDR;
+ return (NULL);
+ }
+ if (n_entries % ie->ds_gran != 0) {
+ *ierrnop = ITAB_BAD_GRAN;
+ inittab_msg("inittab_encode: number of entries "
+ "not compatible with option granularity");
+ free(result);
+ return (NULL);
+ }
+
+ for (valuep = value, i = 0; i < n_entries; i++, valuep++) {
+
+ currp = strchr(valuep, ' ');
+ if (currp != NULL)
+ *currp = '\0';
+ if (inet_pton(AF_INET, valuep,
+ &result[i * sizeof (ipaddr_t)]) != 1) {
+ *ierrnop = ITAB_BAD_IPADDR;
+ inittab_msg("inittab_encode: bogus ip address");
+ free(result);
+ return (NULL);
+ }
+
+ valuep = currp;
+ if (valuep == NULL) {
+ if (i < (n_entries - 1)) {
+ *ierrnop = ITAB_NOT_ENOUGH_IP;
+ inittab_msg("inittab_encode: too few "
+ "ip addresses");
+ free(result);
+ return (NULL);
+ }
+ break;
+ }
+ }
+ break;
+
+ case DSYM_NUMBER: /* FALLTHRU */
+ case DSYM_UNUMBER8: /* FALLTHRU */
+ case DSYM_SNUMBER8: /* FALLTHRU */
+ case DSYM_UNUMBER16: /* FALLTHRU */
+ case DSYM_SNUMBER16: /* FALLTHRU */
+ case DSYM_UNUMBER32: /* FALLTHRU */
+ case DSYM_SNUMBER32: /* FALLTHRU */
+ case DSYM_UNUMBER64: /* FALLTHRU */
+ case DSYM_SNUMBER64:
+
+ if (result == NULL) {
+ *ierrnop = ITAB_BAD_NUMBER;
+ return (NULL);
+ }
+
+ is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
+ ie->ds_type == DSYM_SNUMBER32 ||
+ ie->ds_type == DSYM_SNUMBER16 ||
+ ie->ds_type == DSYM_SNUMBER8);
+
+ if (encode_number(n_entries, type_size, is_signed, 0, value,
+ result, ierrnop) == B_FALSE) {
+ free(result);
+ return (NULL);
+ }
+ break;
+
+ default:
+ if (ie->ds_type == DSYM_BOOL)
+ *ierrnop = ITAB_BAD_BOOLEAN;
+ else
+ *ierrnop = ITAB_SYNTAX_ERROR;
+
+ inittab_msg("inittab_encode: unsupported type `%d'",
+ ie->ds_type);
+
+ free(result);
+ return (NULL);
+ }
+
+ /*
+ * if just_payload is false, then we need to slide the option
+ * code and length fields in. (length includes them in its
+ * count, so we have to subtract 2)
+ */
+ if (just_payload == B_FALSE) {
+ (void) memmove(result + 2, result, length - 2);
+ result[0] = ie->ds_code;
+ result[1] = length - 2;
+ }
+
+ if (lengthp != NULL)
+ *lengthp = length;
+
+ return (result);
+}
+
+/*
+ * inittab_decode_e(): converts a binary representation of a given datatype into
+ * a string; used for decoding DHCP options in a packet off
+ * the wire into ascii
+ *
+ * input: dhcp_symbol_t *: the entry describing the payload option
+ * uchar_t *: the payload to convert
+ * uint16_t: the payload length (only used if just_payload is true)
+ * boolean_t: if false, payload is assumed to be a DHCP option
+ * int *: set to extended error code if error occurs.
+ * output: char *: a dynamically allocated string containing the converted data
+ */
+char *
+inittab_decode_e(dhcp_symbol_t *ie, uchar_t *payload, uint16_t length,
+ boolean_t just_payload, int *ierrnop)
+{
+ char *resultp, *end, *result = NULL;
+ char *currp;
+ uchar_t n_entries;
+ struct in_addr in_addr;
+ uint8_t type_size = inittab_type_to_size(ie);
+ boolean_t is_signed;
+
+ *ierrnop = 0;
+ if (type_size == 0) {
+ *ierrnop = ITAB_SYNTAX_ERROR;
+ return (NULL);
+ }
+
+ if (just_payload == B_FALSE) {
+ length = payload[1];
+ payload += 2;
+ }
+
+ /*
+ * figure out the number of elements to convert. note that
+ * for ds_type NUMBER, the granularity is really 1 since the
+ * value of ds_gran is the number of bytes in the number.
+ */
+ if (ie->ds_type == DSYM_NUMBER)
+ n_entries = MIN(ie->ds_max, length / type_size);
+ else
+ n_entries = MIN(ie->ds_max * ie->ds_gran, length / type_size);
+
+ if (n_entries == 0)
+ n_entries = length / type_size;
+
+ if ((length % type_size) != 0) {
+ inittab_msg("inittab_decode: length of string not compatible "
+ "with option type `%i'", ie->ds_type);
+ *ierrnop = ITAB_BAD_STRING;
+ return (NULL);
+ }
+
+ switch (ie->ds_type) {
+
+ case DSYM_ASCII:
+
+ result = malloc(n_entries + 1);
+ if (result == NULL) {
+ *ierrnop = ITAB_NOMEM;
+ return (NULL);
+ }
+
+ (void) memcpy(result, payload, n_entries);
+ result[n_entries] = '\0';
+ break;
+
+ case DSYM_OCTET:
+
+ result = malloc(n_entries * (sizeof ("0xNN") + 1));
+ if (result == NULL) {
+ *ierrnop = ITAB_NOMEM;
+ return (NULL);
+ }
+
+ for (resultp = result; n_entries != 0; n_entries--) {
+ currp = resultp;
+ resultp += sprintf(resultp, "0x%02X ", *payload++);
+ if (currp == resultp) {
+ free(result);
+ *ierrnop = ITAB_BAD_OCTET;
+ return (NULL);
+ }
+ }
+
+ resultp[-1] = '\0';
+ break;
+
+ case DSYM_IP:
+
+ if ((length / sizeof (ipaddr_t)) % ie->ds_gran != 0) {
+ *ierrnop = ITAB_BAD_GRAN;
+ inittab_msg("inittab_decode: number of entries "
+ "not compatible with option granularity");
+ return (NULL);
+ }
+
+ result = malloc(n_entries * (sizeof ("aaa.bbb.ccc.ddd") + 1));
+ end = &result[n_entries * (sizeof ("aaa.bbb.ccc.ddd") + 1)];
+ if (result == NULL) {
+ *ierrnop = ITAB_NOMEM;
+ return (NULL);
+ }
+
+ for (resultp = result; n_entries != 0; n_entries--) {
+ (void) memcpy(&in_addr.s_addr, payload,
+ sizeof (ipaddr_t));
+ currp = resultp;
+ resultp += snprintf(resultp, end - resultp, "%s ",
+ inet_ntoa(in_addr));
+ if (currp == resultp) {
+ free(result);
+ *ierrnop = ITAB_BAD_IPADDR;
+ return (NULL);
+ }
+ payload += sizeof (ipaddr_t);
+ }
+
+ resultp[-1] = '\0';
+ break;
+
+ case DSYM_NUMBER: /* FALLTHRU */
+ case DSYM_UNUMBER8: /* FALLTHRU */
+ case DSYM_SNUMBER8: /* FALLTHRU */
+ case DSYM_UNUMBER16: /* FALLTHRU */
+ case DSYM_SNUMBER16: /* FALLTHRU */
+ case DSYM_UNUMBER32: /* FALLTHRU */
+ case DSYM_SNUMBER32: /* FALLTHRU */
+ case DSYM_UNUMBER64: /* FALLTHRU */
+ case DSYM_SNUMBER64:
+
+ is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
+ ie->ds_type == DSYM_SNUMBER32 ||
+ ie->ds_type == DSYM_SNUMBER16 ||
+ ie->ds_type == DSYM_SNUMBER8);
+
+ result = malloc(n_entries * ITAB_MAX_NUMBER_LEN);
+ if (result == NULL) {
+ *ierrnop = ITAB_NOMEM;
+ return (NULL);
+ }
+
+ if (decode_number(n_entries, type_size, is_signed, ie->ds_gran,
+ payload, result, ierrnop) == B_FALSE) {
+ free(result);
+ return (NULL);
+ }
+ break;
+
+ default:
+ inittab_msg("inittab_decode: unsupported type `%d'",
+ ie->ds_type);
+ break;
+ }
+
+ return (result);
+}
+
+/*
+ * inittab_encode(): converts a string representation of a given datatype into
+ * binary; used for encoding ascii values into a form that
+ * can be put in DHCP packets to be sent on the wire.
+ *
+ * input: dhcp_symbol_t *: the entry describing the value option
+ * const char *: the value to convert
+ * uint16_t *: set to the length of the binary data returned
+ * boolean_t: if false, return a full DHCP option
+ * output: uchar_t *: a dynamically allocated byte array with converted data
+ */
+uchar_t *
+inittab_encode(dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
+ boolean_t just_payload)
+{
+ int ierrno;
+
+ return (inittab_encode_e(ie, value, lengthp, just_payload, &ierrno));
+}
+
+/*
+ * inittab_decode(): converts a binary representation of a given datatype into
+ * a string; used for decoding DHCP options in a packet off
+ * the wire into ascii
+ *
+ * input: dhcp_symbol_t *: the entry describing the payload option
+ * uchar_t *: the payload to convert
+ * uint16_t: the payload length (only used if just_payload is true)
+ * boolean_t: if false, payload is assumed to be a DHCP option
+ * output: char *: a dynamically allocated string containing the converted data
+ */
+char *
+inittab_decode(dhcp_symbol_t *ie, uchar_t *payload, uint16_t length,
+ boolean_t just_payload)
+{
+ int ierrno;
+
+ return (inittab_decode_e(ie, payload, length, just_payload, &ierrno));
+}
+
+/*
+ * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
+ *
+ * const char *: a printf-like format string
+ * ...: arguments to the format string
+ * output: void
+ */
+/*PRINTFLIKE1*/
+static void
+inittab_msg(const char *fmt, ...)
+{
+ enum { INITTAB_MSG_CHECK, INITTAB_MSG_RETURN, INITTAB_MSG_OUTPUT };
+
+ va_list ap;
+ char buf[512];
+ static int action = INITTAB_MSG_CHECK;
+
+ /*
+ * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
+ * the the cached result (stored in `action').
+ */
+ switch (action) {
+
+ case INITTAB_MSG_CHECK:
+
+ if (getenv("DHCP_INITTAB_DEBUG") == NULL) {
+ action = INITTAB_MSG_RETURN;
+ return;
+ }
+
+ action = INITTAB_MSG_OUTPUT;
+
+ /* FALLTHRU into INITTAB_MSG_OUTPUT */
+
+ case INITTAB_MSG_OUTPUT:
+
+ va_start(ap, fmt);
+
+ (void) snprintf(buf, sizeof (buf), "inittab: %s\n", fmt);
+ (void) vfprintf(stderr, buf, ap);
+
+ va_end(ap);
+ break;
+
+ case INITTAB_MSG_RETURN:
+
+ return;
+ }
+}
+
+/*
+ * decode_number(): decodes a sequence of numbers from binary into ascii;
+ * binary is coming off of the network, so it is in nbo
+ *
+ * input: uint8_t: the number of "granularity" numbers to decode
+ * uint8_t: the length of each number
+ * boolean_t: whether the numbers should be considered signed
+ * uint8_t: the number of numbers per granularity
+ * const uint8_t *: where to decode the numbers from
+ * char *: where to decode the numbers to
+ * output: boolean_t: true on successful conversion, false on failure
+ */
+static boolean_t
+decode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
+ uint8_t granularity, const uint8_t *from, char *to, int *ierrnop)
+{
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+
+ if (granularity != 0) {
+ if ((granularity % n_entries) != 0) {
+ inittab_msg("decode_number: number of entries "
+ "not compatible with option granularity");
+ *ierrnop = ITAB_BAD_GRAN;
+ return (B_FALSE);
+ }
+ }
+
+ for (; n_entries != 0; n_entries--, from += size) {
+
+ switch (size) {
+
+ case 1:
+ to += sprintf(to, is_signed ? "%d " : "%u ", *from);
+ break;
+
+ case 2:
+ (void) memcpy(&uint16, from, 2);
+ to += sprintf(to, is_signed ? "%hd " : "%hu ",
+ ntohs(uint16));
+ break;
+
+ case 4:
+ (void) memcpy(&uint32, from, 4);
+ to += sprintf(to, is_signed ? "%ld " : "%lu ",
+ ntohl(uint32));
+ break;
+
+ case 8:
+ (void) memcpy(&uint64, from, 8);
+ to += sprintf(to, is_signed ? "%lld " : "%llu ",
+ dhcp_ntohll(uint64));
+ break;
+
+ default:
+ *ierrnop = ITAB_BAD_NUMBER;
+ inittab_msg("decode_number: unknown integer size `%d'",
+ size);
+ return (B_FALSE);
+ }
+ }
+
+ to[-1] = '\0';
+ return (B_TRUE);
+}
+
+/*
+ * encode_number(): encodes a sequence of numbers from ascii into binary;
+ * number will end up on the wire so it needs to be in nbo
+ *
+ * input: uint8_t: the number of "granularity" numbers to encode
+ * uint8_t: the length of each number
+ * boolean_t: whether the numbers should be considered signed
+ * uint8_t: the number of numbers per granularity
+ * const uint8_t *: where to encode the numbers from
+ * char *: where to encode the numbers to
+ * int *: set to extended error code if error occurs.
+ * output: boolean_t: true on successful conversion, false on failure
+ */
+static boolean_t /* ARGSUSED */
+encode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
+ uint8_t granularity, const char *from, uint8_t *to, int *ierrnop)
+{
+ uint8_t i;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ char *endptr;
+
+ if (granularity != 0) {
+ if ((granularity % n_entries) != 0) {
+ *ierrnop = ITAB_BAD_GRAN;
+ inittab_msg("encode_number: number of entries "
+ "not compatible with option granularity");
+ return (B_FALSE);
+ }
+ }
+
+ for (i = 0; i < n_entries; i++, from++) {
+
+ /*
+ * totally obscure c factoid: it is legal to pass a
+ * string representing a negative number to strtoul().
+ * in this case, strtoul() will return an unsigned
+ * long that if cast to a long, would represent the
+ * negative number. we take advantage of this to
+ * cut down on code here.
+ */
+
+ errno = 0;
+ switch (size) {
+
+ case 1:
+ to[i] = strtoul(from, &endptr, 0);
+ if (errno != 0 || from == endptr) {
+ goto error;
+ }
+ break;
+
+ case 2:
+ uint16 = htons(strtoul(from, &endptr, 0));
+ if (errno != 0 || from == endptr) {
+ goto error;
+ }
+ (void) memcpy(to + (i * 2), &uint16, 2);
+ break;
+
+ case 4:
+ uint32 = htonl(strtoul(from, &endptr, 0));
+ if (errno != 0 || from == endptr) {
+ goto error;
+ }
+ (void) memcpy(to + (i * 4), &uint32, 4);
+ break;
+
+ case 8:
+ uint64 = dhcp_htonll(strtoull(from, &endptr, 0));
+ if (errno != 0 || from == endptr) {
+ goto error;
+ }
+ (void) memcpy(to + (i * 8), &uint64, 8);
+ break;
+
+ default:
+ inittab_msg("encode_number: unsupported integer "
+ "size `%d'", size);
+ return (B_FALSE);
+ }
+
+ from = strchr(from, ' ');
+ if (from == NULL)
+ break;
+ }
+
+ return (B_TRUE);
+
+error:
+ *ierrnop = ITAB_BAD_NUMBER;
+ inittab_msg("encode_number: cannot convert to integer");
+ return (B_FALSE);
+}
+
+/*
+ * inittab_type_to_size(): given an inittab entry, returns size of one entry of
+ * its type
+ *
+ * input: dhcp_symbol_t *: an entry of the given type
+ * output: uint8_t: the size in bytes of an entry of that type
+ */
+uint8_t
+inittab_type_to_size(dhcp_symbol_t *ie)
+{
+ switch (ie->ds_type) {
+
+ case DSYM_ASCII:
+ case DSYM_OCTET:
+ case DSYM_SNUMBER8:
+ case DSYM_UNUMBER8:
+
+ return (1);
+
+ case DSYM_SNUMBER16:
+ case DSYM_UNUMBER16:
+
+ return (2);
+
+ case DSYM_SNUMBER32:
+ case DSYM_UNUMBER32:
+ case DSYM_IP:
+
+ return (4);
+
+ case DSYM_SNUMBER64:
+ case DSYM_UNUMBER64:
+
+ return (8);
+
+ case DSYM_NUMBER:
+
+ return (ie->ds_gran);
+ }
+
+ return (0);
+}
+
+/*
+ * itabcode_to_dsymcode(): maps an inittab category code to its dsym
+ * representation
+ *
+ * input: uchar_t: the inittab category code
+ * output: dsym_category_t: the dsym category code
+ */
+static dsym_category_t
+itabcode_to_dsymcode(uchar_t itabcode)
+{
+
+ unsigned int i;
+
+ for (i = 0; i < ITAB_CAT_COUNT; i++)
+ if (category_map[i].cme_itabcode == itabcode)
+ return (category_map[i].cme_dsymcode);
+
+ return (DSYM_BAD_CAT);
+}
+
+/*
+ * category_to_code(): maps a category name to its numeric representation
+ *
+ * input: const char *: the category name
+ * output: uchar_t: its internal code (numeric representation)
+ */
+static uchar_t
+category_to_code(const char *category)
+{
+ unsigned int i;
+
+ for (i = 0; i < ITAB_CAT_COUNT; i++)
+ if (strcasecmp(category_map[i].cme_name, category) == 0)
+ return (category_map[i].cme_itabcode);
+
+ return (0);
+}
+
+/*
+ * dhcp_htonll(): converts a 64-bit number from host to network byte order
+ *
+ * input: uint64_t: the number to convert
+ * output: uint64_t: its value in network byte order
+ */
+static uint64_t
+dhcp_htonll(uint64_t uint64_hbo)
+{
+ return (dhcp_ntohll(uint64_hbo));
+}
+
+/*
+ * dhcp_ntohll(): converts a 64-bit number from network to host byte order
+ *
+ * input: uint64_t: the number to convert
+ * output: uint64_t: its value in host byte order
+ */
+static uint64_t
+dhcp_ntohll(uint64_t uint64_nbo)
+{
+#ifdef _LITTLE_ENDIAN
+ return ((uint64_t)ntohl(uint64_nbo & 0xffffffff) << 32 |
+ ntohl(uint64_nbo >> 32));
+#else
+ return (uint64_nbo);
+#endif
+}
+
+/*
+ * our internal table of DHCP option values, used by inittab_verify()
+ */
+static dhcp_symbol_t inittab_table[] =
+{
+{ DSYM_INTERNAL, 1024, "Hostname", DSYM_BOOL, 0, 0 },
+{ DSYM_INTERNAL, 1025, "LeaseNeg", DSYM_BOOL, 0, 0 },
+{ DSYM_INTERNAL, 1026, "EchoVC", DSYM_BOOL, 0, 0 },
+{ DSYM_INTERNAL, 1027, "BootPath", DSYM_ASCII, 1, 128 },
+{ DSYM_FIELD, 0, "Opcode", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_FIELD, 1, "Htype", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_FIELD, 2, "HLen", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_FIELD, 3, "Hops", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_FIELD, 4, "Xid", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_FIELD, 8, "Secs", DSYM_UNUMBER16, 1, 1 },
+{ DSYM_FIELD, 10, "Flags", DSYM_OCTET, 1, 2 },
+{ DSYM_FIELD, 12, "Ciaddr", DSYM_IP, 1, 1 },
+{ DSYM_FIELD, 16, "Yiaddr", DSYM_IP, 1, 1 },
+{ DSYM_FIELD, 20, "BootSrvA", DSYM_IP, 1, 1 },
+{ DSYM_FIELD, 24, "Giaddr", DSYM_IP, 1, 1 },
+{ DSYM_FIELD, 28, "Chaddr", DSYM_OCTET, 1, 16 },
+{ DSYM_FIELD, 44, "BootSrvN", DSYM_ASCII, 1, 64 },
+{ DSYM_FIELD, 108, "BootFile", DSYM_ASCII, 1, 128 },
+{ DSYM_FIELD, 236, "Magic", DSYM_OCTET, 1, 4 },
+{ DSYM_FIELD, 240, "Options", DSYM_OCTET, 1, 60 },
+{ DSYM_STANDARD, 1, "Subnet", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 2, "UTCoffst", DSYM_SNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 3, "Router", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 4, "Timeserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 5, "IEN116ns", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 6, "DNSserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 7, "Logserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 8, "Cookie", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 9, "Lprserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 10, "Impress", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 11, "Resource", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 12, "Hostname", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 13, "Bootsize", DSYM_UNUMBER16, 1, 1 },
+{ DSYM_STANDARD, 14, "Dumpfile", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 15, "DNSdmain", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 16, "Swapserv", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 17, "Rootpath", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 18, "ExtendP", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 19, "IpFwdF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 20, "NLrouteF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 21, "PFilter", DSYM_IP, 2, 0 },
+{ DSYM_STANDARD, 22, "MaxIpSiz", DSYM_UNUMBER16, 1, 1 },
+{ DSYM_STANDARD, 23, "IpTTL", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 24, "PathTO", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 25, "PathTbl", DSYM_UNUMBER16, 1, 0 },
+{ DSYM_STANDARD, 26, "MTU", DSYM_UNUMBER16, 1, 1 },
+{ DSYM_STANDARD, 27, "SameMtuF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 28, "Broadcst", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 29, "MaskDscF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 30, "MaskSupF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 31, "RDiscvyF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 32, "RSolictS", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 33, "StaticRt", DSYM_IP, 2, 0 },
+{ DSYM_STANDARD, 34, "TrailerF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 35, "ArpTimeO", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 36, "EthEncap", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 37, "TcpTTL", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 38, "TcpKaInt", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 39, "TcpKaGbF", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 40, "NISdmain", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 41, "NISservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 42, "NTPservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 43, "Vendor", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 44, "NetBNms", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 45, "NetBDsts", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 46, "NetBNdT", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 47, "NetBScop", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 48, "XFontSrv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 49, "XDispMgr", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 50, "ReqIP", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 51, "LeaseTim", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 52, "OptOvrld", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 53, "DHCPType", DSYM_UNUMBER8, 1, 1 },
+{ DSYM_STANDARD, 54, "ServerID", DSYM_IP, 1, 1 },
+{ DSYM_STANDARD, 55, "ReqList", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 56, "Message", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 57, "DHCP_MTU", DSYM_UNUMBER16, 1, 1 },
+{ DSYM_STANDARD, 58, "T1Time", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 59, "T2Time", DSYM_UNUMBER32, 1, 1 },
+{ DSYM_STANDARD, 60, "ClassID", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 61, "ClientID", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 62, "NW_dmain", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 63, "NWIPOpts", DSYM_OCTET, 1, 128 },
+{ DSYM_STANDARD, 64, "NIS+dom", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 65, "NIS+serv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 66, "TFTPsrvN", DSYM_ASCII, 1, 64 },
+{ DSYM_STANDARD, 67, "OptBootF", DSYM_ASCII, 1, 128 },
+{ DSYM_STANDARD, 68, "MblIPAgt", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 69, "SMTPserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 70, "POP3serv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 71, "NNTPserv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 72, "WWWservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 73, "Fingersv", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 74, "IRCservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 75, "STservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 76, "STDAservs", DSYM_IP, 1, 0 },
+{ DSYM_STANDARD, 77, "UserClas", DSYM_ASCII, 1, 0 },
+{ DSYM_STANDARD, 78, "SLP_DA", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 79, "SLP_SS", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 82, "AgentOpt", DSYM_OCTET, 1, 0 },
+{ DSYM_STANDARD, 89, "FQDN", DSYM_OCTET, 1, 0 },
+{ 0, 0, "", 0, 0, 0 }
+};
diff --git a/usr/src/lib/libdhcputil/common/dhcp_inittab.h b/usr/src/lib/libdhcputil/common/dhcp_inittab.h
new file mode 100644
index 0000000000..ed70ad20b4
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcp_inittab.h
@@ -0,0 +1,117 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _DHCP_INITTAB_H
+#define _DHCP_INITTAB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <dhcp_symbol.h>
+#include <limits.h>
+
+/*
+ * dhcp_inittab.[ch] make up the interface to the inittab file, which
+ * is a table of all known DHCP options. please see `README.inittab'
+ * for more background on the inittab api, and dhcp_inittab.c for details
+ * on how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * On-disk inittab attributes and limits.
+ */
+#define ITAB_INITTAB_PATH "/etc/dhcp/inittab"
+#define ITAB_MAX_LINE_LEN 8192 /* bytes */
+#define ITAB_MAX_NUMBER_LEN 30 /* digits */
+#define ITAB_COMMENT_CHAR '#'
+#define ITAB_CODE_MAX UCHAR_MAX /* for now */
+#define ITAB_GRAN_MAX UCHAR_MAX
+#define ITAB_MAX_MAX UCHAR_MAX
+
+/*
+ * Return values from the inittab API.
+ */
+#define ITAB_FAILURE 0
+#define ITAB_SUCCESS 1
+#define ITAB_UNKNOWN 2
+
+/*
+ * Categories to pass to inittab functions; note that these may be
+ * bitwise-OR'd to request more than one. Note that these should
+ * not be used otherwise.
+ */
+#define ITAB_CAT_STANDARD 0x01
+#define ITAB_CAT_FIELD 0x02
+#define ITAB_CAT_INTERNAL 0x04
+#define ITAB_CAT_VENDOR 0x08
+#define ITAB_CAT_SITE 0x10
+#define ITAB_CAT_COUNT 5
+
+/*
+ * Consumer which is using the inittab functions.
+ */
+#define ITAB_CONS_INFO 'i'
+#define ITAB_CONS_SERVER 'd'
+#define ITAB_CONS_SNOOP 's'
+#define ITAB_CONS_MANAGER 'm'
+#define ITAB_CONS_COUNT (sizeof ("idsm") - 1)
+
+/*
+ * Extended error codes, for use with inittab_{en,de}code_e().
+ */
+#define ITAB_SYNTAX_ERROR (-1)
+#define ITAB_BAD_IPADDR (-2)
+#define ITAB_BAD_STRING (-3)
+#define ITAB_BAD_OCTET (-4)
+#define ITAB_BAD_NUMBER (-5)
+#define ITAB_BAD_BOOLEAN (-6)
+#define ITAB_NOT_ENOUGH_IP (-7)
+#define ITAB_BAD_GRAN (-8)
+#define ITAB_NOMEM (-9)
+
+extern uint8_t inittab_type_to_size(dhcp_symbol_t *);
+extern int inittab_verify(dhcp_symbol_t *, dhcp_symbol_t *);
+extern dhcp_symbol_t *inittab_load(uchar_t, char, size_t *);
+extern dhcp_symbol_t *inittab_getbyname(uchar_t, char, const char *);
+extern dhcp_symbol_t *inittab_getbycode(uchar_t, char, uint16_t);
+extern uchar_t *inittab_encode(dhcp_symbol_t *, const char *,
+ uint16_t *, boolean_t);
+extern uchar_t *inittab_encode_e(dhcp_symbol_t *, const char *,
+ uint16_t *, boolean_t, int *);
+extern char *inittab_decode(dhcp_symbol_t *, uchar_t *,
+ uint16_t, boolean_t);
+extern char *inittab_decode_e(dhcp_symbol_t *, uchar_t *,
+ uint16_t, boolean_t, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DHCP_INITTAB_H */
diff --git a/usr/src/lib/libdhcputil/common/dhcp_symbol.c b/usr/src/lib/libdhcputil/common/dhcp_symbol.c
new file mode 100644
index 0000000000..d02d3461a7
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcp_symbol.c
@@ -0,0 +1,884 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <strings.h>
+#include <limits.h>
+#include <errno.h>
+#include <dhcp_impl.h>
+
+#include "dhcp_symbol.h"
+
+/*
+ * The following structure and table are used to define the attributes
+ * of a DHCP symbol category.
+ */
+typedef struct dsym_cat {
+ char *dc_string; /* string value for the category */
+ int dc_minlen; /* min. chars of dc_string to match */
+ dsym_category_t dc_id; /* numerical value for the category */
+ boolean_t dc_dhcptab; /* valid for dhcptab use? */
+ ushort_t dc_min; /* minimum valid code */
+ ushort_t dc_max; /* maximum valid code */
+} dsym_cat_t;
+
+static dsym_cat_t cats[DSYM_CATEGORY_NUM] = {
+ { "Extend", 6, DSYM_EXTEND, B_TRUE, DHCP_LAST_STD + 1,
+ DHCP_SITE_OPT - 1 },
+ { "Vendor=", 6, DSYM_VENDOR, B_TRUE, DHCP_FIRST_OPT,
+ DHCP_LAST_OPT },
+ { "Site", 4, DSYM_SITE, B_TRUE, DHCP_SITE_OPT, DHCP_LAST_OPT },
+ { "Standard", 8, DSYM_STANDARD, B_FALSE, DHCP_FIRST_OPT,
+ DHCP_LAST_STD },
+ { "Field", 5, DSYM_FIELD, B_FALSE, CD_PACKET_START,
+ CD_PACKET_END },
+ { "Internal", 8, DSYM_INTERNAL, B_FALSE, CD_INTRNL_START,
+ CD_INTRNL_END }
+};
+
+/*
+ * The following structure and table are used to define the attributes
+ * of a DHCP symbol type.
+ */
+typedef struct dsym_type {
+ char *dt_string; /* string value for the type */
+ dsym_cdtype_t dt_id; /* numerical value for the type */
+ boolean_t dt_dhcptab; /* valid for dhcptab use? */
+} dsym_type_t;
+
+static dsym_type_t types[DSYM_CDTYPE_NUM] = {
+ { "ASCII", DSYM_ASCII, B_TRUE },
+ { "OCTET", DSYM_OCTET, B_TRUE },
+ { "IP", DSYM_IP, B_TRUE },
+ { "NUMBER", DSYM_NUMBER, B_TRUE },
+ { "BOOL", DSYM_BOOL, B_TRUE },
+ { "INCLUDE", DSYM_INCLUDE, B_FALSE },
+ { "UNUMBER8", DSYM_UNUMBER8, B_TRUE },
+ { "UNUMBER16", DSYM_UNUMBER16, B_TRUE },
+ { "UNUMBER32", DSYM_UNUMBER32, B_TRUE },
+ { "UNUMBER64", DSYM_UNUMBER64, B_TRUE },
+ { "SNUMBER8", DSYM_SNUMBER8, B_TRUE },
+ { "SNUMBER16", DSYM_SNUMBER16, B_TRUE },
+ { "SNUMBER32", DSYM_SNUMBER32, B_TRUE },
+ { "SNUMBER64", DSYM_SNUMBER64, B_TRUE }
+};
+
+/*
+ * symbol delimiters and constants
+ */
+#define DSYM_CLASS_DEL " \t\n"
+#define DSYM_FIELD_DEL ","
+#define DSYM_VENDOR_DEL '='
+#define DSYM_QUOTE '"'
+
+/*
+ * dsym_trim(): trims all whitespace from either side of a string
+ *
+ * input: char **: a pointer to a string to trim of whitespace.
+ * output: none
+ */
+static void
+dsym_trim(char **str)
+{
+
+ char *tmpstr = *str;
+
+ /*
+ * Trim all whitespace from the front of the string.
+ */
+ while (*tmpstr != '\0' && isspace(*tmpstr)) {
+ tmpstr++;
+ }
+
+ /*
+ * Move the str pointer to first non-whitespace char.
+ */
+ *str = tmpstr;
+
+ /*
+ * Check case where the string is nothing but whitespace.
+ */
+ if (*tmpstr == '\0') {
+
+ /*
+ * Trim all whitespace from the end of the string.
+ */
+ tmpstr = *str + strlen(*str) - 1;
+ while (tmpstr >= *str && isspace(*tmpstr)) {
+ tmpstr--;
+ }
+
+ /*
+ * terminate after last non-whitespace char.
+ */
+ *(tmpstr+1) = '\0';
+ }
+}
+
+/*
+ * dsym_get_token(): strtok_r() like routine, except consecutive delimiters
+ * result in an empty string
+ *
+ * note: original string is modified
+ *
+ * input: char *: string in which to search for tokens
+ * char *: list of possible token delimiter characters
+ * char **: location for next call to routine
+ * boolean_t: should delimiters be ignored if within quoted string?
+ * output: char *: token, or NULL if no more tokens
+ */
+static char *
+dsym_get_token(char *str, char *dels, char **lasts, boolean_t quote_support)
+{
+
+ char *ptr = str;
+ char *del;
+ boolean_t found = B_FALSE;
+ boolean_t in_quote = B_FALSE;
+
+ /*
+ * If incoming string has no tokens return a NULL
+ * pointer to signify no more tokens.
+ */
+ if (*ptr == '\0') {
+ return (NULL);
+ }
+
+ /*
+ * Loop until either a token has been identified or until end
+ * of string has been reached.
+ */
+ while (!found && *ptr != '\0') {
+
+ /*
+ * If pointer currently lies within a quoted string,
+ * then do not check for the delimiter.
+ */
+ if (!in_quote) {
+ for (del = dels; !found && *del != '\0'; del++) {
+ if (*del == *ptr) {
+ *ptr++ = '\0';
+ found = B_TRUE;
+ }
+ }
+ }
+
+ /*
+ * If the pointer is pointing at a delimiter, then
+ * check to see if it points to at a quote and update
+ * the state appropriately.
+ */
+ if (!found) {
+ if (quote_support && *ptr == DSYM_QUOTE) {
+ in_quote = !in_quote;
+ }
+ ptr++;
+ }
+ }
+
+ *lasts = ptr;
+
+ return (str);
+}
+
+/*
+ * dsym_get_long(): given a numeric string, returns its long value
+ *
+ * input: const char *: the numeric string
+ * long *: the return location for the long value
+ * output: DSYM_SUCCESS, DSYM_VALUE_OUT_OF_RANGE or DSYM_SYNTAX_ERROR
+ */
+static dsym_errcode_t
+dsym_get_long(const char *str, long *val)
+{
+
+ int ret = DSYM_SUCCESS;
+ int i;
+
+ for (i = 0; str[i] != '\0'; i++) {
+ if (!isdigit(str[i])) {
+ return (DSYM_SYNTAX_ERROR);
+ }
+ }
+
+ errno = 0;
+ *val = strtol(str, NULL, 10);
+ if (errno != 0) {
+ ret = DSYM_VALUE_OUT_OF_RANGE;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_free_classes(): frees the classes allocated by dsym_parse_classes()
+ *
+ * input: dhcp_classes_t *: pointer to structure containing classes to free
+ * output: none
+ */
+void
+dsym_free_classes(dhcp_classes_t *classes)
+{
+
+ int i;
+
+ if (classes->dc_names == NULL) {
+ return;
+ }
+
+ for (i = 0; i < classes->dc_cnt; i++) {
+ free(classes->dc_names[i]);
+ }
+
+ free(classes->dc_names);
+ classes->dc_names = NULL;
+ classes->dc_cnt = 0;
+}
+
+/*
+ * dsym_parse_classes(): given a "Vendor" class string, builds and returns
+ * the list of vendor classes
+ *
+ * input: char *: the "Vendor" class string
+ * dhcp_classes_t *: pointer to the classes structure
+ * output: DSYM_SUCCESS, DSYM_INVALID_CAT, DSYM_EXCEEDS_MAX_CLASS_SIZE,
+ * DSYM_EXCEEDS_CLASS_SIZE, DSYM_SYNTAX_ERROR, or DSYM_NO_MEMORY
+ */
+static dsym_errcode_t
+dsym_parse_classes(char *ptr, dhcp_classes_t *classes_ret)
+{
+
+ char **classes = NULL;
+ char *cp;
+ int len;
+ int ret = DSYM_SUCCESS;
+ int i;
+
+ while (*ptr != '\0') {
+ if (*ptr == DSYM_VENDOR_DEL) {
+ ptr++;
+ break;
+ }
+ ptr++;
+ }
+
+ if (*ptr == '\0') {
+ return (DSYM_INVALID_CAT);
+ }
+
+ if (strlen(ptr) > DSYM_MAX_CLASS_SIZE) {
+ return (DSYM_EXCEEDS_MAX_CLASS_SIZE);
+ }
+
+ dsym_trim(&ptr);
+ classes_ret->dc_cnt = 0;
+ for (i = 0; ret == DSYM_SUCCESS; i++) {
+ cp = dsym_get_token(ptr, DSYM_CLASS_DEL, &ptr, B_TRUE);
+ if (cp == NULL) {
+ break;
+ }
+
+ len = strlen(cp);
+
+ if (len == 0) {
+ continue;
+ } else if (len > DSYM_CLASS_SIZE) {
+ ret = DSYM_EXCEEDS_CLASS_SIZE;
+ continue;
+ }
+
+ if (cp[0] == DSYM_QUOTE && cp[len-1] != DSYM_QUOTE) {
+ ret = DSYM_SYNTAX_ERROR;
+ continue;
+ }
+
+ /* Strip off the quotes */
+ if (cp[0] == DSYM_QUOTE) {
+ cp[len-1] = '\0';
+ cp++;
+ }
+
+ classes = realloc(classes_ret->dc_names,
+ (sizeof (char **)) * (classes_ret->dc_cnt + 1));
+ if (classes == NULL ||
+ (classes[classes_ret->dc_cnt] = strdup(cp))
+ == NULL) {
+ ret = DSYM_NO_MEMORY;
+ continue;
+ }
+ classes_ret->dc_names = classes;
+ classes_ret->dc_cnt++;
+ }
+
+ if (ret != DSYM_SUCCESS) {
+ dsym_free_classes(classes_ret);
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_get_cat_by_name(): given a category field, returns the pointer to its
+ * entry in the internal category table.
+ *
+ * input: const char *: the category name
+ * dsym_cat_t *: the return location for the pointer to the table entry
+ * boolean_t: case-sensitive name compare
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
+ */
+static dsym_errcode_t
+dsym_get_cat_by_name(const char *cat, dsym_cat_t **entry, boolean_t cs)
+{
+
+ dsym_cat_t *entryp = NULL;
+ int ret = DSYM_SUCCESS;
+ int cnt = sizeof (cats) / sizeof (dsym_cat_t);
+ int result;
+ int len;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+
+ len = cats[i].dc_minlen;
+ if (cs) {
+ result = strncmp(cat, cats[i].dc_string, len);
+ } else {
+ result = strncasecmp(cat, cats[i].dc_string, len);
+ }
+
+ if (result == 0) {
+ entryp = &cats[i];
+ break;
+ }
+ }
+
+ if (entryp != NULL) {
+ /*
+ * Special code required for the Vendor category, because we
+ * allow whitespace between the keyword and the delimiter.
+ * If there is no delimiter, then this is an illegal category.
+ */
+ const char *ptr = cat + entryp->dc_minlen;
+ if (entryp->dc_id == DSYM_VENDOR) {
+ while (*ptr != '\0' && isspace(*ptr)) {
+ ptr++;
+ }
+ if (*ptr != DSYM_VENDOR_DEL) {
+ ret = DSYM_INVALID_CAT;
+ }
+ } else {
+ if (*ptr != '\0') {
+ ret = DSYM_INVALID_CAT;
+ }
+ }
+ } else {
+ ret = DSYM_INVALID_CAT;
+ }
+
+ if (ret == DSYM_SUCCESS) {
+ *entry = entryp;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_parse_cat(): given a category field, returns the category value
+ * Note: The category must be a valid dhcptab category.
+ *
+ * input: const char *: a category field
+ * dsym_category_t *: the return location for the category value
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
+ */
+static dsym_errcode_t
+dsym_parse_cat(const char *field, dsym_category_t *cat)
+{
+
+ dsym_cat_t *entry;
+ int ret;
+
+ ret = dsym_get_cat_by_name(field, &entry, B_TRUE);
+ if (ret == DSYM_SUCCESS) {
+ /*
+ * Since this routine is meant to be used to parse dhcptab
+ * symbol definitions, only a subset of the DHCP categories
+ * are valid in this context.
+ */
+ if (entry->dc_dhcptab) {
+ *cat = entry->dc_id;
+ } else {
+ ret = DSYM_INVALID_CAT;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_parse_intrange(): given a DHCP integer field, returns the value
+ *
+ * input: const char *: a DHCP code field
+ * int *: the return location for the value
+ * int: the minimum valid value
+ * int: the maximum valid value
+ * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, or DSYM_VALUE_OUT_OF_RANGE
+ */
+static dsym_errcode_t
+dsym_parse_intrange(const char *field, int *intval, int min, int max)
+{
+
+ int ret;
+ long longval;
+
+ ret = dsym_get_long(field, &longval);
+ if (ret == DSYM_SUCCESS) {
+ if (longval < min || longval > max) {
+ ret = DSYM_VALUE_OUT_OF_RANGE;
+ } else {
+ *intval = (int)longval;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * dsym_validate_code(): given a symbol category and code, validates
+ * that the code is valid for the category
+ *
+ * input: dsym_category_t: the symbol category
+ * uint16_t: the symbol code
+ * output: DSYM_SUCCESS, DSYM_INVALID_CAT or DSYM_CODE_OUT_OF_RANGE
+ */
+static dsym_errcode_t
+dsym_validate_code(dsym_category_t cat, ushort_t code)
+{
+
+ int cnt = sizeof (cats) / sizeof (dsym_cat_t);
+ int i;
+
+ /*
+ * Find the category entry from the internal table.
+ */
+ for (i = 0; i < cnt; i++) {
+ dsym_cat_t *entry;
+ if (cat == cats[i].dc_id) {
+ entry = &cats[i];
+ if (code < entry->dc_min || code > entry->dc_max) {
+ return (DSYM_CODE_OUT_OF_RANGE);
+ }
+ return (DSYM_SUCCESS);
+ }
+ }
+
+ return (DSYM_INVALID_CAT);
+}
+
+/*
+ * dsym_validate_granularity(): given a symbol type, validates
+ * that the granularity is valid for the type
+ *
+ * input: dsym_cdtype_t: the symbol type
+ * uchar_t: the symbol granularity
+ * output: DSYM_SUCCESS or DSYM_VALUE_OUT_OF_RANGE
+ */
+static dsym_errcode_t
+dsym_validate_granularity(dsym_cdtype_t type, uchar_t gran)
+{
+ /*
+ * We only need to check for a 0 with non-boolean types, as
+ * anything else is already validated by the ranges passed to
+ * dsym_parse_intrange() in dsym_parse_field().
+ */
+ if (gran == 0 && type != DSYM_BOOL) {
+ return (DSYM_VALUE_OUT_OF_RANGE);
+ }
+ return (DSYM_SUCCESS);
+}
+
+/*
+ * dsym_get_type_by_name(): given a type field, returns the pointer to its
+ * entry in the internal type table.
+ *
+ * input: const char *: the type name
+ * dsym_type_t *: the return location for the pointer to the table entry
+ * boolean_t: case-sensitive name compare
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
+ */
+static dsym_errcode_t
+dsym_get_type_by_name(const char *type, dsym_type_t **entry, boolean_t cs)
+{
+ int cnt = sizeof (types) / sizeof (dsym_type_t);
+ int result;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+
+ if (cs) {
+ result = strcmp(type, types[i].dt_string);
+ } else {
+ result = strcasecmp(type, types[i].dt_string);
+ }
+
+ if (result == 0) {
+ *entry = &types[i];
+ return (DSYM_SUCCESS);
+ }
+ }
+
+ return (DSYM_INVALID_TYPE);
+}
+
+/*
+ * dsym_parse_type(): given a DHCP type string, returns the type id
+ *
+ * input: char *: a DHCP type string
+ * dsym_cdtype_t *: the return location for the type id
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
+ */
+static dsym_errcode_t
+dsym_parse_type(char *field, dsym_cdtype_t *type)
+{
+
+ dsym_type_t *entry;
+ int ret;
+
+ ret = dsym_get_type_by_name(field, &entry, B_TRUE);
+ if (ret == DSYM_SUCCESS) {
+ /*
+ * Since this routine is meant to be used to parse dhcptab
+ * symbol definitions, only a subset of the DHCP type
+ * are valid in this context.
+ */
+ if (entry->dt_dhcptab) {
+ *type = entry->dt_id;
+ } else {
+ ret = DSYM_INVALID_TYPE;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_free_fields(): frees an array of fields allocated by
+ * dsym_init_parser().
+ *
+ * input: char **: array of fields to free
+ * output: none
+ */
+void
+dsym_free_fields(char **fields)
+{
+ int i;
+ if (fields != NULL) {
+ for (i = 0; i < DSYM_NUM_FIELDS; i++) {
+ free(fields[i]);
+ }
+ free(fields);
+ }
+}
+
+/*
+ * dsym_close_parser(): free up all resources associated with the parser
+ *
+ * input: char **: the fields allocated by dsym_init_parser()
+ * dhcp_symbol_t *: the structure populated by dsym_init_parser()
+ * output: none
+ */
+void
+dsym_close_parser(char **fields, dhcp_symbol_t *sym)
+{
+ dsym_free_fields(fields);
+ dsym_free_classes(&sym->ds_classes);
+}
+
+/*
+ * dsym_init_parser(): initializes the structures used to parse a symbol
+ * value.
+ *
+ * input: const char *: the symbol name
+ * const char *: the symbol value in dhcptab format
+ * char ***: the return location for the symbol fields
+ * dhcp_symbol_t *: the structure which eventually will
+ * be the repository for the parsed symbol data
+ * output: int: DSYM_SUCCESS, DYSM_NO_MEMORY, DSYM_NULL_FIELD or
+ * DSYM_TOO_MANY_FIELDS
+ */
+dsym_errcode_t
+dsym_init_parser(const char *name, const char *value, char ***fields_ret,
+ dhcp_symbol_t *sym)
+{
+
+ int ret = DSYM_SUCCESS;
+ char *cp;
+ char *next;
+ char *field;
+ char **fields;
+ int i;
+
+ /*
+ * Initialize the symbol structure.
+ */
+ sym->ds_category = 0;
+ sym->ds_code = 0;
+ (void) strlcpy(sym->ds_name, name, DSYM_MAX_SYM_LEN);
+ sym->ds_type = 0;
+ sym->ds_gran = 0;
+ sym->ds_max = 0;
+ sym->ds_classes.dc_names = NULL;
+ sym->ds_classes.dc_cnt = 0;
+
+ if ((cp = strdup(value)) == NULL ||
+ (fields = calloc(DSYM_NUM_FIELDS, sizeof (char *))) == NULL) {
+ ret = DSYM_NO_MEMORY;
+ }
+
+ next = cp;
+ for (i = 0; ret == DSYM_SUCCESS && i < DSYM_NUM_FIELDS; i++) {
+
+ field = dsym_get_token(next, DSYM_FIELD_DEL, &next,
+ B_FALSE);
+
+ if (field == NULL) {
+ ret = DSYM_NULL_FIELD;
+ continue;
+ }
+
+ dsym_trim(&field);
+
+ if (strlen(field) == 0) {
+ ret = DSYM_NULL_FIELD;
+ continue;
+ }
+
+ if ((fields[i] = strdup(field)) == NULL) {
+ ret = DSYM_NO_MEMORY;
+ continue;
+ }
+ }
+
+ if (ret == DSYM_SUCCESS &&
+ dsym_get_token(next, DSYM_FIELD_DEL, &next, B_FALSE) != NULL) {
+ ret = DSYM_TOO_MANY_FIELDS;
+ }
+
+ if (ret != DSYM_SUCCESS) {
+ dsym_free_fields(fields);
+ } else {
+ *fields_ret = fields;
+ }
+
+ free(cp);
+ return (ret);
+}
+
+/*
+ * dsym_parse_field(): parses the specified symbol field.
+ *
+ * input: int: the field number to be parsed.
+ * char **: symbol fields initialized by dsym_init_parser()
+ * dhcp_symbol_t *: the structure which will be the repository
+ * for the parsed field
+ * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, DSYM_CODE_OUT_OF_RANGE,
+ * DSYM_INVALID_CAT, DSYM_INVALID_TYPE, DSYM_EXCEEDS_CLASS_SIZE,
+ * DSYM_EXCEEDS_MAX_CLASS_SIZE, DSYM_NO_MEMORY,
+ * DSYM_INVALID_FIELD_NUM, DSYM_VALUE_OUT_OF_RANGE
+ */
+dsym_errcode_t
+dsym_parse_field(int field_num, char **fields, dhcp_symbol_t *sym)
+{
+
+ int ret = DSYM_SUCCESS;
+ int intval;
+
+ switch (field_num) {
+
+ case DSYM_CAT_FIELD:
+ ret = dsym_parse_cat(fields[field_num], &sym->ds_category);
+ if (ret == DSYM_SUCCESS && sym->ds_category == DSYM_VENDOR) {
+ ret = dsym_parse_classes(fields[field_num],
+ &sym->ds_classes);
+ }
+ break;
+
+ case DSYM_CODE_FIELD:
+ ret = dsym_parse_intrange(fields[field_num], &intval, 0,
+ USHRT_MAX);
+ if (ret == DSYM_SUCCESS) {
+ sym->ds_code = (ushort_t)intval;
+ ret = dsym_validate_code(sym->ds_category,
+ sym->ds_code);
+ }
+ break;
+
+ case DSYM_TYPE_FIELD:
+ ret = dsym_parse_type(fields[field_num], &sym->ds_type);
+ break;
+
+ case DSYM_GRAN_FIELD:
+ ret = dsym_parse_intrange(fields[field_num], &intval, 0,
+ UCHAR_MAX);
+ if (ret == DSYM_SUCCESS) {
+ sym->ds_gran = (uchar_t)intval;
+ ret = dsym_validate_granularity(sym->ds_type,
+ sym->ds_gran);
+ }
+ break;
+
+ case DSYM_MAX_FIELD:
+ ret = dsym_parse_intrange(fields[field_num], &intval, 0,
+ UCHAR_MAX);
+ if (ret == DSYM_SUCCESS) {
+ sym->ds_max = (uchar_t)intval;
+ }
+ break;
+ default:
+ ret = DSYM_INVALID_FIELD_NUM;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_parser(): parses a DHCP symbol value
+ *
+ * input: char **: symbol fields initialized by dsym_init_parser()
+ * dhcp_symbol_t *: the structure which will be the repository
+ * for the parsed field
+ * int *: last field processed
+ * boolean_t: parse all fields even though errors occur?
+ * output: int: DSYM_SUCCESS, DSYM_SYNTAX_ERROR, DSYM_CODE_OUT_OF_RANGE,
+ * DSYM_INVALID_CAT, DSYM_INVALID_TYPE, DSYM_EXCEEDS_CLASS_SIZE,
+ * DSYM_EXCEEDS_MAX_CLASS_SIZE, DSYM_NO_MEMORY
+ * DSYM_INVALID_FIELD_NUM, DSYM_VALUE_OUT_OF_RANGE
+ */
+dsym_errcode_t
+dsym_parser(char **fields, dhcp_symbol_t *sym, int *lastField,
+ boolean_t bestEffort)
+{
+
+ int ret = DSYM_SUCCESS;
+ int tret = DSYM_SUCCESS;
+ int i;
+
+ *lastField = -1;
+ for (i = DSYM_FIRST_FIELD;
+ tret == DSYM_SUCCESS && i < DSYM_NUM_FIELDS; i++) {
+
+ tret = dsym_parse_field(i, fields, sym);
+ if (tret != DSYM_SUCCESS) {
+ if (ret == DSYM_SUCCESS) {
+ ret = tret;
+ }
+ if (bestEffort) {
+ *lastField = i;
+ tret = DSYM_SUCCESS;
+ }
+ }
+ }
+
+ if (*lastField == -1) {
+ *lastField = i - 1;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_get_cat_id(): given a category string, return the associated id.
+ *
+ * input: const char *: the category name
+ * dsym_category_t *: the return location for the id
+ * boolean_t: case-sensitive name compare
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
+ */
+dsym_errcode_t
+dsym_get_cat_id(const char *cat, dsym_category_t *id, boolean_t cs)
+{
+
+ dsym_cat_t *entry;
+ int ret;
+
+ ret = dsym_get_cat_by_name(cat, &entry, cs);
+ if (ret == DSYM_SUCCESS) {
+ *id = entry->dc_id;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_get_code_ranges(): given a category field, returns its valid code
+ * ranges.
+ *
+ * input: const char *: the category name
+ * ushort *: return location for the minimum code value.
+ * ushort *: return location for the maximum code value.
+ * boolean_t: case-sensitive name compare
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_CAT
+ */
+dsym_errcode_t
+dsym_get_code_ranges(const char *cat, ushort_t *min, ushort_t *max,
+ boolean_t cs)
+{
+
+ dsym_cat_t *entry;
+ int ret;
+
+ ret = dsym_get_cat_by_name(cat, &entry, cs);
+ if (ret == DSYM_SUCCESS) {
+ *min = entry->dc_min;
+ *max = entry->dc_max;
+ }
+
+ return (ret);
+}
+
+/*
+ * dsym_get_type_id(): given a type string, return the associated type id.
+ *
+ * input: const char *: the type name
+ * dsym_cdtype_t *: the return location for the id
+ * boolean_t: case-sensitive name compare
+ * output: int: DSYM_SUCCESS or DSYM_INVALID_TYPE
+ */
+dsym_errcode_t
+dsym_get_type_id(const char *type, dsym_cdtype_t *id, boolean_t cs)
+{
+
+ dsym_type_t *entry;
+ int ret;
+
+ ret = dsym_get_type_by_name(type, &entry, cs);
+ if (ret == DSYM_SUCCESS) {
+ *id = entry->dt_id;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/lib/libdhcputil/common/dhcp_symbol.h b/usr/src/lib/libdhcputil/common/dhcp_symbol.h
new file mode 100644
index 0000000000..fcfd65c20f
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcp_symbol.h
@@ -0,0 +1,131 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DHCP_SYMBOL_H
+#define _DHCP_SYMBOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file, along with <dhcp_symbol_common.h>, contains the DHCP symbol
+ * constants and the definitions for the external interfaces to the parsing
+ * logic (contained in dhcp_symbol.c) for symbol definitions. These
+ * definitions can and should be used by all consumers of DHCP symbols.
+ */
+
+#include <sys/types.h>
+#include <dhcp_svc_private.h>
+#include <dhcp_symbol_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Vendor class length (and implicitly, the number of classes)
+ */
+#define DSYM_CLASS_SIZE DSVC_MAX_MACSYM_LEN /* Single class max */
+#define DSYM_MAX_CLASS_SIZE (DSYM_CLASS_SIZE * 10) /* At least 10 */
+
+/*
+ * Maximum symbol length is defined by the libdhcpsvc.
+ */
+#define DSYM_MAX_SYM_LEN DSVC_MAX_MACSYM_LEN
+
+/*
+ * symbol parsing error codes
+ */
+typedef enum {
+ DSYM_SUCCESS,
+ DSYM_SYNTAX_ERROR,
+ DSYM_NULL_FIELD,
+ DSYM_TOO_MANY_FIELDS,
+ DSYM_CODE_OUT_OF_RANGE,
+ DSYM_VALUE_OUT_OF_RANGE,
+ DSYM_INVALID_CAT,
+ DSYM_INVALID_TYPE,
+ DSYM_EXCEEDS_CLASS_SIZE,
+ DSYM_EXCEEDS_MAX_CLASS_SIZE,
+ DSYM_NO_MEMORY,
+ DSYM_INVALID_FIELD_NUM
+} dsym_errcode_t;
+
+/*
+ * symbol fields
+ */
+#define DSYM_CAT_FIELD 0
+#define DSYM_CODE_FIELD 1
+#define DSYM_TYPE_FIELD 2
+#define DSYM_GRAN_FIELD 3
+#define DSYM_MAX_FIELD 4
+#define DSYM_NUM_FIELDS 5
+#define DSYM_FIRST_FIELD DSYM_CAT_FIELD
+
+/*
+ * This structure is used by the dhcp_symbol_t structure below
+ * when the option being defined is a vendor option. In which case,
+ * this structure contains the client classes for which the option
+ * applies.
+ */
+typedef struct dhcp_classes {
+ char **dc_names;
+ uint8_t dc_cnt;
+} dhcp_classes_t;
+
+/*
+ * This structure is used to define a DHCP symbol. The structure is
+ * used by both the inittab parsing routines and by the dhcptab parsing
+ * routines to define a symbol definition in either of those tables.
+ */
+typedef struct dhcp_symbol {
+ dsym_category_t ds_category; /* category */
+ ushort_t ds_code; /* option code */
+ char ds_name[DSYM_MAX_SYM_LEN + 1]; /* option name */
+ dsym_cdtype_t ds_type; /* type of parm */
+ uchar_t ds_gran; /* granularity */
+ uchar_t ds_max; /* maximum number */
+ dhcp_classes_t ds_classes; /* client classes */
+} dhcp_symbol_t;
+
+extern void dsym_free_fields(char **);
+extern void dsym_free_classes(dhcp_classes_t *);
+extern void dsym_close_parser(char **, dhcp_symbol_t *);
+extern dsym_errcode_t dsym_init_parser(const char *, const char *, char ***,
+ dhcp_symbol_t *);
+extern dsym_errcode_t dsym_parse_field(int, char **, dhcp_symbol_t *);
+extern dsym_errcode_t dsym_parser(char **, dhcp_symbol_t *, int *, boolean_t);
+extern dsym_errcode_t dsym_get_cat_id(const char *, dsym_category_t *,
+ boolean_t);
+extern dsym_errcode_t dsym_get_code_ranges(const char *cat, ushort_t *,
+ ushort_t *, boolean_t);
+extern dsym_errcode_t dsym_get_type_id(const char *, dsym_cdtype_t *,
+ boolean_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DHCP_SYMBOL_H */
diff --git a/usr/src/lib/libdhcputil/common/dhcpmsg.c b/usr/src/lib/libdhcputil/common/dhcpmsg.c
new file mode 100644
index 0000000000..5329af5fbf
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcpmsg.c
@@ -0,0 +1,216 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <libintl.h>
+#include <string.h>
+#include <limits.h>
+
+#include "dhcpmsg.h"
+
+static boolean_t is_daemon = B_FALSE;
+static boolean_t is_verbose = B_FALSE;
+static char program[PATH_MAX] = "<unknown>";
+static int debug_level;
+
+static const char *err_to_string(int);
+static int err_to_syslog(int);
+
+/*
+ * dhcpmsg(): logs a message to the console or to syslog
+ *
+ * input: int: the level to log the message at
+ * const char *: a printf-like format string
+ * ...: arguments to the format string
+ * output: void
+ */
+
+/*PRINTFLIKE2*/
+void
+dhcpmsg(int errlevel, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[512];
+ char *errmsg;
+
+ if ((errlevel == MSG_DEBUG2 && (debug_level < 2)) ||
+ (errlevel == MSG_DEBUG && (debug_level < 1)) ||
+ (errlevel == MSG_VERBOSE && !is_verbose))
+ return;
+
+ va_start(ap, fmt);
+
+ /*
+ * either log to stderr, or log to syslog. print out unix
+ * error message if errlevel is MSG_ERR and errno is set
+ */
+
+ if (is_daemon) {
+ (void) snprintf(buf, sizeof (buf), (errlevel == MSG_ERR &&
+ errno != 0) ? "%s: %%m\n" : "%s\n", gettext(fmt));
+ (void) vsyslog(err_to_syslog(errlevel), buf, ap);
+ } else {
+ errmsg = strerror(errno);
+ if (errmsg == NULL)
+ errmsg = dgettext(TEXT_DOMAIN, "<unknown error>");
+
+ (void) snprintf(buf, sizeof (buf), (errlevel == MSG_ERR &&
+ errno != 0) ? "%s: %s: %s: %s\n" : "%s: %s: %s\n", program,
+ dgettext(TEXT_DOMAIN, err_to_string(errlevel)),
+ gettext(fmt), errmsg);
+
+ (void) vfprintf(stderr, buf, ap);
+ }
+
+ va_end(ap);
+}
+
+/*
+ * dhcpmsg_init(): opens and initializes the DHCP messaging facility
+ *
+ * input: const char *: the name of the executable
+ * boolean_t: whether the executable is a daemon
+ * boolean_t: whether the executable is running "verbosely"
+ * int: the debugging level the executable is being run at
+ * output: void
+ */
+
+void
+dhcpmsg_init(const char *program_name, boolean_t daemon, boolean_t verbose,
+ int level)
+{
+ (void) strlcpy(program, program_name, sizeof (program));
+
+ debug_level = level;
+ is_verbose = verbose;
+
+ if (daemon) {
+ is_daemon = B_TRUE;
+ (void) openlog(program, LOG_PID, LOG_DAEMON);
+ if (is_verbose) {
+ syslog(err_to_syslog(MSG_VERBOSE), "%s",
+ dgettext(TEXT_DOMAIN, "Daemon started"));
+ }
+ }
+}
+
+/*
+ * dhcpmsg_fini(): closes the DHCP messaging facility.
+ *
+ * input: void
+ * output: void
+ */
+
+void
+dhcpmsg_fini(void)
+{
+ if (is_daemon) {
+ if (is_verbose) {
+ syslog(err_to_syslog(MSG_VERBOSE), "%s",
+ dgettext(TEXT_DOMAIN, "Daemon terminated"));
+ }
+ closelog();
+ }
+}
+
+/*
+ * err_to_syslog(): converts a dhcpmsg log level into a syslog log level
+ *
+ * input: int: the dhcpmsg log level
+ * output: int: the syslog log level
+ */
+
+static int
+err_to_syslog(int errlevel)
+{
+ switch (errlevel) {
+
+ case MSG_DEBUG:
+ case MSG_DEBUG2:
+ return (LOG_DEBUG);
+
+ case MSG_ERROR:
+ case MSG_ERR:
+ return (LOG_ERR);
+
+ case MSG_WARNING:
+ return (LOG_WARNING);
+
+ case MSG_NOTICE:
+ return (LOG_NOTICE);
+
+ case MSG_CRIT:
+ return (LOG_CRIT);
+
+ case MSG_VERBOSE:
+ case MSG_INFO:
+ return (LOG_INFO);
+ }
+
+ return (LOG_INFO);
+}
+
+/*
+ * err_to_string(): converts a log level into a string
+ *
+ * input: int: the log level
+ * output: const char *: the stringified log level
+ */
+
+static const char *
+err_to_string(int errlevel)
+{
+ switch (errlevel) {
+
+ case MSG_DEBUG:
+ case MSG_DEBUG2:
+ return ("debug");
+
+ case MSG_ERR:
+ case MSG_ERROR:
+ return ("error");
+
+ case MSG_WARNING:
+ return ("warning");
+
+ case MSG_NOTICE:
+ return ("notice");
+
+ case MSG_CRIT:
+ return ("CRITICAL");
+
+ case MSG_VERBOSE:
+ case MSG_INFO:
+ return ("info");
+ }
+
+ return ("<unknown>");
+}
diff --git a/usr/src/lib/libdhcputil/common/dhcpmsg.h b/usr/src/lib/libdhcputil/common/dhcpmsg.h
new file mode 100644
index 0000000000..822359cd46
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/dhcpmsg.h
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _DHCPMSG_H
+#define _DHCPMSG_H
+
+#pragma ident "%W% %E% SMI"
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h> /* since consumers may want to 0 errno */
+
+/*
+ * dhcpmsg.[ch] comprise the interface used to log messages, either to
+ * syslog(3C), or to the screen, depending on the debug level. see
+ * dhcpmsg.c for documentation on how to use the exported functions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * the syslog levels, while useful, do not provide enough flexibility
+ * to do everything we want. consequently, we introduce another set
+ * of levels, which map to a syslog level, but also potentially add
+ * additional behavior.
+ */
+
+enum {
+ MSG_DEBUG, /* LOG_DEBUG, only if debug_level is 1 */
+ MSG_DEBUG2, /* LOG_DEBUG, only if debug_level is 1 or 2 */
+ MSG_INFO, /* LOG_INFO */
+ MSG_VERBOSE, /* LOG_INFO, only if is_verbose is true */
+ MSG_NOTICE, /* LOG_NOTICE */
+ MSG_WARNING, /* LOG_WARNING */
+ MSG_ERR, /* LOG_ERR, use errno if nonzero */
+ MSG_ERROR, /* LOG_ERR */
+ MSG_CRIT /* LOG_CRIT */
+};
+
+extern void dhcpmsg(int, const char *, ...);
+extern void dhcpmsg_init(const char *, boolean_t, boolean_t, int);
+extern void dhcpmsg_fini(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DHCPMSG_H */
diff --git a/usr/src/lib/libdhcputil/common/llib-ldhcputil b/usr/src/lib/libdhcputil/common/llib-ldhcputil
new file mode 100644
index 0000000000..799c396a38
--- /dev/null
+++ b/usr/src/lib/libdhcputil/common/llib-ldhcputil
@@ -0,0 +1,35 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <dhcp_symbol.h>
+#include <dhcp_inittab.h>
+#include <dhcpmsg.h>
+#include <dhcp_impl.h>
diff --git a/usr/src/lib/libdhcputil/i386/Makefile b/usr/src/lib/libdhcputil/i386/Makefile
new file mode 100644
index 0000000000..f800c19b30
--- /dev/null
+++ b/usr/src/lib/libdhcputil/i386/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdhcputil/i386/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdhcputil/libdhcputil.xcl b/usr/src/lib/libdhcputil/libdhcputil.xcl
new file mode 100644
index 0000000000..f70fae37d1
--- /dev/null
+++ b/usr/src/lib/libdhcputil/libdhcputil.xcl
@@ -0,0 +1,25 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+msgid "%s: %%m\n"
+msgid "%s\n"
+msgid "%s: %s: %s: %s\n"
+msgid "%s: %s: %s\n"
diff --git a/usr/src/lib/libdhcputil/req.flg b/usr/src/lib/libdhcputil/req.flg
new file mode 100644
index 0000000000..d7246ea4b0
--- /dev/null
+++ b/usr/src/lib/libdhcputil/req.flg
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+find_files "s.*" usr/src/common/net/dhcp
diff --git a/usr/src/lib/libdhcputil/sparc/Makefile b/usr/src/lib/libdhcputil/sparc/Makefile
new file mode 100644
index 0000000000..b83201c1d5
--- /dev/null
+++ b/usr/src/lib/libdhcputil/sparc/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdhcputil/sparc/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdhcputil/spec/Makefile b/usr/src/lib/libdhcputil/spec/Makefile
new file mode 100644
index 0000000000..ad76aff4a5
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%W% %E% SMI"
+#
+# lib/libdhcputil/spec/Makefile
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libdhcputil/spec/Makefile.targ b/usr/src/lib/libdhcputil/spec/Makefile.targ
new file mode 100644
index 0000000000..d0cbd76ba4
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/Makefile.targ
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%W% %E% SMI"
+#
+# lib/libdhcputil/spec/Makefile.targ
+
+LIBRARY = libdhcputil.a
+VERS = .1
+OBJECTS = dhcputil.o
+SPECCPP = -I../.. -I$(SRC)/common/net/dhcp
diff --git a/usr/src/lib/libdhcputil/spec/amd64/Makefile b/usr/src/lib/libdhcputil/spec/amd64/Makefile
new file mode 100644
index 0000000000..4ee7613aaf
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/amd64/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+.KEEP_STATE:
+
+all clean clobber install lint:
+ @echo "Nothing to $@ on amd64"
diff --git a/usr/src/lib/libdhcputil/spec/dhcputil.spec b/usr/src/lib/libdhcputil/spec/dhcputil.spec
new file mode 100644
index 0000000000..eaaed806b3
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/dhcputil.spec
@@ -0,0 +1,178 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdhcpagent/spec/dhcputil.spec
+
+function dhcpmsg
+include <dhcpmsg.h>
+declaration void dhcpmsg(int level, const char *format, ...)
+version SUNWprivate_1.1
+end
+
+function dhcpmsg_init
+include <dhcpmsg.h>
+declaration void dhcpmsg_init(const char *program_name, boolean_t \
+ is_daemon, boolean_t is_verbose, int debugging_level)
+version SUNWprivate_1.1
+end
+
+function dhcpmsg_fini
+include <dhcpmsg.h>
+declaration void dhcpmsg_fini(void)
+version SUNWprivate_1.1
+end
+
+function inittab_load
+include <dhcp_inittab.h>
+declaration dhcp_symbol_t *inittab_load(uchar_t categories, char \
+ consumer, size_t *n_entries)
+version SUNWprivate_1.1
+end
+
+function inittab_getbyname
+include <dhcp_inittab.h>
+declaration dhcp_symbol_t *inittab_getbyname(uchar_t categories, \
+ char consumer, const char *name)
+version SUNWprivate_1.1
+end
+
+function inittab_getbycode
+include <dhcp_inittab.h>
+declaration dhcp_symbol_t *inittab_getbycode(uchar_t categories, \
+ char consumer, uint16_t code)
+version SUNWprivate_1.1
+end
+
+function inittab_verify
+include <dhcp_inittab.h>
+declaration int inittab_verify(dhcp_symbol_t *inittab_entry, \
+ dhcp_symbol_t *internal_entry)
+version SUNWprivate_1.1
+end
+
+function inittab_encode
+include <dhcp_inittab.h>
+declaration uchar_t *inittab_encode(dhcp_symbol_t *inittab_entry, \
+ const char *value, uint16_t *lengthp, boolean_t \
+ just_payload)
+version SUNWprivate_1.1
+end
+
+function inittab_decode
+include <dhcp_inittab.h>
+declaration char *inittab_decode(dhcp_symbol_t *inittab_entry, \
+ uchar_t *payload, uint16_t length, boolean_t \
+ just_payload)
+version SUNWprivate_1.1
+end
+
+function inittab_encode_e
+include <dhcp_inittab.h>
+declaration uchar_t *inittab_encode_e(dhcp_symbol_t *inittab_entry, \
+ const char *value, uint16_t *lengthp, boolean_t \
+ just_payload, int *ierrnop)
+version SUNWprivate_1.1
+end
+
+function inittab_decode_e
+include <dhcp_inittab.h>
+declaration char *inittab_decode_e(dhcp_symbol_t *inittab_entry, \
+ uchar_t *payload, uint16_t length, boolean_t \
+ just_payload, int *ierrno)
+version SUNWprivate_1.1
+end
+
+function inittab_type_to_size
+include <dhcp_inittab.h>
+declaration uint8_t inittab_type_to_size(dhcp_symbol_t *inittab_entry)
+version SUNWprivate_1.1
+end
+
+function dsym_close_parser
+include <dhcp_symbol.h>
+declaration void dsym_close_parser(char **fields, dhcp_symbol_t *sym)
+version SUNWprivate_1.1
+end
+
+function dsym_free_classes
+include <dhcp_symbol.h>
+declaration void dsym_free_classes(dhcp_classes_t *classes)
+version SUNWprivate_1.1
+end
+
+function dsym_free_fields
+include <dhcp_symbol.h>
+declaration void dsym_free_fields(char **fields)
+version SUNWprivate_1.1
+end
+
+function dsym_init_parser
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_init_parser(const char * name, \
+ const char *value, char ***fields_ret, dhcp_symbol_t *sym)
+version SUNWprivate_1.1
+end
+
+function dsym_parse_field
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_parse_field(int field_num, \
+ char **fields, dhcp_symbol_t *sym)
+version SUNWprivate_1.1
+end
+
+function dsym_parser
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_parser(char **fields, dhcp_symbol_t *sym, \
+ int *lastField, boolean_t bestEffort)
+version SUNWprivate_1.1
+end
+
+function dsym_get_cat_id
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_get_cat_id(const char *cat, \
+ dsym_category_t *id, boolean_t cs)
+version SUNWprivate_1.1
+end
+
+function dsym_get_code_ranges
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_get_code_ranges(const char *cat, \
+ ushort_t *min, ushort_t *max, boolean_t cs)
+version SUNWprivate_1.1
+end
+
+function dsym_get_type_id
+include <dhcp_symbol.h>
+declaration dsym_errcode_t dsym_get_type_id(const char *type, \
+ dsym_cdtype_t *id, boolean_t cs)
+version SUNWprivate_1.1
+end
+
+function dhcp_options_scan
+include <dhcp_impl.h>
+declaration int dhcp_options_scan(PKT_LIST *pl, boolean_t scan_vendor)
+version SUNWprivate_1.2
+end
diff --git a/usr/src/lib/libdhcputil/spec/i386/Makefile b/usr/src/lib/libdhcputil/spec/i386/Makefile
new file mode 100644
index 0000000000..6ad76c1208
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/i386/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%W% %E% SMI"
+#
+# lib/libdhcputil/spec/i386/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libdhcputil/spec/sparc/Makefile b/usr/src/lib/libdhcputil/spec/sparc/Makefile
new file mode 100644
index 0000000000..4aba178c6f
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/sparc/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%W% %E% SMI"
+#
+# lib/libdhcputil/spec/sparc/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libdhcputil/spec/sparcv9/Makefile b/usr/src/lib/libdhcputil/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..86fdf91c6b
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/sparcv9/Makefile
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%W% %E% SMI"
+#
+# lib/libdhcputil/spec/sparcv9/Makefile
+
+.KEEP_STATE:
+
+all clean clobber install lint:
+ @echo "Nothing to $@ on sparcv9"
diff --git a/usr/src/lib/libdhcputil/spec/versions b/usr/src/lib/libdhcputil/spec/versions
new file mode 100644
index 0000000000..05e8e3e9ca
--- /dev/null
+++ b/usr/src/lib/libdhcputil/spec/versions
@@ -0,0 +1,43 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+sparc {
+ SUNWprivate_1.2: {SUNWprivate_1.1};
+ SUNWprivate_1.1;
+}
+sparcv9 {
+ SUNWprivate_1.2: {SUNWprivate_1.1};
+ SUNWprivate_1.1;
+}
+i386 {
+ SUNWprivate_1.2: {SUNWprivate_1.1};
+ SUNWprivate_1.1;
+}
+amd64 {
+ SUNWprivate_1.2: {SUNWprivate_1.1};
+ SUNWprivate_1.1;
+}