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