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