1 /* $OpenBSD: print-snmp.c,v 1.23 2018/09/20 12:23:13 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 5 * John Robert LoVerso. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * 30 * This implementation has been influenced by the CMU SNMP release, 31 * by Steve Waldbusser. However, this shares no code with that system. 32 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_. 33 * Earlier forms of this implementation were derived and/or inspired by an 34 * awk script originally written by C. Philip Wood of LANL (but later 35 * heavily modified by John Robert LoVerso). The copyright notice for 36 * that work is preserved below, even though it may not rightly apply 37 * to this file. 38 * 39 * This started out as a very simple program, but the incremental decoding 40 * (into the BE structure) complicated things. 41 * 42 # Los Alamos National Laboratory 43 # 44 # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 45 # This software was produced under a U.S. Government contract 46 # (W-7405-ENG-36) by Los Alamos National Laboratory, which is 47 # operated by the University of California for the U.S. Department 48 # of Energy. The U.S. Government is licensed to use, reproduce, 49 # and distribute this software. Permission is granted to the 50 # public to copy and use this software without charge, provided 51 # that this Notice and any statement of authorship are reproduced 52 # on all copies. Neither the Government nor the University makes 53 # any warranty, express or implied, or assumes any liability or 54 # responsibility for the use of this software. 55 # @(#)snmp.awk.x 1.1 (LANL) 1/15/90 56 */ 57 58 #include <sys/time.h> 59 60 #include <ctype.h> 61 #include <stdio.h> 62 #include <string.h> 63 64 #include "interface.h" 65 #include "addrtoname.h" 66 67 /* 68 * Universal ASN.1 types 69 * (we only care about the tag values for those allowed in the Internet SMI) 70 */ 71 char *Universal[] = { 72 "U-0", 73 "Boolean", 74 "Integer", 75 #define INTEGER 2 76 "Bitstring", 77 "String", 78 #define STRING 4 79 "Null", 80 #define ASN_NULL 5 81 "ObjID", 82 #define OBJECTID 6 83 "ObjectDes", 84 "U-8","U-9","U-10","U-11", /* 8-11 */ 85 "U-12","U-13","U-14","U-15", /* 12-15 */ 86 "Sequence", 87 #define SEQUENCE 16 88 "Set" 89 }; 90 91 /* 92 * Application-wide ASN.1 types from the Internet SMI and their tags 93 */ 94 char *Application[] = { 95 "IpAddress", 96 #define IPADDR 0 97 "Counter", 98 #define COUNTER 1 99 "Gauge", 100 #define GAUGE 2 101 "TimeTicks", 102 #define TIMETICKS 3 103 "Opaque", 104 #define OPAQUE 4 105 "NsapAddress", 106 #define NSAPADDR 5 107 "Counter64", 108 #define COUNTER64 6 109 "UInteger32" 110 #define UINTEGER32 7 111 }; 112 113 /* 114 * Context-specific ASN.1 types for the SNMP PDUs and their tags 115 */ 116 char *Context[] = { 117 "GetRequest", 118 #define GETREQ 0 119 "GetNextRequest", 120 #define GETNEXTREQ 1 121 "GetResponse", 122 #define GETRESP 2 123 "SetRequest", 124 #define SETREQ 3 125 "Trap", 126 #define TRAP 4 127 "GetBulkReq", 128 #define GETBULKREQ 5 129 "InformReq", 130 #define INFORMREQ 6 131 "TrapV2", 132 #define TRAPV2 7 133 "Report" 134 #define REPORT 8 135 }; 136 137 /* 138 * Private ASN.1 types 139 * The Internet SMI does not specify any 140 */ 141 char *Private[] = { 142 "P-0" 143 }; 144 145 /* 146 * error-status values for any SNMP PDU 147 */ 148 char *ErrorStatus[] = { 149 "noError", 150 "tooBig", 151 "noSuchName", 152 "badValue", 153 "readOnly", 154 "genErr", 155 "noAccess", 156 "wrongType", 157 "wrongLength", 158 "wrongEnc", 159 "wrongValue", 160 "noCreation", 161 "inconValue", 162 "resUnavail", 163 "commitFailed", 164 "undoFailed", 165 "authError", 166 "notWritable", 167 "inconName" 168 }; 169 #define DECODE_ErrorStatus(e) \ 170 ( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \ 171 ? ErrorStatus[e] : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf)) 172 173 /* 174 * generic-trap values in the SNMP Trap-PDU 175 */ 176 char *GenericTrap[] = { 177 "coldStart", 178 "warmStart", 179 "linkDown", 180 "linkUp", 181 "authenticationFailure", 182 "egpNeighborLoss", 183 "enterpriseSpecific" 184 #define GT_ENTERPRISE 6 185 }; 186 #define DECODE_GenericTrap(t) \ 187 ( t >= 0 && t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \ 188 ? GenericTrap[t] : (snprintf(buf, sizeof(buf), "gt=%d", t), buf)) 189 190 /* 191 * ASN.1 type class table 192 * Ties together the preceding Universal, Application, Context, and Private 193 * type definitions. 194 */ 195 #define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */ 196 struct { 197 char *name; 198 char **Id; 199 int numIDs; 200 } Class[] = { 201 defineCLASS(Universal), 202 #define UNIVERSAL 0 203 defineCLASS(Application), 204 #define APPLICATION 1 205 defineCLASS(Context), 206 #define CONTEXT 2 207 defineCLASS(Private), 208 #define PRIVATE 3 209 }; 210 211 /* 212 * defined forms for ASN.1 types 213 */ 214 char *Form[] = { 215 "Primitive", 216 #define PRIMITIVE 0 217 "Constructed", 218 #define CONSTRUCTED 1 219 }; 220 221 /* 222 * A structure for the OID tree for the compiled-in MIB. 223 * This is stored as a general-order tree. 224 */ 225 struct obj { 226 char *desc; /* name of object */ 227 u_int oid; /* sub-id following parent */ 228 u_char type; /* object type (unused) */ 229 struct obj *child, *next; /* child and next sibling pointers */ 230 } *objp = NULL; 231 232 /* 233 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding 234 * RFC-1156 format files into "makemib". "mib.h" MUST define at least 235 * a value for `mibroot'. 236 * 237 * In particular, this is gross, as this is including initialized structures, 238 * and by right shouldn't be an "include" file. 239 */ 240 #include "mib.h" 241 242 /* 243 * This defines a list of OIDs which will be abbreviated on output. 244 * Currently, this includes the prefixes for the Internet MIB, the 245 * private enterprises tree, and the experimental tree. 246 */ 247 struct obj_abrev { 248 char *prefix; /* prefix for this abrev */ 249 struct obj *node; /* pointer into object table */ 250 char *oid; /* ASN.1 encoded OID */ 251 } obj_abrev_list[] = { 252 #ifndef NO_ABREV_MIB 253 /* .iso.org.dod.internet.mgmt.mib */ 254 { "", &_mib_obj, "\53\6\1\2\1" }, 255 #endif 256 #ifndef NO_ABREV_ENTER 257 /* .iso.org.dod.internet.private.enterprises */ 258 { "E:", &_enterprises_obj, "\53\6\1\4\1" }, 259 #endif 260 #ifndef NO_ABREV_EXPERI 261 /* .iso.org.dod.internet.experimental */ 262 { "X:", &_experimental_obj, "\53\6\1\3" }, 263 #endif 264 #ifndef NO_ABREV_SNMPMIBOBJECTS 265 /* .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects */ 266 { "S:", &_snmpmibobjects_obj, "\53\6\1\6\3\1\1" }, 267 #endif 268 { 0,0,0 } 269 }; 270 271 /* 272 * This is used in the OID print routine to walk down the object tree 273 * rooted at `mibroot'. 274 */ 275 #define OBJ_PRINT(o, suppressdot) \ 276 { \ 277 if (objp) { \ 278 do { \ 279 if ((o) == objp->oid) \ 280 break; \ 281 } while ((objp = objp->next) != NULL); \ 282 } \ 283 if (objp) { \ 284 printf(suppressdot?"%s":".%s", objp->desc); \ 285 objp = objp->child; \ 286 } else \ 287 printf(suppressdot?"%u":".%u", (o)); \ 288 } 289 290 /* 291 * This is the definition for the Any-Data-Type storage used purely for 292 * temporary internal representation while decoding an ASN.1 data stream. 293 */ 294 struct be { 295 u_int32_t asnlen; 296 union { 297 caddr_t raw; 298 int32_t integer; 299 u_int32_t uns; 300 u_int64_t uns64; 301 const u_char *str; 302 } data; 303 u_short id; 304 u_char form, class; /* tag info */ 305 u_char type; 306 #define BE_ANY 255 307 #define BE_NONE 0 308 #define BE_NULL 1 309 #define BE_OCTET 2 310 #define BE_OID 3 311 #define BE_INT 4 312 #define BE_UNS 5 313 #define BE_STR 6 314 #define BE_SEQ 7 315 #define BE_INETADDR 8 316 #define BE_PDU 9 317 #define BE_UNS64 10 318 }; 319 320 /* 321 * Defaults for SNMP PDU components 322 */ 323 #define DEF_COMMUNITY "public" 324 #define DEF_VERSION 1 325 326 /* 327 * constants for ASN.1 decoding 328 */ 329 #define OIDMUX 40 330 #define ASNLEN_INETADDR 4 331 #define ASN_SHIFT7 7 332 #define ASN_SHIFT8 8 333 #define ASN_BIT8 0x80 334 #define ASN_LONGLEN 0x80 335 336 #define ASN_ID_BITS 0x1f 337 #define ASN_FORM_BITS 0x20 338 #define ASN_FORM_SHIFT 5 339 #define ASN_CLASS_BITS 0xc0 340 #define ASN_CLASS_SHIFT 6 341 342 #define ASN_ID_EXT 0x1f /* extension ID in tag field */ 343 344 /* 345 * truncated==1 means the packet was complete, but we don't have all of 346 * it to decode. 347 */ 348 static int truncated; 349 350 /* 351 * This decodes the next ASN.1 object in the stream pointed to by "p" 352 * (and of real-length "len") and stores the intermediate data in the 353 * provided BE object. 354 * 355 * This returns -l if it fails (i.e., the ASN.1 stream is not valid). 356 * O/w, this returns the number of bytes parsed from "p". 357 */ 358 static int 359 asn1_parse(const u_char *p, u_int len, struct be *elem) 360 { 361 u_char form, class, id; 362 int i, hdr; 363 364 elem->asnlen = 0; 365 elem->type = BE_ANY; 366 if (len < 1) { 367 if (truncated) 368 fputs("[|snmp]", stdout); 369 else 370 fputs("[nothing to parse]", stdout); 371 return -1; 372 } 373 374 /* 375 * it would be nice to use a bit field, but you can't depend on them. 376 * +---+---+---+---+---+---+---+---+ 377 * + class |frm| id | 378 * +---+---+---+---+---+---+---+---+ 379 * 7 6 5 4 3 2 1 0 380 */ 381 id = *p & ASN_ID_BITS; /* lower 5 bits, range 00-1f */ 382 #ifdef notdef 383 form = (*p & 0xe0) >> 5; /* move upper 3 bits to lower 3 */ 384 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */ 385 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */ 386 #else 387 form = (u_char)(*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT; 388 class = (u_char)(*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT; 389 #endif 390 elem->form = form; 391 elem->class = class; 392 elem->id = id; 393 if (vflag) 394 printf("|%.2x", *p); 395 p++; len--; hdr = 1; 396 /* extended tag field */ 397 if (id == ASN_ID_EXT) { 398 for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) { 399 if (vflag) 400 printf("|%.2x", *p); 401 id = (id << 7) | (*p & ~ASN_BIT8); 402 } 403 if (len == 0 && *p & ASN_BIT8) { 404 if (truncated) 405 fputs("[|snmp]", stdout); 406 else 407 fputs("[Xtagfield?]", stdout); 408 return -1; 409 } 410 elem->id = id = (id << 7) | *p; 411 --len; 412 ++hdr; 413 ++p; 414 } 415 if (len < 1) { 416 if (truncated) 417 fputs("[|snmp]", stdout); 418 else 419 fputs("[no asnlen]", stdout); 420 return -1; 421 } 422 elem->asnlen = *p; 423 if (vflag) 424 printf("|%.2x", *p); 425 p++; len--; hdr++; 426 if (elem->asnlen & ASN_BIT8) { 427 int noct = elem->asnlen % ASN_BIT8; 428 elem->asnlen = 0; 429 if (len < noct) { 430 if (truncated) 431 fputs("[|snmp]", stdout); 432 else 433 printf("[asnlen? %d<%d]", len, noct); 434 return -1; 435 } 436 for (; noct-- > 0; len--, hdr++) { 437 if (vflag) 438 printf("|%.2x", *p); 439 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++; 440 } 441 } 442 if (len < elem->asnlen) { 443 if (!truncated) { 444 printf("[len%d<asnlen%u]", len, elem->asnlen); 445 return -1; 446 } 447 /* maybe should check at least 4? */ 448 elem->asnlen = len; 449 } 450 if (form >= sizeof(Form)/sizeof(Form[0])) { 451 if (truncated) 452 fputs("[|snmp]", stdout); 453 else 454 printf("[form?%d]", form); 455 return -1; 456 } 457 if (class >= sizeof(Class)/sizeof(Class[0])) { 458 if (truncated) 459 fputs("[|snmp]", stdout); 460 else 461 printf("[class?%c/%d]", *Form[form], class); 462 return -1; 463 } 464 if ((int)id >= Class[class].numIDs) { 465 if (truncated) 466 fputs("[|snmp]", stdout); 467 else 468 printf("[id?%c/%s/%d]", *Form[form], 469 Class[class].name, id); 470 return -1; 471 } 472 473 switch (form) { 474 case PRIMITIVE: 475 switch (class) { 476 case UNIVERSAL: 477 switch (id) { 478 case STRING: 479 elem->type = BE_STR; 480 elem->data.str = p; 481 break; 482 483 case INTEGER: { 484 int32_t data; 485 elem->type = BE_INT; 486 data = 0; 487 488 if (*p & ASN_BIT8) /* negative */ 489 data = -1; 490 for (i = elem->asnlen; i-- > 0; p++) 491 data = (data << ASN_SHIFT8) | *p; 492 elem->data.integer = data; 493 break; 494 } 495 496 case OBJECTID: 497 elem->type = BE_OID; 498 elem->data.raw = (caddr_t)p; 499 break; 500 501 case ASN_NULL: 502 elem->type = BE_NULL; 503 elem->data.raw = NULL; 504 break; 505 506 default: 507 elem->type = BE_OCTET; 508 elem->data.raw = (caddr_t)p; 509 printf("[P/U/%s]", 510 Class[class].Id[id]); 511 break; 512 } 513 break; 514 515 case APPLICATION: 516 switch (id) { 517 case IPADDR: 518 elem->type = BE_INETADDR; 519 elem->data.raw = (caddr_t)p; 520 break; 521 522 case COUNTER: 523 case GAUGE: 524 case TIMETICKS: 525 case OPAQUE: 526 case NSAPADDR: 527 case UINTEGER32: { 528 u_int32_t data; 529 elem->type = BE_UNS; 530 data = 0; 531 for (i = elem->asnlen; i-- > 0; p++) 532 data = (data << 8) + *p; 533 elem->data.uns = data; 534 break; 535 } 536 537 case COUNTER64: { 538 u_int64_t data; 539 elem->type = BE_UNS64; 540 data = 0; 541 for (i = elem->asnlen; i-- > 0; p++) 542 data = (data << 8) + *p; 543 elem->data.uns64 = data; 544 break; 545 } 546 547 default: 548 elem->type = BE_OCTET; 549 elem->data.raw = (caddr_t)p; 550 printf("[P/A/%s]", 551 Class[class].Id[id]); 552 break; 553 } 554 break; 555 556 default: 557 elem->type = BE_OCTET; 558 elem->data.raw = (caddr_t)p; 559 printf("[P/%s/%s]", 560 Class[class].name, Class[class].Id[id]); 561 break; 562 } 563 break; 564 565 case CONSTRUCTED: 566 switch (class) { 567 case UNIVERSAL: 568 switch (id) { 569 case SEQUENCE: 570 elem->type = BE_SEQ; 571 elem->data.raw = (caddr_t)p; 572 break; 573 574 default: 575 elem->type = BE_OCTET; 576 elem->data.raw = (caddr_t)p; 577 printf("C/U/%s", Class[class].Id[id]); 578 break; 579 } 580 break; 581 582 case CONTEXT: 583 elem->type = BE_PDU; 584 elem->data.raw = (caddr_t)p; 585 break; 586 587 default: 588 elem->type = BE_OCTET; 589 elem->data.raw = (caddr_t)p; 590 printf("C/%s/%s", 591 Class[class].name, Class[class].Id[id]); 592 break; 593 } 594 break; 595 } 596 p += elem->asnlen; 597 len -= elem->asnlen; 598 return elem->asnlen + hdr; 599 } 600 601 /* 602 * Display the ASN.1 object represented by the BE object. 603 * This used to be an integral part of asn1_parse() before the intermediate 604 * BE form was added. 605 */ 606 static void 607 asn1_print(struct be *elem) 608 { 609 u_char *p = (u_char *)elem->data.raw; 610 u_int32_t asnlen = elem->asnlen; 611 int i; 612 613 switch (elem->type) { 614 615 case BE_OCTET: 616 for (i = asnlen; i-- > 0; p++) 617 printf("_%.2x", *p); 618 break; 619 620 case BE_NULL: 621 break; 622 623 case BE_OID: { 624 int o = 0, first = -1, i = asnlen; 625 626 if (!nflag && asnlen > 2) { 627 struct obj_abrev *a = &obj_abrev_list[0]; 628 for (; a->node; a++) { 629 if (!memcmp(a->oid, (char *)p, 630 strlen(a->oid))) { 631 objp = a->node->child; 632 i -= strlen(a->oid); 633 p += strlen(a->oid); 634 fputs(a->prefix, stdout); 635 first = 1; 636 break; 637 } 638 } 639 } 640 for (; i-- > 0; p++) { 641 o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8); 642 if (*p & ASN_LONGLEN) 643 continue; 644 645 /* 646 * first subitem encodes two items with 1st*OIDMUX+2nd 647 */ 648 if (first < 0) { 649 if (!nflag) 650 objp = mibroot; 651 first = 0; 652 OBJ_PRINT(o/OIDMUX, first); 653 o %= OIDMUX; 654 } 655 OBJ_PRINT(o, first); 656 if (--first < 0) 657 first = 0; 658 o = 0; 659 } 660 break; 661 } 662 663 case BE_INT: 664 printf("%d", elem->data.integer); 665 break; 666 667 case BE_UNS: 668 printf("%d", elem->data.uns); 669 break; 670 671 case BE_UNS64: 672 printf("%lld", elem->data.uns64); 673 break; 674 675 case BE_STR: { 676 int printable = 1, first = 1; 677 const u_char *p = elem->data.str; 678 for (i = asnlen; printable && i-- > 0; p++) 679 printable = isprint(*p) || isspace(*p); 680 p = elem->data.str; 681 if (printable) { 682 putchar('"'); 683 (void)fn_print(p, p + asnlen); 684 putchar('"'); 685 } else 686 for (i = asnlen; i-- > 0; p++) { 687 printf(first ? "%.2x" : "_%.2x", *p); 688 first = 0; 689 } 690 break; 691 } 692 693 case BE_SEQ: 694 printf("Seq(%u)", elem->asnlen); 695 break; 696 697 case BE_INETADDR: { 698 char sep; 699 if (asnlen != ASNLEN_INETADDR) 700 printf("[inetaddr len!=%d]", ASNLEN_INETADDR); 701 sep='['; 702 for (i = asnlen; i-- > 0; p++) { 703 printf("%c%u", sep, *p); 704 sep='.'; 705 } 706 putchar(']'); 707 break; 708 } 709 710 case BE_PDU: 711 printf("%s(%u)", 712 Class[CONTEXT].Id[elem->id], elem->asnlen); 713 break; 714 715 case BE_ANY: 716 fputs("[BE_ANY!?]", stdout); 717 break; 718 719 default: 720 fputs("[be!?]", stdout); 721 break; 722 } 723 } 724 725 #ifdef notdef 726 /* 727 * This is a brute force ASN.1 printer: recurses to dump an entire structure. 728 * This will work for any ASN.1 stream, not just an SNMP PDU. 729 * 730 * By adding newlines and spaces at the correct places, this would print in 731 * Rose-Normal-Form. 732 * 733 * This is not currently used. 734 */ 735 static void 736 asn1_decode(u_char *p, u_int length) 737 { 738 struct be elem; 739 int i = 0; 740 741 while (i >= 0 && length > 0) { 742 i = asn1_parse(p, length, &elem); 743 if (i >= 0) { 744 fputs(" ", stdout); 745 asn1_print(&elem); 746 if (elem.type == BE_SEQ || elem.type == BE_PDU) { 747 fputs(" {", stdout); 748 asn1_decode(elem.data.raw, elem.asnlen); 749 fputs(" }", stdout); 750 } 751 length -= i; 752 p += i; 753 } 754 } 755 } 756 #endif 757 758 /* 759 * General SNMP header 760 * SEQUENCE { 761 * version INTEGER {version-1(0)}, 762 * community OCTET STRING, 763 * data ANY -- PDUs 764 * } 765 * PDUs for all but Trap: (see rfc1157 from page 15 on) 766 * SEQUENCE { 767 * request-id INTEGER, 768 * error-status INTEGER, 769 * error-index INTEGER, 770 * varbindlist SEQUENCE OF 771 * SEQUENCE { 772 * name ObjectName, 773 * value ObjectValue 774 * } 775 * } 776 * PDU for Trap: 777 * SEQUENCE { 778 * enterprise OBJECT IDENTIFIER, 779 * agent-addr NetworkAddress, 780 * generic-trap INTEGER, 781 * specific-trap INTEGER, 782 * time-stamp TimeTicks, 783 * varbindlist SEQUENCE OF 784 * SEQUENCE { 785 * name ObjectName, 786 * value ObjectValue 787 * } 788 * } 789 */ 790 791 /* 792 * Decode SNMP varBind 793 */ 794 static void 795 varbind_print(u_char pduid, const u_char *np, u_int length, int error) 796 { 797 struct be elem; 798 int count = 0, ind; 799 800 /* Sequence of varBind */ 801 if ((count = asn1_parse(np, length, &elem)) < 0) 802 return; 803 if (elem.type != BE_SEQ) { 804 fputs("[!SEQ of varbind]", stdout); 805 asn1_print(&elem); 806 return; 807 } 808 if (count < length) 809 printf("[%d extra after SEQ of varbind]", length - count); 810 /* descend */ 811 length = elem.asnlen; 812 np = (u_char *)elem.data.raw; 813 814 for (ind = 1; length > 0; ind++) { 815 const u_char *vbend; 816 u_int vblength; 817 818 if (!error || ind == error) 819 fputs(" ", stdout); 820 821 /* Sequence */ 822 if ((count = asn1_parse(np, length, &elem)) < 0) 823 return; 824 if (elem.type != BE_SEQ) { 825 fputs("[!varbind]", stdout); 826 asn1_print(&elem); 827 return; 828 } 829 vbend = np + count; 830 vblength = length - count; 831 /* descend */ 832 length = elem.asnlen; 833 np = (u_char *)elem.data.raw; 834 835 /* objName (OID) */ 836 if ((count = asn1_parse(np, length, &elem)) < 0) 837 return; 838 if (elem.type != BE_OID) { 839 fputs("[objName!=OID]", stdout); 840 asn1_print(&elem); 841 return; 842 } 843 if (!error || ind == error) 844 asn1_print(&elem); 845 length -= count; 846 np += count; 847 848 if (pduid != GETREQ && pduid != GETNEXTREQ && !error) 849 fputs("=", stdout); 850 851 /* objVal (ANY) */ 852 if ((count = asn1_parse(np, length, &elem)) < 0) 853 return; 854 if (pduid == GETREQ || pduid == GETNEXTREQ || pduid == GETBULKREQ) { 855 if (elem.type != BE_NULL) { 856 fputs("[objVal!=NULL]", stdout); 857 asn1_print(&elem); 858 } 859 } else { 860 if (error && ind == error && elem.type != BE_NULL) 861 fputs("[err objVal!=NULL]", stdout); 862 if (!error || ind == error) 863 asn1_print(&elem); 864 } 865 866 length = vblength; 867 np = vbend; 868 } 869 } 870 871 /* 872 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest 873 */ 874 static void 875 snmppdu_print(u_char pduid, const u_char *np, u_int length) 876 { 877 struct be elem; 878 int count = 0, error; 879 880 /* reqId (Integer) */ 881 if ((count = asn1_parse(np, length, &elem)) < 0) 882 return; 883 if (elem.type != BE_INT) { 884 fputs("[reqId!=INT]", stdout); 885 asn1_print(&elem); 886 return; 887 } 888 /* ignore the reqId */ 889 length -= count; 890 np += count; 891 892 /* errorStatus (Integer) */ 893 if ((count = asn1_parse(np, length, &elem)) < 0) 894 return; 895 if (elem.type != BE_INT) { 896 fputs("[errorStatus!=INT]", stdout); 897 asn1_print(&elem); 898 return; 899 } 900 error = 0; 901 if ((pduid == GETREQ || pduid == GETNEXTREQ) 902 && elem.data.integer != 0) { 903 char errbuf[20]; 904 printf("[errorStatus(%s)!=0]", 905 DECODE_ErrorStatus(elem.data.integer)); 906 } else if (pduid == GETBULKREQ) 907 printf(" non-repeaters=%d", elem.data.integer); 908 else if (elem.data.integer != 0) { 909 char errbuf[20]; 910 printf(" %s", DECODE_ErrorStatus(elem.data.integer)); 911 error = elem.data.integer; 912 } 913 length -= count; 914 np += count; 915 916 /* errorIndex (Integer) */ 917 if ((count = asn1_parse(np, length, &elem)) < 0) 918 return; 919 if (elem.type != BE_INT) { 920 fputs("[errorIndex!=INT]", stdout); 921 asn1_print(&elem); 922 return; 923 } 924 if ((pduid == GETREQ || pduid == GETNEXTREQ) 925 && elem.data.integer != 0) 926 printf("[errorIndex(%d)!=0]", elem.data.integer); 927 else if (pduid == GETBULKREQ) 928 printf(" max-repetitions=%d", elem.data.integer); 929 else if (elem.data.integer != 0) { 930 if (!error) 931 printf("[errorIndex(%d) w/o errorStatus]", 932 elem.data.integer); 933 else { 934 printf("@%d", elem.data.integer); 935 error = elem.data.integer; 936 } 937 } else if (error) { 938 fputs("[errorIndex==0]", stdout); 939 error = 0; 940 } 941 length -= count; 942 np += count; 943 944 varbind_print(pduid, np, length, error); 945 return; 946 } 947 948 /* 949 * Decode SNMP Trap PDU 950 */ 951 static void 952 trap_print(const u_char *np, u_int length) 953 { 954 struct be elem; 955 int count = 0, generic; 956 957 putchar(' '); 958 959 /* enterprise (oid) */ 960 if ((count = asn1_parse(np, length, &elem)) < 0) 961 return; 962 if (elem.type != BE_OID) { 963 fputs("[enterprise!=OID]", stdout); 964 asn1_print(&elem); 965 return; 966 } 967 asn1_print(&elem); 968 length -= count; 969 np += count; 970 971 putchar(' '); 972 973 /* agent-addr (inetaddr) */ 974 if ((count = asn1_parse(np, length, &elem)) < 0) 975 return; 976 if (elem.type != BE_INETADDR) { 977 fputs("[agent-addr!=INETADDR]", stdout); 978 asn1_print(&elem); 979 return; 980 } 981 asn1_print(&elem); 982 length -= count; 983 np += count; 984 985 /* generic-trap (Integer) */ 986 if ((count = asn1_parse(np, length, &elem)) < 0) 987 return; 988 if (elem.type != BE_INT) { 989 fputs("[generic-trap!=INT]", stdout); 990 asn1_print(&elem); 991 return; 992 } 993 generic = elem.data.integer; 994 { 995 char buf[20]; 996 printf(" %s", DECODE_GenericTrap(generic)); 997 } 998 length -= count; 999 np += count; 1000 1001 /* specific-trap (Integer) */ 1002 if ((count = asn1_parse(np, length, &elem)) < 0) 1003 return; 1004 if (elem.type != BE_INT) { 1005 fputs("[specific-trap!=INT]", stdout); 1006 asn1_print(&elem); 1007 return; 1008 } 1009 if (generic != GT_ENTERPRISE) { 1010 if (elem.data.integer != 0) 1011 printf("[specific-trap(%d)!=0]", elem.data.integer); 1012 } else 1013 printf(" s=%d", elem.data.integer); 1014 length -= count; 1015 np += count; 1016 1017 putchar(' '); 1018 1019 /* time-stamp (TimeTicks) */ 1020 if ((count = asn1_parse(np, length, &elem)) < 0) 1021 return; 1022 if (elem.type != BE_UNS) { /* XXX */ 1023 fputs("[time-stamp!=TIMETICKS]", stdout); 1024 asn1_print(&elem); 1025 return; 1026 } 1027 asn1_print(&elem); 1028 length -= count; 1029 np += count; 1030 1031 varbind_print (TRAP, np, length, 0); 1032 return; 1033 } 1034 1035 /* 1036 * Decode SNMP header and pass on to PDU printing routines 1037 */ 1038 void 1039 snmp_print(const u_char *np, u_int length) 1040 { 1041 struct be elem, pdu; 1042 int count = 0; 1043 1044 truncated = 0; 1045 1046 /* truncated packet? */ 1047 if (np + length > snapend) { 1048 truncated = 1; 1049 length = snapend - np; 1050 } 1051 1052 /* initial Sequence */ 1053 if ((count = asn1_parse(np, length, &elem)) < 0) 1054 return; 1055 if (elem.type != BE_SEQ) { 1056 fputs("[!init SEQ]", stdout); 1057 asn1_print(&elem); 1058 return; 1059 } 1060 if (count < length) 1061 printf("[%d extra after iSEQ]", length - count); 1062 /* descend */ 1063 length = elem.asnlen; 1064 np = (u_char *)elem.data.raw; 1065 /* Version (Integer) */ 1066 if ((count = asn1_parse(np, length, &elem)) < 0) 1067 return; 1068 if (elem.type != BE_INT) { 1069 fputs("[version!=INT]", stdout); 1070 asn1_print(&elem); 1071 return; 1072 } 1073 /* only handle version 1 and 2 */ 1074 if (elem.data.integer > DEF_VERSION) { 1075 printf("[version(%d)>%d]", elem.data.integer, DEF_VERSION); 1076 return; 1077 } 1078 length -= count; 1079 np += count; 1080 1081 /* Community (String) */ 1082 if ((count = asn1_parse(np, length, &elem)) < 0) 1083 return; 1084 if (elem.type != BE_STR) { 1085 fputs("[comm!=STR]", stdout); 1086 asn1_print(&elem); 1087 return; 1088 } 1089 /* default community */ 1090 if (strncmp((char *)elem.data.str, DEF_COMMUNITY, 1091 sizeof(DEF_COMMUNITY) - 1)) 1092 /* ! "public" */ 1093 printf("C=%.*s ", (int)elem.asnlen, elem.data.str); 1094 length -= count; 1095 np += count; 1096 1097 /* PDU (Context) */ 1098 if ((count = asn1_parse(np, length, &pdu)) < 0) 1099 return; 1100 if (pdu.type != BE_PDU) { 1101 fputs("[no PDU]", stdout); 1102 return; 1103 } 1104 if (count < length) 1105 printf("[%d extra after PDU]", length - count); 1106 asn1_print(&pdu); 1107 /* descend into PDU */ 1108 length = pdu.asnlen; 1109 np = (u_char *)pdu.data.raw; 1110 1111 switch (pdu.id) { 1112 case TRAP: 1113 trap_print(np, length); 1114 break; 1115 case GETREQ: 1116 case GETNEXTREQ: 1117 case GETRESP: 1118 case SETREQ: 1119 case GETBULKREQ: 1120 case INFORMREQ: 1121 case TRAPV2: 1122 case REPORT: 1123 snmppdu_print(pdu.id, np, length); 1124 break; 1125 } 1126 return; 1127 } 1128