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