1 /* $OpenBSD: print-snmp.c,v 1.21 2016/03/15 05:03:11 mmcc 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 length = vblength; 866 np = vbend; 867 } 868 } 869 870 /* 871 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest 872 */ 873 static void 874 snmppdu_print(u_char pduid, const u_char *np, u_int length) 875 { 876 struct be elem; 877 int count = 0, error; 878 879 /* reqId (Integer) */ 880 if ((count = asn1_parse(np, length, &elem)) < 0) 881 return; 882 if (elem.type != BE_INT) { 883 fputs("[reqId!=INT]", stdout); 884 asn1_print(&elem); 885 return; 886 } 887 /* ignore the reqId */ 888 length -= count; 889 np += count; 890 891 /* errorStatus (Integer) */ 892 if ((count = asn1_parse(np, length, &elem)) < 0) 893 return; 894 if (elem.type != BE_INT) { 895 fputs("[errorStatus!=INT]", stdout); 896 asn1_print(&elem); 897 return; 898 } 899 error = 0; 900 if ((pduid == GETREQ || pduid == GETNEXTREQ) 901 && elem.data.integer != 0) { 902 char errbuf[20]; 903 printf("[errorStatus(%s)!=0]", 904 DECODE_ErrorStatus(elem.data.integer)); 905 } else if (pduid == GETBULKREQ) 906 printf(" non-repeaters=%d", elem.data.integer); 907 else if (elem.data.integer != 0) { 908 char errbuf[20]; 909 printf(" %s", DECODE_ErrorStatus(elem.data.integer)); 910 error = elem.data.integer; 911 } 912 length -= count; 913 np += count; 914 915 /* errorIndex (Integer) */ 916 if ((count = asn1_parse(np, length, &elem)) < 0) 917 return; 918 if (elem.type != BE_INT) { 919 fputs("[errorIndex!=INT]", stdout); 920 asn1_print(&elem); 921 return; 922 } 923 if ((pduid == GETREQ || pduid == GETNEXTREQ) 924 && elem.data.integer != 0) 925 printf("[errorIndex(%d)!=0]", elem.data.integer); 926 else if (pduid == GETBULKREQ) 927 printf(" max-repetitions=%d", elem.data.integer); 928 else if (elem.data.integer != 0) { 929 if (!error) 930 printf("[errorIndex(%d) w/o errorStatus]", 931 elem.data.integer); 932 else { 933 printf("@%d", elem.data.integer); 934 error = elem.data.integer; 935 } 936 } else if (error) { 937 fputs("[errorIndex==0]", stdout); 938 error = 0; 939 } 940 length -= count; 941 np += count; 942 943 varbind_print(pduid, np, length, error); 944 return; 945 } 946 947 /* 948 * Decode SNMP Trap PDU 949 */ 950 static void 951 trap_print(const u_char *np, u_int length) 952 { 953 struct be elem; 954 int count = 0, generic; 955 956 putchar(' '); 957 958 /* enterprise (oid) */ 959 if ((count = asn1_parse(np, length, &elem)) < 0) 960 return; 961 if (elem.type != BE_OID) { 962 fputs("[enterprise!=OID]", stdout); 963 asn1_print(&elem); 964 return; 965 } 966 asn1_print(&elem); 967 length -= count; 968 np += count; 969 970 putchar(' '); 971 972 /* agent-addr (inetaddr) */ 973 if ((count = asn1_parse(np, length, &elem)) < 0) 974 return; 975 if (elem.type != BE_INETADDR) { 976 fputs("[agent-addr!=INETADDR]", stdout); 977 asn1_print(&elem); 978 return; 979 } 980 asn1_print(&elem); 981 length -= count; 982 np += count; 983 984 /* generic-trap (Integer) */ 985 if ((count = asn1_parse(np, length, &elem)) < 0) 986 return; 987 if (elem.type != BE_INT) { 988 fputs("[generic-trap!=INT]", stdout); 989 asn1_print(&elem); 990 return; 991 } 992 generic = elem.data.integer; 993 { 994 char buf[20]; 995 printf(" %s", DECODE_GenericTrap(generic)); 996 } 997 length -= count; 998 np += count; 999 1000 /* specific-trap (Integer) */ 1001 if ((count = asn1_parse(np, length, &elem)) < 0) 1002 return; 1003 if (elem.type != BE_INT) { 1004 fputs("[specific-trap!=INT]", stdout); 1005 asn1_print(&elem); 1006 return; 1007 } 1008 if (generic != GT_ENTERPRISE) { 1009 if (elem.data.integer != 0) 1010 printf("[specific-trap(%d)!=0]", elem.data.integer); 1011 } else 1012 printf(" s=%d", elem.data.integer); 1013 length -= count; 1014 np += count; 1015 1016 putchar(' '); 1017 1018 /* time-stamp (TimeTicks) */ 1019 if ((count = asn1_parse(np, length, &elem)) < 0) 1020 return; 1021 if (elem.type != BE_UNS) { /* XXX */ 1022 fputs("[time-stamp!=TIMETICKS]", stdout); 1023 asn1_print(&elem); 1024 return; 1025 } 1026 asn1_print(&elem); 1027 length -= count; 1028 np += count; 1029 1030 varbind_print (TRAP, np, length, 0); 1031 return; 1032 } 1033 1034 /* 1035 * Decode SNMP header and pass on to PDU printing routines 1036 */ 1037 void 1038 snmp_print(const u_char *np, u_int length) 1039 { 1040 struct be elem, pdu; 1041 int count = 0; 1042 1043 truncated = 0; 1044 1045 /* truncated packet? */ 1046 if (np + length > snapend) { 1047 truncated = 1; 1048 length = snapend - np; 1049 } 1050 1051 putchar(' '); 1052 1053 /* initial Sequence */ 1054 if ((count = asn1_parse(np, length, &elem)) < 0) 1055 return; 1056 if (elem.type != BE_SEQ) { 1057 fputs("[!init SEQ]", stdout); 1058 asn1_print(&elem); 1059 return; 1060 } 1061 if (count < length) 1062 printf("[%d extra after iSEQ]", length - count); 1063 /* descend */ 1064 length = elem.asnlen; 1065 np = (u_char *)elem.data.raw; 1066 /* Version (Integer) */ 1067 if ((count = asn1_parse(np, length, &elem)) < 0) 1068 return; 1069 if (elem.type != BE_INT) { 1070 fputs("[version!=INT]", stdout); 1071 asn1_print(&elem); 1072 return; 1073 } 1074 /* only handle version 1 and 2 */ 1075 if (elem.data.integer > DEF_VERSION) { 1076 printf("[version(%d)>%d]", elem.data.integer, DEF_VERSION); 1077 return; 1078 } 1079 length -= count; 1080 np += count; 1081 1082 /* Community (String) */ 1083 if ((count = asn1_parse(np, length, &elem)) < 0) 1084 return; 1085 if (elem.type != BE_STR) { 1086 fputs("[comm!=STR]", stdout); 1087 asn1_print(&elem); 1088 return; 1089 } 1090 /* default community */ 1091 if (strncmp((char *)elem.data.str, DEF_COMMUNITY, 1092 sizeof(DEF_COMMUNITY) - 1)) 1093 /* ! "public" */ 1094 printf("C=%.*s ", (int)elem.asnlen, elem.data.str); 1095 length -= count; 1096 np += count; 1097 1098 /* PDU (Context) */ 1099 if ((count = asn1_parse(np, length, &pdu)) < 0) 1100 return; 1101 if (pdu.type != BE_PDU) { 1102 fputs("[no PDU]", stdout); 1103 return; 1104 } 1105 if (count < length) 1106 printf("[%d extra after PDU]", length - count); 1107 asn1_print(&pdu); 1108 /* descend into PDU */ 1109 length = pdu.asnlen; 1110 np = (u_char *)pdu.data.raw; 1111 1112 switch (pdu.id) { 1113 case TRAP: 1114 trap_print(np, length); 1115 break; 1116 case GETREQ: 1117 case GETNEXTREQ: 1118 case GETRESP: 1119 case SETREQ: 1120 case GETBULKREQ: 1121 case INFORMREQ: 1122 case TRAPV2: 1123 case REPORT: 1124 snmppdu_print(pdu.id, np, length); 1125 break; 1126 } 1127 return; 1128 } 1129