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