]> git.ipfire.org Git - thirdparty/squid.git/blob - lib/snmplib/parse.c
SourceFormat Enforcement
[thirdparty/squid.git] / lib / snmplib / parse.c
1 /*
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /***********************************************************
10 Copyright 1989 by Carnegie Mellon University
11
12 All Rights Reserved
13
14 Permission to use, copy, modify, and distribute this software and its
15 documentation for any purpose and without fee is hereby granted,
16 provided that the above copyright notice appear in all copies and that
17 both that copyright notice and this permission notice appear in
18 supporting documentation, and that the name of CMU not be
19 used in advertising or publicity pertaining to distribution of the
20 software without specific, written prior permission.
21
22 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
24 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
25 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
26 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
27 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 SOFTWARE.
29 ******************************************************************/
30
31 #include "squid.h"
32 #include "asn1.h"
33 #include "cache_snmp.h"
34 #include "parse.h"
35 #include "snmp_debug.h"
36 #include "snmp_pdu.h"
37 #include "snmp_vars.h"
38 #include "util.h"
39
40 #if HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #if HAVE_STDLIB_H
44 #include <stdlib.h>
45 #endif
46 #if HAVE_SYS_TYPES_H
47 #include <sys/types.h>
48 #endif
49 #if HAVE_CTYPE_H
50 #include <ctype.h>
51 #endif
52 #if HAVE_GNUMALLOC_H
53 #include <gnumalloc.h>
54 #elif HAVE_MALLOC_H
55 #include <malloc.h>
56 #endif
57 #if HAVE_MEMORY_H
58 #include <memory.h>
59 #endif
60 #if HAVE_STRING_H
61 #include <string.h>
62 #endif
63 #if HAVE_STRINGS_H
64 #include <strings.h>
65 #endif
66 #if HAVE_BSTRING_H
67 #include <bstring.h>
68 #endif
69 #if HAVE_SYS_SOCKET_H
70 #include <sys/socket.h>
71 #endif
72 #if HAVE_NETINET_IN_H
73 #include <netinet/in.h>
74 #endif
75 #if HAVE_ARPA_INET_H
76 #include <arpa/inet.h>
77 #endif
78 #if HAVE_SYS_TIME_H
79 #include <sys/time.h>
80 #endif
81 #if HAVE_NETDB_H
82 #include <netdb.h>
83 #endif
84 #if HAVE_ASSERT_H
85 #include <assert.h>
86 #endif
87 #if HAVE_ERRNO_H
88 #include <errno.h>
89 #endif
90
91 /*
92 * This is one element of an object identifier with either an integer subidentifier,
93 * or a textual string label, or both.
94 * The subid is -1 if not present, and label is NULL if not present.
95 */
96 struct subid {
97 int subid;
98 char *label;
99 };
100
101 /*
102 * A linked list of nodes.
103 */
104 struct node {
105 struct node *next;
106 char label[64]; /* This node's (unique) textual name */
107 u_int subid; /* This node's integer subidentifier */
108 char parent[64]; /* The parent's textual name */
109 int type; /* The type of object this represents */
110 struct enum_list *enums; /* (optional) list of enumerated integers (otherwise NULL) */
111 };
112
113 int Line = 1;
114
115 /* types of tokens */
116 #define CONTINUE -1
117 #define ENDOFFILE 0
118 #define LABEL 1
119 #define SUBTREE 2
120 #define SYNTAX 3
121 #undef OBJID
122 #define OBJID 4
123 #define OCTETSTR 5
124 #undef INTEGER
125 #define INTEGER 6
126 #define NETADDR 7
127 #define IPADDR 8
128 #define COUNTER 9
129 #define GAUGE 10
130 #define TIMETICKS 11
131 #define SNMP_OPAQUE 12
132 #define NUL 13
133 #define SEQUENCE 14
134 #define OF 15 /* SEQUENCE OF */
135 #define OBJTYPE 16
136 #define ACCESS 17
137 #define READONLY 18
138 #define READWRITE 19
139 #define WRITEONLY 20
140 #undef NOACCESS
141 #define NOACCESS 21
142 #define SNMP_STATUS 22
143 #define MANDATORY 23
144 #define SNMP_OPTIONAL 24
145 #define OBSOLETE 25
146 #define RECOMMENDED 26
147 #define PUNCT 27
148 #define EQUALS 28
149 #define NUMBER 29
150 #define LEFTBRACKET 30
151 #define RIGHTBRACKET 31
152 #define LEFTPAREN 32
153 #define RIGHTPAREN 33
154 #define COMMA 34
155 /* For SNMPv2 SMI pseudo-compliance */
156 #define DESCRIPTION 35
157 #define INDEX 36
158 #define QUOTE 37
159
160 struct tok {
161 const char *name; /* token name */
162 int len; /* length not counting nul */
163 int token; /* value */
164 int hash; /* hash of name */
165 struct tok *next; /* pointer to next in hash table */
166 };
167
168 struct tok tokens[] = {
169 {"obsolete", sizeof("obsolete") - 1, OBSOLETE},
170 {"Opaque", sizeof("Opaque") - 1, SNMP_OPAQUE},
171 {"recommended", sizeof("recommended") - 1, RECOMMENDED},
172 {"optional", sizeof("optional") - 1, SNMP_OPTIONAL},
173 {"mandatory", sizeof("mandatory") - 1, MANDATORY},
174 {"current", sizeof("current") - 1, MANDATORY},
175 {"not-accessible", sizeof("not-accessible") - 1, NOACCESS},
176 {"write-only", sizeof("write-only") - 1, WRITEONLY},
177 {"read-write", sizeof("read-write") - 1, READWRITE},
178 {"TimeTicks", sizeof("TimeTicks") - 1, TIMETICKS},
179 {"OBJECTIDENTIFIER", sizeof("OBJECTIDENTIFIER") - 1, OBJID},
180 /*
181 * This CONTINUE appends the next word onto OBJECT,
182 * hopefully matching OBJECTIDENTIFIER above.
183 */
184 {"OBJECT", sizeof("OBJECT") - 1, CONTINUE},
185 {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR},
186 {"Gauge", sizeof("Gauge") - 1, GAUGE},
187 {"OCTETSTRING", sizeof("OCTETSTRING") - 1, OCTETSTR},
188 {"OCTET", sizeof("OCTET") - 1, -1},
189 {"OF", sizeof("OF") - 1, OF},
190 {"SEQUENCE", sizeof("SEQUENCE") - 1, SEQUENCE},
191 {"NULL", sizeof("NULL") - 1, NUL},
192 {"IpAddress", sizeof("IpAddress") - 1, IPADDR},
193 {"INTEGER", sizeof("INTEGER") - 1, INTEGER},
194 {"Counter", sizeof("Counter") - 1, COUNTER},
195 {"read-only", sizeof("read-only") - 1, READONLY},
196 {"ACCESS", sizeof("ACCESS") - 1, ACCESS},
197 {"MAX-ACCESS", sizeof("MAX-ACCESS") - 1, ACCESS},
198 {"STATUS", sizeof("STATUS") - 1, SNMP_STATUS},
199 {"SYNTAX", sizeof("SYNTAX") - 1, SYNTAX},
200 {"OBJECT-TYPE", sizeof("OBJECT-TYPE") - 1, OBJTYPE},
201 {"{", sizeof("{") - 1, LEFTBRACKET},
202 {"}", sizeof("}") - 1, RIGHTBRACKET},
203 {"::=", sizeof("::=") - 1, EQUALS},
204 {"(", sizeof("(") - 1, LEFTPAREN},
205 {")", sizeof(")") - 1, RIGHTPAREN},
206 {",", sizeof(",") - 1, COMMA},
207 {"DESCRIPTION", sizeof("DESCRIPTION") - 1, DESCRIPTION},
208 {"INDEX", sizeof("INDEX") - 1, INDEX},
209 {"\"", sizeof("\"") - 1, QUOTE},
210 {"END", sizeof("END") - 1, ENDOFFILE},
211 /* Hacks for easier MIBFILE coercing */
212 {"read-create", sizeof("read-create") - 1, READWRITE},
213 {NULL}
214 };
215
216 #define HASHSIZE 32
217 #define BUCKET(x) (x & 0x01F)
218
219 static struct tok *buckets[HASHSIZE];
220
221 static void
222 hash_init(void)
223 {
224 register struct tok *tp;
225 register const char *cp;
226 register int h;
227 register int b;
228
229 memset((char *) buckets, '\0', sizeof(buckets));
230 for (tp = tokens; tp->name; tp++) {
231 for (h = 0, cp = tp->name; *cp; cp++)
232 h += *cp;
233 tp->hash = h;
234 b = BUCKET(h);
235 if (buckets[b])
236 tp->next = buckets[b]; /* BUG ??? */
237 buckets[b] = tp;
238 }
239 }
240
241 #define NHASHSIZE 128
242 #define NBUCKET(x) (x & 0x7F)
243 struct node *nbuckets[NHASHSIZE];
244
245 static void
246 init_node_hash(struct node *nodes)
247 {
248 register struct node *np, *nextp;
249 register char *cp;
250 register int hash;
251
252 memset((char *) nbuckets, '\0', sizeof(nbuckets));
253 for (np = nodes; np;) {
254 nextp = np->next;
255 hash = 0;
256 for (cp = np->parent; *cp; cp++)
257 hash += *cp;
258 np->next = nbuckets[NBUCKET(hash)];
259 nbuckets[NBUCKET(hash)] = np;
260 np = nextp;
261 }
262 }
263
264 static void
265 print_error(const char *string, const char *token, int type)
266 {
267 assert(string != NULL);
268 if (type == ENDOFFILE)
269 snmplib_debug(0, "%s(EOF): On or around line %d\n", string, Line);
270 else if (token)
271 snmplib_debug(0, "%s(%s): On or around line %d\n", string, token, Line);
272 else
273 snmplib_debug(0, "%s: On or around line %d\n", string, Line);
274 }
275
276 #if TEST
277 print_subtree(tree, count)
278 struct snmp_mib_tree *tree;
279 int count;
280 {
281 struct snmp_mib_tree *tp;
282 int i;
283
284 for (i = 0; i < count; i++)
285 printf(" ");
286 printf("Children of %s:\n", tree->label);
287 count++;
288 for (tp = tree->child_list; tp; tp = tp->next_peer) {
289 for (i = 0; i < count; i++)
290 printf(" ");
291 printf("%s\n", tp->label);
292 }
293 for (tp = tree->child_list; tp; tp = tp->next_peer) {
294 print_subtree(tp, count);
295 }
296 }
297 #endif /* TEST */
298
299 int translation_table[40];
300
301 static void
302 build_translation_table(void)
303 {
304 int count;
305
306 for (count = 0; count < 40; count++) {
307 switch (count) {
308 case OBJID:
309 translation_table[count] = TYPE_OBJID;
310 break;
311 case OCTETSTR:
312 translation_table[count] = TYPE_OCTETSTR;
313 break;
314 case INTEGER:
315 translation_table[count] = TYPE_INTEGER;
316 break;
317 case NETADDR:
318 translation_table[count] = TYPE_IPADDR;
319 break;
320 case IPADDR:
321 translation_table[count] = TYPE_IPADDR;
322 break;
323 case COUNTER:
324 translation_table[count] = TYPE_COUNTER;
325 break;
326 case GAUGE:
327 translation_table[count] = TYPE_GAUGE;
328 break;
329 case TIMETICKS:
330 translation_table[count] = TYPE_TIMETICKS;
331 break;
332 case SNMP_OPAQUE:
333 translation_table[count] = TYPE_OPAQUE;
334 break;
335 case NUL:
336 translation_table[count] = TYPE_NULL;
337 break;
338 default:
339 translation_table[count] = TYPE_OTHER;
340 break;
341 }
342 }
343 }
344
345 /*
346 * Find all the children of root in the list of nodes. Link them into the
347 * tree and out of the nodes list.
348 */
349 static void
350 do_subtree(struct snmp_mib_tree *root, struct node **nodes)
351 {
352 register struct snmp_mib_tree *tp;
353 struct snmp_mib_tree *peer = NULL;
354 register struct node *np = NULL, **headp = NULL;
355 struct node *oldnp = NULL, *child_list = NULL, *childp = NULL;
356 char *cp;
357 int hash;
358
359 tp = root;
360 hash = 0;
361 for (cp = tp->label; *cp; cp++)
362 hash += *cp;
363 headp = &nbuckets[NBUCKET(hash)];
364 /*
365 * Search each of the nodes for one whose parent is root, and
366 * move each into a separate list.
367 */
368 for (np = *headp; np; np = np->next) {
369 if ((*tp->label != *np->parent) || strcmp(tp->label, np->parent)) {
370 if ((*tp->label == *np->label) && !strcmp(tp->label, np->label)) {
371 /* if there is another node with the same label, assume that
372 * any children after this point in the list belong to the other node.
373 * This adds some scoping to the table and allows vendors to
374 * reuse names such as "ip".
375 */
376 break;
377 }
378 oldnp = np;
379 } else {
380 if (child_list == NULL) {
381 child_list = childp = np; /* first entry in child list */
382 } else {
383 childp->next = np;
384 childp = np;
385 }
386 /* take this node out of the node list */
387 if (oldnp == NULL) {
388 *headp = np->next; /* fix root of node list */
389 } else {
390 oldnp->next = np->next; /* link around this node */
391 }
392 }
393 }
394 if (childp)
395 childp->next = 0; /* re-terminate list */
396 /*
397 * Take each element in the child list and place it into the tree.
398 */
399 for (np = child_list; np; np = np->next) {
400 tp = (struct snmp_mib_tree *) xmalloc(sizeof(struct snmp_mib_tree));
401 tp->parent = root;
402 tp->next_peer = NULL;
403 tp->child_list = NULL;
404 strcpy(tp->label, np->label);
405 tp->subid = np->subid;
406 tp->type = translation_table[np->type];
407 tp->enums = np->enums;
408 np->enums = NULL; /* so we don't free them later */
409 if (root->child_list == NULL) {
410 root->child_list = tp;
411 } else if (peer) {
412 peer->next_peer = tp;
413 }
414 peer = tp;
415 /* if (tp->type == TYPE_OTHER) */
416 do_subtree(tp, nodes); /* recurse on this child if it isn't an end node */
417 }
418 /* free all nodes that were copied into tree */
419 oldnp = NULL;
420 for (np = child_list; np; np = np->next) {
421 if (oldnp)
422 xfree(oldnp);
423 oldnp = np;
424 }
425 if (oldnp)
426 xfree(oldnp);
427 }
428
429 #if !TEST
430 static
431 #endif
432 struct snmp_mib_tree *
433 build_tree(struct node *nodes) {
434 struct node *np;
435 struct snmp_mib_tree *tp;
436 int bucket, nodes_left = 0;
437
438 /* build root node */
439 tp = (struct snmp_mib_tree *) xmalloc(sizeof(struct snmp_mib_tree));
440 tp->parent = NULL;
441 tp->next_peer = NULL;
442 tp->child_list = NULL;
443 tp->enums = NULL;
444 strcpy(tp->label, "iso");
445 tp->subid = 1;
446 tp->type = 0;
447 build_translation_table();
448 /* grow tree from this root node */
449 init_node_hash(nodes);
450 /* XXX nodes isn't needed in do_subtree() ??? */
451 do_subtree(tp, &nodes);
452 #if TEST
453 print_subtree(tp, 0);
454 #endif /* TEST */
455 /* If any nodes are left, the tree is probably inconsistent */
456 for (bucket = 0; bucket < NHASHSIZE; bucket++) {
457 if (nbuckets[bucket]) {
458 nodes_left = 1;
459 break;
460 }
461 }
462 if (nodes_left) {
463 snmplib_debug(0, "The mib description doesn't seem to be consistent.\n");
464 snmplib_debug(0, "Some nodes couldn't be linked under the \"iso\" tree.\n");
465 snmplib_debug(0, "these nodes are left:\n");
466 for (bucket = 0; bucket < NHASHSIZE; bucket++) {
467 for (np = nbuckets[bucket]; np; np = np->next)
468 snmplib_debug(5, "%s ::= { %s %d } (%d)\n", np->label, np->parent, np->subid,
469 np->type);
470 }
471 }
472 return tp;
473 }
474
475 /*
476 * Parses a token from the file. The type of the token parsed is returned,
477 * and the text is placed in the string pointed to by token.
478 */
479 static char last = ' ';
480
481 static int
482 get_token(register FILE *fp, register char *token)
483 {
484 register int ch;
485 register char *cp = token;
486 register int hash = 0;
487 register struct tok *tp;
488
489 *cp = 0;
490 ch = (unsigned char)last;
491 /* skip all white space */
492 while (xisspace(ch) && ch != -1) {
493 ch = getc(fp);
494 if (ch == '\n')
495 Line++;
496 }
497 if (ch == -1)
498 return ENDOFFILE;
499
500 /*
501 * Accumulate characters until end of token is found. Then attempt to match this
502 * token as a reserved word. If a match is found, return the type. Else it is
503 * a label.
504 */
505 do {
506 if (ch == '\n')
507 Line++;
508 if (xisspace(ch) || ch == '(' || ch == ')' ||
509 ch == '{' || ch == '}' || ch == ',' ||
510 ch == '"') {
511 if (!xisspace(ch) && *token == 0) {
512 hash += ch;
513 *cp++ = ch;
514 last = ' ';
515 } else {
516 last = ch;
517 }
518 *cp = '\0';
519
520 for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
521 if ((tp->hash == hash) && (strcmp(tp->name, token) == 0))
522 break;
523 }
524 if (tp) {
525 if (tp->token == CONTINUE)
526 continue;
527 return (tp->token);
528 }
529 if (token[0] == '-' && token[1] == '-') {
530 /* strip comment */
531 while ((ch = getc(fp)) != -1)
532 if (ch == '\n') {
533 Line++;
534 break;
535 }
536 if (ch == -1)
537 return ENDOFFILE;
538 last = ch;
539 return get_token(fp, token);
540 }
541 for (cp = token; *cp; cp++)
542 if (!xisdigit(*cp))
543 return LABEL;
544 return NUMBER;
545 } else {
546 hash += ch;
547 *cp++ = ch;
548 if (ch == '\n')
549 Line++;
550 }
551
552 } while ((ch = getc(fp)) != -1);
553 return ENDOFFILE;
554 }
555
556 /*
557 * Takes a list of the form:
558 * { iso org(3) dod(6) 1 }
559 * and creates several nodes, one for each parent-child pair.
560 * Returns NULL on error.
561 * register struct subid *SubOid; an array of subids
562 * int length; the length of the array
563 */
564 static int
565 getoid(register FILE *fp, register struct subid *SubOid, int length)
566 {
567 register int count;
568 int type;
569 char token[128];
570 register char *cp;
571
572 if ((type = get_token(fp, token)) != LEFTBRACKET) {
573 print_error("Expected \"{\"", token, type);
574 return 0;
575 }
576 type = get_token(fp, token);
577 for (count = 0; count < length; count++, SubOid++) {
578 SubOid->label = 0;
579 SubOid->subid = -1;
580 if (type == RIGHTBRACKET) {
581 return count;
582 } else if (type != LABEL && type != NUMBER) {
583 print_error("Not valid for object identifier", token, type);
584 return 0;
585 }
586 if (type == LABEL) {
587 /* this entry has a label */
588 cp = (char *) xmalloc((unsigned) strlen(token) + 1);
589 strcpy(cp, token);
590 SubOid->label = cp;
591 type = get_token(fp, token);
592 if (type == LEFTPAREN) {
593 type = get_token(fp, token);
594 if (type == NUMBER) {
595 SubOid->subid = atoi(token);
596 if ((type = get_token(fp, token)) != RIGHTPAREN) {
597 print_error("Unexpected a closing parenthesis", token, type);
598 return 0;
599 }
600 } else {
601 print_error("Expected a number", token, type);
602 return 0;
603 }
604 } else {
605 continue;
606 }
607 } else {
608 /* this entry has just an integer sub-identifier */
609 SubOid->subid = atoi(token);
610 }
611 type = get_token(fp, token);
612 }
613 return count;
614
615 }
616
617 static void
618 free_node(struct node *np)
619 {
620 struct enum_list *ep, *tep;
621
622 ep = np->enums;
623 while (ep) {
624 tep = ep;
625 ep = ep->next;
626 xfree((char *) tep);
627 }
628 xfree((char *) np);
629 }
630
631 static void
632 free_node_list(struct node *nl)
633 {
634 while (nl) {
635 struct node *t = nl->next;
636 free_node(nl);
637 nl = t;
638 }
639 }
640
641 /*
642 * Parse an entry of the form:
643 * label OBJECT IDENTIFIER ::= { parent 2 }
644 * The "label OBJECT IDENTIFIER" portion has already been parsed.
645 * Returns 0 on error.
646 */
647 static struct node *
648 parse_objectid(FILE *fp, char *name) {
649 int type;
650 char token[64];
651 register int count;
652 register struct subid *op, *nop;
653 int length;
654 struct subid SubOid[32];
655 struct node *np, *root, *oldnp = NULL;
656
657 type = get_token(fp, token);
658 if (type != EQUALS) {
659 print_error("Bad format", token, type);
660 return 0;
661 }
662 if ((length = getoid(fp, SubOid, 32)) != 0) {
663 np = root = (struct node *) xmalloc(sizeof(struct node));
664 memset((char *) np, '\0', sizeof(struct node));
665 /*
666 * For each parent-child subid pair in the subid array,
667 * create a node and link it into the node list.
668 */
669 for (count = 0, op = SubOid, nop = SubOid + 1; count < (length - 2); count++,
670 op++, nop++) {
671 /* every node must have parent's name and child's name or number */
672 if (op->label && (nop->label || (nop->subid != -1))) {
673 strncpy(np->parent, op->label, sizeof(np->parent) - 1);
674 if (nop->label)
675 strncpy(np->label, nop->label, sizeof(np->label) - 1);
676 if (nop->subid != -1)
677 np->subid = nop->subid;
678 np->type = 0;
679 np->enums = 0;
680 /* set up next entry */
681 np->next = (struct node *) xmalloc(sizeof(*np->next));
682 memset((char *) np->next, '\0', sizeof(struct node));
683 oldnp = np;
684 np = np->next;
685 }
686 }
687 np->next = (struct node *) NULL;
688 /*
689 * The above loop took care of all but the last pair. This pair is taken
690 * care of here. The name for this node is taken from the label for this
691 * entry.
692 * np still points to an unused entry.
693 */
694 if (count == (length - 2)) {
695 if (op->label) {
696 strncpy(np->parent, op->label, sizeof(np->parent)-1);
697 strncpy(np->label, name, sizeof(np->label)-1);
698 if (nop->subid != -1)
699 np->subid = nop->subid;
700 else
701 print_error("Warning: This entry is pretty silly", np->label, type);
702 } else {
703 free_node(np);
704 if (oldnp)
705 oldnp->next = NULL;
706 else {
707 free_node_list(root); // we need to clear the newly allocated list
708 return NULL;
709 }
710 }
711 } else {
712 print_error("Missing end of oid", (char *) NULL, type);
713 free_node_list(root); // we need to clear the newly allocated list
714 if (oldnp)
715 oldnp->next = NULL;
716 return NULL;
717 }
718 /* free the oid array */
719 for (count = 0, op = SubOid; count < length; count++, op++) {
720 if (op->label)
721 xfree(op->label);
722 op->label = 0;
723 }
724 return root;
725 } else {
726 print_error("Bad object identifier", (char *) NULL, type);
727 return 0;
728 }
729 }
730
731 /*
732 * Parses an asn type. This structure is ignored by this parser.
733 * Returns NULL on error.
734 */
735 static int
736 parse_asntype(FILE *fp)
737 {
738 int type;
739 char token[64];
740
741 type = get_token(fp, token);
742 if (type != SEQUENCE) {
743 print_error("Not a sequence", token, type); /* should we handle this */
744 return ENDOFFILE;
745 }
746 while ((type = get_token(fp, token)) != ENDOFFILE) {
747 if (type == RIGHTBRACKET)
748 return type;
749 }
750 print_error("Expected \"}\"", token, type);
751 return ENDOFFILE;
752 }
753
754 /*
755 * Parses an OBJECT TYPE macro.
756 * Returns 0 on error.
757 */
758 static struct node *
759 parse_objecttype(register FILE *fp, char *name) {
760 register int type;
761 char token[64];
762 int count, length;
763 struct subid SubOid[32];
764 char syntax[64];
765 int nexttype;
766 char nexttoken[64];
767 register struct node *np = NULL;
768 register struct enum_list *ep = NULL;
769
770 type = get_token(fp, token);
771 if (type != SYNTAX) {
772 print_error("Bad format for OBJECT TYPE", token, type);
773 return 0;
774 }
775 np = (struct node *) xmalloc(sizeof(struct node));
776 np->next = 0;
777 np->enums = 0;
778 type = get_token(fp, token);
779 nexttype = get_token(fp, nexttoken);
780 np->type = type;
781 switch (type) {
782 case SEQUENCE:
783 strcpy(syntax, token);
784 if (nexttype == OF) {
785 strcat(syntax, " ");
786 strcat(syntax, nexttoken);
787 nexttype = get_token(fp, nexttoken);
788 strcat(syntax, " ");
789 strcat(syntax, nexttoken);
790 nexttype = get_token(fp, nexttoken);
791 }
792 break;
793 case INTEGER:
794 strcpy(syntax, token);
795 if (nexttype == LEFTBRACKET) {
796 /* if there is an enumeration list, parse it */
797 while ((type = get_token(fp, token)) != ENDOFFILE) {
798 if (type == RIGHTBRACKET)
799 break;
800 if (type == LABEL) {
801 /* this is an enumerated label */
802 if (np->enums == 0) {
803 ep = np->enums = (struct enum_list *)
804 xmalloc(sizeof(struct enum_list));
805 } else {
806 ep->next = (struct enum_list *)
807 xmalloc(sizeof(struct enum_list));
808 ep = ep->next;
809 }
810 ep->next = 0;
811 /* a reasonable approximation for the length */
812 ep->label = (char *) xmalloc((unsigned) strlen(token) + 1);
813 strcpy(ep->label, token);
814 type = get_token(fp, token);
815 if (type != LEFTPAREN) {
816 print_error("Expected \"(\"", token, type);
817 free_node(np);
818 return 0;
819 }
820 type = get_token(fp, token);
821 if (type != NUMBER) {
822 print_error("Expected integer", token, type);
823 free_node(np);
824 return 0;
825 }
826 ep->value = atoi(token);
827 type = get_token(fp, token);
828 if (type != RIGHTPAREN) {
829 print_error("Expected \")\"", token, type);
830 free_node(np);
831 return 0;
832 }
833 }
834 }
835 if (type == ENDOFFILE) {
836 print_error("Expected \"}\"", token, type);
837 free_node(np);
838 return 0;
839 }
840 nexttype = get_token(fp, nexttoken);
841 } else if (nexttype == LEFTPAREN) {
842 /* ignore the "constrained integer" for now */
843 nexttype = get_token(fp, nexttoken);
844 nexttype = get_token(fp, nexttoken);
845 nexttype = get_token(fp, nexttoken);
846 }
847 break;
848 case OBJID:
849 case OCTETSTR:
850 case NETADDR:
851 case IPADDR:
852 case COUNTER:
853 case GAUGE:
854 case TIMETICKS:
855 case SNMP_OPAQUE:
856 case NUL:
857 case LABEL:
858 strcpy(syntax, token);
859 break;
860 default:
861 print_error("Bad syntax", token, type);
862 free_node(np);
863 return 0;
864 }
865 if (nexttype != ACCESS) {
866 print_error("Should be ACCESS", nexttoken, nexttype);
867 free_node(np);
868 return 0;
869 }
870 type = get_token(fp, token);
871 if (type != READONLY && type != READWRITE && type != WRITEONLY
872 && type != NOACCESS) {
873 print_error("Bad access type", nexttoken, nexttype);
874 free_node(np);
875 return 0;
876 }
877 type = get_token(fp, token);
878 if (type != SNMP_STATUS) {
879 print_error("Should be STATUS", token, nexttype);
880 free_node(np);
881 return 0;
882 }
883 type = get_token(fp, token);
884 if (type != MANDATORY && type != SNMP_OPTIONAL && type != OBSOLETE && type != RECOMMENDED) {
885 print_error("Bad status", token, type);
886 free_node(np);
887 return 0;
888 }
889 /* Fetch next token. Either:
890 *
891 * -> EQUALS (Old MIB format)
892 * -> DESCRIPTION, INDEX (New MIB format)
893 */
894 type = get_token(fp, token);
895 if ((type != DESCRIPTION) && (type != INDEX) && (type != EQUALS)) {
896 print_error("Should be DESCRIPTION, INDEX, or EQUALS", token, nexttype);
897 free_node(np);
898 return 0;
899 }
900 if (type == DESCRIPTION) {
901
902 type = get_token(fp, token);
903 if (type != QUOTE) {
904 print_error("Should be Description open quote", token, nexttype);
905 free_node(np);
906 return 0;
907 }
908 /* Fetch description string */
909 {
910 int ReadChar;
911
912 ReadChar = last;
913 /* skip everything until closing quote */
914 while ((ReadChar != '"') && (ReadChar != -1)) {
915 ReadChar = getc(fp);
916 if (ReadChar == '\n')
917 Line++;
918 }
919 last = ' ';
920 }
921 /* ASSERT: Done with description. */
922 type = get_token(fp, token);
923 }
924 if ((type != INDEX) && (type != EQUALS)) {
925 print_error("Should be INDEX, or EQUALS", token, nexttype);
926 free_node(np);
927 return 0;
928 }
929 if (type == INDEX) {
930
931 /* Scarf INDEX */
932
933 type = get_token(fp, token);
934 if (type != LEFTBRACKET) {
935 print_error("Should be INDEX left brace", token, type);
936 free_node(np);
937 return 0;
938 }
939 /* Fetch description string */
940 {
941 int ReadChar;
942
943 ReadChar = last;
944 /* skip everything until closing quote */
945 while ((ReadChar != '}') && (ReadChar != -1)) {
946 ReadChar = getc(fp);
947 if (ReadChar == '\n')
948 Line++;
949 }
950 last = ' ';
951 }
952 /* ASSERT: Done with INDEX. */
953 type = get_token(fp, token);
954 }
955 if (type != EQUALS) {
956 print_error("Bad format", token, type);
957 free_node(np);
958 return 0;
959 }
960 length = getoid(fp, SubOid, 32);
961 if (length > 1 && length <= 32) {
962 /* just take the last pair in the oid list */
963 if (SubOid[length - 2].label) {
964 strncpy(np->parent, SubOid[length - 2].label, 64);
965 np->parent[63] = '\0';
966 }
967 strncpy(np->label, name, sizeof(np->label));
968 np->label[sizeof(np->label) - 1] = '\0';
969 if (SubOid[length - 1].subid != -1)
970 np->subid = SubOid[length - 1].subid;
971 else
972 print_error("Warning: This entry is pretty silly", np->label, type);
973 } else {
974 print_error("No end to oid", (char *) NULL, type);
975 free_node(np);
976 np = 0;
977 }
978 /* free oid array */
979 for (count = 0; count < length; count++) {
980 if (SubOid[count].label)
981 xfree(SubOid[count].label);
982 SubOid[count].label = 0;
983 }
984 return np;
985 }
986
987 /*
988 * Parses a mib file and returns a linked list of nodes found in the file.
989 * Returns NULL on error.
990 */
991 #if !TEST
992 static
993 #endif
994 struct node *
995 parse(FILE *fp) {
996 char token[64];
997 char name[64];
998 int type = 1;
999 struct node *np = NULL, *root = NULL;
1000
1001 hash_init();
1002
1003 while (type != ENDOFFILE) {
1004 type = get_token(fp, token);
1005 if (type != LABEL) {
1006 if (type == ENDOFFILE) {
1007 return root;
1008 }
1009 print_error(token, "is a reserved word", type);
1010 free_node_list(root);
1011 return NULL;
1012 }
1013 strncpy(name, token, 64);
1014 name[63] = '\0';
1015 type = get_token(fp, token);
1016 if (type == OBJTYPE) {
1017 if (root == NULL) {
1018 /* first link in chain */
1019 np = root = parse_objecttype(fp, name);
1020 if (np == NULL) {
1021 print_error("Bad parse of object type", (char *) NULL, type);
1022 return NULL;
1023 }
1024 } else {
1025 np->next = parse_objecttype(fp, name);
1026 if (np->next == NULL) {
1027 print_error("Bad parse of objecttype", (char *) NULL, type);
1028 free_node_list(root);
1029 return NULL;
1030 }
1031 }
1032 /* now find end of chain */
1033 while (np->next)
1034 np = np->next;
1035 } else if (type == OBJID) {
1036 if (root == NULL) {
1037 /* first link in chain */
1038 np = root = parse_objectid(fp, name);
1039 if (np == NULL) {
1040 print_error("Bad parse of object id", (char *) NULL, type);
1041 return NULL;
1042 }
1043 } else {
1044 np->next = parse_objectid(fp, name);
1045 if (np->next == NULL) {
1046 print_error("Bad parse of object type", (char *) NULL, type);
1047 free_node_list(root);
1048 return NULL;
1049 }
1050 }
1051 /* now find end of chain */
1052 while (np->next)
1053 np = np->next;
1054 } else if (type == EQUALS) {
1055 type = parse_asntype(fp);
1056 } else if (type == ENDOFFILE) {
1057 break;
1058 } else {
1059 print_error("Bad operator", (char *) NULL, type);
1060 free_node_list(root);
1061 return NULL;
1062 }
1063 }
1064 #if TEST
1065 {
1066 struct enum_list *ep;
1067
1068 for (np = root; np; np = np->next) {
1069 printf("%s ::= { %s %d } (%d)\n", np->label, np->parent, np->subid,
1070 np->type);
1071 if (np->enums) {
1072 printf("Enums: \n");
1073 for (ep = np->enums; ep; ep = ep->next) {
1074 printf("%s(%d)\n", ep->label, ep->value);
1075 }
1076 }
1077 }
1078 }
1079 #endif /* TEST */
1080 return root;
1081 }
1082
1083 struct snmp_mib_tree *
1084 read_mib(char *filename) {
1085 FILE *fp;
1086 struct node *nodes;
1087 struct snmp_mib_tree *tree;
1088 char mbuf[256];
1089 char *p;
1090
1091 fp = fopen(filename, "r");
1092 if (fp == NULL) {
1093 snmplib_debug(1, "init_mib: %s: %s\n", filename, xstrerror());
1094 return (NULL);
1095 }
1096 mbuf[0] = '\0';
1097 while ((p = fgets(mbuf, 256, fp)) && strncmp(mbuf, "DUMMY",
1098 strlen("DUMMY")));
1099 if (!p) {
1100 snmplib_debug(0, "Bad MIB version or tag missing, install original!\n");
1101 fclose(fp);
1102 return NULL;
1103 }
1104 if (!strcmp(mbuf, "DUMMY")) {
1105 snmplib_debug(0, "You need to update your MIB!\n");
1106 fclose(fp);
1107 return NULL;
1108 }
1109 nodes = parse(fp);
1110 fclose(fp);
1111 if (!nodes) {
1112 snmplib_debug(0, "Mib table is bad. Exiting\n");
1113 return NULL;
1114 }
1115 tree = build_tree(nodes);
1116 return (tree);
1117 }
1118