1 /*
   2  * Routines and structures which are used from CMU's 2.2 bootp implementation
   3  * are labelled as such. Code not labelled is:
   4  *
   5  * Copyright 1997-2002 Sun Microsystems, Inc.  All rights reserved.
   6  * Use is subject to license terms.
   7  */
   8 
   9 #pragma ident   "%Z%%M% %I%     %E% SMI"
  10 
  11 /*
  12  *      Copyright 1988, 1991 by Carnegie Mellon University
  13  *
  14  *                      All Rights Reserved
  15  *
  16  * Permission to use, copy, modify, and distribute this software and its
  17  * documentation for any purpose and without fee is hereby granted, provided
  18  * that the above copyright notice appear in all copies and that both that
  19  * copyright notice and this permission notice appear in supporting
  20  * documentation, and that the name of Carnegie Mellon University not be used
  21  * in advertising or publicity pertaining to distribution of the software
  22  * without specific, written prior permission.
  23  *
  24  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
  25  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  26  * IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  27  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  28  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  29  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  30  * THIS SOFTWARE.
  31  */
  32 
  33 /*
  34  * in.dhcpd configuration file reading code.
  35  *
  36  * The routines in this file deal with reading, interpreting, and storing
  37  * the information found in the in.dhcpd configuration file (usually
  38  * /etc/dhcptab).
  39  */
  40 
  41 /*
  42  * TODO: What's missing: Symbol code is very generic, but doesn't allow
  43  * per symbol granularity checking - ie, using goodname() to check the
  44  * hostname, for example. Perhaps each symbol should have a verifier
  45  * function possibly associated with it (null is ok), which would return
  46  * B_TRUE if ok, B_FALSE if not, and print out a nasty message.
  47  *
  48  * Option overload. If set, then NO BOOTFILE or SNAME values can exist.
  49  */
  50 
  51 #include <stdio.h>
  52 #include <stdlib.h>
  53 #include <string.h>
  54 #include <ctype.h>
  55 #include <errno.h>
  56 #include <assert.h>
  57 #include <stdarg.h>
  58 #include <sys/types.h>
  59 #include <sys/byteorder.h>
  60 #include <sys/stat.h>
  61 #include <sys/file.h>
  62 #include <sys/time.h>
  63 #include <sys/socket.h>
  64 #include <net/if.h>
  65 #include <netinet/in.h>
  66 #include <arpa/inet.h>
  67 #include <syslog.h>
  68 #include <dhcp_inittab.h>
  69 #include <dhcp_symbol.h>
  70 #include "netinet/dhcp.h"
  71 #include "hash.h"
  72 #include "dhcpd.h"
  73 #include <locale.h>
  74 
  75 /*
  76  * Local constants
  77  */
  78 #define OP_ADDITION               1     /* Operations on tags */
  79 #define OP_DELETION               2
  80 #define OP_BOOLEAN                3
  81 
  82 #define MAXENTRYLEN             3072    /* Max size of an entire entry */
  83 #define MAX_ITEMS               16      /* Max number of items in entry */
  84 #define MAX_MACRO_NESTING       20      /* Max number of nested includes */
  85 
  86 #define DB_DHCP_MAC             'm'     /* Like TBL_DHCP_{MACRO,SYMBOL} */
  87 #define DB_DHCP_SYM             's'     /* But not strings! */
  88 
  89 static time_t   mtable_time;            /* load time of hash table */
  90 
  91 static uint_t   nentries;               /* Total number of entries */
  92 static int      include_nest;           /* macro nesting counter */
  93 static dt_rec_list_t **newtbl;          /* reordered Tbl. */
  94 static dt_rec_list_t **oldtbl;          /* The original tbl. */
  95 static hash_tbl *mtable;
  96 static mutex_t  mtable_mtx;             /* Reinitialization mutex */
  97 static cond_t   mtable_cv;              /* Reinitialization cv */
  98 static int      mtable_refcnt;          /* Current reference count */
  99 static int      mtable_closing;         /* macros are going away */
 100 
 101 #define INCLUDE_SYM     "Include"       /* symbol for macro include */
 102 
 103 /*
 104  * Forward declarations.
 105  */
 106 static dhcp_symbol_t *sym_list;
 107 static size_t sym_num_items;
 108 static int check_includes(int, char *);
 109 static MACRO *process_entry(dt_rec_list_t *);
 110 static int eval_symbol(char **, MACRO *);
 111 static char *get_string(char **, char *, uchar_t *);
 112 static void adjust(char **);
 113 static void eat_whitespace(char **);
 114 static int first_macro_row(dt_rec_list_t **);
 115 static void get_sym_name(char *, char **);
 116 static void print_error_msg(int, uchar_t);
 117 static boolean_t define_symbol(char **, char *);
 118 static int scan_include(char **, char *);
 119 static boolean_t free_macro(MACRO *, boolean_t);
 120 static int macro_cmp(MACRO *, MACRO *);
 121 static void add_vndlist(ENCODE *, MACRO *, dhcp_symbol_t *);
 122 
 123 /*
 124  * Initialize the hash table.
 125  */
 126 int
 127 initmtab(void)
 128 {
 129         /*
 130          * Allocate hash table
 131          */
 132         mtable = hash_Init(0, NULL, 0, B_FALSE);
 133 
 134         assert(mtable != NULL);
 135 
 136         (void) mutex_init(&mtable_mtx, USYNC_THREAD, NULL);
 137         (void) cond_init(&mtable_cv, USYNC_THREAD, 0);
 138 
 139         return (0);
 140 }
 141 
 142 /*
 143  * Check presence/access to dhcptab database file.
 144  */
 145 int
 146 checktab(void)
 147 {
 148         int             err;
 149         dsvc_handle_t   dh;
 150 
 151         err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB, DSVC_READ);
 152         switch (err) {
 153         case DSVC_SUCCESS:
 154                 (void) close_dd(&dh);
 155                 break;
 156         case DSVC_ACCESS:
 157                 dhcpmsg(LOG_ERR,
 158                     "No permission to access dhcptab in %s (%s)\n",
 159                     datastore.d_resource, datastore.d_location);
 160                 err = EACCES;
 161                 break;
 162         case DSVC_NOENT:
 163         case DSVC_NO_TABLE:
 164                 dhcpmsg(LOG_INFO,
 165                     "Dhcptab table does not exist in %s (%s)\n",
 166                     datastore.d_resource, datastore.d_location);
 167                 err = ENOENT;
 168                 break;
 169         default:
 170                 dhcpmsg(LOG_ERR,
 171                         "Error checking status of dhcptab in %s (%s)\n",
 172                         datastore.d_resource, datastore.d_location);
 173         }
 174         return (err);
 175 }
 176 
 177 /*
 178  * Read dhcptab database file.
 179  */
 180 int
 181 readtab(int preserve)
 182 {
 183         int             err = 0, first_mac;
 184         MACRO           *mc;
 185         uint32_t        query;
 186         dt_rec_t        dt;
 187         dt_rec_list_t   **dhcptab = NULL;
 188         dt_rec_list_t   *dhcptab_list = NULL;
 189         dt_rec_list_t   *dtep;
 190         int             i;
 191         int             ind;
 192         uint_t          records;
 193         timestruc_t     tm;
 194         dsvc_handle_t   dh;
 195         boolean_t       tab_open = B_FALSE;
 196 
 197         (void) mutex_lock(&mtable_mtx);
 198 
 199         /*
 200          * Wait for any current thread(s) to complete using macros.
 201          */
 202         mtable_closing = 1;
 203         while (mtable_refcnt > 0) {
 204                 tm.tv_sec = 1;
 205                 tm.tv_nsec = 0;
 206                 (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm);
 207         }
 208 
 209         /* Get the *entire* dhcptab. */
 210         while ((err = open_dd(&dh, &datastore, DSVC_DHCPTAB, DT_DHCPTAB,
 211             DSVC_READ)) != DSVC_SUCCESS) {
 212                 if (err == DSVC_BUSY) {
 213                         continue;
 214                 }
 215                 if (err == DSVC_NOENT) {
 216                         dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n");
 217                         err = 0;        /* not a "real" error */
 218                 } else
 219                         dhcpmsg(LOG_ERR, "Error opening macro database: %s\n",
 220                                 dhcpsvc_errmsg(err));
 221                 goto leave_readtab;
 222         }
 223 
 224         tab_open = B_TRUE;
 225 
 226         DSVC_QINIT(query);
 227         (void) memset(&dt, 0, sizeof (dt_rec_t));
 228 
 229         dhcptab_list = NULL;
 230         nentries = 0;
 231         err = lookup_dd(dh, B_FALSE, query, -1, (const void *)&dt,
 232                         (void **)&dhcptab_list, &nentries);
 233         if (err != DSVC_SUCCESS) {
 234                 if (verbose && err == DSVC_NOENT) {
 235                         dhcpmsg(LOG_INFO, "Error access macro database: %s\n",
 236                                 dhcpsvc_errmsg(err));
 237                 }
 238                 goto leave_readtab;
 239         } else
 240                 err = 0;
 241         if (nentries == 0) {
 242                 dhcpmsg(LOG_INFO, "Empty dhcptab macro database.\n");
 243                 err = 0;        /* not a "real" error */
 244                 goto leave_readtab;
 245         }
 246 
 247         /*
 248          * Because libdhcpsvc doesn't guarantee any order, we need to
 249          * preprocess the macro list to guarantee that macros which are
 250          * included in other macro definitions are already defined prior
 251          * to their use. This means that macro processing is a two step process.
 252          */
 253         dhcptab = (dt_rec_list_t **)smalloc((nentries + 1) *
 254                 sizeof (dt_rec_list_t *));
 255 
 256         /* Extract symbols first. */
 257         ind = 0;
 258         for (i = 0, dtep = dhcptab_list; dtep != NULL;
 259                 i++, dtep = dtep->dtl_next) {
 260                 if (dtep->dtl_rec->dt_type == DT_SYMBOL) {
 261                         dhcptab[ind++] = dtep;
 262                 }
 263         }
 264         /* Copy macros */
 265         for (i = 0, dtep = dhcptab_list; dtep != NULL;
 266                 i++, dtep = dtep->dtl_next) {
 267                 if (dtep->dtl_rec->dt_type == DT_MACRO) {
 268                         dhcptab[ind++] = dtep;
 269                 }
 270         }
 271 
 272         first_mac = first_macro_row(dhcptab);
 273         include_nest = 0;
 274         if (first_mac >= 0) {
 275                 oldtbl = dhcptab;
 276                 newtbl = (dt_rec_list_t **)smalloc((nentries + 1) *
 277                         sizeof (dt_rec_list_t *));
 278                 for (i = 0; i < first_mac; i++)
 279                         newtbl[i] = oldtbl[i];  /* copy symdefs */
 280                 for (i = first_mac; i < nentries; i++) {
 281                         if ((err = check_includes(first_mac,
 282                             oldtbl[i]->dtl_rec->dt_key)) != 0)
 283                                 break;
 284                 }
 285                 if (err != 0) {
 286                         free(newtbl);
 287                         free(dhcptab);
 288                         goto leave_readtab;
 289                 } else {
 290                         free(dhcptab);
 291                         dhcptab = newtbl;
 292                 }
 293         }
 294 
 295         resettab(B_FALSE);
 296 
 297         /*
 298          * Now table is reordered. process as usual.
 299          */
 300         records = 0;
 301         for (i = 0; i < nentries; i++) {
 302 
 303                 if ((mc = process_entry(dhcptab[i])) == (MACRO *)NULL)
 304                         continue;
 305 
 306                 if (hash_Insert(mtable, mc->nm, strlen(mc->nm), macro_cmp,
 307                     mc->nm, mc) == NULL) {
 308                         dhcpmsg(LOG_WARNING,
 309                             "Duplicate macro definition: %s\n", mc->nm);
 310                         continue;
 311                 }
 312                 records++;
 313         }
 314 
 315         mtable_time = time(NULL);
 316 
 317         if (verbose) {
 318                 dhcpmsg(LOG_INFO,
 319                     "Read %d entries from DHCP macro database on %s",
 320                     records, ctime(&mtable_time));
 321         }
 322 
 323         free(dhcptab);
 324 
 325 leave_readtab:
 326 
 327         if (dhcptab_list != NULL)
 328                 free_dd_list(dh, dhcptab_list);
 329 
 330         if (tab_open)
 331                 (void) close_dd(&dh);
 332 
 333         if (preserve && err != 0) {
 334                 dhcpmsg(LOG_WARNING,
 335                     "DHCP macro database rescan failed %d, using scan: %s",
 336                     err, ctime(&mtable_time));
 337                 err = 0;
 338         }
 339 
 340         mtable_closing = 0;
 341         (void) mutex_unlock(&mtable_mtx);
 342         return (err);
 343 }
 344 
 345 /*
 346  *  Reset the dhcptab hash table, free any dynamic symbol definitions.
 347  */
 348 void
 349 resettab(boolean_t lck)
 350 {
 351         int             i;
 352         dhcp_symbol_t   tmp;
 353         timestruc_t     tm;
 354 
 355         if (lck == B_TRUE)
 356                 (void) mutex_lock(&mtable_mtx);
 357 
 358         /*
 359          * Wait for any current thread(s) to complete using macros.
 360          */
 361         if (mtable_closing == 0) {
 362                 mtable_closing = 1;
 363                 while (mtable_refcnt > 0) {
 364                         tm.tv_sec = 1;
 365                         tm.tv_nsec = 0;
 366                         (void) cond_reltimedwait(&mtable_cv, &mtable_mtx, &tm);
 367                 }
 368         }
 369 
 370         /* Entirely erase all hash tables. */
 371         hash_Reset(mtable, free_macro);
 372 
 373         /*
 374          * Dump any dynamically defined symbol definitions, and reinitialize.
 375          */
 376         if (sym_list != NULL) {
 377 
 378                 /*
 379                  * Free class resources for each symbol.
 380                  */
 381                 for (i = 0; i < sym_num_items; i++) {
 382                         dsym_free_classes(&sym_list[i].ds_classes);
 383                 }
 384 
 385                 free(sym_list);
 386                 sym_list = NULL;
 387         }
 388 
 389         if (time_to_go) {
 390                 if (lck == B_TRUE) {
 391                         (void) mutex_unlock(&mtable_mtx);
 392                         (void) mutex_destroy(&mtable_mtx);
 393                         (void) cond_destroy(&mtable_cv);
 394                 }
 395                 return;
 396         }
 397 
 398         /* Allocate the inittab and class tables */
 399         sym_list = inittab_load(ITAB_CAT_STANDARD|ITAB_CAT_FIELD|
 400                         ITAB_CAT_INTERNAL|ITAB_CAT_VENDOR,
 401                         ITAB_CONS_SERVER, &sym_num_items);
 402         /*
 403          * Allocate the internal INCLUDE_SYM macro include symbol.
 404          * Since this is not part of inittab, it must be added
 405          * manually to the list.
 406          */
 407         sym_list = (dhcp_symbol_t *)realloc(sym_list,
 408                 (sym_num_items + 1) * sizeof (dhcp_symbol_t));
 409 
 410         if (sym_num_items == 0 || sym_list == NULL) {
 411                 dhcpmsg(LOG_ERR, "Cannot allocate inittab, exiting\n");
 412                 (void) exit(1);
 413         }
 414 
 415         (void) memset(&sym_list[sym_num_items], 0, sizeof (dhcp_symbol_t));
 416         (void) strcpy(sym_list[sym_num_items].ds_name, INCLUDE_SYM);
 417         sym_list[sym_num_items].ds_type = DSYM_INCLUDE;
 418         sym_list[sym_num_items].ds_max = 32;
 419         sym_num_items++;
 420 
 421         /* Verify the inittab entries */
 422         for (i = 0; i < sym_num_items; i++) {
 423                 if (inittab_verify(&sym_list[i], &tmp) == ITAB_FAILURE) {
 424                         print_error_msg(ITAB_SYNTAX_ERROR, i);
 425                         (void) memcpy(&sym_list[i], &tmp,
 426                                 sizeof (dhcp_symbol_t));
 427                 }
 428         }
 429 
 430         mtable_closing = 0;
 431         if (lck == B_TRUE)
 432                 (void) mutex_unlock(&mtable_mtx);
 433 }
 434 
 435 /*
 436  * Given an value field pptr, return the first INCLUDE_SYM value found in
 437  * include, updating pptr along the way. Returns nonzero if no INCLUDE_SYM
 438  * symbol is found (pptr is still updated).
 439  */
 440 static int
 441 scan_include(char **cpp, char *include)
 442 {
 443         char    t_sym[DSVC_MAX_MACSYM_LEN + 1];
 444         uchar_t ilen;
 445 
 446         while (*cpp && **cpp != '\0') {
 447                 eat_whitespace(cpp);
 448                 get_sym_name(t_sym, cpp);
 449                 if (strcmp(t_sym, INCLUDE_SYM) == 0) {
 450                         ilen = DHCP_SCRATCH;
 451                         if (**cpp == '=')
 452                                 (*cpp)++;
 453                         (void) get_string(cpp, include, &ilen);
 454                         include[ilen] = '\0';
 455                         return (0);
 456                 } else
 457                         adjust(cpp);
 458         }
 459         return (1);
 460 }
 461 
 462 /*
 463  * Return the first macro row in dhcptab. Returns -1 if no macros exist.
 464  */
 465 static int
 466 first_macro_row(dt_rec_list_t **tblp)
 467 {
 468         int i;
 469 
 470         for (i = 0; i < nentries; i++) {
 471                 if (tolower(tblp[i]->dtl_rec->dt_type) == (int)DT_MACRO)
 472                         return (i);
 473         }
 474         return (-1);
 475 }
 476 
 477 /*
 478  * RECURSIVE function: Scans for included macros, and reorders Tbl to
 479  * ensure macro definitions occur in the correct order.
 480  *
 481  * Returns 0 for success, nonzero otherwise.
 482  */
 483 static int
 484 check_includes(int first, char *mname)
 485 {
 486         char    include[DHCP_SCRATCH + 1];
 487         int     m, err = 0;
 488         dt_rec_list_t *current_rowp = NULL;
 489         char    *cp;
 490 
 491         include_nest++;
 492 
 493         if (include_nest > MAX_MACRO_NESTING) {
 494                 dhcpmsg(LOG_ERR,
 495                     "Circular macro definition using: %s\n", mname);
 496                 err = -1;
 497                 goto leave_check_include;
 498         }
 499 
 500         for (m = first; m < nentries; m++) {
 501                 if (newtbl[m] != NULL &&
 502                     strcmp(newtbl[m]->dtl_rec->dt_key, mname) == 0) {
 503                         err = 0; /* already processed */
 504                         goto leave_check_include;
 505                 }
 506         }
 507 
 508         /*
 509          * is it defined someplace?
 510          */
 511         for (m = first; m < nentries; m++) {
 512                 if (strcmp(oldtbl[m]->dtl_rec->dt_key, mname) == 0) {
 513                         current_rowp = oldtbl[m];
 514                         break;
 515                 }
 516         }
 517 
 518         if (current_rowp == NULL) {
 519                 dhcpmsg(LOG_ERR, "Undefined macro: %s\n", mname);
 520                 err = -1;
 521                 goto leave_check_include;
 522         }
 523 
 524         /*
 525          * Scan value field, looking for includes.
 526          */
 527         cp = current_rowp->dtl_rec->dt_value;
 528         while (cp) {
 529                 adjust(&cp);
 530                 if (scan_include(&cp, include) != 0) {
 531                         /* find a free entry */
 532                         for (m = first; m < nentries; m++) {
 533                                 if (newtbl[m] == NULL)
 534                                         break;
 535                         }
 536                         if (m >= nentries) {
 537                                 dhcpmsg(LOG_ERR,
 538                                     "Macro expansion (Include=%s) error!\n",
 539                                     mname);
 540                                 err = -1;
 541                         } else {
 542                                 newtbl[m] = current_rowp;
 543                                 err = 0;
 544                         }
 545                         break;
 546                 }
 547 
 548                 if (*include == '\0') {
 549                         /*
 550                          * Null value for macro name. We can safely ignore
 551                          * this entry. An error message will be generated
 552                          * later during encode processing.
 553                          */
 554                         continue;
 555                 }
 556 
 557                 if (strcmp(mname, include) == 0) {
 558                         dhcpmsg(LOG_ERR,
 559                             "Circular macro definition using: %s\n", mname);
 560                         err = -1;
 561                         break;
 562                 }
 563 
 564                 /* Recurse. */
 565                 if ((err = check_includes(first, include)) != 0)
 566                         break;
 567         }
 568 
 569 leave_check_include:
 570         include_nest--;
 571         return (err);
 572 }
 573 
 574 /*
 575  * open_macros: open reference to macro table.
 576  */
 577 void
 578 open_macros(void) {
 579         (void) mutex_lock(&mtable_mtx);
 580         mtable_refcnt++;
 581         (void) mutex_unlock(&mtable_mtx);
 582 }
 583 
 584 /*
 585  * close_macros: close reference to macro table.
 586  */
 587 void
 588 close_macros(void) {
 589         (void) mutex_lock(&mtable_mtx);
 590         mtable_refcnt--;
 591         (void) cond_signal(&mtable_cv);
 592         (void) mutex_unlock(&mtable_mtx);
 593 }
 594 
 595 /*
 596  * Given a macro name, look it up in the hash table.
 597  * Returns ptr to MACRO structure, NULL if error occurs.
 598  */
 599 MACRO *
 600 get_macro(char *mnamep)
 601 {
 602         if (mnamep == (char *)NULL)
 603                 return ((MACRO *)NULL);
 604 
 605         return ((MACRO *)hash_Lookup(mtable, mnamep, strlen(mnamep), macro_cmp,
 606             mnamep, B_FALSE));
 607 }
 608 
 609 /*ARGSUSED*/
 610 static boolean_t
 611 free_macro(MACRO *mp, boolean_t force)
 612 {
 613         int i;
 614 
 615         if (mp) {
 616                 free_encode_list(mp->head);
 617                 for (i = 0; i < mp->classes; i++) {
 618                         if (mp->list[i]->head != NULL)
 619                                 free_encode_list(mp->list[i]->head);
 620                         free(mp->list[i]);
 621                 }
 622                 free(mp->list);
 623                 free(mp);
 624         }
 625         return (B_TRUE);
 626 }
 627 
 628 static int
 629 macro_cmp(MACRO *m1, MACRO *m2)
 630 {
 631         if (!m1 || !m2)
 632                 return (B_FALSE);
 633 
 634         if (strcmp(m1->nm, m2->nm) == 0)
 635                 return (B_TRUE);
 636         else
 637                 return (B_FALSE);
 638 }
 639 
 640 /*
 641  * Parse out all the various tags and parameters in the row entry pointed
 642  * to by "src".
 643  *
 644  * Returns 0 for success, nozero otherwise.
 645  */
 646 static MACRO *
 647 process_entry(dt_rec_list_t *src)
 648 {
 649         char *cp;
 650         MACRO *mc, *retval = NULL;
 651 
 652         assert(src != NULL);
 653 
 654         if (strlen(src->dtl_rec->dt_key) > DSVC_MAX_MACSYM_LEN) {
 655                 dhcpmsg(LOG_ERR,
 656                     "Token: %s is too long. Limit: %d characters.\n",
 657                     src->dtl_rec->dt_key, DSVC_MAX_MACSYM_LEN);
 658                 return (retval);
 659         }
 660 
 661         switch (tolower(src->dtl_rec->dt_type)) {
 662         case DT_SYMBOL:
 663                 /* New Symbol definition */
 664                 cp = src->dtl_rec->dt_value;
 665                 if (!define_symbol(&cp, src->dtl_rec->dt_key))
 666                         dhcpmsg(LOG_ERR,
 667                             "Bad Runtime symbol definition: %s\n",
 668                             src->dtl_rec->dt_key);
 669                 /* Success. Treat new symbol like the predefines. */
 670                 break;
 671         case DT_MACRO:
 672                 /* Macro definition */
 673 
 674                 mc = (MACRO *)smalloc(sizeof (MACRO));
 675                 (void) strcpy(mc->nm, src->dtl_rec->dt_key);
 676 
 677                 cp = src->dtl_rec->dt_value;
 678                 adjust(&cp);
 679                 while (*cp != '\0') {
 680                         if (eval_symbol(&cp, mc) != 0) {
 681                                 dhcpmsg(LOG_ERR,
 682                                     "Error processing macro: %s\n", mc->nm);
 683                                 (void) free_macro(mc, B_TRUE);
 684                                 return (NULL);
 685                         }
 686                         adjust(&cp);
 687                         eat_whitespace(&cp);
 688                 }
 689                 retval = mc;
 690                 break;
 691         default:
 692                 dhcpmsg(LOG_ERR, "Unrecognized token: %s.\n",
 693                         src->dtl_rec->dt_key);
 694                 break;
 695         }
 696         return (retval);
 697 }
 698 
 699 /*
 700  * This function processes the parameter name pointed to by "symbol" and
 701  * updates the appropriate ENCODE structure in data if one already exists,
 702  * or allocates a new one for this parameter.
 703  */
 704 static int
 705 eval_symbol(char **symbol, MACRO *mc)
 706 {
 707         int     index, optype, i, j, err = 0;
 708         dhcp_symbol_t   *sp;
 709         char            **clp;
 710         ENCODE  *tmp;
 711         VNDLIST **mpp, **ipp;
 712         MACRO   *ic;
 713         char    *cp;
 714         uchar_t ilen;
 715         uint16_t len;
 716         char    t_sym[DSVC_MAX_MACSYM_LEN + 1];
 717         char    include[DHCP_SCRATCH + 1];
 718         /*
 719          * The following buffer must be aligned on a int64_t boundary.
 720          */
 721         uint64_t scratch[(UCHAR_MAX + sizeof (int64_t) - 1) /
 722             sizeof (int64_t)];
 723 
 724         if ((*symbol)[0] == ':')
 725                 return (0);
 726 
 727         eat_whitespace(symbol);
 728         get_sym_name(t_sym, symbol);
 729 
 730         for (index = 0; index < sym_num_items; index++) {
 731                 if (strcmp(t_sym, sym_list[index].ds_name) == 0)
 732                         break;
 733         }
 734         if (index >= sym_num_items) {
 735                 dhcpmsg(LOG_ERR, "Unrecognized symbol name: '%s'\n", t_sym);
 736                 return (-1);
 737         } else {
 738                 sp = &sym_list[index];
 739                 clp = sp->ds_classes.dc_names;
 740         }
 741         /*
 742          * Determine the type of operation to be done on this symbol
 743          */
 744         switch (**symbol) {
 745         case '=':
 746                 optype = OP_ADDITION;
 747                 (*symbol)++;
 748                 break;
 749         case '@':
 750                 optype = OP_DELETION;
 751                 (*symbol)++;
 752                 break;
 753         case ':':
 754         case '\0':
 755                 optype = OP_BOOLEAN;
 756                 break;
 757         default:
 758                 dhcpmsg(LOG_ERR, "Syntax error: symbol: '%s' in macro: %s\n",
 759                     t_sym, mc->nm);
 760                 return (-1);
 761         }
 762 
 763         switch (optype) {
 764         case OP_ADDITION:
 765                 switch (sp->ds_type) {
 766                 case DSYM_BOOL:
 767                         err = -1;
 768                         break;
 769 
 770                 case DSYM_INCLUDE:
 771                         /*
 772                          * If symbol type is INCLUDE, then walk the encode
 773                          * list, replacing any previous encodes with those
 774                          * from the INCLUDed macro. Vendor options are also
 775                          * merged, if their class and vendor codes match.
 776                          */
 777                         ilen = DHCP_SCRATCH;
 778                         (void) get_string(symbol, include, &ilen);
 779                         include[ilen] = '\0';
 780                         ic = get_macro(include);
 781                         if (ic == (MACRO *)NULL) {
 782                                 dhcpmsg(LOG_ERR, "WARNING: No macro: '%1$s' \
 783 defined for 'Include' symbol in macro: %2$s\n",
 784                                     include, mc->nm);
 785                                 adjust(symbol);
 786                                 return (0);
 787                         }
 788 
 789                         mc->head = combine_encodes(mc->head, ic->head,
 790                             ENC_DONT_COPY);
 791 
 792                         if (ic->list == NULL && mc->list == NULL)
 793                                 break;
 794 
 795                         /* Vendor options. */
 796                         if (mc->list == NULL) {
 797                                 /*
 798                                  * No combining necessary. Just duplicate
 799                                  * ic's vendor options - all classes.
 800                                  */
 801                                 mc->list = (VNDLIST **)smalloc(
 802                                     sizeof (VNDLIST **) * ic->classes);
 803                                 for (i = 0;  i < ic->classes; i++) {
 804                                         mc->list[i] = (VNDLIST *)smalloc(
 805                                             sizeof (VNDLIST));
 806                                         (void) strcpy(mc->list[i]->class,
 807                                             ic->list[i]->class);
 808                                         mc->list[i]->head = dup_encode_list(
 809                                             ic->list[i]->head);
 810                                 }
 811                                 mc->classes = ic->classes;
 812                         } else {
 813                                 /* Class and vendor code must match. */
 814                                 for (i = 0, ipp = ic->list;
 815                                     ipp && i < ic->classes; i++) {
 816                                         for (j = 0, mpp = mc->list;
 817                                             j < mc->classes; j++) {
 818                                                 if (strcmp(mpp[j]->class,
 819                                                     ipp[i]->class) == 0) {
 820                                                         mpp[j]->head =
 821                                                             combine_encodes(
 822                                                             mpp[j]->head,
 823                                                             ipp[i]->head,
 824                                                             ENC_DONT_COPY);
 825                                                         break;
 826                                                 }
 827                                         }
 828                                 }
 829                         }
 830                         break;
 831 
 832                 default:
 833                         /*
 834                          * Get encode associated with symbol value.
 835                          */
 836                         tmp = (ENCODE *)smalloc(sizeof (ENCODE));
 837 
 838                         if (sp->ds_type == DSYM_ASCII) {
 839                                 if (sp->ds_max)
 840                                         ilen = sp->ds_max;
 841                                 else
 842                                         ilen = UCHAR_MAX;
 843                                 (void) get_string(symbol, (char *)scratch,
 844                                         &ilen);
 845                                 include[ilen] = '\0';
 846 
 847                                 tmp->data = inittab_encode_e(sp,
 848                                         (char *)scratch, &len, B_TRUE, &err);
 849                         } else {
 850 
 851                                 if ((cp = strchr(*symbol, ':')) != NULL)
 852                                         *cp = '\0';
 853 
 854                                 tmp->data = inittab_encode_e(sp, *symbol, &len,
 855                                         B_TRUE, &err);
 856                                 /*
 857                                  * Advance symbol pointer to next encode.
 858                                  */
 859                                 if (cp != NULL) {
 860                                         *cp = ':';
 861                                         *symbol = cp;
 862                                 } else {
 863                                         while (*symbol != '\0')
 864                                                 symbol++;
 865                                 }
 866                         }
 867 
 868                         tmp->len = len;
 869                         tmp->category = sp->ds_category;
 870                         tmp->code = sp->ds_code;
 871 
 872                         if (err != 0 || tmp->data == NULL) {
 873                                 if (err == 0)
 874                                         err = -1;
 875                                 free_encode(tmp);
 876                         } else {
 877                                 /*
 878                                  * Find/replace/add encode.
 879                                  */
 880                                 if (sp->ds_category != DSYM_VENDOR) {
 881                                         replace_encode(&mc->head, tmp,
 882                                             ENC_DONT_COPY);
 883                                 } else
 884                                         add_vndlist(tmp, mc, sp);
 885                         }
 886                         break;
 887                 }
 888                 break;
 889 
 890         case OP_DELETION:
 891                 if (sp->ds_type == DSYM_INCLUDE)
 892                         return (-1);
 893 
 894                 if (sp->ds_category != DSYM_VENDOR) {
 895                         tmp = find_encode(mc->head, sp->ds_category,
 896                                 sp->ds_code);
 897                         if (tmp != (ENCODE *)NULL) {
 898                                 if (tmp->prev != (ENCODE *)NULL)
 899                                         tmp->prev->next = tmp->next;
 900                                 else
 901                                         mc->head = mc->head->next;
 902                                 free_encode(tmp);
 903                         }
 904                 } else {
 905                         for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
 906                                 for (j = 0; mc->list && j < mc->classes;
 907                                     j++) {
 908                                         if (strcmp(clp[i],
 909                                             mc->list[j]->class) == 0) {
 910                                                 tmp = find_encode(
 911                                                     mc->list[j]->head,
 912                                                     sp->ds_category,
 913                                                     sp->ds_code);
 914                                                 if (tmp == NULL)
 915                                                         continue;
 916                                                 if (tmp->prev != NULL) {
 917                                                         tmp->prev->next =
 918                                                             tmp->next;
 919                                                 } else {
 920                                                         mc->list[j]->head =
 921                                                             mc->list[j]->
 922                                                             head->next;
 923                                                 }
 924                                                 free_encode(tmp);
 925                                         }
 926                                 }
 927                         }
 928                 }
 929 
 930                 err = 0;
 931                 break;
 932 
 933         case OP_BOOLEAN:
 934                 if (sp->ds_type == DSYM_INCLUDE)
 935                         return (-1);
 936                 /*
 937                  * True signified by existence, false by omission.
 938                  */
 939                 if (sp->ds_category != DSYM_VENDOR) {
 940                         tmp = find_encode(mc->head, sp->ds_category,
 941                                 sp->ds_code);
 942                         if (tmp == (ENCODE *)NULL) {
 943                                 tmp = make_encode(sp->ds_category, sp->ds_code,
 944                                         0, NULL, ENC_DONT_COPY);
 945                                 replace_encode(&mc->head, tmp,
 946                                     ENC_DONT_COPY);
 947                         }
 948                 } else {
 949                         for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
 950                                 for (j = 0; mc->list && j < mc->classes;
 951                                     j++) {
 952                                         if (strcmp((const char *)clp[i],
 953                                             mc->list[j]->class) == 0) {
 954                                                 tmp = find_encode(
 955                                                     mc->list[j]->head,
 956                                                     sp->ds_category,
 957                                                     sp->ds_code);
 958                                                 if (tmp == NULL) {
 959                                                         tmp = make_encode(
 960                                                             sp->ds_category,
 961                                                             sp->ds_code, 0,
 962                                                             NULL,
 963                                                             ENC_DONT_COPY);
 964                                                         replace_encode(
 965                                                             &mc->list[j]->
 966                                                             head, tmp,
 967                                                             ENC_DONT_COPY);
 968                                                 }
 969                                         }
 970                                 }
 971                         }
 972                 }
 973 
 974                 err = 0;
 975                 break;
 976         }
 977         if (err)
 978                 print_error_msg(ITAB_SYNTAX_ERROR, index);
 979         return (err);
 980 }
 981 
 982 /*
 983  * Find/add option to appropriate client classes.
 984  */
 985 static void
 986 add_vndlist(ENCODE *vp, MACRO *mp, dhcp_symbol_t *sp)
 987 {
 988         int     i, j, class_exists, copy;
 989         VNDLIST **tmp;
 990         char **cp = sp->ds_classes.dc_names;
 991 
 992         copy = ENC_DONT_COPY;
 993         for (i = 0; i < sp->ds_classes.dc_cnt; i++) {
 994                 class_exists = 0;
 995                 for (j = 0; mp->list && j < mp->classes; j++) {
 996                         if (strcmp(cp[i], mp->list[j]->class) == 0) {
 997                                 class_exists = 1;
 998                                 replace_encode(&mp->list[j]->head, vp, copy);
 999                                 if (copy == ENC_DONT_COPY)
1000                                         copy = ENC_COPY;
1001                         }
1002                 }
1003                 if (!class_exists) {
1004                         tmp = (VNDLIST **)realloc(mp->list,
1005                             sizeof (VNDLIST **) * (j + 1));
1006                         if (tmp != NULL)
1007                                 mp->list = tmp;
1008                         else {
1009                                 dhcpmsg(LOG_ERR, "Warning: ran out of \
1010 memory adding vendor class: '%1$s' for symbol: '%2$s'\n",
1011                                     cp[i], sp->ds_name);
1012                                 break;
1013                         }
1014                         mp->list[j] = (VNDLIST *)smalloc(sizeof (VNDLIST));
1015                         (void) strcpy(mp->list[j]->class, cp[i]);
1016                         if (copy == ENC_DONT_COPY) {
1017                                 mp->list[j]->head = vp;
1018                                 copy = ENC_COPY;
1019                         } else
1020                                 mp->list[j]->head = dup_encode(vp);
1021                         mp->classes++;
1022                 }
1023         }
1024 }
1025 
1026 /*
1027  * CMU 2.2 routine.
1028  *
1029  * Read a string from the buffer indirectly pointed to through "src" and
1030  * move it into the buffer pointed to by "dest".  A pointer to the maximum
1031  * allowable length of the string (including null-terminator) is passed as
1032  * "length".  The actual length of the string which was read is returned in
1033  * the unsigned integer pointed to by "length".  This value is the same as
1034  * that which would be returned by applying the strlen() function on the
1035  * destination string (i.e the terminating null is not counted as a
1036  * character).  Trailing whitespace is removed from the string.  For
1037  * convenience, the function returns the new value of "dest".
1038  *
1039  * The string is read until the maximum number of characters, an unquoted
1040  * colon (:), or a null character is read.  The return string in "dest" is
1041  * null-terminated.
1042  */
1043 static char *
1044 get_string(char **src, char *dest, uchar_t *length)
1045 {
1046         int n = 0, len, quoteflag;
1047 
1048         quoteflag = B_FALSE;
1049         len = *length - 1;
1050         while ((n < len) && (**src)) {
1051                 if (quoteflag == B_FALSE && (**src == ':'))
1052                         break;
1053                 if (**src == '"') {
1054                         (*src)++;
1055                         quoteflag = !quoteflag;
1056                         continue;
1057                 }
1058                 if (**src == '\\') {
1059                         (*src)++;
1060                         if (!**src)
1061                                 break;
1062                 }
1063                 *dest++ = *(*src)++;
1064                 n++;
1065         }
1066 
1067         /*
1068          * Remove that troublesome trailing whitespace. . .
1069          */
1070         while ((n > 0) && isspace(*(char *)(dest - 1))) {
1071                 dest--;
1072                 n--;
1073         }
1074 
1075         *dest = '\0';
1076         *length = n;
1077         return (dest);
1078 }
1079 
1080 /*
1081  * This function adjusts the caller's pointer to point just past the
1082  * first-encountered colon.  If it runs into a null character, it leaves
1083  * the pointer pointing to it.
1084  */
1085 static void
1086 adjust(char **s)
1087 {
1088         char *t;
1089 
1090         t = *s;
1091         while (*t && (*t != ':'))
1092                 t++;
1093 
1094         if (*t)
1095                 t++;
1096         *s = t;
1097 }
1098 
1099 /*
1100  * This function adjusts the caller's pointer to point to the first
1101  * non-whitespace character.  If it runs into a null character, it leaves
1102  * the pointer pointing to it.
1103  */
1104 static void
1105 eat_whitespace(char **s)
1106 {
1107         char *t;
1108 
1109         t = *s;
1110         while (*t && isspace(*t))
1111                 t++;
1112         *s = t;
1113 }
1114 
1115 /*
1116  *  Copy symbol name into buffer. Sym ends up pointing to the end of the
1117  * token.
1118  */
1119 static void
1120 get_sym_name(char *buf, char **sym)
1121 {
1122         int i;
1123 
1124         for (i = 0; i < DSVC_MAX_MACSYM_LEN; i++) {
1125                 if (**sym == ':' || **sym == '=' || **sym == '@' ||
1126                     **sym == '\0')
1127                         break;
1128                 *buf++ = *(*sym)++;
1129         }
1130         *buf = '\0';
1131 }
1132 
1133 static void
1134 print_error_msg(int error, uchar_t index)
1135 {
1136         switch (error) {
1137         case ITAB_BAD_IPADDR:
1138                 dhcpmsg(LOG_ERR, "Error processing Internet address \
1139 value(s) for symbol: '%s'\n", sym_list[index].ds_name);
1140                 break;
1141         case ITAB_BAD_STRING:
1142                 dhcpmsg(LOG_ERR, "Error processing ASCII string value for \
1143 symbol: '%s'\n", sym_list[index].ds_name);
1144                 break;
1145         case ITAB_BAD_OCTET:
1146                 dhcpmsg(LOG_ERR, "Error processing OCTET string value for \
1147 symbol: '%s'\n", sym_list[index].ds_name);
1148                 break;
1149         case ITAB_BAD_NUMBER:
1150                 dhcpmsg(LOG_ERR, "Error processing NUMBER value for \
1151 symbol: '%s'\n", sym_list[index].ds_name);
1152                 break;
1153         case ITAB_BAD_BOOLEAN:
1154                 dhcpmsg(LOG_ERR,
1155                     "Error processing BOOLEAN value for symbol: '%s'\n",
1156                     sym_list[index].ds_name);
1157                 break;
1158         case ITAB_SYNTAX_ERROR:
1159         /* FALLTHRU */
1160         default:
1161                 dhcpmsg(LOG_ERR,
1162                     "Syntax error found processing value for symbol: '%s'\n",
1163                     sym_list[index].ds_name);
1164                 break;
1165         }
1166 }
1167 
1168 /*
1169  * Define new symbols for things like site-wide and vendor options.
1170  */
1171 static boolean_t
1172 define_symbol(char **ptr, char *name)
1173 {
1174 
1175         dhcp_symbol_t sym;
1176         char **fields;
1177         int last = 0;
1178         dsym_errcode_t ret = DSYM_SUCCESS;
1179         ushort_t min;
1180         ushort_t max;
1181         int i;
1182 
1183         /*
1184          * Only permit new symbol definitions, not old ones. I suppose we
1185          * could allow the administrator to redefine symbols, but what if
1186          * they redefine subnetmask to be a new brownie recipe? Let's stay
1187          * out of that rat hole for now.
1188          */
1189         for (i = 0; i < sym_num_items; i++) {
1190                 if (strcmp(name, sym_list[i].ds_name) == 0) {
1191                         dhcpmsg(LOG_ERR, "Symbol: %s already defined. New "
1192                             "definition ignored.\n", name);
1193                         adjust(ptr);
1194                         return (0);
1195                 }
1196         }
1197 
1198         ret = dsym_init_parser(name, *ptr, &fields, &sym);
1199         if (ret != DSYM_SUCCESS) {
1200                 switch (ret) {
1201                 case DSYM_NULL_FIELD:
1202                         dhcpmsg(LOG_ERR,
1203                             "Item is missing in symbol definition: '%s'\n",
1204                             name);
1205                         break;
1206 
1207                 case DSYM_TOO_MANY_FIELDS:
1208                         dhcpmsg(LOG_ERR,
1209                             "Too many items exist in symbol definition: %s\n",
1210                             name);
1211                         break;
1212                 case DSYM_NO_MEMORY:
1213                         dhcpmsg(LOG_ERR,
1214                             "Ran out of memory processing symbol: '%s'\n",
1215                             name);
1216                         break;
1217                 default:
1218                         dhcpmsg(LOG_ERR,
1219                             "Internal error processing symbol: '%s'\n",
1220                             name);
1221                         break;
1222 
1223                 }
1224                 return (B_FALSE);
1225         }
1226 
1227         ret = dsym_parser(fields, &sym, &last, B_FALSE);
1228         if (ret != DSYM_SUCCESS) {
1229                 switch (ret) {
1230                 case DSYM_SYNTAX_ERROR:
1231                         dhcpmsg(LOG_ERR,
1232                             "Syntax error parsing symbol definition: '%s'\n",
1233                             name);
1234                         break;
1235 
1236                 case DSYM_CODE_OUT_OF_RANGE:
1237                         (void) dsym_get_code_ranges(fields[DSYM_CAT_FIELD],
1238                             &min, &max, B_TRUE);
1239                         dhcpmsg(LOG_ERR, "Out of range (%d-%d) option code: "
1240                             "%d in symbol definition: '%s'\n",
1241                             min, max, sym.ds_code, name);
1242                         break;
1243 
1244                 case DSYM_VALUE_OUT_OF_RANGE:
1245                         dhcpmsg(LOG_ERR,
1246                             "Bad item, %s, in symbol definition: '%s'\n",
1247                             fields[last], name);
1248                         break;
1249 
1250                 case DSYM_INVALID_CAT:
1251                         dhcpmsg(LOG_ERR, "Missing/Incorrect Site/Vendor flag "
1252                             "in symbol definition: '%s'\n", name);
1253                         break;
1254 
1255                 case DSYM_INVALID_TYPE:
1256                         dhcpmsg(LOG_ERR, "Unrecognized value descriptor: %s "
1257                             "in symbol definition: '%s'\n",
1258                             fields[DSYM_TYPE_FIELD], name);
1259                         break;
1260 
1261                 case DSYM_EXCEEDS_CLASS_SIZE:
1262                         dhcpmsg(LOG_ERR, "Client class is too "
1263                             "long for vendor symbol: '%s'. Must be "
1264                             "less than: %d\n", name, DSYM_CLASS_SIZE);
1265                         break;
1266 
1267                 case DSYM_EXCEEDS_MAX_CLASS_SIZE:
1268                         dhcpmsg(LOG_ERR, "Client class is too long for "
1269                             "vendor symbol: '%s'. Must be less than: %d\n",
1270                             name, DSYM_MAX_CLASS_SIZE);
1271                         break;
1272 
1273                 case DSYM_NO_MEMORY:
1274                         dhcpmsg(LOG_ERR,
1275                             "Ran out of memory processing symbol: '%s'\n",
1276                             name);
1277                         break;
1278 
1279                 default:
1280                         dhcpmsg(LOG_ERR,
1281                             "Internal error processing symbol: '%s'\n",
1282                             name);
1283                         break;
1284                 }
1285                 dsym_close_parser(fields, &sym);
1286                 return (B_FALSE);
1287         }
1288 
1289         /*
1290          * Don't free the symbol structure resources, we need those.
1291          * Just free the fields memory. We will free the symbol structure
1292          * resources later.
1293          */
1294         dsym_free_fields(fields);
1295 
1296         /*
1297          * Now add it to the existing definitions, reallocating
1298          * the dynamic symbol list.
1299          */
1300         sym_list = (dhcp_symbol_t *)realloc(sym_list,
1301                         (sym_num_items + 1) * sizeof (dhcp_symbol_t));
1302         if (sym_list != (dhcp_symbol_t *)NULL) {
1303                 sym_num_items++;
1304                 (void) memcpy(&sym_list[sym_num_items - 1], &sym,
1305                     sizeof (dhcp_symbol_t));
1306         } else {
1307                 dhcpmsg(LOG_ERR,
1308                     "Cannot extend symbol table, using predefined table.\n");
1309                 resettab(B_FALSE);
1310         }
1311 
1312         return (B_TRUE);
1313 }