1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <sys/types.h>
  29 #include <string.h>
  30 #include <stdlib.h>
  31 #include <stdio.h>
  32 #include <errno.h>
  33 #include <stdarg.h>
  34 #include <limits.h>
  35 #include <ctype.h>
  36 #include <libgen.h>
  37 #include <sys/isa_defs.h>
  38 #include <sys/socket.h>
  39 #include <net/if_arp.h>
  40 #include <netinet/in.h>
  41 #include <arpa/inet.h>
  42 #include <sys/sysmacros.h>
  43 #include <libinetutil.h>
  44 #include <libdlpi.h>
  45 #include <netinet/dhcp6.h>
  46 
  47 #include "dhcp_symbol.h"
  48 #include "dhcp_inittab.h"
  49 
  50 static uint64_t         dhcp_htonll(uint64_t);
  51 static uint64_t         dhcp_ntohll(uint64_t);
  52 static void             inittab_msg(const char *, ...);
  53 static uchar_t          category_to_code(const char *);
  54 static boolean_t        encode_number(uint8_t, uint8_t, boolean_t, uint8_t,
  55                             const char *, uint8_t *, int *);
  56 static boolean_t        decode_number(uint8_t, uint8_t, boolean_t, uint8_t,
  57                             const uint8_t *, char *, int *);
  58 static dhcp_symbol_t    *inittab_lookup(uchar_t, char, const char *, int32_t,
  59                             size_t *);
  60 static dsym_category_t  itabcode_to_dsymcode(uchar_t);
  61 static boolean_t        parse_entry(char *, char **);
  62 
  63 /*
  64  * forward declaration of our internal inittab_table[].  too bulky to put
  65  * up front -- check the end of this file for its definition.
  66  *
  67  * Note: we have only an IPv4 version here.  The inittab_verify() function is
  68  * used by the DHCP server and manager.  We'll need a new function if the
  69  * server is extended to DHCPv6.
  70  */
  71 static dhcp_symbol_t    inittab_table[];
  72 
  73 /*
  74  * the number of fields in the inittab and names for the fields.  note that
  75  * this order is meaningful to parse_entry(); other functions should just
  76  * use them as indexes into the array returned from parse_entry().
  77  */
  78 #define ITAB_FIELDS     7
  79 enum { ITAB_NAME, ITAB_CODE, ITAB_TYPE, ITAB_GRAN, ITAB_MAX, ITAB_CONS,
  80     ITAB_CAT };
  81 
  82 /*
  83  * the category_map_entry_t is used to map the inittab category codes to
  84  * the dsym codes.  the reason the codes are different is that the inittab
  85  * needs to have the codes be ORable such that queries can retrieve more
  86  * than one category at a time.  this map is also used to map the inittab
  87  * string representation of a category to its numerical code.
  88  */
  89 typedef struct category_map_entry {
  90         dsym_category_t cme_dsymcode;
  91         char            *cme_name;
  92         uchar_t         cme_itabcode;
  93 } category_map_entry_t;
  94 
  95 static category_map_entry_t category_map[] = {
  96         { DSYM_STANDARD,        "STANDARD",     ITAB_CAT_STANDARD },
  97         { DSYM_FIELD,           "FIELD",        ITAB_CAT_FIELD },
  98         { DSYM_INTERNAL,        "INTERNAL",     ITAB_CAT_INTERNAL },
  99         { DSYM_VENDOR,          "VENDOR",       ITAB_CAT_VENDOR },
 100         { DSYM_SITE,            "SITE",         ITAB_CAT_SITE }
 101 };
 102 
 103 /*
 104  * inittab_load(): returns all inittab entries with the specified criteria
 105  *
 106  *   input: uchar_t: the categories the consumer is interested in
 107  *          char: the consumer type of the caller
 108  *          size_t *: set to the number of entries returned
 109  *  output: dhcp_symbol_t *: an array of dynamically allocated entries
 110  *          on success, NULL upon failure
 111  */
 112 
 113 dhcp_symbol_t   *
 114 inittab_load(uchar_t categories, char consumer, size_t *n_entries)
 115 {
 116         return (inittab_lookup(categories, consumer, NULL, -1, n_entries));
 117 }
 118 
 119 /*
 120  * inittab_getbyname(): returns an inittab entry with the specified criteria
 121  *
 122  *   input: int: the categories the consumer is interested in
 123  *          char: the consumer type of the caller
 124  *          char *: the name of the inittab entry the consumer wants
 125  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
 126  *          on success, NULL upon failure
 127  */
 128 
 129 dhcp_symbol_t   *
 130 inittab_getbyname(uchar_t categories, char consumer, const char *name)
 131 {
 132         return (inittab_lookup(categories, consumer, name, -1, NULL));
 133 }
 134 
 135 /*
 136  * inittab_getbycode(): returns an inittab entry with the specified criteria
 137  *
 138  *   input: uchar_t: the categories the consumer is interested in
 139  *          char: the consumer type of the caller
 140  *          uint16_t: the code of the inittab entry the consumer wants
 141  *  output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
 142  *          on success, NULL upon failure
 143  */
 144 
 145 dhcp_symbol_t   *
 146 inittab_getbycode(uchar_t categories, char consumer, uint16_t code)
 147 {
 148         return (inittab_lookup(categories, consumer, NULL, code, NULL));
 149 }
 150 
 151 /*
 152  * inittab_lookup(): returns inittab entries with the specified criteria
 153  *
 154  *   input: uchar_t: the categories the consumer is interested in
 155  *          char: the consumer type of the caller
 156  *          const char *: the name of the entry the caller is interested
 157  *              in, or NULL if the caller doesn't care
 158  *          int32_t: the code the caller is interested in, or -1 if the
 159  *              caller doesn't care
 160  *          size_t *: set to the number of entries returned
 161  *  output: dhcp_symbol_t *: dynamically allocated dhcp_symbol structures
 162  *          on success, NULL upon failure
 163  */
 164 
 165 static dhcp_symbol_t *
 166 inittab_lookup(uchar_t categories, char consumer, const char *name,
 167     int32_t code, size_t *n_entriesp)
 168 {
 169         FILE                    *inittab_fp;
 170         dhcp_symbol_t           *new_entries, *entries = NULL;
 171         dhcp_symbol_t           entry;
 172         char                    buffer[ITAB_MAX_LINE_LEN];
 173         char                    *fields[ITAB_FIELDS];
 174         unsigned long           line = 0;
 175         size_t                  i, n_entries = 0;
 176         const char              *inittab_path;
 177         uchar_t                 category_code;
 178         dsym_cdtype_t           type;
 179 
 180         if (categories & ITAB_CAT_V6) {
 181                 inittab_path = getenv("DHCP_INITTAB6_PATH");
 182                 if (inittab_path == NULL)
 183                         inittab_path = ITAB_INITTAB6_PATH;
 184         } else {
 185                 inittab_path = getenv("DHCP_INITTAB_PATH");
 186                 if (inittab_path == NULL)
 187                         inittab_path = ITAB_INITTAB_PATH;
 188         }
 189 
 190         inittab_fp = fopen(inittab_path, "r");
 191         if (inittab_fp == NULL) {
 192                 inittab_msg("inittab_lookup: fopen: %s: %s",
 193                     inittab_path, strerror(errno));
 194                 return (NULL);
 195         }
 196 
 197         (void) bufsplit(",\n", 0, NULL);
 198         while (fgets(buffer, sizeof (buffer), inittab_fp) != NULL) {
 199 
 200                 line++;
 201 
 202                 /*
 203                  * make sure the string didn't overflow our buffer
 204                  */
 205                 if (strchr(buffer, '\n') == NULL) {
 206                         inittab_msg("inittab_lookup: line %li: too long, "
 207                             "skipping", line);
 208                         continue;
 209                 }
 210 
 211                 /*
 212                  * skip `pure comment' lines
 213                  */
 214                 for (i = 0; buffer[i] != '\0'; i++)
 215                         if (isspace(buffer[i]) == 0)
 216                                 break;
 217 
 218                 if (buffer[i] == ITAB_COMMENT_CHAR || buffer[i] == '\0')
 219                         continue;
 220 
 221                 /*
 222                  * parse the entry out into fields.
 223                  */
 224                 if (parse_entry(buffer, fields) == B_FALSE) {
 225                         inittab_msg("inittab_lookup: line %li: syntax error, "
 226                             "skipping", line);
 227                         continue;
 228                 }
 229 
 230                 /*
 231                  * validate the values in the entries; skip if invalid.
 232                  */
 233                 if (atoi(fields[ITAB_GRAN]) > ITAB_GRAN_MAX) {
 234                         inittab_msg("inittab_lookup: line %li: granularity `%s'"
 235                             " out of range, skipping", line, fields[ITAB_GRAN]);
 236                         continue;
 237                 }
 238 
 239                 if (atoi(fields[ITAB_MAX]) > ITAB_MAX_MAX) {
 240                         inittab_msg("inittab_lookup: line %li: maximum `%s' "
 241                             "out of range, skipping", line, fields[ITAB_MAX]);
 242                         continue;
 243                 }
 244 
 245                 if (dsym_get_type_id(fields[ITAB_TYPE], &type, B_FALSE) !=
 246                     DSYM_SUCCESS) {
 247                         inittab_msg("inittab_lookup: line %li: type `%s' "
 248                             "is invalid, skipping", line, fields[ITAB_TYPE]);
 249                         continue;
 250                 }
 251 
 252                 /*
 253                  * find out whether this entry of interest to our consumer,
 254                  * and if so, throw it onto the set of entries we'll return.
 255                  * check categories last since it's the most expensive check.
 256                  */
 257                 if (strchr(fields[ITAB_CONS], consumer) == NULL)
 258                         continue;
 259 
 260                 if (code != -1 && atoi(fields[ITAB_CODE]) != code)
 261                         continue;
 262 
 263                 if (name != NULL && strcasecmp(fields[ITAB_NAME], name) != 0)
 264                         continue;
 265 
 266                 category_code = category_to_code(fields[ITAB_CAT]);
 267                 if ((category_code & categories) == 0)
 268                         continue;
 269 
 270                 /*
 271                  * looks like a match.  allocate an entry and fill it in
 272                  */
 273                 new_entries = realloc(entries, (n_entries + 1) *
 274                     sizeof (dhcp_symbol_t));
 275 
 276                 /*
 277                  * if we run out of memory, might as well return what we can
 278                  */
 279                 if (new_entries == NULL) {
 280                         inittab_msg("inittab_lookup: ran out of memory "
 281                             "allocating dhcp_symbol_t's");
 282                         break;
 283                 }
 284 
 285                 entry.ds_max      = atoi(fields[ITAB_MAX]);
 286                 entry.ds_code     = atoi(fields[ITAB_CODE]);
 287                 entry.ds_type     = type;
 288                 entry.ds_gran     = atoi(fields[ITAB_GRAN]);
 289                 entry.ds_category = itabcode_to_dsymcode(category_code);
 290                 entry.ds_classes.dc_cnt   = 0;
 291                 entry.ds_classes.dc_names = NULL;
 292                 (void) strlcpy(entry.ds_name, fields[ITAB_NAME],
 293                     sizeof (entry.ds_name));
 294                 entry.ds_dhcpv6   = (categories & ITAB_CAT_V6) ? 1 : 0;
 295 
 296                 entries = new_entries;
 297                 entries[n_entries++] = entry;
 298         }
 299 
 300         if (ferror(inittab_fp) != 0) {
 301                 inittab_msg("inittab_lookup: error on inittab stream");
 302                 clearerr(inittab_fp);
 303         }
 304 
 305         (void) fclose(inittab_fp);
 306 
 307         if (n_entriesp != NULL)
 308                 *n_entriesp = n_entries;
 309 
 310         return (entries);
 311 }
 312 
 313 /*
 314  * parse_entry(): parses an entry out into its constituent fields
 315  *
 316  *   input: char *: the entry
 317  *          char **: an array of ITAB_FIELDS length which contains
 318  *                   pointers into the entry on upon return
 319  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
 320  */
 321 
 322 static boolean_t
 323 parse_entry(char *entry, char **fields)
 324 {
 325         char    *category, *spacep;
 326         size_t  n_fields, i;
 327 
 328         /*
 329          * due to a mistake made long ago, the first and second fields of
 330          * each entry are not separated by a comma, but rather by
 331          * whitespace -- have bufsplit() treat the two fields as one, then
 332          * pull them apart afterwards.
 333          */
 334         n_fields = bufsplit(entry, ITAB_FIELDS - 1, fields);
 335         if (n_fields != (ITAB_FIELDS - 1))
 336                 return (B_FALSE);
 337 
 338         /*
 339          * pull the first and second fields apart.  this is complicated
 340          * since the first field can contain embedded whitespace (so we
 341          * must separate the two fields by the last span of whitespace).
 342          *
 343          * first, find the initial span of whitespace.  if there isn't one,
 344          * then the entry is malformed.
 345          */
 346         category = strpbrk(fields[ITAB_NAME], " \t");
 347         if (category == NULL)
 348                 return (B_FALSE);
 349 
 350         /*
 351          * find the last span of whitespace.
 352          */
 353         do {
 354                 while (isspace(*category))
 355                         category++;
 356 
 357                 spacep = strpbrk(category, " \t");
 358                 if (spacep != NULL)
 359                         category = spacep;
 360         } while (spacep != NULL);
 361 
 362         /*
 363          * NUL-terminate the first byte of the last span of whitespace, so
 364          * that the first field doesn't have any residual trailing
 365          * whitespace.
 366          */
 367         spacep = category - 1;
 368         while (isspace(*spacep))
 369                 spacep--;
 370 
 371         if (spacep <= fields[0])
 372                 return (B_FALSE);
 373 
 374         *++spacep = '\0';
 375 
 376         /*
 377          * remove any whitespace from the fields.
 378          */
 379         for (i = 0; i < n_fields; i++) {
 380                 while (isspace(*fields[i]))
 381                         fields[i]++;
 382         }
 383         fields[ITAB_CAT] = category;
 384 
 385         return (B_TRUE);
 386 }
 387 
 388 /*
 389  * inittab_verify(): verifies that a given inittab entry matches an internal
 390  *                   definition
 391  *
 392  *   input: dhcp_symbol_t *: the inittab entry to verify
 393  *          dhcp_symbol_t *: if non-NULL, a place to store the internal
 394  *                             inittab entry upon return
 395  *  output: int: ITAB_FAILURE, ITAB_SUCCESS, or ITAB_UNKNOWN
 396  *
 397  *   notes: IPv4 only
 398  */
 399 
 400 int
 401 inittab_verify(const dhcp_symbol_t *inittab_ent, dhcp_symbol_t *internal_ent)
 402 {
 403         unsigned int    i;
 404 
 405         for (i = 0; inittab_table[i].ds_name[0] != '\0'; i++) {
 406 
 407                 if (inittab_ent->ds_category != inittab_table[i].ds_category)
 408                         continue;
 409 
 410                 if (inittab_ent->ds_code == inittab_table[i].ds_code) {
 411                         if (internal_ent != NULL)
 412                                 *internal_ent = inittab_table[i];
 413 
 414                         if (inittab_table[i].ds_type != inittab_ent->ds_type ||
 415                             inittab_table[i].ds_gran != inittab_ent->ds_gran ||
 416                             inittab_table[i].ds_max  != inittab_ent->ds_max)
 417                                 return (ITAB_FAILURE);
 418 
 419                         return (ITAB_SUCCESS);
 420                 }
 421         }
 422 
 423         return (ITAB_UNKNOWN);
 424 }
 425 
 426 /*
 427  * get_hw_type(): interpret ",hwtype" in the input string, as part of a DUID.
 428  *                The hwtype string is optional, and must be 0-65535 if
 429  *                present.
 430  *
 431  *   input: char **: pointer to string pointer
 432  *          int *: error return value
 433  *  output: int: hardware type, or -1 for empty, or -2 for error.
 434  */
 435 
 436 static int
 437 get_hw_type(char **strp, int *ierrnop)
 438 {
 439         char *str = *strp;
 440         ulong_t hwtype;
 441 
 442         if (*str++ != ',') {
 443                 *ierrnop = ITAB_BAD_NUMBER;
 444                 return (-2);
 445         }
 446         if (*str == ',' || *str == '\0') {
 447                 *strp = str;
 448                 return (-1);
 449         }
 450         hwtype = strtoul(str, strp, 0);
 451         if (errno != 0 || *strp == str || hwtype > 65535) {
 452                 *ierrnop = ITAB_BAD_NUMBER;
 453                 return (-2);
 454         } else {
 455                 return ((int)hwtype);
 456         }
 457 }
 458 
 459 /*
 460  * get_mac_addr(): interpret ",macaddr" in the input string, as part of a DUID.
 461  *                 The 'macaddr' may be a hex string (in any standard format),
 462  *                 or the name of a physical interface.  If an interface name
 463  *                 is given, then the interface type is extracted as well.
 464  *
 465  *   input: const char *: input string
 466  *          int *: error return value
 467  *          uint16_t *: hardware type output (network byte order)
 468  *          int: hardware type input; -1 for empty
 469  *          uchar_t *: output buffer for MAC address
 470  *  output: int: length of MAC address, or -1 for error
 471  */
 472 
 473 static int
 474 get_mac_addr(const char *str, int *ierrnop, uint16_t *hwret, int hwtype,
 475     uchar_t *outbuf)
 476 {
 477         int maclen;
 478         int dig, val;
 479         dlpi_handle_t dh;
 480         dlpi_info_t dlinfo;
 481         char chr;
 482 
 483         if (*str != '\0') {
 484                 if (*str++ != ',')
 485                         goto failed;
 486                 if (dlpi_open(str, &dh, 0) != DLPI_SUCCESS) {
 487                         maclen = 0;
 488                         dig = val = 0;
 489                         /*
 490                          * Allow MAC addresses with separators matching regexp
 491                          * (:|-| *).
 492                          */
 493                         while ((chr = *str++) != '\0') {
 494                                 if (isdigit(chr)) {
 495                                         val = (val << 4) + chr - '0';
 496                                 } else if (isxdigit(chr)) {
 497                                         val = (val << 4) + chr -
 498                                             (isupper(chr) ? 'A' : 'a') + 10;
 499                                 } else if (isspace(chr) && dig == 0) {
 500                                         continue;
 501                                 } else if (chr == ':' || chr == '-' ||
 502                                     isspace(chr)) {
 503                                         dig = 1;
 504                                 } else {
 505                                         goto failed;
 506                                 }
 507                                 if (++dig == 2) {
 508                                         *outbuf++ = val;
 509                                         maclen++;
 510                                         dig = val = 0;
 511                                 }
 512                         }
 513                 } else {
 514                         if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
 515                                 dlpi_close(dh);
 516                                 goto failed;
 517                         }
 518                         maclen = dlinfo.di_physaddrlen;
 519                         (void) memcpy(outbuf, dlinfo.di_physaddr, maclen);
 520                         dlpi_close(dh);
 521                         if (hwtype == -1)
 522                                 hwtype = dlpi_arptype(dlinfo.di_mactype);
 523                 }
 524         }
 525         if (hwtype == -1)
 526                 goto failed;
 527         *hwret = htons(hwtype);
 528         return (maclen);
 529 
 530 failed:
 531         *ierrnop = ITAB_BAD_NUMBER;
 532         return (-1);
 533 }
 534 
 535 /*
 536  * inittab_encode_e(): converts a string representation of a given datatype into
 537  *                   binary; used for encoding ascii values into a form that
 538  *                   can be put in DHCP packets to be sent on the wire.
 539  *
 540  *   input: const dhcp_symbol_t *: the entry describing the value option
 541  *          const char *: the value to convert
 542  *          uint16_t *: set to the length of the binary data returned
 543  *          boolean_t: if false, return a full DHCP option
 544  *          int *: error return value
 545  *  output: uchar_t *: a dynamically allocated byte array with converted data
 546  */
 547 
 548 uchar_t *
 549 inittab_encode_e(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
 550     boolean_t just_payload, int *ierrnop)
 551 {
 552         int             hlen = 0;
 553         uint16_t        length;
 554         uchar_t         n_entries = 0;
 555         const char      *valuep;
 556         char            *currp;
 557         uchar_t         *result = NULL;
 558         uchar_t         *optstart;
 559         unsigned int    i;
 560         uint8_t         type_size = inittab_type_to_size(ie);
 561         boolean_t       is_signed;
 562         uint_t          vallen, reslen;
 563         dhcpv6_option_t *d6o;
 564         int             type;
 565         char            *cp2;
 566 
 567         *ierrnop = 0;
 568         if (type_size == 0) {
 569                 *ierrnop = ITAB_SYNTAX_ERROR;
 570                 return (NULL);
 571         }
 572 
 573         switch (ie->ds_type) {
 574         case DSYM_ASCII:
 575                 n_entries = strlen(value);              /* no NUL */
 576                 break;
 577 
 578         case DSYM_OCTET:
 579                 vallen = strlen(value);
 580                 n_entries = vallen / 2;
 581                 n_entries += vallen % 2;
 582                 break;
 583 
 584         case DSYM_DOMAIN:
 585                 /*
 586                  * Maximum (worst-case) encoded length is one byte more than
 587                  * the number of characters on input.
 588                  */
 589                 n_entries = strlen(value) + 1;
 590                 break;
 591 
 592         case DSYM_DUID:
 593                 /* Worst case is ":::::" */
 594                 n_entries = strlen(value);
 595                 if (n_entries < DLPI_PHYSADDR_MAX)
 596                         n_entries = DLPI_PHYSADDR_MAX;
 597                 n_entries += sizeof (duid_llt_t);
 598                 break;
 599 
 600         default:
 601                 /*
 602                  * figure out the number of entries by counting the spaces
 603                  * in the value string
 604                  */
 605                 for (valuep = value; valuep++ != NULL; n_entries++)
 606                         valuep = strchr(valuep, ' ');
 607                 break;
 608         }
 609 
 610         /*
 611          * if we're gonna return a complete option, then include the
 612          * option length and code in the size of the packet we allocate
 613          */
 614         if (!just_payload)
 615                 hlen = ie->ds_dhcpv6 ? sizeof (*d6o) : 2;
 616 
 617         length = n_entries * type_size;
 618         if (hlen + length > 0)
 619                 result = malloc(hlen + length);
 620 
 621         if ((optstart = result) != NULL && !just_payload)
 622                 optstart += hlen;
 623 
 624         switch (ie->ds_type) {
 625 
 626         case DSYM_ASCII:
 627 
 628                 if (optstart == NULL) {
 629                         *ierrnop = ITAB_NOMEM;
 630                         return (NULL);
 631                 }
 632 
 633                 (void) memcpy(optstart, value, length);
 634                 break;
 635 
 636         case DSYM_DOMAIN:
 637                 if (optstart == NULL) {
 638                         *ierrnop = ITAB_NOMEM;
 639                         return (NULL);
 640                 }
 641 
 642                 /*
 643                  * Note that this encoder always presents the trailing 0-octet
 644                  * when dealing with a list.  This means that you can't have
 645                  * non-fully-qualified members anywhere but at the end of a
 646                  * list (or as the only member of the list).
 647                  */
 648                 valuep = value;
 649                 while (*valuep != '\0') {
 650                         int dig, val, inchr;
 651                         boolean_t escape;
 652                         uchar_t *flen;
 653 
 654                         /*
 655                          * Skip over whitespace that delimits list members.
 656                          */
 657                         if (isascii(*valuep) && isspace(*valuep)) {
 658                                 valuep++;
 659                                 continue;
 660                         }
 661                         dig = val = 0;
 662                         escape = B_FALSE;
 663                         flen = optstart++;
 664                         while ((inchr = *valuep) != '\0') {
 665                                 valuep++;
 666                                 /*
 667                                  * Just copy non-ASCII text directly to the
 668                                  * output string.  This simplifies the use of
 669                                  * other ctype macros below, as, unlike the
 670                                  * special isascii function, they don't handle
 671                                  * non-ASCII.
 672                                  */
 673                                 if (!isascii(inchr)) {
 674                                         escape = B_FALSE;
 675                                         *optstart++ = inchr;
 676                                         continue;
 677                                 }
 678                                 if (escape) {
 679                                         /*
 680                                          * Handle any of \D, \DD, or \DDD for
 681                                          * a digit escape.
 682                                          */
 683                                         if (isdigit(inchr)) {
 684                                                 val = val * 10 + inchr - '0';
 685                                                 if (++dig == 3) {
 686                                                         *optstart++ = val;
 687                                                         dig = val = 0;
 688                                                         escape = B_FALSE;
 689                                                 }
 690                                                 continue;
 691                                         } else if (dig > 0) {
 692                                                 /*
 693                                                  * User terminated \D or \DD
 694                                                  * with non-digit.  An error,
 695                                                  * but we can assume he means
 696                                                  * to treat as \00D or \0DD.
 697                                                  */
 698                                                 *optstart++ = val;
 699                                                 dig = val = 0;
 700                                         }
 701                                         /* Fall through and copy character */
 702                                         escape = B_FALSE;
 703                                 } else if (inchr == '\\') {
 704                                         escape = B_TRUE;
 705                                         continue;
 706                                 } else if (inchr == '.') {
 707                                         /*
 708                                          * End of component.  Write the length
 709                                          * prefix.  If the component is zero
 710                                          * length (i.e., ".."), the just omit
 711                                          * it.
 712                                          */
 713                                         *flen = (optstart - flen) - 1;
 714                                         if (*flen > 0)
 715                                                 flen = optstart++;
 716                                         continue;
 717                                 } else if (isspace(inchr)) {
 718                                         /*
 719                                          * Unescaped space; end of domain name
 720                                          * in list.
 721                                          */
 722                                         break;
 723                                 }
 724                                 *optstart++ = inchr;
 725                         }
 726                         /*
 727                          * Handle trailing escape sequence.  If string ends
 728                          * with \, then assume user wants \ at end of encoded
 729                          * string.  If it ends with \D or \DD, assume \00D or
 730                          * \0DD.
 731                          */
 732                         if (escape)
 733                                 *optstart++ = dig > 0 ? val : '\\';
 734                         *flen = (optstart - flen) - 1;
 735                         /*
 736                          * If user specified FQDN with trailing '.', then above
 737                          * will result in zero for the last component length.
 738                          * We're done, and optstart already points to the start
 739                          * of the next in list.  Otherwise, we need to write a
 740                          * single zero byte to end the entry, if there are more
 741                          * entries that will be decoded.
 742                          */
 743                         while (isascii(*valuep) && isspace(*valuep))
 744                                 valuep++;
 745                         if (*flen > 0 && *valuep != '\0')
 746                                 *optstart++ = '\0';
 747                 }
 748                 length = (optstart - result) - hlen;
 749                 break;
 750 
 751         case DSYM_DUID:
 752                 if (optstart == NULL) {
 753                         *ierrnop = ITAB_NOMEM;
 754                         return (NULL);
 755                 }
 756 
 757                 errno = 0;
 758                 type = strtoul(value, &currp, 0);
 759                 if (errno != 0 || value == currp || type > 65535 ||
 760                     (*currp != ',' && *currp != '\0')) {
 761                         free(result);
 762                         *ierrnop = ITAB_BAD_NUMBER;
 763                         return (NULL);
 764                 }
 765                 switch (type) {
 766                 case DHCPV6_DUID_LLT: {
 767                         duid_llt_t dllt;
 768                         int hwtype;
 769                         ulong_t tstamp;
 770                         int maclen;
 771 
 772                         if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
 773                                 free(result);
 774                                 return (NULL);
 775                         }
 776                         if (*currp++ != ',') {
 777                                 free(result);
 778                                 *ierrnop = ITAB_BAD_NUMBER;
 779                                 return (NULL);
 780                         }
 781                         if (*currp == ',' || *currp == '\0') {
 782                                 tstamp = time(NULL) - DUID_TIME_BASE;
 783                         } else {
 784                                 tstamp = strtoul(currp, &cp2, 0);
 785                                 if (errno != 0 || currp == cp2) {
 786                                         free(result);
 787                                         *ierrnop = ITAB_BAD_NUMBER;
 788                                         return (NULL);
 789                                 }
 790                                 currp = cp2;
 791                         }
 792                         maclen = get_mac_addr(currp, ierrnop,
 793                             &dllt.dllt_hwtype, hwtype,
 794                             optstart + sizeof (dllt));
 795                         if (maclen == -1) {
 796                                 free(result);
 797                                 return (NULL);
 798                         }
 799                         dllt.dllt_dutype = htons(type);
 800                         dllt.dllt_time = htonl(tstamp);
 801                         (void) memcpy(optstart, &dllt, sizeof (dllt));
 802                         length = maclen + sizeof (dllt);
 803                         break;
 804                 }
 805                 case DHCPV6_DUID_EN: {
 806                         duid_en_t den;
 807                         ulong_t enterp;
 808 
 809                         if (*currp++ != ',') {
 810                                 free(result);
 811                                 *ierrnop = ITAB_BAD_NUMBER;
 812                                 return (NULL);
 813                         }
 814                         enterp = strtoul(currp, &cp2, 0);
 815                         DHCPV6_SET_ENTNUM(&den, enterp);
 816                         if (errno != 0 || currp == cp2 ||
 817                             enterp != DHCPV6_GET_ENTNUM(&den) ||
 818                             (*cp2 != ',' && *cp2 != '\0')) {
 819                                 free(result);
 820                                 *ierrnop = ITAB_BAD_NUMBER;
 821                                 return (NULL);
 822                         }
 823                         if (*cp2 == ',')
 824                                 cp2++;
 825                         vallen = strlen(cp2);
 826                         reslen = (vallen + 1) / 2;
 827                         if (hexascii_to_octet(cp2, vallen,
 828                             optstart + sizeof (den), &reslen) != 0) {
 829                                 free(result);
 830                                 *ierrnop = ITAB_BAD_NUMBER;
 831                                 return (NULL);
 832                         }
 833                         den.den_dutype = htons(type);
 834                         (void) memcpy(optstart, &den, sizeof (den));
 835                         length = reslen + sizeof (den);
 836                         break;
 837                 }
 838                 case DHCPV6_DUID_LL: {
 839                         duid_ll_t dll;
 840                         int hwtype;
 841                         int maclen;
 842 
 843                         if ((hwtype = get_hw_type(&currp, ierrnop)) == -2) {
 844                                 free(result);
 845                                 return (NULL);
 846                         }
 847                         maclen = get_mac_addr(currp, ierrnop, &dll.dll_hwtype,
 848                             hwtype, optstart + sizeof (dll));
 849                         if (maclen == -1) {
 850                                 free(result);
 851                                 return (NULL);
 852                         }
 853                         dll.dll_dutype = htons(type);
 854                         (void) memcpy(optstart, &dll, sizeof (dll));
 855                         length = maclen + sizeof (dll);
 856                         break;
 857                 }
 858                 default:
 859                         if (*currp == ',')
 860                                 currp++;
 861                         vallen = strlen(currp);
 862                         reslen = (vallen + 1) / 2;
 863                         if (hexascii_to_octet(currp, vallen, optstart + 2,
 864                             &reslen) != 0) {
 865                                 free(result);
 866                                 *ierrnop = ITAB_BAD_NUMBER;
 867                                 return (NULL);
 868                         }
 869                         optstart[0] = type >> 8;
 870                         optstart[1] = type;
 871                         length = reslen + 2;
 872                         break;
 873                 }
 874                 break;
 875 
 876         case DSYM_OCTET:
 877 
 878                 if (optstart == NULL) {
 879                         *ierrnop = ITAB_BAD_OCTET;
 880                         return (NULL);
 881                 }
 882 
 883                 reslen = length;
 884                 /* Call libinetutil function to decode */
 885                 if (hexascii_to_octet(value, vallen, optstart, &reslen) != 0) {
 886                         free(result);
 887                         *ierrnop = ITAB_BAD_OCTET;
 888                         return (NULL);
 889                 }
 890                 break;
 891 
 892         case DSYM_IP:
 893         case DSYM_IPV6:
 894 
 895                 if (optstart == NULL) {
 896                         *ierrnop = ITAB_BAD_IPADDR;
 897                         return (NULL);
 898                 }
 899                 if (n_entries % ie->ds_gran != 0) {
 900                         *ierrnop = ITAB_BAD_GRAN;
 901                         inittab_msg("inittab_encode: number of entries "
 902                             "not compatible with option granularity");
 903                         free(result);
 904                         return (NULL);
 905                 }
 906 
 907                 for (valuep = value, i = 0; i < n_entries; i++, valuep++) {
 908 
 909                         currp = strchr(valuep, ' ');
 910                         if (currp != NULL)
 911                                 *currp = '\0';
 912                         if (inet_pton(ie->ds_type == DSYM_IP ? AF_INET :
 913                             AF_INET6, valuep, optstart) != 1) {
 914                                 *ierrnop = ITAB_BAD_IPADDR;
 915                                 inittab_msg("inittab_encode: bogus ip address");
 916                                 free(result);
 917                                 return (NULL);
 918                         }
 919 
 920                         valuep = currp;
 921                         if (valuep == NULL) {
 922                                 if (i < (n_entries - 1)) {
 923                                         *ierrnop = ITAB_NOT_ENOUGH_IP;
 924                                         inittab_msg("inittab_encode: too few "
 925                                             "ip addresses");
 926                                         free(result);
 927                                         return (NULL);
 928                                 }
 929                                 break;
 930                         }
 931                         optstart += type_size;
 932                 }
 933                 break;
 934 
 935         case DSYM_NUMBER:                               /* FALLTHRU */
 936         case DSYM_UNUMBER8:                             /* FALLTHRU */
 937         case DSYM_SNUMBER8:                             /* FALLTHRU */
 938         case DSYM_UNUMBER16:                            /* FALLTHRU */
 939         case DSYM_SNUMBER16:                            /* FALLTHRU */
 940         case DSYM_UNUMBER24:                            /* FALLTHRU */
 941         case DSYM_UNUMBER32:                            /* FALLTHRU */
 942         case DSYM_SNUMBER32:                            /* FALLTHRU */
 943         case DSYM_UNUMBER64:                            /* FALLTHRU */
 944         case DSYM_SNUMBER64:
 945 
 946                 if (optstart == NULL) {
 947                         *ierrnop = ITAB_BAD_NUMBER;
 948                         return (NULL);
 949                 }
 950 
 951                 is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
 952                     ie->ds_type == DSYM_SNUMBER32 ||
 953                     ie->ds_type == DSYM_SNUMBER16 ||
 954                     ie->ds_type == DSYM_SNUMBER8);
 955 
 956                 if (encode_number(n_entries, type_size, is_signed, 0, value,
 957                     optstart, ierrnop) == B_FALSE) {
 958                         free(result);
 959                         return (NULL);
 960                 }
 961                 break;
 962 
 963         default:
 964                 if (ie->ds_type == DSYM_BOOL)
 965                         *ierrnop = ITAB_BAD_BOOLEAN;
 966                 else
 967                         *ierrnop = ITAB_SYNTAX_ERROR;
 968 
 969                 inittab_msg("inittab_encode: unsupported type `%d'",
 970                     ie->ds_type);
 971 
 972                 free(result);
 973                 return (NULL);
 974         }
 975 
 976         /*
 977          * if just_payload is false, then we need to add the option
 978          * code and length fields in.
 979          */
 980         if (!just_payload) {
 981                 if (ie->ds_dhcpv6) {
 982                         /* LINTED: alignment */
 983                         d6o = (dhcpv6_option_t *)result;
 984                         d6o->d6o_code = htons(ie->ds_code);
 985                         d6o->d6o_len = htons(length);
 986                 } else {
 987                         result[0] = ie->ds_code;
 988                         result[1] = length;
 989                 }
 990         }
 991 
 992         if (lengthp != NULL)
 993                 *lengthp = length + hlen;
 994 
 995         return (result);
 996 }
 997 
 998 /*
 999  * inittab_decode_e(): converts a binary representation of a given datatype into
1000  *                   a string; used for decoding DHCP options in a packet off
1001  *                   the wire into ascii
1002  *
1003  *   input: dhcp_symbol_t *: the entry describing the payload option
1004  *          uchar_t *: the payload to convert
1005  *          uint16_t: the payload length (only used if just_payload is true)
1006  *          boolean_t: if false, payload is assumed to be a DHCP option
1007  *          int *: set to extended error code if error occurs.
1008  *  output: char *: a dynamically allocated string containing the converted data
1009  */
1010 
1011 char *
1012 inittab_decode_e(const dhcp_symbol_t *ie, const uchar_t *payload,
1013     uint16_t length, boolean_t just_payload, int *ierrnop)
1014 {
1015         char            *resultp, *result = NULL;
1016         uint_t          n_entries;
1017         struct in_addr  in_addr;
1018         in6_addr_t      in6_addr;
1019         uint8_t         type_size = inittab_type_to_size(ie);
1020         boolean_t       is_signed;
1021         int             type;
1022 
1023         *ierrnop = 0;
1024         if (type_size == 0) {
1025                 *ierrnop = ITAB_SYNTAX_ERROR;
1026                 return (NULL);
1027         }
1028 
1029         if (!just_payload) {
1030                 if (ie->ds_dhcpv6) {
1031                         dhcpv6_option_t d6o;
1032 
1033                         (void) memcpy(&d6o, payload, sizeof (d6o));
1034                         length = ntohs(d6o.d6o_len);
1035                         payload += sizeof (d6o);
1036                 } else {
1037                         length = payload[1];
1038                         payload += 2;
1039                 }
1040         }
1041 
1042         /*
1043          * figure out the number of elements to convert.  note that
1044          * for ds_type NUMBER, the granularity is really 1 since the
1045          * value of ds_gran is the number of bytes in the number.
1046          */
1047         if (ie->ds_type == DSYM_NUMBER)
1048                 n_entries = MIN(ie->ds_max, length / type_size);
1049         else
1050                 n_entries = MIN(ie->ds_max * ie->ds_gran, length / type_size);
1051 
1052         if (n_entries == 0)
1053                 n_entries = length / type_size;
1054 
1055         if ((length % type_size) != 0) {
1056                 inittab_msg("inittab_decode: length of string not compatible "
1057                     "with option type `%i'", ie->ds_type);
1058                 *ierrnop = ITAB_BAD_STRING;
1059                 return (NULL);
1060         }
1061 
1062         switch (ie->ds_type) {
1063 
1064         case DSYM_ASCII:
1065 
1066                 result = malloc(n_entries + 1);
1067                 if (result == NULL) {
1068                         *ierrnop = ITAB_NOMEM;
1069                         return (NULL);
1070                 }
1071 
1072                 (void) memcpy(result, payload, n_entries);
1073                 result[n_entries] = '\0';
1074                 break;
1075 
1076         case DSYM_DOMAIN:
1077 
1078                 /*
1079                  * A valid, decoded RFC 1035 domain string or sequence of
1080                  * strings is always the same size as the encoded form, but we
1081                  * allow for RFC 1035 \DDD and \\ and \. escaping.
1082                  *
1083                  * Decoding stops at the end of the input or the first coding
1084                  * violation.  Coding violations result in discarding the
1085                  * offending list entry entirely.  Note that we ignore the 255
1086                  * character overall limit on domain names.
1087                  */
1088                 if ((result = malloc(4 * length + 1)) == NULL) {
1089                         *ierrnop = ITAB_NOMEM;
1090                         return (NULL);
1091                 }
1092                 resultp = result;
1093                 while (length > 0) {
1094                         char *dstart;
1095                         int slen;
1096 
1097                         dstart = resultp;
1098                         while (length > 0) {
1099                                 slen = *payload++;
1100                                 length--;
1101                                 /* Upper two bits of length must be zero */
1102                                 if ((slen & 0xc0) != 0 || slen > length) {
1103                                         length = 0;
1104                                         resultp = dstart;
1105                                         break;
1106                                 }
1107                                 if (resultp != dstart)
1108                                         *resultp++ = '.';
1109                                 if (slen == 0)
1110                                         break;
1111                                 length -= slen;
1112                                 while (slen > 0) {
1113                                         if (!isascii(*payload) ||
1114                                             !isgraph(*payload)) {
1115                                                 (void) snprintf(resultp, 5,
1116                                                     "\\%03d",
1117                                                     *(unsigned char *)payload);
1118                                                 resultp += 4;
1119                                                 payload++;
1120                                         } else {
1121                                                 if (*payload == '.' ||
1122                                                     *payload == '\\')
1123                                                         *resultp++ = '\\';
1124                                                 *resultp++ = *payload++;
1125                                         }
1126                                         slen--;
1127                                 }
1128                         }
1129                         if (resultp != dstart && length > 0)
1130                                 *resultp++ = ' ';
1131                 }
1132                 *resultp = '\0';
1133                 break;
1134 
1135         case DSYM_DUID:
1136 
1137                 /*
1138                  * First, determine the type of DUID.  We need at least two
1139                  * octets worth of data to grab the type code.  Once we have
1140                  * that, the number of octets required for representation
1141                  * depends on the type.
1142                  */
1143 
1144                 if (length < 2) {
1145                         *ierrnop = ITAB_BAD_GRAN;
1146                         return (NULL);
1147                 }
1148                 type = (payload[0] << 8) + payload[1];
1149                 switch (type) {
1150                 case DHCPV6_DUID_LLT: {
1151                         duid_llt_t dllt;
1152 
1153                         if (length < sizeof (dllt)) {
1154                                 *ierrnop = ITAB_BAD_GRAN;
1155                                 return (NULL);
1156                         }
1157                         (void) memcpy(&dllt, payload, sizeof (dllt));
1158                         payload += sizeof (dllt);
1159                         length -= sizeof (dllt);
1160                         n_entries = sizeof ("1,65535,4294967295,") +
1161                             length * 3;
1162                         if ((result = malloc(n_entries)) == NULL) {
1163                                 *ierrnop = ITAB_NOMEM;
1164                                 return (NULL);
1165                         }
1166                         (void) snprintf(result, n_entries, "%d,%u,%u,", type,
1167                             ntohs(dllt.dllt_hwtype), ntohl(dllt.dllt_time));
1168                         break;
1169                 }
1170                 case DHCPV6_DUID_EN: {
1171                         duid_en_t den;
1172 
1173                         if (length < sizeof (den)) {
1174                                 *ierrnop = ITAB_BAD_GRAN;
1175                                 return (NULL);
1176                         }
1177                         (void) memcpy(&den, payload, sizeof (den));
1178                         payload += sizeof (den);
1179                         length -= sizeof (den);
1180                         n_entries = sizeof ("2,4294967295,") + length * 2;
1181                         if ((result = malloc(n_entries)) == NULL) {
1182                                 *ierrnop = ITAB_NOMEM;
1183                                 return (NULL);
1184                         }
1185                         (void) snprintf(result, n_entries, "%d,%u,", type,
1186                             DHCPV6_GET_ENTNUM(&den));
1187                         break;
1188                 }
1189                 case DHCPV6_DUID_LL: {
1190                         duid_ll_t dll;
1191 
1192                         if (length < sizeof (dll)) {
1193                                 *ierrnop = ITAB_BAD_GRAN;
1194                                 return (NULL);
1195                         }
1196                         (void) memcpy(&dll, payload, sizeof (dll));
1197                         payload += sizeof (dll);
1198                         length -= sizeof (dll);
1199                         n_entries = sizeof ("3,65535,") + length * 3;
1200                         if ((result = malloc(n_entries)) == NULL) {
1201                                 *ierrnop = ITAB_NOMEM;
1202                                 return (NULL);
1203                         }
1204                         (void) snprintf(result, n_entries, "%d,%u,", type,
1205                             ntohs(dll.dll_hwtype));
1206                         break;
1207                 }
1208                 default:
1209                         n_entries = sizeof ("0,") + length * 2;
1210                         if ((result = malloc(n_entries)) == NULL) {
1211                                 *ierrnop = ITAB_NOMEM;
1212                                 return (NULL);
1213                         }
1214                         (void) snprintf(result, n_entries, "%d,", type);
1215                         break;
1216                 }
1217                 resultp = result + strlen(result);
1218                 n_entries -= strlen(result);
1219                 if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
1220                         if (length > 0) {
1221                                 resultp += snprintf(resultp, 3, "%02X",
1222                                     *payload++);
1223                                 length--;
1224                         }
1225                         while (length-- > 0) {
1226                                 resultp += snprintf(resultp, 4, ":%02X",
1227                                     *payload++);
1228                         }
1229                 } else {
1230                         while (length-- > 0) {
1231                                 resultp += snprintf(resultp, 3, "%02X",
1232                                     *payload++);
1233                         }
1234                 }
1235                 break;
1236 
1237         case DSYM_OCTET:
1238 
1239                 result = malloc(n_entries * (sizeof ("0xNN") + 1));
1240                 if (result == NULL) {
1241                         *ierrnop = ITAB_NOMEM;
1242                         return (NULL);
1243                 }
1244 
1245                 result[0] = '\0';
1246                 resultp = result;
1247                 if (n_entries > 0) {
1248                         resultp += sprintf(resultp, "0x%02X", *payload++);
1249                         n_entries--;
1250                 }
1251                 while (n_entries-- > 0)
1252                         resultp += sprintf(resultp, " 0x%02X", *payload++);
1253 
1254                 break;
1255 
1256         case DSYM_IP:
1257         case DSYM_IPV6:
1258                 if ((length / type_size) % ie->ds_gran != 0) {
1259                         *ierrnop = ITAB_BAD_GRAN;
1260                         inittab_msg("inittab_decode: number of entries "
1261                             "not compatible with option granularity");
1262                         return (NULL);
1263                 }
1264 
1265                 result = malloc(n_entries * (ie->ds_type == DSYM_IP ?
1266                     INET_ADDRSTRLEN : INET6_ADDRSTRLEN));
1267                 if (result == NULL) {
1268                         *ierrnop = ITAB_NOMEM;
1269                         return (NULL);
1270                 }
1271 
1272                 for (resultp = result; n_entries != 0; n_entries--) {
1273                         if (ie->ds_type == DSYM_IP) {
1274                                 (void) memcpy(&in_addr.s_addr, payload,
1275                                     sizeof (ipaddr_t));
1276                                 (void) strcpy(resultp, inet_ntoa(in_addr));
1277                         } else {
1278                                 (void) memcpy(&in6_addr, payload,
1279                                     sizeof (in6_addr));
1280                                 (void) inet_ntop(AF_INET6, &in6_addr, resultp,
1281                                     INET6_ADDRSTRLEN);
1282                         }
1283                         resultp += strlen(resultp);
1284                         if (n_entries > 1)
1285                                 *resultp++ = ' ';
1286                         payload += type_size;
1287                 }
1288                 *resultp = '\0';
1289                 break;
1290 
1291         case DSYM_NUMBER:                               /* FALLTHRU */
1292         case DSYM_UNUMBER8:                             /* FALLTHRU */
1293         case DSYM_SNUMBER8:                             /* FALLTHRU */
1294         case DSYM_UNUMBER16:                            /* FALLTHRU */
1295         case DSYM_SNUMBER16:                            /* FALLTHRU */
1296         case DSYM_UNUMBER32:                            /* FALLTHRU */
1297         case DSYM_SNUMBER32:                            /* FALLTHRU */
1298         case DSYM_UNUMBER64:                            /* FALLTHRU */
1299         case DSYM_SNUMBER64:
1300 
1301                 is_signed = (ie->ds_type == DSYM_SNUMBER64 ||
1302                     ie->ds_type == DSYM_SNUMBER32 ||
1303                     ie->ds_type == DSYM_SNUMBER16 ||
1304                     ie->ds_type == DSYM_SNUMBER8);
1305 
1306                 result = malloc(n_entries * ITAB_MAX_NUMBER_LEN);
1307                 if (result == NULL) {
1308                         *ierrnop = ITAB_NOMEM;
1309                         return (NULL);
1310                 }
1311 
1312                 if (decode_number(n_entries, type_size, is_signed, ie->ds_gran,
1313                     payload, result, ierrnop) == B_FALSE) {
1314                         free(result);
1315                         return (NULL);
1316                 }
1317                 break;
1318 
1319         default:
1320                 inittab_msg("inittab_decode: unsupported type `%d'",
1321                     ie->ds_type);
1322                 break;
1323         }
1324 
1325         return (result);
1326 }
1327 
1328 /*
1329  * inittab_encode(): converts a string representation of a given datatype into
1330  *                   binary; used for encoding ascii values into a form that
1331  *                   can be put in DHCP packets to be sent on the wire.
1332  *
1333  *   input: dhcp_symbol_t *: the entry describing the value option
1334  *          const char *: the value to convert
1335  *          uint16_t *: set to the length of the binary data returned
1336  *          boolean_t: if false, return a full DHCP option
1337  *  output: uchar_t *: a dynamically allocated byte array with converted data
1338  */
1339 
1340 uchar_t *
1341 inittab_encode(const dhcp_symbol_t *ie, const char *value, uint16_t *lengthp,
1342     boolean_t just_payload)
1343 {
1344         int ierrno;
1345 
1346         return (inittab_encode_e(ie, value, lengthp, just_payload, &ierrno));
1347 }
1348 
1349 /*
1350  * inittab_decode(): converts a binary representation of a given datatype into
1351  *                   a string; used for decoding DHCP options in a packet off
1352  *                   the wire into ascii
1353  *
1354  *   input: dhcp_symbol_t *: the entry describing the payload option
1355  *          uchar_t *: the payload to convert
1356  *          uint16_t: the payload length (only used if just_payload is true)
1357  *          boolean_t: if false, payload is assumed to be a DHCP option
1358  *  output: char *: a dynamically allocated string containing the converted data
1359  */
1360 
1361 char *
1362 inittab_decode(const dhcp_symbol_t *ie, const uchar_t *payload, uint16_t length,
1363     boolean_t just_payload)
1364 {
1365         int ierrno;
1366 
1367         return (inittab_decode_e(ie, payload, length, just_payload, &ierrno));
1368 }
1369 
1370 /*
1371  * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
1372  *
1373  *          const char *: a printf-like format string
1374  *          ...: arguments to the format string
1375  *  output: void
1376  */
1377 
1378 /*PRINTFLIKE1*/
1379 static void
1380 inittab_msg(const char *fmt, ...)
1381 {
1382         enum { INITTAB_MSG_CHECK, INITTAB_MSG_RETURN, INITTAB_MSG_OUTPUT };
1383 
1384         va_list         ap;
1385         char            buf[512];
1386         static int      action = INITTAB_MSG_CHECK;
1387 
1388         /*
1389          * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
1390          * the the cached result (stored in `action').
1391          */
1392         switch (action) {
1393 
1394         case INITTAB_MSG_CHECK:
1395 
1396                 if (getenv("DHCP_INITTAB_DEBUG") == NULL) {
1397                         action = INITTAB_MSG_RETURN;
1398                         return;
1399                 }
1400 
1401                 action = INITTAB_MSG_OUTPUT;
1402 
1403                 /* FALLTHRU into INITTAB_MSG_OUTPUT */
1404 
1405         case INITTAB_MSG_OUTPUT:
1406 
1407                 va_start(ap, fmt);
1408 
1409                 (void) snprintf(buf, sizeof (buf), "inittab: %s\n", fmt);
1410                 (void) vfprintf(stderr, buf, ap);
1411 
1412                 va_end(ap);
1413                 break;
1414 
1415         case INITTAB_MSG_RETURN:
1416 
1417                 return;
1418         }
1419 }
1420 
1421 /*
1422  * decode_number(): decodes a sequence of numbers from binary into ascii;
1423  *                  binary is coming off of the network, so it is in nbo
1424  *
1425  *   input: uint8_t: the number of "granularity" numbers to decode
1426  *          uint8_t: the length of each number
1427  *          boolean_t: whether the numbers should be considered signed
1428  *          uint8_t: the number of numbers per granularity
1429  *          const uint8_t *: where to decode the numbers from
1430  *          char *: where to decode the numbers to
1431  *  output: boolean_t: true on successful conversion, false on failure
1432  */
1433 
1434 static boolean_t
1435 decode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1436     uint8_t granularity, const uint8_t *from, char *to, int *ierrnop)
1437 {
1438         uint16_t        uint16;
1439         uint32_t        uint32;
1440         uint64_t        uint64;
1441 
1442         if (granularity != 0) {
1443                 if ((granularity % n_entries) != 0) {
1444                         inittab_msg("decode_number: number of entries "
1445                             "not compatible with option granularity");
1446                         *ierrnop = ITAB_BAD_GRAN;
1447                         return (B_FALSE);
1448                 }
1449         }
1450 
1451         for (; n_entries != 0; n_entries--, from += size) {
1452 
1453                 switch (size) {
1454 
1455                 case 1:
1456                         to += sprintf(to, is_signed ? "%d" : "%u", *from);
1457                         break;
1458 
1459                 case 2:
1460                         (void) memcpy(&uint16, from, 2);
1461                         to += sprintf(to, is_signed ? "%hd" : "%hu",
1462                             ntohs(uint16));
1463                         break;
1464 
1465                 case 3:
1466                         uint32 = 0;
1467                         (void) memcpy((uchar_t *)&uint32 + 1, from, 3);
1468                         to += sprintf(to, is_signed ? "%ld" : "%lu",
1469                             ntohl(uint32));
1470                         break;
1471 
1472                 case 4:
1473                         (void) memcpy(&uint32, from, 4);
1474                         to += sprintf(to, is_signed ? "%ld" : "%lu",
1475                             ntohl(uint32));
1476                         break;
1477 
1478                 case 8:
1479                         (void) memcpy(&uint64, from, 8);
1480                         to += sprintf(to, is_signed ? "%lld" : "%llu",
1481                             dhcp_ntohll(uint64));
1482                         break;
1483 
1484                 default:
1485                         *ierrnop = ITAB_BAD_NUMBER;
1486                         inittab_msg("decode_number: unknown integer size `%d'",
1487                             size);
1488                         return (B_FALSE);
1489                 }
1490                 if (n_entries > 0)
1491                         *to++ = ' ';
1492         }
1493 
1494         *to = '\0';
1495         return (B_TRUE);
1496 }
1497 
1498 /*
1499  * encode_number(): encodes a sequence of numbers from ascii into binary;
1500  *                  number will end up on the wire so it needs to be in nbo
1501  *
1502  *   input: uint8_t: the number of "granularity" numbers to encode
1503  *          uint8_t: the length of each number
1504  *          boolean_t: whether the numbers should be considered signed
1505  *          uint8_t: the number of numbers per granularity
1506  *          const uint8_t *: where to encode the numbers from
1507  *          char *: where to encode the numbers to
1508  *          int *: set to extended error code if error occurs.
1509  *  output: boolean_t: true on successful conversion, false on failure
1510  */
1511 
1512 static boolean_t /* ARGSUSED */
1513 encode_number(uint8_t n_entries, uint8_t size, boolean_t is_signed,
1514     uint8_t granularity, const char *from, uint8_t *to, int *ierrnop)
1515 {
1516         uint8_t         i;
1517         uint16_t        uint16;
1518         uint32_t        uint32;
1519         uint64_t        uint64;
1520         char            *endptr;
1521 
1522         if (granularity != 0) {
1523                 if ((granularity % n_entries) != 0) {
1524                         *ierrnop = ITAB_BAD_GRAN;
1525                         inittab_msg("encode_number: number of entries "
1526                             "not compatible with option granularity");
1527                         return (B_FALSE);
1528                 }
1529         }
1530 
1531         for (i = 0; i < n_entries; i++, from++, to += size) {
1532 
1533                 /*
1534                  * totally obscure c factoid: it is legal to pass a
1535                  * string representing a negative number to strtoul().
1536                  * in this case, strtoul() will return an unsigned
1537                  * long that if cast to a long, would represent the
1538                  * negative number.  we take advantage of this to
1539                  * cut down on code here.
1540                  */
1541 
1542                 errno = 0;
1543                 switch (size) {
1544 
1545                 case 1:
1546                         *to = strtoul(from, &endptr, 0);
1547                         if (errno != 0 || from == endptr) {
1548                                 goto error;
1549                         }
1550                         break;
1551 
1552                 case 2:
1553                         uint16 = htons(strtoul(from, &endptr, 0));
1554                         if (errno != 0 || from == endptr) {
1555                                 goto error;
1556                         }
1557                         (void) memcpy(to, &uint16, 2);
1558                         break;
1559 
1560                 case 3:
1561                         uint32 = htonl(strtoul(from, &endptr, 0));
1562                         if (errno != 0 || from == endptr) {
1563                                 goto error;
1564                         }
1565                         (void) memcpy(to, (uchar_t *)&uint32 + 1, 3);
1566                         break;
1567 
1568                 case 4:
1569                         uint32 = htonl(strtoul(from, &endptr, 0));
1570                         if (errno != 0 || from == endptr) {
1571                                 goto error;
1572                         }
1573                         (void) memcpy(to, &uint32, 4);
1574                         break;
1575 
1576                 case 8:
1577                         uint64 = dhcp_htonll(strtoull(from, &endptr, 0));
1578                         if (errno != 0 || from == endptr) {
1579                                 goto error;
1580                         }
1581                         (void) memcpy(to, &uint64, 8);
1582                         break;
1583 
1584                 default:
1585                         inittab_msg("encode_number: unsupported integer "
1586                             "size `%d'", size);
1587                         return (B_FALSE);
1588                 }
1589 
1590                 from = strchr(from, ' ');
1591                 if (from == NULL)
1592                         break;
1593         }
1594 
1595         return (B_TRUE);
1596 
1597 error:
1598         *ierrnop = ITAB_BAD_NUMBER;
1599         inittab_msg("encode_number: cannot convert to integer");
1600         return (B_FALSE);
1601 }
1602 
1603 /*
1604  * inittab_type_to_size(): given an inittab entry, returns size of one entry of
1605  *                    its type
1606  *
1607  *   input: dhcp_symbol_t *: an entry of the given type
1608  *  output: uint8_t: the size in bytes of an entry of that type
1609  */
1610 
1611 uint8_t
1612 inittab_type_to_size(const dhcp_symbol_t *ie)
1613 {
1614         switch (ie->ds_type) {
1615 
1616         case DSYM_DUID:
1617         case DSYM_DOMAIN:
1618         case DSYM_ASCII:
1619         case DSYM_OCTET:
1620         case DSYM_SNUMBER8:
1621         case DSYM_UNUMBER8:
1622 
1623                 return (1);
1624 
1625         case DSYM_SNUMBER16:
1626         case DSYM_UNUMBER16:
1627 
1628                 return (2);
1629 
1630         case DSYM_UNUMBER24:
1631 
1632                 return (3);
1633 
1634         case DSYM_SNUMBER32:
1635         case DSYM_UNUMBER32:
1636         case DSYM_IP:
1637 
1638                 return (4);
1639 
1640         case DSYM_SNUMBER64:
1641         case DSYM_UNUMBER64:
1642 
1643                 return (8);
1644 
1645         case DSYM_NUMBER:
1646 
1647                 return (ie->ds_gran);
1648 
1649         case DSYM_IPV6:
1650 
1651                 return (sizeof (in6_addr_t));
1652         }
1653 
1654         return (0);
1655 }
1656 
1657 /*
1658  * itabcode_to_dsymcode(): maps an inittab category code to its dsym
1659  *                         representation
1660  *
1661  *   input: uchar_t: the inittab category code
1662  *  output: dsym_category_t: the dsym category code
1663  */
1664 
1665 static dsym_category_t
1666 itabcode_to_dsymcode(uchar_t itabcode)
1667 {
1668 
1669         unsigned int    i;
1670 
1671         for (i = 0; i < ITAB_CAT_COUNT; i++)
1672                 if (category_map[i].cme_itabcode == itabcode)
1673                         return (category_map[i].cme_dsymcode);
1674 
1675         return (DSYM_BAD_CAT);
1676 }
1677 
1678 /*
1679  * category_to_code(): maps a category name to its numeric representation
1680  *
1681  *   input: const char *: the category name
1682  *  output: uchar_t: its internal code (numeric representation)
1683  */
1684 
1685 static uchar_t
1686 category_to_code(const char *category)
1687 {
1688         unsigned int    i;
1689 
1690         for (i = 0; i < ITAB_CAT_COUNT; i++)
1691                 if (strcasecmp(category_map[i].cme_name, category) == 0)
1692                         return (category_map[i].cme_itabcode);
1693 
1694         return (0);
1695 }
1696 
1697 /*
1698  * dhcp_htonll(): converts a 64-bit number from host to network byte order
1699  *
1700  *   input: uint64_t: the number to convert
1701  *  output: uint64_t: its value in network byte order
1702  */
1703 
1704 static uint64_t
1705 dhcp_htonll(uint64_t uint64_hbo)
1706 {
1707         return (dhcp_ntohll(uint64_hbo));
1708 }
1709 
1710 /*
1711  * dhcp_ntohll(): converts a 64-bit number from network to host byte order
1712  *
1713  *   input: uint64_t: the number to convert
1714  *  output: uint64_t: its value in host byte order
1715  */
1716 
1717 static uint64_t
1718 dhcp_ntohll(uint64_t uint64_nbo)
1719 {
1720 #ifdef  _LITTLE_ENDIAN
1721         return ((uint64_t)ntohl(uint64_nbo & 0xffffffff) << 32 |
1722             ntohl(uint64_nbo >> 32));
1723 #else
1724         return (uint64_nbo);
1725 #endif
1726 }
1727 
1728 /*
1729  * our internal table of DHCP option values, used by inittab_verify()
1730  */
1731 static dhcp_symbol_t inittab_table[] =
1732 {
1733 { DSYM_INTERNAL,        1024,   "Hostname",     DSYM_BOOL,      0,      0 },
1734 { DSYM_INTERNAL,        1025,   "LeaseNeg",     DSYM_BOOL,      0,      0 },
1735 { DSYM_INTERNAL,        1026,   "EchoVC",       DSYM_BOOL,      0,      0 },
1736 { DSYM_INTERNAL,        1027,   "BootPath",     DSYM_ASCII,     1,      128 },
1737 { DSYM_FIELD,           0,      "Opcode",       DSYM_UNUMBER8,  1,      1 },
1738 { DSYM_FIELD,           1,      "Htype",        DSYM_UNUMBER8,  1,      1 },
1739 { DSYM_FIELD,           2,      "HLen",         DSYM_UNUMBER8,  1,      1 },
1740 { DSYM_FIELD,           3,      "Hops",         DSYM_UNUMBER8,  1,      1 },
1741 { DSYM_FIELD,           4,      "Xid",          DSYM_UNUMBER32, 1,      1 },
1742 { DSYM_FIELD,           8,      "Secs",         DSYM_UNUMBER16, 1,      1 },
1743 { DSYM_FIELD,           10,     "Flags",        DSYM_OCTET,     1,      2 },
1744 { DSYM_FIELD,           12,     "Ciaddr",       DSYM_IP,        1,      1 },
1745 { DSYM_FIELD,           16,     "Yiaddr",       DSYM_IP,        1,      1 },
1746 { DSYM_FIELD,           20,     "BootSrvA",     DSYM_IP,        1,      1 },
1747 { DSYM_FIELD,           24,     "Giaddr",       DSYM_IP,        1,      1 },
1748 { DSYM_FIELD,           28,     "Chaddr",       DSYM_OCTET,     1,      16 },
1749 { DSYM_FIELD,           44,     "BootSrvN",     DSYM_ASCII,     1,      64 },
1750 { DSYM_FIELD,           108,    "BootFile",     DSYM_ASCII,     1,      128 },
1751 { DSYM_FIELD,           236,    "Magic",        DSYM_OCTET,     1,      4 },
1752 { DSYM_FIELD,           240,    "Options",      DSYM_OCTET,     1,      60 },
1753 { DSYM_STANDARD,        1,      "Subnet",       DSYM_IP,        1,      1 },
1754 { DSYM_STANDARD,        2,      "UTCoffst",     DSYM_SNUMBER32, 1,      1 },
1755 { DSYM_STANDARD,        3,      "Router",       DSYM_IP,        1,      0 },
1756 { DSYM_STANDARD,        4,      "Timeserv",     DSYM_IP,        1,      0 },
1757 { DSYM_STANDARD,        5,      "IEN116ns",     DSYM_IP,        1,      0 },
1758 { DSYM_STANDARD,        6,      "DNSserv",      DSYM_IP,        1,      0 },
1759 { DSYM_STANDARD,        7,      "Logserv",      DSYM_IP,        1,      0 },
1760 { DSYM_STANDARD,        8,      "Cookie",       DSYM_IP,        1,      0 },
1761 { DSYM_STANDARD,        9,      "Lprserv",      DSYM_IP,        1,      0 },
1762 { DSYM_STANDARD,        10,     "Impress",      DSYM_IP,        1,      0 },
1763 { DSYM_STANDARD,        11,     "Resource",     DSYM_IP,        1,      0 },
1764 { DSYM_STANDARD,        12,     "Hostname",     DSYM_ASCII,     1,      0 },
1765 { DSYM_STANDARD,        13,     "Bootsize",     DSYM_UNUMBER16, 1,      1 },
1766 { DSYM_STANDARD,        14,     "Dumpfile",     DSYM_ASCII,     1,      0 },
1767 { DSYM_STANDARD,        15,     "DNSdmain",     DSYM_ASCII,     1,      0 },
1768 { DSYM_STANDARD,        16,     "Swapserv",     DSYM_IP,        1,      1 },
1769 { DSYM_STANDARD,        17,     "Rootpath",     DSYM_ASCII,     1,      0 },
1770 { DSYM_STANDARD,        18,     "ExtendP",      DSYM_ASCII,     1,      0 },
1771 { DSYM_STANDARD,        19,     "IpFwdF",       DSYM_UNUMBER8,  1,      1 },
1772 { DSYM_STANDARD,        20,     "NLrouteF",     DSYM_UNUMBER8,  1,      1 },
1773 { DSYM_STANDARD,        21,     "PFilter",      DSYM_IP,        2,      0 },
1774 { DSYM_STANDARD,        22,     "MaxIpSiz",     DSYM_UNUMBER16, 1,      1 },
1775 { DSYM_STANDARD,        23,     "IpTTL",        DSYM_UNUMBER8,  1,      1 },
1776 { DSYM_STANDARD,        24,     "PathTO",       DSYM_UNUMBER32, 1,      1 },
1777 { DSYM_STANDARD,        25,     "PathTbl",      DSYM_UNUMBER16, 1,      0 },
1778 { DSYM_STANDARD,        26,     "MTU",          DSYM_UNUMBER16, 1,      1 },
1779 { DSYM_STANDARD,        27,     "SameMtuF",     DSYM_UNUMBER8,  1,      1 },
1780 { DSYM_STANDARD,        28,     "Broadcst",     DSYM_IP,        1,      1 },
1781 { DSYM_STANDARD,        29,     "MaskDscF",     DSYM_UNUMBER8,  1,      1 },
1782 { DSYM_STANDARD,        30,     "MaskSupF",     DSYM_UNUMBER8,  1,      1 },
1783 { DSYM_STANDARD,        31,     "RDiscvyF",     DSYM_UNUMBER8,  1,      1 },
1784 { DSYM_STANDARD,        32,     "RSolictS",     DSYM_IP,        1,      1 },
1785 { DSYM_STANDARD,        33,     "StaticRt",     DSYM_IP,        2,      0 },
1786 { DSYM_STANDARD,        34,     "TrailerF",     DSYM_UNUMBER8,  1,      1 },
1787 { DSYM_STANDARD,        35,     "ArpTimeO",     DSYM_UNUMBER32, 1,      1 },
1788 { DSYM_STANDARD,        36,     "EthEncap",     DSYM_UNUMBER8,  1,      1 },
1789 { DSYM_STANDARD,        37,     "TcpTTL",       DSYM_UNUMBER8,  1,      1 },
1790 { DSYM_STANDARD,        38,     "TcpKaInt",     DSYM_UNUMBER32, 1,      1 },
1791 { DSYM_STANDARD,        39,     "TcpKaGbF",     DSYM_UNUMBER8,  1,      1 },
1792 { DSYM_STANDARD,        40,     "NISdmain",     DSYM_ASCII,     1,      0 },
1793 { DSYM_STANDARD,        41,     "NISservs",     DSYM_IP,        1,      0 },
1794 { DSYM_STANDARD,        42,     "NTPservs",     DSYM_IP,        1,      0 },
1795 { DSYM_STANDARD,        43,     "Vendor",       DSYM_OCTET,     1,      0 },
1796 { DSYM_STANDARD,        44,     "NetBNms",      DSYM_IP,        1,      0 },
1797 { DSYM_STANDARD,        45,     "NetBDsts",     DSYM_IP,        1,      0 },
1798 { DSYM_STANDARD,        46,     "NetBNdT",      DSYM_UNUMBER8,  1,      1 },
1799 { DSYM_STANDARD,        47,     "NetBScop",     DSYM_ASCII,     1,      0 },
1800 { DSYM_STANDARD,        48,     "XFontSrv",     DSYM_IP,        1,      0 },
1801 { DSYM_STANDARD,        49,     "XDispMgr",     DSYM_IP,        1,      0 },
1802 { DSYM_STANDARD,        50,     "ReqIP",        DSYM_IP,        1,      1 },
1803 { DSYM_STANDARD,        51,     "LeaseTim",     DSYM_UNUMBER32, 1,      1 },
1804 { DSYM_STANDARD,        52,     "OptOvrld",     DSYM_UNUMBER8,  1,      1 },
1805 { DSYM_STANDARD,        53,     "DHCPType",     DSYM_UNUMBER8,  1,      1 },
1806 { DSYM_STANDARD,        54,     "ServerID",     DSYM_IP,        1,      1 },
1807 { DSYM_STANDARD,        55,     "ReqList",      DSYM_OCTET,     1,      0 },
1808 { DSYM_STANDARD,        56,     "Message",      DSYM_ASCII,     1,      0 },
1809 { DSYM_STANDARD,        57,     "DHCP_MTU",     DSYM_UNUMBER16, 1,      1 },
1810 { DSYM_STANDARD,        58,     "T1Time",       DSYM_UNUMBER32, 1,      1 },
1811 { DSYM_STANDARD,        59,     "T2Time",       DSYM_UNUMBER32, 1,      1 },
1812 { DSYM_STANDARD,        60,     "ClassID",      DSYM_ASCII,     1,      0 },
1813 { DSYM_STANDARD,        61,     "ClientID",     DSYM_OCTET,     1,      0 },
1814 { DSYM_STANDARD,        62,     "NW_dmain",     DSYM_ASCII,     1,      0 },
1815 { DSYM_STANDARD,        63,     "NWIPOpts",     DSYM_OCTET,     1,      128 },
1816 { DSYM_STANDARD,        64,     "NIS+dom",      DSYM_ASCII,     1,      0 },
1817 { DSYM_STANDARD,        65,     "NIS+serv",     DSYM_IP,        1,      0 },
1818 { DSYM_STANDARD,        66,     "TFTPsrvN",     DSYM_ASCII,     1,      64 },
1819 { DSYM_STANDARD,        67,     "OptBootF",     DSYM_ASCII,     1,      128 },
1820 { DSYM_STANDARD,        68,     "MblIPAgt",     DSYM_IP,        1,      0 },
1821 { DSYM_STANDARD,        69,     "SMTPserv",     DSYM_IP,        1,      0 },
1822 { DSYM_STANDARD,        70,     "POP3serv",     DSYM_IP,        1,      0 },
1823 { DSYM_STANDARD,        71,     "NNTPserv",     DSYM_IP,        1,      0 },
1824 { DSYM_STANDARD,        72,     "WWWservs",     DSYM_IP,        1,      0 },
1825 { DSYM_STANDARD,        73,     "Fingersv",     DSYM_IP,        1,      0 },
1826 { DSYM_STANDARD,        74,     "IRCservs",     DSYM_IP,        1,      0 },
1827 { DSYM_STANDARD,        75,     "STservs",      DSYM_IP,        1,      0 },
1828 { DSYM_STANDARD,        76,     "STDAservs",    DSYM_IP,        1,      0 },
1829 { DSYM_STANDARD,        77,     "UserClas",     DSYM_ASCII,     1,      0 },
1830 { DSYM_STANDARD,        78,     "SLP_DA",       DSYM_OCTET,     1,      0 },
1831 { DSYM_STANDARD,        79,     "SLP_SS",       DSYM_OCTET,     1,      0 },
1832 { DSYM_STANDARD,        82,     "AgentOpt",     DSYM_OCTET,     1,      0 },
1833 { DSYM_STANDARD,        89,     "FQDN",         DSYM_OCTET,     1,      0 },
1834 { 0,                    0,      "",             0,              0,      0 }
1835 };