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