# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. # Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved. # Use is subject to license terms. # 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) # Hammerhead: amd64-only SUBDIRS = $(MACH64) 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 .KEEP_STATE: all clean clobber install: $(SUBDIRS) install_h: $(ROOTHDRS) $(ROOTHDRDIR)/%: $(COMHDRDIR)/% $(INS.file) check: $(CHECKHDRS) $(POFILE): pofile_MSGFILES _msg: $(MSGDOMAINPOFILE) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include $(SRC)/Makefile.msg.targ include $(SRC)/lib/Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. 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) LDLIBS += -lc -lgen -linetutil -ldlpi SRCDIR = ../common SRCS = $(LOCOBJS:%.o=$(SRCDIR)/%.c) $(COMOBJS:%.o=$(COMDIR)/%.c) CFLAGS += $(CCVERBOSE) CERRWARN += -Wno-switch CERRWARN += $(CNOWARN_UNINIT) CPPFLAGS += -I$(COMDIR) # not linted SMATCH=off .KEEP_STATE: all: $(LIBS) pics/%.o: $(COMDIR)/%.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) include ../../Makefile.targ # # 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 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(5), dhcptab(5)). 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(5). 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(1) 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(5) 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(5), inittab is authoritative. EXTEND symbol definitions in dhcptab(5) 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) # # 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 2012 OmniTI Computer Consulting, Inc. All rights reserved. # include ../Makefile.com include ../../Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTCOMPATLINKS64) /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dhcp_symbol.h" #include "dhcp_inittab.h" 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. * * Note: we have only an IPv4 version here. The inittab_verify() function is * used by the DHCP server and manager. We'll need a new function if the * server is extended to DHCPv6. */ 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; const char *inittab_path; uchar_t category_code; dsym_cdtype_t type; if (categories & ITAB_CAT_V6) { inittab_path = getenv("DHCP_INITTAB6_PATH"); if (inittab_path == NULL) inittab_path = ITAB_INITTAB6_PATH; } else { 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", 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)); entry.ds_dhcpv6 = (categories & ITAB_CAT_V6) ? 1 : 0; 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 * * notes: IPv4 only */ int inittab_verify(const 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); } /* * get_hw_type(): interpret ",hwtype" in the input string, as part of a DUID. * The hwtype string is optional, and must be 0-65535 if * present. * * input: char **: pointer to string pointer * int *: error return value * output: int: hardware type, or -1 for empty, or -2 for error. */ static int get_hw_type(char **strp, int *ierrnop) { char *str = *strp; ulong_t hwtype; if (*str++ != ',') { *ierrnop = ITAB_BAD_NUMBER; return (-2); } if (*str == ',' || *str == '\0') { *strp = str; return (-1); } hwtype = strtoul(str, strp, 0); if (errno != 0 || *strp == str || hwtype > 65535) { *ierrnop = ITAB_BAD_NUMBER; return (-2); } else { return ((int)hwtype); } } /* * get_mac_addr(): interpret ",macaddr" in the input string, as part of a DUID. * The 'macaddr' may be a hex string (in any standard format), * or the name of a physical interface. If an interface name * is given, then the interface type is extracted as well. * * input: const char *: input string * int *: error return value * uint16_t *: hardware type output (network byte order) * int: hardware type input; -1 for empty * uchar_t *: output buffer for MAC address * output: int: length of MAC address, or -1 for error */ static int get_mac_addr(const char *str, int *ierrnop, uint16_t *hwret, int hwtype, uchar_t *outbuf) { int maclen; int dig, val; dlpi_handle_t dh; dlpi_info_t dlinfo; char chr; if (*str != '\0') { if (*str++ != ',') goto failed; if (dlpi_open(str, &dh, 0) != DLPI_SUCCESS) { maclen = 0; dig = val = 0; /* * Allow MAC addresses with separators matching regexp * (:|-| *). */ while ((chr = *str++) != '\0') { if (isdigit(chr)) { val = (val << 4) + chr - '0'; } else if (isxdigit(chr)) { val = (val << 4) + chr - (isupper(chr) ? 'A' : 'a') + 10; } else if (isspace(chr) && dig == 0) { continue; } else if (chr == ':' || chr == '-' || isspace(chr)) { dig = 1; } else { goto failed; } if (++dig == 2) { *outbuf++ = val; maclen++; dig = val = 0; } } } else { if (dlpi_bind(dh, DLPI_ANY_SAP, NULL) != DLPI_SUCCESS || dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) { dlpi_close(dh); goto failed; } maclen = dlinfo.di_physaddrlen; (void) memcpy(outbuf, dlinfo.di_physaddr, maclen); dlpi_close(dh); if (hwtype == -1) hwtype = dlpi_arptype(dlinfo.di_mactype); } } if (hwtype == -1) goto failed; *hwret = htons(hwtype); return (maclen); failed: *ierrnop = ITAB_BAD_NUMBER; return (-1); } /* * 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: const 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 * int *: error return value * output: uchar_t *: a dynamically allocated byte array with converted data */ uchar_t * inittab_encode_e(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp, boolean_t just_payload, int *ierrnop) { int hlen = 0; uint16_t length; uchar_t n_entries = 0; const char *valuep; char *currp; uchar_t *result = NULL; uchar_t *optstart; unsigned int i; uint8_t type_size = inittab_type_to_size(ie); boolean_t is_signed; uint_t vallen, reslen; dhcpv6_option_t *d6o; int type; char *cp2; *ierrnop = 0; if (type_size == 0) { *ierrnop = ITAB_SYNTAX_ERROR; return (NULL); } switch (ie->ds_type) { case DSYM_ASCII: n_entries = strlen(value); /* no NUL */ break; case DSYM_OCTET: vallen = strlen(value); n_entries = vallen / 2; n_entries += vallen % 2; break; case DSYM_DOMAIN: /* * Maximum (worst-case) encoded length is one byte more than * the number of characters on input. */ n_entries = strlen(value) + 1; break; case DSYM_DUID: /* Worst case is ":::::" */ n_entries = strlen(value); if (n_entries < DLPI_PHYSADDR_MAX) n_entries = DLPI_PHYSADDR_MAX; n_entries += sizeof (duid_llt_t); break; default: /* * figure out the number of entries by counting the spaces * in the value string */ for (valuep = value; valuep++ != NULL; n_entries++) valuep = strchr(valuep, ' '); break; } /* * 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) hlen = ie->ds_dhcpv6 ? sizeof (*d6o) : 2; length = n_entries * type_size; if (hlen + length > 0) result = malloc(hlen + length); if ((optstart = result) != NULL && !just_payload) optstart += hlen; switch (ie->ds_type) { case DSYM_ASCII: if (optstart == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } (void) memcpy(optstart, value, length); break; case DSYM_DOMAIN: if (optstart == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } /* * Note that this encoder always presents the trailing 0-octet * when dealing with a list. This means that you can't have * non-fully-qualified members anywhere but at the end of a * list (or as the only member of the list). */ valuep = value; while (*valuep != '\0') { int dig, val, inchr; boolean_t escape; uchar_t *flen; /* * Skip over whitespace that delimits list members. */ if (isascii(*valuep) && isspace(*valuep)) { valuep++; continue; } dig = val = 0; escape = B_FALSE; flen = optstart++; while ((inchr = *valuep) != '\0') { valuep++; /* * Just copy non-ASCII text directly to the * output string. This simplifies the use of * other ctype macros below, as, unlike the * special isascii function, they don't handle * non-ASCII. */ if (!isascii(inchr)) { escape = B_FALSE; *optstart++ = inchr; continue; } if (escape) { /* * Handle any of \D, \DD, or \DDD for * a digit escape. */ if (isdigit(inchr)) { val = val * 10 + inchr - '0'; if (++dig == 3) { *optstart++ = val; dig = val = 0; escape = B_FALSE; } continue; } else if (dig > 0) { /* * User terminated \D or \DD * with non-digit. An error, * but we can assume they mean * to treat as \00D or \0DD. */ *optstart++ = val; dig = val = 0; } /* Fall through and copy character */ escape = B_FALSE; } else if (inchr == '\\') { escape = B_TRUE; continue; } else if (inchr == '.') { /* * End of component. Write the length * prefix. If the component is zero * length (i.e., ".."), the just omit * it. */ *flen = (optstart - flen) - 1; if (*flen > 0) flen = optstart++; continue; } else if (isspace(inchr)) { /* * Unescaped space; end of domain name * in list. */ break; } *optstart++ = inchr; } /* * Handle trailing escape sequence. If string ends * with \, then assume user wants \ at end of encoded * string. If it ends with \D or \DD, assume \00D or * \0DD. */ if (escape) *optstart++ = dig > 0 ? val : '\\'; *flen = (optstart - flen) - 1; /* * If user specified FQDN with trailing '.', then above * will result in zero for the last component length. * We're done, and optstart already points to the start * of the next in list. Otherwise, we need to write a * single zero byte to end the entry, if there are more * entries that will be decoded. */ while (isascii(*valuep) && isspace(*valuep)) valuep++; if (*flen > 0 && *valuep != '\0') *optstart++ = '\0'; } length = (optstart - result) - hlen; break; case DSYM_DUID: if (optstart == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } errno = 0; type = strtoul(value, &currp, 0); if (errno != 0 || value == currp || type > 65535 || (*currp != ',' && *currp != '\0')) { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } switch (type) { case DHCPV6_DUID_LLT: { duid_llt_t dllt; int hwtype; ulong_t tstamp; int maclen; if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) { free(result); return (NULL); } if (*currp++ != ',') { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } if (*currp == ',' || *currp == '\0') { tstamp = time(NULL) - DUID_TIME_BASE; } else { tstamp = strtoul(currp, &cp2, 0); if (errno != 0 || currp == cp2) { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } currp = cp2; } maclen = get_mac_addr(currp, ierrnop, &dllt.dllt_hwtype, hwtype, optstart + sizeof (dllt)); if (maclen == -1) { free(result); return (NULL); } dllt.dllt_dutype = htons(type); dllt.dllt_time = htonl(tstamp); (void) memcpy(optstart, &dllt, sizeof (dllt)); length = maclen + sizeof (dllt); break; } case DHCPV6_DUID_EN: { duid_en_t den; ulong_t enterp; if (*currp++ != ',') { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } enterp = strtoul(currp, &cp2, 0); DHCPV6_SET_ENTNUM(&den, enterp); if (errno != 0 || currp == cp2 || enterp != DHCPV6_GET_ENTNUM(&den) || (*cp2 != ',' && *cp2 != '\0')) { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } if (*cp2 == ',') cp2++; vallen = strlen(cp2); reslen = (vallen + 1) / 2; if (hexascii_to_octet(cp2, vallen, optstart + sizeof (den), &reslen) != 0) { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } den.den_dutype = htons(type); (void) memcpy(optstart, &den, sizeof (den)); length = reslen + sizeof (den); break; } case DHCPV6_DUID_LL: { duid_ll_t dll; int hwtype; int maclen; if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) { free(result); return (NULL); } maclen = get_mac_addr(currp, ierrnop, &dll.dll_hwtype, hwtype, optstart + sizeof (dll)); if (maclen == -1) { free(result); return (NULL); } dll.dll_dutype = htons(type); (void) memcpy(optstart, &dll, sizeof (dll)); length = maclen + sizeof (dll); break; } default: if (*currp == ',') currp++; vallen = strlen(currp); reslen = (vallen + 1) / 2; if (hexascii_to_octet(currp, vallen, optstart + 2, &reslen) != 0) { free(result); *ierrnop = ITAB_BAD_NUMBER; return (NULL); } optstart[0] = type >> 8; optstart[1] = type; length = reslen + 2; break; } break; case DSYM_OCTET: if (optstart == NULL) { *ierrnop = ITAB_BAD_OCTET; return (NULL); } reslen = length; /* Call libinetutil function to decode */ if (hexascii_to_octet(value, vallen, optstart, &reslen) != 0) { free(result); *ierrnop = ITAB_BAD_OCTET; return (NULL); } break; case DSYM_IP: case DSYM_IPV6: if (optstart == 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(ie->ds_type == DSYM_IP ? AF_INET : AF_INET6, valuep, optstart) != 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; } optstart += type_size; } break; case DSYM_NUMBER: /* FALLTHRU */ case DSYM_UNUMBER8: /* FALLTHRU */ case DSYM_SNUMBER8: /* FALLTHRU */ case DSYM_UNUMBER16: /* FALLTHRU */ case DSYM_SNUMBER16: /* FALLTHRU */ case DSYM_UNUMBER24: /* FALLTHRU */ case DSYM_UNUMBER32: /* FALLTHRU */ case DSYM_SNUMBER32: /* FALLTHRU */ case DSYM_UNUMBER64: /* FALLTHRU */ case DSYM_SNUMBER64: if (optstart == 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, optstart, 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 add the option * code and length fields in. */ if (!just_payload) { if (ie->ds_dhcpv6) { /* LINTED: alignment */ d6o = (dhcpv6_option_t *)result; d6o->d6o_code = htons(ie->ds_code); d6o->d6o_len = htons(length); } else { result[0] = ie->ds_code; result[1] = length; } } if (lengthp != NULL) *lengthp = length + hlen; 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(const dhcp_symbol_t *ie, const uchar_t *payload, uint16_t length, boolean_t just_payload, int *ierrnop) { char *resultp, *result = NULL; uint_t n_entries; struct in_addr in_addr; in6_addr_t in6_addr; uint8_t type_size = inittab_type_to_size(ie); boolean_t is_signed; int type; *ierrnop = 0; if (type_size == 0) { *ierrnop = ITAB_SYNTAX_ERROR; return (NULL); } if (!just_payload) { if (ie->ds_dhcpv6) { dhcpv6_option_t d6o; (void) memcpy(&d6o, payload, sizeof (d6o)); length = ntohs(d6o.d6o_len); payload += sizeof (d6o); } else { 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_DOMAIN: /* * A valid, decoded RFC 1035 domain string or sequence of * strings is always the same size as the encoded form, but we * allow for RFC 1035 \DDD and \\ and \. escaping. * * Decoding stops at the end of the input or the first coding * violation. Coding violations result in discarding the * offending list entry entirely. Note that we ignore the 255 * character overall limit on domain names. */ if ((result = malloc(4 * length + 1)) == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } resultp = result; while (length > 0) { char *dstart; int slen; dstart = resultp; while (length > 0) { slen = *payload++; length--; /* Upper two bits of length must be zero */ if ((slen & 0xc0) != 0 || slen > length) { length = 0; resultp = dstart; break; } if (resultp != dstart) *resultp++ = '.'; if (slen == 0) break; length -= slen; while (slen > 0) { if (!isascii(*payload) || !isgraph(*payload)) { (void) snprintf(resultp, 5, "\\%03d", *(unsigned char *)payload); resultp += 4; payload++; } else { if (*payload == '.' || *payload == '\\') *resultp++ = '\\'; *resultp++ = *payload++; } slen--; } } if (resultp != dstart && length > 0) *resultp++ = ' '; } *resultp = '\0'; break; case DSYM_DUID: /* * First, determine the type of DUID. We need at least two * octets worth of data to grab the type code. Once we have * that, the number of octets required for representation * depends on the type. */ if (length < 2) { *ierrnop = ITAB_BAD_GRAN; return (NULL); } type = (payload[0] << 8) + payload[1]; switch (type) { case DHCPV6_DUID_LLT: { duid_llt_t dllt; if (length < sizeof (dllt)) { *ierrnop = ITAB_BAD_GRAN; return (NULL); } (void) memcpy(&dllt, payload, sizeof (dllt)); payload += sizeof (dllt); length -= sizeof (dllt); n_entries = sizeof ("1,65535,4294967295,") + length * 3; if ((result = malloc(n_entries)) == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } (void) snprintf(result, n_entries, "%d,%u,%u,", type, ntohs(dllt.dllt_hwtype), ntohl(dllt.dllt_time)); break; } case DHCPV6_DUID_EN: { duid_en_t den; if (length < sizeof (den)) { *ierrnop = ITAB_BAD_GRAN; return (NULL); } (void) memcpy(&den, payload, sizeof (den)); payload += sizeof (den); length -= sizeof (den); n_entries = sizeof ("2,4294967295,") + length * 2; if ((result = malloc(n_entries)) == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } (void) snprintf(result, n_entries, "%d,%u,", type, DHCPV6_GET_ENTNUM(&den)); break; } case DHCPV6_DUID_LL: { duid_ll_t dll; if (length < sizeof (dll)) { *ierrnop = ITAB_BAD_GRAN; return (NULL); } (void) memcpy(&dll, payload, sizeof (dll)); payload += sizeof (dll); length -= sizeof (dll); n_entries = sizeof ("3,65535,") + length * 3; if ((result = malloc(n_entries)) == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } (void) snprintf(result, n_entries, "%d,%u,", type, ntohs(dll.dll_hwtype)); break; } default: n_entries = sizeof ("0,") + length * 2; if ((result = malloc(n_entries)) == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } (void) snprintf(result, n_entries, "%d,", type); break; } resultp = result + strlen(result); n_entries -= strlen(result); if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) { if (length > 0) { resultp += snprintf(resultp, 3, "%02X", *payload++); length--; } while (length-- > 0) { resultp += snprintf(resultp, 4, ":%02X", *payload++); } } else { while (length-- > 0) { resultp += snprintf(resultp, 3, "%02X", *payload++); } } break; case DSYM_OCTET: result = malloc(n_entries * (sizeof ("0xNN") + 1)); if (result == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } result[0] = '\0'; resultp = result; if (n_entries > 0) { resultp += sprintf(resultp, "0x%02X", *payload++); n_entries--; } while (n_entries-- > 0) resultp += sprintf(resultp, " 0x%02X", *payload++); break; case DSYM_IP: case DSYM_IPV6: if ((length / type_size) % 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 * (ie->ds_type == DSYM_IP ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN)); if (result == NULL) { *ierrnop = ITAB_NOMEM; return (NULL); } for (resultp = result; n_entries != 0; n_entries--) { if (ie->ds_type == DSYM_IP) { (void) memcpy(&in_addr.s_addr, payload, sizeof (ipaddr_t)); (void) strcpy(resultp, inet_ntoa(in_addr)); } else { (void) memcpy(&in6_addr, payload, sizeof (in6_addr)); (void) inet_ntop(AF_INET6, &in6_addr, resultp, INET6_ADDRSTRLEN); } resultp += strlen(resultp); if (n_entries > 1) *resultp++ = ' '; payload += type_size; } *resultp = '\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(const 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(const dhcp_symbol_t *ie, const 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; /* FALLTHROUGH */ 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 3: uint32 = 0; (void) memcpy((uchar_t *)&uint32 + 1, from, 3); to += sprintf(to, is_signed ? "%ld" : "%lu", ntohl(uint32)); 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", ntohll(uint64)); break; default: *ierrnop = ITAB_BAD_NUMBER; inittab_msg("decode_number: unknown integer size `%d'", size); return (B_FALSE); } if (n_entries > 0) *to++ = ' '; } *to = '\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++, to += size) { /* * 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 = 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, &uint16, 2); break; case 3: uint32 = htonl(strtoul(from, &endptr, 0)); if (errno != 0 || from == endptr) { goto error; } (void) memcpy(to, (uchar_t *)&uint32 + 1, 3); break; case 4: uint32 = htonl(strtoul(from, &endptr, 0)); if (errno != 0 || from == endptr) { goto error; } (void) memcpy(to, &uint32, 4); break; case 8: uint64 = htonll(strtoull(from, &endptr, 0)); if (errno != 0 || from == endptr) { goto error; } (void) memcpy(to, &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(const dhcp_symbol_t *ie) { switch (ie->ds_type) { case DSYM_DUID: case DSYM_DOMAIN: case DSYM_ASCII: case DSYM_OCTET: case DSYM_SNUMBER8: case DSYM_UNUMBER8: return (1); case DSYM_SNUMBER16: case DSYM_UNUMBER16: return (2); case DSYM_UNUMBER24: return (3); 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); case DSYM_IPV6: return (sizeof (in6_addr_t)); } 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); } /* * 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 } }; /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DHCP_INITTAB_H #define _DHCP_INITTAB_H #include #include #include /* * 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_INITTAB6_PATH "/etc/dhcp/inittab6" #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_V6 0x20 #define ITAB_CAT_COUNT 6 /* * 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(const dhcp_symbol_t *); extern int inittab_verify(const 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(const dhcp_symbol_t *, const char *, uint16_t *, boolean_t); extern uchar_t *inittab_encode_e(const dhcp_symbol_t *, const char *, uint16_t *, boolean_t, int *); extern char *inittab_decode(const dhcp_symbol_t *, const uchar_t *, uint16_t, boolean_t); extern char *inittab_decode_e(const dhcp_symbol_t *, const uchar_t *, uint16_t, boolean_t, int *); #ifdef __cplusplus } #endif #endif /* _DHCP_INITTAB_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #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[] = { { "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[] = { { "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 }, { "UNUMBER24", DSYM_UNUMBER24, 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 }, { "IPV6", DSYM_IPV6, B_TRUE }, { "DUID", DSYM_DUID, B_TRUE }, { "DOMAIN", DSYM_DOMAIN, 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); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2014 Garrett D'Amore * * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DHCP_SYMBOL_H #define _DHCP_SYMBOL_H /* * This file, along with , 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 #include #ifdef __cplusplus extern "C" { #endif /* * Vendor class length (and implicitly, the number of classes) */ #define DSYM_CLASS_SIZE 128 /* Single class max */ #define DSYM_MAX_CLASS_SIZE (DSYM_CLASS_SIZE * 10) /* At least 10 */ /* * Maximum symbol length */ #define DSYM_MAX_SYM_LEN 128 /* * 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. * Note that ds_dhcpv6 is defined last so that it needn't be initialized * as part of the inittab_table[] definition. */ 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 */ uchar_t ds_dhcpv6; /* dhcpv6 flag */ } 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 */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include "dhcpmsg.h" static boolean_t is_daemon = B_FALSE; static boolean_t is_verbose = B_FALSE; static char program[PATH_MAX] = ""; 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 */ 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, ""); (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 (""); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DHCPMSG_H #define _DHCPMSG_H #include #include #include #include /* 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 */ }; /* PRINTFLIKE2 */ 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 */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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) 2006, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # # There really should be only one SUNWprivate version. # Don't add any more. Add new private symbols to SUNWprivate_1.2 $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.2 { global: dhcp_options_scan; dhcpv6_find_option; dhcpv6_pkt_option; } SUNWprivate_1.1; SYMBOL_VERSION SUNWprivate_1.1 { global: dhcpmsg; dhcpmsg_fini; dhcpmsg_init; dsym_close_parser; dsym_free_classes; dsym_free_fields; dsym_get_cat_id; dsym_get_code_ranges; dsym_get_type_id; dsym_init_parser; dsym_parse_field; dsym_parser; inittab_decode; inittab_decode_e; inittab_encode; inittab_encode_e; inittab_getbycode; inittab_getbyname; inittab_load; inittab_type_to_size; inittab_verify; local: *; }; # # 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" #!/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