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