blob: 062a3e2d7707864ad9ffcef2af10b1457cdea045 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package 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
51public 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}