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