1 /*-
2  * Copyright (c) 2006 The FreeBSD Project
3  * All rights reserved.
4  *
5  * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6  *
7  * Redistribution of this software and documentation and use in source and
8  * binary forms, with or without modification, are permitted provided that
9  * the following conditions are met:
10  *
11  * 1. Redistributions of source code or documentation must retain the above
12  *    copyright 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 THE 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 THE 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 
30 /*
31  * Read file containing table description - reuse magic from gensnmptree.c.
32  * Hopefully one day most of the code here will be part of libbsnmp and
33  * this duplication won't be necessary.
34  *
35  * Syntax is:
36  * ---------
37  * file := top | top file
38  *
39  * top := tree | typedef | include
40  *
41  * tree := head elements ')'
42  *
43  * entry := head ':' index STRING elements ')'
44  *
45  * leaf := head type STRING ACCESS ')'
46  *
47  * column := head type ACCESS ')'
48  *
49  * type := BASETYPE | BASETYPE '|' subtype | enum | bits
50  *
51  * subtype := STRING
52  *
53  * enum := ENUM '(' value ')'
54  *
55  * bits := BITS '(' value ')'
56  *
57  * value := INT STRING | INT STRING value
58  *
59  * head := '(' INT STRING
60  *
61  * elements := EMPTY | elements element
62  *
63  * element := tree | leaf | column
64  *
65  * index := type | index type
66  *
67  * typedef := 'typedef' STRING type
68  *
69  * include := 'include' filespec
70  *
71  * filespec := '"' STRING '"' | '<' STRING '>'
72  */
73 
74 #include <sys/param.h>
75 #include <sys/queue.h>
76 #include <sys/uio.h>
77 
78 #include <ctype.h>
79 #include <err.h>
80 #include <errno.h>
81 #include <fcntl.h>
82 #include <paths.h>
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <syslog.h>
87 #include <unistd.h>
88 
89 #include <bsnmp/asn1.h>
90 #include <bsnmp/snmp.h>
91 #include <bsnmp/snmpagent.h>	/* SNMP_INDEXES_MAX */
92 #include "bsnmptc.h"
93 #include "bsnmptools.h"
94 
95 enum snmp_tbl_entry {
96 	ENTRY_NONE = 0,
97 	ENTRY_INDEX,
98 	ENTRY_DATA
99 };
100 
101 enum {
102 	FL_GET	= 0x01,
103 	FL_SET	= 0x02,
104 };
105 
106 /************************************************************
107  *
108  * Allocate memory and panic just in the case...
109  */
110 static void *
111 xalloc(size_t size)
112 {
113 	void *ptr;
114 
115 	if ((ptr = malloc(size)) == NULL)
116 		err(1, "allocing %zu bytes", size);
117 
118 	return (ptr);
119 }
120 
121 static char *
122 savestr(const char *s)
123 {
124 	if (s == NULL)
125 		return (NULL);
126 
127 	return (strcpy(xalloc(strlen(s) + 1), s));
128 }
129 
130 /************************************************************
131  *
132  * Input stack
133  */
134 struct input {
135 	FILE		*fp;
136 	uint32_t	lno;
137 	char		*fname;
138 	char		*path;
139 	LIST_ENTRY(input) link;
140 };
141 
142 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
143 static struct input *input = NULL;
144 static int32_t pbchar = -1;
145 
146 #define	MAX_PATHS	100
147 
148 static const char *paths[MAX_PATHS + 1] = {
149 	"/usr/share/snmp/defs",
150 	_PATH_LOCALBASE "/share/snmp/defs",
151 	NULL
152 };
153 
154 static void
155 input_new(FILE *fp, const char *path, const char *fname)
156 {
157 	struct input *ip;
158 
159 	ip = xalloc(sizeof(*ip));
160 	ip->fp = fp;
161 	ip->lno = 1;
162 	ip->fname = savestr(fname);
163 	ip->path = savestr(path);
164 	LIST_INSERT_HEAD(&inputs, ip, link);
165 
166 	input = ip;
167 }
168 
169 static void
170 input_close(void)
171 {
172 	if (input == NULL)
173 		return;
174 
175 	fclose(input->fp);
176 	free(input->fname);
177 	free(input->path);
178 	LIST_REMOVE(input, link);
179 	free(input);
180 
181 	input = LIST_FIRST(&inputs);
182 }
183 
184 static FILE *
185 tryopen(const char *path, const char *fname)
186 {
187 	char *fn;
188 	FILE *fp;
189 
190 	if (path == NULL)
191 		fn = savestr(fname);
192 	else {
193 		fn = xalloc(strlen(path) + strlen(fname) + 2);
194 		sprintf(fn, "%s/%s", path, fname);
195 	}
196 	fp = fopen(fn, "r");
197 	free(fn);
198 	return (fp);
199 }
200 
201 static int32_t
202 input_fopen(const char *fname)
203 {
204 	FILE *fp;
205 	u_int p;
206 
207 	if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') {
208 		if ((fp = tryopen(NULL, fname)) != NULL) {
209 			input_new(fp, NULL, fname);
210 			return (0);
211 		}
212 
213 	} else {
214 
215 		for (p = 0; paths[p] != NULL; p++)
216 			if ((fp = tryopen(paths[p], fname)) != NULL) {
217 				input_new(fp, paths[p], fname);
218 				return (0);
219 			}
220 	}
221 
222 	warnx("cannot open '%s'", fname);
223 	return (-1);
224 }
225 
226 static int32_t
227 tgetc(void)
228 {
229 	int c;
230 
231 	if (pbchar != -1) {
232 		c = pbchar;
233 		pbchar = -1;
234 		return (c);
235 	}
236 
237 	for (;;) {
238 		if (input == NULL)
239 			return (EOF);
240 
241 		if ((c = getc(input->fp)) != EOF)
242 			return (c);
243 
244 		input_close();
245 	}
246 }
247 
248 static int32_t
249 tungetc(int c)
250 {
251 
252 	if (pbchar != -1)
253 		return (-1);
254 
255 	pbchar = c;
256 	return (1);
257 }
258 
259 /************************************************************
260  *
261  * Parsing input
262  */
263 enum tok {
264 	TOK_EOF = 0200,	/* end-of-file seen */
265 	TOK_NUM,	/* number */
266 	TOK_STR,	/* string */
267 	TOK_ACCESS,	/* access operator */
268 	TOK_TYPE,	/* type operator */
269 	TOK_ENUM,	/* enum token (kind of a type) */
270 	TOK_TYPEDEF,	/* typedef directive */
271 	TOK_DEFTYPE,	/* defined type */
272 	TOK_INCLUDE,	/* include directive */
273 	TOK_FILENAME,	/* filename ("foo.bar" or <foo.bar>) */
274 	TOK_BITS,	/* bits token (kind of a type) */
275 	TOK_ERR		/* unexpected char - exit */
276 };
277 
278 static const struct {
279 	const char	*str;
280 	enum tok	tok;
281 	uint32_t	val;
282 } keywords[] = {
283 	{ "GET", TOK_ACCESS, FL_GET },
284 	{ "SET", TOK_ACCESS, FL_SET },
285 	{ "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
286 	{ "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
287 	{ "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
288 	{ "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
289 	{ "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
290 	{ "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
291 	{ "OID", TOK_TYPE, SNMP_SYNTAX_OID },
292 	{ "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
293 	{ "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
294 	{ "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
295 	{ "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
296 	{ "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },
297 	{ "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },
298 	{ "typedef", TOK_TYPEDEF, 0 },
299 	{ "include", TOK_INCLUDE, 0 },
300 	{ NULL, 0, 0 }
301 };
302 
303 static struct {
304 	/* Current OID type, regarding table membership. */
305 	enum snmp_tbl_entry	tbl_type;
306 	/* A pointer to a structure in table list to add to its members. */
307 	struct snmp_index_entry	*table_idx;
308 } table_data;
309 
310 static struct asn_oid current_oid;
311 static char nexttok[MAXSTR];
312 static u_long val;		/* integer values */
313 static int32_t	all_cond;	/* all conditions are true */
314 static int32_t saved_token = -1;
315 
316 /* Prepare the global data before parsing a new file. */
317 static void
318 snmp_import_init(struct asn_oid *append)
319 {
320 	memset(&table_data, 0, sizeof(table_data));
321 	memset(&current_oid, 0, sizeof(struct asn_oid));
322 	memset(nexttok, 0, MAXSTR);
323 
324 	if (append != NULL)
325 		asn_append_oid(&current_oid, append);
326 
327 	all_cond = 0;
328 	val = 0;
329 	saved_token = -1;
330 }
331 
332 static int32_t
333 gettoken(struct snmp_toolinfo *snmptoolctx)
334 {
335 	int c;
336 	struct enum_type *t;
337 
338 	if (saved_token != -1) {
339 		c = saved_token;
340 		saved_token = -1;
341 		return (c);
342 	}
343 
344   again:
345 	/*
346 	 * Skip any whitespace before the next token.
347 	 */
348 	while ((c = tgetc()) != EOF) {
349 		if (c == '\n')
350 			input->lno++;
351 		if (!isspace(c))
352 			break;
353 	}
354 	if (c == EOF)
355 		return (TOK_EOF);
356 
357 	if (!isascii(c)) {
358 		warnx("unexpected character %#2x", (u_int) c);
359 		return (TOK_ERR);
360 	}
361 
362 	/*
363 	 * Skip comments.
364 	 */
365 	if (c == '#') {
366 		while ((c = tgetc()) != EOF) {
367 			if (c == '\n') {
368 				input->lno++;
369 				goto again;
370 			}
371 		}
372 		warnx("unexpected EOF in comment");
373 		return (TOK_ERR);
374 	}
375 
376 	/*
377 	 * Single character tokens.
378 	 */
379 	if (strchr("():|", c) != NULL)
380 		return (c);
381 
382 	if (c == '"' || c == '<') {
383 		int32_t end = c;
384 		size_t n = 0;
385 
386 		val = 1;
387 		if (c == '<') {
388 			val = 0;
389 			end = '>';
390 		}
391 
392 		while ((c = tgetc()) != EOF) {
393 			if (c == end)
394 				break;
395 			if (n == sizeof(nexttok) - 1) {
396 				nexttok[n++] = '\0';
397 				warnx("filename too long '%s...'", nexttok);
398 				return (TOK_ERR);
399 			}
400 			nexttok[n++] = c;
401 		}
402 		nexttok[n++] = '\0';
403 		return (TOK_FILENAME);
404 	}
405 
406 	/*
407 	 * Sort out numbers.
408 	 */
409 	if (isdigit(c)) {
410 		size_t n = 0;
411 		nexttok[n++] = c;
412 		while ((c = tgetc()) != EOF) {
413 			if (!isdigit(c)) {
414 				if (tungetc(c) < 0)
415 					return (TOK_ERR);
416 				break;
417 			}
418 			if (n == sizeof(nexttok) - 1) {
419 				nexttok[n++] = '\0';
420 				warnx("number too long '%s...'", nexttok);
421 				return (TOK_ERR);
422 			}
423 			nexttok[n++] = c;
424 		}
425 		nexttok[n++] = '\0';
426 		sscanf(nexttok, "%lu", &val);
427 		return (TOK_NUM);
428 	}
429 
430 	/*
431 	 * So that has to be a string.
432 	 */
433 	if (isalpha(c) || c == '_' || c == '-') {
434 		size_t n = 0;
435 		nexttok[n++] = c;
436 		while ((c = tgetc()) != EOF) {
437 			if (!isalnum(c) && c != '_' && c != '-') {
438 				if (tungetc (c) < 0)
439 					return (TOK_ERR);
440 				break;
441 			}
442 			if (n == sizeof(nexttok) - 1) {
443 				nexttok[n++] = '\0';
444 				warnx("string too long '%s...'", nexttok);
445 				return (TOK_ERR);
446 			}
447 			nexttok[n++] = c;
448 		}
449 		nexttok[n++] = '\0';
450 
451 		/*
452 		 * Keywords.
453 		 */
454 		for (c = 0; keywords[c].str != NULL; c++)
455 			if (strcmp(keywords[c].str, nexttok) == 0) {
456 				val = keywords[c].val;
457 				return (keywords[c].tok);
458 			}
459 
460 		if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) {
461 			val = t->syntax;
462 			return (TOK_DEFTYPE);
463 		}
464 
465 		return (TOK_STR);
466 	}
467 
468 	if (isprint(c))
469 		warnx("%u: unexpected character '%c'", input->lno, c);
470 	else
471 		warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c);
472 
473 	return (TOK_ERR);
474 }
475 
476 /*
477  * Update table information.
478  */
479 static struct snmp_index_entry *
480 snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl)
481 {
482 	switch (te) {
483 		case ENTRY_NONE:
484 			if (table_data.tbl_type == ENTRY_NONE)
485 				return (NULL);
486 			if (table_data.tbl_type == ENTRY_INDEX)
487 				table_data.table_idx = NULL;
488 			table_data.tbl_type--;
489 			return (NULL);
490 
491 		case ENTRY_INDEX:
492 			if (tbl == NULL)
493 				warnx("No table_index to add!!!");
494 			table_data.table_idx = tbl;
495 			table_data.tbl_type = ENTRY_INDEX;
496 			return (tbl);
497 
498 		case ENTRY_DATA:
499 			if (table_data.tbl_type == ENTRY_INDEX) {
500 				table_data.tbl_type = ENTRY_DATA;
501 				return (table_data.table_idx);
502 			}
503 			return (NULL);
504 
505 		default:
506 			/* NOTREACHED */
507 			warnx("Unknown table entry type!!!");
508 			break;
509 	}
510 
511 	return (NULL);
512 }
513 
514 static int32_t
515 parse_enum(struct snmp_toolinfo *snmptoolctx, int32_t *tok,
516     struct enum_pairs *enums)
517 {
518 	while ((*tok = gettoken(snmptoolctx)) == TOK_STR) {
519 		if (enum_pair_insert(enums, val, nexttok) < 0)
520 			return (-1);
521 		if ((*tok = gettoken(snmptoolctx)) != TOK_NUM)
522 			break;
523 	}
524 
525 	if (*tok != ')') {
526 		warnx("')' at end of enums");
527 		return (-1);
528 	}
529 
530 	return (1);
531 }
532 
533 static int32_t
534 parse_subtype(struct snmp_toolinfo *snmptoolctx, int32_t *tok,
535     enum snmp_tc *tc)
536 {
537 	if ((*tok = gettoken(snmptoolctx)) != TOK_STR) {
538 		warnx("subtype expected after '|'");
539 		return (-1);
540 	}
541 
542 	*tc = snmp_get_tc(nexttok);
543 	*tok = gettoken(snmptoolctx);
544 
545 	return (1);
546 }
547 
548 static int32_t
549 parse_type(struct snmp_toolinfo *snmptoolctx, int32_t *tok,
550     enum snmp_tc *tc, struct enum_pairs **snmp_enum)
551 {
552 	int32_t syntax, mem;
553 
554 	syntax = val;
555 	*tc = 0;
556 
557 	if (*tok == TOK_ENUM || *tok == TOK_BITS) {
558 		if (*snmp_enum == NULL) {
559 			if ((*snmp_enum = enum_pairs_init()) == NULL)
560 				return (-1);
561 			mem = 1;
562 			*tc = SNMP_TC_OWN;
563 		} else
564 			mem = 0;
565 
566 		if (gettoken(snmptoolctx) != '(') {
567 			warnx("'(' expected after ENUM/BITS");
568 			return (-1);
569 		}
570 
571 		if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) {
572 			warnx("need value for ENUM//BITS");
573 			if (mem == 1) {
574 				free(*snmp_enum);
575 				*snmp_enum = NULL;
576 			}
577 			return (-1);
578 		}
579 
580 		if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) {
581 			enum_pairs_free(*snmp_enum);
582 			*snmp_enum = NULL;
583 			return (-1);
584 		}
585 
586 		*tok = gettoken(snmptoolctx);
587 
588 	} else if (*tok == TOK_DEFTYPE) {
589 		struct enum_type *t;
590 
591 		*tc = 0;
592 		t = snmp_enumtc_lookup(snmptoolctx, nexttok);
593 		if (t != NULL)
594 			*snmp_enum = t->snmp_enum;
595 
596 		*tok = gettoken(snmptoolctx);
597 
598 	} else {
599 		if ((*tok = gettoken(snmptoolctx)) == '|') {
600 			if (parse_subtype(snmptoolctx, tok, tc) < 0)
601 				return (-1);
602 		}
603 	}
604 
605 	return (syntax);
606 }
607 
608 static int32_t
609 snmp_import_head(struct snmp_toolinfo *snmptoolctx)
610 {
611 	enum tok tok;
612 
613 	if ((tok = gettoken(snmptoolctx)) == '(')
614 		tok = gettoken(snmptoolctx);
615 
616 	if (tok != TOK_NUM  || val > ASN_MAXID ) {
617 		warnx("Suboid expected - line %d", input->lno);
618 		return (-1);
619 	}
620 
621 	if (gettoken(snmptoolctx) != TOK_STR) {
622 		warnx("Node name expected at line %d", input->lno);
623 		return (-1);
624 	}
625 
626 	return (1);
627 }
628 
629 static int32_t
630 snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj)
631 {
632 	int32_t i, tok;
633 	enum snmp_tc tc;
634 	struct snmp_index_entry *entry;
635 
636 	if ((entry = calloc(1, sizeof(struct snmp_index_entry))) == NULL) {
637 		syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
638 		return (-1);
639 	}
640 
641 	STAILQ_INIT(&(entry->index_list));
642 
643 	for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) {
644 		int32_t syntax;
645 		struct enum_pairs *enums = NULL;
646 
647 		if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM &&
648 		    tok != TOK_BITS)
649 			break;
650 
651 		if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) {
652 			enum_pairs_free(enums);
653 			snmp_index_listfree(&(entry->index_list));
654 			free(entry);
655 			return (-1);
656 		}
657 
658 		if (snmp_syntax_insert(&(entry->index_list), enums, syntax,
659 		    tc) < 0) {
660 			snmp_index_listfree(&(entry->index_list));
661 			enum_pairs_free(enums);
662 			free(entry);
663 			return (-1);
664 		}
665 	}
666 
667 	if (i == 0 || i > SNMP_INDEXES_MAX) {
668 		warnx("Bad number of indexes at line %d", input->lno);
669 		snmp_index_listfree(&(entry->index_list));
670 		free(entry);
671 		return (-1);
672 	}
673 
674 	if (tok != TOK_STR) {
675 		warnx("String expected after indexes at line %d", input->lno);
676 		snmp_index_listfree(&(entry->index_list));
677 		free(entry);
678 		return (-1);
679 	}
680 
681 	entry->string = obj->string;
682 	entry->strlen = obj->strlen;
683 	asn_append_oid(&(entry->var), &(obj->var));
684 
685 	if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) {
686 		snmp_index_listfree(&(entry->index_list));
687 		free(entry);
688 		return (-1);
689 	} else if (i == 0) {
690 		/* Same entry already present in lists. */
691 		free(entry->string);
692 		free(entry);
693 		return (0);
694 	}
695 
696 	(void) snmp_import_update_table(ENTRY_INDEX, entry);
697 
698 	return (1);
699 }
700 
701 /*
702  * Read everything after the syntax type that is certainly a leaf OID info.
703  */
704 static int32_t
705 snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, int32_t *tok,
706     struct snmp_oid2str *oid2str)
707 {
708 	int32_t i, syntax;
709 
710 	if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum)))
711 	    < 0)
712 		return(-1);
713 
714 	oid2str->syntax = syntax;
715 	/*
716 	 * That is the name of the function, corresponding to the entry.
717 	 * It is used by bsnmpd, but is not interesting for us.
718 	 */
719 	if (*tok == TOK_STR)
720 		*tok = gettoken(snmptoolctx);
721 
722 	for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) {
723 		oid2str->access |=  (uint32_t) val;
724 		*tok = gettoken(snmptoolctx);
725 	}
726 
727 	if (*tok != ')') {
728 		warnx("')' expected at end of line %d", input->lno);
729 		return (-1);
730 	}
731 
732 	oid2str->table_idx = snmp_import_update_table(ENTRY_DATA,  NULL);
733 
734 	if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) {
735 		warnx("Error adding leaf %s to list", oid2str->string);
736 		return (-1);
737 	}
738 
739 	/*
740 	 * Same entry is already present in the mapping lists and
741 	 * the new one was not inserted.
742 	 */
743 	if (i == 0)  {
744 		free(oid2str->string);
745 		free(oid2str);
746 	}
747 
748 	(void) snmp_import_update_table(ENTRY_NONE, NULL);
749 
750 	return (1);
751 }
752 
753 static int32_t
754 snmp_import_object(struct snmp_toolinfo *snmptoolctx)
755 {
756 	char *string;
757 	int i;
758 	int32_t tok;
759 	struct snmp_oid2str *oid2str;
760 
761 	if (snmp_import_head(snmptoolctx) < 0)
762 		return (-1);
763 
764 	if ((oid2str = calloc(1, sizeof(struct snmp_oid2str))) == NULL) {
765 		syslog(LOG_ERR, "calloc() failed: %s", strerror(errno));
766 		return (-1);
767 	}
768 
769 	if ((string = strdup(nexttok)) == NULL) {
770 		syslog(LOG_ERR, "strdup() failed: %s", strerror(errno));
771 		free(oid2str);
772 		return (-1);
773 	}
774 
775 	oid2str->string = string;
776 	oid2str->strlen = strlen(nexttok);
777 
778 	asn_append_oid(&(oid2str->var), &(current_oid));
779 	if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0)
780 		goto error;
781 
782 	/*
783 	 * Prepared the entry - now figure out where to insert it.
784 	 * After the object we have following options:
785 	 * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist;
786 	 * 2) new line , ( - nonleaf oid -> snmp_nodelist;
787 	 * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more)
788 	 *     may follow and second string must end line -> snmp_tablelist;
789 	 * 3) OID , string  ) - this is a trap entry or a leaf -> snmp_oidlist;
790 	 * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last
791 	 *     and )- this is definitely a leaf.
792 	 */
793 
794 	switch (tok = gettoken(snmptoolctx)) {
795 	    case  ')':
796 		if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0)
797 			goto error;
798 		if (i == 0) {
799 			free(oid2str->string);
800 			free(oid2str);
801 		}
802 		return (1);
803 
804 	    case '(':
805 		if (snmp_suboid_append(&current_oid, (asn_subid_t) val) < 0)
806 			goto error;
807 
808 		/*
809 		 * Ignore the error for nodes since the .def files currently
810 		 * contain different strings for 1.3.6.1.2.1 - mibII. Only make
811 		 * sure the memory is freed and don't complain.
812 		 */
813 		if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) {
814 			free(string);
815 			free(oid2str);
816 		}
817 		return (snmp_import_object(snmptoolctx));
818 
819 	    case ':':
820 		if (snmp_suboid_append(&current_oid, (asn_subid_t) val) < 0)
821 			goto error;
822 		if (snmp_import_table(snmptoolctx, oid2str) < 0)
823 			goto error;
824 		/*
825 		 * A different table entry type was malloced and the data is
826 		 * contained there.
827 		 */
828 		free(oid2str);
829 		return (1);
830 
831 	    case TOK_TYPE:
832 		/* FALLTHROUGH */
833 	    case TOK_DEFTYPE:
834 		/* FALLTHROUGH */
835 	    case TOK_ENUM:
836 	    	/* FALLTHROUGH */
837 	    case TOK_BITS:
838 		if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0)
839 				goto error;
840 		return (1);
841 
842 	    default:
843 		warnx("Unexpected token at line %d - %s", input->lno,
844 		    input->fname);
845 		break;
846 	}
847 
848 error:
849 	snmp_mapping_entryfree(oid2str);
850 
851 	return (-1);
852 }
853 
854 static int32_t
855 snmp_import_tree(struct snmp_toolinfo *snmptoolctx, int32_t *tok)
856 {
857 	while (*tok != TOK_EOF) {
858 		switch (*tok) {
859 		    case TOK_ERR:
860 			return (-1);
861 		    case '(':
862 			if (snmp_import_object(snmptoolctx) < 0)
863 			    return (-1);
864 			break;
865 		    case ')':
866 			if (snmp_suboid_pop(&current_oid) < 0)
867 			    return (-1);
868 			(void) snmp_import_update_table(ENTRY_NONE, NULL);
869 			break;
870 		    default:
871 			/* Anything else here would be illegal. */
872 			return (-1);
873 		}
874 		*tok = gettoken(snmptoolctx);
875 	}
876 
877 	return (0);
878 }
879 
880 static int32_t
881 snmp_import_top(struct snmp_toolinfo *snmptoolctx, int32_t *tok)
882 {
883 	enum snmp_tc tc;
884 	struct enum_type *t;
885 
886 	if (*tok == '(')
887 		return (snmp_import_tree(snmptoolctx, tok));
888 
889 	if (*tok == TOK_TYPEDEF) {
890 		if ((*tok = gettoken(snmptoolctx)) != TOK_STR) {
891 			warnx("type name expected after typedef - %s",
892 			    input->fname);
893 			return (-1);
894 		}
895 
896 		t = snmp_enumtc_init(nexttok);
897 
898 		*tok = gettoken(snmptoolctx);
899 		t->is_enum = (*tok == TOK_ENUM);
900 		t->is_bits = (*tok == TOK_BITS);
901 		t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum));
902 		snmp_enumtc_insert(snmptoolctx, t);
903 
904 		return (1);
905 	}
906 
907 	if (*tok == TOK_INCLUDE) {
908 		int i;
909 
910 		*tok = gettoken(snmptoolctx);
911 		if (*tok != TOK_FILENAME) {
912 			warnx("filename expected in include directive - %s",
913 			    nexttok);
914 			return (-1);
915 		}
916 
917 		if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) {
918 			*tok = gettoken(snmptoolctx);
919 			return (1);
920 		}
921 
922 		if (i == -1)
923 			return (-1);
924 
925 		input_fopen(nexttok);
926 		*tok = gettoken(snmptoolctx);
927 		return (1);
928 	}
929 
930 	warnx("'(' or 'typedef' expected - %s", nexttok);
931 	return (-1);
932 }
933 
934 static int32_t
935 snmp_import(struct snmp_toolinfo *snmptoolctx)
936 {
937 	int i;
938 	int32_t tok;
939 
940 	tok = gettoken(snmptoolctx);
941 
942 	do
943 		i = snmp_import_top(snmptoolctx, &tok);
944 	while (i > 0);
945 
946 	return (i);
947 }
948 
949 /*
950  * Read a .def file and import oid<->string mapping.
951  * Mappings are inserted into a global structure containing list for each OID
952  * syntax type.
953  */
954 int32_t
955 snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file)
956 {
957 	int idx;
958 
959 	snmp_import_init(&(file->cut));
960 	input_fopen(file->name);
961 	if ((idx = snmp_import(snmptoolctx)) < 0)
962 		warnx("Failed to read mappings from file %s", file->name);
963 
964 	input_close();
965 
966 	return (idx);
967 }
968