1 /*
   2  * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package com.sun.jmx.snmp;
  28 
  29 
  30 
  31 
  32 /**
  33  * The <CODE>BerDecoder</CODE> class is used for decoding
  34  * BER-encoded data.
  35  *
  36  * A <CODE>BerDecoder</CODE> needs to be set up with the byte string containing
  37  * the encoding. It maintains a current position in the byte string.
  38  *
  39  * Methods allows to fetch integer, string, OID, etc., from the current
  40  * position. After a fetch the current position is moved forward.
  41  *
  42  * A fetch throws a <CODE>BerException</CODE> if the encoding is not of the
  43  * expected type.
  44  *
  45  * <p><b>This API is a Sun Microsystems internal API  and is subject
  46  * to change without notice.</b></p>
  47  *
  48  * @since 1.5
  49  */
  50 
  51 public class BerDecoder {
  52 
  53   /**
  54   * Constructs a new decoder and attaches it to the specified byte string.
  55   *
  56   * @param b The byte string containing the encoded data.
  57   */
  58 
  59   public BerDecoder(byte b[]) {
  60     bytes = b ;
  61     reset() ;
  62   }
  63 
  64   public void reset() {
  65     next = 0 ;
  66     stackTop = 0 ;
  67   }
  68 
  69   /**
  70   * Fetch an integer.
  71   *
  72   * @return The decoded integer.
  73   *
  74   * @exception BerException Current position does not point to an integer.
  75   */
  76 
  77   public int fetchInteger() throws BerException {
  78     return fetchInteger(IntegerTag) ;
  79   }
  80 
  81 
  82   /**
  83   * Fetch an integer with the specified tag.
  84   *
  85   * @param tag The expected tag.
  86   *
  87   * @return The decoded integer.
  88   *
  89   * @exception BerException Current position does not point to an integer
  90   *                         or the tag is not the expected one.
  91   */
  92 
  93   public int fetchInteger(int tag) throws BerException {
  94     int result = 0 ;
  95     final int backup = next ;
  96     try {
  97       if (fetchTag() != tag) {
  98         throw new BerException() ;
  99       }
 100       result = fetchIntegerValue() ;
 101     }
 102     catch(BerException e) {
 103       next = backup ;
 104       throw e ;
 105     }
 106 
 107     return result ;
 108   }
 109 
 110 
 111 
 112   /**
 113   * Fetch an integer and return a long value.
 114   *
 115   * @return The decoded integer.
 116   *
 117   * @exception BerException Current position does not point to an integer.
 118   */
 119 
 120   public long fetchIntegerAsLong() throws BerException {
 121     return fetchIntegerAsLong(IntegerTag) ;
 122   }
 123 
 124 
 125   /**
 126   * Fetch an integer with the specified tag and return a long value.
 127   *
 128   * @param tag The expected tag.
 129   *
 130   * @return The decoded integer.
 131   *
 132   * @exception BerException Current position does not point to an integer
 133   *                         or the tag is not the expected one.
 134   */
 135 
 136   public long fetchIntegerAsLong(int tag) throws BerException {
 137     long result = 0 ;
 138     final int backup = next ;
 139     try {
 140       if (fetchTag() != tag) {
 141         throw new BerException() ;
 142       }
 143       result = fetchIntegerValueAsLong() ;
 144     }
 145     catch(BerException e) {
 146       next = backup ;
 147       throw e ;
 148     }
 149 
 150     return result ;
 151   }
 152 
 153 
 154 
 155   /**
 156   * Fetch an octet string.
 157   *
 158   * @return The decoded string.
 159   *
 160   * @exception BerException Current position does not point to an octet string.
 161   */
 162 
 163   public byte[] fetchOctetString() throws BerException {
 164     return fetchOctetString(OctetStringTag) ;
 165   }
 166 
 167 
 168   /**
 169   * Fetch an octet string with a specified tag.
 170   *
 171   * @param tag The expected tag.
 172   *
 173   * @return The decoded string.
 174   *
 175   * @exception BerException Current position does not point to an octet string
 176   *                         or the tag is not the expected one.
 177   */
 178 
 179   public byte[] fetchOctetString(int tag) throws BerException {
 180     byte[] result = null ;
 181     final int backup = next ;
 182     try {
 183       if (fetchTag() != tag) {
 184         throw new BerException() ;
 185       }
 186       result = fetchStringValue() ;
 187     }
 188     catch(BerException e) {
 189       next = backup ;
 190       throw e ;
 191     }
 192 
 193     return result ;
 194   }
 195 
 196 
 197   /**
 198   * Fetch an object identifier.
 199   *
 200   * @return The decoded object identifier as an array of long.
 201   */
 202 
 203   public long[] fetchOid() throws BerException {
 204     return fetchOid(OidTag) ;
 205   }
 206 
 207 
 208   /**
 209   * Fetch an object identifier with a specified tag.
 210   *
 211   * @param tag The expected tag.
 212   *
 213   * @return The decoded object identifier as an array of long.
 214   *
 215   * @exception BerException Current position does not point to an oid
 216   *                         or the tag is not the expected one.
 217   */
 218 
 219   public long[] fetchOid(int tag) throws BerException {
 220     long[] result = null ;
 221     final int backup = next ;
 222     try {
 223       if (fetchTag() != tag) {
 224         throw new BerException() ;
 225       }
 226       result = fetchOidValue() ;
 227     }
 228     catch(BerException e) {
 229       next = backup ;
 230       throw e ;
 231     }
 232 
 233     return result ;
 234   }
 235 
 236 
 237   /**
 238   * Fetch a <CODE>NULL</CODE> value.
 239   *
 240   * @exception BerException Current position does not point to <CODE>NULL</CODE> value.
 241   */
 242 
 243   public void fetchNull() throws BerException {
 244     fetchNull(NullTag) ;
 245   }
 246 
 247 
 248   /**
 249   * Fetch a <CODE>NULL</CODE> value with a specified tag.
 250   *
 251   * @param tag The expected tag.
 252   *
 253   * @exception BerException Current position does not point to
 254   *            <CODE>NULL</CODE> value or the tag is not the expected one.
 255   */
 256 
 257   public void fetchNull(int tag) throws BerException {
 258     final int backup = next ;
 259     try {
 260       if (fetchTag() != tag) {
 261         throw new BerException() ;
 262       }
 263       final int length = fetchLength();
 264       if (length != 0) throw new BerException();
 265     }
 266     catch(BerException e) {
 267       next = backup ;
 268       throw e ;
 269     }
 270   }
 271 
 272 
 273 
 274   /**
 275   * Fetch an <CODE>ANY</CODE> value. In fact, this method does not decode anything
 276   * it simply returns the next TLV as an array of bytes.
 277   *
 278   * @return The TLV as a byte array.
 279   *
 280   * @exception BerException The next TLV is really badly encoded...
 281   */
 282 
 283   public byte[] fetchAny() throws BerException {
 284     byte[] result = null ;
 285     final int backup = next ;
 286     try {
 287       final int tag = fetchTag() ;
 288       final int contentLength = fetchLength() ;
 289       if (contentLength < 0) throw new BerException() ;
 290       final int tlvLength = next + contentLength - backup ;
 291       if (contentLength > (bytes.length - next))
 292           throw new IndexOutOfBoundsException("Decoded length exceeds buffer");
 293       final byte[] data = new byte[tlvLength] ;
 294       java.lang.System.arraycopy(bytes,backup,data,0,tlvLength);
 295       // for (int i = 0 ; i < tlvLength ; i++) {
 296       //  data[i] = bytes[backup + i] ;
 297       // }
 298       next = next + contentLength ;
 299       result = data;
 300     }
 301     catch(IndexOutOfBoundsException e) {
 302       next = backup ;
 303       throw new BerException() ;
 304     }
 305     // catch(Error e) {
 306     //    debug("fetchAny: Error decoding BER: " + e);
 307     //    throw e;
 308     // }
 309 
 310     return result ;
 311   }
 312 
 313 
 314   /**
 315   * Fetch an <CODE>ANY</CODE> value with a specific tag.
 316   *
 317   * @param tag The expected tag.
 318   *
 319   * @return The TLV as a byte array.
 320   *
 321   * @exception BerException The next TLV is really badly encoded...
 322   */
 323 
 324   public byte[] fetchAny(int tag) throws BerException {
 325     if (getTag() != tag) {
 326       throw new BerException() ;
 327     }
 328     return fetchAny() ;
 329   }
 330 
 331 
 332 
 333   /**
 334   * Fetch a sequence header.
 335   * The decoder computes the end position of the sequence and push it
 336   * on its stack.
 337   *
 338   * @exception BerException Current position does not point to a sequence header.
 339   */
 340 
 341   public void openSequence() throws BerException {
 342     openSequence(SequenceTag) ;
 343   }
 344 
 345 
 346   /**
 347   * Fetch a sequence header with a specific tag.
 348   *
 349   * @param tag The expected tag.
 350   *
 351   * @exception BerException Current position does not point to a sequence header
 352   *                         or the tag is not the expected one.
 353   */
 354 
 355   public void openSequence(int tag) throws BerException {
 356     final int backup = next ;
 357     try {
 358       if (fetchTag() != tag) {
 359         throw new BerException() ;
 360       }
 361       final int l = fetchLength() ;
 362       if (l < 0) throw new BerException();
 363       if (l > (bytes.length - next)) throw new BerException();
 364       stackBuf[stackTop++] = next + l ;
 365     }
 366     catch(BerException e) {
 367       next = backup ;
 368       throw e ;
 369     }
 370   }
 371 
 372 
 373   /**
 374   * Close a sequence.
 375   * The decode pull the stack and verifies that the current position
 376   * matches with the calculated end of the sequence. If not it throws
 377   * an exception.
 378   *
 379   * @exception BerException The sequence is not expected to finish here.
 380   */
 381 
 382   public void closeSequence() throws BerException {
 383     if (stackBuf[stackTop - 1] == next) {
 384       stackTop-- ;
 385     }
 386     else {
 387       throw new BerException() ;
 388     }
 389   }
 390 
 391 
 392   /**
 393   * Return <CODE>true</CODE> if the end of the current sequence is not reached.
 394   * When this method returns <CODE>false</CODE>, <CODE>closeSequence</CODE> can (and must) be
 395   * invoked.
 396   *
 397   * @return <CODE>true</CODE> if there is still some data in the sequence.
 398   */
 399 
 400   public boolean cannotCloseSequence() {
 401     return (next < stackBuf[stackTop - 1]) ;
 402   }
 403 
 404 
 405   /**
 406   * Get the tag of the data at the current position.
 407   * Current position is unchanged.
 408   *
 409   * @return The next tag.
 410   */
 411 
 412   public int getTag() throws BerException {
 413     int result = 0 ;
 414     final int backup = next ;
 415     try {
 416       result = fetchTag() ;
 417     }
 418     finally {
 419       next = backup ;
 420     }
 421 
 422     return result ;
 423   }
 424 
 425 
 426 
 427   public String toString() {
 428     final StringBuffer result = new StringBuffer(bytes.length * 2) ;
 429     for (int i = 0 ; i < bytes.length ; i++) {
 430       final int b = (bytes[i] > 0) ? bytes[i] : bytes[i] + 256 ;
 431       if (i == next) {
 432         result.append("(") ;
 433       }
 434       result.append(Character.forDigit(b / 16, 16)) ;
 435       result.append(Character.forDigit(b % 16, 16)) ;
 436       if (i == next) {
 437         result.append(")") ;
 438       }
 439     }
 440     if (bytes.length == next) {
 441       result.append("()") ;
 442     }
 443 
 444     return new String(result) ;
 445   }
 446 
 447 
 448   //
 449   // Some standard tags
 450   //
 451   public final static int BooleanTag      = 1 ;
 452   public final static int IntegerTag      = 2 ;
 453   public final static int OctetStringTag  = 4 ;
 454   public final static int NullTag          = 5 ;
 455   public final static int OidTag          = 6 ;
 456   public final static int SequenceTag      = 0x30 ;
 457 
 458 
 459 
 460 
 461   ////////////////////////// PRIVATE ///////////////////////////////
 462 
 463 
 464 
 465   /**
 466   * Fetch a tag and move the current position forward.
 467   *
 468   * @return The tag
 469   */
 470 
 471   private final int fetchTag() throws BerException {
 472     int result = 0 ;
 473     final int backup = next ;
 474 
 475     try {
 476       final byte b0 = bytes[next++] ;
 477       result = (b0 >= 0) ? b0 : b0 + 256 ;
 478       if ((result & 31) == 31) {
 479         while ((bytes[next] & 128) != 0) {
 480           result = result << 7 ;
 481           result = result | (bytes[next++] & 127);
 482         }
 483       }
 484     }
 485     catch(IndexOutOfBoundsException e) {
 486       next = backup ;
 487       throw new BerException() ;
 488     }
 489 
 490     return result ;
 491   }
 492 
 493 
 494   /**
 495   * Fetch a length and move the current position forward.
 496   *
 497   * @return The length
 498   */
 499 
 500   private final int fetchLength() throws BerException {
 501     int result = 0 ;
 502     final int backup = next ;
 503 
 504     try {
 505       final byte b0 = bytes[next++] ;
 506       if (b0 >= 0) {
 507         result = b0 ;
 508       }
 509       else {
 510         for (int c = 128 + b0 ; c > 0 ; c--) {
 511           final byte bX = bytes[next++] ;
 512           result = result << 8 ;
 513           result = result | ((bX >= 0) ? bX : bX+256) ;
 514         }
 515       }
 516     }
 517     catch(IndexOutOfBoundsException e) {
 518       next = backup ;
 519       throw new BerException() ;
 520     }
 521 
 522     return result ;
 523   }
 524 
 525 
 526   /**
 527   * Fetch an integer value and move the current position forward.
 528   *
 529   * @return The integer
 530   */
 531 
 532   private int fetchIntegerValue() throws BerException {
 533     int result = 0 ;
 534     final int backup = next ;
 535 
 536     try {
 537       final int length = fetchLength() ;
 538       if (length <= 0) throw new BerException() ;
 539       if (length > (bytes.length - next)) throw
 540           new IndexOutOfBoundsException("Decoded length exceeds buffer");
 541       final int end = next + length ;
 542       result = bytes[next++] ;
 543       while (next < end) {
 544         final byte b = bytes[next++] ;
 545         if (b < 0) {
 546           result = (result << 8) | (256 + b) ;
 547         }
 548         else {
 549           result = (result << 8) | b ;
 550         }
 551       }
 552     }
 553     catch(BerException e) {
 554       next = backup ;
 555       throw e ;
 556     }
 557     catch(IndexOutOfBoundsException e) {
 558       next = backup ;
 559       throw new BerException() ;
 560     }
 561     catch(ArithmeticException e) {
 562       next = backup ;
 563       throw new BerException() ;
 564     }
 565     return result ;
 566   }
 567 
 568 
 569   /**
 570   * Fetch an integer value and return a long value.
 571   * FIX ME: someday we could have only on fetchIntegerValue() which always
 572   * returns a long value.
 573   *
 574   * @return The integer
 575   */
 576 
 577   private final long fetchIntegerValueAsLong() throws BerException {
 578     long result = 0 ;
 579     final int backup = next ;
 580 
 581     try {
 582       final int length = fetchLength() ;
 583       if (length <= 0) throw new BerException() ;
 584       if (length > (bytes.length - next)) throw
 585           new IndexOutOfBoundsException("Decoded length exceeds buffer");
 586 
 587       final int end = next + length ;
 588       result = bytes[next++] ;
 589       while (next < end) {
 590         final byte b = bytes[next++] ;
 591         if (b < 0) {
 592           result = (result << 8) | (256 + b) ;
 593         }
 594         else {
 595           result = (result << 8) | b ;
 596         }
 597       }
 598     }
 599     catch(BerException e) {
 600       next = backup ;
 601       throw e ;
 602     }
 603     catch(IndexOutOfBoundsException e) {
 604       next = backup ;
 605       throw new BerException() ;
 606     }
 607     catch(ArithmeticException e) {
 608       next = backup ;
 609       throw new BerException() ;
 610     }
 611     return result ;
 612   }
 613 
 614 
 615   /**
 616   * Fetch a byte string and move the current position forward.
 617   *
 618   * @return The byte string
 619   */
 620 
 621   private byte[] fetchStringValue() throws BerException {
 622     byte[] result = null ;
 623     final int backup = next ;
 624 
 625     try {
 626       final int length = fetchLength() ;
 627       if (length < 0) throw new BerException() ;
 628       if (length > (bytes.length - next))
 629           throw new IndexOutOfBoundsException("Decoded length exceeds buffer");
 630       final byte data[] = new byte[length] ;
 631       java.lang.System.arraycopy(bytes,next,data,0,length);
 632       next += length;
 633       //      int i = 0 ;
 634       //      while (i < length) {
 635       //          result[i++] = bytes[next++] ;
 636       //      }
 637       result = data;
 638     }
 639     catch(BerException e) {
 640         next = backup ;
 641       throw e ;
 642     }
 643     catch(IndexOutOfBoundsException e) {
 644       next = backup ;
 645       throw new BerException() ;
 646     }
 647     catch(ArithmeticException e) {
 648       next = backup ;
 649       throw new BerException() ;
 650     }
 651     // catch(Error e) {
 652     //  debug("fetchStringValue: Error decoding BER: " + e);
 653     //  throw e;
 654     // }
 655 
 656     return result ;
 657   }
 658 
 659 
 660 
 661   /**
 662   * Fetch an oid and move the current position forward.
 663   *
 664   * @return The oid
 665   */
 666 
 667   private final long[] fetchOidValue() throws BerException {
 668     long[] result = null ;
 669     final int backup = next ;
 670 
 671     try {
 672       final int length = fetchLength() ;
 673       if (length <= 0) throw new BerException() ;
 674       if (length > (bytes.length - next))
 675           throw new IndexOutOfBoundsException("Decoded length exceeds buffer");
 676       // Count how many bytes have their 8th bit to 0
 677       // -> this gives the number of components in the oid
 678       int subidCount = 2 ;
 679       for (int i = 1 ; i < length ; i++) {
 680         if ((bytes[next + i] & 0x80) == 0) {
 681           subidCount++ ;
 682         }
 683       }
 684       final int datalen = subidCount;
 685       final long[] data = new long[datalen];
 686       final byte b0 = bytes[next++] ;
 687 
 688       // bugId 4641746
 689       // The 8th bit of the first byte should always be set to 0
 690       if (b0 < 0) throw new BerException();
 691 
 692       // bugId 4641746
 693       // The first sub Id cannot be greater than 2
 694       final long lb0 =  b0 / 40 ;
 695       if (lb0 > 2) throw new BerException();
 696 
 697       final long lb1 = b0 % 40;
 698       data[0] = lb0 ;
 699       data[1] = lb1 ;
 700       int i = 2 ;
 701       while (i < datalen) {
 702         long subid = 0 ;
 703         byte b = bytes[next++] ;
 704         while ((b & 0x80) != 0) {
 705           subid = (subid << 7) | (b & 0x7f) ;
 706           // bugId 4654674
 707           if (subid < 0) throw new BerException();
 708           b = bytes[next++] ;
 709         }
 710         subid = (subid << 7) | b ;
 711         // bugId 4654674
 712         if (subid < 0) throw new BerException();
 713         data[i++] = subid ;
 714       }
 715       result = data;
 716     }
 717     catch(BerException e) {
 718       next = backup ;
 719       throw e ;
 720     }
 721     catch(IndexOutOfBoundsException e) {
 722       next = backup ;
 723       throw new BerException() ;
 724     }
 725     // catch(Error e) {
 726     //  debug("fetchOidValue: Error decoding BER: " + e);
 727     //  throw e;
 728     // }
 729 
 730     return result ;
 731   }
 732 
 733     // private static final void debug(String str) {
 734     //   System.out.println(str);
 735     // }
 736 
 737   //
 738   // This is the byte array containing the encoding.
 739   //
 740   private final byte bytes[];
 741 
 742   //
 743   // This is the current location. It is the next byte
 744   // to be decoded. It's an index in bytes[].
 745   //
 746   private int next = 0 ;
 747 
 748   //
 749   // This is the stack where end of sequences are kept.
 750   // A value is computed and pushed in it each time openSequence()
 751   // is invoked.
 752   // A value is pulled and checked each time closeSequence() is called.
 753   //
 754   private final int stackBuf[] = new int[200] ;
 755   private int stackTop = 0 ;
 756 
 757 }