/*- * 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. * * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents, * bsnmpset can be used to set MIB objects in an agent. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bsnmptc.h" #include "bsnmptools.h" static const char *program_name = NULL; static enum program_e { BSNMPGET, BSNMPWALK, BSNMPSET } program; /* ***************************************************************************** * Common bsnmptools functions. */ static void usage(void) { fprintf(stderr, "Usage:\n" "%s %s [-A options] [-b buffersize] [-C options] [-I options]\n" "\t[-i filelist] [-l filename]%s [-o output] [-P options]\n" "\t%s[-r retries] [-s [trans::][community@][server][:port]]\n" "\t[-t timeout] [-U options] [-v version]%s\n", program_name, (program == BSNMPGET) ? "[-aDdehnK]" : (program == BSNMPWALK) ? "[-dhnK]" : (program == BSNMPSET) ? "[-adehnK]" : "", (program == BSNMPGET || program == BSNMPWALK) ? " [-M max-repetitions] [-N non-repeaters]" : "", (program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "", (program == BSNMPGET) ? " OID [OID ...]" : (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" : "" ); } static int32_t parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { uint32_t v; assert(opt_arg != NULL); v = strtoul(opt_arg, (void *) NULL, 10); if (v > SNMP_MAX_BINDINGS) { warnx("Max repetitions value greater than %d maximum allowed.", SNMP_MAX_BINDINGS); return (-1); } SET_MAXREP(snmptoolctx, v); return (2); } static int32_t parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { uint32_t v; assert(opt_arg != NULL); v = strtoul(opt_arg, (void *) NULL, 10); if (v > SNMP_MAX_BINDINGS) { warnx("Non repeaters value greater than %d maximum allowed.", SNMP_MAX_BINDINGS); return (-1); } SET_NONREP(snmptoolctx, v); return (2); } static int32_t parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg) { assert(opt_arg != NULL); if (strcasecmp(opt_arg, "getbulk") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK); else if (strcasecmp(opt_arg, "getnext") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT); else if (strcasecmp(opt_arg, "get") == 0) SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET); else { warnx("PDU type '%s' not supported.", opt_arg); return (-1); } return (2); } static int32_t snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv) { int32_t count, optnum = 0; int ch; const char *opts; switch (program) { case BSNMPWALK: opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; break; case BSNMPGET: opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:"; break; case BSNMPSET: opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:"; break; default: return (-1); } while ((ch = getopt(argc, argv, opts)) != EOF) { switch (ch) { case 'A': count = parse_authentication(snmptoolctx, optarg); break; case 'a': count = parse_skip_access(snmptoolctx); break; case 'b': count = parse_buflen(optarg); break; case 'D': count = parse_discovery(snmptoolctx); break; case 'd': count = parse_debug(); break; case 'e': count = parse_errors(snmptoolctx); break; case 'h': usage(); return (-2); case 'C': count = parse_context(snmptoolctx, optarg); break; case 'I': count = parse_include(snmptoolctx, optarg); break; case 'i': count = parse_file(snmptoolctx, optarg); break; case 'K': count = parse_local_key(snmptoolctx); break; case 'l': count = parse_local_path(optarg); break; case 'M': count = parse_max_repetitions(snmptoolctx, optarg); break; case 'N': count = parse_non_repeaters(snmptoolctx, optarg); break; case 'n': count = parse_num_oids(snmptoolctx); break; case 'o': count = parse_output(snmptoolctx, optarg); break; case 'P': count = parse_privacy(snmptoolctx, optarg); break; case 'p': count = parse_pdu_type(snmptoolctx, optarg); break; case 'r': count = parse_retry(optarg); break; case 's': count = parse_server(optarg); break; case 't': count = parse_timeout(optarg); break; case 'U': count = parse_user_security(snmptoolctx, optarg); break; case 'v': count = parse_version(optarg); break; case '?': default: usage(); return (-1); } if (count < 0) return (-1); optnum += count; } return (optnum); } /* * Read user input OID - one of following formats: * 1) 1.2.1.1.2.1.0 - that is if option numeric was given; * 2) string - in such case append .0 to the asn_oid subs; * 3) string.1 - no additional processing required in such case. */ static char * snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { char string[MAXSTR], *str; int32_t i = 0; struct asn_oid in_oid; str = argv; if (*str == '.') str++; while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) { str++; i++; } if (i <= 0 || i >= MAXSTR) return (NULL); memset(&in_oid, 0, sizeof(struct asn_oid)); if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) { warnx("Invalid OID - %s", argv); return (NULL); } strlcpy(string, argv, i + 1); if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) { warnx("No entry for %s in mapping lists", string); return (NULL); } /* If OID given on command line append it. */ if (in_oid.len > 0) asn_append_oid(&(obj->val.var), &in_oid); else if (*str == '[') { if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL) return (NULL); } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GET) { if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0) return (NULL); } return (str); } static int32_t snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { if (argv == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) { if (snmp_parse_numoid(argv, &(obj->val.var)) < 0) return (-1); } else { if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL && snmp_parse_numoid(argv, &(obj->val.var)) < 0) return (-1); } return (1); } static int32_t snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) { if (obj->error > 0) return (0); asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); pdu->nbindings++; return (pdu->nbindings); } /* ***************************************************************************** * bsnmpget private functions. */ static int32_t snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->version == SNMP_V1 && obj->val.syntax == SNMP_SYNTAX_COUNTER64) { warnx("64-bit counters are not supported in SNMPv1 PDU"); return (-1); } if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT || pdu->type == SNMP_PDU_GETBULK) return (1); if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) { warnx("Only leaf object values can be added to GET PDU"); return (-1); } return (1); } /* * In case of a getbulk PDU, the error_status and error_index fields are used by * libbsnmp to hold the values of the non-repeaters and max-repetitions fields * that are present only in the getbulk - so before sending the PDU make sure * these have correct values as well. */ static void snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep) { assert(pdu != NULL); if (pdu->nbindings < non_rep) pdu->error_status = pdu->nbindings; else pdu->error_status = non_rep; if (max_rep > 0) pdu->error_index = max_rep; else pdu->error_index = 1; } static int snmptool_get(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind, snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); if (snmp_dialog(&req, &resp) == -1) { warn("Snmp dialog"); break; } if (snmp_parse_resp(&resp, &req) >= 0) { snmp_output_resp(snmptoolctx, &resp, NULL); snmp_pdu_free(&resp); break; } snmp_output_err_resp(snmptoolctx, &resp); if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK || !ISSET_RETRY(snmptoolctx)) { snmp_pdu_free(&resp); break; } /* * Loop through the object list and set object->error to the * varbinding that caused the error. */ if (snmp_object_seterror(snmptoolctx, &(resp.bindings[resp.error_index - 1]), resp.error_status) <= 0) { snmp_pdu_free(&resp); break; } fprintf(stderr, "Retrying...\n"); snmp_pdu_free(&resp); snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx)); } snmp_pdu_free(&req); return (0); } /* ***************************************************************************** * bsnmpwalk private functions. */ /* The default tree to walk. */ static const struct asn_oid snmp_mibII_OID = { 6 , { 1, 3, 6, 1, 2, 1 } }; static int32_t snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused, struct snmp_object *obj, char *string __unused) { asn_append_oid(&(obj->val.var), &snmp_mibII_OID); return (1); } /* * Prepare the next GetNext/Get PDU to send. */ static void snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu) { snmp_pdu_create(pdu, op); asn_append_oid(&(pdu->bindings[0].var), var); pdu->nbindings = 1; } static int snmptool_walk(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; struct asn_oid root; /* Keep the initial oid. */ int32_t outputs, rc; uint32_t op; if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK) op = SNMP_PDU_GETBULK; else op = SNMP_PDU_GETNEXT; snmp_pdu_create(&req, op); while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL, snmptool_add_vbind, &req, 1)) > 0) { /* Remember the root where the walk started from. */ memset(&root, 0, sizeof(struct asn_oid)); asn_append_oid(&root, &(req.bindings[0].var)); if (op == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); outputs = 0; while (snmp_dialog(&req, &resp) >= 0) { if ((snmp_parse_resp(&resp, &req)) < 0) { snmp_output_err_resp(snmptoolctx, &resp); snmp_pdu_free(&resp); outputs = -1; break; } rc = snmp_output_resp(snmptoolctx, &resp, &root); if (rc < 0) { snmp_pdu_free(&resp); outputs = -1; break; } outputs += rc; if ((u_int)rc < resp.nbindings) { snmp_pdu_free(&resp); break; } snmpwalk_nextpdu_create(op, &(resp.bindings[resp.nbindings - 1].var), &req); if (op == SNMP_PDU_GETBULK) snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx), GET_NONREP(snmptoolctx)); snmp_pdu_free(&resp); } /* Just in case our root was a leaf. */ if (outputs == 0) { snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req); if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) { if (snmp_parse_resp(&resp, &req) < 0) snmp_output_err_resp(snmptoolctx, &resp); else snmp_output_resp(snmptoolctx, &resp, NULL); snmp_pdu_free(&resp); } else warn("Snmp dialog"); } if (snmp_object_remove(snmptoolctx, &root) < 0) { warnx("snmp_object_remove"); break; } snmp_pdu_free(&req); snmp_pdu_create(&req, op); } snmp_pdu_free(&req); if (rc == 0) return (0); else return (1); } /* ***************************************************************************** * bsnmpset private functions. */ static int32_t parse_oid_numeric(struct snmp_value *value, char *val) { char *endptr; int32_t saved_errno; asn_subid_t suboid; do { saved_errno = errno; errno = 0; suboid = strtoul(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } errno = saved_errno; if ((asn_subid_t) suboid > ASN_MAXID) { warnx("Suboid %u > ASN_MAXID", suboid); return (-1); } if (snmp_suboid_append(&(value->v.oid), suboid) < 0) return (-1); val = endptr + 1; } while (*endptr == '.'); if (*endptr != '\0') warnx("OID value %s not supported", val); value->syntax = SNMP_SYNTAX_OID; return (0); } /* * Allow OID leaf in both forms: * 1) 1.3.6.1.2... -> in such case call directly the function reading raw OIDs; * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that. */ static int32_t parse_oid_string(struct snmp_toolinfo *snmptoolctx, struct snmp_value *value, char *string) { struct snmp_object obj; if (isdigit(string[0])) return (parse_oid_numeric(value, string)); memset(&obj, 0, sizeof(struct snmp_object)); if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { warnx("Unknown OID enum string - %s", string); return (-1); } asn_append_oid(&(value->v.oid), &(obj.val.var)); return (1); } static int32_t parse_ip(struct snmp_value * value, char * val) { char *endptr, *str; int32_t i; uint32_t v; str = val; for (i = 0; i < 4; i++) { v = strtoul(str, &endptr, 10); if (v > 0xff) return (-1); if (*endptr != '.' && *endptr != '\0' && i != 3) break; str = endptr + 1; value->v.ipaddress[i] = (uint8_t) v; } value->syntax = SNMP_SYNTAX_IPADDRESS; return (0); } static int32_t parse_int(struct snmp_value *value, char *val) { char *endptr; int32_t v, saved_errno; saved_errno = errno; errno = 0; v = strtol(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } value->syntax = SNMP_SYNTAX_INTEGER; value->v.integer = v; errno = saved_errno; return (0); } static int32_t parse_int_string(struct snmp_object *object, char *val) { int32_t v; if (isdigit(val[0])) return ((parse_int(&(object->val), val))); if (object->info == NULL) { warnx("Unknown enumerated integer type - %s", val); return (-1); } if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0) warnx("Unknown enumerated integer type - %s", val); object->val.v.integer = v; return (1); } /* * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, * SNMP_SYNTAX_TIMETICKS. */ static int32_t parse_uint(struct snmp_value *value, char *val) { char *endptr; uint32_t v = 0; int32_t saved_errno; saved_errno = errno; errno = 0; v = strtoul(val, &endptr, 10); if (errno != 0) { warn("Value %s not supported", val); errno = saved_errno; return (-1); } value->v.uint32 = v; errno = saved_errno; return (0); } static int32_t parse_ticks(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_TIMETICKS; return (0); } static int32_t parse_gauge(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_GAUGE; return (0); } static int32_t parse_counter(struct snmp_value *value, char *val) { if (parse_uint(value, val) < 0) return (-1); value->syntax = SNMP_SYNTAX_COUNTER; return (0); } static int32_t parse_uint64(struct snmp_value *value, char *val) { char *endptr; int32_t saved_errno; uint64_t v; saved_errno = errno; errno = 0; v = strtoull(val, &endptr, 10); if (errno != 0) { warnx("Value %s not supported", val); errno = saved_errno; return (-1); } value->syntax = SNMP_SYNTAX_COUNTER64; value->v.counter64 = v; errno = saved_errno; return (0); } static int32_t parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val) { switch (syntax) { case SNMP_SYNTAX_INTEGER: return (parse_int(value, val)); case SNMP_SYNTAX_IPADDRESS: return (parse_ip(value, val)); case SNMP_SYNTAX_COUNTER: return (parse_counter(value, val)); case SNMP_SYNTAX_GAUGE: return (parse_gauge(value, val)); case SNMP_SYNTAX_TIMETICKS: return (parse_ticks(value, val)); case SNMP_SYNTAX_COUNTER64: return (parse_uint64(value, val)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oct(SNMP_STRING, value, val)); case SNMP_SYNTAX_OID: return (parse_oid_numeric(value, val)); default: /* NOTREACHED */ break; } return (-1); } /* * Parse a command line argument of type OID=syntax:value and fill in whatever * fields can be derived from the input into snmp_value structure. Reads numeric * OIDs. */ static int32_t parse_pair_numoid_val(char *str, struct snmp_value *snmp_val) { int32_t cnt; char *ptr; enum snmp_syntax syntax; char oid_str[ASN_OIDSTRLEN]; ptr = str; for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++) if (ptr[cnt] == '=') break; if (cnt >= ASN_OIDSTRLEN) { warnx("OID too long - %s", str); return (-1); } strlcpy(oid_str, ptr, (size_t) (cnt + 1)); ptr = str + cnt + 1; for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++) if(ptr[cnt] == ':') break; if (cnt >= MAX_CMD_SYNTAX_LEN) { warnx("Unknown syntax in OID - %s", str); return (-1); } if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) { warnx("Unknown syntax in OID - %s", ptr); return (-1); } ptr = ptr + cnt + 1; for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++) if (ptr[cnt] == '\0') break; if (ptr[cnt] != '\0') { warnx("Value string too long - %s", ptr); return (-1); } /* * Here try parsing the OIDs and syntaxes and then check values - have * to know syntax to check value boundaries. */ if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) { warnx("Error parsing OID %s", oid_str); return (-1); } if (parse_syntax_val(snmp_val, syntax, ptr) < 0) return (-1); return (1); } static int32_t parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, struct snmp_object *object, char *str) { uint32_t len; enum snmp_syntax syn; /* * Syntax string here not required - still may be present. */ if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { for (len = 0 ; *(str + len) != ':'; len++) { if (*(str + len) == '\0') { warnx("Syntax missing in value - %s", str); return (-1); } } if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { warnx("Unknown syntax in - %s", str); return (-1); } if (syn != object->val.syntax) { if (!ISSET_ERRIGNORE(snmptoolctx)) { warnx("Bad syntax in - %s", str); return (-1); } else object->val.syntax = syn; } len++; } else len = 0; switch (object->val.syntax) { case SNMP_SYNTAX_INTEGER: return (parse_int_string(object, str + len)); case SNMP_SYNTAX_IPADDRESS: return (parse_ip(&(object->val), str + len)); case SNMP_SYNTAX_COUNTER: return (parse_counter(&(object->val), str + len)); case SNMP_SYNTAX_GAUGE: return (parse_gauge(&(object->val), str + len)); case SNMP_SYNTAX_TIMETICKS: return (parse_ticks(&(object->val), str + len)); case SNMP_SYNTAX_COUNTER64: return (parse_uint64(&(object->val), str + len)); case SNMP_SYNTAX_OCTETSTRING: return (snmp_tc2oct(object->info->tc, &(object->val), str + len)); case SNMP_SYNTAX_OID: return (parse_oid_string(snmptoolctx, &(object->val), str + len)); default: /* NOTREACHED */ break; } return (-1); } static int32_t parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { char *ptr; if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL) return (-1); if (*ptr != '=') { warnx("Value to set expected after OID"); return (-1); } if (parse_syntax_strval(snmptoolctx, obj, ptr + 1) < 0) return (-1); return (1); } static int32_t snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, char *argv) { if (argv == NULL) return (-1); if (ISSET_NUMERIC(snmptoolctx)) { if (parse_pair_numoid_val(argv, &(obj->val)) < 0) return (-1); } else { if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0) return (-1); } return (1); } static int32_t add_ip_syntax(struct snmp_value *dst, struct snmp_value *src) { int8_t i; dst->syntax = SNMP_SYNTAX_IPADDRESS; for (i = 0; i < 4; i++) dst->v.ipaddress[i] = src->v.ipaddress[i]; return (1); } static int32_t add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src) { if (src->v.octetstring.len > ASN_MAXOCTETSTRING) { warnx("OctetString len too big - %u", src->v.octetstring.len); return (-1); } if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) == NULL) { syslog(LOG_ERR, "malloc() failed - %s", strerror(errno)); return (-1); } memcpy(dst->v.octetstring.octets, src->v.octetstring.octets, src->v.octetstring.len); dst->syntax = SNMP_SYNTAX_OCTETSTRING; dst->v.octetstring.len = src->v.octetstring.len; return(0); } static int32_t add_oid_syntax(struct snmp_value *dst, struct snmp_value *src) { asn_append_oid(&(dst->v.oid), &(src->v.oid)); dst->syntax = SNMP_SYNTAX_OID; return (0); } /* * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT, * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known - * return error. */ static int32_t snmpset_add_value(struct snmp_value *dst, struct snmp_value *src) { if (dst == NULL || src == NULL) return (-1); switch (src->syntax) { case SNMP_SYNTAX_INTEGER: dst->v.integer = src->v.integer; dst->syntax = SNMP_SYNTAX_INTEGER; break; case SNMP_SYNTAX_TIMETICKS: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_TIMETICKS; break; case SNMP_SYNTAX_GAUGE: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_GAUGE; break; case SNMP_SYNTAX_COUNTER: dst->v.uint32 = src->v.uint32; dst->syntax = SNMP_SYNTAX_COUNTER; break; case SNMP_SYNTAX_COUNTER64: dst->v.counter64 = src->v.counter64; dst->syntax = SNMP_SYNTAX_COUNTER64; break; case SNMP_SYNTAX_IPADDRESS: add_ip_syntax(dst, src); break; case SNMP_SYNTAX_OCTETSTRING: add_octstring_syntax(dst, src); break; case SNMP_SYNTAX_OID: add_oid_syntax(dst, src); break; default: warnx("Unknown syntax %d", src->syntax); return (-1); } return (0); } static int32_t snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->version == SNMP_V1 && obj->val.syntax == SNMP_SYNTAX_COUNTER64) { warnx("64-bit counters are not supported in SNMPv1 PDU"); return (-1); } if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx)) return (1); if (obj->info->access < SNMP_ACCESS_SET) { warnx("Object %s not accessible for set - try 'bsnmpset -a'", obj->info->string); return (-1); } return (1); } static int32_t snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj) { if (pdu->nbindings > SNMP_MAX_BINDINGS) { warnx("Too many OIDs for one PDU"); return (-1); } if (obj->error > 0) return (0); if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val)) < 0) return (-1); asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var)); pdu->nbindings++; return (pdu->nbindings); } static int snmptool_set(struct snmp_toolinfo *snmptoolctx) { struct snmp_pdu req, resp; snmp_pdu_create(&req, SNMP_PDU_SET); while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind, snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) { if (snmp_dialog(&req, &resp)) { warn("Snmp dialog"); break; } if (snmp_pdu_check(&req, &resp) > 0) { if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) snmp_output_resp(snmptoolctx, &resp, NULL); snmp_pdu_free(&resp); break; } snmp_output_err_resp(snmptoolctx, &resp); if (!ISSET_RETRY(snmptoolctx)) { snmp_pdu_free(&resp); break; } if (snmp_object_seterror(snmptoolctx, &(resp.bindings[resp.error_index - 1]), resp.error_status) <= 0) { snmp_pdu_free(&resp); break; } fprintf(stderr, "Retrying...\n"); snmp_pdu_free(&req); snmp_pdu_create(&req, SNMP_PDU_SET); } snmp_pdu_free(&req); return (0); } /* ***************************************************************************** * main */ /* * According to command line options prepare SNMP Get | GetNext | GetBulk PDU. * Wait for a response and print it. */ /* * Do a 'snmp walk' - according to command line options request for values * lexicographically subsequent and subrooted at a common node. Send a GetNext * PDU requesting the value for each next variable and print the response. Stop * when a Response PDU is received that contains the value of a variable not * subrooted at the variable the walk started. */ int main(int argc, char ** argv) { struct snmp_toolinfo snmptoolctx; int32_t oid_cnt, last_oid, opt_num; int rc = 0; /* Make sure program_name is set and valid. */ if (*argv == NULL) program_name = "snmptool"; else { program_name = strrchr(*argv, '/'); if (program_name != NULL) program_name++; else program_name = *argv; } if (program_name == NULL) { fprintf(stderr, "Error: No program name?\n"); exit (1); } else if (strcmp(program_name, "bsnmpget") == 0) program = BSNMPGET; else if (strcmp(program_name, "bsnmpwalk") == 0) program = BSNMPWALK; else if (strcmp(program_name, "bsnmpset") == 0) program = BSNMPSET; else { fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name); exit (1); } /* Initialize. */ if (snmptool_init(&snmptoolctx) < 0) exit (1); if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) { snmp_tool_freeall(&snmptoolctx); /* On -h (help) exit without error. */ if (opt_num == -2) exit(0); else exit(1); } oid_cnt = argc - opt_num - 1; if (oid_cnt == 0) { switch (program) { case BSNMPGET: if (!ISSET_EDISCOVER(&snmptoolctx) && !ISSET_LOCALKEY(&snmptoolctx)) { fprintf(stderr, "No OID given.\n"); usage(); snmp_tool_freeall(&snmptoolctx); exit(1); } break; case BSNMPWALK: if (snmp_object_add(&snmptoolctx, snmpwalk_add_default, NULL) < 0) { fprintf(stderr, "Error setting default subtree.\n"); snmp_tool_freeall(&snmptoolctx); exit(1); } break; case BSNMPSET: fprintf(stderr, "No OID given.\n"); usage(); snmp_tool_freeall(&snmptoolctx); exit(1); } } if (snmp_import_all(&snmptoolctx) < 0) { snmp_tool_freeall(&snmptoolctx); exit(1); } /* A simple sanity check - can not send GETBULK when using SNMPv1. */ if (program == BSNMPGET && snmp_client.version == SNMP_V1 && GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) { fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n"); snmp_tool_freeall(&snmptoolctx); exit(1); } for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) { if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ? snmpset_parse_oid : snmptools_parse_oid, argv[last_oid])) < 0) { fprintf(stderr, "Error parsing OID string '%s'.\n", argv[last_oid]); snmp_tool_freeall(&snmptoolctx); exit(1); } } if (snmp_open(NULL, NULL, NULL, NULL)) { warn("Failed to open snmp session"); snmp_tool_freeall(&snmptoolctx); exit(1); } if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0) SET_EDISCOVER(&snmptoolctx); if (ISSET_EDISCOVER(&snmptoolctx) && snmp_discover_engine(snmptoolctx.passwd) < 0) { warn("Unknown SNMP Engine ID"); rc = 1; goto cleanup; } if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || ISSET_EDISCOVER(&snmptoolctx)) snmp_output_engine(); if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) && !ISSET_EDISCOVER(&snmptoolctx)) { if (snmp_passwd_to_keys(&snmp_client.user, snmptoolctx.passwd) != SNMP_CODE_OK || snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id, snmp_client.engine.engine_len) != SNMP_CODE_OK) { warn("Failed to get keys"); rc = 1; goto cleanup; } } if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE || ISSET_EDISCOVER(&snmptoolctx)) snmp_output_keys(); if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0) goto cleanup; switch (program) { case BSNMPGET: rc = snmptool_get(&snmptoolctx); break; case BSNMPWALK: rc = snmptool_walk(&snmptoolctx); break; case BSNMPSET: rc = snmptool_set(&snmptoolctx); break; } cleanup: snmp_tool_freeall(&snmptoolctx); snmp_close(); exit(rc); }