/*- * Copyright (c) 2006 The FreeBSD Project * All rights reserved. * * Author: Shteryana Shopova * * Redistribution of this software and documentation and use in source and * binary forms, with or without modification, are permitted provided that * the following conditions are met: * * 1. Redistributions of source code or documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Read file containing table description - reuse magic from gensnmptree.c. * Hopefully one day most of the code here will be part of libbsnmp and * this duplication won't be necessary. * * Syntax is: * --------- * file := top | top file * * top := tree | typedef | include * * tree := head elements ')' * * entry := head ':' index STRING elements ')' * * leaf := head type STRING ACCESS ')' * * column := head type ACCESS ')' * * type := BASETYPE | BASETYPE '|' subtype | enum | bits * * subtype := STRING * * enum := ENUM '(' value ')' * * bits := BITS '(' value ')' * * value := INT STRING | INT STRING value * * head := '(' INT STRING * * elements := EMPTY | elements element * * element := tree | leaf | column * * index := type | index type * * typedef := 'typedef' STRING type * * include := 'include' filespec * * filespec := '"' STRING '"' | '<' STRING '>' */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* SNMP_INDEXES_MAX */ #include "bsnmptc.h" #include "bsnmptools.h" enum snmp_tbl_entry { ENTRY_NONE = 0, ENTRY_INDEX, ENTRY_DATA }; enum { FL_GET = 0x01, FL_SET = 0x02, }; /************************************************************ * * Allocate memory and panic just in the case... */ static void * xalloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) err(1, "allocing %zu bytes", size); return (ptr); } static char * savestr(const char *s) { if (s == NULL) return (NULL); return (strcpy(xalloc(strlen(s) + 1), s)); } /************************************************************ * * Input stack */ struct input { FILE *fp; uint32_t lno; char *fname; char *path; LIST_ENTRY(input) link; }; LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs); struct input *input = NULL; int32_t pbchar = -1; #define MAX_PATHS 100 static const char *paths[MAX_PATHS + 1] = { "/usr/share/snmp/defs", "/usr/local/share/snmp/defs", NULL }; static void input_new(FILE *fp, const char *path, const char *fname) { struct input *ip; ip = xalloc(sizeof(*ip)); ip->fp = fp; ip->lno = 1; ip->fname = savestr(fname); ip->path = savestr(path); LIST_INSERT_HEAD(&inputs, ip, link); input = ip; } static void input_close(void) { if (input == NULL) return; fclose(input->fp); free(input->fname); free(input->path); LIST_REMOVE(input, link); free(input); input = LIST_FIRST(&inputs); } static FILE * tryopen(const char *path, const char *fname) { char *fn; FILE *fp; if (path == NULL) fn = savestr(fname); else { fn = xalloc(strlen(path) + strlen(fname) + 2); sprintf(fn, "%s/%s", path, fname); } fp = fopen(fn, "r"); free(fn); return (fp); } static int32_t input_fopen(const char *fname) { FILE *fp; u_int p; if (fname[0] == '/' || fname[0] == '.' || fname[0] == '~') { if ((fp = tryopen(NULL, fname)) != NULL) { input_new(fp, NULL, fname); return (0); } } else { for (p = 0; paths[p] != NULL; p++) if ((fp = tryopen(paths[p], fname)) != NULL) { input_new(fp, paths[p], fname); return (0); } } warnx("cannot open '%s'", fname); return (-1); } static int32_t tgetc(void) { int c; if (pbchar != -1) { c = pbchar; pbchar = -1; return (c); } for (;;) { if (input == NULL) return (EOF); if ((c = getc(input->fp)) != EOF) return (c); input_close(); } } static int32_t tungetc(int c) { if (pbchar != -1) return (-1); pbchar = c; return (1); } /************************************************************ * * Parsing input */ enum tok { TOK_EOF = 0200, /* end-of-file seen */ TOK_NUM, /* number */ TOK_STR, /* string */ TOK_ACCESS, /* access operator */ TOK_TYPE, /* type operator */ TOK_ENUM, /* enum token (kind of a type) */ TOK_TYPEDEF, /* typedef directive */ TOK_DEFTYPE, /* defined type */ TOK_INCLUDE, /* include directive */ TOK_FILENAME, /* filename ("foo.bar" or ) */ TOK_BITS, /* bits token (kind of a type) */ TOK_ERR /* unexpected char - exit */ }; static const struct { const char *str; enum tok tok; uint32_t val; } keywords[] = { { "GET", TOK_ACCESS, FL_GET }, { "SET", TOK_ACCESS, FL_SET }, { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL }, { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER }, { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER }, { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE }, { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING }, { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS }, { "OID", TOK_TYPE, SNMP_SYNTAX_OID }, { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS }, { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER }, { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE }, { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 }, { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER }, { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING }, { "typedef", TOK_TYPEDEF, 0 }, { "include", TOK_INCLUDE, 0 }, { NULL, 0, 0 } }; struct { /* Current OID type, regarding table membership. */ enum snmp_tbl_entry tbl_type; /* A pointer to a structure in table list to add to its members. */ struct snmp_index_entry *table_idx; } table_data; struct asn_oid current_oid; char nexttok[MAXSTR]; u_long val; /* integer values */ int32_t all_cond; /* all conditions are true */ int32_t saved_token = -1; /* Prepare the global data before parsing a new file. */ static void snmp_import_init(struct asn_oid *append) { memset(&table_data, 0, sizeof(table_data)); memset(¤t_oid, 0, sizeof(struct asn_oid)); memset(nexttok, 0, MAXSTR); if (append != NULL) asn_append_oid(¤t_oid, append); all_cond = 0; val = 0; saved_token = -1; } static int32_t gettoken(struct snmp_toolinfo *snmptoolctx) { int c; struct enum_type *t; if (saved_token != -1) { c = saved_token; saved_token = -1; return (c); } again: /* * Skip any whitespace before the next token. */ while ((c = tgetc()) != EOF) { if (c == '\n') input->lno++; if (!isspace(c)) break; } if (c == EOF) return (TOK_EOF); if (!isascii(c)) { warnx("unexpected character %#2x", (u_int) c); return (TOK_ERR); } /* * Skip comments. */ if (c == '#') { while ((c = tgetc()) != EOF) { if (c == '\n') { input->lno++; goto again; } } warnx("unexpected EOF in comment"); return (TOK_ERR); } /* * Single character tokens. */ if (strchr("():|", c) != NULL) return (c); if (c == '"' || c == '<') { int32_t end = c; size_t n = 0; val = 1; if (c == '<') { val = 0; end = '>'; } while ((c = tgetc()) != EOF) { if (c == end) break; if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("filename too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; return (TOK_FILENAME); } /* * Sort out numbers. */ if (isdigit(c)) { size_t n = 0; nexttok[n++] = c; while ((c = tgetc()) != EOF) { if (!isdigit(c)) { if (tungetc(c) < 0) return (TOK_ERR); break; } if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("number too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; sscanf(nexttok, "%lu", &val); return (TOK_NUM); } /* * So that has to be a string. */ if (isalpha(c) || c == '_' || c == '-') { size_t n = 0; nexttok[n++] = c; while ((c = tgetc()) != EOF) { if (!isalnum(c) && c != '_' && c != '-') { if (tungetc (c) < 0) return (TOK_ERR); break; } if (n == sizeof(nexttok) - 1) { nexttok[n++] = '\0'; warnx("string too long '%s...'", nexttok); return (TOK_ERR); } nexttok[n++] = c; } nexttok[n++] = '\0'; /* * Keywords. */ for (c = 0; keywords[c].str != NULL; c++) if (strcmp(keywords[c].str, nexttok) == 0) { val = keywords[c].val; return (keywords[c].tok); } if ((t = snmp_enumtc_lookup(snmptoolctx, nexttok)) != NULL) { val = t->syntax; return (TOK_DEFTYPE); } return (TOK_STR); } if (isprint(c)) warnx("%u: unexpected character '%c'", input->lno, c); else warnx("%u: unexpected character 0x%02x", input->lno, (u_int) c); return (TOK_ERR); } /* * Update table information. */ static struct snmp_index_entry * snmp_import_update_table(enum snmp_tbl_entry te, struct snmp_index_entry *tbl) { switch (te) { case ENTRY_NONE: if (table_data.tbl_type == ENTRY_NONE) return (NULL); if (table_data.tbl_type == ENTRY_INDEX) table_data.table_idx = NULL; table_data.tbl_type--; return (NULL); case ENTRY_INDEX: if (tbl == NULL) warnx("No table_index to add!!!"); table_data.table_idx = tbl; table_data.tbl_type = ENTRY_INDEX; return (tbl); case ENTRY_DATA: if (table_data.tbl_type == ENTRY_INDEX) { table_data.tbl_type = ENTRY_DATA; return (table_data.table_idx); } return (NULL); default: /* NOTREACHED */ warnx("Unknown table entry type!!!"); break; } return (NULL); } static int32_t parse_enum(struct snmp_toolinfo *snmptoolctx, enum tok *tok, struct enum_pairs *enums) { while ((*tok = gettoken(snmptoolctx)) == TOK_STR) { if (enum_pair_insert(enums, val, nexttok) < 0) return (-1); if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) break; } if (*tok != ')') { warnx("')' at end of enums"); return (-1); } return (1); } static int32_t parse_subtype(struct snmp_toolinfo *snmptoolctx, enum tok *tok, enum snmp_tc *tc) { if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { warnx("subtype expected after '|'"); return (-1); } *tc = snmp_get_tc(nexttok); *tok = gettoken(snmptoolctx); return (1); } static int32_t parse_type(struct snmp_toolinfo *snmptoolctx, enum tok *tok, enum snmp_tc *tc, struct enum_pairs **snmp_enum) { int32_t syntax, mem; syntax = val; *tc = 0; if (*tok == TOK_ENUM || *tok == TOK_BITS) { if (*snmp_enum == NULL) { if ((*snmp_enum = enum_pairs_init()) == NULL) return (-1); mem = 1; *tc = SNMP_TC_OWN; } else mem = 0; if (gettoken(snmptoolctx) != '(') { warnx("'(' expected after ENUM/BITS"); return (-1); } if ((*tok = gettoken(snmptoolctx)) != TOK_NUM) { warnx("need value for ENUM//BITS"); if (mem == 1) { free(*snmp_enum); *snmp_enum = NULL; } return (-1); } if (parse_enum(snmptoolctx, tok, *snmp_enum) < 0) { enum_pairs_free(*snmp_enum); *snmp_enum = NULL; return (-1); } *tok = gettoken(snmptoolctx); } else if (*tok == TOK_DEFTYPE) { struct enum_type *t; *tc = 0; t = snmp_enumtc_lookup(snmptoolctx, nexttok); if (t != NULL) *snmp_enum = t->snmp_enum; *tok = gettoken(snmptoolctx); } else { if ((*tok = gettoken(snmptoolctx)) == '|') { if (parse_subtype(snmptoolctx, tok, tc) < 0) return (-1); } } return (syntax); } static int32_t snmp_import_head(struct snmp_toolinfo *snmptoolctx) { enum tok tok; if ((tok = gettoken(snmptoolctx)) == '(') tok = gettoken(snmptoolctx); if (tok != TOK_NUM || val > ASN_MAXID ) { warnx("Suboid expected - line %d", input->lno); return (-1); } if (gettoken(snmptoolctx) != TOK_STR) { warnx("Node name expected at line %d", input->lno); return (-1); } return (1); } static int32_t snmp_import_table(struct snmp_toolinfo *snmptoolctx, struct snmp_oid2str *obj) { int32_t i; enum snmp_tc tc; enum tok tok; struct snmp_index_entry *entry; if ((entry = malloc(sizeof(struct snmp_index_entry))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } memset(entry, 0, sizeof(struct snmp_index_entry)); STAILQ_INIT(&(entry->index_list)); for (i = 0, tok = gettoken(snmptoolctx); i < SNMP_INDEXES_MAX; i++) { int32_t syntax; struct enum_pairs *enums = NULL; if (tok != TOK_TYPE && tok != TOK_DEFTYPE && tok != TOK_ENUM && tok != TOK_BITS) break; if ((syntax = parse_type(snmptoolctx, &tok, &tc, &enums)) < 0) { enum_pairs_free(enums); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } if (snmp_syntax_insert(&(entry->index_list), enums, syntax, tc) < 0) { snmp_index_listfree(&(entry->index_list)); enum_pairs_free(enums); free(entry); return (-1); } } if (i == 0 || i > SNMP_INDEXES_MAX) { warnx("Bad number of indexes at line %d", input->lno); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } if (tok != TOK_STR) { warnx("String expected after indexes at line %d", input->lno); snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } entry->string = obj->string; entry->strlen = obj->strlen; asn_append_oid(&(entry->var), &(obj->var)); if ((i = snmp_table_insert(snmptoolctx, entry)) < 0) { snmp_index_listfree(&(entry->index_list)); free(entry); return (-1); } else if (i == 0) { /* Same entry already present in lists. */ free(entry->string); free(entry); } (void) snmp_import_update_table(ENTRY_INDEX, entry); return (1); } /* * Read everything after the syntax type that is certainly a leaf OID info. */ static int32_t snmp_import_leaf(struct snmp_toolinfo *snmptoolctx, enum tok *tok, struct snmp_oid2str *oid2str) { int32_t i, syntax; if ((syntax = parse_type(snmptoolctx, tok, &(oid2str->tc), &(oid2str->snmp_enum))) < 0) return(-1); oid2str->syntax = syntax; /* * That is the name of the function, corresponding to the entry. * It is used by bsnmpd, but is not interesting for us. */ if (*tok == TOK_STR) *tok = gettoken(snmptoolctx); for (i = 0; i < SNMP_ACCESS_GETSET && *tok == TOK_ACCESS; i++) { oid2str->access |= (uint32_t) val; *tok = gettoken(snmptoolctx); } if (*tok != ')') { warnx("')' expected at end of line %d", input->lno); return (-1); } oid2str->table_idx = snmp_import_update_table(ENTRY_DATA, NULL); if ((i = snmp_leaf_insert(snmptoolctx, oid2str)) < 0) { warnx("Error adding leaf %s to list", oid2str->string); return (-1); } /* * Same entry is already present in the mapping lists and * the new one was not inserted. */ if (i == 0) { free(oid2str->string); free(oid2str); } (void) snmp_import_update_table(ENTRY_NONE, NULL); return (1); } static int32_t snmp_import_object(struct snmp_toolinfo *snmptoolctx) { char *string; int i; enum tok tok; struct snmp_oid2str *oid2str; if (snmp_import_head(snmptoolctx) < 0) return (-1); if ((oid2str = malloc(sizeof(struct snmp_oid2str))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } if ((string = malloc(strlen(nexttok) + 1)) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); free(oid2str); return (-1); } memset(oid2str, 0, sizeof(struct snmp_oid2str)); strlcpy(string, nexttok, strlen(nexttok) + 1); oid2str->string = string; oid2str->strlen = strlen(nexttok); asn_append_oid(&(oid2str->var), &(current_oid)); if (snmp_suboid_append(&(oid2str->var), (asn_subid_t) val) < 0) goto error; /* * Prepared the entry - now figure out where to insert it. * After the object we have following options: * 1) new line, blank, ) - then it is an enum oid -> snmp_enumlist; * 2) new line , ( - nonleaf oid -> snmp_nodelist; * 2) ':' - table entry - a variable length SYNTAX_TYPE (one or more) * may follow and second string must end line -> snmp_tablelist; * 3) OID , string ) - this is a trap entry or a leaf -> snmp_oidlist; * 4) SYNTAX_TYPE, string (not always), get/set modifier - always last * and )- this is definitely a leaf. */ switch (tok = gettoken(snmptoolctx)) { case ')': if ((i = snmp_enum_insert(snmptoolctx, oid2str)) < 0) goto error; if (i == 0) { free(oid2str->string); free(oid2str); } return (1); case '(': if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) goto error; /* * Ignore the error for nodes since the .def files currently * contain different strings for 1.3.6.1.2.1 - mibII. Only make * sure the memory is freed and don't complain. */ if ((i = snmp_node_insert(snmptoolctx, oid2str)) <= 0) { free(string); free(oid2str); } return (snmp_import_object(snmptoolctx)); case ':': if (snmp_suboid_append(¤t_oid, (asn_subid_t) val) < 0) goto error; if (snmp_import_table(snmptoolctx, oid2str) < 0) goto error; /* * A different table entry type was malloced and the data is * contained there. */ free(oid2str); return (1); case TOK_TYPE: /* FALLTHROUGH */ case TOK_DEFTYPE: /* FALLTHROUGH */ case TOK_ENUM: /* FALLTHROUGH */ case TOK_BITS: if (snmp_import_leaf(snmptoolctx, &tok, oid2str) < 0) goto error; return (1); default: warnx("Unexpected token at line %d - %s", input->lno, input->fname); break; } error: snmp_mapping_entryfree(oid2str); return (-1); } static int32_t snmp_import_tree(struct snmp_toolinfo *snmptoolctx, enum tok *tok) { while (*tok != TOK_EOF) { switch (*tok) { case TOK_ERR: return (-1); case '(': if (snmp_import_object(snmptoolctx) < 0) return (-1); break; case ')': if (snmp_suboid_pop(¤t_oid) < 0) return (-1); (void) snmp_import_update_table(ENTRY_NONE, NULL); break; default: /* Anything else here would be illegal. */ return (-1); } *tok = gettoken(snmptoolctx); } return (0); } static int32_t snmp_import_top(struct snmp_toolinfo *snmptoolctx, enum tok *tok) { enum snmp_tc tc; struct enum_type *t; if (*tok == '(') return (snmp_import_tree(snmptoolctx, tok)); if (*tok == TOK_TYPEDEF) { if ((*tok = gettoken(snmptoolctx)) != TOK_STR) { warnx("type name expected after typedef - %s", input->fname); return (-1); } t = snmp_enumtc_init(nexttok); *tok = gettoken(snmptoolctx); t->is_enum = (*tok == TOK_ENUM); t->is_bits = (*tok == TOK_BITS); t->syntax = parse_type(snmptoolctx, tok, &tc, &(t->snmp_enum)); snmp_enumtc_insert(snmptoolctx, t); return (1); } if (*tok == TOK_INCLUDE) { int i; *tok = gettoken(snmptoolctx); if (*tok != TOK_FILENAME) { warnx("filename expected in include directive - %s", nexttok); return (-1); } if (( i = add_filename(snmptoolctx, nexttok, NULL, 1)) == 0) { *tok = gettoken(snmptoolctx); return (1); } if (i == -1) return (-1); input_fopen(nexttok); *tok = gettoken(snmptoolctx); return (1); } warnx("'(' or 'typedef' expected - %s", nexttok); return (-1); } static int32_t snmp_import(struct snmp_toolinfo *snmptoolctx) { int i; enum tok tok; tok = gettoken(snmptoolctx); do i = snmp_import_top(snmptoolctx, &tok); while (i > 0); return (i); } /* * Read a .def file and import oid<->string mapping. * Mappings are inserted into a global structure containing list for each OID * syntax type. */ int32_t snmp_import_file(struct snmp_toolinfo *snmptoolctx, struct fname *file) { int idx; snmp_import_init(&(file->cut)); input_fopen(file->name); if ((idx = snmp_import(snmptoolctx)) < 0) warnx("Failed to read mappings from file %s", file->name); input_close(); return (idx); }