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 }