Index: stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c =================================================================== --- stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 299015) +++ stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmpimport.c (revision 299016) @@ -1,971 +1,972 @@ /*- * 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); + return (0); } (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); } Index: stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c =================================================================== --- stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 299015) +++ stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptc.c (revision 299016) @@ -1,1287 +1,1288 @@ /*- * 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. * * Textual conventions for OctetStrings * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" /* OctetString, DisplayString */ static char *snmp_oct2str(uint32_t, char *, char *); static char *snmp_str2asn_oid(char *, struct asn_oid *); static int parse_octetstring(struct snmp_value *, char *); /* DateAndTime */ static char *snmp_octstr2date(uint32_t, char *, char *); static char *snmp_date2asn_oid(char * , struct asn_oid *); static int parse_dateandtime(struct snmp_value *, char *); /* PhysAddress */ static char *snmp_oct2physAddr(uint32_t, char *, char *); static char *snmp_addr2asn_oid(char *, struct asn_oid *); static int parse_physaddress(struct snmp_value *, char *); /* NTPTimeStamp */ static char *snmp_oct2ntp_ts(uint32_t, char *, char *); static char *snmp_ntp_ts2asn_oid(char *, struct asn_oid *); static int parse_ntp_ts(struct snmp_value *, char *); /* BridgeId */ static char *snmp_oct2bridgeid(uint32_t, char *, char *); static char *snmp_bridgeid2oct(char *, struct asn_oid *); static int parse_bridge_id(struct snmp_value *, char *); /* BridgePortId */ static char *snmp_oct2bport_id(uint32_t, char *, char *); static char *snmp_bport_id2oct(char *, struct asn_oid *); static int parse_bport_id(struct snmp_value *, char *); /* InetAddress */ static char *snmp_oct2inetaddr(uint32_t len, char *octets, char *buf); static char *snmp_inetaddr2oct(char *str, struct asn_oid *oid); static int32_t parse_inetaddr(struct snmp_value *value, char *string); static char *snmp_oct2bits(uint32_t len, char *octets, char *buf); static char *snmp_bits2oct(char *str, struct asn_oid *oid); static int32_t parse_bits(struct snmp_value *value, char *string); struct snmp_text_conv { enum snmp_tc tc; const char *tc_str; int32_t len; snmp_oct2tc_f oct2tc; snmp_tc2oid_f tc2oid; snmp_tc2oct_f tc2oct; } text_convs[] = { { SNMP_STRING, "OctetString", SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, { SNMP_DISPLAYSTRING, "DisplayString" , SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring }, { SNMP_DATEANDTIME, "DateAndTime", SNMP_DATETIME_STRSZ, snmp_octstr2date, snmp_date2asn_oid, parse_dateandtime }, { SNMP_PHYSADDR, "PhysAddress", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_ATMESI, "AtmESI", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_NTP_TIMESTAMP, "NTPTimeStamp", SNMP_NTP_TS_STRSZ, snmp_oct2ntp_ts, snmp_ntp_ts2asn_oid, parse_ntp_ts }, { SNMP_MACADDRESS, "MacAddress", SNMP_PHYSADDR_STRSZ, snmp_oct2physAddr, snmp_addr2asn_oid, parse_physaddress }, { SNMP_BRIDGE_ID, "BridgeId", SNMP_BRIDGEID_STRSZ, snmp_oct2bridgeid, snmp_bridgeid2oct, parse_bridge_id }, { SNMP_BPORT_ID, "BridgePortId", SNMP_BPORT_STRSZ, snmp_oct2bport_id, snmp_bport_id2oct, parse_bport_id }, { SNMP_INETADDRESS, "InetAddress", SNMP_INADDRS_STRSZ, snmp_oct2inetaddr, snmp_inetaddr2oct, parse_inetaddr }, { SNMP_TC_OWN, "BITS", SNMP_VAR_STRSZ, snmp_oct2bits, snmp_bits2oct, parse_bits }, { SNMP_UNKNOWN, "Unknown", SNMP_VAR_STRSZ, snmp_oct2str, snmp_str2asn_oid, parse_octetstring } /* keep last */ }; /* Common API */ enum snmp_tc snmp_get_tc(char *str) { int i; for (i = 0; i < SNMP_UNKNOWN; i++) { if (!strncmp(text_convs[i].tc_str, str, strlen(text_convs[i].tc_str))) return (text_convs[i].tc); } return (SNMP_STRING); } char * snmp_oct2tc(enum snmp_tc tc, uint32_t len, char *octets) { uint32_t tc_len; char * buf; if (tc < 0 || tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; if (text_convs[tc].len > 0) tc_len = text_convs[tc].len; else tc_len = 2 * len + 3; if ((buf = malloc(tc_len)) == NULL ) { syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); return (NULL); } memset(buf, 0, tc_len); if (text_convs[tc].oct2tc(len, octets, buf) == NULL) { free(buf); return (NULL); } return (buf); } char * snmp_tc2oid(enum snmp_tc tc, char *str, struct asn_oid *oid) { if (tc < 0 || tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; return (text_convs[tc].tc2oid(str, oid)); } int32_t snmp_tc2oct(enum snmp_tc tc, struct snmp_value *value, char *string) { if (tc < 0 || tc > SNMP_UNKNOWN) tc = SNMP_UNKNOWN; return (text_convs[tc].tc2oct(value, string)); } /***************************************************** * Basic OctetString type. */ static char * snmp_oct2str(uint32_t len, char *octets, char *buf) { uint8_t binary = 0; uint32_t i; char *ptr; if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) return (NULL); for (ptr = buf, i = 0; i < len; i++) if (!isprint(octets[i])) { binary = 1; buf += sprintf(buf, "0x"); break; } for (ptr = buf, i = 0; i < len; i++) if (!binary) ptr += sprintf(ptr, "%c", octets[i]); else ptr += sprintf(ptr, "%2.2x", (u_char)octets[i]); return (buf); } static char * snmp_str2asn_oid(char *str, struct asn_oid *oid) { uint32_t i, len = 0; /* * OctetStrings are allowed max length of ASN_MAXOCTETSTRING, * but trying to index an entry with such a long OctetString * will fail anyway. */ for (len = 0; len < ASN_MAXOIDLEN; len++) { if (strchr(",]", *(str + len)) != NULL) break; } if (len >= ASN_MAXOIDLEN) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) len) < 0) return (NULL); for (i = 0; i < len; i++) if (snmp_suboid_append(oid, (asn_subid_t) *(str + i)) < 0) return (NULL); return (str + len); } static int32_t parse_octetstring(struct snmp_value *value, char *val) { size_t len; if ((len = strlen(val)) >= MAX_OCTSTRING_LEN) { warnx("Octetstring too long - %d is max allowed", MAX_OCTSTRING_LEN - 1); return (-1); } value->v.octetstring.len = len; if((value->v.octetstring.octets = malloc(len)) == NULL) { syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); return (-1); } memcpy(value->v.octetstring.octets, val, len); value->syntax = SNMP_SYNTAX_OCTETSTRING; return (0); } /************************************************************* * DateAndTime ************************************************************* * rfc 2579 specification: * DateAndTime ::= TEXTUAL-CONVENTION * DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d" * STATUS current * DESCRIPTION * "A date-time specification. * * field octets contents range * ----- ------ -------- ----- * 1 1-2 year* 0..65536 * 2 3 month 1..12 * 3 4 day 1..31 * 4 5 hour 0..23 * 5 6 minutes 0..59 * 6 7 seconds 0..60 * (use 60 for leap-second) * 7 8 deci-seconds 0..9 * 8 9 direction from UTC '+' / '-' * 9 10 hours from UTC* 0..13 * 10 11 minutes from UTC 0..59 * * * Notes: * - the value of year is in network-byte order * - daylight saving time in New Zealand is +13 * * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be * displayed as: * * 1992-5-26,13:30:15.0,-4:0 */ static char * snmp_octstr2date(uint32_t len, char *octets, char *buf) { int year; char *ptr; if (len != SNMP_DATETIME_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; year = (octets[0] << 8); year += (octets[1]); ptr = buf; ptr += sprintf(ptr, "%4.4d-%.2d-%.2d, ", year, octets[2],octets[3]); ptr += sprintf(ptr, "%2.2d:%2.2d:%2.2d.%.2d, ", octets[4],octets[5], octets[6],octets[7]); ptr += sprintf(ptr, "%c%.2d:%.2d", octets[8],octets[9],octets[10]); return (buf); } static char * snmp_date2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; - uint32_t v; + static const char UTC[3] = "UTC"; int32_t saved_errno; + uint32_t v; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_DATETIME_OCTETS) < 0) return (NULL); /* Read 'YYYY-' and write it in two subs. */ ptr = str; saved_errno = errno; errno = 0; v = strtoul(ptr, &endptr, 10); if (v > 0xffff) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) ((v & 0xff00) >> 8)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) return (NULL); /* 'MM-' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'DD,' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '-') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'HH:' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'MM:' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'SS.' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != '.') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'M(mseconds),' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ',') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'UTC' - optional */ ptr = endptr + 1; - if (*ptr == 'U' && *(ptr + 1) == 'T' && *(ptr + 1) == 'C') - ptr += 3; + if (strncmp(ptr, UTC, sizeof(UTC)) == 0) + ptr += sizeof(UTC); /* '+/-' */ if (*ptr == '-' || *ptr == '+') { if (snmp_suboid_append(oid, (asn_subid_t) (*ptr)) < 0) return (NULL); } else goto error1; /* 'HH:' */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (*endptr != ':') goto error1; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); /* 'MM' - last one - ignore endptr here. */ ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0) goto error; else errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); error: errno = saved_errno; error1: warnx("Date value %s not supported", str); return (NULL); } /* Read a DateAndTime string eg. 1992-5-26,13:30:15.0,-4:0. */ static int32_t parse_dateandtime(struct snmp_value *sv, char *val) { char *endptr; uint32_t v; uint8_t date[SNMP_DATETIME_OCTETS]; /* 'YYYY-' */ v = strtoul(val, &endptr, 10); if (v > 0xffff || *endptr != '-') goto error; date[0] = ((v & 0xff00) >> 8); date[1] = (v & 0xff); val = endptr + 1; /* 'MM-' */ v = strtoul(val, &endptr, 10); if (v == 0 || v > 12 || *endptr != '-') goto error; date[2] = v; val = endptr + 1; /* 'DD,' */ v = strtoul(val, &endptr, 10); if (v == 0 || v > 31 || *endptr != ',') goto error; date[3] = v; val = endptr + 1; /* 'HH:' */ v = strtoul(val, &endptr, 10); if (v > 23 || *endptr != ':') goto error; date[4] = v; val = endptr + 1; /* 'MM:' */ v = strtoul(val, &endptr, 10); if (v > 59 || *endptr != ':') goto error; date[5] = v; val = endptr + 1; /* 'SS.' */ v = strtoul(val, &endptr, 10); if (v > 60 || *endptr != '.') goto error; date[6] = v; val = endptr + 1; /* '(deci-)s,' */ v = strtoul(val, &endptr, 10); if (v > 9 || *endptr != ',') goto error; date[7] = v; val = endptr + 1; /* offset - '+/-' */ if (*val != '-' && *val != '+') goto error; date[8] = (uint8_t) *val; val = endptr + 1; /* 'HH:' - offset from UTC */ v = strtoul(val, &endptr, 10); if (v > 13 || *endptr != ':') goto error; date[9] = v; val = endptr + 1; /* 'MM'\0'' offset from UTC */ v = strtoul(val, &endptr, 10); if (v > 59 || *endptr != '\0') goto error; date[10] = v; if ((sv->v.octetstring.octets = malloc(SNMP_DATETIME_OCTETS)) == NULL) { warnx("malloc() failed - %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_DATETIME_OCTETS; memcpy(sv->v.octetstring.octets, date, SNMP_DATETIME_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); error: warnx("Date value %s not supported", val); return (-1); } /************************************************************** * PhysAddress */ static char * snmp_oct2physAddr(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i; if (len != SNMP_PHYSADDR_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; ptr += sprintf(ptr, "%2.2x", octets[0]); for (i = 1; i < 6; i++) ptr += sprintf(ptr, ":%2.2x", octets[i]); return (buf); } static char * snmp_addr2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_PHYSADDR_OCTETS) < 0) return (NULL); ptr = str; for (i = 0; i < 5; i++) { saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (*endptr != ':') { warnx("Failed adding oid - %s",str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } /* The last one - don't check the ending char here. */ saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_physaddress(struct snmp_value *sv, char *val) { char *endptr; int32_t i; uint32_t v; uint8_t phys_addr[SNMP_PHYSADDR_OCTETS]; for (i = 0; i < 5; i++) { v = strtoul(val, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", val); return (-1); } if(*endptr != ':') { warnx("Failed reading octet - %s", val); return (-1); } phys_addr[i] = v; val = endptr + 1; } /* The last one - don't check the ending char here. */ v = strtoul(val, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", val); return (-1); } phys_addr[5] = v; if ((sv->v.octetstring.octets = malloc(SNMP_PHYSADDR_OCTETS)) == NULL) { syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_PHYSADDR_OCTETS; memcpy(sv->v.octetstring.octets, phys_addr, SNMP_PHYSADDR_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * NTPTimeStamp ************************************************************** * NTP MIB, Revision 0.2, 7/25/97: * NTPTimeStamp ::= TEXTUAL-CONVENTION * DISPLAY-HINT "4x.4x" * STATUS current * DESCRIPTION * "" * SYNTAX OCTET STRING (SIZE(8)) */ static char * snmp_oct2ntp_ts(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i; if (len != SNMP_NTP_TS_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; i = octets[0] * 1000 + octets[1] * 100 + octets[2] * 10 + octets[3]; ptr += sprintf(ptr, "%4.4d", i); i = octets[4] * 1000 + octets[5] * 100 + octets[6] * 10 + octets[7]; ptr += sprintf(ptr, ".%4.4d", i); return (buf); } static char * snmp_ntp_ts2asn_oid(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i, d; struct asn_oid suboid; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_NTP_TS_OCTETS) < 0) return (NULL); ptr = str; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { warnx("Integer value %s not supported", str); errno = saved_errno; return (NULL); } else errno = saved_errno; if (*endptr != '.') { warnx("Failed adding oid - %s",str); return (NULL); } memset(&suboid, 0, sizeof(struct asn_oid)); suboid.len = SNMP_NTP_TS_OCTETS; for (i = 0, d = 1000; i < 4; i++) { suboid.subs[i] = v / d; v = v % d; d = d / 10; } ptr = endptr + 1; saved_errno = errno; v = strtoul(ptr, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { warnx("Integer value %s not supported", str); errno = saved_errno; return (NULL); } else errno = saved_errno; for (i = 0, d = 1000; i < 4; i++) { suboid.subs[i + 4] = v / d; v = v % d; d = d / 10; } asn_append_oid(oid, &suboid); return (endptr); } static int32_t parse_ntp_ts(struct snmp_value *sv, char *val) { char *endptr; int32_t i, d, saved_errno; uint32_t v; uint8_t ntp_ts[SNMP_NTP_TS_OCTETS]; saved_errno = errno; v = strtoul(val, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { saved_errno = errno; warnx("Integer value %s not supported", val); return (-1); } else saved_errno = errno; if (*endptr != '.') { warnx("Failed reading octet - %s", val); return (-1); } for (i = 0, d = 1000; i < 4; i++) { ntp_ts[i] = v / d; v = v % d; d = d / 10; } val = endptr + 1; saved_errno = errno; v = strtoul(val, &endptr, 10); if (errno != 0 || (v / 1000) > 9) { saved_errno = errno; warnx("Integer value %s not supported", val); return (-1); } else saved_errno = errno; for (i = 0, d = 1000; i < 4; i++) { ntp_ts[i + 4] = v / d; v = v % d; d = d / 10; } if ((sv->v.octetstring.octets = malloc(SNMP_NTP_TS_OCTETS)) == NULL) { syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_NTP_TS_OCTETS; memcpy(sv->v.octetstring.octets, ntp_ts, SNMP_NTP_TS_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * BridgeId ************************************************************** * BRIDGE-MIB, REVISION "200509190000Z" * BridgeId ::= TEXTUAL-CONVENTION * STATUS current * DESCRIPTION * "The Bridge-Identifier, as used in the Spanning Tree * Protocol, to uniquely identify a bridge. Its first two * octets (in network byte order) contain a priority value, * and its last 6 octets contain the MAC address used to * refer to a bridge in a unique fashion (typically, the * numerically smallest MAC address of all ports on the * bridge)." * SYNTAX OCTET STRING (SIZE (8)) */ static char * snmp_oct2bridgeid(uint32_t len, char *octets, char *buf) { char *ptr; uint32_t i, priority; if (len != SNMP_BRIDGEID_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; priority = octets[0] << 8; priority += octets[1]; if (priority > SNMP_MAX_BRIDGE_PRIORITY) { warnx("Invalid bridge priority %d", priority); return (NULL); } else ptr += sprintf(ptr, "%d.", octets[0]); ptr += sprintf(ptr, "%2.2x", octets[2]); for (i = 1; i < 6; i++) ptr += sprintf(ptr, ":%2.2x", octets[i + 2]); return (buf); } static char * snmp_bridgeid2oct(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v, i; int32_t saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BRIDGEID_OCTETS) < 0) return (NULL); ptr = str; /* Read the priority. */ saved_errno = errno; v = strtoul(ptr, &endptr, 10); errno = 0; if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge priority value %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff00)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xff)) < 0) return (NULL); ptr = endptr + 1; for (i = 0; i < 5; i++) { saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (*endptr != ':') { warnx("Failed adding oid - %s",str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } /* The last one - don't check the ending char here. */ saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Integer value %s not supported", str); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_bridge_id(struct snmp_value *sv, char *string) { char *ptr, *endptr; int32_t i, saved_errno; uint32_t v; uint8_t bridge_id[SNMP_BRIDGEID_OCTETS]; ptr = string; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(string, &endptr, 10); errno = saved_errno; if (v > SNMP_MAX_BRIDGE_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge priority value %d", v); return (-1); } bridge_id[0] = (v & 0xff00); bridge_id[1] = (v & 0xff); string = endptr + 1; for (i = 0; i < 5; i++) { v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", string); return (-1); } if(*endptr != ':') { warnx("Failed reading octet - %s", string); return (-1); } bridge_id[i + 2] = v; string = endptr + 1; } /* The last one - don't check the ending char here. */ v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Integer value %s not supported", string); return (-1); } bridge_id[7] = v; if ((sv->v.octetstring.octets = malloc(SNMP_BRIDGEID_OCTETS)) == NULL) { syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); return (-1); } sv->v.octetstring.len = SNMP_BRIDGEID_OCTETS; memcpy(sv->v.octetstring.octets, bridge_id, SNMP_BRIDGEID_OCTETS); sv->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * BridgePortId ************************************************************** * BEGEMOT-BRIDGE-MIB, LAST-UPDATED "200608100000Z" * BridgePortId ::= TEXTUAL-CONVENTION * DISPLAY-HINT "1x.1x" * STATUS current * DESCRIPTION * "A port identifier that contains a bridge port's STP priority * in the first octet and the port number in the second octet." * SYNTAX OCTET STRING (SIZE(2)) */ static char * snmp_oct2bport_id(uint32_t len, char *octets, char *buf) { char *ptr; if (len != SNMP_BPORT_OCTETS || octets == NULL || buf == NULL) return (NULL); buf[0]= '\0'; ptr = buf; ptr += sprintf(ptr, "%d.", octets[0]); ptr += sprintf(ptr, "%d", octets[1]); return (buf); } static char * snmp_bport_id2oct(char *str, struct asn_oid *oid) { char *endptr, *ptr; uint32_t v; int saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) SNMP_BPORT_OCTETS) < 0) return (NULL); ptr = str; /* Read the priority. */ saved_errno = errno; v = strtoul(ptr, &endptr, 10); errno = 0; if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge port priority value %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); saved_errno = errno; v = strtoul(ptr, &endptr, 16); errno = saved_errno; if (v > 0xff) { warnx("Bad port number - %d", v); return (NULL); } if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static int32_t parse_bport_id(struct snmp_value *value, char *string) { char *ptr, *endptr; int saved_errno; uint32_t v; uint8_t bport_id[SNMP_BPORT_OCTETS]; ptr = string; /* Read the priority. */ saved_errno = errno; errno = 0; v = strtoul(string, &endptr, 10); errno = saved_errno; if (v > SNMP_MAX_BPORT_PRIORITY || errno != 0 || *endptr != '.') { errno = saved_errno; warnx("Bad bridge port priority value %d", v); return (-1); } bport_id[0] = v; string = endptr + 1; v = strtoul(string, &endptr, 16); if (v > 0xff) { warnx("Bad port number - %d", v); return (-1); } bport_id[1] = v; if ((value->v.octetstring.octets = malloc(SNMP_BPORT_OCTETS)) == NULL) { syslog(LOG_ERR,"malloc failed: %s", strerror(errno)); return (-1); } value->v.octetstring.len = SNMP_BPORT_OCTETS; memcpy(value->v.octetstring.octets, bport_id, SNMP_BPORT_OCTETS); value->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } /************************************************************** * InetAddress ************************************************************** * INET-ADDRESS-MIB, REVISION "200502040000Z" * InetAddress ::= TEXTUAL-CONVENTION * STATUS current * DESCRIPTION * "Denotes a generic Internet address. * * An InetAddress value is always interpreted within the context * of an InetAddressType value. Every usage of the InetAddress * textual convention is required to specify the InetAddressType * object that provides the context. It is suggested that the * InetAddressType object be logically registered before the * object(s) that use the InetAddress textual convention, if * they appear in the same logical row. * * The value of an InetAddress object must always be * consistent with the value of the associated InetAddressType * object. Attempts to set an InetAddress object to a value * inconsistent with the associated InetAddressType * must fail with an inconsistentValue error. * * When this textual convention is used as the syntax of an * index object, there may be issues with the limit of 128 * sub-identifiers specified in SMIv2, STD 58. In this case, * the object definition MUST include a 'SIZE' clause to * limit the number of potential instance sub-identifiers; * otherwise the applicable constraints MUST be stated in * the appropriate conceptual row DESCRIPTION clauses, or * in the surrounding documentation if there is no single * DESCRIPTION clause that is appropriate." * SYNTAX OCTET STRING (SIZE (0..255)) ************************************************************** * TODO: FIXME!!! syrinx: Since we do not support checking the * consistency of a varbinding based on the value of a previous * one, try to guess the type of address based on the * OctetString SIZE - 4 for IPv4, 16 for IPv6, others currently * not supported. */ static char * snmp_oct2inetaddr(uint32_t len, char *octets, char *buf) { int af; void *ip; struct in_addr ipv4; struct in6_addr ipv6; if (len > MAX_OCTSTRING_LEN || octets == NULL || buf == NULL) return (NULL); switch (len) { /* XXX: FIXME - IPv4*/ case 4: memcpy(&ipv4.s_addr, octets, sizeof(ipv4.s_addr)); af = AF_INET; ip = &ipv4; break; /* XXX: FIXME - IPv4*/ case 16: memcpy(ipv6.s6_addr, octets, sizeof(ipv6.s6_addr)); af = AF_INET6; ip = &ipv6; break; default: return (NULL); } if (inet_ntop(af, ip, buf, SNMP_INADDRS_STRSZ) == NULL) { warnx("inet_ntop failed - %s", strerror(errno)); return (NULL); } return (buf); } static char * snmp_inetaddr2oct(char *str, struct asn_oid *oid) { return (NULL); } static int32_t parse_inetaddr(struct snmp_value *value, char *string) { return (-1); } /************************************************************** * SNMP BITS type - XXX: FIXME **************************************************************/ static char * snmp_oct2bits(uint32_t len, char *octets, char *buf) { int i, bits; uint64_t value; if (len > sizeof(value) || octets == NULL || buf == NULL) return (NULL); for (i = len, value = 0, bits = 0; i > 0; i--, bits += 8) value += octets[i] << bits; buf[0]= '\0'; sprintf(buf, "0x%llx.",(long long unsigned) value); return (buf); } static char * snmp_bits2oct(char *str, struct asn_oid *oid) { char *endptr; int i, size, bits, saved_errno; uint64_t v, mask = 0xFF00000000000000; saved_errno = errno; errno = 0; v = strtoull(str, &endptr, 16); if (errno != 0) { warnx("Bad BITS value %s - %s", str, strerror(errno)); errno = saved_errno; return (NULL); } bits = 8; /* Determine length - up to 8 octets supported so far. */ for (size = sizeof(v); size > 0; size--) { if ((v & mask) != 0) break; mask = mask >> bits; } if (size == 0) size = 1; if (snmp_suboid_append(oid, (asn_subid_t) size) < 0) return (NULL); for (i = 0, bits = 0; i < size; i++, bits += 8) if (snmp_suboid_append(oid, (asn_subid_t)((v & mask) >> bits)) < 0) return (NULL); return (endptr); } static int32_t parse_bits(struct snmp_value *value, char *string) { char *endptr; int i, size, bits, saved_errno; uint64_t v, mask = 0xFF00000000000000; saved_errno = errno; errno = 0; v = strtoull(string, &endptr, 16); if (errno != 0) { warnx("Bad BITS value %s - %s", string, strerror(errno)); errno = saved_errno; return (-1); } bits = 8; /* Determine length - up to 8 octets supported so far. */ for (size = sizeof(v); size > 0; size--) { if ((v & mask) != 0) break; mask = mask >> bits; } if (size == 0) size = 1; if ((value->v.octetstring.octets = malloc(size)) == NULL) { syslog(LOG_ERR, "malloc failed: %s", strerror(errno)); return (-1); } value->v.octetstring.len = size; for (i = 0, bits = 0; i < size; i++, bits += 8) value->v.octetstring.octets[i] = (v & mask) >> bits; value->syntax = SNMP_SYNTAX_OCTETSTRING; return (1); } Index: stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c =================================================================== --- stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 299015) +++ stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 299016) @@ -1,2132 +1,2131 @@ /*- * Copyright (c) 2005-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. * * Helper functions for snmp client tools * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" /* Internal varibale to turn on library debugging for testing and to * find bugs. It is not exported via the header file. * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */ int _bsnmptools_debug = 0; /* Default files to import mapping from if none explicitly provided. */ #define bsnmpd_defs "/usr/share/snmp/defs/tree.def" #define mibII_defs "/usr/share/snmp/defs/mibII_tree.def" /* * The .iso.org.dod oid that has to be prepended to every OID when requesting * a value. */ const struct asn_oid IsoOrgDod_OID = { 3, { 1, 3, 6 } }; #define SNMP_ERR_UNKNOWN 0 /* * An array of error strings corresponding to error definitions from libbsnmp. */ static const struct { const char *str; int32_t error; } error_strings[] = { { "Unknown", SNMP_ERR_UNKNOWN }, { "Too big ", SNMP_ERR_TOOBIG }, { "No such Name", SNMP_ERR_NOSUCHNAME }, { "Bad Value", SNMP_ERR_BADVALUE }, { "Readonly", SNMP_ERR_READONLY }, { "General error", SNMP_ERR_GENERR }, { "No access", SNMP_ERR_NO_ACCESS }, { "Wrong type", SNMP_ERR_WRONG_TYPE }, { "Wrong length", SNMP_ERR_WRONG_LENGTH }, { "Wrong encoding", SNMP_ERR_WRONG_ENCODING }, { "Wrong value", SNMP_ERR_WRONG_VALUE }, { "No creation", SNMP_ERR_NO_CREATION }, { "Inconsistent value", SNMP_ERR_INCONS_VALUE }, { "Resource unavailable", SNMP_ERR_RES_UNAVAIL }, { "Commit failed", SNMP_ERR_COMMIT_FAILED }, { "Undo failed", SNMP_ERR_UNDO_FAILED }, { "Authorization error", SNMP_ERR_AUTH_ERR }, { "Not writable", SNMP_ERR_NOT_WRITEABLE }, { "Inconsistent name", SNMP_ERR_INCONS_NAME }, { NULL, 0 } }; /* This one and any following are exceptions. */ #define SNMP_SYNTAX_UNKNOWN SNMP_SYNTAX_NOSUCHOBJECT static const struct { const char *str; enum snmp_syntax stx; } syntax_strings[] = { { "Null", SNMP_SYNTAX_NULL }, { "Integer", SNMP_SYNTAX_INTEGER }, { "OctetString", SNMP_SYNTAX_OCTETSTRING }, { "OID", SNMP_SYNTAX_OID }, { "IpAddress", SNMP_SYNTAX_IPADDRESS }, { "Counter32", SNMP_SYNTAX_COUNTER }, { "Gauge", SNMP_SYNTAX_GAUGE }, { "TimeTicks", SNMP_SYNTAX_TIMETICKS }, { "Counter64", SNMP_SYNTAX_COUNTER64 }, { "Unknown", SNMP_SYNTAX_UNKNOWN }, }; int snmptool_init(struct snmp_toolinfo *snmptoolctx) { char *str; size_t slen; memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo)); snmptoolctx->objects = 0; snmptoolctx->mappings = NULL; snmptoolctx->flags = SNMP_PDU_GET; /* XXX */ SLIST_INIT(&snmptoolctx->filelist); snmp_client_init(&snmp_client); SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS); if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0) warnx("Error adding file %s to list", bsnmpd_defs); if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0) warnx("Error adding file %s to list", mibII_defs); /* Read the environment */ if ((str = getenv("SNMPAUTH")) != NULL) { slen = strlen(str); if (slen == strlen("md5") && strcasecmp(str, "md5") == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; else if (slen != 0) warnx("Bad authentication type - %s in SNMPAUTH", str); } if ((str = getenv("SNMPPRIV")) != NULL) { slen = strlen(str); if (slen == strlen("des") && strcasecmp(str, "des") == 0) snmp_client.user.priv_proto = SNMP_PRIV_DES; else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0) snmp_client.user.priv_proto = SNMP_PRIV_AES; else if (slen != 0) warnx("Bad privacy type - %s in SNMPPRIV", str); } if ((str = getenv("SNMPUSER")) != NULL) { if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) { warnx("Username too long - %s in SNMPUSER", str); return (-1); } if (slen > 0) { strlcpy(snmp_client.user.sec_name, str, sizeof(snmp_client.user.sec_name)); snmp_client.version = SNMP_V3; } } if ((str = getenv("SNMPPASSWD")) != NULL) { if ((slen = strlen(str)) > MAXSTR) slen = MAXSTR - 1; if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) { warnx("malloc() failed - %s", strerror(errno)); return (-1); } if (slen > 0) strlcpy(snmptoolctx->passwd, str, slen + 1); } return (0); } #define OBJECT_IDX_LIST(o) o->info->table_idx->index_list /* * Walk through the file list and import string<->oid mappings from each file. */ int32_t snmp_import_all(struct snmp_toolinfo *snmptoolctx) { int32_t fc; struct fname *tmp; if (snmptoolctx == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) return (0); if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL) return (-1); fc = 0; if (SLIST_EMPTY(&snmptoolctx->filelist)) { warnx("No files to read OID <-> string conversions from"); return (-1); } else { SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) { if (tmp->done) continue; if (snmp_import_file(snmptoolctx, tmp) < 0) { fc = -1; break; } fc++; } } snmp_mapping_dump(snmptoolctx); return (fc); } /* * Add a filename to the file list - the initial idea of keeping a list with all * files to read OIDs from was that an application might want to have loaded in * memory the OIDs from a single file only and when done with them read the OIDs * from another file. This is not used yet but might be a good idea at some * point. Size argument is number of bytes in string including trailing '\0', * not string length. */ int32_t add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename, const struct asn_oid *cut, int32_t done) { char *fstring; struct fname *entry; if (snmptoolctx == NULL) return (-1); /* Make sure file was not in list. */ SLIST_FOREACH(entry, &snmptoolctx->filelist, link) { if (strncmp(entry->name, filename, strlen(entry->name)) == 0) return (0); } if ((fstring = malloc(strlen(filename) + 1)) == NULL) { warnx("malloc() failed - %s", strerror(errno)); return (-1); } if ((entry = malloc(sizeof(struct fname))) == NULL) { warnx("malloc() failed - %s", strerror(errno)); free(fstring); return (-1); } memset(entry, 0, sizeof(struct fname)); if (cut != NULL) asn_append_oid(&(entry->cut), cut); strlcpy(fstring, filename, strlen(filename) + 1); entry->name = fstring; entry->done = done; SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link); return (1); } void free_filelist(struct snmp_toolinfo *snmptoolctx) { struct fname *f; if (snmptoolctx == NULL) return; /* XXX error handling */ while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) { SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link); if (f->name) free(f->name); free(f); } } static char isvalid_fchar(char c, int pos) { if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' || (pos != 0 && isdigit(c))){ return (c); } if (c == '\0') return (0); if (!isascii(c) || !isprint(c)) warnx("Unexpected character %#2x", (u_int) c); else warnx("Illegal character '%c'", c); return (-1); } /* * Re-implement getsubopt from scratch, because the second argument is broken * and will not compile with WARNS=5. * Copied from src/contrib/bsnmp/snmpd/main.c. */ static int getsubopt1(char **arg, const char *const *options, char **valp, char **optp) { static const char *const delim = ",\t "; u_int i; char *ptr; *optp = NULL; /* Skip leading junk. */ for (ptr = *arg; *ptr != '\0'; ptr++) if (strchr(delim, *ptr) == NULL) break; if (*ptr == '\0') { *arg = ptr; return (-1); } *optp = ptr; /* Find the end of the option. */ while (*++ptr != '\0') if (strchr(delim, *ptr) != NULL || *ptr == '=') break; if (*ptr != '\0') { if (*ptr == '=') { *ptr++ = '\0'; *valp = ptr; while (*ptr != '\0' && strchr(delim, *ptr) == NULL) ptr++; if (*ptr != '\0') *ptr++ = '\0'; } else *ptr++ = '\0'; } *arg = ptr; for (i = 0; *options != NULL; options++, i++) if (strcmp(*optp, *options) == 0) return (i); return (-1); } static int32_t parse_path(char *value) { int32_t i, len; if (value == NULL) return (-1); for (len = 0; len < MAXPATHLEN; len++) { i = isvalid_fchar(*(value + len), len) ; if (i == 0) break; else if (i < 0) return (-1); } if (len >= MAXPATHLEN || value[len] != '\0') { warnx("Bad pathname - '%s'", value); return (-1); } return (len); } static int32_t parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path, const struct asn_oid *cut) { int32_t namelen; char filename[MAXPATHLEN + 1]; if (value == NULL) return (-1); do { memset(filename, 0, MAXPATHLEN + 1); if (isalpha(*value) && (path == NULL || path[0] == '\0')) { strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1); namelen = strlen(SNMP_DEFS_DIR); } else if (path != NULL){ strlcpy(filename, path, MAXPATHLEN + 1); namelen = strlen(path); } else namelen = 0; for ( ; namelen < MAXPATHLEN; value++) { if (isvalid_fchar(*value, namelen) > 0) { filename[namelen++] = *value; continue; } if (*value == ',' ) value++; else if (*value == '\0') ; else { if (!isascii(*value) || !isprint(*value)) warnx("Unexpected character %#2x in" " filename", (u_int) *value); else warnx("Illegal character '%c' in" " filename", *value); return (-1); } filename[namelen]='\0'; break; } if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) { warnx("Filename %s too long", filename); return (-1); } if (add_filename(snmptoolctx, filename, cut, 0) < 0) { warnx("Error adding file %s to list", filename); return (-1); } } while (*value != '\0'); return(1); } static int32_t parse_ascii(char *ascii, uint8_t *binstr, size_t binlen) { int32_t alen, count, saved_errno, i; uint32_t val; char dptr[3]; /* Filter 0x at the beginning */ if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x') i = 2; else i = 0; saved_errno = errno; errno = 0; for (count = 0; i < alen; i += 2) { /* XXX: consider strlen(ascii) % 2 != 0 */ dptr[0] = ascii[i]; dptr[1] = ascii[i + 1]; dptr[2] = '\0'; if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) { errno = saved_errno; return (-1); } binstr[count] = (uint8_t) val; if (++count >= binlen) { warnx("Key %s too long - truncating to %zu octets", ascii, binlen); break; } } return (count); } /* * Functions to parse common input options for client tools and fill in the * snmp_client structure. */ int32_t parse_authentication(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "proto", "key", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'proto' requires an argument"); return (-1); } if (strlen(val) != 3) { warnx("Unknown auth protocol - %s", val); return (-1); } if (strncasecmp("md5", val, strlen("md5")) == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; else if (strncasecmp("sha", val, strlen("sha")) == 0) snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; else { warnx("Unknown auth protocol - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'key' requires an argument"); return (-1); } if (parse_ascii(val, snmp_client.user.auth_key, SNMP_AUTH_KEY_SIZ) < 0) { warnx("Bad authentication key- %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_privacy(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "proto", "key", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'proto' requires an argument"); return (-1); } if (strlen(val) != 3) { warnx("Unknown privacy protocol - %s", val); return (-1); } if (strncasecmp("aes", val, strlen("aes")) == 0) snmp_client.user.priv_proto = SNMP_PRIV_AES; else if (strncasecmp("des", val, strlen("des")) == 0) snmp_client.user.priv_proto = SNMP_PRIV_DES; else { warnx("Unknown privacy protocol - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'key' requires an argument"); return (-1); } if (parse_ascii(val, snmp_client.user.priv_key, SNMP_PRIV_KEY_SIZ) < 0) { warnx("Bad privacy key- %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_context(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { int32_t count, subopt; char *val, *option; const char *const subopts[] = { "context", "context-engine", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'context' - no argument"); return (-1); } strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ); break; case 1: if (val == NULL) { warnx("Suboption 'context-engine' - no argument"); return (-1); } if ((snmp_client.clen = parse_ascii(val, snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) < 0) { warnx("Bad EngineID - %s", val); return (-1); } break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_user_security(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { int32_t count, subopt, saved_errno; char *val, *option; const char *const subopts[] = { "engine", "engine-boots", "engine-time", "name", NULL }; assert(opt_arg != NULL); count = 1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case 0: if (val == NULL) { warnx("Suboption 'engine' - no argument"); return (-1); } snmp_client.engine.engine_len = parse_ascii(val, snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ); if (snmp_client.engine.engine_len < 0) { warnx("Bad EngineID - %s", val); return (-1); } break; case 1: if (val == NULL) { warnx("Suboption 'engine-boots' - no argument"); return (-1); } saved_errno = errno; errno = 0; snmp_client.engine.engine_boots = strtoul(val, NULL, 10); if (errno != 0) { warnx("Bad 'engine-boots' value %s - %s", val, strerror(errno)); errno = saved_errno; return (-1); } errno = saved_errno; break; case 2: if (val == NULL) { warnx("Suboption 'engine-time' - no argument"); return (-1); } saved_errno = errno; errno = 0; snmp_client.engine.engine_time = strtoul(val, NULL, 10); if (errno != 0) { warnx("Bad 'engine-time' value %s - %s", val, strerror(errno)); errno = saved_errno; return (-1); } errno = saved_errno; break; case 3: strlcpy(snmp_client.user.sec_name, val, SNMP_ADM_STR32_SIZ); break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } count += 1; } return (2/* count */); } int32_t parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0) return (-1); return (2); } int32_t parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { char path[MAXPATHLEN + 1]; int32_t cut_dflt, len, subopt; struct asn_oid cut; char *val, *option; const char *const subopts[] = { "cut", "path", "file", NULL }; #define INC_CUT 0 #define INC_PATH 1 #define INC_LIST 2 assert(opt_arg != NULL); /* if (opt == 'i') free_filelist(snmptoolctx, ); */ /* * This function should be called only after getopt(3) - otherwise if * no previous validation of opt_arg strlen() may not return what is * expected. */ path[0] = '\0'; memset(&cut, 0, sizeof(struct asn_oid)); cut_dflt = -1; while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { switch (subopt) { case INC_CUT: if (val == NULL) { warnx("Suboption 'cut' requires an argument"); return (-1); } else { if (snmp_parse_numoid(val, &cut) < 0) return (-1); } cut_dflt = 1; break; case INC_PATH: if ((len = parse_path(val)) < 0) return (-1); strlcpy(path, val, len + 1); break; case INC_LIST: if (val == NULL) return (-1); if (cut_dflt == -1) len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID); else len = parse_flist(snmptoolctx, val, path, &cut); if (len < 0) return (-1); break; default: warnx("Unknown suboption - '%s'", suboptarg); return (-1); } } /* XXX: Fix me - returning two is wrong here */ return (2); } int32_t parse_server(char *opt_arg) { assert(opt_arg != NULL); if (snmp_parse_server(&snmp_client, opt_arg) < 0) return (-1); if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) { if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL + 1))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL); } return (2); } int32_t parse_timeout(char *opt_arg) { int32_t v, saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtol(opt_arg, NULL, 10); if (errno != 0) { warnx( "Error parsing timeout value - %s", strerror(errno)); errno = saved_errno; return (-1); } snmp_client.timeout.tv_sec = v; errno = saved_errno; return (2); } int32_t parse_retry(char *opt_arg) { uint32_t v; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtoul(opt_arg, NULL, 10); if (errno != 0) { warnx("Error parsing retries count - %s", strerror(errno)); errno = saved_errno; return (-1); } snmp_client.retries = v; errno = saved_errno; return (2); } int32_t parse_version(char *opt_arg) { uint32_t v; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; v = strtoul(opt_arg, NULL, 10); if (errno != 0) { warnx("Error parsing version - %s", strerror(errno)); errno = saved_errno; return (-1); } switch (v) { case 1: snmp_client.version = SNMP_V1; break; case 2: snmp_client.version = SNMP_V2c; break; case 3: snmp_client.version = SNMP_V3; break; default: warnx("Unsupported SNMP version - %u", v); errno = saved_errno; return (-1); } errno = saved_errno; return (2); } int32_t parse_local_path(char *opt_arg) { assert(opt_arg != NULL); if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) { warnx("Filename too long - %s", opt_arg); return (-1); } strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH)); return (2); } int32_t parse_buflen(char *opt_arg) { uint32_t size; int32_t saved_errno; assert(opt_arg != NULL); saved_errno = errno; errno = 0; size = strtoul(opt_arg, NULL, 10); if (errno != 0) { warnx("Error parsing buffer size - %s", strerror(errno)); errno = saved_errno; return (-1); } if (size > MAX_BUFF_SIZE) { warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE); errno = saved_errno; return (-1); } snmp_client.txbuflen = snmp_client.rxbuflen = size; errno = saved_errno; return (2); } int32_t parse_debug(void) { snmp_client.dump_pdus = 1; return (1); } int32_t parse_discovery(struct snmp_toolinfo *snmptoolctx) { SET_EDISCOVER(snmptoolctx); snmp_client.version = SNMP_V3; return (1); } int32_t parse_local_key(struct snmp_toolinfo *snmptoolctx) { SET_LOCALKEY(snmptoolctx); snmp_client.version = SNMP_V3; return (1); } int32_t parse_num_oids(struct snmp_toolinfo *snmptoolctx) { SET_NUMERIC(snmptoolctx); return (1); } int32_t parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (strlen(opt_arg) > strlen("verbose")) { warnx( "Invalid output option - %s",opt_arg); return (-1); } if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_SHORT); else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE); else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR); else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0) SET_OUTPUT(snmptoolctx, OUTPUT_QUIET); else { warnx( "Invalid output option - %s", opt_arg); return (-1); } return (2); } int32_t parse_errors(struct snmp_toolinfo *snmptoolctx) { SET_RETRY(snmptoolctx); return (1); } int32_t parse_skip_access(struct snmp_toolinfo *snmptoolctx) { SET_ERRIGNORE(snmptoolctx); return (1); } char * snmp_parse_suboid(char *str, struct asn_oid *oid) { char *endptr; asn_subid_t suboid; if (*str == '.') str++; if (*str < '0' || *str > '9') return (str); do { suboid = strtoul(str, &endptr, 10); if ((asn_subid_t) suboid > ASN_MAXID) { warnx("Suboid %u > ASN_MAXID", suboid); return (NULL); } if (snmp_suboid_append(oid, suboid) < 0) return (NULL); str = endptr + 1; } while (*endptr == '.'); return (endptr); } static char * snmp_int2asn_oid(char *str, struct asn_oid *oid) { char *endptr; int32_t v, saved_errno; saved_errno = errno; errno = 0; v = strtol(str, &endptr, 10); if (errno != 0) { warnx("Integer value %s not supported - %s", str, strerror(errno)); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } /* It is a bit weird to have a table indexed by OID but still... */ static char * snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str, struct asn_oid *oid) { int32_t i; char string[MAXSTR], *endptr; struct snmp_object obj; for (i = 0; i < MAXSTR; i++) if (isalpha (*(str + i)) == 0) break; endptr = str + i; memset(&obj, 0, sizeof(struct snmp_object)); if (i == 0) { if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0) return (NULL); } else { strlcpy(string, str, i + 1); string[i] = '\0'; if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { - warnx("Unknown string - %s",string); + warnx("Unknown string - %s", string); return (NULL); } - free(string); } asn_append_oid(oid, &(obj.val.var)); return (endptr); } static char * snmp_ip2asn_oid(char *str, struct asn_oid *oid) { uint32_t v; int32_t i; char *endptr, *ptr; ptr = str; for (i = 0; i < 4; i++) { v = strtoul(ptr, &endptr, 10); if (v > 0xff) return (NULL); if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); ptr = endptr + 1; } return (endptr); } /* 32-bit counter, gauge, timeticks. */ static char * snmp_uint2asn_oid(char *str, struct asn_oid *oid) { char *endptr; uint32_t v; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoul(str, &endptr, 10); if (errno != 0) { warnx("Integer value %s not supported - %s\n", str, strerror(errno)); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) return (NULL); return (endptr); } static char * snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid) { char *endptr; uint64_t v; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoull(str, &endptr, 10); if (errno != 0) { warnx("Integer value %s not supported - %s", str, strerror(errno)); errno = saved_errno; return (NULL); } errno = saved_errno; if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0) return (NULL); if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0) return (NULL); return (endptr); } enum snmp_syntax parse_syntax(char *str) { int32_t i; for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) { if (strncmp(syntax_strings[i].str, str, strlen(syntax_strings[i].str)) == 0) return (syntax_strings[i].stx); } return (SNMP_SYNTAX_NULL); } static char * snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str, struct index *idx, struct snmp_object *object) { char *ptr; int32_t i; enum snmp_syntax stx; char syntax[MAX_CMD_SYNTAX_LEN]; ptr = str; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) { if (*(ptr + i) == ':') break; } if (i >= MAX_CMD_SYNTAX_LEN) { warnx("Unknown syntax in OID - %s", str); return (NULL); } /* Expect a syntax string here. */ if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { warnx("Invalid syntax - %s",syntax); return (NULL); } if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) { warnx("Syntax mismatch - %d expected, %d given", idx->syntax, stx); return (NULL); } /* * That is where the suboid started + the syntax length + one * character for ':'. */ ptr = str + i + 1; } else stx = idx->syntax; switch (stx) { case SNMP_SYNTAX_INTEGER: return (snmp_int2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_OID: return (snmp_oid2asn_oid(snmptoolctx, ptr, &(object->val.var))); case SNMP_SYNTAX_IPADDRESS: return (snmp_ip2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_COUNTER: /* FALLTHROUGH */ case SNMP_SYNTAX_GAUGE: /* FALLTHROUGH */ case SNMP_SYNTAX_TIMETICKS: return (snmp_uint2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_COUNTER64: return (snmp_cnt64_2asn_oid(ptr, &(object->val.var))); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oid(idx->tc, ptr, &(object->val.var))); default: /* NOTREACHED */ break; } return (NULL); } char * snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str, struct snmp_object *object) { char *ptr; struct index *temp; if (object->info->table_idx == NULL) return (NULL); ptr = NULL; STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) { if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object)) == NULL) return (NULL); if (*ptr != ',' && *ptr != ']') return (NULL); str = ptr + 1; } if (ptr == NULL || *ptr != ']') { warnx("Mismatching index - %s", str); return (NULL); } return (ptr + 1); } /* * Fill in the struct asn_oid member of snmp_value with suboids from input. * If an error occurs - print message on stderr and return (-1). * If all is ok - return the length of the oid. */ int32_t snmp_parse_numoid(char *argv, struct asn_oid *var) { char *endptr, *str; asn_subid_t suboid; str = argv; if (*str == '.') str++; do { if (var->len == ASN_MAXOIDLEN) { warnx("Oid too long - %u", var->len); return (-1); } suboid = strtoul(str, &endptr, 10); if (suboid > ASN_MAXID) { warnx("Oid too long - %u", var->len); return (-1); } var->subs[var->len++] = suboid; str = endptr + 1; } while ( *endptr == '.'); if (*endptr != '\0') { warnx("Invalid oid string - %s", argv); return (-1); } return (var->len); } /* Append a length 1 suboid to an asn_oid structure. */ int32_t snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid) { if (var == NULL) return (-1); if (var->len >= ASN_MAXOIDLEN) { warnx("Oid too long - %u", var->len); return (-1); } var->subs[var->len++] = suboid; return (1); } /* Pop the last suboid from an asn_oid structure. */ int32_t snmp_suboid_pop(struct asn_oid *var) { asn_subid_t suboid; if (var == NULL) return (-1); if (var->len < 1) return (-1); suboid = var->subs[--(var->len)]; var->subs[var->len] = 0; return (suboid); } /* * Parse the command-line provided string into an OID - alocate memory for a new * snmp object, fill in its fields and insert it in the object list. A * (snmp_verify_inoid_f) function must be provided to validate the input string. */ int32_t snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func, char *string) { struct snmp_object *obj; if (snmptoolctx == NULL) return (-1); /* XXX-BZ does that chack make sense? */ if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) { warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1); return (-1); } if ((obj = malloc(sizeof(struct snmp_object))) == NULL) { syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); return (-1); } memset(obj, 0, sizeof(struct snmp_object)); if (func(snmptoolctx, obj, string) < 0) { warnx("Invalid OID - %s", string); free(obj); return (-1); } snmptoolctx->objects++; SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link); return (1); } /* Given an OID, find it in the object list and remove it. */ int32_t snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) { struct snmp_object *temp; if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) { warnx("Object list already empty"); return (-1); } SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link) if (asn_compare_oid(&(temp->val.var), oid) == 0) break; if (temp == NULL) { warnx("No such object in list"); return (-1); } SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link); if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING && temp->val.v.octetstring.octets != NULL) free(temp->val.v.octetstring.octets); free(temp); return (1); } static void snmp_object_freeall(struct snmp_toolinfo *snmptoolctx) { struct snmp_object *o; while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) { SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link); if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING && o->val.v.octetstring.octets != NULL) free(o->val.v.octetstring.octets); free(o); } } /* Do all possible memory release before exit. */ void snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx) { if (snmp_client.chost != NULL) { free(snmp_client.chost); snmp_client.chost = NULL; } if (snmp_client.cport != NULL) { free(snmp_client.cport); snmp_client.cport = NULL; } snmp_mapping_free(snmptoolctx); free_filelist(snmptoolctx); snmp_object_freeall(snmptoolctx); if (snmptoolctx->passwd != NULL) { free(snmptoolctx->passwd); snmptoolctx->passwd = NULL; } } /* * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f) * function should check whether the variable is consistent in this PDU * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the * function actually adds the variable to the PDU and must not be NULL. */ int32_t snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx, snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc, struct snmp_pdu *pdu, int32_t maxcount) { int32_t nbindings, abind; struct snmp_object *obj; if (pdu == NULL || afunc == NULL) return (-1); /* Return 0 in case of no more work todo. */ if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) return (0); if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) { warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS"); return (-1); } nbindings = 0; SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) { if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) { nbindings = -1; break; } if ((abind = afunc(pdu, obj)) < 0) { nbindings = -1; break; } if (abind > 0) { /* Do not put more varbindings than requested. */ if (++nbindings >= maxcount) break; } } return (nbindings); } /* * Locate an object in the object list and set a corresponding error status. */ int32_t snmp_object_seterror(struct snmp_toolinfo *snmptoolctx, struct snmp_value *err_value, int32_t error_status) { struct snmp_object *obj; if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL) return (-1); SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) { obj->error = error_status; return (1); } return (0); } /* * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request * but don't compare syntaxes - when sending a request PDU they must be null. * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes * checks and some other checks skipped. */ int32_t snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { uint32_t i; for (i = 0; i < req->nbindings; i++) { if (asn_compare_oid(&req->bindings[i].var, &resp->bindings[i].var) != 0) { warnx("Bad OID in response"); return (-1); } if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax == SNMP_SYNTAX_NOSUCHINSTANCE)) return (0); } return (1); } int32_t snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { int32_t N, R, M, r; if (req->error_status > (int32_t) resp->nbindings) { warnx("Bad number of bindings in response"); return (-1); } for (N = 0; N < req->error_status; N++) { if (asn_is_suboid(&req->bindings[N].var, &resp->bindings[N].var) == 0) return (0); if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); } for (R = N , r = N; R < (int32_t) req->nbindings; R++) { for (M = 0; M < req->error_index && (r + M) < (int32_t) resp->nbindings; M++) { if (asn_is_suboid(&req->bindings[R].var, &resp->bindings[r + M].var) == 0) return (0); if (resp->bindings[r + M].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) { M++; break; } } r += M; } return (0); } int32_t snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { uint32_t i; for (i = 0; i < req->nbindings; i++) { if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var) == 0) return (0); if (resp->version != SNMP_V1 && resp->bindings[i].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) return (0); } return (1); } /* * Should be called to check a response to get/getnext/getbulk. */ int32_t snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req) { if (resp == NULL || req == NULL) return (-2); if (resp->version != req->version) { warnx("Response has wrong version"); return (-1); } if (resp->error_status == SNMP_ERR_NOSUCHNAME) { warnx("Error - No Such Name"); return (0); } if (resp->error_status != SNMP_ERR_NOERROR) { warnx("Error %d in response", resp->error_status); return (-1); } if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){ warnx("Bad number of bindings in response"); return (-1); } switch (req->type) { case SNMP_PDU_GET: return (snmp_parse_get_resp(resp,req)); case SNMP_PDU_GETBULK: return (snmp_parse_getbulk_resp(resp,req)); case SNMP_PDU_GETNEXT: return (snmp_parse_getnext_resp(resp,req)); default: /* NOTREACHED */ break; } return (-2); } static void snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, uint32_t len, uint8_t *octets) { char *buf; if (len == 0 || octets == NULL) return; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OCTETSTRING].str); if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) { fprintf(stdout, "%s", buf); free(buf); } } static void snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, struct asn_oid *oid) { uint32_t i; uint8_t *s; if ((s = malloc(oid->subs[0] + 1)) == NULL) syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); else { for (i = 0; i < oid->subs[0]; i++) s[i] = (u_char) (oid->subs[i + 1]); snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s); free(s); } } /* * Check and output syntax type and value. */ static void snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) { char oid_string[ASN_OIDSTRLEN]; struct snmp_object obj; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str); if(!ISSET_NUMERIC(snmptoolctx)) { memset(&obj, 0, sizeof(struct snmp_object)); asn_append_oid(&(obj.val.var), oid); if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0) fprintf(stdout, "%s" , obj.info->string); else { (void) asn_oid2str_r(oid, oid_string); fprintf(stdout, "%s", oid_string); } } else { (void) asn_oid2str_r(oid, oid_string); fprintf(stdout, "%s", oid_string); } } static void snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums, int32_t int_val) { char *string; if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_INTEGER].str); if (enums != NULL && (string = enum_string_lookup(enums, int_val)) != NULL) fprintf(stdout, "%s", string); else fprintf(stdout, "%d", int_val); } static void snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_IPADDRESS].str); fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); } static void snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_COUNTER].str); fprintf(stdout, "%u", counter); } static void snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str); fprintf(stdout, "%u", gauge); } static void snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_TIMETICKS].str); fprintf(stdout, "%u", ticks); } static void snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64) { if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_COUNTER64].str); fprintf(stdout,"%ju", counter64); } int32_t snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val, struct snmp_oid2str *entry) { if (val == NULL) return (-1); if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) fprintf(stdout, " = "); switch (val->syntax) { case SNMP_SYNTAX_INTEGER: if (entry != NULL) snmp_output_int(snmptoolctx, entry->snmp_enum, val->v.integer); else snmp_output_int(snmptoolctx, NULL, val->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: if (entry != NULL) snmp_output_octetstring(snmptoolctx, entry->tc, val->v.octetstring.len, val->v.octetstring.octets); else snmp_output_octetstring(snmptoolctx, SNMP_STRING, val->v.octetstring.len, val->v.octetstring.octets); break; case SNMP_SYNTAX_OID: snmp_output_oid_value(snmptoolctx, &(val->v.oid)); break; case SNMP_SYNTAX_IPADDRESS: snmp_output_ipaddress(snmptoolctx, val->v.ipaddress); break; case SNMP_SYNTAX_COUNTER: snmp_output_counter(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_GAUGE: snmp_output_gauge(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_TIMETICKS: snmp_output_ticks(snmptoolctx, val->v.uint32); break; case SNMP_SYNTAX_COUNTER64: snmp_output_counter64(snmptoolctx, val->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: fprintf(stdout, "No Such Object\n"); return (val->syntax); case SNMP_SYNTAX_NOSUCHINSTANCE: fprintf(stdout, "No Such Instance\n"); return (val->syntax); case SNMP_SYNTAX_ENDOFMIBVIEW: fprintf(stdout, "End of Mib View\n"); return (val->syntax); case SNMP_SYNTAX_NULL: /* NOTREACHED */ fprintf(stdout, "agent returned NULL Syntax\n"); return (val->syntax); default: /* NOTREACHED - If here - then all went completely wrong. */ fprintf(stdout, "agent returned unknown syntax\n"); return (-1); } fprintf(stdout, "\n"); return (0); } static int32_t snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, struct snmp_value *val) { int32_t rc; asn_subid_t suboid; if (obj == NULL || val == NULL) return (-1); if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID) return (-1); memset(obj, 0, sizeof(struct snmp_object)); asn_append_oid(&(obj->val.var), &(val->var)); obj->val.syntax = val->syntax; if (obj->val.syntax > 0) rc = snmp_lookup_leafstring(snmptoolctx, obj); else rc = snmp_lookup_nonleaf_string(snmptoolctx, obj); (void) snmp_suboid_append(&(val->var), suboid); (void) snmp_suboid_append(&(obj->val.var), suboid); return (rc); } static int32_t snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx, struct asn_oid *oid) { uint8_t ip[4]; uint32_t bytes = 1; uint64_t cnt64; struct asn_oid temp, out; if (oid->len < bytes) return (-1); memset(&temp, 0, sizeof(struct asn_oid)); asn_append_oid(&temp, oid); switch (stx->syntax) { case SNMP_SYNTAX_INTEGER: snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]); break; case SNMP_SYNTAX_OCTETSTRING: if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] > ASN_MAXOCTETSTRING)) return (-1); snmp_output_octetindex(snmptoolctx, stx->tc, &temp); bytes += temp.subs[0]; break; case SNMP_SYNTAX_OID: if ((temp.subs[0] > temp.len -1) || (temp.subs[0] > ASN_MAXOIDLEN)) return (-1); bytes += temp.subs[0]; memset(&out, 0, sizeof(struct asn_oid)); asn_slice_oid(&out, &temp, 1, bytes); snmp_output_oid_value(snmptoolctx, &out); break; case SNMP_SYNTAX_IPADDRESS: if (temp.len < 4) return (-1); for (bytes = 0; bytes < 4; bytes++) ip[bytes] = temp.subs[bytes]; snmp_output_ipaddress(snmptoolctx, ip); bytes = 4; break; case SNMP_SYNTAX_COUNTER: snmp_output_counter(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_GAUGE: snmp_output_gauge(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_TIMETICKS: snmp_output_ticks(snmptoolctx, temp.subs[0]); break; case SNMP_SYNTAX_COUNTER64: if (oid->len < 2) return (-1); bytes = 2; memcpy(&cnt64, temp.subs, bytes); snmp_output_counter64(snmptoolctx, cnt64); break; default: return (-1); } return (bytes); } static int32_t snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o) { int32_t i, first, len; struct asn_oid oid; struct index *temp; if (ISSET_NUMERIC(snmptoolctx)) return (-1); if (o->info->table_idx == NULL) { fprintf(stdout,"%s.%d", o->info->string, o->val.var.subs[o->val.var.len - 1]); return (1); } fprintf(stdout,"%s[", o->info->string); memset(&oid, 0, sizeof(struct asn_oid)); len = 1; asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), o->val.var.len); first = 1; STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) { if(first) first = 0; else fprintf(stdout, ", "); if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0) break; len += i; memset(&oid, 0, sizeof(struct asn_oid)); asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), o->val.var.len + 1); } fprintf(stdout,"]"); return (1); } void snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) { char buf[ASN_OIDSTRLEN]; struct snmp_object object; if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) { fprintf(stdout,"Invalid error index in PDU\n"); return; } fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost, snmp_client.cport); if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, &(pdu->bindings[pdu->error_index - 1])) > 0)) snmp_output_object(snmptoolctx, &object); else { asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf); fprintf(stdout,"%s", buf); } fprintf(stdout," caused error - "); if ((pdu->error_status > 0) && (pdu->error_status <= SNMP_ERR_INCONS_NAME)) fprintf(stdout, "%s\n", error_strings[pdu->error_status].str); else fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str); } int32_t snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct asn_oid *root) { int32_t error; char p[ASN_OIDSTRLEN]; uint32_t i; struct snmp_object object; i = error = 0; while (i < pdu->nbindings) { if (root != NULL && !(asn_is_suboid(root, &(pdu->bindings[i].var)))) break; if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) { if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, &(pdu->bindings[i])) > 0)) snmp_output_object(snmptoolctx, &object); else { asn_oid2str_r(&(pdu->bindings[i].var), p); fprintf(stdout, "%s", p); } } error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info); i++; } if (error) return (-1); return (i); } void snmp_output_engine(void) { uint32_t i; char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; cptr = engine; for (i = 0; i < snmp_client.engine.engine_len; i++) cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]); *cptr++ = '\0'; fprintf(stdout, "Engine ID 0x%s\n", engine); fprintf(stdout, "Boots : %u\t\tTime : %d\n", snmp_client.engine.engine_boots, snmp_client.engine.engine_time); } void snmp_output_keys(void) { uint32_t i, keylen = 0; char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2]; fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name); if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) { fprintf(stdout, "MD5 : 0x"); keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) { fprintf(stdout, "SHA : 0x"); keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; } if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) { cptr = extkey; for (i = 0; i < keylen; i++) cptr += sprintf(cptr, "%.2x", snmp_client.user.auth_key[i]); *cptr++ = '\0'; fprintf(stdout, "%s\n", extkey); } if (snmp_client.user.priv_proto == SNMP_PRIV_DES) { fprintf(stdout, "DES : 0x"); keylen = SNMP_PRIV_DES_KEY_SIZ; } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) { fprintf(stdout, "AES : 0x"); keylen = SNMP_PRIV_AES_KEY_SIZ; } if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) { cptr = extkey; for (i = 0; i < keylen; i++) cptr += sprintf(cptr, "%.2x", snmp_client.user.priv_key[i]); *cptr++ = '\0'; fprintf(stdout, "%s\n", extkey); } } Index: stable/10 =================================================================== --- stable/10 (revision 299015) +++ stable/10 (revision 299016) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r298451-298452,298507,298750