xref: /freebsd/contrib/bsnmp/snmpd/config.c (revision c697fb7f)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Begemot: bsnmp/snmpd/config.c,v 1.25 2006/02/14 09:04:20 brandt_h Exp $
30  *
31  * Parse configuration file.
32  */
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdarg.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 #include <limits.h>
46 #include <netdb.h>
47 #include <setjmp.h>
48 #include <inttypes.h>
49 
50 #include "snmpmod.h"
51 #include "snmpd.h"
52 #include "tree.h"
53 
54 /*
55 #define DEBUGGING
56 */
57 
58 /*
59  * config_file: EMPTY | config_file line
60  *
61  * line: oid '=' value
62  *     | '%' STRING
63  *     | STRING := REST_OF_LINE
64  *     | STRING ?= REST_OF_LINE
65  *     | . INCLUDE STRING
66  *
67  * oid: STRING suboid
68  *
69  * suboid: EMPTY | suboid '.' subid
70  *
71  * subid: NUM | STRING | '[' STRING ']'
72  *
73  * value: EMPTY | STRING | NUM
74  */
75 
76 /*
77  * Input context for macros and includes
78  */
79 enum input_type {
80 	INPUT_FILE	= 1,
81 	INPUT_STRING
82 };
83 struct input {
84 	enum input_type	type;
85 	union {
86 	    struct {
87 		FILE	*fp;
88 		char	*filename;
89 		u_int	lno;
90 	    }		file;
91 	    struct {
92 		char	*macro;
93 		char	*str;
94 		char	*ptr;
95 		size_t	left;
96 	    }		str;
97 	} u;
98 	LIST_ENTRY(input) link;
99 };
100 static LIST_HEAD(, input) inputs;
101 
102 #define input_fp	u.file.fp
103 #define input_filename	u.file.filename
104 #define input_lno	u.file.lno
105 #define input_macro	u.str.macro
106 #define input_str	u.str.str
107 #define input_ptr	u.str.ptr
108 #define input_left	u.str.left
109 
110 static int input_push;
111 static int input_buf[2];
112 
113 /*
114  * Configuration data. The configuration file is handled as one single
115  * SNMP transaction. So we need to keep the assignment data for the
116  * commit or rollback pass. Note, that dependencies and finish functions
117  * are NOT allowed here.
118  */
119 struct assign {
120 	struct snmp_value value;
121 	struct snmp_scratch scratch;
122 	const char *node_name;
123 
124 	TAILQ_ENTRY(assign) link;
125 };
126 static TAILQ_HEAD(assigns, assign) assigns = TAILQ_HEAD_INITIALIZER(assigns);
127 
128 
129 static struct snmp_context *snmp_ctx;
130 
131 struct macro {
132 	char	*name;
133 	char	*value;
134 	size_t	length;
135 	LIST_ENTRY(macro) link;
136 	int	perm;
137 };
138 static LIST_HEAD(, macro) macros = LIST_HEAD_INITIALIZER(macros);
139 
140 enum {
141 	TOK_EOF	= 0200,
142 	TOK_EOL,
143 	TOK_NUM,
144 	TOK_STR,
145 	TOK_HOST,
146 	TOK_ASSIGN,
147 	TOK_QASSIGN,
148 };
149 
150 /* lexer values and last token */
151 static uint64_t	numval;
152 static char	strval[_POSIX2_LINE_MAX];
153 static size_t	strvallen;
154 static int	token;
155 
156 /* error return */
157 static jmp_buf	errjmp[4];
158 static volatile int errstk;
159 
160 # define ERRPUSH()	(setjmp(errjmp[errstk++]))
161 # define ERRPOP()	((void)(errstk--))
162 # define ERRNEXT()	(longjmp(errjmp[--errstk], 1))
163 # define ERR()		(longjmp(errjmp[--errstk], 1))
164 
165 /* section context */
166 static int ignore;
167 
168 /*
169  * Report an error and jump to the error label
170  */
171 static void report(const char *fmt, ...) __dead2 __printflike(1, 2);
172 
173 static void
174 report(const char *fmt, ...)
175 {
176 	va_list ap;
177 	const struct input *input;
178 
179 	va_start(ap, fmt);
180 	vsyslog(LOG_ERR, fmt, ap);
181 	va_end(ap);
182 
183 	LIST_FOREACH(input, &inputs, link) {
184 		switch (input->type) {
185 
186 		  case INPUT_FILE:
187 			syslog(LOG_ERR, "  in file %s line %u",
188 			    input->input_filename, input->input_lno);
189 			break;
190 
191 		  case INPUT_STRING:
192 			syslog(LOG_ERR, "  in macro %s pos %td",
193 			    input->input_macro,
194 			    input->input_ptr - input->input_str);
195 			break;
196 		}
197 	}
198 	ERR();
199 }
200 
201 /*
202  * Open a file for input
203  */
204 static int
205 input_open_file(const char *fname, int sysdir)
206 {
207 	struct input *input;
208 	FILE *fp;
209 	char path[PATH_MAX + 1];
210 	const char *col;
211 	const char *ptr;
212 
213 	if (sysdir) {
214 		ptr = syspath;
215 		fp = NULL;
216 		while (*ptr != '\0') {
217 			if ((col = strchr(ptr, ':')) == NULL) {
218 				snprintf(path, sizeof(path), "%s/%s",
219 				    ptr, fname);
220 				col = ptr + strlen(ptr) - 1;
221 			} else if (col == ptr)
222 				snprintf(path, sizeof(path), "./%s", fname);
223 			else
224 				snprintf(path, sizeof(path), "%.*s/%s",
225 				    (int)(col - ptr), ptr, fname);
226 			if ((fp = fopen(path, "r")) != NULL)
227 				break;
228 			ptr = col + 1;
229 		}
230 	} else
231 		fp = fopen(fname, "r");
232 
233 	if (fp == NULL)
234 		report("%s: %m", fname);
235 
236 	if ((input = malloc(sizeof(*input))) == NULL) {
237 		fclose(fp);
238 		return (-1);
239 	}
240 	if ((input->input_filename = malloc(strlen(fname) + 1)) == NULL) {
241 		fclose(fp);
242 		free(input);
243 		return (-1);
244 	}
245 	strcpy(input->input_filename, fname);
246 	input->input_fp = fp;
247 	input->input_lno = 1;
248 	input->type = INPUT_FILE;
249 	LIST_INSERT_HEAD(&inputs, input, link);
250 	return (0);
251 }
252 
253 /*
254  * Make a macro the next input
255  */
256 static void
257 input_open_macro(struct macro *m)
258 {
259 	struct input *input;
260 
261 	if ((input = malloc(sizeof(*input))) == NULL)
262 		report("%m");
263 	input->type = INPUT_STRING;
264 	input->input_macro = m->name;
265 	if ((input->input_str = malloc(m->length)) == NULL) {
266 		free(input);
267 		report("%m");
268 	}
269 	memcpy(input->input_str, m->value, m->length);
270 	input->input_ptr = input->input_str;
271 	input->input_left = m->length;
272 	LIST_INSERT_HEAD(&inputs, input, link);
273 }
274 
275 /*
276  * Close top input source
277  */
278 static void
279 input_close(void)
280 {
281 	struct input *input;
282 
283 	if ((input = LIST_FIRST(&inputs)) == NULL)
284 		abort();
285 	switch (input->type) {
286 
287 	  case INPUT_FILE:
288 		fclose(input->input_fp);
289 		free(input->input_filename);
290 		break;
291 
292 	  case INPUT_STRING:
293 		free(input->input_str);
294 		break;
295 	}
296 	LIST_REMOVE(input, link);
297 	free(input);
298 }
299 
300 /*
301  * Close all inputs
302  */
303 static void
304 input_close_all(void)
305 {
306 	while (!LIST_EMPTY(&inputs))
307 		input_close();
308 }
309 
310 /*
311  * Push back one character
312  */
313 static void
314 input_ungetc(int c)
315 {
316 	if (c == EOF)
317 		report("pushing EOF");
318 	if (input_push == 2)
319 		report("pushing third char");
320 	input_buf[input_push++] = c;
321 }
322 
323 
324 /*
325  * Return next character from the input without preprocessing.
326  */
327 static int
328 input_getc_raw(void)
329 {
330 	int c;
331 	struct input *input;
332 
333 	if (input_push != 0) {
334 		c = input_buf[--input_push];
335 		goto ok;
336 	}
337 	while ((input = LIST_FIRST(&inputs)) != NULL) {
338 		switch (input->type) {
339 
340 		  case INPUT_FILE:
341 			if ((c = getc(input->input_fp)) == EOF) {
342 				if (ferror(input->input_fp))
343 					report("read error: %m");
344 				input_close();
345 				break;
346 			}
347 			if (c == '\n')
348 				input->input_lno++;
349 			goto ok;
350 
351 		  case INPUT_STRING:
352 			if (input->input_left-- == 0) {
353 				input_close();
354 				break;
355 			}
356 			c = *input->input_ptr++;
357 			goto ok;
358 		}
359 	}
360 # ifdef DEBUGGING
361 	fprintf(stderr, "EOF");
362 # endif
363 	return (EOF);
364 
365   ok:
366 # ifdef DEBUGGING
367 	if (!isascii(c) || !isprint(c))
368 		fprintf(stderr, "'%#2x'", c);
369 	else
370 		fprintf(stderr, "'%c'", c);
371 # endif
372 	return (c);
373 }
374 
375 /*
376  * Get character with and \\n -> processing.
377  */
378 static int
379 input_getc_plain(void)
380 {
381 	int c;
382 
383   again:
384 	if ((c = input_getc_raw()) == '\\') {
385 		if ((c = input_getc_raw()) == '\n')
386 			goto again;
387 		if (c != EOF)
388 			input_ungetc(c);
389 		return ('\\');
390 	}
391 	return (c);
392 }
393 
394 /*
395  * Get next character with substitution of macros
396  */
397 static int
398 input_getc(void)
399 {
400 	int c;
401 	struct macro *m;
402 	char	name[_POSIX2_LINE_MAX];
403 	size_t	namelen;
404 
405   again:
406 	if ((c = input_getc_plain()) != '$')
407 		return (c);
408 
409 	if ((c = input_getc()) == EOF)
410 		report("unexpected EOF");
411 	if (c != '(')
412 		report("expecting '(' after '$'");
413 
414 	namelen = 0;
415 	while ((c = input_getc()) != EOF && c != ')') {
416 		if (isalpha(c) || c == '_' || (namelen != 0 && isdigit(c)))
417 			name[namelen++] = c;
418 		else
419 			goto badchar;
420 	}
421 	if (c == EOF)
422 		report("unexpected EOF");
423 	name[namelen++] = '\0';
424 
425 	LIST_FOREACH(m, &macros, link)
426 		if (strcmp(m->name, name) == 0)
427 			break;
428 	if (m == NULL)
429 		report("undefined macro '%s'", name);
430 
431 	input_open_macro(m);
432 	goto again;
433 
434   badchar:
435 	if (!isascii(c) || !isprint(c))
436 		report("unexpected character %#2x", (u_int)c);
437 	else
438 		report("bad character '%c'", c);
439 }
440 
441 
442 static void
443 input_getnum(u_int base, u_int flen)
444 {
445 	int c;
446 	u_int cnt;
447 
448 	cnt = 0;
449 	numval = 0;
450 	while (flen == 0 || cnt < flen) {
451 		if ((c = input_getc()) == EOF) {
452 			if (cnt == 0)
453 				report("bad number");
454 			return;
455 		}
456 		if (isdigit(c)) {
457 			if (base == 8 && (c == '8' || c == '9')) {
458 				input_ungetc(c);
459 				if (cnt == 0)
460 					report("bad number");
461 				return;
462 			}
463 			numval = numval * base + (c - '0');
464 		} else if (base == 16 && isxdigit(c)) {
465 			if (islower(c))
466 				numval = numval * base + (c - 'a' + 10);
467 			else
468 				numval = numval * base + (c - 'A' + 10);
469 		} else {
470 			input_ungetc(c);
471 			if (cnt == 0)
472 				report("bad number");
473 			return;
474 		}
475 		cnt++;
476 	}
477 }
478 
479 static int
480 # ifdef DEBUGGING
481 _gettoken(void)
482 # else
483 gettoken(void)
484 # endif
485 {
486 	int c;
487 	char *end;
488 	static const char esc[] = "abfnrtv";
489 	static const char chr[] = "\a\b\f\n\r\t\v";
490 
491 	/*
492 	 * Skip any whitespace before the next token
493 	 */
494 	while ((c = input_getc()) != EOF) {
495 		if (!isspace(c) || c == '\n')
496 			break;
497 	}
498 	if (c == EOF)
499 		return (token = TOK_EOF);
500 	if (!isascii(c))
501 		goto badchar;
502 
503 	/*
504 	 * Skip comments
505 	 */
506 	if (c == '#') {
507 		while ((c = input_getc_plain()) != EOF) {
508 			if (c == '\n')
509 				return (token = TOK_EOL);
510 		}
511 		goto badeof;
512 	}
513 
514 	/*
515 	 * Single character tokens
516 	 */
517 	if (c == '\n')
518 		return (token = TOK_EOL);
519 	if (c == '.' || c == '%' || c == '=' || c == '<' || c == '>')
520 		return (token = c);
521 	if (c == ':') {
522 		if ((c = input_getc()) == '=')
523 			return (token = TOK_ASSIGN);
524 		input_ungetc(c);
525 		return (token = ':');
526 	}
527 	if (c == '?') {
528 		if ((c = input_getc()) == '=')
529 			return (token = TOK_QASSIGN);
530 		input_ungetc(c);
531 		goto badchar;
532 	}
533 
534 	/*
535 	 * Sort out numbers
536 	 */
537 	if (isdigit(c)) {
538 		if (c == '0') {
539 			if ((c = input_getc()) == 'x' || c == 'X') {
540 				input_getnum(16, 0);
541 			} else if (isdigit(c)) {
542 				input_ungetc(c);
543 				input_getnum(8, 0);
544 			} else {
545 				if (c != EOF)
546 					input_ungetc(c);
547 				numval = 0;
548 				c = 1;
549 			}
550 		} else {
551 			input_ungetc(c);
552 			input_getnum(10, 0);
553 		}
554 		return (token = TOK_NUM);
555 	}
556 
557 	/*
558 	 * Must be a string then
559 	 */
560 	strvallen = 0;
561 
562 # define GETC(C) do {							\
563 	if ((c = input_getc()) == EOF)					\
564 		goto badeof;						\
565 	if (!isascii(c) || (!isprint(c) && c != '\t')) 			\
566 		goto badchar;						\
567 } while(0)
568 
569 	if (c == '"') {
570 		for(;;) {
571 			GETC(c);
572 			if (c == '"') {
573 				strval[strvallen] = '\0';
574 				break;
575 			}
576 			if (c != '\\') {
577 				strval[strvallen++] = c;
578 				continue;
579 			}
580 			GETC(c);
581 			if ((end = strchr(esc, c)) != NULL) {
582 				strval[strvallen++] = chr[end - esc];
583 				continue;
584 			}
585 			if (c == 'x') {
586 				input_getnum(16, 2);
587 				c = numval;
588 			} else if (c >= '0' && c <= '7') {
589 				input_ungetc(c);
590 				input_getnum(8, 3);
591 				c = numval;
592 			}
593 			strval[strvallen++] = c;
594 		}
595 # undef GETC
596 
597 	} else if (c == '[') {
598 		/*
599 		 * Skip leading space
600 		 */
601 		while ((c = input_getc()) != EOF && isspace(c))
602 			;
603 		if (c == EOF)
604 			goto badeof;
605 		while (c != ']' && !isspace(c)) {
606 			if (!isalnum(c) && c != '.' && c != '-')
607 				goto badchar;
608 			strval[strvallen++] = c;
609 			if ((c = input_getc()) == EOF)
610 				goto badeof;
611 		}
612 		while (c != ']' && isspace(c)) {
613 			if ((c = input_getc()) == EOF)
614 				goto badeof;
615 		}
616 		if (c != ']')
617 			goto badchar;
618 		strval[strvallen] = '\0';
619 		return (token = TOK_HOST);
620 
621 	} else if (!isalpha(c) && c != '_') {
622 		goto badchar;
623 
624 	} else {
625 		for (;;) {
626 			strval[strvallen++] = c;
627 			if ((c = input_getc()) == EOF)
628 				goto badeof;
629 			if (!isalnum(c) && c != '_' && c != '-') {
630 				input_ungetc(c);
631 				strval[strvallen] = '\0';
632 				break;
633 			}
634 		}
635 	}
636 
637 	return (token = TOK_STR);
638 
639   badeof:
640 	report("unexpected EOF");
641 
642   badchar:
643 	if (!isascii(c) || !isprint(c))
644 		report("unexpected character %#2x", (u_int)c);
645 	else
646 		report("bad character '%c'", c);
647 }
648 
649 # ifdef DEBUGGING
650 static int
651 gettoken()
652 {
653 	_gettoken();
654 	if (isascii(token) && isprint(token))
655 		printf("(%c)", token);
656 	else {
657 		switch (token) {
658 
659 		  case TOK_EOF:
660 			printf("(EOF)");
661 			break;
662 		  case TOK_EOL:
663 			printf("(EOL)");
664 			break;
665 		  case TOK_NUM:
666 			printf("(NUM %ju)", (uintmax_t)numval);
667 			break;
668 		  case TOK_STR:
669 			printf("(STR %.*s)", (int)strvallen, strval);
670 			break;
671 		  case TOK_HOST:
672 			printf("(HOST %s)", strval);
673 			break;
674 		  default:
675 			printf("(%#2x)", token);
676 			break;
677 		}
678 	}
679 	return (token);
680 }
681 #endif
682 
683 
684 /*
685  * Try to execute the assignment.
686  */
687 static void
688 handle_assignment(const struct snmp_node *node, struct asn_oid *vindex,
689     const struct snmp_value *value)
690 {
691 	u_int i;
692 	int err;
693 	struct assign *tp;
694 	char nodename[100];
695 
696 	if (node->type == SNMP_NODE_LEAF) {
697 		/* index must be one single zero or no index at all */
698 		if (vindex->len > 1 || (vindex->len == 1 &&
699 		    vindex->subs[0] != 0))
700 			report("bad index on leaf node");
701 		vindex->len = 1;
702 		vindex->subs[0] = 0;
703 	} else {
704 		/* resulting oid must not be too long */
705 		if (node->oid.len + vindex->len > ASN_MAXOIDLEN)
706 			report("resulting OID too long");
707 	}
708 
709 	/*
710 	 * Get the next assignment entry for the transaction.
711 	 */
712 	if ((tp = malloc(sizeof(*tp))) == NULL)
713 		report("%m");
714 
715 	tp->value = *value;
716 	tp->node_name = node->name;
717 
718 	/*
719 	 * Build the OID
720 	 */
721 	tp->value.var = node->oid;
722 	for (i = 0; i < vindex->len; i++)
723 		tp->value.var.subs[tp->value.var.len++] = vindex->subs[i];
724 
725 	/*
726 	 * Puzzle together the variables for the call and call the
727 	 * set routine. The set routine may make our node pointer
728 	 * invalid (if we happend to call the module loader) so
729 	 * get a copy of the node name beforehands.
730 	 */
731 	snprintf(nodename, sizeof(nodename), "%s", node->name);
732 	snmp_ctx->scratch = &tp->scratch;
733 	snmp_ctx->var_index = 0;
734 	err = (*node->op)(snmp_ctx, &tp->value, node->oid.len, node->index,
735 	    SNMP_OP_SET);
736 	if (err != 0) {
737 		free(tp);
738 		report("assignment to %s.%s returns %d", nodename,
739 		    asn_oid2str(vindex), err);
740 	}
741 
742 	TAILQ_INSERT_TAIL(&assigns, tp, link);
743 }
744 
745 
746 /*
747  * Parse the section statement
748  */
749 static void
750 parse_section(const struct lmodule *mod)
751 {
752 	if (token != TOK_STR)
753 		report("expecting section name");
754 
755 	if (strcmp(strval, "snmpd") == 0) {
756 		if (mod != NULL)
757 			/* loading a module - ignore common stuff */
758 			ignore = 1;
759 		else
760 			/* global configuration - don't ignore */
761 			ignore = 0;
762 	} else {
763 		if (mod == NULL) {
764 			/* global configuration - ignore module stuff */
765 			ignore = 1;
766 		} else {
767 			/* loading module - check if it's our section */
768 			ignore = (strcmp(strval, mod->section) != 0);
769 		}
770 	}
771 	gettoken();
772 }
773 
774 /*
775  * Convert a hostname to four u_chars
776  */
777 static void
778 gethost(const char *host, u_char *ip)
779 {
780 	struct addrinfo hints, *res;
781 	int error;
782 	struct sockaddr_in *sain;
783 
784 	memset(&hints, 0, sizeof(hints));
785 	hints.ai_family = AF_INET;
786 	hints.ai_socktype = SOCK_DGRAM;
787 	hints.ai_protocol = IPPROTO_UDP;
788 	hints.ai_flags = AI_PASSIVE;
789 	error = getaddrinfo(host, NULL, &hints, &res);
790 	if (error != 0)
791 		report("%s: %s", host, gai_strerror(error));
792 	if (res == NULL)
793 		report("%s: unknown hostname", host);
794 
795 	sain = (struct sockaddr_in *)(void *)res->ai_addr;
796 	sain->sin_addr.s_addr = ntohl(sain->sin_addr.s_addr);
797 	ip[0] = sain->sin_addr.s_addr >> 24;
798 	ip[1] = sain->sin_addr.s_addr >> 16;
799 	ip[2] = sain->sin_addr.s_addr >>  8;
800 	ip[3] = sain->sin_addr.s_addr >>  0;
801 
802 	freeaddrinfo(res);
803 }
804 
805 /*
806  * Parse the left hand side of a config line.
807  */
808 static const struct snmp_node *
809 parse_oid(const char *varname, struct asn_oid *oid)
810 {
811 	struct snmp_node *node;
812 	u_int i;
813 	u_char ip[4];
814 	struct asn_oid str_oid;
815 
816 	for (node = tree; node < &tree[tree_size]; node++)
817 		if (strcmp(varname, node->name) == 0)
818 			break;
819 	if (node == &tree[tree_size])
820 		node = NULL;
821 
822 	oid->len = 0;
823 	while (token == '.') {
824 		if (gettoken() == TOK_NUM) {
825 			if (numval > ASN_MAXID)
826 				report("subid too large %#jx",
827 				    (uintmax_t)numval);
828 			if (oid->len == ASN_MAXOIDLEN)
829 				report("index too long");
830 			if (gettoken() != ':')
831 				oid->subs[oid->len++] = numval;
832 			else {
833 				str_oid.len = 0;
834 				str_oid.subs[str_oid.len++] = numval;
835 				while (gettoken() == TOK_NUM) {
836 					str_oid.subs[str_oid.len++] = numval;
837 					if (gettoken() != ':')
838 						break;
839 				}
840 				oid->subs[oid->len++] = str_oid.len;
841 				asn_append_oid(oid, &str_oid);
842 			}
843 
844 		} else if (token == TOK_STR) {
845 			if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
846 				report("oid too long");
847 			oid->subs[oid->len++] = strvallen;
848 			for (i = 0; i < strvallen; i++)
849 				oid->subs[oid->len++] = strval[i];
850 			gettoken();
851 
852 		} else if (token == TOK_HOST) {
853 			gethost(strval, ip);
854 			if (oid->len + 4 > ASN_MAXOIDLEN)
855 				report("index too long");
856 			for (i = 0; i < 4; i++)
857 				oid->subs[oid->len++] = ip[i];
858 			gettoken();
859 		} else
860 			report("bad token in index");
861 	}
862 
863 	return (node);
864 }
865 
866 /*
867  * Parse the value for an assignment.
868  */
869 static void
870 parse_syntax_null(struct snmp_value *value __unused)
871 {
872 	if (token != TOK_EOL)
873 		report("bad NULL syntax");
874 }
875 
876 static void
877 parse_syntax_integer(struct snmp_value *value)
878 {
879 	if (token != TOK_NUM)
880 		report("bad INTEGER syntax");
881 	if (numval > 0x7fffffff)
882 		report("INTEGER too large %ju", (uintmax_t)numval);
883 
884 	value->v.integer = numval;
885 	gettoken();
886 }
887 
888 static void
889 parse_syntax_counter64(struct snmp_value *value)
890 {
891 	if (token != TOK_NUM)
892 		report("bad COUNTER64 syntax");
893 
894 	value->v.counter64 = numval;
895 	gettoken();
896 }
897 
898 static void
899 parse_syntax_octetstring(struct snmp_value *value)
900 {
901 	u_long alloc;
902 	u_char *noct;
903 
904 	if (token == TOK_STR) {
905 		value->v.octetstring.len = strvallen;
906 		value->v.octetstring.octets = malloc(strvallen);
907 		(void)memcpy(value->v.octetstring.octets, strval, strvallen);
908 		gettoken();
909 		return;
910 	}
911 
912 	/* XX:XX:XX syntax */
913 	value->v.octetstring.octets = NULL;
914 	value->v.octetstring.len = 0;
915 
916 	if (token != TOK_NUM)
917 		/* empty string is allowed */
918 		return;
919 
920 	if (ERRPUSH()) {
921 		free(value->v.octetstring.octets);
922 		ERRNEXT();
923 	}
924 
925 	alloc = 0;
926 	for (;;) {
927 		if (token != TOK_NUM)
928 			report("bad OCTETSTRING syntax");
929 		if (numval > 0xff)
930 			report("byte value too large");
931 		if (alloc == value->v.octetstring.len) {
932 			alloc += 100;
933 			noct = realloc(value->v.octetstring.octets, alloc);
934 			if (noct == NULL)
935 				report("%m");
936 			value->v.octetstring.octets = noct;
937 		}
938 		value->v.octetstring.octets[value->v.octetstring.len++]
939 		    = numval;
940 		if (gettoken() != ':')
941 			break;
942 		gettoken();
943 	}
944 	ERRPOP();
945 }
946 
947 static void
948 parse_syntax_oid(struct snmp_value *value)
949 {
950 	value->v.oid.len = 0;
951 
952 	if (token != TOK_NUM)
953 		return;
954 
955 	for (;;) {
956 		if (token != TOK_NUM)
957 			report("bad OID syntax");
958 		if (numval > ASN_MAXID)
959 			report("subid too large");
960 		if (value->v.oid.len == ASN_MAXOIDLEN)
961 			report("OID too long");
962 		value->v.oid.subs[value->v.oid.len++] = numval;
963 		if (gettoken() != '.')
964 			break;
965 		gettoken();
966 	}
967 }
968 
969 static void
970 parse_syntax_ipaddress(struct snmp_value *value)
971 {
972 	int i;
973 	u_char ip[4];
974 
975 	if (token == TOK_NUM) {
976 		/* numerical address */
977 		i = 0;
978 		for (;;) {
979 			if (numval >= 256)
980 				report("ip address part too large");
981 			value->v.ipaddress[i++] = numval;
982 			if (i == 4)
983 				break;
984 			if (gettoken() != '.')
985 				report("expecting '.' in ip address");
986 		}
987 		gettoken();
988 
989 	} else if (token == TOK_HOST) {
990 		/* host name */
991 		gethost(strval, ip);
992 		for (i = 0; i < 4; i++)
993 			value->v.ipaddress[i] = ip[i];
994 		gettoken();
995 
996 	} else
997 		report("bad ip address syntax");
998 }
999 
1000 static void
1001 parse_syntax_uint32(struct snmp_value *value)
1002 {
1003 
1004 	if (token != TOK_NUM)
1005 		report("bad number syntax");
1006 	if (numval > 0xffffffff)
1007 		report("number too large");
1008 	value->v.uint32 = numval;
1009 	gettoken();
1010 }
1011 
1012 /*
1013  * Parse an assignement line
1014  */
1015 static void
1016 parse_assign(const char *varname)
1017 {
1018 	struct snmp_value value;
1019 	struct asn_oid vindex;
1020 	const struct snmp_node *node;
1021 
1022 	node = parse_oid(varname, &vindex);
1023 	if (token != '=')
1024 		report("'=' expected, got '%c'", token);
1025 	gettoken();
1026 
1027 	if (ignore) {
1028 		/* skip rest of line */
1029 		while (token != TOK_EOL && token != TOK_EOF)
1030 			gettoken();
1031 		return;
1032 	}
1033 	if (node == NULL)
1034 		report("unknown variable");
1035 
1036 	switch (value.syntax = node->syntax) {
1037 
1038 	  case SNMP_SYNTAX_NULL:
1039 		parse_syntax_null(&value);
1040 		break;
1041 
1042 	  case SNMP_SYNTAX_INTEGER:
1043 		parse_syntax_integer(&value);
1044 		break;
1045 
1046 	  case SNMP_SYNTAX_COUNTER64:
1047 		parse_syntax_counter64(&value);
1048 		break;
1049 
1050 	  case SNMP_SYNTAX_OCTETSTRING:
1051 		parse_syntax_octetstring(&value);
1052 		break;
1053 
1054 	  case SNMP_SYNTAX_OID:
1055 		parse_syntax_oid(&value);
1056 		break;
1057 
1058 	  case SNMP_SYNTAX_IPADDRESS:
1059 		parse_syntax_ipaddress(&value);
1060 		break;
1061 
1062 	  case SNMP_SYNTAX_COUNTER:
1063 	  case SNMP_SYNTAX_GAUGE:
1064 	  case SNMP_SYNTAX_TIMETICKS:
1065 		parse_syntax_uint32(&value);
1066 		break;
1067 
1068 	  case SNMP_SYNTAX_NOSUCHOBJECT:
1069 	  case SNMP_SYNTAX_NOSUCHINSTANCE:
1070 	  case SNMP_SYNTAX_ENDOFMIBVIEW:
1071 		abort();
1072 	}
1073 
1074 	if (ERRPUSH()) {
1075 		snmp_value_free(&value);
1076 		ERRNEXT();
1077 	}
1078 
1079 	handle_assignment(node, &vindex, &value);
1080 
1081 	ERRPOP();
1082 }
1083 
1084 /*
1085  * Handle macro definition line
1086  * We have already seen the := and the input now stands at the character
1087  * after the =. Skip whitespace and then call the input routine directly to
1088  * eat up characters.
1089  */
1090 static void
1091 parse_define(const char *varname)
1092 {
1093 	char *volatile string;
1094 	char *new;
1095 	volatile size_t alloc, length;
1096 	int c;
1097 	struct macro *m;
1098 	int t = token;
1099 
1100 	alloc = 100;
1101 	length = 0;
1102 	if ((string = malloc(alloc)) == NULL)
1103 		report("%m");
1104 
1105 	if (ERRPUSH()) {
1106 		free(string);
1107 		ERRNEXT();
1108 	}
1109 
1110 	while ((c = input_getc_plain()) != EOF) {
1111 		if (c == '\n' || !isspace(c))
1112 			break;
1113 	}
1114 
1115 	while (c != EOF && c != '#' && c != '\n') {
1116 		if (alloc == length) {
1117 			alloc *= 2;
1118 			if ((new = realloc(string, alloc)) == NULL)
1119 				report("%m");
1120 			string = new;
1121 		}
1122 		string[length++] = c;
1123 		c = input_getc_plain();
1124 	}
1125 	if (c == '#') {
1126 		while ((c = input_getc_plain()) != EOF && c != '\n')
1127 			;
1128 	}
1129 	if (c == EOF)
1130 		report("EOF in macro definition");
1131 
1132 	LIST_FOREACH(m, &macros, link)
1133 		if (strcmp(m->name, varname) == 0)
1134 			break;
1135 
1136 	if (m == NULL) {
1137 		if ((m = malloc(sizeof(*m))) == NULL)
1138 			report("%m");
1139 		if ((m->name = malloc(strlen(varname) + 1)) == NULL) {
1140 			free(m);
1141 			report("%m");
1142 		}
1143 		strcpy(m->name, varname);
1144 		m->perm = 0;
1145 		LIST_INSERT_HEAD(&macros, m, link);
1146 
1147 		m->value = string;
1148 		m->length = length;
1149 	} else {
1150 		if (t == TOK_ASSIGN) {
1151 			free(m->value);
1152 			m->value = string;
1153 			m->length = length;
1154 		} else
1155 			free(string);
1156 	}
1157 
1158 	token = TOK_EOL;
1159 
1160 	ERRPOP();
1161 }
1162 
1163 /*
1164  * Free all macros
1165  */
1166 static void
1167 macro_free_all(void)
1168 {
1169 	static struct macro *m, *m1;
1170 
1171 	m = LIST_FIRST(&macros);
1172 	while (m != NULL) {
1173 		m1 = LIST_NEXT(m, link);
1174 		if (!m->perm) {
1175 			free(m->name);
1176 			free(m->value);
1177 			LIST_REMOVE(m, link);
1178 			free(m);
1179 		}
1180 		m = m1;
1181 	}
1182 }
1183 
1184 /*
1185  * Parse an include directive and switch to the new file
1186  */
1187 static void
1188 parse_include(void)
1189 {
1190 	int sysdir = 0;
1191 	char fname[_POSIX2_LINE_MAX];
1192 
1193 	if (gettoken() == '<') {
1194 		sysdir = 1;
1195 		if (gettoken() != TOK_STR)
1196 			report("expecting filename after in .include");
1197 	} else if (token != TOK_STR)
1198 		report("expecting filename after in .include");
1199 
1200 	strcpy(fname, strval);
1201 	if (sysdir && gettoken() != '>')
1202 		report("expecting '>'");
1203 	gettoken();
1204 	if (input_open_file(fname, sysdir) == -1)
1205 		report("%s: %m", fname);
1206 }
1207 
1208 /*
1209  * Parse the configuration file
1210  */
1211 static void
1212 parse_file(const struct lmodule *mod)
1213 {
1214 	char varname[_POSIX2_LINE_MAX];
1215 
1216 	while (gettoken() != TOK_EOF) {
1217 		if (token == TOK_EOL)
1218 			/* empty line */
1219 			continue;
1220 		if (token == '%') {
1221 			gettoken();
1222 			parse_section(mod);
1223 		} else if (token == '.') {
1224 			if (gettoken() != TOK_STR)
1225 				report("keyword expected after '.'");
1226 			if (strcmp(strval, "include") == 0)
1227 				parse_include();
1228 			else
1229 				report("unknown keyword '%s'", strval);
1230 		} else if (token == TOK_STR) {
1231 			strcpy(varname, strval);
1232 			if (gettoken() == TOK_ASSIGN || token == TOK_QASSIGN)
1233 				parse_define(varname);
1234 			else
1235 				parse_assign(varname);
1236 		}
1237 		if (token != TOK_EOL)
1238 			report("eol expected");
1239 	}
1240 }
1241 
1242 /*
1243  * Do rollback on errors
1244  */
1245 static void
1246 do_rollback(void)
1247 {
1248 	struct assign *tp;
1249 	struct snmp_node *node;
1250 
1251 	while ((tp = TAILQ_LAST(&assigns, assigns)) != NULL) {
1252 		TAILQ_REMOVE(&assigns, tp, link);
1253 		for (node = tree; node < &tree[tree_size]; node++)
1254 			if (node->name == tp->node_name) {
1255 				snmp_ctx->scratch = &tp->scratch;
1256 				(void)(*node->op)(snmp_ctx, &tp->value,
1257 				    node->oid.len, node->index,
1258 				    SNMP_OP_ROLLBACK);
1259 				break;
1260 			}
1261 		if (node == &tree[tree_size])
1262 			syslog(LOG_ERR, "failed to find node for "
1263 			    "rollback");
1264 		snmp_value_free(&tp->value);
1265 		free(tp);
1266 	}
1267 }
1268 
1269 /*
1270  * Do commit
1271  */
1272 static void
1273 do_commit(void)
1274 {
1275 	struct assign *tp;
1276 	struct snmp_node *node;
1277 
1278 	while ((tp = TAILQ_FIRST(&assigns)) != NULL) {
1279 		TAILQ_REMOVE(&assigns, tp, link);
1280 		for (node = tree; node < &tree[tree_size]; node++)
1281 			if (node->name == tp->node_name) {
1282 				snmp_ctx->scratch = &tp->scratch;
1283 				(void)(*node->op)(snmp_ctx, &tp->value,
1284 				    node->oid.len, node->index, SNMP_OP_COMMIT);
1285 				break;
1286 			}
1287 		if (node == &tree[tree_size])
1288 			syslog(LOG_ERR, "failed to find node for commit");
1289 		snmp_value_free(&tp->value);
1290 		free(tp);
1291 	}
1292 }
1293 
1294 /*
1295  * Read the configuration file. Handle the entire file as one transaction.
1296  *
1297  * If lodmod is NULL, the sections for 'snmpd' and all loaded modules are
1298  * executed. If it is not NULL, only the sections for that module are handled.
1299  */
1300 int
1301 read_config(const char *fname, struct lmodule *lodmod)
1302 {
1303 	int err;
1304 	char objbuf[ASN_OIDSTRLEN];
1305 	char idxbuf[ASN_OIDSTRLEN];
1306 
1307 	ignore = 0;
1308 
1309 	input_push = 0;
1310 
1311 	if (ERRPUSH())
1312 		return (-1);
1313 	if (input_open_file(fname, 0) == -1) {
1314 		syslog(LOG_ERR, "%s: %m", fname);
1315 		return (-1);
1316 	}
1317 	ERRPOP();
1318 	community = COMM_INITIALIZE;
1319 
1320 	if ((snmp_ctx = snmp_init_context()) == NULL) {
1321 		input_close_all();
1322 		syslog(LOG_ERR, "%m");
1323 		return (-1);
1324 	}
1325 
1326 	if (ERRPUSH()) {
1327 		do_rollback();
1328 		input_close_all();
1329 		macro_free_all();
1330 		free(snmp_ctx);
1331 		return (-1);
1332 	}
1333 	parse_file(lodmod);
1334 	ERRPOP();
1335 
1336 	if ((err = snmp_dep_commit(snmp_ctx)) != SNMP_ERR_NOERROR) {
1337 		syslog(LOG_ERR, "init dep failed: %u %s %s", err,
1338 		    asn_oid2str_r(&snmp_ctx->dep->obj, objbuf),
1339 		    asn_oid2str_r(&snmp_ctx->dep->idx, idxbuf));
1340 		snmp_dep_rollback(snmp_ctx);
1341 		do_rollback();
1342 		input_close_all();
1343 		macro_free_all();
1344 		free(snmp_ctx);
1345 		return (-1);
1346 	}
1347 
1348 	do_commit();
1349 	snmp_dep_finish(snmp_ctx);
1350 
1351 	macro_free_all();
1352 
1353 	free(snmp_ctx);
1354 
1355 	return (0);
1356 }
1357 
1358 /*
1359  * Define a permanent macro
1360  */
1361 int
1362 define_macro(const char *name, const char *value)
1363 {
1364 	struct macro *m;
1365 
1366 	if ((m = malloc(sizeof(*m))) == NULL)
1367 		return (-1);
1368 	if ((m->name = malloc(strlen(name) + 1)) == NULL) {
1369 		free(m);
1370 		return (-1);
1371 	}
1372 	strcpy(m->name, name);
1373 	if ((m->value = malloc(strlen(value) + 1)) == NULL) {
1374 		free(m->name);
1375 		free(m);
1376 		return (-1);
1377 	}
1378 	strcpy(m->value, value);
1379 	m->length = strlen(value);
1380 	m->perm = 1;
1381 	LIST_INSERT_HEAD(&macros, m, link);
1382 	return (0);
1383 }
1384