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