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