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