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