1 /* $OpenBSD: snmpc.c,v 1.17 2019/10/26 19:34:15 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 <netdb.h> 33 #include <poll.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <stdint.h> 37 #include <string.h> 38 #include <time.h> 39 #include <unistd.h> 40 41 #include "smi.h" 42 #include "snmp.h" 43 #include "usm.h" 44 45 #define GETOPT_COMMON "A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:" 46 47 int snmpc_get(int, char *[]); 48 int snmpc_walk(int, char *[]); 49 int snmpc_set(int, char *[]); 50 int snmpc_trap(int, char *[]); 51 int snmpc_mibtree(int, char *[]); 52 struct snmp_agent *snmpc_connect(char *, char *); 53 int snmpc_parseagent(char *, char *); 54 int snmpc_print(struct ber_element *); 55 __dead void snmpc_printerror(enum snmp_error, struct ber_element *, int, 56 const char *); 57 char *snmpc_hex2bin(char *, size_t *); 58 struct ber_element *snmpc_varbindparse(int, char *[]); 59 void usage(void); 60 61 struct snmp_app { 62 const char *name; 63 const int usecommonopt; 64 const char *optstring; 65 const char *usage; 66 int (*exec)(int, char *[]); 67 }; 68 69 struct snmp_app snmp_apps[] = { 70 { "get", 1, NULL, "agent oid ...", snmpc_get }, 71 { "getnext", 1, NULL, "agent oid ...", snmpc_get }, 72 { "walk", 1, "C:", "[-C cIipt] [-C E endoid] agent [oid]", snmpc_walk }, 73 { "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", snmpc_get }, 74 { "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] agent [oid]", snmpc_walk }, 75 { "set", 1, NULL, "agent oid type value [oid type value] ...", snmpc_set }, 76 { "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap }, 77 { "mibtree", 0, "O:", "[-O fnS]", snmpc_mibtree } 78 }; 79 struct snmp_app *snmp_app = NULL; 80 81 char *community = "public"; 82 struct snmp_v3 *v3; 83 char *mib = "mib_2"; 84 int retries = 5; 85 int timeout = 1; 86 enum snmp_version version = SNMP_V2C; 87 int print_equals = 1; 88 int print_varbind_only = 0; 89 int print_summary = 0; 90 int print_time = 0; 91 int walk_check_increase = 1; 92 int walk_fallback_oid = 1; 93 int walk_include_oid = 0; 94 int smi_print_hint = 1; 95 int non_repeaters = 0; 96 int max_repetitions = 10; 97 struct ber_oid walk_end = {{0}, 0}; 98 enum smi_oid_lookup oid_lookup = smi_oidl_short; 99 enum smi_output_string output_string = smi_os_default; 100 101 int 102 main(int argc, char *argv[]) 103 { 104 const EVP_MD *md = NULL; 105 const EVP_CIPHER *cipher = NULL; 106 struct snmp_sec *sec; 107 char *user = NULL; 108 enum usm_key_level authkeylevel; 109 char *authkey = NULL; 110 size_t authkeylen = 0; 111 enum usm_key_level privkeylevel; 112 char *privkey = NULL; 113 size_t privkeylen = 0; 114 int seclevel = SNMP_MSGFLAG_REPORT; 115 char *ctxname = NULL; 116 char *ctxengineid = NULL, *secengineid = NULL; 117 size_t ctxengineidlen, secengineidlen; 118 int zflag = 0; 119 long long boots, time; 120 char optstr[BUFSIZ]; 121 const char *errstr; 122 char *strtolp; 123 int ch; 124 size_t i; 125 126 if (pledge("stdio inet dns", NULL) == -1) 127 err(1, "pledge"); 128 129 if (argc <= 1) 130 usage(); 131 132 optstr[0] = '\0'; 133 for (i = 0; i < sizeof(snmp_apps)/sizeof(*snmp_apps); i++) { 134 if (strcmp(snmp_apps[i].name, argv[1]) == 0) { 135 snmp_app = &snmp_apps[i]; 136 if (snmp_app->optstring != NULL) { 137 if (strlcpy(optstr, snmp_app->optstring, 138 sizeof(optstr)) > sizeof(optstr)) 139 errx(1, "strlcat"); 140 } 141 break; 142 } 143 } 144 if (snmp_app == NULL) 145 usage(); 146 147 if (snmp_app->usecommonopt) { 148 if (strlcat(optstr, GETOPT_COMMON, sizeof(optstr)) > 149 sizeof(optstr)) 150 errx(1, "strlcpy"); 151 } 152 153 argc--; 154 argv++; 155 156 smi_init(); 157 158 while ((ch = getopt(argc, argv, optstr)) != -1) { 159 switch (ch) { 160 case 'A': 161 authkey = optarg; 162 authkeylen = strlen(authkey); 163 authkeylevel = USM_KEY_PASSWORD; 164 break; 165 case 'a': 166 if (strcasecmp(optarg, "MD5") == 0) 167 md = EVP_md5(); 168 else if (strcasecmp(optarg, "SHA") == 0) 169 md = EVP_sha1(); 170 else if (strcasecmp(optarg, "SHA-224") == 0) 171 md = EVP_sha224(); 172 else if (strcasecmp(optarg, "SHA-256") == 0) 173 md = EVP_sha256(); 174 else if (strcasecmp(optarg, "SHA-384") == 0) 175 md = EVP_sha384(); 176 else if (strcasecmp(optarg, "SHA-512") == 0) 177 md = EVP_sha512(); 178 else 179 errx(1, "Invalid authentication protocol " 180 "specified after -a flag: %s", optarg); 181 break; 182 case 'c': 183 community = optarg; 184 break; 185 case 'E': 186 ctxengineid = snmpc_hex2bin(optarg, 187 &ctxengineidlen); 188 if (ctxengineid == NULL) { 189 if (errno == EINVAL) 190 errx(1, "Bad engine ID value " 191 "after -3E flag."); 192 err(1, "-3E"); 193 } 194 break; 195 case 'e': 196 secengineid = snmpc_hex2bin(optarg, 197 &secengineidlen); 198 if (secengineid == NULL) { 199 if (errno == EINVAL) 200 errx(1, "Bad engine ID value " 201 "after -3e flag."); 202 err(1, "-3e"); 203 } 204 break; 205 case 'K': 206 privkey = snmpc_hex2bin(optarg, &privkeylen); 207 if (privkey == NULL) { 208 if (errno == EINVAL) 209 errx(1, "Bad key value after " 210 "-3K flag."); 211 errx(1, "-3K"); 212 } 213 privkeylevel = USM_KEY_LOCALIZED; 214 break; 215 case 'k': 216 authkey = snmpc_hex2bin(optarg, &authkeylen); 217 if (authkey == NULL) { 218 if (errno == EINVAL) 219 errx(1, "Bad key value after -k flag."); 220 err(1, "-k"); 221 } 222 authkeylevel = USM_KEY_LOCALIZED; 223 break; 224 case 'l': 225 if (strcasecmp(optarg, "noAuthNoPriv") == 0) 226 seclevel = SNMP_MSGFLAG_REPORT; 227 else if (strcasecmp(optarg, "authNoPriv") == 0) 228 seclevel = SNMP_MSGFLAG_AUTH | 229 SNMP_MSGFLAG_REPORT; 230 else if (strcasecmp(optarg, "authPriv") == 0) 231 seclevel = SNMP_MSGFLAG_AUTH | 232 SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT; 233 else 234 errx(1, "Invalid security level specified " 235 "after -l flag: %s", optarg); 236 break; 237 case 'n': 238 ctxname = optarg; 239 break; 240 case 'r': 241 if ((retries = strtonum(optarg, 0, INT_MAX, 242 &errstr)) == 0) { 243 if (errstr != NULL) 244 errx(1, "-r: %s argument", errstr); 245 } 246 break; 247 case 't': 248 if ((timeout = strtonum(optarg, 1, INT_MAX, 249 &errstr)) == 0) { 250 if (errstr != NULL) 251 errx(1, "-t: %s argument", errstr); 252 } 253 break; 254 case 'u': 255 user = optarg; 256 break; 257 case 'v': 258 if (strcmp(optarg, "1") == 0) 259 version = SNMP_V1; 260 else if (strcmp(optarg, "2c") == 0) 261 version = SNMP_V2C; 262 else if (strcmp(optarg, "3") == 0) 263 version = SNMP_V3; 264 else 265 errc(1, EINVAL, "-v"); 266 break; 267 case 'C': 268 for (i = 0; i < strlen(optarg); i++) { 269 switch (optarg[i]) { 270 case 'c': 271 if (strcmp(snmp_app->name, "walk") && 272 strcmp(snmp_app->name, "bulkwalk")) 273 usage(); 274 walk_check_increase = 0; 275 break; 276 case 'i': 277 if (strcmp(snmp_app->name, "walk") && 278 strcmp(snmp_app->name, "bulkwalk")) 279 usage(); 280 walk_include_oid = 1; 281 break; 282 case 'n': 283 if (strcmp(snmp_app->name, "bulkget") && 284 strcmp(snmp_app->name, "bulkwalk")) 285 usage(); 286 errno = 0; 287 non_repeaters = strtol(&optarg[i + 1], 288 &strtolp, 10); 289 if (non_repeaters < 0 || 290 errno == ERANGE) { 291 if (non_repeaters < 0) 292 errx(1, "%s%s", 293 "-Cn: too small ", 294 "argument"); 295 else 296 errx(1, "%s%s", 297 "-Cn: too large", 298 "argument"); 299 } else if (&optarg[i + 1] == strtolp) 300 errx(1, "-Cn invalid argument"); 301 i = strtolp - optarg - 1; 302 break; 303 case 'p': 304 if (strcmp(snmp_app->name, "walk") && 305 strcmp(snmp_app->name, "bulkwalk")) 306 usage(); 307 print_summary = 1; 308 break; 309 case 'r': 310 if (strcmp(snmp_app->name, "bulkget") && 311 strcmp(snmp_app->name, "bulkwalk")) 312 usage(); 313 errno = 0; 314 max_repetitions = strtol(&optarg[i + 1], 315 &strtolp, 10); 316 if (max_repetitions < 0 || 317 errno == ERANGE) { 318 if (max_repetitions < 0) 319 errx(1, "%s%s", 320 "-Cr: too small ", 321 "argument"); 322 else 323 errx(1, "%s%s", 324 "-Cr: too large", 325 "argument"); 326 } else if (&optarg[i + 1] == strtolp) 327 errx(1, "-Cr invalid argument"); 328 i = strtolp - optarg - 1; 329 break; 330 case 't': 331 if (strcmp(snmp_app->name, "walk")) 332 usage(); 333 print_time = 1; 334 break; 335 case 'E': 336 if (strcmp(snmp_app->name, "walk")) 337 usage(); 338 if (smi_string2oid(argv[optind], 339 &walk_end) != 0) 340 errx(1, "%s: %s", 341 "Unknown Object Identifier", 342 argv[optind]); 343 optind++; 344 continue; 345 case 'I': 346 if (strcmp(snmp_app->name, "walk")) 347 usage(); 348 walk_fallback_oid = 0; 349 break; 350 default: 351 usage(); 352 } 353 if (optarg[i] == 'E') 354 break; 355 } 356 break; 357 case 'O': 358 for (i = 0; i < strlen(optarg); i++) { 359 if (strcmp(snmp_app->name, "mibtree") == 0 && 360 optarg[i] != 'f' && optarg[i] != 'n' && 361 optarg[i] != 'S') 362 usage(); 363 switch (optarg[i]) { 364 case 'a': 365 output_string = smi_os_ascii; 366 break; 367 case 'f': 368 oid_lookup = smi_oidl_full; 369 break; 370 case 'n': 371 oid_lookup = smi_oidl_numeric; 372 break; 373 case 'q': 374 print_equals = 0; 375 smi_print_hint = 0; 376 break; 377 case 'v': 378 print_varbind_only = 1; 379 break; 380 case 'x': 381 output_string = smi_os_hex; 382 break; 383 case 'S': 384 oid_lookup = smi_oidl_short; 385 break; 386 case 'Q': 387 smi_print_hint = 0; 388 break; 389 default: 390 usage(); 391 } 392 } 393 break; 394 case 'X': 395 privkey = optarg; 396 privkeylen = strlen(privkey); 397 privkeylevel = USM_KEY_PASSWORD; 398 break; 399 case 'x': 400 if (strcasecmp(optarg, "DES") == 0) 401 cipher = EVP_des_cbc(); 402 else if (strcasecmp(optarg, "AES") == 0) 403 cipher = EVP_aes_128_cfb128(); 404 else 405 errx(1, "Invalid privacy protocol " 406 "specified after -3x flag: %s", 407 optarg); 408 break; 409 case 'Z': 410 boots = strtoll(optarg, &strtolp, 10); 411 if (boots < 0 || strtolp == optarg || strtolp[0] != ',') 412 usage(); 413 strtolp++; 414 while (strtolp[0] == ' ' && strtolp[0] == '\t') 415 strtolp++; 416 time = strtoll(strtolp, &strtolp, 10); 417 if (boots < 0 || strtolp == optarg) 418 usage(); 419 zflag = 1; 420 break; 421 default: 422 usage(); 423 } 424 } 425 argc -= optind; 426 argv += optind; 427 428 if (version == SNMP_V3) { 429 /* Setup USM */ 430 if (user == NULL || user[0] == '\0') 431 errx(1, "No securityName specified"); 432 if ((sec = usm_init(user, strlen(user))) == NULL) 433 err(1, "usm_init"); 434 if (seclevel & SNMP_MSGFLAG_AUTH) { 435 if (md == NULL) 436 md = EVP_md5(); 437 if (authkey == NULL) 438 errx(1, "No authKey or authPassword specified"); 439 if (usm_setauth(sec, md, authkey, authkeylen, 440 authkeylevel) == -1) 441 err(1, "Can't set authkey"); 442 } 443 if (seclevel & SNMP_MSGFLAG_PRIV) { 444 if (cipher == NULL) 445 cipher = EVP_des_cbc(); 446 if (privkey == NULL) 447 errx(1, "No privKey or privPassword specified"); 448 if (usm_setpriv(sec, cipher, privkey, privkeylen, 449 privkeylevel) == -1) 450 err(1, "Can't set authkey"); 451 } 452 if (secengineid != NULL) { 453 if (usm_setengineid(sec, secengineid, 454 secengineidlen) == -1) 455 err(1, "Can't set secengineid"); 456 } 457 if (zflag) 458 if (usm_setbootstime(sec, boots, time) == -1) 459 err(1, "Can't set boots/time"); 460 v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 : 461 strlen(ctxname), sec); 462 if (v3 == NULL) 463 err(1, "snmp_v3_init"); 464 if (ctxengineid != NULL) { 465 if (snmp_v3_setengineid(v3, ctxengineid, 466 ctxengineidlen) == -1) 467 err(1, "Can't set ctxengineid"); 468 } 469 } 470 471 472 return snmp_app->exec(argc, argv); 473 } 474 475 int 476 snmpc_get(int argc, char *argv[]) 477 { 478 struct ber_oid *oid; 479 struct ber_element *pdu, *varbind; 480 struct snmp_agent *agent; 481 int errorstatus, errorindex; 482 int i; 483 int class; 484 unsigned type; 485 char *hint = NULL; 486 487 if (argc < 2) 488 usage(); 489 490 if ((agent = snmpc_connect(argv[0], "161")) == NULL) 491 err(1, "%s", snmp_app->name); 492 agent->timeout = timeout; 493 agent->retries = retries; 494 495 if (pledge("stdio", NULL) == -1) 496 err(1, "pledge"); 497 argc--; 498 argv++; 499 500 oid = reallocarray(NULL, argc, sizeof(*oid)); 501 if (oid == NULL) 502 err(1, "malloc"); 503 for (i = 0; i < argc; i++) { 504 if (smi_string2oid(argv[i], &oid[i]) == -1) 505 errx(1, "%s: Unknown object identifier", argv[i]); 506 } 507 if (strcmp(snmp_app->name, "getnext") == 0) { 508 if ((pdu = snmp_getnext(agent, oid, argc)) == NULL) 509 err(1, "getnext"); 510 } else if (strcmp(snmp_app->name, "bulkget") == 0) { 511 if (version < SNMP_V2C) 512 errx(1, "Cannot send V2 PDU on V1 session"); 513 if (non_repeaters > argc) 514 errx(1, "need more objects than -Cn<num>"); 515 if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters, 516 max_repetitions)) == NULL) 517 err(1, "bulkget"); 518 } else { 519 if ((pdu = snmp_get(agent, oid, argc)) == NULL) 520 err(1, "get"); 521 } 522 523 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus, 524 &errorindex, &varbind); 525 if (errorstatus != 0) { 526 if (errorindex >= 1 && errorindex <= argc) 527 hint = argv[errorindex - 1]; 528 snmpc_printerror((enum snmp_error) errorstatus, varbind, 529 errorindex, hint); 530 } 531 532 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 533 printf("Received report:\n"); 534 for (; varbind != NULL; varbind = varbind->be_next) { 535 if (!snmpc_print(varbind)) 536 err(1, "Can't print response"); 537 } 538 ober_free_elements(pdu); 539 snmp_free_agent(agent); 540 return 0; 541 } 542 543 int 544 snmpc_walk(int argc, char *argv[]) 545 { 546 struct ber_oid oid, loid, noid; 547 struct ber_element *pdu, *varbind, *value; 548 struct timespec start, finish; 549 struct snmp_agent *agent; 550 const char *oids; 551 int n = 0, prev_cmp; 552 int errorstatus, errorindex; 553 int class; 554 unsigned type; 555 556 if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C) 557 errx(1, "Cannot send V2 PDU on V1 session"); 558 if (argc < 1 || argc > 2) 559 usage(); 560 oids = argc == 1 ? mib : argv[1]; 561 562 if ((agent = snmpc_connect(argv[0], "161"))== NULL) 563 err(1, "%s", snmp_app->name); 564 agent->timeout = timeout; 565 agent->retries = retries; 566 if (pledge("stdio", NULL) == -1) 567 err(1, "pledge"); 568 569 if (smi_string2oid(oids, &oid) == -1) 570 errx(1, "%s: Unknown object identifier", oids); 571 bcopy(&oid, &noid, sizeof(noid)); 572 if (print_time) 573 clock_gettime(CLOCK_MONOTONIC, &start); 574 575 if (walk_include_oid) { 576 if ((pdu = snmp_get(agent, &oid, 1)) == NULL) 577 err(1, "%s", snmp_app->name); 578 579 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 580 &errorstatus, &errorindex, &varbind); 581 if (errorstatus != 0) 582 snmpc_printerror((enum snmp_error) errorstatus, varbind, 583 errorindex, oids); 584 585 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 586 printf("Received report:\n"); 587 if (!snmpc_print(varbind)) 588 err(1, "Can't print response"); 589 ober_free_element(pdu); 590 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 591 return 1; 592 n++; 593 } 594 while (1) { 595 bcopy(&noid, &loid, sizeof(loid)); 596 if (strcmp(snmp_app->name, "bulkwalk") == 0) { 597 if ((pdu = snmp_getbulk(agent, &noid, 1, 598 non_repeaters, max_repetitions)) == NULL) 599 err(1, "bulkwalk"); 600 } else { 601 if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL) 602 err(1, "walk"); 603 } 604 605 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 606 &errorstatus, &errorindex, &varbind); 607 if (errorstatus != 0) { 608 snmpc_printerror((enum snmp_error) errorstatus, varbind, 609 errorindex, NULL); 610 } 611 612 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 613 printf("Received report:\n"); 614 for (; varbind != NULL; varbind = varbind->be_next) { 615 (void) ober_scanf_elements(varbind, "{oe}", &noid, 616 &value); 617 if (value->be_class == BER_CLASS_CONTEXT && 618 value->be_type == BER_TYPE_EOC) 619 break; 620 prev_cmp = ober_oid_cmp(&loid, &noid); 621 if (walk_check_increase && prev_cmp == -1) 622 errx(1, "OID not increasing"); 623 if (prev_cmp == 0 || ober_oid_cmp(&oid, &noid) != 2) 624 break; 625 if (walk_end.bo_n != 0 && 626 ober_oid_cmp(&walk_end, &noid) != -1) 627 break; 628 629 if (!snmpc_print(varbind)) 630 err(1, "Can't print response"); 631 n++; 632 } 633 ober_free_elements(pdu); 634 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 635 return 1; 636 if (varbind != NULL) 637 break; 638 } 639 if (walk_fallback_oid && n == 0) { 640 if ((pdu = snmp_get(agent, &oid, 1)) == NULL) 641 err(1, "%s", snmp_app->name); 642 643 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, 644 &errorstatus, &errorindex, &varbind); 645 if (errorstatus != 0) 646 snmpc_printerror((enum snmp_error) errorstatus, varbind, 647 errorindex, oids); 648 649 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 650 printf("Received report:\n"); 651 if (!snmpc_print(varbind)) 652 err(1, "Can't print response"); 653 ober_free_element(pdu); 654 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 655 return 1; 656 n++; 657 } 658 if (print_time) 659 clock_gettime(CLOCK_MONOTONIC, &finish); 660 if (print_summary) 661 printf("Variables found: %d\n", n); 662 if (print_time) { 663 if ((finish.tv_nsec -= start.tv_nsec) < 0) { 664 finish.tv_sec -= 1; 665 finish.tv_nsec += 1000000000; 666 } 667 finish.tv_sec -= start.tv_sec; 668 fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n", 669 finish.tv_sec, finish.tv_nsec); 670 } 671 snmp_free_agent(agent); 672 return 0; 673 } 674 675 int 676 snmpc_set(int argc, char *argv[]) 677 { 678 struct snmp_agent *agent; 679 struct ber_element *pdu, *varbind; 680 int errorstatus, errorindex; 681 int class; 682 unsigned type; 683 char *hint = NULL; 684 685 if (argc < 4) 686 usage(); 687 if ((agent = snmpc_connect(argv[0], "161")) == NULL) 688 err(1, "%s", snmp_app->name); 689 argc--; 690 argv++; 691 692 if (pledge("stdio", NULL) == -1) 693 err(1, "pledge"); 694 695 if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL) 696 err(1, "set"); 697 698 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus, 699 &errorindex, &varbind); 700 if (errorstatus != 0) { 701 if (errorindex >= 1 && errorindex <= argc / 3) 702 hint = argv[(errorindex - 1) * 3]; 703 snmpc_printerror((enum snmp_error) errorstatus, varbind, 704 errorindex, hint); 705 } 706 707 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) 708 printf("Received report:\n"); 709 for (; varbind != NULL; varbind = varbind->be_next) { 710 if (!snmpc_print(varbind)) 711 err(1, "Can't print response"); 712 } 713 ober_free_elements(pdu); 714 snmp_free_agent(agent); 715 return 0; 716 } 717 718 int 719 snmpc_trap(int argc, char *argv[]) 720 { 721 struct snmp_agent *agent; 722 struct timespec ts; 723 struct ber_oid trapoid; 724 const char *errstr = NULL; 725 long long lval; 726 727 if (version == SNMP_V1) 728 errx(1, "trap is not supported for snmp v1"); 729 730 if ((agent = snmpc_connect(argv[0], "162")) == NULL) 731 err(1, "%s", snmp_app->name); 732 733 if (pledge("stdio", NULL) == -1) 734 err(1, "pledge"); 735 736 if (argv[1][0] == '\0') { 737 if (clock_gettime(CLOCK_UPTIME, &ts) == -1) 738 err(1, "clock_gettime"); 739 } else { 740 lval = strtonum(argv[1], 0, LLONG_MAX, &errstr); 741 if (errstr != NULL) 742 errx(1, "Bad value notation (%s)", argv[1]); 743 ts.tv_sec = lval / 100; 744 ts.tv_nsec = (lval % 100) * 10000000; 745 } 746 if (smi_string2oid(argv[2], &trapoid) == -1) 747 errx(1, "Invalid oid: %s\n", argv[2]); 748 749 argc -= 3; 750 argv += 3; 751 752 snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv)); 753 754 return 0; 755 } 756 757 int 758 snmpc_mibtree(int argc, char *argv[]) 759 { 760 struct oid *oid; 761 char buf[BUFSIZ]; 762 763 for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) { 764 smi_oid2string(&oid->o_id, buf, sizeof(buf), oid_lookup); 765 printf("%s\n", buf); 766 } 767 return 0; 768 } 769 770 struct snmp_agent * 771 snmpc_connect(char *host, char *port) 772 { 773 switch (version) { 774 case SNMP_V1: 775 case SNMP_V2C: 776 return snmp_connect_v12(snmpc_parseagent(host, port), version, 777 community); 778 case SNMP_V3: 779 return snmp_connect_v3(snmpc_parseagent(host, port), v3); 780 } 781 return NULL; 782 } 783 784 int 785 snmpc_print(struct ber_element *elm) 786 { 787 struct ber_oid oid; 788 char oids[SNMP_MAX_OID_STRLEN]; 789 char *value; 790 791 elm = elm->be_sub; 792 if (ober_get_oid(elm, &oid) != 0) { 793 errno = EINVAL; 794 return 0; 795 } 796 797 elm = elm->be_next; 798 value = smi_print_element(elm, smi_print_hint, output_string, oid_lookup); 799 if (value == NULL) 800 return 0; 801 802 if (print_varbind_only) 803 printf("%s\n", value); 804 else if (print_equals) { 805 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup); 806 printf("%s = %s\n", oids, value); 807 } else { 808 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup); 809 printf("%s %s\n", oids, value); 810 } 811 free(value); 812 813 return 1; 814 } 815 816 __dead void 817 snmpc_printerror(enum snmp_error error, struct ber_element *varbind, 818 int index, const char *hint) 819 { 820 struct ber_oid hoid, vboid; 821 char oids[SNMP_MAX_OID_STRLEN]; 822 const char *oid = NULL; 823 int i; 824 825 if (index >= 1) { 826 /* Only print if the index is in the reply */ 827 for (i = 1; varbind != NULL && i <= index; 828 varbind = varbind->be_next) 829 i++; 830 if (varbind != NULL && 831 ober_get_oid(varbind->be_sub, &vboid) == 0) { 832 /* If user and reply conform print user input */ 833 if (hint != NULL && 834 smi_string2oid(hint, &hoid) == 0 && 835 ober_oid_cmp(&hoid, &vboid) == 0) 836 oid = hint; 837 else 838 oid = smi_oid2string(&vboid, oids, 839 sizeof(oids), oid_lookup); 840 } 841 } 842 if (oid == NULL) 843 oid = "?"; 844 845 switch (error) { 846 case SNMP_ERROR_NONE: 847 errx(1, "No error, how did I get here?"); 848 case SNMP_ERROR_TOOBIG: 849 errx(1, "Can't parse oid %s: Response too big", oid); 850 case SNMP_ERROR_NOSUCHNAME: 851 errx(1, "Can't parse oid %s: No such object", oid); 852 case SNMP_ERROR_BADVALUE: 853 errx(1, "Can't parse oid %s: Bad value", oid); 854 case SNMP_ERROR_READONLY: 855 errx(1, "Can't parse oid %s: Read only", oid); 856 case SNMP_ERROR_GENERR: 857 errx(1, "Can't parse oid %s: Generic error", oid); 858 case SNMP_ERROR_NOACCESS: 859 errx(1, "Can't parse oid %s: Access denied", oid); 860 case SNMP_ERROR_WRONGTYPE: 861 errx(1, "Can't parse oid %s: Wrong type", oid); 862 case SNMP_ERROR_WRONGLENGTH: 863 errx(1, "Can't parse oid %s: Wrong length", oid); 864 case SNMP_ERROR_WRONGENC: 865 errx(1, "Can't parse oid %s: Wrong encoding", oid); 866 case SNMP_ERROR_WRONGVALUE: 867 errx(1, "Can't parse oid %s: Wrong value", oid); 868 case SNMP_ERROR_NOCREATION: 869 errx(1, "Can't parse oid %s: Can't be created", oid); 870 case SNMP_ERROR_INCONVALUE: 871 errx(1, "Can't parse oid %s: Inconsistent value", oid); 872 case SNMP_ERROR_RESUNAVAIL: 873 errx(1, "Can't parse oid %s: Resource unavailable", oid); 874 case SNMP_ERROR_COMMITFAILED: 875 errx(1, "Can't parse oid %s: Commit failed", oid); 876 case SNMP_ERROR_UNDOFAILED: 877 errx(1, "Can't parse oid %s: Undo faild", oid); 878 case SNMP_ERROR_AUTHERROR: 879 errx(1, "Can't parse oid %s: Authorization error", oid); 880 case SNMP_ERROR_NOTWRITABLE: 881 errx(1, "Can't parse oid %s: Not writable", oid); 882 case SNMP_ERROR_INCONNAME: 883 errx(1, "Can't parse oid %s: Inconsistent name", oid); 884 } 885 errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error); 886 } 887 888 int 889 snmpc_parseagent(char *agent, char *defaultport) 890 { 891 struct addrinfo hints, *ai, *ai0 = NULL; 892 struct sockaddr_un saddr; 893 char *agentdup, *specifier, *hostname, *port = NULL; 894 int error; 895 int s; 896 897 if ((agentdup = specifier = strdup(agent)) == NULL) 898 err(1, NULL); 899 900 bzero(&hints, sizeof(hints)); 901 if ((hostname = strchr(specifier, ':')) != NULL) { 902 *hostname++ = '\0'; 903 if (strcasecmp(specifier, "udp") == 0) { 904 hints.ai_family = AF_INET; 905 hints.ai_socktype = SOCK_DGRAM; 906 } else if (strcasecmp(specifier, "tcp") == 0) { 907 hints.ai_family = AF_INET; 908 hints.ai_socktype = SOCK_STREAM; 909 } else if (strcasecmp(specifier, "udp6") == 0 || 910 strcasecmp(specifier, "udpv6") == 0 || 911 strcasecmp(specifier, "udpipv6") == 0) { 912 hints.ai_family = AF_INET6; 913 hints.ai_socktype = SOCK_DGRAM; 914 } else if (strcasecmp(specifier, "tcp6") == 0 || 915 strcasecmp(specifier, "tcpv6") == 0 || 916 strcasecmp(specifier, "tcpipv6") == 0) { 917 hints.ai_family = AF_INET6; 918 hints.ai_socktype = SOCK_STREAM; 919 } else if (strcasecmp(specifier, "unix") == 0) { 920 hints.ai_family = AF_UNIX; 921 hints.ai_socktype = SOCK_STREAM; 922 hints.ai_addr = (struct sockaddr *)&saddr; 923 hints.ai_addrlen = sizeof(saddr); 924 saddr.sun_len = sizeof(saddr); 925 saddr.sun_family = AF_UNIX; 926 if (strlcpy(saddr.sun_path, hostname, 927 sizeof(saddr.sun_path)) > sizeof(saddr.sun_path)) 928 errx(1, "Hostname path too long"); 929 ai = &hints; 930 } else { 931 port = hostname; 932 hostname = specifier; 933 specifier = NULL; 934 hints.ai_family = AF_INET; 935 hints.ai_socktype = SOCK_DGRAM; 936 } 937 if (port == NULL) { 938 if (hints.ai_family == AF_INET) { 939 if ((port = strchr(hostname, ':')) != NULL) 940 *port++ = '\0'; 941 } else if (hints.ai_family == AF_INET6) { 942 if (hostname[0] == '[') { 943 hostname++; 944 if ((port = strchr(hostname, ']')) == NULL) 945 errx(1, "invalid agent"); 946 *port++ = '\0'; 947 if (port[0] == ':') 948 *port++ = '\0'; 949 else 950 port = NULL; 951 } else { 952 if ((port = strrchr(hostname, ':')) == NULL) 953 errx(1, "invalid agent"); 954 *port++ = '\0'; 955 } 956 } 957 } 958 } else { 959 hostname = specifier; 960 hints.ai_family = AF_INET; 961 hints.ai_socktype = SOCK_DGRAM; 962 } 963 964 if (hints.ai_family != AF_UNIX) { 965 if (port == NULL) 966 port = defaultport; 967 error = getaddrinfo(hostname, port, &hints, &ai0); 968 if (error) 969 errx(1, "%s", gai_strerror(error)); 970 s = -1; 971 for (ai = ai0; ai != NULL; ai = ai->ai_next) { 972 if ((s = socket(ai->ai_family, ai->ai_socktype, 973 ai->ai_protocol)) == -1) 974 continue; 975 break; 976 } 977 } else 978 s = socket(hints.ai_family, hints.ai_socktype, 979 hints.ai_protocol); 980 if (s == -1) 981 err(1, "socket"); 982 983 if (connect(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) == -1) 984 err(1, "Can't connect to %s", agent); 985 986 if (ai0 != NULL) 987 freeaddrinfo(ai0); 988 free(agentdup); 989 return s; 990 } 991 992 char * 993 snmpc_hex2bin(char *hexstr, size_t *binlen) 994 { 995 char *decstr; 996 997 if (hexstr[0] == '0' && hexstr[1] == 'x') 998 hexstr += 2; 999 while (hexstr[0] == ' ' || hexstr[0] == '\t') 1000 hexstr++; 1001 1002 if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL) 1003 return NULL; 1004 1005 for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) { 1006 hexstr[0] = toupper(hexstr[0]); 1007 hexstr[1] = toupper(hexstr[1]); 1008 if (hexstr[0] >= '0' && hexstr[0] <= '9') 1009 decstr[*binlen] = (hexstr[0] - '0') << 4; 1010 else if (hexstr[0] >= 'A' && hexstr[0] <= 'F') 1011 decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4; 1012 else 1013 goto fail; 1014 if (hexstr[1] >= '0' && hexstr[1] <= '9') 1015 decstr[*binlen] |= (hexstr[1] - '0'); 1016 else if (hexstr[1] >= 'A' && hexstr[1] <= 'F') 1017 decstr[*binlen] |= (hexstr[1] - 'A') + 10; 1018 else 1019 goto fail; 1020 1021 hexstr += 2; 1022 while (hexstr[0] == ' ' || hexstr[0] == '\t') 1023 hexstr++; 1024 } 1025 1026 return decstr; 1027 fail: 1028 errno = EINVAL; 1029 free(decstr); 1030 return NULL; 1031 } 1032 1033 struct ber_element * 1034 snmpc_varbindparse(int argc, char *argv[]) 1035 { 1036 struct ber_oid oid, oidval; 1037 struct in_addr addr4; 1038 char *addr = (char *)&addr4; 1039 char *str = NULL, *tmpstr, *endstr; 1040 const char *errstr = NULL; 1041 struct ber_element *varbind = NULL, *vblist = NULL; 1042 int i, ret; 1043 size_t strl, byte; 1044 long long lval; 1045 1046 if (argc % 3 != 0) 1047 usage(); 1048 for (i = 0; i < argc; i += 3) { 1049 if (smi_string2oid(argv[i], &oid) == -1) 1050 errx(1, "Invalid oid: %s\n", argv[i]); 1051 switch (argv[i + 1][0]) { 1052 case 'a': 1053 ret = inet_pton(AF_INET, argv[i + 2], &addr4); 1054 if (ret == -1) 1055 err(1, "inet_pton"); 1056 if (ret == 0) 1057 errx(1, "%s: Bad value notation (%s)", argv[i], 1058 argv[i + 2]); 1059 if ((varbind = ober_printf_elements(varbind, "{Oxt}", 1060 &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION, 1061 SNMP_T_IPADDR)) == NULL) 1062 err(1, "ober_printf_elements"); 1063 break; 1064 case 'b': 1065 tmpstr = argv[i + 2]; 1066 strl = 0; 1067 do { 1068 lval = strtoll(tmpstr, &endstr, 10); 1069 if (endstr[0] != ' ' && endstr[0] != '\t' && 1070 endstr[0] != ',' && endstr[0] != '\0') 1071 errx(1, "%s: Bad value notation (%s)", 1072 argv[i], argv[i + 2]); 1073 if (tmpstr == endstr) { 1074 tmpstr++; 1075 continue; 1076 } 1077 if (lval < 0) 1078 errx(1, "%s: Bad value notation (%s)", 1079 argv[i], argv[i + 2]); 1080 byte = lval / 8; 1081 if (byte >= strl) { 1082 if ((str = recallocarray(str, strl, 1083 byte + 1, 1)) == NULL) 1084 err(1, "malloc"); 1085 strl = byte + 1; 1086 } 1087 str[byte] |= 0x80 >> (lval % 8); 1088 tmpstr = endstr + 1; 1089 } while (endstr[0] != '\0'); 1090 /* 1091 * RFC3416 Section 2.5 1092 * A BITS value is encoded as an OCTET STRING 1093 */ 1094 goto pastestring; 1095 case 'c': 1096 lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX, 1097 &errstr); 1098 if (errstr != NULL) 1099 errx(1, "%s: Bad value notation (%s)", argv[i], 1100 argv[i + 2]); 1101 if ((varbind = ober_printf_elements(varbind, "{Oit}", 1102 &oid, lval, BER_CLASS_APPLICATION, 1103 SNMP_T_COUNTER32)) == NULL) 1104 err(1, "ober_printf_elements"); 1105 break; 1106 case 'd': 1107 /* String always shrinks */ 1108 if ((str = malloc(strlen(argv[i + 2]))) == NULL) 1109 err(1, "malloc"); 1110 tmpstr = argv[i + 2]; 1111 strl = 0; 1112 do { 1113 lval = strtoll(tmpstr, &endstr, 10); 1114 if (endstr[0] != ' ' && endstr[0] != '\t' && 1115 endstr[0] != '\0') 1116 errx(1, "%s: Bad value notation (%s)", 1117 argv[i], argv[i + 2]); 1118 if (tmpstr == endstr) { 1119 tmpstr++; 1120 continue; 1121 } 1122 if (lval < 0 || lval > 0xff) 1123 errx(1, "%s: Bad value notation (%s)", 1124 argv[i], argv[i + 2]); 1125 str[strl++] = (unsigned char) lval; 1126 tmpstr = endstr + 1; 1127 } while (endstr[0] != '\0'); 1128 goto pastestring; 1129 case 'u': 1130 case 'i': 1131 lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, 1132 &errstr); 1133 if (errstr != NULL) 1134 errx(1, "%s: Bad value notation (%s)", argv[i], 1135 argv[i + 2]); 1136 if ((varbind = ober_printf_elements(varbind, "{Oi}", 1137 &oid, lval)) == NULL) 1138 err(1, "ober_printf_elements"); 1139 break; 1140 case 'n': 1141 if ((varbind = ober_printf_elements(varbind, "{O0}", 1142 &oid)) == NULL) 1143 err(1, "ober_printf_elements"); 1144 break; 1145 case 'o': 1146 if (smi_string2oid(argv[i + 2], &oidval) == -1) 1147 errx(1, "%s: Unknown Object Identifier (Sub-id " 1148 "not found: (top) -> %s)", argv[i], 1149 argv[i + 2]); 1150 if ((varbind = ober_printf_elements(varbind, "{OO}", 1151 &oid, &oidval)) == NULL) 1152 err(1, "ober_printf_elements"); 1153 break; 1154 case 's': 1155 if ((str = strdup(argv[i + 2])) == NULL) 1156 err(1, NULL); 1157 strl = strlen(argv[i + 2]); 1158 pastestring: 1159 if ((varbind = ober_printf_elements(varbind, "{Ox}", 1160 &oid, str, strl)) == NULL) 1161 err(1, "ober_printf_elements"); 1162 free(str); 1163 break; 1164 case 't': 1165 lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, 1166 &errstr); 1167 if (errstr != NULL) 1168 errx(1, "%s: Bad value notation (%s)", argv[i], 1169 argv[i + 2]); 1170 if ((varbind = ober_printf_elements(varbind, "{Oit}", 1171 &oid, lval, BER_CLASS_APPLICATION, 1172 SNMP_T_TIMETICKS)) == NULL) 1173 err(1, "ober_printf_elements"); 1174 break; 1175 case 'x': 1176 /* String always shrinks */ 1177 if ((str = malloc(strlen(argv[i + 2]))) == NULL) 1178 err(1, "malloc"); 1179 tmpstr = argv[i + 2]; 1180 strl = 0; 1181 do { 1182 lval = strtoll(tmpstr, &endstr, 16); 1183 if (endstr[0] != ' ' && endstr[0] != '\t' && 1184 endstr[0] != '\0') 1185 errx(1, "%s: Bad value notation (%s)", 1186 argv[i], argv[i + 2]); 1187 if (tmpstr == endstr) { 1188 tmpstr++; 1189 continue; 1190 } 1191 if (lval < 0 || lval > 0xff) 1192 errx(1, "%s: Bad value notation (%s)", 1193 argv[i], argv[i + 2]); 1194 str[strl++] = (unsigned char) lval; 1195 tmpstr = endstr + 1; 1196 } while (endstr[0] != '\0'); 1197 goto pastestring; 1198 default: 1199 usage(); 1200 } 1201 if (vblist == NULL) 1202 vblist = varbind; 1203 } 1204 1205 return vblist; 1206 } 1207 1208 __dead void 1209 usage(void) 1210 { 1211 size_t i; 1212 1213 if (snmp_app != NULL) { 1214 fprintf(stderr, "usage: snmp %s%s%s\n", 1215 snmp_app->name, 1216 snmp_app->usecommonopt ? 1217 " [-A authpass] [-a digest] [-c community] [-e secengineid]\n" 1218 " [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n" 1219 " [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n" 1220 " [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n" 1221 " " : "", 1222 snmp_app->usage == NULL ? "" : snmp_app->usage); 1223 exit(1); 1224 } 1225 for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) { 1226 if (i == 0) 1227 fprintf(stderr, "usage: "); 1228 else 1229 fprintf(stderr, " "); 1230 fprintf(stderr, "snmp %s%s %s\n", 1231 snmp_apps[i].name, 1232 snmp_apps[i].usecommonopt ? 1233 " [options]" : "", 1234 snmp_apps[i].usage ? snmp_apps[i].usage : ""); 1235 } 1236 exit(1); 1237 } 1238