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 }