bsnmptools.c revision 300471
1/*- 2 * Copyright (c) 2005-2006 The FreeBSD Project 3 * All rights reserved. 4 * 5 * Author: Shteryana Shopova <syrinx@FreeBSD.org> 6 * 7 * Redistribution of this software and documentation and use in source and 8 * binary forms, with or without modification, are permitted provided that 9 * the following conditions are met: 10 * 11 * 1. Redistributions of source code or documentation must retain the above 12 * copyright notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * Helper functions for snmp client tools 30 * 31 * $FreeBSD: stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c 300471 2016-05-23 05:41:53Z ngie $ 32 */ 33 34#include <sys/param.h> 35#include <sys/queue.h> 36#include <sys/uio.h> 37 38#include <assert.h> 39#include <ctype.h> 40#include <err.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <syslog.h> 47#include <unistd.h> 48 49#include <bsnmp/asn1.h> 50#include <bsnmp/snmp.h> 51#include <bsnmp/snmpclient.h> 52#include "bsnmptc.h" 53#include "bsnmptools.h" 54 55/* Internal varibale to turn on library debugging for testing and to 56 * find bugs. It is not exported via the header file. 57 * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */ 58int _bsnmptools_debug = 0; 59 60/* Default files to import mapping from if none explicitly provided. */ 61#define bsnmpd_defs "/usr/share/snmp/defs/tree.def" 62#define mibII_defs "/usr/share/snmp/defs/mibII_tree.def" 63 64/* 65 * The .iso.org.dod oid that has to be prepended to every OID when requesting 66 * a value. 67 */ 68const struct asn_oid IsoOrgDod_OID = { 69 3, { 1, 3, 6 } 70}; 71 72 73#define SNMP_ERR_UNKNOWN 0 74 75/* 76 * An array of error strings corresponding to error definitions from libbsnmp. 77 */ 78static const struct { 79 const char *str; 80 int32_t error; 81} error_strings[] = { 82 { "Unknown", SNMP_ERR_UNKNOWN }, 83 { "Too big ", SNMP_ERR_TOOBIG }, 84 { "No such Name", SNMP_ERR_NOSUCHNAME }, 85 { "Bad Value", SNMP_ERR_BADVALUE }, 86 { "Readonly", SNMP_ERR_READONLY }, 87 { "General error", SNMP_ERR_GENERR }, 88 { "No access", SNMP_ERR_NO_ACCESS }, 89 { "Wrong type", SNMP_ERR_WRONG_TYPE }, 90 { "Wrong length", SNMP_ERR_WRONG_LENGTH }, 91 { "Wrong encoding", SNMP_ERR_WRONG_ENCODING }, 92 { "Wrong value", SNMP_ERR_WRONG_VALUE }, 93 { "No creation", SNMP_ERR_NO_CREATION }, 94 { "Inconsistent value", SNMP_ERR_INCONS_VALUE }, 95 { "Resource unavailable", SNMP_ERR_RES_UNAVAIL }, 96 { "Commit failed", SNMP_ERR_COMMIT_FAILED }, 97 { "Undo failed", SNMP_ERR_UNDO_FAILED }, 98 { "Authorization error", SNMP_ERR_AUTH_ERR }, 99 { "Not writable", SNMP_ERR_NOT_WRITEABLE }, 100 { "Inconsistent name", SNMP_ERR_INCONS_NAME }, 101 { NULL, 0 } 102}; 103 104/* This one and any following are exceptions. */ 105#define SNMP_SYNTAX_UNKNOWN SNMP_SYNTAX_NOSUCHOBJECT 106 107static const struct { 108 const char *str; 109 enum snmp_syntax stx; 110} syntax_strings[] = { 111 { "Null", SNMP_SYNTAX_NULL }, 112 { "Integer", SNMP_SYNTAX_INTEGER }, 113 { "OctetString", SNMP_SYNTAX_OCTETSTRING }, 114 { "OID", SNMP_SYNTAX_OID }, 115 { "IpAddress", SNMP_SYNTAX_IPADDRESS }, 116 { "Counter32", SNMP_SYNTAX_COUNTER }, 117 { "Gauge", SNMP_SYNTAX_GAUGE }, 118 { "TimeTicks", SNMP_SYNTAX_TIMETICKS }, 119 { "Counter64", SNMP_SYNTAX_COUNTER64 }, 120 { "Unknown", SNMP_SYNTAX_UNKNOWN }, 121}; 122 123int 124snmptool_init(struct snmp_toolinfo *snmptoolctx) 125{ 126 char *str; 127 size_t slen; 128 129 memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo)); 130 snmptoolctx->objects = 0; 131 snmptoolctx->mappings = NULL; 132 snmptoolctx->flags = SNMP_PDU_GET; /* XXX */ 133 SLIST_INIT(&snmptoolctx->filelist); 134 snmp_client_init(&snmp_client); 135 SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS); 136 137 if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0) 138 warnx("Error adding file %s to list", bsnmpd_defs); 139 140 if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0) 141 warnx("Error adding file %s to list", mibII_defs); 142 143 /* Read the environment */ 144 if ((str = getenv("SNMPAUTH")) != NULL) { 145 slen = strlen(str); 146 if (slen == strlen("md5") && strcasecmp(str, "md5") == 0) 147 snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5; 148 else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0) 149 snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA; 150 else if (slen != 0) 151 warnx("Bad authentication type - %s in SNMPAUTH", str); 152 } 153 154 if ((str = getenv("SNMPPRIV")) != NULL) { 155 slen = strlen(str); 156 if (slen == strlen("des") && strcasecmp(str, "des") == 0) 157 snmp_client.user.priv_proto = SNMP_PRIV_DES; 158 else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0) 159 snmp_client.user.priv_proto = SNMP_PRIV_AES; 160 else if (slen != 0) 161 warnx("Bad privacy type - %s in SNMPPRIV", str); 162 } 163 164 if ((str = getenv("SNMPUSER")) != NULL) { 165 if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) { 166 warnx("Username too long - %s in SNMPUSER", str); 167 return (-1); 168 } 169 if (slen > 0) { 170 strlcpy(snmp_client.user.sec_name, str, 171 sizeof(snmp_client.user.sec_name)); 172 snmp_client.version = SNMP_V3; 173 } 174 } 175 176 if ((str = getenv("SNMPPASSWD")) != NULL) { 177 if ((slen = strlen(str)) > MAXSTR) 178 slen = MAXSTR - 1; 179 if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) { 180 warnx("malloc() failed - %s", strerror(errno)); 181 return (-1); 182 } 183 if (slen > 0) 184 strlcpy(snmptoolctx->passwd, str, slen + 1); 185 } 186 187 return (0); 188} 189 190#define OBJECT_IDX_LIST(o) o->info->table_idx->index_list 191 192/* 193 * Walk through the file list and import string<->oid mappings from each file. 194 */ 195int32_t 196snmp_import_all(struct snmp_toolinfo *snmptoolctx) 197{ 198 int32_t fc; 199 struct fname *tmp; 200 201 if (snmptoolctx == NULL) 202 return (-1); 203 204 if (ISSET_NUMERIC(snmptoolctx)) 205 return (0); 206 207 if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL) 208 return (-1); 209 210 fc = 0; 211 if (SLIST_EMPTY(&snmptoolctx->filelist)) { 212 warnx("No files to read OID <-> string conversions from"); 213 return (-1); 214 } else { 215 SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) { 216 if (tmp->done) 217 continue; 218 if (snmp_import_file(snmptoolctx, tmp) < 0) { 219 fc = -1; 220 break; 221 } 222 fc++; 223 } 224 } 225 226 snmp_mapping_dump(snmptoolctx); 227 return (fc); 228} 229 230/* 231 * Add a filename to the file list - the initial idea of keeping a list with all 232 * files to read OIDs from was that an application might want to have loaded in 233 * memory the OIDs from a single file only and when done with them read the OIDs 234 * from another file. This is not used yet but might be a good idea at some 235 * point. Size argument is number of bytes in string including trailing '\0', 236 * not string length. 237 */ 238int32_t 239add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename, 240 const struct asn_oid *cut, int32_t done) 241{ 242 char *fstring; 243 struct fname *entry; 244 245 if (snmptoolctx == NULL) 246 return (-1); 247 248 /* Make sure file was not in list. */ 249 SLIST_FOREACH(entry, &snmptoolctx->filelist, link) { 250 if (strncmp(entry->name, filename, strlen(entry->name)) == 0) 251 return (0); 252 } 253 254 if ((fstring = malloc(strlen(filename) + 1)) == NULL) { 255 warnx("malloc() failed - %s", strerror(errno)); 256 return (-1); 257 } 258 259 if ((entry = calloc(1, sizeof(struct fname))) == NULL) { 260 warnx("malloc() failed - %s", strerror(errno)); 261 free(fstring); 262 return (-1); 263 } 264 265 if (cut != NULL) 266 asn_append_oid(&(entry->cut), cut); 267 strlcpy(fstring, filename, strlen(filename) + 1); 268 entry->name = fstring; 269 entry->done = done; 270 SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link); 271 272 return (1); 273} 274 275void 276free_filelist(struct snmp_toolinfo *snmptoolctx) 277{ 278 struct fname *f; 279 280 if (snmptoolctx == NULL) 281 return; /* XXX error handling */ 282 283 while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) { 284 SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link); 285 if (f->name) 286 free(f->name); 287 free(f); 288 } 289} 290 291static char 292isvalid_fchar(char c, int pos) 293{ 294 if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' || 295 (pos != 0 && isdigit(c))){ 296 return (c); 297 } 298 299 if (c == '\0') 300 return (0); 301 302 if (!isascii(c) || !isprint(c)) 303 warnx("Unexpected character %#2x", (u_int) c); 304 else 305 warnx("Illegal character '%c'", c); 306 307 return (-1); 308} 309 310/* 311 * Re-implement getsubopt from scratch, because the second argument is broken 312 * and will not compile with WARNS=5. 313 * Copied from src/contrib/bsnmp/snmpd/main.c. 314 */ 315static int 316getsubopt1(char **arg, const char *const *options, char **valp, char **optp) 317{ 318 static const char *const delim = ",\t "; 319 u_int i; 320 char *ptr; 321 322 *optp = NULL; 323 324 /* Skip leading junk. */ 325 for (ptr = *arg; *ptr != '\0'; ptr++) 326 if (strchr(delim, *ptr) == NULL) 327 break; 328 if (*ptr == '\0') { 329 *arg = ptr; 330 return (-1); 331 } 332 *optp = ptr; 333 334 /* Find the end of the option. */ 335 while (*++ptr != '\0') 336 if (strchr(delim, *ptr) != NULL || *ptr == '=') 337 break; 338 339 if (*ptr != '\0') { 340 if (*ptr == '=') { 341 *ptr++ = '\0'; 342 *valp = ptr; 343 while (*ptr != '\0' && strchr(delim, *ptr) == NULL) 344 ptr++; 345 if (*ptr != '\0') 346 *ptr++ = '\0'; 347 } else 348 *ptr++ = '\0'; 349 } 350 351 *arg = ptr; 352 353 for (i = 0; *options != NULL; options++, i++) 354 if (strcmp(*optp, *options) == 0) 355 return (i); 356 return (-1); 357} 358 359static int32_t 360parse_path(char *value) 361{ 362 int32_t i, len; 363 364 if (value == NULL) 365 return (-1); 366 367 for (len = 0; len < MAXPATHLEN; len++) { 368 i = isvalid_fchar(*(value + len), len) ; 369 370 if (i == 0) 371 break; 372 else if (i < 0) 373 return (-1); 374 } 375 376 if (len >= MAXPATHLEN || value[len] != '\0') { 377 warnx("Bad pathname - '%s'", value); 378 return (-1); 379 } 380 381 return (len); 382} 383 384static int32_t 385parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path, 386 const struct asn_oid *cut) 387{ 388 int32_t namelen; 389 char filename[MAXPATHLEN + 1]; 390 391 if (value == NULL) 392 return (-1); 393 394 do { 395 memset(filename, 0, MAXPATHLEN + 1); 396 397 if (isalpha(*value) && (path == NULL || path[0] == '\0')) { 398 strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1); 399 namelen = strlen(SNMP_DEFS_DIR); 400 } else if (path != NULL){ 401 strlcpy(filename, path, MAXPATHLEN + 1); 402 namelen = strlen(path); 403 } else 404 namelen = 0; 405 406 for ( ; namelen < MAXPATHLEN; value++) { 407 if (isvalid_fchar(*value, namelen) > 0) { 408 filename[namelen++] = *value; 409 continue; 410 } 411 412 if (*value == ',' ) 413 value++; 414 else if (*value == '\0') 415 ; 416 else { 417 if (!isascii(*value) || !isprint(*value)) 418 warnx("Unexpected character %#2x in" 419 " filename", (u_int) *value); 420 else 421 warnx("Illegal character '%c' in" 422 " filename", *value); 423 return (-1); 424 } 425 426 filename[namelen]='\0'; 427 break; 428 } 429 430 if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) { 431 warnx("Filename %s too long", filename); 432 return (-1); 433 } 434 435 if (add_filename(snmptoolctx, filename, cut, 0) < 0) { 436 warnx("Error adding file %s to list", filename); 437 return (-1); 438 } 439 } while (*value != '\0'); 440 441 return(1); 442} 443 444static int32_t 445parse_ascii(char *ascii, uint8_t *binstr, size_t binlen) 446{ 447 char dptr[3]; 448 size_t count; 449 int32_t alen, i, saved_errno; 450 uint32_t val; 451 452 /* Filter 0x at the beginning */ 453 if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x') 454 i = 2; 455 else 456 i = 0; 457 458 saved_errno = errno; 459 errno = 0; 460 for (count = 0; i < alen; i += 2) { 461 /* XXX: consider strlen(ascii) % 2 != 0 */ 462 dptr[0] = ascii[i]; 463 dptr[1] = ascii[i + 1]; 464 dptr[2] = '\0'; 465 if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) { 466 errno = saved_errno; 467 return (-1); 468 } 469 binstr[count] = (uint8_t) val; 470 if (++count >= binlen) { 471 warnx("Key %s too long - truncating to %zu octets", 472 ascii, binlen); 473 break; 474 } 475 } 476 477 return (count); 478} 479 480/* 481 * Functions to parse common input options for client tools and fill in the 482 * snmp_client structure. 483 */ 484int32_t 485parse_authentication(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) 486{ 487 int32_t count, subopt; 488 char *val, *option; 489 const char *const subopts[] = { 490 "proto", 491 "key", 492 NULL 493 }; 494 495 assert(opt_arg != NULL); 496 count = 1; 497 while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { 498 switch (subopt) { 499 case 0: 500 if (val == NULL) { 501 warnx("Suboption 'proto' requires an argument"); 502 return (-1); 503 } 504 if (strlen(val) != 3) { 505 warnx("Unknown auth protocol - %s", val); 506 return (-1); 507 } 508 if (strncasecmp("md5", val, strlen("md5")) == 0) 509 snmp_client.user.auth_proto = 510 SNMP_AUTH_HMAC_MD5; 511 else if (strncasecmp("sha", val, strlen("sha")) == 0) 512 snmp_client.user.auth_proto = 513 SNMP_AUTH_HMAC_SHA; 514 else { 515 warnx("Unknown auth protocol - %s", val); 516 return (-1); 517 } 518 break; 519 case 1: 520 if (val == NULL) { 521 warnx("Suboption 'key' requires an argument"); 522 return (-1); 523 } 524 if (parse_ascii(val, snmp_client.user.auth_key, 525 SNMP_AUTH_KEY_SIZ) < 0) { 526 warnx("Bad authentication key- %s", val); 527 return (-1); 528 } 529 break; 530 default: 531 warnx("Unknown suboption - '%s'", suboptarg); 532 return (-1); 533 } 534 count += 1; 535 } 536 return (2/* count */); 537} 538 539int32_t 540parse_privacy(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) 541{ 542 int32_t count, subopt; 543 char *val, *option; 544 const char *const subopts[] = { 545 "proto", 546 "key", 547 NULL 548 }; 549 550 assert(opt_arg != NULL); 551 count = 1; 552 while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { 553 switch (subopt) { 554 case 0: 555 if (val == NULL) { 556 warnx("Suboption 'proto' requires an argument"); 557 return (-1); 558 } 559 if (strlen(val) != 3) { 560 warnx("Unknown privacy protocol - %s", val); 561 return (-1); 562 } 563 if (strncasecmp("aes", val, strlen("aes")) == 0) 564 snmp_client.user.priv_proto = SNMP_PRIV_AES; 565 else if (strncasecmp("des", val, strlen("des")) == 0) 566 snmp_client.user.priv_proto = SNMP_PRIV_DES; 567 else { 568 warnx("Unknown privacy protocol - %s", val); 569 return (-1); 570 } 571 break; 572 case 1: 573 if (val == NULL) { 574 warnx("Suboption 'key' requires an argument"); 575 return (-1); 576 } 577 if (parse_ascii(val, snmp_client.user.priv_key, 578 SNMP_PRIV_KEY_SIZ) < 0) { 579 warnx("Bad privacy key- %s", val); 580 return (-1); 581 } 582 break; 583 default: 584 warnx("Unknown suboption - '%s'", suboptarg); 585 return (-1); 586 } 587 count += 1; 588 } 589 return (2/* count */); 590} 591 592int32_t 593parse_context(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) 594{ 595 int32_t count, subopt; 596 char *val, *option; 597 const char *const subopts[] = { 598 "context", 599 "context-engine", 600 NULL 601 }; 602 603 assert(opt_arg != NULL); 604 count = 1; 605 while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { 606 switch (subopt) { 607 case 0: 608 if (val == NULL) { 609 warnx("Suboption 'context' - no argument"); 610 return (-1); 611 } 612 strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ); 613 break; 614 case 1: 615 if (val == NULL) { 616 warnx("Suboption 'context-engine' - no argument"); 617 return (-1); 618 } 619 if ((snmp_client.clen = parse_ascii(val, 620 snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) < 0) { 621 warnx("Bad EngineID - %s", val); 622 return (-1); 623 } 624 break; 625 default: 626 warnx("Unknown suboption - '%s'", suboptarg); 627 return (-1); 628 } 629 count += 1; 630 } 631 return (2/* count */); 632} 633 634int32_t 635parse_user_security(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg) 636{ 637 int32_t count, subopt, saved_errno; 638 char *val, *option; 639 const char *const subopts[] = { 640 "engine", 641 "engine-boots", 642 "engine-time", 643 "name", 644 NULL 645 }; 646 647 assert(opt_arg != NULL); 648 count = 1; 649 while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { 650 switch (subopt) { 651 case 0: 652 if (val == NULL) { 653 warnx("Suboption 'engine' - no argument"); 654 return (-1); 655 } 656 snmp_client.engine.engine_len = parse_ascii(val, 657 snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ); 658 if (snmp_client.engine.engine_len < 0) { 659 warnx("Bad EngineID - %s", val); 660 return (-1); 661 } 662 break; 663 case 1: 664 if (val == NULL) { 665 warnx("Suboption 'engine-boots' - no argument"); 666 return (-1); 667 } 668 saved_errno = errno; 669 errno = 0; 670 snmp_client.engine.engine_boots = strtoul(val, NULL, 10); 671 if (errno != 0) { 672 warnx("Bad 'engine-boots' value %s - %s", val, 673 strerror(errno)); 674 errno = saved_errno; 675 return (-1); 676 } 677 errno = saved_errno; 678 break; 679 case 2: 680 if (val == NULL) { 681 warnx("Suboption 'engine-time' - no argument"); 682 return (-1); 683 } 684 saved_errno = errno; 685 errno = 0; 686 snmp_client.engine.engine_time = strtoul(val, NULL, 10); 687 if (errno != 0) { 688 warnx("Bad 'engine-time' value %s - %s", val, 689 strerror(errno)); 690 errno = saved_errno; 691 return (-1); 692 } 693 errno = saved_errno; 694 break; 695 case 3: 696 strlcpy(snmp_client.user.sec_name, val, 697 SNMP_ADM_STR32_SIZ); 698 break; 699 default: 700 warnx("Unknown suboption - '%s'", suboptarg); 701 return (-1); 702 } 703 count += 1; 704 } 705 return (2/* count */); 706} 707 708int32_t 709parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 710{ 711 assert(opt_arg != NULL); 712 713 if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0) 714 return (-1); 715 716 return (2); 717} 718 719int32_t 720parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 721{ 722 char path[MAXPATHLEN + 1]; 723 int32_t cut_dflt, len, subopt; 724 struct asn_oid cut; 725 char *val, *option; 726 const char *const subopts[] = { 727 "cut", 728 "path", 729 "file", 730 NULL 731 }; 732 733#define INC_CUT 0 734#define INC_PATH 1 735#define INC_LIST 2 736 737 assert(opt_arg != NULL); 738 739 /* if (opt == 'i') 740 free_filelist(snmptoolctx, ); */ 741 /* 742 * This function should be called only after getopt(3) - otherwise if 743 * no previous validation of opt_arg strlen() may not return what is 744 * expected. 745 */ 746 747 path[0] = '\0'; 748 memset(&cut, 0, sizeof(struct asn_oid)); 749 cut_dflt = -1; 750 751 while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) { 752 switch (subopt) { 753 case INC_CUT: 754 if (val == NULL) { 755 warnx("Suboption 'cut' requires an argument"); 756 return (-1); 757 } else { 758 if (snmp_parse_numoid(val, &cut) < 0) 759 return (-1); 760 } 761 cut_dflt = 1; 762 break; 763 764 case INC_PATH: 765 if ((len = parse_path(val)) < 0) 766 return (-1); 767 strlcpy(path, val, len + 1); 768 break; 769 770 case INC_LIST: 771 if (val == NULL) 772 return (-1); 773 if (cut_dflt == -1) 774 len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID); 775 else 776 len = parse_flist(snmptoolctx, val, path, &cut); 777 if (len < 0) 778 return (-1); 779 break; 780 781 default: 782 warnx("Unknown suboption - '%s'", suboptarg); 783 return (-1); 784 } 785 } 786 787 /* XXX: Fix me - returning two is wrong here */ 788 return (2); 789} 790 791int32_t 792parse_server(char *opt_arg) 793{ 794 assert(opt_arg != NULL); 795 796 if (snmp_parse_server(&snmp_client, opt_arg) < 0) 797 return (-1); 798 799 if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) { 800 if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL) + 1)) 801 == NULL) { 802 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); 803 return (-1); 804 } 805 strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL); 806 } 807 808 return (2); 809} 810 811int32_t 812parse_timeout(char *opt_arg) 813{ 814 int32_t v, saved_errno; 815 816 assert(opt_arg != NULL); 817 818 saved_errno = errno; 819 errno = 0; 820 821 v = strtol(opt_arg, NULL, 10); 822 if (errno != 0) { 823 warnx( "Error parsing timeout value - %s", strerror(errno)); 824 errno = saved_errno; 825 return (-1); 826 } 827 828 snmp_client.timeout.tv_sec = v; 829 errno = saved_errno; 830 return (2); 831} 832 833int32_t 834parse_retry(char *opt_arg) 835{ 836 uint32_t v; 837 int32_t saved_errno; 838 839 assert(opt_arg != NULL); 840 841 saved_errno = errno; 842 errno = 0; 843 844 v = strtoul(opt_arg, NULL, 10); 845 if (errno != 0) { 846 warnx("Error parsing retries count - %s", strerror(errno)); 847 errno = saved_errno; 848 return (-1); 849 } 850 851 snmp_client.retries = v; 852 errno = saved_errno; 853 return (2); 854} 855 856int32_t 857parse_version(char *opt_arg) 858{ 859 uint32_t v; 860 int32_t saved_errno; 861 862 assert(opt_arg != NULL); 863 864 saved_errno = errno; 865 errno = 0; 866 867 v = strtoul(opt_arg, NULL, 10); 868 if (errno != 0) { 869 warnx("Error parsing version - %s", strerror(errno)); 870 errno = saved_errno; 871 return (-1); 872 } 873 874 switch (v) { 875 case 1: 876 snmp_client.version = SNMP_V1; 877 break; 878 case 2: 879 snmp_client.version = SNMP_V2c; 880 break; 881 case 3: 882 snmp_client.version = SNMP_V3; 883 break; 884 default: 885 warnx("Unsupported SNMP version - %u", v); 886 errno = saved_errno; 887 return (-1); 888 } 889 890 errno = saved_errno; 891 return (2); 892} 893 894int32_t 895parse_local_path(char *opt_arg) 896{ 897 assert(opt_arg != NULL); 898 899 if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) { 900 warnx("Filename too long - %s", opt_arg); 901 return (-1); 902 } 903 904 strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH)); 905 return (2); 906} 907 908int32_t 909parse_buflen(char *opt_arg) 910{ 911 uint32_t size; 912 int32_t saved_errno; 913 914 assert(opt_arg != NULL); 915 916 saved_errno = errno; 917 errno = 0; 918 919 size = strtoul(opt_arg, NULL, 10); 920 if (errno != 0) { 921 warnx("Error parsing buffer size - %s", strerror(errno)); 922 errno = saved_errno; 923 return (-1); 924 } 925 926 if (size > MAX_BUFF_SIZE) { 927 warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE); 928 errno = saved_errno; 929 return (-1); 930 } 931 932 snmp_client.txbuflen = snmp_client.rxbuflen = size; 933 errno = saved_errno; 934 return (2); 935} 936 937int32_t 938parse_debug(void) 939{ 940 snmp_client.dump_pdus = 1; 941 return (1); 942} 943 944int32_t 945parse_discovery(struct snmp_toolinfo *snmptoolctx) 946{ 947 SET_EDISCOVER(snmptoolctx); 948 snmp_client.version = SNMP_V3; 949 return (1); 950} 951 952int32_t 953parse_local_key(struct snmp_toolinfo *snmptoolctx) 954{ 955 SET_LOCALKEY(snmptoolctx); 956 snmp_client.version = SNMP_V3; 957 return (1); 958} 959 960int32_t 961parse_num_oids(struct snmp_toolinfo *snmptoolctx) 962{ 963 SET_NUMERIC(snmptoolctx); 964 return (1); 965} 966 967int32_t 968parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg) 969{ 970 assert(opt_arg != NULL); 971 972 if (strlen(opt_arg) > strlen("verbose")) { 973 warnx( "Invalid output option - %s",opt_arg); 974 return (-1); 975 } 976 977 if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0) 978 SET_OUTPUT(snmptoolctx, OUTPUT_SHORT); 979 else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0) 980 SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE); 981 else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0) 982 SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR); 983 else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0) 984 SET_OUTPUT(snmptoolctx, OUTPUT_QUIET); 985 else { 986 warnx( "Invalid output option - %s", opt_arg); 987 return (-1); 988 } 989 990 return (2); 991} 992 993int32_t 994parse_errors(struct snmp_toolinfo *snmptoolctx) 995{ 996 SET_RETRY(snmptoolctx); 997 return (1); 998} 999 1000int32_t 1001parse_skip_access(struct snmp_toolinfo *snmptoolctx) 1002{ 1003 SET_ERRIGNORE(snmptoolctx); 1004 return (1); 1005} 1006 1007char * 1008snmp_parse_suboid(char *str, struct asn_oid *oid) 1009{ 1010 char *endptr; 1011 asn_subid_t suboid; 1012 1013 if (*str == '.') 1014 str++; 1015 1016 if (*str < '0' || *str > '9') 1017 return (str); 1018 1019 do { 1020 suboid = strtoul(str, &endptr, 10); 1021 if ((asn_subid_t) suboid > ASN_MAXID) { 1022 warnx("Suboid %u > ASN_MAXID", suboid); 1023 return (NULL); 1024 } 1025 if (snmp_suboid_append(oid, suboid) < 0) 1026 return (NULL); 1027 str = endptr + 1; 1028 } while (*endptr == '.'); 1029 1030 return (endptr); 1031} 1032 1033static char * 1034snmp_int2asn_oid(char *str, struct asn_oid *oid) 1035{ 1036 char *endptr; 1037 int32_t v, saved_errno; 1038 1039 saved_errno = errno; 1040 errno = 0; 1041 1042 v = strtol(str, &endptr, 10); 1043 if (errno != 0) { 1044 warnx("Integer value %s not supported - %s", str, 1045 strerror(errno)); 1046 errno = saved_errno; 1047 return (NULL); 1048 } 1049 errno = saved_errno; 1050 1051 if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) 1052 return (NULL); 1053 1054 return (endptr); 1055} 1056 1057/* It is a bit weird to have a table indexed by OID but still... */ 1058static char * 1059snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str, 1060 struct asn_oid *oid) 1061{ 1062 int32_t i; 1063 char string[MAXSTR], *endptr; 1064 struct snmp_object obj; 1065 1066 for (i = 0; i < MAXSTR; i++) 1067 if (isalpha (*(str + i)) == 0) 1068 break; 1069 1070 endptr = str + i; 1071 memset(&obj, 0, sizeof(struct snmp_object)); 1072 if (i == 0) { 1073 if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL) 1074 return (NULL); 1075 if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0) 1076 return (NULL); 1077 } else { 1078 strlcpy(string, str, i + 1); 1079 string[i] = '\0'; 1080 if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) { 1081 warnx("Unknown string - %s", string); 1082 return (NULL); 1083 } 1084 } 1085 1086 asn_append_oid(oid, &(obj.val.var)); 1087 return (endptr); 1088} 1089 1090static char * 1091snmp_ip2asn_oid(char *str, struct asn_oid *oid) 1092{ 1093 uint32_t v; 1094 int32_t i; 1095 char *endptr, *ptr; 1096 1097 ptr = str; 1098 for (i = 0; i < 4; i++) { 1099 v = strtoul(ptr, &endptr, 10); 1100 if (v > 0xff) 1101 return (NULL); 1102 if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3) 1103 return (NULL); 1104 if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) 1105 return (NULL); 1106 ptr = endptr + 1; 1107 } 1108 1109 return (endptr); 1110} 1111 1112/* 32-bit counter, gauge, timeticks. */ 1113static char * 1114snmp_uint2asn_oid(char *str, struct asn_oid *oid) 1115{ 1116 char *endptr; 1117 uint32_t v; 1118 int32_t saved_errno; 1119 1120 saved_errno = errno; 1121 errno = 0; 1122 1123 v = strtoul(str, &endptr, 10); 1124 if (errno != 0) { 1125 warnx("Integer value %s not supported - %s\n", str, 1126 strerror(errno)); 1127 errno = saved_errno; 1128 return (NULL); 1129 } 1130 errno = saved_errno; 1131 if (snmp_suboid_append(oid, (asn_subid_t) v) < 0) 1132 return (NULL); 1133 1134 return (endptr); 1135} 1136 1137static char * 1138snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid) 1139{ 1140 char *endptr; 1141 uint64_t v; 1142 int32_t saved_errno; 1143 1144 saved_errno = errno; 1145 errno = 0; 1146 1147 v = strtoull(str, &endptr, 10); 1148 1149 if (errno != 0) { 1150 warnx("Integer value %s not supported - %s", str, 1151 strerror(errno)); 1152 errno = saved_errno; 1153 return (NULL); 1154 } 1155 errno = saved_errno; 1156 if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0) 1157 return (NULL); 1158 1159 if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0) 1160 return (NULL); 1161 1162 return (endptr); 1163} 1164 1165enum snmp_syntax 1166parse_syntax(char *str) 1167{ 1168 int32_t i; 1169 1170 for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) { 1171 if (strncmp(syntax_strings[i].str, str, 1172 strlen(syntax_strings[i].str)) == 0) 1173 return (syntax_strings[i].stx); 1174 } 1175 1176 return (SNMP_SYNTAX_NULL); 1177} 1178 1179static char * 1180snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str, 1181 struct index *idx, struct snmp_object *object) 1182{ 1183 char *ptr; 1184 int32_t i; 1185 enum snmp_syntax stx; 1186 char syntax[MAX_CMD_SYNTAX_LEN]; 1187 1188 ptr = str; 1189 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) { 1190 for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) { 1191 if (*(ptr + i) == ':') 1192 break; 1193 } 1194 1195 if (i >= MAX_CMD_SYNTAX_LEN) { 1196 warnx("Unknown syntax in OID - %s", str); 1197 return (NULL); 1198 } 1199 /* Expect a syntax string here. */ 1200 if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) { 1201 warnx("Invalid syntax - %s",syntax); 1202 return (NULL); 1203 } 1204 1205 if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) { 1206 warnx("Syntax mismatch - %d expected, %d given", 1207 idx->syntax, stx); 1208 return (NULL); 1209 } 1210 /* 1211 * That is where the suboid started + the syntax length + one 1212 * character for ':'. 1213 */ 1214 ptr = str + i + 1; 1215 } else 1216 stx = idx->syntax; 1217 1218 switch (stx) { 1219 case SNMP_SYNTAX_INTEGER: 1220 return (snmp_int2asn_oid(ptr, &(object->val.var))); 1221 case SNMP_SYNTAX_OID: 1222 return (snmp_oid2asn_oid(snmptoolctx, ptr, 1223 &(object->val.var))); 1224 case SNMP_SYNTAX_IPADDRESS: 1225 return (snmp_ip2asn_oid(ptr, &(object->val.var))); 1226 case SNMP_SYNTAX_COUNTER: 1227 /* FALLTHROUGH */ 1228 case SNMP_SYNTAX_GAUGE: 1229 /* FALLTHROUGH */ 1230 case SNMP_SYNTAX_TIMETICKS: 1231 return (snmp_uint2asn_oid(ptr, &(object->val.var))); 1232 case SNMP_SYNTAX_COUNTER64: 1233 return (snmp_cnt64_2asn_oid(ptr, &(object->val.var))); 1234 case SNMP_SYNTAX_OCTETSTRING: 1235 return (snmp_tc2oid(idx->tc, ptr, &(object->val.var))); 1236 default: 1237 /* NOTREACHED */ 1238 break; 1239 } 1240 1241 return (NULL); 1242} 1243 1244char * 1245snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str, 1246 struct snmp_object *object) 1247{ 1248 char *ptr; 1249 struct index *temp; 1250 1251 if (object->info->table_idx == NULL) 1252 return (NULL); 1253 1254 ptr = NULL; 1255 STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) { 1256 if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object)) 1257 == NULL) 1258 return (NULL); 1259 1260 if (*ptr != ',' && *ptr != ']') 1261 return (NULL); 1262 str = ptr + 1; 1263 } 1264 1265 if (ptr == NULL || *ptr != ']') { 1266 warnx("Mismatching index - %s", str); 1267 return (NULL); 1268 } 1269 1270 return (ptr + 1); 1271} 1272 1273/* 1274 * Fill in the struct asn_oid member of snmp_value with suboids from input. 1275 * If an error occurs - print message on stderr and return (-1). 1276 * If all is ok - return the length of the oid. 1277 */ 1278int32_t 1279snmp_parse_numoid(char *argv, struct asn_oid *var) 1280{ 1281 char *endptr, *str; 1282 asn_subid_t suboid; 1283 1284 str = argv; 1285 1286 if (*str == '.') 1287 str++; 1288 1289 do { 1290 if (var->len == ASN_MAXOIDLEN) { 1291 warnx("Oid too long - %u", var->len); 1292 return (-1); 1293 } 1294 1295 suboid = strtoul(str, &endptr, 10); 1296 if (suboid > ASN_MAXID) { 1297 warnx("Oid too long - %u", var->len); 1298 return (-1); 1299 } 1300 1301 var->subs[var->len++] = suboid; 1302 str = endptr + 1; 1303 } while ( *endptr == '.'); 1304 1305 if (*endptr != '\0') { 1306 warnx("Invalid oid string - %s", argv); 1307 return (-1); 1308 } 1309 1310 return (var->len); 1311} 1312 1313/* Append a length 1 suboid to an asn_oid structure. */ 1314int32_t 1315snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid) 1316{ 1317 if (var == NULL) 1318 return (-1); 1319 1320 if (var->len >= ASN_MAXOIDLEN) { 1321 warnx("Oid too long - %u", var->len); 1322 return (-1); 1323 } 1324 1325 var->subs[var->len++] = suboid; 1326 1327 return (1); 1328} 1329 1330/* Pop the last suboid from an asn_oid structure. */ 1331int32_t 1332snmp_suboid_pop(struct asn_oid *var) 1333{ 1334 asn_subid_t suboid; 1335 1336 if (var == NULL) 1337 return (-1); 1338 1339 if (var->len < 1) 1340 return (-1); 1341 1342 suboid = var->subs[--(var->len)]; 1343 var->subs[var->len] = 0; 1344 1345 return (suboid); 1346} 1347 1348/* 1349 * Parse the command-line provided string into an OID - alocate memory for a new 1350 * snmp object, fill in its fields and insert it in the object list. A 1351 * (snmp_verify_inoid_f) function must be provided to validate the input string. 1352 */ 1353int32_t 1354snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func, 1355 char *string) 1356{ 1357 struct snmp_object *obj; 1358 1359 if (snmptoolctx == NULL) 1360 return (-1); 1361 1362 /* XXX-BZ does that chack make sense? */ 1363 if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) { 1364 warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1); 1365 return (-1); 1366 } 1367 1368 if ((obj = calloc(1, sizeof(struct snmp_object))) == NULL) { 1369 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno)); 1370 return (-1); 1371 } 1372 1373 if (func(snmptoolctx, obj, string) < 0) { 1374 warnx("Invalid OID - %s", string); 1375 free(obj); 1376 return (-1); 1377 } 1378 1379 snmptoolctx->objects++; 1380 SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link); 1381 1382 return (1); 1383} 1384 1385/* Given an OID, find it in the object list and remove it. */ 1386int32_t 1387snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) 1388{ 1389 struct snmp_object *temp; 1390 1391 if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) { 1392 warnx("Object list already empty"); 1393 return (-1); 1394 } 1395 1396 1397 SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link) 1398 if (asn_compare_oid(&(temp->val.var), oid) == 0) 1399 break; 1400 1401 if (temp == NULL) { 1402 warnx("No such object in list"); 1403 return (-1); 1404 } 1405 1406 SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link); 1407 if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING && 1408 temp->val.v.octetstring.octets != NULL) 1409 free(temp->val.v.octetstring.octets); 1410 free(temp); 1411 1412 return (1); 1413} 1414 1415static void 1416snmp_object_freeall(struct snmp_toolinfo *snmptoolctx) 1417{ 1418 struct snmp_object *o; 1419 1420 while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) { 1421 SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link); 1422 1423 if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING && 1424 o->val.v.octetstring.octets != NULL) 1425 free(o->val.v.octetstring.octets); 1426 free(o); 1427 } 1428} 1429 1430/* Do all possible memory release before exit. */ 1431void 1432snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx) 1433{ 1434 if (snmp_client.chost != NULL) { 1435 free(snmp_client.chost); 1436 snmp_client.chost = NULL; 1437 } 1438 1439 if (snmp_client.cport != NULL) { 1440 free(snmp_client.cport); 1441 snmp_client.cport = NULL; 1442 } 1443 1444 snmp_mapping_free(snmptoolctx); 1445 free_filelist(snmptoolctx); 1446 snmp_object_freeall(snmptoolctx); 1447 1448 if (snmptoolctx->passwd != NULL) { 1449 free(snmptoolctx->passwd); 1450 snmptoolctx->passwd = NULL; 1451 } 1452} 1453 1454/* 1455 * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f) 1456 * function should check whether the variable is consistent in this PDU 1457 * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to 1458 * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the 1459 * function actually adds the variable to the PDU and must not be NULL. 1460 */ 1461int32_t 1462snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx, 1463 snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc, 1464 struct snmp_pdu *pdu, int32_t maxcount) 1465{ 1466 int32_t nbindings, abind; 1467 struct snmp_object *obj; 1468 1469 if (pdu == NULL || afunc == NULL) 1470 return (-1); 1471 1472 /* Return 0 in case of no more work todo. */ 1473 if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) 1474 return (0); 1475 1476 if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) { 1477 warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS"); 1478 return (-1); 1479 } 1480 1481 nbindings = 0; 1482 SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) { 1483 if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) { 1484 nbindings = -1; 1485 break; 1486 } 1487 if ((abind = afunc(pdu, obj)) < 0) { 1488 nbindings = -1; 1489 break; 1490 } 1491 1492 if (abind > 0) { 1493 /* Do not put more varbindings than requested. */ 1494 if (++nbindings >= maxcount) 1495 break; 1496 } 1497 } 1498 1499 return (nbindings); 1500} 1501 1502/* 1503 * Locate an object in the object list and set a corresponding error status. 1504 */ 1505int32_t 1506snmp_object_seterror(struct snmp_toolinfo *snmptoolctx, 1507 struct snmp_value *err_value, int32_t error_status) 1508{ 1509 struct snmp_object *obj; 1510 1511 if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL) 1512 return (-1); 1513 1514 SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) 1515 if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) { 1516 obj->error = error_status; 1517 return (1); 1518 } 1519 1520 return (0); 1521} 1522 1523/* 1524 * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request 1525 * but don't compare syntaxes - when sending a request PDU they must be null. 1526 * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes 1527 * checks and some other checks skipped. 1528 */ 1529int32_t 1530snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req) 1531{ 1532 uint32_t i; 1533 1534 for (i = 0; i < req->nbindings; i++) { 1535 if (asn_compare_oid(&req->bindings[i].var, 1536 &resp->bindings[i].var) != 0) { 1537 warnx("Bad OID in response"); 1538 return (-1); 1539 } 1540 1541 if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax 1542 == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax == 1543 SNMP_SYNTAX_NOSUCHINSTANCE)) 1544 return (0); 1545 } 1546 1547 return (1); 1548} 1549 1550int32_t 1551snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req) 1552{ 1553 int32_t N, R, M, r; 1554 1555 if (req->error_status > (int32_t) resp->nbindings) { 1556 warnx("Bad number of bindings in response"); 1557 return (-1); 1558 } 1559 1560 for (N = 0; N < req->error_status; N++) { 1561 if (asn_is_suboid(&req->bindings[N].var, 1562 &resp->bindings[N].var) == 0) 1563 return (0); 1564 if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW) 1565 return (0); 1566 } 1567 1568 for (R = N , r = N; R < (int32_t) req->nbindings; R++) { 1569 for (M = 0; M < req->error_index && (r + M) < 1570 (int32_t) resp->nbindings; M++) { 1571 if (asn_is_suboid(&req->bindings[R].var, 1572 &resp->bindings[r + M].var) == 0) 1573 return (0); 1574 1575 if (resp->bindings[r + M].syntax == 1576 SNMP_SYNTAX_ENDOFMIBVIEW) { 1577 M++; 1578 break; 1579 } 1580 } 1581 r += M; 1582 } 1583 1584 return (0); 1585} 1586 1587int32_t 1588snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req) 1589{ 1590 uint32_t i; 1591 1592 for (i = 0; i < req->nbindings; i++) { 1593 if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var) 1594 == 0) 1595 return (0); 1596 1597 if (resp->version != SNMP_V1 && resp->bindings[i].syntax == 1598 SNMP_SYNTAX_ENDOFMIBVIEW) 1599 return (0); 1600 } 1601 1602 return (1); 1603} 1604 1605/* 1606 * Should be called to check a response to get/getnext/getbulk. 1607 */ 1608int32_t 1609snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req) 1610{ 1611 if (resp == NULL || req == NULL) 1612 return (-2); 1613 1614 if (resp->version != req->version) { 1615 warnx("Response has wrong version"); 1616 return (-1); 1617 } 1618 1619 if (resp->error_status == SNMP_ERR_NOSUCHNAME) { 1620 warnx("Error - No Such Name"); 1621 return (0); 1622 } 1623 1624 if (resp->error_status != SNMP_ERR_NOERROR) { 1625 warnx("Error %d in response", resp->error_status); 1626 return (-1); 1627 } 1628 1629 if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){ 1630 warnx("Bad number of bindings in response"); 1631 return (-1); 1632 } 1633 1634 switch (req->type) { 1635 case SNMP_PDU_GET: 1636 return (snmp_parse_get_resp(resp,req)); 1637 case SNMP_PDU_GETBULK: 1638 return (snmp_parse_getbulk_resp(resp,req)); 1639 case SNMP_PDU_GETNEXT: 1640 return (snmp_parse_getnext_resp(resp,req)); 1641 default: 1642 /* NOTREACHED */ 1643 break; 1644 } 1645 1646 return (-2); 1647} 1648 1649static void 1650snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, 1651 uint32_t len, uint8_t *octets) 1652{ 1653 char *buf; 1654 1655 if (len == 0 || octets == NULL) 1656 return; 1657 1658 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1659 fprintf(stdout, "%s : ", 1660 syntax_strings[SNMP_SYNTAX_OCTETSTRING].str); 1661 1662 if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) { 1663 fprintf(stdout, "%s", buf); 1664 free(buf); 1665 } 1666} 1667 1668static void 1669snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc, 1670 struct asn_oid *oid) 1671{ 1672 uint32_t i; 1673 uint8_t *s; 1674 1675 if ((s = malloc(oid->subs[0] + 1)) == NULL) 1676 syslog(LOG_ERR, "malloc failed - %s", strerror(errno)); 1677 else { 1678 for (i = 0; i < oid->subs[0]; i++) 1679 s[i] = (u_char) (oid->subs[i + 1]); 1680 1681 snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s); 1682 free(s); 1683 } 1684} 1685 1686/* 1687 * Check and output syntax type and value. 1688 */ 1689static void 1690snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid) 1691{ 1692 char oid_string[ASN_OIDSTRLEN]; 1693 struct snmp_object obj; 1694 1695 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1696 fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str); 1697 1698 if(!ISSET_NUMERIC(snmptoolctx)) { 1699 memset(&obj, 0, sizeof(struct snmp_object)); 1700 asn_append_oid(&(obj.val.var), oid); 1701 1702 if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0) 1703 fprintf(stdout, "%s" , obj.info->string); 1704 else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0) 1705 fprintf(stdout, "%s" , obj.info->string); 1706 else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0) 1707 fprintf(stdout, "%s" , obj.info->string); 1708 else { 1709 (void) asn_oid2str_r(oid, oid_string); 1710 fprintf(stdout, "%s", oid_string); 1711 } 1712 } else { 1713 (void) asn_oid2str_r(oid, oid_string); 1714 fprintf(stdout, "%s", oid_string); 1715 } 1716} 1717 1718static void 1719snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums, 1720 int32_t int_val) 1721{ 1722 char *string; 1723 1724 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1725 fprintf(stdout, "%s : ", 1726 syntax_strings[SNMP_SYNTAX_INTEGER].str); 1727 1728 if (enums != NULL && (string = enum_string_lookup(enums, int_val)) 1729 != NULL) 1730 fprintf(stdout, "%s", string); 1731 else 1732 fprintf(stdout, "%d", int_val); 1733} 1734 1735static void 1736snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip) 1737{ 1738 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1739 fprintf(stdout, "%s : ", 1740 syntax_strings[SNMP_SYNTAX_IPADDRESS].str); 1741 1742 fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); 1743} 1744 1745static void 1746snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter) 1747{ 1748 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1749 fprintf(stdout, "%s : ", 1750 syntax_strings[SNMP_SYNTAX_COUNTER].str); 1751 1752 fprintf(stdout, "%u", counter); 1753} 1754 1755static void 1756snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge) 1757{ 1758 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1759 fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str); 1760 1761 fprintf(stdout, "%u", gauge); 1762} 1763 1764static void 1765snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks) 1766{ 1767 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1768 fprintf(stdout, "%s : ", 1769 syntax_strings[SNMP_SYNTAX_TIMETICKS].str); 1770 1771 fprintf(stdout, "%u", ticks); 1772} 1773 1774static void 1775snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64) 1776{ 1777 if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) 1778 fprintf(stdout, "%s : ", 1779 syntax_strings[SNMP_SYNTAX_COUNTER64].str); 1780 1781 fprintf(stdout,"%ju", counter64); 1782} 1783 1784int32_t 1785snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val, 1786 struct snmp_oid2str *entry) 1787{ 1788 if (val == NULL) 1789 return (-1); 1790 1791 if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) 1792 fprintf(stdout, " = "); 1793 1794 switch (val->syntax) { 1795 case SNMP_SYNTAX_INTEGER: 1796 if (entry != NULL) 1797 snmp_output_int(snmptoolctx, entry->snmp_enum, 1798 val->v.integer); 1799 else 1800 snmp_output_int(snmptoolctx, NULL, val->v.integer); 1801 break; 1802 1803 case SNMP_SYNTAX_OCTETSTRING: 1804 if (entry != NULL) 1805 snmp_output_octetstring(snmptoolctx, entry->tc, 1806 val->v.octetstring.len, val->v.octetstring.octets); 1807 else 1808 snmp_output_octetstring(snmptoolctx, SNMP_STRING, 1809 val->v.octetstring.len, val->v.octetstring.octets); 1810 break; 1811 1812 case SNMP_SYNTAX_OID: 1813 snmp_output_oid_value(snmptoolctx, &(val->v.oid)); 1814 break; 1815 1816 case SNMP_SYNTAX_IPADDRESS: 1817 snmp_output_ipaddress(snmptoolctx, val->v.ipaddress); 1818 break; 1819 1820 case SNMP_SYNTAX_COUNTER: 1821 snmp_output_counter(snmptoolctx, val->v.uint32); 1822 break; 1823 1824 case SNMP_SYNTAX_GAUGE: 1825 snmp_output_gauge(snmptoolctx, val->v.uint32); 1826 break; 1827 1828 case SNMP_SYNTAX_TIMETICKS: 1829 snmp_output_ticks(snmptoolctx, val->v.uint32); 1830 break; 1831 1832 case SNMP_SYNTAX_COUNTER64: 1833 snmp_output_counter64(snmptoolctx, val->v.counter64); 1834 break; 1835 1836 case SNMP_SYNTAX_NOSUCHOBJECT: 1837 fprintf(stdout, "No Such Object\n"); 1838 return (val->syntax); 1839 1840 case SNMP_SYNTAX_NOSUCHINSTANCE: 1841 fprintf(stdout, "No Such Instance\n"); 1842 return (val->syntax); 1843 1844 case SNMP_SYNTAX_ENDOFMIBVIEW: 1845 fprintf(stdout, "End of Mib View\n"); 1846 return (val->syntax); 1847 1848 case SNMP_SYNTAX_NULL: 1849 /* NOTREACHED */ 1850 fprintf(stdout, "agent returned NULL Syntax\n"); 1851 return (val->syntax); 1852 1853 default: 1854 /* NOTREACHED - If here - then all went completely wrong. */ 1855 fprintf(stdout, "agent returned unknown syntax\n"); 1856 return (-1); 1857 } 1858 1859 fprintf(stdout, "\n"); 1860 1861 return (0); 1862} 1863 1864static int32_t 1865snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj, 1866 struct snmp_value *val) 1867{ 1868 int32_t rc; 1869 asn_subid_t suboid; 1870 1871 if (obj == NULL || val == NULL) 1872 return (-1); 1873 1874 if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID) 1875 return (-1); 1876 1877 memset(obj, 0, sizeof(struct snmp_object)); 1878 asn_append_oid(&(obj->val.var), &(val->var)); 1879 obj->val.syntax = val->syntax; 1880 1881 if (obj->val.syntax > 0) 1882 rc = snmp_lookup_leafstring(snmptoolctx, obj); 1883 else 1884 rc = snmp_lookup_nonleaf_string(snmptoolctx, obj); 1885 1886 (void) snmp_suboid_append(&(val->var), suboid); 1887 (void) snmp_suboid_append(&(obj->val.var), suboid); 1888 1889 return (rc); 1890} 1891 1892static int32_t 1893snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx, 1894 struct asn_oid *oid) 1895{ 1896 uint8_t ip[4]; 1897 uint32_t bytes = 1; 1898 uint64_t cnt64; 1899 struct asn_oid temp, out; 1900 1901 if (oid->len < bytes) 1902 return (-1); 1903 1904 memset(&temp, 0, sizeof(struct asn_oid)); 1905 asn_append_oid(&temp, oid); 1906 1907 switch (stx->syntax) { 1908 case SNMP_SYNTAX_INTEGER: 1909 snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]); 1910 break; 1911 1912 case SNMP_SYNTAX_OCTETSTRING: 1913 if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] > 1914 ASN_MAXOCTETSTRING)) 1915 return (-1); 1916 snmp_output_octetindex(snmptoolctx, stx->tc, &temp); 1917 bytes += temp.subs[0]; 1918 break; 1919 1920 case SNMP_SYNTAX_OID: 1921 if ((temp.subs[0] > temp.len -1) || (temp.subs[0] > 1922 ASN_MAXOIDLEN)) 1923 return (-1); 1924 1925 bytes += temp.subs[0]; 1926 memset(&out, 0, sizeof(struct asn_oid)); 1927 asn_slice_oid(&out, &temp, 1, bytes); 1928 snmp_output_oid_value(snmptoolctx, &out); 1929 break; 1930 1931 case SNMP_SYNTAX_IPADDRESS: 1932 if (temp.len < 4) 1933 return (-1); 1934 for (bytes = 0; bytes < 4; bytes++) 1935 ip[bytes] = temp.subs[bytes]; 1936 1937 snmp_output_ipaddress(snmptoolctx, ip); 1938 bytes = 4; 1939 break; 1940 1941 case SNMP_SYNTAX_COUNTER: 1942 snmp_output_counter(snmptoolctx, temp.subs[0]); 1943 break; 1944 1945 case SNMP_SYNTAX_GAUGE: 1946 snmp_output_gauge(snmptoolctx, temp.subs[0]); 1947 break; 1948 1949 case SNMP_SYNTAX_TIMETICKS: 1950 snmp_output_ticks(snmptoolctx, temp.subs[0]); 1951 break; 1952 1953 case SNMP_SYNTAX_COUNTER64: 1954 if (oid->len < 2) 1955 return (-1); 1956 bytes = 2; 1957 memcpy(&cnt64, temp.subs, bytes); 1958 snmp_output_counter64(snmptoolctx, cnt64); 1959 break; 1960 1961 default: 1962 return (-1); 1963 } 1964 1965 return (bytes); 1966} 1967 1968static int32_t 1969snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o) 1970{ 1971 int32_t i, first, len; 1972 struct asn_oid oid; 1973 struct index *temp; 1974 1975 if (ISSET_NUMERIC(snmptoolctx)) 1976 return (-1); 1977 1978 if (o->info->table_idx == NULL) { 1979 fprintf(stdout,"%s.%d", o->info->string, 1980 o->val.var.subs[o->val.var.len - 1]); 1981 return (1); 1982 } 1983 1984 fprintf(stdout,"%s[", o->info->string); 1985 memset(&oid, 0, sizeof(struct asn_oid)); 1986 1987 len = 1; 1988 asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len), 1989 o->val.var.len); 1990 1991 first = 1; 1992 STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) { 1993 if(first) 1994 first = 0; 1995 else 1996 fprintf(stdout, ", "); 1997 if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0) 1998 break; 1999 len += i; 2000 memset(&oid, 0, sizeof(struct asn_oid)); 2001 asn_slice_oid(&oid, &(o->val.var), 2002 (o->info->table_idx->var.len + len), o->val.var.len + 1); 2003 } 2004 2005 fprintf(stdout,"]"); 2006 return (1); 2007} 2008 2009void 2010snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu) 2011{ 2012 char buf[ASN_OIDSTRLEN]; 2013 struct snmp_object object; 2014 2015 if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) { 2016 fprintf(stdout,"Invalid error index in PDU\n"); 2017 return; 2018 } 2019 2020 fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost, 2021 snmp_client.cport); 2022 2023 if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object, 2024 &(pdu->bindings[pdu->error_index - 1])) > 0)) 2025 snmp_output_object(snmptoolctx, &object); 2026 else { 2027 asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf); 2028 fprintf(stdout,"%s", buf); 2029 } 2030 2031 fprintf(stdout," caused error - "); 2032 if ((pdu->error_status > 0) && (pdu->error_status <= 2033 SNMP_ERR_INCONS_NAME)) 2034 fprintf(stdout, "%s\n", error_strings[pdu->error_status].str); 2035 else 2036 fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str); 2037} 2038 2039int32_t 2040snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu, 2041 struct asn_oid *root) 2042{ 2043 int32_t error; 2044 char p[ASN_OIDSTRLEN]; 2045 uint32_t i; 2046 struct snmp_object object; 2047 2048 i = error = 0; 2049 while (i < pdu->nbindings) { 2050 if (root != NULL && !(asn_is_suboid(root, 2051 &(pdu->bindings[i].var)))) 2052 break; 2053 2054 if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) { 2055 if (!ISSET_NUMERIC(snmptoolctx) && 2056 (snmp_fill_object(snmptoolctx, &object, 2057 &(pdu->bindings[i])) > 0)) 2058 snmp_output_object(snmptoolctx, &object); 2059 else { 2060 asn_oid2str_r(&(pdu->bindings[i].var), p); 2061 fprintf(stdout, "%s", p); 2062 } 2063 } 2064 error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info); 2065 i++; 2066 } 2067 2068 if (error) 2069 return (-1); 2070 2071 return (i); 2072} 2073 2074void 2075snmp_output_engine(void) 2076{ 2077 uint32_t i; 2078 char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2]; 2079 2080 cptr = engine; 2081 for (i = 0; i < snmp_client.engine.engine_len; i++) 2082 cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]); 2083 *cptr++ = '\0'; 2084 2085 fprintf(stdout, "Engine ID 0x%s\n", engine); 2086 fprintf(stdout, "Boots : %u\t\tTime : %d\n", 2087 snmp_client.engine.engine_boots, 2088 snmp_client.engine.engine_time); 2089} 2090 2091void 2092snmp_output_keys(void) 2093{ 2094 uint32_t i, keylen = 0; 2095 char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2]; 2096 2097 fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name); 2098 if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) { 2099 fprintf(stdout, "MD5 : 0x"); 2100 keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; 2101 } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) { 2102 fprintf(stdout, "SHA : 0x"); 2103 keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; 2104 } 2105 if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) { 2106 cptr = extkey; 2107 for (i = 0; i < keylen; i++) 2108 cptr += sprintf(cptr, "%.2x", 2109 snmp_client.user.auth_key[i]); 2110 *cptr++ = '\0'; 2111 fprintf(stdout, "%s\n", extkey); 2112 } 2113 2114 if (snmp_client.user.priv_proto == SNMP_PRIV_DES) { 2115 fprintf(stdout, "DES : 0x"); 2116 keylen = SNMP_PRIV_DES_KEY_SIZ; 2117 } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) { 2118 fprintf(stdout, "AES : 0x"); 2119 keylen = SNMP_PRIV_AES_KEY_SIZ; 2120 } 2121 if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) { 2122 cptr = extkey; 2123 for (i = 0; i < keylen; i++) 2124 cptr += sprintf(cptr, "%.2x", 2125 snmp_client.user.priv_key[i]); 2126 *cptr++ = '\0'; 2127 fprintf(stdout, "%s\n", extkey); 2128 } 2129} 2130