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