1 /* $OpenBSD: snmpc.c,v 1.28 2020/08/03 14:45:54 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> 5 * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/limits.h> 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 25 #include <arpa/inet.h> 26 #include <openssl/evp.h> 27 28 #include <ber.h> 29 #include <ctype.h> 30 #include <err.h> 31 #include <errno.h> 32 #include <locale.h> 33 #include <netdb.h> 34 #include <poll.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <stdint.h> 38 #include <string.h> 39 #include <time.h> 40 #include <unistd.h> 41 #include <util.h> 42 43 #include "smi.h" 44 #include "snmp.h" 45 #include "usm.h" 46 47 #define GETOPT_COMMON "A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:" 48 49 int snmpc_get(int, char *[]); 50 int snmpc_walk(int, char *[]); 51 int snmpc_set(int, char *[]); 52 int snmpc_trap(int, char *[]); 53 int snmpc_df(int, char *[]); 54 int snmpc_mibtree(int, char *[]); 55 struct snmp_agent *snmpc_connect(char *, char *); 56 int snmpc_parseagent(char *, char *); 57 int snmpc_print(struct ber_element *); 58 __dead void snmpc_printerror(enum snmp_error, struct ber_element *, int, 59 const char *); 60 char *snmpc_hex2bin(char *, size_t *); 61 struct ber_element *snmpc_varbindparse(int, char *[]); 62 void usage(void); 63 64 struct snmp_app { 65 const char *name; 66 const int usecommonopt; 67 const char *optstring; 68 const char *usage; 69 int (*exec)(int, char *[]); 70 }; 71 72 struct snmp_app snmp_apps[] = { 73 { "get", 1, NULL, "agent oid ...", snmpc_get }, 74 { "getnext", 1, NULL, "agent oid ...", snmpc_get }, 75 { "walk", 1, "C:", "[-C cIipt] [-C E endoid] [-C s skipoid] agent [oid]", snmpc_walk }, 76 { "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", snmpc_get }, 77 { "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] [-C s skipoid] agent [oid]", snmpc_walk }, 78 { "set", 1, NULL, "agent oid type value [oid type value] ...", snmpc_set }, 79 { "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap }, 80 { "df", 1, "C:", "[-Ch] [-Cr<maxrep>] agent", snmpc_df }, 81 { "mibtree", 0, "O:", "[-O fnS]", snmpc_mibtree } 82 }; 83 struct snmp_app *snmp_app = NULL; 84 85 char *community = "public"; 86 struct snmp_v3 *v3; 87 char *mib = "mib_2"; 88 int retries = 5; 89 int timeout = 1; 90 enum snmp_version version = SNMP_V2C; 91 int print_equals = 1; 92 int print_varbind_only = 0; 93 int print_summary = 0; 94 int print_time = 0; 95 int print_human = 0; 96 int walk_check_increase = 1; 97 int walk_fallback_oid = 1; 98 int walk_include_oid = 0; 99 int smi_print_hint = 1; 100 int non_repeaters = 0; 101 int max_repetitions = 10; 102 struct ber_oid walk_end = {{0}, 0}; 103 struct ber_oid *walk_skip = NULL; 104 size_t walk_skip_len = 0; 105 enum smi_oid_lookup oid_lookup = smi_oidl_short; 106 enum smi_output_string output_string = smi_os_default; 107 int utf8 = 0; 108 109 int 110 main(int argc, char *argv[]) 111 { 112 const EVP_MD *md = NULL; 113 const EVP_CIPHER *cipher = NULL; 114 struct snmp_sec *sec; 115 char *user = NULL; 116 enum usm_key_level authkeylevel = USM_KEY_UNSET; 117 char *authkey = NULL; 118 size_t authkeylen = 0; 119 enum usm_key_level privkeylevel = USM_KEY_UNSET; 120 char *privkey = NULL; 121 size_t privkeylen = 0; 122 int seclevel = SNMP_MSGFLAG_REPORT; 123 char *ctxname = NULL; 124 char *ctxengineid = NULL, *secengineid = NULL; 125 size_t ctxengineidlen, secengineidlen; 126 int zflag = 0; 127 long long boots = 0, time = 0; 128 char optstr[BUFSIZ]; 129 const char *errstr; 130 char *strtolp; 131 int ch; 132 size_t i; 133 134 /* 135 * Determine if output can handle UTF-8 based on locale. 136 */ 137 setlocale(LC_CTYPE, ""); 138 utf8 = MB_CUR_MAX > 1; 139 /* 140 * SMIv2 allows for UTF-8 text at some locations. 141 * Set it explicitly so we can handle it on the input side. 142 */ 143 if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) 144 errx(1, "setlocale(LC_CTYPE, \"en_US.UTF-8\") failed"); 145 146 if (pledge("stdio inet dns unix", NULL) == -1) 147 err(1, "pledge"); 148 149 if (argc <= 1) 150 usage(); 151 152 optstr[0] = '\0'; 153 for (i = 0; i < sizeof(snmp_apps)/sizeof(*snmp_apps); i++) { 154 if (strcmp(snmp_apps[i].name, argv[1]) == 0) { 155 snmp_app = &snmp_apps[i]; 156 if (snmp_app->optstring != NULL) { 157 if (strlcpy(optstr, snmp_app->optstring, 158 sizeof(optstr)) > sizeof(optstr)) 159 errx(1, "strlcat"); 160 } 161 break; 162 } 163 } 164 if (snmp_app == NULL) 165 usage(); 166 167 if (snmp_app->usecommonopt) { 168 if (strlcat(optstr, GETOPT_COMMON, sizeof(optstr)) > 169 sizeof(optstr)) 170 errx(1, "strlcpy"); 171 } 172 173 argc--; 174 argv++; 175 176 smi_init(); 177 178 while ((ch = getopt(argc, argv, optstr)) != -1) { 179 switch (ch) { 180 case 'A': 181 authkey = optarg; 182 authkeylen = strlen(authkey); 183 authkeylevel = USM_KEY_PASSWORD; 184 break; 185 case 'a': 186 if (strcasecmp(optarg, "MD5") == 0) 187 md = EVP_md5(); 188 else if (strcasecmp(optarg, "SHA") == 0) 189 md = EVP_sha1(); 190 else if (strcasecmp(optarg, "SHA-224") == 0) 191 md = EVP_sha224(); 192 else if (strcasecmp(optarg, "SHA-256") == 0) 193 md = EVP_sha256(); 194 else if (strcasecmp(optarg, "SHA-384") == 0) 195 md = EVP_sha384(); 196 else if (strcasecmp(optarg, "SHA-512") == 0) 197 md = EVP_sha512(); 198 else 199 errx(1, "Invalid authentication protocol " 200 "specified after -a flag: %s", optarg); 201 break; 202 case 'c': 203 community = optarg; 204 break; 205 case 'E': 206 ctxengineid = snmpc_hex2bin(optarg, 207 &ctxengineidlen); 208 if (ctxengineid == NULL) { 209 if (errno == EINVAL) 210 errx(1, "Bad engine ID value " 211 "after -3E flag."); 212 err(1, "-3E"); 213 } 214 break; 215 case 'e': 216 secengineid = snmpc_hex2bin(optarg, 217 &secengineidlen); 218 if (secengineid == NULL) { 219 if (errno == EINVAL) 220 errx(1, "Bad engine ID value " 221 "after -3e flag."); 222 err(1, "-3e"); 223 } 224 break; 225 case 'K': 226 privkey = snmpc_hex2bin(optarg, &privkeylen); 227 if (privkey == NULL) { 228 if (errno == EINVAL) 229 errx(1, "Bad key value after " 230 "-3K flag."); 231 errx(1, "-3K"); 232 } 233 privkeylevel = USM_KEY_LOCALIZED; 234 break; 235 case 'k': 236 authkey = snmpc_hex2bin(optarg, &authkeylen); 237 if (authkey == NULL) { 238 if (errno == EINVAL) 239 errx(1, "Bad key value after -k flag."); 240 err(1, "-k"); 241 } 242 authkeylevel = USM_KEY_LOCALIZED; 243 break; 244 case 'l': 245 if (strcasecmp(optarg, "noAuthNoPriv") == 0) 246 seclevel = SNMP_MSGFLAG_REPORT; 247 else if (strcasecmp(optarg, "authNoPriv") == 0) 248 seclevel = SNMP_MSGFLAG_AUTH | 249 SNMP_MSGFLAG_REPORT; 250 else if (strcasecmp(optarg, "authPriv") == 0) 251 seclevel = SNMP_MSGFLAG_AUTH | 252 SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT; 253 else 254 errx(1, "Invalid security level specified " 255 "after -l flag: %s", optarg); 256 break; 257 case 'n': 258 ctxname = optarg; 259 break; 260 case 'r': 261 if ((retries = strtonum(optarg, 0, INT_MAX, 262 &errstr)) == 0) { 263 if (errstr != NULL) 264 errx(1, "-r: %s argument", errstr); 265 } 266 break; 267 case 't': 268 if ((timeout = strtonum(optarg, 1, INT_MAX, 269 &errstr)) == 0) { 270 if (errstr != NULL) 271 errx(1, "-t: %s argument", errstr); 272 } 273 break; 274 case 'u': 275 user = optarg; 276 break; 277 case 'v': 278 if (strcmp(optarg, "1") == 0) 279 version = SNMP_V1; 280 else if (strcmp(optarg, "2c") == 0) 281 version = SNMP_V2C; 282 else if (strcmp(optarg, "3") == 0) 283 version = SNMP_V3; 284 else 285 errc(1, EINVAL, "-v"); 286 break; 287 case 'C': 288 for (i = 0; i < strlen(optarg); i++) { 289 switch (optarg[i]) { 290 case 'c': 291 if (strcmp(snmp_app->name, "walk") && 292 strcmp(snmp_app->name, "bulkwalk")) 293 usage(); 294 walk_check_increase = 0; 295 break; 296 case 'h': 297 if (strcmp(snmp_app->name, "df")) 298 usage(); 299 print_human = 1; 300 break; 301 case 'i': 302 if (strcmp(snmp_app->name, "walk") && 303 strcmp(snmp_app->name, "bulkwalk")) 304 usage(); 305 walk_include_oid = 1; 306 break; 307 case 'n': 308 if (strcmp(snmp_app->name, "bulkget") && 309 strcmp(snmp_app->name, "bulkwalk")) 310 usage(); 311 errno = 0; 312 non_repeaters = strtol(&optarg[i + 1], 313 &strtolp, 10); 314 if (non_repeaters < 0 || 315 errno == ERANGE) { 316 if (non_repeaters < 0) 317 errx(1, "%s%s", 318 "-Cn: too small ", 319 "argument"); 320 else 321 errx(1, "%s%s", 322 "-Cn: too large", 323 "argument"); 324 } else if (&optarg[i + 1] == strtolp) 325 errx(1, "-Cn invalid argument"); 326 i = strtolp - optarg - 1; 327 break; 328 case 'p': 329 if (strcmp(snmp_app->name, "walk") && 330 strcmp(snmp_app->name, "bulkwalk")) 331 usage(); 332 print_summary = 1; 333 break; 334 case 'r': 335 if (strcmp(snmp_app->name, "bulkget") && 336 strcmp(snmp_app->name, "bulkwalk") && 337 strcmp(snmp_app->name, "df")) 338 usage(); 339 errno = 0; 340 max_repetitions = strtol(&optarg[i + 1], 341 &strtolp, 10); 342 if (max_repetitions < 0 || 343 errno == ERANGE) { 344 if (max_repetitions < 0) 345 errx(1, "%s%s", 346 "-Cr: too small ", 347 "argument"); 348 else 349 errx(1, "%s%s", 350 "-Cr: too large", 351 "argument"); 352 } else if (&optarg[i + 1] == strtolp) 353 errx(1, "-Cr invalid argument"); 354 i = strtolp - optarg - 1; 355 break; 356 case 's': 357 if (strcmp(snmp_app->name, "walk") && 358 strcmp(snmp_app->name, "bulkwalk")) 359 usage(); 360 if ((walk_skip = recallocarray( 361 walk_skip, walk_skip_len, 362 walk_skip_len + 1, 363 sizeof(*walk_skip))) == NULL) 364 errx(1, "malloc"); 365 if (smi_string2oid(argv[optind], 366 &(walk_skip[walk_skip_len])) != 0) 367 errx(1, "%s: %s", 368 "Unknown Object Identifier", 369 argv[optind]); 370 walk_skip_len++; 371 optind++; 372 break; 373 case 't': 374 if (strcmp(snmp_app->name, "walk")) 375 usage(); 376 print_time = 1; 377 break; 378 case 'E': 379 if (strcmp(snmp_app->name, "walk")) 380 usage(); 381 if (smi_string2oid(argv[optind], 382 &walk_end) != 0) 383 errx(1, "%s: %s", 384 "Unknown Object Identifier", 385 argv[optind]); 386 optind++; 387 continue; 388 case 'I': 389 if (strcmp(snmp_app->name, "walk")) 390 usage(); 391 walk_fallback_oid = 0; 392 break; 393 default: 394 usage(); 395 } 396 if (optarg[i] == 'E') 397 break; 398 } 399 break; 400 case 'O': 401 for (i = 0; i < strlen(optarg); i++) { 402 if (strcmp(snmp_app->name, "mibtree") == 0 && 403 optarg[i] != 'f' && optarg[i] != 'n' && 404 optarg[i] != 'S') 405 usage(); 406 switch (optarg[i]) { 407 case 'a': 408 output_string = smi_os_ascii; 409 break; 410 case 'f': 411 oid_lookup = smi_oidl_full; 412 break; 413 case 'n': 414 oid_lookup = smi_oidl_numeric; 415 break; 416 case 'q': 417 print_equals = 0; 418 smi_print_hint = 0; 419 break; 420 case 'v': 421 print_varbind_only = 1; 422 break; 423 case 'x': 424 output_string = smi_os_hex; 425 break; 426 case 'S': 427 oid_lookup = smi_oidl_short; 428 break; 429 case 'Q': 430 smi_print_hint = 0; 431 break; 432 default: 433 usage(); 434 } 435 } 436 break; 437 case 'X': 438 privkey = optarg; 439 privkeylen = strlen(privkey); 440 privkeylevel = USM_KEY_PASSWORD; 441 break; 442 case 'x': 443 if (strcasecmp(optarg, "DES") == 0) 444 cipher = EVP_des_cbc(); 445 else if (strcasecmp(optarg, "AES") == 0) 446 cipher = EVP_aes_128_cfb128(); 447 else 448 errx(1, "Invalid privacy protocol " 449 "specified after -3x flag: %s", 450 optarg); 451 break; 452 case 'Z': 453 boots = strtoll(optarg, &strtolp, 10); 454 if (boots < 0 || strtolp == optarg || strtolp[0] != ',') 455 usage(); 456 strtolp++; 457 while (strtolp[0] == ' ' && strtolp[0] == '\t') 458 strtolp++; 459 time = strtoll(strtolp, &strtolp, 10); 460 if (boots < 0 || strtolp == optarg) 461 usage(); 462 zflag = 1; 463 break; 464 default: 465 usage(); 466 } 467 } 468 argc -= optind; 469 argv += optind; 470 471 if (version == SNMP_V3) { 472 /* Setup USM */ 473 if (user == NULL || user[0] == '\0') 474 errx(1, "No securityName specified"); 475 if ((sec = usm_init(user, strlen(user))) == NULL) 476 err(1, "usm_init"); 477 if (seclevel & SNMP_MSGFLAG_AUTH) { 478 if (md == NULL) 479 md = EVP_md5(); 480 if (authkey == NULL) 481 errx(1, "No authKey or authPassword specified"); 482 if (usm_setauth(sec, md, authkey, authkeylen, 483 authkeylevel) == -1) 484 err(1, "Can't set authkey"); 485 } 486 if (seclevel & SNMP_MSGFLAG_PRIV) { 487 if (cipher == NULL) 488 cipher = EVP_des_cbc(); 489 if (privkey == NULL) 490 errx(1, "No privKey or privPassword specified"); 491 if (usm_setpriv(sec, cipher, privkey, privkeylen, 492 privkeylevel) == -1) 493 err(1, "Can't set authkey"); 494 } 495 if (secengineid != NULL) { 496 if (usm_setengineid(sec, secengineid, 497 secengineidlen) == -1) 498 err(1, "Can't set secengineid"); 499 } 500 if (zflag) 501 if (usm_setbootstime(sec, boots, time) == -1) 502 err(1, "Can't set boots/time"); 503 v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 : 504 strlen(ctxname), sec); 505 if (v3 == NULL) 506 err(1, "snmp_v3_init"); 507 if (ctxengineid != NULL) { 508 if (snmp_v3_setengineid(v3, ctxengineid, 509 ctxengineidlen) == -1) 510 err(1, "Can't set ctxengineid"); 511 } 512 } 513 514 515 return snmp_app->exec(argc, argv); 516 } 517 518 int 519 snmpc_get(int argc, char *argv[]) 520 { 521 struct ber_oid *oid; 522 struct ber_element *pdu, *varbind; 523 struct snmp_agent *agent; 524 int errorstatus, errorindex; 525 int i; 526 int class; 527 unsigned type; 528 char *hint = NULL; 529 530 if (argc < 2) 531 usage(); 532 533 if ((agent = snmpc_connect(argv[0], "161")) == NULL) 534 err(1, "%s", snmp_app->name); 535 agent->timeout = timeout; 536 agent->retries = retries; 537 538 if (pledge("stdio", NULL) == -1) 539 err(1, "pledge"); 540 argc--; 541 argv++; 542 543 oid = reallocarray(NULL, argc, sizeof(*oid)); 544 if (oid == NULL) 545 err(1, "malloc"); 546 for (i = 0; i < argc; i++) { 547 if (smi_string2oid(argv[i], &oid[i]) == -1) 548 errx(1, "%s: Unknown object identifier", argv[i]); 549 } 550 if (strcmp(snmp_app->name, "getnext") == 0) { 551 if ((pdu = snmp_getnext(agent, oid, argc)) == NULL) 552 err(1, "getnext"); 553 } else if (strcmp(snmp_app->name, "bulkget") == 0) { 554 if (version < SNMP_V2C) 555 errx(1, "Cannot send V2 PDU on V1 session"); 556 if (non_repeaters > argc) 557 errx(1, "need more objects than -Cn<num>"); 558 if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters, 559 max_repetitions)) == NULL) 560 err(1, "bulkget"); 561 } else { 562 if ((pdu = snmp_get(agent, oid, argc)) == NULL) 563 err(1, "get"); 564 } 565 566 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus, 567 &errorindex, &varbind); 568 if (errorstatus != 0) { 569 if (errorindex >= 1 && errorindex <= argc) 570 hint = argv[errorindex - 1]; 571 snmpc_printerror((enum snmp_error) errorstatus, varbind, 572 errorindex, hint); 573 } 574 575 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 576 printf("Received report:\n"); 577 for (; varbind != NULL; varbind = varbind->be_next) { 578 if (!snmpc_print(varbind)) 579 err(1, "Can't print response"); 580 } 581 ober_free_elements(pdu); 582 snmp_free_agent(agent); 583 return 0; 584 } 585 586 int 587 snmpc_walk(int argc, char *argv[]) 588 { 589 struct ber_oid oid, loid, noid; 590 struct ber_element *pdu, *varbind, *value; 591 struct timespec start, finish; 592 struct snmp_agent *agent; 593 const char *oids; 594 int n = 0, prev_cmp, skip_cmp; 595 int errorstatus, errorindex; 596 int class; 597 size_t i; 598 unsigned type; 599 600 if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C) 601 errx(1, "Cannot send V2 PDU on V1 session"); 602 if (argc < 1 || argc > 2) 603 usage(); 604 oids = argc == 1 ? mib : argv[1]; 605 606 if ((agent = snmpc_connect(argv[0], "161"))== NULL) 607 err(1, "%s", snmp_app->name); 608 agent->timeout = timeout; 609 agent->retries = retries; 610 if (pledge("stdio", NULL) == -1) 611 err(1, "pledge"); 612 613 if (smi_string2oid(oids, &oid) == -1) 614 errx(1, "%s: Unknown object identifier", oids); 615 bcopy(&oid, &noid, sizeof(noid)); 616 if (print_time) 617 clock_gettime(CLOCK_MONOTONIC, &start); 618 619 if (walk_include_oid) { 620 if ((pdu = snmp_get(agent, &oid, 1)) == NULL) 621 err(1, "%s", snmp_app->name); 622 623 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 624 &errorstatus, &errorindex, &varbind); 625 if (errorstatus != 0) 626 snmpc_printerror((enum snmp_error) errorstatus, varbind, 627 errorindex, oids); 628 629 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 630 printf("Received report:\n"); 631 if (!snmpc_print(varbind)) 632 err(1, "Can't print response"); 633 ober_free_element(pdu); 634 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 635 return 1; 636 n++; 637 } 638 while (1) { 639 for (i = 0; i < walk_skip_len; i++) { 640 skip_cmp = ober_oid_cmp(&(walk_skip[i]), &noid); 641 if (skip_cmp == 0 || skip_cmp == 2) { 642 bcopy(&(walk_skip[i]), &noid, sizeof(noid)); 643 noid.bo_id[noid.bo_n -1]++; 644 break; 645 } 646 } 647 bcopy(&noid, &loid, sizeof(loid)); 648 if (strcmp(snmp_app->name, "bulkwalk") == 0) { 649 if ((pdu = snmp_getbulk(agent, &noid, 1, 650 non_repeaters, max_repetitions)) == NULL) 651 err(1, "bulkwalk"); 652 } else { 653 if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL) 654 err(1, "walk"); 655 } 656 657 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 658 &errorstatus, &errorindex, &varbind); 659 if (errorstatus != 0) { 660 snmpc_printerror((enum snmp_error) errorstatus, varbind, 661 errorindex, NULL); 662 } 663 664 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 665 printf("Received report:\n"); 666 for (; varbind != NULL; varbind = varbind->be_next) { 667 (void) ober_scanf_elements(varbind, "{oe}", &noid, 668 &value); 669 if (value->be_class == BER_CLASS_CONTEXT && 670 value->be_type == BER_TYPE_EOC) 671 break; 672 for (i = 0; i < walk_skip_len; i++) { 673 skip_cmp = ober_oid_cmp(&(walk_skip[i]), &noid); 674 if (skip_cmp == 0 || skip_cmp == 2) 675 break; 676 } 677 if (i < walk_skip_len) 678 continue; 679 prev_cmp = ober_oid_cmp(&loid, &noid); 680 if (walk_check_increase && prev_cmp == -1) 681 errx(1, "OID not increasing"); 682 if (prev_cmp == 0 || ober_oid_cmp(&oid, &noid) != 2) 683 break; 684 if (walk_end.bo_n != 0 && 685 ober_oid_cmp(&walk_end, &noid) != -1) 686 break; 687 688 if (!snmpc_print(varbind)) 689 err(1, "Can't print response"); 690 n++; 691 } 692 ober_free_elements(pdu); 693 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 694 return 1; 695 if (varbind != NULL) 696 break; 697 } 698 if (walk_fallback_oid && n == 0) { 699 if ((pdu = snmp_get(agent, &oid, 1)) == NULL) 700 err(1, "%s", snmp_app->name); 701 702 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 703 &errorstatus, &errorindex, &varbind); 704 if (errorstatus != 0) 705 snmpc_printerror((enum snmp_error) errorstatus, varbind, 706 errorindex, oids); 707 708 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 709 printf("Received report:\n"); 710 if (!snmpc_print(varbind)) 711 err(1, "Can't print response"); 712 ober_free_element(pdu); 713 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 714 return 1; 715 n++; 716 } 717 if (print_time) 718 clock_gettime(CLOCK_MONOTONIC, &finish); 719 if (print_summary) 720 printf("Variables found: %d\n", n); 721 if (print_time) { 722 if ((finish.tv_nsec -= start.tv_nsec) < 0) { 723 finish.tv_sec -= 1; 724 finish.tv_nsec += 1000000000; 725 } 726 finish.tv_sec -= start.tv_sec; 727 fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n", 728 finish.tv_sec, finish.tv_nsec); 729 } 730 snmp_free_agent(agent); 731 return 0; 732 } 733 734 int 735 snmpc_set(int argc, char *argv[]) 736 { 737 struct snmp_agent *agent; 738 struct ber_element *pdu, *varbind; 739 int errorstatus, errorindex; 740 int class; 741 unsigned type; 742 char *hint = NULL; 743 744 if (argc < 4) 745 usage(); 746 if ((agent = snmpc_connect(argv[0], "161")) == NULL) 747 err(1, "%s", snmp_app->name); 748 argc--; 749 argv++; 750 751 if (pledge("stdio", NULL) == -1) 752 err(1, "pledge"); 753 754 if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL) 755 err(1, "set"); 756 757 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus, 758 &errorindex, &varbind); 759 if (errorstatus != 0) { 760 if (errorindex >= 1 && errorindex <= argc / 3) 761 hint = argv[(errorindex - 1) * 3]; 762 snmpc_printerror((enum snmp_error) errorstatus, varbind, 763 errorindex, hint); 764 } 765 766 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 767 printf("Received report:\n"); 768 for (; varbind != NULL; varbind = varbind->be_next) { 769 if (!snmpc_print(varbind)) 770 err(1, "Can't print response"); 771 } 772 ober_free_elements(pdu); 773 snmp_free_agent(agent); 774 return 0; 775 } 776 777 int 778 snmpc_trap(int argc, char *argv[]) 779 { 780 struct snmp_agent *agent; 781 struct timespec ts; 782 struct ber_oid trapoid; 783 const char *errstr = NULL; 784 long long lval; 785 786 if (version == SNMP_V1) 787 errx(1, "trap is not supported for snmp v1"); 788 789 if ((agent = snmpc_connect(argv[0], "162")) == NULL) 790 err(1, "%s", snmp_app->name); 791 792 if (pledge("stdio", NULL) == -1) 793 err(1, "pledge"); 794 795 if (argv[1][0] == '\0') { 796 if (clock_gettime(CLOCK_UPTIME, &ts) == -1) 797 err(1, "clock_gettime"); 798 } else { 799 lval = strtonum(argv[1], 0, LLONG_MAX, &errstr); 800 if (errstr != NULL) 801 errx(1, "Bad value notation (%s)", argv[1]); 802 ts.tv_sec = lval / 100; 803 ts.tv_nsec = (lval % 100) * 10000000; 804 } 805 if (smi_string2oid(argv[2], &trapoid) == -1) 806 errx(1, "Invalid oid: %s\n", argv[2]); 807 808 argc -= 3; 809 argv += 3; 810 811 snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv)); 812 813 return 0; 814 } 815 816 #define INCR_NEXTTAB(x) ((x + 8) & ~7) 817 #define NEXTTAB(x) (8 - (x & 7)) 818 int 819 snmpc_df(int argc, char *argv[]) 820 { 821 struct snmpc_df { 822 uint32_t index; 823 /* DisplayString is 255a DISPLAY-HINT */ 824 char descr[256]; 825 /* Theoretical maximum for 2 32 bit values multiplied */ 826 char size[21]; 827 char used[21]; 828 char avail[21]; 829 char proc[5]; 830 } *df = NULL; 831 struct ber_oid descroid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 3 }, 11}; 832 struct ber_oid unitsoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 4 }, 11}; 833 struct ber_oid sizeoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 5 }, 11}; 834 struct ber_oid usedoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 6 }, 11}; 835 struct ber_oid oid, *reqoid; 836 struct ber_element *pdu, *varbind; 837 struct snmp_agent *agent; 838 int errorstatus, errorindex; 839 int class; 840 size_t i, j, rows = 0; 841 unsigned type; 842 char *string; 843 int descrlen = 0, sizelen = 0, usedlen = 0, availlen = 0, proclen = 0; 844 int len; 845 long long units, size, used; 846 int fmtret; 847 848 if (argc != 1) 849 usage(); 850 851 if ((agent = snmpc_connect(argv[0], "161")) == NULL) 852 err(1, "%s", snmp_app->name); 853 agent->timeout = timeout; 854 agent->retries = retries; 855 856 if (pledge("stdio", NULL) == -1) 857 err(1, "pledge"); 858 859 descrlen = sizeof("Description") - 1; 860 sizelen = sizeof("Size") - 1; 861 usedlen = sizeof("Used") - 1; 862 availlen = sizeof("Available") - 1; 863 proclen = sizeof("Used%") - 1; 864 865 bcopy(&descroid, &oid, sizeof(descroid)); 866 867 i = 0; 868 while(1) { 869 if (version < SNMP_V2C) { 870 if ((pdu = snmp_getnext(agent, &oid, 1)) == NULL) 871 err(1, "df"); 872 } else { 873 if ((pdu = snmp_getbulk(agent, &oid, 1, 0, 874 max_repetitions)) == NULL) 875 err(1, "df"); 876 } 877 878 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 879 &errorstatus, &errorindex, &varbind); 880 if (errorstatus != 0) 881 snmpc_printerror((enum snmp_error) errorstatus, varbind, 882 errorindex, NULL); 883 884 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) { 885 printf("Received report:\n"); 886 for (; varbind != NULL; varbind = varbind->be_next) { 887 if (!snmpc_print(varbind)) 888 err(1, "Can't print response"); 889 } 890 return 1; 891 } 892 for (; varbind != NULL; varbind = varbind->be_next) { 893 (void) ober_scanf_elements(varbind, "{oS", &oid); 894 if (ober_oid_cmp(&descroid, &oid) != 2) 895 break; 896 rows++; 897 } 898 if ((df = reallocarray(df, rows, sizeof(*df))) == NULL) 899 err(1, "malloc"); 900 (void) ober_scanf_elements(pdu, "{SSS{e", &varbind); 901 for (; i < rows; varbind = varbind->be_next, i++) { 902 if (ober_scanf_elements(varbind, "{os", &oid, 903 &string) == -1) { 904 i--; 905 rows--; 906 continue; 907 } 908 df[i].index = oid.bo_id[oid.bo_n - 1]; 909 len = strlcpy(df[i].descr, string, 910 sizeof(df[i].descr)); 911 if (len > (int) sizeof(df[i].descr)) 912 len = (int) sizeof(df[i].descr) - 1; 913 if (len > descrlen) 914 descrlen = len; 915 } 916 ober_free_elements(pdu); 917 if (varbind != NULL) 918 break; 919 } 920 921 if (max_repetitions < 3) 922 max_repetitions = 3; 923 if ((reqoid = reallocarray(NULL, max_repetitions, sizeof(*reqoid))) == NULL) 924 err(1, "malloc"); 925 for (i = 0; i < rows;) { 926 for (j = 0; i + j < rows && j < (size_t)max_repetitions / 3; 927 j++) { 928 bcopy(&unitsoid, &(reqoid[(j * 3) + 0]), 929 sizeof(unitsoid)); 930 reqoid[(j * 3) + 0].bo_id[ 931 reqoid[(j * 3) + 0].bo_n++] = df[i + j].index; 932 bcopy(&sizeoid, &(reqoid[(j * 3) + 1]), 933 sizeof(sizeoid)); 934 reqoid[(j * 3) + 1].bo_id[ 935 reqoid[(j * 3) + 1].bo_n++] = df[i + j].index; 936 bcopy(&usedoid, &(reqoid[(j * 3) + 2]), 937 sizeof(usedoid)); 938 reqoid[(j * 3) + 2].bo_id[ 939 reqoid[(j * 3) + 2].bo_n++] = df[i + j].index; 940 } 941 if ((pdu = snmp_get(agent, reqoid, j * 3)) == NULL) 942 err(1, "df"); 943 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 944 &errorstatus, &errorindex, &varbind); 945 if (errorstatus != 0) 946 snmpc_printerror((enum snmp_error) errorstatus, varbind, 947 errorindex, NULL); 948 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) { 949 printf("Received report:\n"); 950 for (; varbind != NULL; varbind = varbind->be_next) { 951 if (!snmpc_print(varbind)) 952 err(1, "Can't print response"); 953 } 954 } 955 for (j = 0; varbind != NULL; i++) { 956 if (ober_scanf_elements(varbind, "{oi}{oi}{oi}", 957 &(reqoid[0]), &units, &(reqoid[1]), &size, 958 &(reqoid[2]), &used, &varbind) == -1) { 959 break; 960 } 961 varbind = varbind->be_next->be_next->be_next; 962 963 unitsoid.bo_id[unitsoid.bo_n++] = df[i].index; 964 if (ober_oid_cmp(&unitsoid, &(reqoid[0])) != 0) { 965 warnx("df: received invalid object"); 966 break; 967 } 968 unitsoid.bo_n--; 969 sizeoid.bo_id[sizeoid.bo_n++] = df[i].index; 970 if (ober_oid_cmp(&sizeoid, &(reqoid[1])) != 0) { 971 warnx("df: received invalid object"); 972 break; 973 } 974 sizeoid.bo_n--; 975 usedoid.bo_id[usedoid.bo_n++] = df[i].index; 976 if (ober_oid_cmp(&usedoid, &(reqoid[2])) != 0) { 977 warnx("df: received invalid object"); 978 break; 979 } 980 usedoid.bo_n--; 981 if (print_human) 982 fmtret = fmt_scaled((units * size), df[i].size); 983 if (!print_human || fmtret == -1) 984 snprintf(df[i].size, sizeof(df[i].size), "%lld", 985 (units * size) / 1024); 986 len = (int) strlen(df[i].size); 987 if (len > sizelen) 988 sizelen = len; 989 if (print_human) 990 fmtret = fmt_scaled(units * used, df[i].used); 991 if (!print_human || fmtret == -1) 992 snprintf(df[i].used, sizeof(df[i].used), "%lld", 993 (units * used) / 1024); 994 len = (int) strlen(df[i].used); 995 if (len > usedlen) 996 usedlen = len; 997 if (print_human) 998 fmtret = fmt_scaled(units * (size - used), 999 df[i].avail); 1000 if (!print_human || fmtret == -1) 1001 snprintf(df[i].avail, sizeof(df[i].avail), 1002 "%lld", (units * (size - used)) / 1024); 1003 len = (int) strlen(df[i].avail); 1004 if (len > availlen) 1005 availlen = len; 1006 if (size == 0) 1007 strlcpy(df[i].proc, "0%", sizeof(df[i].proc)); 1008 else { 1009 snprintf(df[i].proc, sizeof(df[i].proc), 1010 "%lld%%", (used * 100) / size); 1011 } 1012 len = (int) strlen(df[i].proc); 1013 if (len > proclen) 1014 proclen = len; 1015 j++; 1016 } 1017 if (j == 0) { 1018 warnx("Failed to retrieve information for %s", 1019 df[i].descr); 1020 memmove(df + i, df + i + 1, 1021 (rows - i - 1) * sizeof(*df)); 1022 rows--; 1023 i--; 1024 } 1025 } 1026 1027 printf("%-*s%*s%*s%*s%*s\n", 1028 descrlen, "Description", 1029 NEXTTAB(descrlen) + sizelen, "Size", 1030 NEXTTAB(sizelen) + usedlen, "Used", 1031 NEXTTAB(usedlen) + availlen, "Available", 1032 NEXTTAB(availlen) + proclen, "Used%"); 1033 for (i = 0; i < rows; i++) { 1034 printf("%-*s%*s%*s%*s%*s\n", 1035 descrlen, df[i].descr, 1036 NEXTTAB(descrlen) + sizelen, df[i].size, 1037 NEXTTAB(sizelen) + usedlen, df[i].used, 1038 NEXTTAB(usedlen) + availlen, df[i].avail, 1039 NEXTTAB(availlen) + proclen, df[i].proc); 1040 } 1041 1042 return 0; 1043 } 1044 1045 int 1046 snmpc_mibtree(int argc, char *argv[]) 1047 { 1048 struct oid *oid; 1049 char buf[BUFSIZ]; 1050 1051 for (oid = NULL; (oid = smi_foreach(oid)) != NULL;) { 1052 smi_oid2string(&oid->o_id, buf, sizeof(buf), oid_lookup); 1053 printf("%s\n", buf); 1054 } 1055 return 0; 1056 } 1057 1058 struct snmp_agent * 1059 snmpc_connect(char *host, char *port) 1060 { 1061 switch (version) { 1062 case SNMP_V1: 1063 case SNMP_V2C: 1064 return snmp_connect_v12(snmpc_parseagent(host, port), version, 1065 community); 1066 case SNMP_V3: 1067 return snmp_connect_v3(snmpc_parseagent(host, port), v3); 1068 } 1069 return NULL; 1070 } 1071 1072 int 1073 snmpc_print(struct ber_element *elm) 1074 { 1075 struct ber_oid oid; 1076 char oids[SNMP_MAX_OID_STRLEN]; 1077 char *value; 1078 1079 elm = elm->be_sub; 1080 if (ober_get_oid(elm, &oid) != 0) { 1081 errno = EINVAL; 1082 return 0; 1083 } 1084 1085 elm = elm->be_next; 1086 value = smi_print_element(&oid, elm, smi_print_hint, output_string, 1087 oid_lookup, utf8); 1088 if (value == NULL) 1089 return 0; 1090 1091 if (print_varbind_only) 1092 printf("%s\n", value); 1093 else if (print_equals) { 1094 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup); 1095 printf("%s = %s\n", oids, value); 1096 } else { 1097 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup); 1098 printf("%s %s\n", oids, value); 1099 } 1100 free(value); 1101 1102 return 1; 1103 } 1104 1105 __dead void 1106 snmpc_printerror(enum snmp_error error, struct ber_element *varbind, 1107 int index, const char *hint) 1108 { 1109 struct ber_oid hoid, vboid; 1110 char oids[SNMP_MAX_OID_STRLEN]; 1111 const char *oid = NULL; 1112 int i; 1113 1114 if (index >= 1) { 1115 /* Only print if the index is in the reply */ 1116 for (i = 1; varbind != NULL && i < index; 1117 varbind = varbind->be_next) 1118 i++; 1119 if (varbind != NULL && 1120 ober_get_oid(varbind->be_sub, &vboid) == 0) { 1121 /* If user and reply conform print user input */ 1122 if (hint != NULL && 1123 smi_string2oid(hint, &hoid) == 0 && 1124 ober_oid_cmp(&hoid, &vboid) == 0) 1125 oid = hint; 1126 else 1127 oid = smi_oid2string(&vboid, oids, 1128 sizeof(oids), oid_lookup); 1129 } 1130 } 1131 if (oid == NULL) 1132 oid = "?"; 1133 1134 switch (error) { 1135 case SNMP_ERROR_NONE: 1136 errx(1, "No error, how did I get here?"); 1137 case SNMP_ERROR_TOOBIG: 1138 errx(1, "Can't parse oid %s: Response too big", oid); 1139 case SNMP_ERROR_NOSUCHNAME: 1140 errx(1, "Can't parse oid %s: No such object", oid); 1141 case SNMP_ERROR_BADVALUE: 1142 errx(1, "Can't parse oid %s: Bad value", oid); 1143 case SNMP_ERROR_READONLY: 1144 errx(1, "Can't parse oid %s: Read only", oid); 1145 case SNMP_ERROR_GENERR: 1146 errx(1, "Can't parse oid %s: Generic error", oid); 1147 case SNMP_ERROR_NOACCESS: 1148 errx(1, "Can't parse oid %s: Access denied", oid); 1149 case SNMP_ERROR_WRONGTYPE: 1150 errx(1, "Can't parse oid %s: Wrong type", oid); 1151 case SNMP_ERROR_WRONGLENGTH: 1152 errx(1, "Can't parse oid %s: Wrong length", oid); 1153 case SNMP_ERROR_WRONGENC: 1154 errx(1, "Can't parse oid %s: Wrong encoding", oid); 1155 case SNMP_ERROR_WRONGVALUE: 1156 errx(1, "Can't parse oid %s: Wrong value", oid); 1157 case SNMP_ERROR_NOCREATION: 1158 errx(1, "Can't parse oid %s: Can't be created", oid); 1159 case SNMP_ERROR_INCONVALUE: 1160 errx(1, "Can't parse oid %s: Inconsistent value", oid); 1161 case SNMP_ERROR_RESUNAVAIL: 1162 errx(1, "Can't parse oid %s: Resource unavailable", oid); 1163 case SNMP_ERROR_COMMITFAILED: 1164 errx(1, "Can't parse oid %s: Commit failed", oid); 1165 case SNMP_ERROR_UNDOFAILED: 1166 errx(1, "Can't parse oid %s: Undo faild", oid); 1167 case SNMP_ERROR_AUTHERROR: 1168 errx(1, "Can't parse oid %s: Authorization error", oid); 1169 case SNMP_ERROR_NOTWRITABLE: 1170 errx(1, "Can't parse oid %s: Not writable", oid); 1171 case SNMP_ERROR_INCONNAME: 1172 errx(1, "Can't parse oid %s: Inconsistent name", oid); 1173 } 1174 errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error); 1175 } 1176 1177 int 1178 snmpc_parseagent(char *agent, char *defaultport) 1179 { 1180 struct addrinfo hints, *ai, *ai0 = NULL; 1181 struct sockaddr_un saddr; 1182 char *agentdup, *specifier, *hostname, *port = NULL; 1183 int error; 1184 int s; 1185 1186 if ((agentdup = specifier = strdup(agent)) == NULL) 1187 err(1, NULL); 1188 1189 bzero(&hints, sizeof(hints)); 1190 if ((hostname = strchr(specifier, ':')) != NULL) { 1191 *hostname++ = '\0'; 1192 if (strcasecmp(specifier, "udp") == 0) { 1193 hints.ai_family = AF_INET; 1194 hints.ai_socktype = SOCK_DGRAM; 1195 } else if (strcasecmp(specifier, "tcp") == 0) { 1196 hints.ai_family = AF_INET; 1197 hints.ai_socktype = SOCK_STREAM; 1198 } else if (strcasecmp(specifier, "udp6") == 0 || 1199 strcasecmp(specifier, "udpv6") == 0 || 1200 strcasecmp(specifier, "udpipv6") == 0) { 1201 hints.ai_family = AF_INET6; 1202 hints.ai_socktype = SOCK_DGRAM; 1203 } else if (strcasecmp(specifier, "tcp6") == 0 || 1204 strcasecmp(specifier, "tcpv6") == 0 || 1205 strcasecmp(specifier, "tcpipv6") == 0) { 1206 hints.ai_family = AF_INET6; 1207 hints.ai_socktype = SOCK_STREAM; 1208 } else if (strcasecmp(specifier, "unix") == 0) { 1209 hints.ai_family = AF_UNIX; 1210 hints.ai_addr = (struct sockaddr *)&saddr; 1211 hints.ai_addrlen = sizeof(saddr); 1212 saddr.sun_len = sizeof(saddr); 1213 saddr.sun_family = AF_UNIX; 1214 if (strlcpy(saddr.sun_path, hostname, 1215 sizeof(saddr.sun_path)) > sizeof(saddr.sun_path)) 1216 errx(1, "Hostname path too long"); 1217 ai = &hints; 1218 } else { 1219 *--hostname = ':'; 1220 hostname = specifier; 1221 } 1222 } else { 1223 hostname = specifier; 1224 } 1225 1226 if (hints.ai_family == AF_INET) { 1227 if ((port = strchr(hostname, ':')) != NULL) 1228 *port++ = '\0'; 1229 } else if (hints.ai_family == AF_INET6 || hints.ai_family == 0) { 1230 if (hostname[0] == '[') { 1231 hints.ai_family = AF_INET6; 1232 hostname++; 1233 if ((port = strchr(hostname, ']')) == NULL) 1234 errx(1, "invalid agent"); 1235 *port++ = '\0'; 1236 if (port[0] == ':') 1237 *port++ = '\0'; 1238 else if (port[0] == '\0') 1239 port = NULL; 1240 else 1241 errx(1, "invalid agent"); 1242 } else { 1243 if ((port = strrchr(hostname, ':')) != NULL) 1244 *port++ = '\0'; 1245 } 1246 } 1247 1248 if (hints.ai_family != AF_UNIX) { 1249 if (hints.ai_socktype == 0) 1250 hints.ai_socktype = SOCK_DGRAM; 1251 if (port == NULL) 1252 port = defaultport; 1253 error = getaddrinfo(hostname, port, &hints, &ai0); 1254 if (error) { 1255 if (error != EAI_NODATA || port == defaultport) 1256 errx(1, "%s", gai_strerror(error)); 1257 *--port = ':'; 1258 error = getaddrinfo(hostname, defaultport, &hints, 1259 &ai0); 1260 if (error) 1261 errx(1, "%s", gai_strerror(error)); 1262 } 1263 s = -1; 1264 for (ai = ai0; ai != NULL; ai = ai->ai_next) { 1265 if ((s = socket(ai->ai_family, ai->ai_socktype, 1266 ai->ai_protocol)) != -1 && 1267 connect(s, (struct sockaddr *)ai->ai_addr, 1268 ai->ai_addrlen) != -1) 1269 break; 1270 close(s); 1271 s = -1; 1272 } 1273 } else { 1274 s = socket(AF_UNIX, SOCK_STREAM, 0); 1275 if (connect(s, (struct sockaddr *)ai->ai_addr, 1276 ai->ai_addrlen) == -1) 1277 err(1, "Can't connect to %s", agent); 1278 } 1279 if (s == -1) 1280 err(1, "Can't connect to agent %s", agent); 1281 1282 1283 if (ai0 != NULL) 1284 freeaddrinfo(ai0); 1285 free(agentdup); 1286 return s; 1287 } 1288 1289 char * 1290 snmpc_hex2bin(char *hexstr, size_t *binlen) 1291 { 1292 char *decstr; 1293 1294 if (hexstr[0] == '0' && hexstr[1] == 'x') 1295 hexstr += 2; 1296 while (hexstr[0] == ' ' || hexstr[0] == '\t') 1297 hexstr++; 1298 1299 if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL) 1300 return NULL; 1301 1302 for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) { 1303 hexstr[0] = toupper(hexstr[0]); 1304 hexstr[1] = toupper(hexstr[1]); 1305 if (hexstr[0] >= '0' && hexstr[0] <= '9') 1306 decstr[*binlen] = (hexstr[0] - '0') << 4; 1307 else if (hexstr[0] >= 'A' && hexstr[0] <= 'F') 1308 decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4; 1309 else 1310 goto fail; 1311 if (hexstr[1] >= '0' && hexstr[1] <= '9') 1312 decstr[*binlen] |= (hexstr[1] - '0'); 1313 else if (hexstr[1] >= 'A' && hexstr[1] <= 'F') 1314 decstr[*binlen] |= (hexstr[1] - 'A') + 10; 1315 else 1316 goto fail; 1317 1318 hexstr += 2; 1319 while (hexstr[0] == ' ' || hexstr[0] == '\t') 1320 hexstr++; 1321 } 1322 1323 return decstr; 1324 fail: 1325 errno = EINVAL; 1326 free(decstr); 1327 return NULL; 1328 } 1329 1330 struct ber_element * 1331 snmpc_varbindparse(int argc, char *argv[]) 1332 { 1333 struct ber_oid oid, oidval; 1334 struct in_addr addr4; 1335 char *addr = (char *)&addr4; 1336 char *str = NULL, *tmpstr, *endstr; 1337 const char *errstr = NULL; 1338 struct ber_element *varbind = NULL, *vblist = NULL; 1339 int i, ret; 1340 size_t strl, byte; 1341 long long lval; 1342 1343 if (argc % 3 != 0) 1344 usage(); 1345 for (i = 0; i < argc; i += 3) { 1346 if (smi_string2oid(argv[i], &oid) == -1) 1347 errx(1, "Invalid oid: %s\n", argv[i]); 1348 switch (argv[i + 1][0]) { 1349 case 'a': 1350 ret = inet_pton(AF_INET, argv[i + 2], &addr4); 1351 if (ret == -1) 1352 err(1, "inet_pton"); 1353 if (ret == 0) 1354 errx(1, "%s: Bad value notation (%s)", argv[i], 1355 argv[i + 2]); 1356 if ((varbind = ober_printf_elements(varbind, "{Oxt}", 1357 &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION, 1358 SNMP_T_IPADDR)) == NULL) 1359 err(1, "ober_printf_elements"); 1360 break; 1361 case 'b': 1362 tmpstr = argv[i + 2]; 1363 strl = 0; 1364 do { 1365 lval = strtoll(tmpstr, &endstr, 10); 1366 if (endstr[0] != ' ' && endstr[0] != '\t' && 1367 endstr[0] != ',' && endstr[0] != '\0') 1368 errx(1, "%s: Bad value notation (%s)", 1369 argv[i], argv[i + 2]); 1370 if (tmpstr == endstr) { 1371 tmpstr++; 1372 continue; 1373 } 1374 if (lval < 0) 1375 errx(1, "%s: Bad value notation (%s)", 1376 argv[i], argv[i + 2]); 1377 byte = lval / 8; 1378 if (byte >= strl) { 1379 if ((str = recallocarray(str, strl, 1380 byte + 1, 1)) == NULL) 1381 err(1, "malloc"); 1382 strl = byte + 1; 1383 } 1384 str[byte] |= 0x80 >> (lval % 8); 1385 tmpstr = endstr + 1; 1386 } while (endstr[0] != '\0'); 1387 /* 1388 * RFC3416 Section 2.5 1389 * A BITS value is encoded as an OCTET STRING 1390 */ 1391 goto pastestring; 1392 case 'c': 1393 lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX, 1394 &errstr); 1395 if (errstr != NULL) 1396 errx(1, "%s: Bad value notation (%s)", argv[i], 1397 argv[i + 2]); 1398 if ((varbind = ober_printf_elements(varbind, "{Oit}", 1399 &oid, lval, BER_CLASS_APPLICATION, 1400 SNMP_T_COUNTER32)) == NULL) 1401 err(1, "ober_printf_elements"); 1402 break; 1403 case 'd': 1404 /* String always shrinks */ 1405 if ((str = malloc(strlen(argv[i + 2]))) == NULL) 1406 err(1, "malloc"); 1407 tmpstr = argv[i + 2]; 1408 strl = 0; 1409 do { 1410 lval = strtoll(tmpstr, &endstr, 10); 1411 if (endstr[0] != ' ' && endstr[0] != '\t' && 1412 endstr[0] != '\0') 1413 errx(1, "%s: Bad value notation (%s)", 1414 argv[i], argv[i + 2]); 1415 if (tmpstr == endstr) { 1416 tmpstr++; 1417 continue; 1418 } 1419 if (lval < 0 || lval > 0xff) 1420 errx(1, "%s: Bad value notation (%s)", 1421 argv[i], argv[i + 2]); 1422 str[strl++] = (unsigned char) lval; 1423 tmpstr = endstr + 1; 1424 } while (endstr[0] != '\0'); 1425 goto pastestring; 1426 case 'u': 1427 case 'i': 1428 lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, 1429 &errstr); 1430 if (errstr != NULL) 1431 errx(1, "%s: Bad value notation (%s)", argv[i], 1432 argv[i + 2]); 1433 if ((varbind = ober_printf_elements(varbind, "{Oi}", 1434 &oid, lval)) == NULL) 1435 err(1, "ober_printf_elements"); 1436 break; 1437 case 'n': 1438 if ((varbind = ober_printf_elements(varbind, "{O0}", 1439 &oid)) == NULL) 1440 err(1, "ober_printf_elements"); 1441 break; 1442 case 'o': 1443 if (smi_string2oid(argv[i + 2], &oidval) == -1) 1444 errx(1, "%s: Unknown Object Identifier (Sub-id " 1445 "not found: (top) -> %s)", argv[i], 1446 argv[i + 2]); 1447 if ((varbind = ober_printf_elements(varbind, "{OO}", 1448 &oid, &oidval)) == NULL) 1449 err(1, "ober_printf_elements"); 1450 break; 1451 case 's': 1452 if ((str = strdup(argv[i + 2])) == NULL) 1453 err(1, NULL); 1454 strl = strlen(argv[i + 2]); 1455 pastestring: 1456 if ((varbind = ober_printf_elements(varbind, "{Ox}", 1457 &oid, str, strl)) == NULL) 1458 err(1, "ober_printf_elements"); 1459 free(str); 1460 break; 1461 case 't': 1462 lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, 1463 &errstr); 1464 if (errstr != NULL) 1465 errx(1, "%s: Bad value notation (%s)", argv[i], 1466 argv[i + 2]); 1467 if ((varbind = ober_printf_elements(varbind, "{Oit}", 1468 &oid, lval, BER_CLASS_APPLICATION, 1469 SNMP_T_TIMETICKS)) == NULL) 1470 err(1, "ober_printf_elements"); 1471 break; 1472 case 'x': 1473 /* String always shrinks */ 1474 if ((str = malloc(strlen(argv[i + 2]))) == NULL) 1475 err(1, "malloc"); 1476 tmpstr = argv[i + 2]; 1477 strl = 0; 1478 do { 1479 lval = strtoll(tmpstr, &endstr, 16); 1480 if (endstr[0] != ' ' && endstr[0] != '\t' && 1481 endstr[0] != '\0') 1482 errx(1, "%s: Bad value notation (%s)", 1483 argv[i], argv[i + 2]); 1484 if (tmpstr == endstr) { 1485 tmpstr++; 1486 continue; 1487 } 1488 if (lval < 0 || lval > 0xff) 1489 errx(1, "%s: Bad value notation (%s)", 1490 argv[i], argv[i + 2]); 1491 str[strl++] = (unsigned char) lval; 1492 tmpstr = endstr + 1; 1493 } while (endstr[0] != '\0'); 1494 goto pastestring; 1495 default: 1496 usage(); 1497 } 1498 if (vblist == NULL) 1499 vblist = varbind; 1500 } 1501 1502 return vblist; 1503 } 1504 1505 __dead void 1506 usage(void) 1507 { 1508 size_t i; 1509 1510 if (snmp_app != NULL) { 1511 fprintf(stderr, "usage: snmp %s%s%s\n", 1512 snmp_app->name, 1513 snmp_app->usecommonopt ? 1514 " [-A authpass] [-a digest] [-c community] [-e secengineid]\n" 1515 " [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n" 1516 " [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n" 1517 " [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n" 1518 " " : "", 1519 snmp_app->usage == NULL ? "" : snmp_app->usage); 1520 exit(1); 1521 } 1522 for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) { 1523 if (i == 0) 1524 fprintf(stderr, "usage: "); 1525 else 1526 fprintf(stderr, " "); 1527 fprintf(stderr, "snmp %s%s %s\n", 1528 snmp_apps[i].name, 1529 snmp_apps[i].usecommonopt ? 1530 " [options]" : "", 1531 snmp_apps[i].usage ? snmp_apps[i].usage : ""); 1532 } 1533 exit(1); 1534 } 1535