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