001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.asn1;
022
023
024
025 import java.io.BufferedInputStream;
026 import java.io.ByteArrayInputStream;
027 import java.io.InputStream;
028 import java.io.IOException;
029 import java.net.SocketTimeoutException;
030 import java.util.logging.Level;
031 import javax.security.sasl.SaslClient;
032
033
034 import static com.unboundid.asn1.ASN1Messages.*;
035 import static com.unboundid.util.Debug.*;
036 import static com.unboundid.util.StaticUtils.*;
037
038
039
040 /**
041 * This class provides a mechanism for ASN.1 elements (including sequences and
042 * sets) from an input stream in a manner that allows the data to be decoded on
043 * the fly without constructing {@code ASN1Element} objects if they are not
044 * needed. If any method in this class throws an {@code IOException}, then the
045 * caller must close this reader and must not attempt to use it any more.
046 * {@code ASN1StreamReader} instances are not threadsafe and must not be
047 * accessed concurrently by multiple threads.
048 */
049 public final class ASN1StreamReader
050 {
051 // Indicates whether socket timeout exceptions should be ignored for the
052 // initial read of an element.
053 private boolean ignoreInitialSocketTimeout;
054
055 // Indicates whether socket timeout exceptions should be ignored for
056 // subsequent reads of an element.
057 private boolean ignoreSubsequentSocketTimeout;
058
059 // The input stream that will be used for reading data after it has been
060 // unwrapped by SASL processing.
061 private volatile ByteArrayInputStream saslInputStream;
062
063 // The input stream from which data will be read.
064 private final InputStream inputStream;
065
066 // The maximum element size that will be allowed.
067 private final int maxElementSize;
068
069 // The total number of bytes read from the underlying input stream.
070 private long totalBytesRead;
071
072 // The SASL client that will be used to unwrap any data read over this
073 // stream reader.
074 private volatile SaslClient saslClient;
075
076
077
078 /**
079 * Creates a new ASN.1 stream reader that will read data from the provided
080 * input stream. It will use a maximum element size of
081 * {@code Integer.MAX_VALUE}.
082 *
083 * @param inputStream The input stream from which data should be read. If
084 * the provided input stream does not support the use of
085 * the {@code mark} and {@code reset} methods, then it
086 * will be wrapped with a {@code BufferedInputStream}.
087 */
088 public ASN1StreamReader(final InputStream inputStream)
089 {
090 this(inputStream, Integer.MAX_VALUE);
091 }
092
093
094
095 /**
096 * Creates a new ASN.1 stream reader that will read data from the provided
097 * input stream. It will use a maximum element size of
098 * {@code Integer.MAX_VALUE}.
099 *
100 * @param inputStream The input stream from which data should be read.
101 * If the provided input stream does not support the
102 * use of the {@code mark} and {@code reset} methods,
103 * then it will be wrapped with a
104 * {@code BufferedInputStream}.
105 * @param maxElementSize The maximum size in bytes of an ASN.1 element that
106 * may be read. A value less than or equal to zero
107 * will be interpreted as {@code Integer.MAX_VALUE}.
108 */
109 public ASN1StreamReader(final InputStream inputStream,
110 final int maxElementSize)
111 {
112 if (inputStream.markSupported())
113 {
114 this.inputStream = inputStream;
115 }
116 else
117 {
118 this.inputStream = new BufferedInputStream(inputStream);
119 }
120
121 if (maxElementSize > 0)
122 {
123 this.maxElementSize = maxElementSize;
124 }
125 else
126 {
127 this.maxElementSize = Integer.MAX_VALUE;
128 }
129
130 totalBytesRead = 0L;
131 ignoreInitialSocketTimeout = false;
132 ignoreSubsequentSocketTimeout = false;
133 saslClient = null;
134 saslInputStream = null;
135 }
136
137
138
139 /**
140 * Closes this ASN.1 stream reader and the underlying input stream. This
141 * reader must not be used after it has been closed.
142 *
143 * @throws IOException If a problem occurs while closing the underlying
144 * input stream.
145 */
146 public void close()
147 throws IOException
148 {
149 inputStream.close();
150 }
151
152
153
154 /**
155 * Retrieves the total number of bytes read so far from the underlying input
156 * stream.
157 *
158 * @return The total number of bytes read so far from the underlying input
159 * stream.
160 */
161 long getTotalBytesRead()
162 {
163 return totalBytesRead;
164 }
165
166
167
168 /**
169 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
170 * exceptions that may be caught during processing.
171 *
172 * @return {@code true} if {@code SocketTimeoutException} exceptions should
173 * be ignored, or {@code false} if they should not be ignored and
174 * should be propagated to the caller.
175 *
176 * @deprecated Use the {@code ignoreInitialSocketTimeoutException()} and
177 * {@code ignoreSubsequentSocketTimeoutException()} methods
178 * instead.
179 */
180 @Deprecated()
181 public boolean ignoreSocketTimeoutException()
182 {
183 return ignoreInitialSocketTimeout;
184 }
185
186
187
188 /**
189 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
190 * exceptions that may be caught while trying to read the first byte of an
191 * element.
192 *
193 * @return {@code true} if {@code SocketTimeoutException} exceptions should
194 * be ignored while trying to read the first byte of an element, or
195 * {@code false} if they should not be ignored and should be
196 * propagated to the caller.
197 */
198 public boolean ignoreInitialSocketTimeoutException()
199 {
200 return ignoreInitialSocketTimeout;
201 }
202
203
204
205 /**
206 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
207 * exceptions that may be caught while trying to read subsequent bytes of an
208 * element (after one or more bytes have already been read for that element).
209 *
210 * @return {@code true} if {@code SocketTimeoutException} exceptions should
211 * be ignored while trying to read subsequent bytes of an element, or
212 * {@code false} if they should not be ignored and should be
213 * propagated to the caller.
214 */
215 public boolean ignoreSubsequentSocketTimeoutException()
216 {
217 return ignoreSubsequentSocketTimeout;
218 }
219
220
221
222 /**
223 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
224 * exceptions that may be caught during processing.
225 *
226 * @param ignoreSocketTimeout Indicates whether to ignore
227 * {@code SocketTimeoutException} exceptions that
228 * may be caught during processing.
229 *
230 * @deprecated Use the {@code setIgnoreSocketTimeout(boolean,boolean)}
231 * method instead.
232 */
233 @Deprecated()
234 public void setIgnoreSocketTimeout(final boolean ignoreSocketTimeout)
235 {
236 ignoreInitialSocketTimeout = ignoreSocketTimeout;
237 ignoreSubsequentSocketTimeout = ignoreSocketTimeout;
238 }
239
240
241
242 /**
243 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
244 * exceptions that may be caught during processing.
245 *
246 * @param ignoreInitialSocketTimeout Indicates whether to ignore
247 * {@code SocketTimeoutException}
248 * exceptions that may be caught while
249 * trying to read the first byte of an
250 * element.
251 * @param ignoreSubsequentSocketTimeout Indicates whether to ignore
252 * {@code SocketTimeoutException}
253 * exceptions that may be caught while
254 * reading beyond the first byte of an
255 * element.
256 */
257 public void setIgnoreSocketTimeout(final boolean ignoreInitialSocketTimeout,
258 final boolean ignoreSubsequentSocketTimeout)
259 {
260 this.ignoreInitialSocketTimeout = ignoreInitialSocketTimeout;
261 this.ignoreSubsequentSocketTimeout = ignoreSubsequentSocketTimeout;
262 }
263
264
265
266 /**
267 * Peeks at the next byte to be read from the input stream without actually
268 * consuming it.
269 *
270 * @return An integer value encapsulating the BER type of the next element in
271 * the input stream, or -1 if the end of the input stream has been
272 * reached and there is no data to be read. If a value of -1 is
273 * returned, then the input stream will not have been closed since
274 * this method is not intended to have any impact on the underlying
275 * input stream.
276 *
277 * @throws IOException If a problem occurs while reading from the input
278 * stream.
279 */
280 public int peek()
281 throws IOException
282 {
283 final InputStream is;
284 if (saslClient == null)
285 {
286 is = inputStream;
287 }
288 else
289 {
290 if (saslInputStream == null)
291 {
292 readAndDecodeSASLData(-1);
293 }
294
295 is = saslInputStream;
296 }
297
298 is.mark(1);
299 final int byteRead = read(true);
300 is.reset();
301
302 return byteRead;
303 }
304
305
306
307 /**
308 * Reads the BER type of the next element from the input stream. This may not
309 * be called if a previous element has been started but not yet completed.
310 *
311 * @return An integer value encapsulating the BER type of the next element in
312 * the input stream, or -1 if the end of the input stream has been
313 * reached and there is no data to be read. If a value of -1 is
314 * returned, then the input stream will have been closed.
315 *
316 * @throws IOException If a problem occurs while reading from the input
317 * stream.
318 */
319 private int readType()
320 throws IOException
321 {
322 final int typeInt = read(true);
323 if (typeInt < 0)
324 {
325 close();
326 }
327 else
328 {
329 totalBytesRead++;
330 }
331 return typeInt;
332 }
333
334
335
336 /**
337 * Reads the length of the next element from the input stream. This may only
338 * be called after reading the BER type.
339 *
340 * @return The length of the next element from the input stream.
341 *
342 * @throws IOException If a problem occurs while reading from the input
343 * stream, if the end of the stream has been reached, or
344 * if the decoded length is greater than the maximum
345 * allowed length.
346 */
347 private int readLength()
348 throws IOException
349 {
350 int length = read(false);
351 if (length < 0)
352 {
353 throw new IOException(ERR_READ_END_BEFORE_FIRST_LENGTH.get());
354 }
355
356 totalBytesRead++;
357 if (length > 127)
358 {
359 final int numLengthBytes = length & 0x7F;
360 length = 0;
361 if ((numLengthBytes < 1) || (numLengthBytes > 4))
362 {
363 throw new IOException(ERR_READ_LENGTH_TOO_LONG.get(numLengthBytes));
364 }
365
366 for (int i=0; i < numLengthBytes; i++)
367 {
368 final int lengthInt = read(false);
369 if (lengthInt < 0)
370 {
371 throw new IOException(ERR_READ_END_BEFORE_LENGTH_END.get());
372 }
373
374 length <<= 8;
375 length |= (lengthInt & 0xFF);
376 }
377
378 totalBytesRead += numLengthBytes;
379 }
380
381 if ((length < 0) || ((maxElementSize > 0) && (length > maxElementSize)))
382 {
383 throw new IOException(ERR_READ_LENGTH_EXCEEDS_MAX.get(length,
384 maxElementSize));
385 }
386
387 return length;
388 }
389
390
391
392 /**
393 * Skips over the specified number of bytes.
394 *
395 * @param numBytes The number of bytes to skip.
396 *
397 * @throws IOException If a problem occurs while reading from the input
398 * stream, or if the end of the stream is reached before
399 * having skipped the specified number of bytes.
400 */
401 private void skip(final int numBytes)
402 throws IOException
403 {
404 if (numBytes <= 0)
405 {
406 return;
407 }
408
409 if (saslClient != null)
410 {
411 int skippedSoFar = 0;
412 final byte[] skipBuffer = new byte[numBytes];
413 while (true)
414 {
415 final int bytesRead = read(skipBuffer, skippedSoFar,
416 (numBytes - skippedSoFar));
417 if (bytesRead < 0)
418 {
419 // We unexpectedly hit the end of the stream. We'll just return since
420 // we clearly can't skip any more, and subsequent read attempts will
421 // fail.
422 return;
423 }
424
425 skippedSoFar += bytesRead;
426 totalBytesRead += bytesRead;
427 if (skippedSoFar >= numBytes)
428 {
429 return;
430 }
431 }
432 }
433
434 long totalBytesSkipped = inputStream.skip(numBytes);
435 while (totalBytesSkipped < numBytes)
436 {
437 final long bytesSkipped = inputStream.skip(numBytes - totalBytesSkipped);
438 if (bytesSkipped <= 0)
439 {
440 while (totalBytesSkipped < numBytes)
441 {
442 final int byteRead = read(false);
443 if (byteRead < 0)
444 {
445 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
446 }
447 totalBytesSkipped++;
448 }
449 }
450 else
451 {
452 totalBytesSkipped += bytesSkipped;
453 }
454 }
455
456 totalBytesRead += numBytes;
457 }
458
459
460
461 /**
462 * Reads a complete ASN.1 element from the input stream.
463 *
464 * @return The ASN.1 element read from the input stream, or {@code null} if
465 * the end of the input stream was reached before any data could be
466 * read. If {@code null} is returned, then the input stream will
467 * have been closed.
468 *
469 * @throws IOException If a problem occurs while reading from the input
470 * stream, if the end of the input stream is reached in
471 * the middle of the element, or or if an attempt is
472 * made to read an element larger than the maximum
473 * allowed size.
474 */
475 public ASN1Element readElement()
476 throws IOException
477 {
478 final int type = readType();
479 if (type < 0)
480 {
481 return null;
482 }
483
484 final int length = readLength();
485
486 int valueBytesRead = 0;
487 int bytesRemaining = length;
488 final byte[] value = new byte[length];
489 while (valueBytesRead < length)
490 {
491 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
492 if (bytesRead < 0)
493 {
494 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
495 }
496
497 valueBytesRead += bytesRead;
498 bytesRemaining -= bytesRead;
499 }
500
501 totalBytesRead += length;
502 final ASN1Element e = new ASN1Element((byte) type, value);
503 debugASN1Read(e);
504 return e;
505 }
506
507
508
509 /**
510 * Reads an ASN.1 Boolean element from the input stream and returns the value
511 * as a {@code Boolean}.
512 *
513 * @return The {@code Boolean} value of the ASN.1 Boolean element read, or
514 * {@code null} if the end of the input stream was reached before any
515 * data could be read. If {@code null} is returned, then the input
516 * stream will have been closed.
517 *
518 * @throws IOException If a problem occurs while reading from the input
519 * stream, if the end of the input stream is reached in
520 * the middle of the element, or or if an attempt is
521 * made to read an element larger than the maximum
522 * allowed size.
523 *
524 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
525 * Boolean element.
526 */
527 public Boolean readBoolean()
528 throws IOException, ASN1Exception
529 {
530 final int type = readType();
531 if (type < 0)
532 {
533 return null;
534 }
535
536 final int length = readLength();
537
538 if (length == 1)
539 {
540 final int value = read(false);
541 if (value < 0)
542 {
543 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
544 }
545
546 totalBytesRead++;
547
548 final Boolean booleanValue = (value != 0x00);
549 debugASN1Read(Level.INFO, "Boolean", type, 1, booleanValue);
550 return booleanValue;
551 }
552 else
553 {
554 skip(length);
555 throw new ASN1Exception(ERR_BOOLEAN_INVALID_LENGTH.get());
556 }
557 }
558
559
560
561 /**
562 * Reads an ASN.1 enumerated element from the input stream and returns the
563 * value as an {@code Integer}.
564 *
565 * @return The {@code Integer} value of the ASN.1 enumerated element read, or
566 * {@code null} if the end of the input stream was reached before any
567 * data could be read. If {@code null} is returned, then the input
568 * stream will have been closed.
569 *
570 * @throws IOException If a problem occurs while reading from the input
571 * stream, if the end of the input stream is reached in
572 * the middle of the element, or or if an attempt is
573 * made to read an element larger than the maximum
574 * allowed size.
575 *
576 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
577 * enumerated element.
578 */
579 public Integer readEnumerated()
580 throws IOException, ASN1Exception
581 {
582 return readInteger();
583 }
584
585
586
587 /**
588 * Reads an ASN.1 integer element from the input stream and returns the value
589 * as an {@code Integer}.
590 *
591 * @return The {@code Integer} value of the ASN.1 integer element read, or
592 * {@code null} if the end of the input stream was reached before any
593 * data could be read. If {@code null} is returned, then the input
594 * stream will have been closed.
595 *
596 * @throws IOException If a problem occurs while reading from the input
597 * stream, if the end of the input stream is reached in
598 * the middle of the element, or or if an attempt is
599 * made to read an element larger than the maximum
600 * allowed size.
601 *
602 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
603 * integer element.
604 */
605 public Integer readInteger()
606 throws IOException, ASN1Exception
607 {
608 final int type = readType();
609 if (type < 0)
610 {
611 return null;
612 }
613
614 final int length = readLength();
615 if ((length == 0) || (length > 4))
616 {
617 skip(length);
618 throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(length));
619 }
620
621 boolean negative = false;
622 int intValue = 0;
623 for (int i=0; i < length; i++)
624 {
625 final int byteRead = read(false);
626 if (byteRead < 0)
627 {
628 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
629 }
630
631 if (i == 0)
632 {
633 negative = ((byteRead & 0x80) != 0x00);
634 }
635
636 intValue <<= 8;
637 intValue |= (byteRead & 0xFF);
638 }
639
640 if (negative)
641 {
642 switch (length)
643 {
644 case 1:
645 intValue |= 0xFFFFFF00;
646 break;
647 case 2:
648 intValue |= 0xFFFF0000;
649 break;
650 case 3:
651 intValue |= 0xFF000000;
652 break;
653 }
654 }
655
656 totalBytesRead += length;
657 debugASN1Read(Level.INFO, "Integer", type, length, intValue);
658 return intValue;
659 }
660
661
662
663 /**
664 * Reads an ASN.1 integer element from the input stream and returns the value
665 * as a {@code Long}.
666 *
667 * @return The {@code Long} value of the ASN.1 integer element read, or
668 * {@code null} if the end of the input stream was reached before any
669 * data could be read. If {@code null} is returned, then the input
670 * stream will have been closed.
671 *
672 * @throws IOException If a problem occurs while reading from the input
673 * stream, if the end of the input stream is reached in
674 * the middle of the element, or or if an attempt is
675 * made to read an element larger than the maximum
676 * allowed size.
677 *
678 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
679 * integer element.
680 */
681 public Long readLong()
682 throws IOException, ASN1Exception
683 {
684 final int type = readType();
685 if (type < 0)
686 {
687 return null;
688 }
689
690 final int length = readLength();
691 if ((length == 0) || (length > 8))
692 {
693 skip(length);
694 throw new ASN1Exception(ERR_LONG_INVALID_LENGTH.get(length));
695 }
696
697 boolean negative = false;
698 long longValue = 0;
699 for (int i=0; i < length; i++)
700 {
701 final int byteRead = read(false);
702 if (byteRead < 0)
703 {
704 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
705 }
706
707 if (i == 0)
708 {
709 negative = ((byteRead & 0x80) != 0x00);
710 }
711
712 longValue <<= 8;
713 longValue |= (byteRead & 0xFFL);
714 }
715
716 if (negative)
717 {
718 switch (length)
719 {
720 case 1:
721 longValue |= 0xFFFFFFFFFFFFFF00L;
722 break;
723 case 2:
724 longValue |= 0xFFFFFFFFFFFF0000L;
725 break;
726 case 3:
727 longValue |= 0xFFFFFFFFFF000000L;
728 break;
729 case 4:
730 longValue |= 0xFFFFFFFF00000000L;
731 break;
732 case 5:
733 longValue |= 0xFFFFFF0000000000L;
734 break;
735 case 6:
736 longValue |= 0xFFFF000000000000L;
737 break;
738 case 7:
739 longValue |= 0xFF00000000000000L;
740 break;
741 }
742 }
743
744 totalBytesRead += length;
745 debugASN1Read(Level.INFO, "Long", type, length, longValue);
746 return longValue;
747 }
748
749
750
751 /**
752 * Reads an ASN.1 null element from the input stream. No value will be
753 * returned but the null element will be consumed.
754 *
755 * @throws IOException If a problem occurs while reading from the input
756 * stream, if the end of the input stream is reached in
757 * the middle of the element, or or if an attempt is
758 * made to read an element larger than the maximum
759 * allowed size.
760 *
761 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 null
762 * element.
763 */
764 public void readNull()
765 throws IOException, ASN1Exception
766 {
767 final int type = readType();
768 if (type < 0)
769 {
770 return;
771 }
772
773 final int length = readLength();
774
775 if (length != 0)
776 {
777 skip(length);
778 throw new ASN1Exception(ERR_NULL_HAS_VALUE.get());
779 }
780 debugASN1Read(Level.INFO, "Null", type, 0, null);
781 }
782
783
784
785 /**
786 * Reads an ASN.1 octet string element from the input stream and returns the
787 * value as a byte array.
788 *
789 * @return The byte array value of the ASN.1 octet string element read, or
790 * {@code null} if the end of the input stream was reached before any
791 * data could be read. If {@code null} is returned, then the input
792 * stream will have been closed.
793 *
794 * @throws IOException If a problem occurs while reading from the input
795 * stream, if the end of the input stream is reached in
796 * the middle of the element, or or if an attempt is
797 * made to read an element larger than the maximum
798 * allowed size.
799 */
800 public byte[] readBytes()
801 throws IOException
802 {
803 final int type = readType();
804 if (type < 0)
805 {
806 return null;
807 }
808
809 final int length = readLength();
810
811 int valueBytesRead = 0;
812 int bytesRemaining = length;
813 final byte[] value = new byte[length];
814 while (valueBytesRead < length)
815 {
816 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
817 if (bytesRead < 0)
818 {
819 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
820 }
821
822 valueBytesRead += bytesRead;
823 bytesRemaining -= bytesRead;
824 }
825
826 totalBytesRead += length;
827 debugASN1Read(Level.INFO, "byte[]", type, length, value);
828 return value;
829 }
830
831
832
833 /**
834 * Reads an ASN.1 octet string element from the input stream and returns the
835 * value as a {@code String} using the UTF-8 encoding.
836 *
837 * @return The {@code String} value of the ASN.1 octet string element read,
838 * or {@code null} if the end of the input stream was reached before
839 * any data could be read. If {@code null} is returned, then the
840 * input stream will have been closed.
841 *
842 * @throws IOException If a problem occurs while reading from the input
843 * stream, if the end of the input stream is reached in
844 * the middle of the element, or or if an attempt is
845 * made to read an element larger than the maximum
846 * allowed size.
847 */
848 public String readString()
849 throws IOException
850 {
851 final int type = readType();
852 if (type < 0)
853 {
854 return null;
855 }
856
857 final int length = readLength();
858
859 int valueBytesRead = 0;
860 int bytesRemaining = length;
861 final byte[] value = new byte[length];
862 while (valueBytesRead < length)
863 {
864 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
865 if (bytesRead < 0)
866 {
867 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
868 }
869
870 valueBytesRead += bytesRead;
871 bytesRemaining -= bytesRead;
872 }
873
874 totalBytesRead += length;
875
876 final String s = toUTF8String(value);
877 debugASN1Read(Level.INFO, "String", type, length, s);
878 return s;
879 }
880
881
882
883 /**
884 * Reads the beginning of an ASN.1 sequence from the input stream and
885 * returns a value that can be used to determine when the end of the sequence
886 * has been reached. Elements which are part of the sequence may be read from
887 * this ASN.1 stream reader until the
888 * {@code ASN1StreamReaderSequence#hasMoreElements} method returns
889 * {@code false}.
890 *
891 * @return An object which may be used to determine when the end of the
892 * sequence has been reached, or {@code null} if the end of the input
893 * stream was reached before any data could be read. If {@code null}
894 * is returned, then the input stream will have been closed.
895 *
896 * @throws IOException If a problem occurs while reading from the input
897 * stream, if the end of the input stream is reached in
898 * the middle of the element, or or if an attempt is
899 * made to read an element larger than the maximum
900 * allowed size.
901 */
902 public ASN1StreamReaderSequence beginSequence()
903 throws IOException
904 {
905 final int type = readType();
906 if (type < 0)
907 {
908 return null;
909 }
910
911 final int length = readLength();
912
913 debugASN1Read(Level.INFO, "Sequence Header", type, length, null);
914 return new ASN1StreamReaderSequence(this, (byte) type, length);
915 }
916
917
918
919 /**
920 * Reads the beginning of an ASN.1 set from the input stream and returns a
921 * value that can be used to determine when the end of the set has been
922 * reached. Elements which are part of the set may be read from this ASN.1
923 * stream reader until the {@code ASN1StreamReaderSet#hasMoreElements} method
924 * returns {@code false}.
925 *
926 * @return An object which may be used to determine when the end of the set
927 * has been reached, or {@code null} if the end of the input stream
928 * was reached before any data could be read. If {@code null} is
929 * returned, then the input stream will have been closed.
930 *
931 * @throws IOException If a problem occurs while reading from the input
932 * stream, if the end of the input stream is reached in
933 * the middle of the element, or or if an attempt is
934 * made to read an element larger than the maximum
935 * allowed size.
936 */
937 public ASN1StreamReaderSet beginSet()
938 throws IOException
939 {
940 final int type = readType();
941 if (type < 0)
942 {
943 return null;
944 }
945
946 final int length = readLength();
947
948 debugASN1Read(Level.INFO, "Set Header", type, length, null);
949 return new ASN1StreamReaderSet(this, (byte) type, length);
950 }
951
952
953
954 /**
955 * Reads a byte of data from the underlying input stream, optionally ignoring
956 * socket timeout exceptions.
957 *
958 * @param initial Indicates whether this is the initial read for an element.
959 *
960 * @return The byte read from the input stream, or -1 if the end of the
961 * input stream was reached.
962 *
963 * @throws IOException If a problem occurs while reading data.
964 */
965 private int read(final boolean initial)
966 throws IOException
967 {
968 if (saslClient != null)
969 {
970 if (saslInputStream != null)
971 {
972 final int b = saslInputStream.read();
973 if (b >= 0)
974 {
975 return b;
976 }
977 }
978
979 readAndDecodeSASLData(-1);
980 return saslInputStream.read();
981 }
982
983 try
984 {
985 final int b = inputStream.read();
986 if ((saslClient == null) || (b < 0))
987 {
988 return b;
989 }
990 else
991 {
992 // This should only happen the first time after the SASL client has been
993 // installed.
994 readAndDecodeSASLData(b);
995 return saslInputStream.read();
996 }
997 }
998 catch (SocketTimeoutException ste)
999 {
1000 debugException(Level.FINEST, ste);
1001
1002 if ((initial && ignoreInitialSocketTimeout) ||
1003 ((! initial) && ignoreSubsequentSocketTimeout))
1004 {
1005 while (true)
1006 {
1007 try
1008 {
1009 return inputStream.read();
1010 }
1011 catch (SocketTimeoutException ste2)
1012 {
1013 debugException(Level.FINEST, ste2);
1014 }
1015 }
1016 }
1017 else
1018 {
1019 throw ste;
1020 }
1021 }
1022 }
1023
1024
1025
1026 /**
1027 * Reads data from the underlying input stream, optionally ignoring socket
1028 * timeout exceptions.
1029 *
1030 * @param buffer The buffer into which the data should be read.
1031 * @param offset The position at which to start placing the data that was
1032 * read.
1033 * @param length The maximum number of bytes to read.
1034 *
1035 * @return The number of bytes read, or -1 if the end of the input stream
1036 * was reached.
1037 *
1038 * @throws IOException If a problem occurs while reading data.
1039 */
1040 private int read(final byte[] buffer, final int offset, final int length)
1041 throws IOException
1042 {
1043 if (saslClient != null)
1044 {
1045 if (saslInputStream != null)
1046 {
1047 final int bytesRead = saslInputStream.read(buffer, offset, length);
1048 if (bytesRead > 0)
1049 {
1050 return bytesRead;
1051 }
1052 }
1053
1054 readAndDecodeSASLData(-1);
1055 return saslInputStream.read(buffer, offset, length);
1056 }
1057
1058 try
1059 {
1060 return inputStream.read(buffer, offset, length);
1061 }
1062 catch (SocketTimeoutException ste)
1063 {
1064 debugException(Level.FINEST, ste);
1065 if (ignoreSubsequentSocketTimeout)
1066 {
1067 while (true)
1068 {
1069 try
1070 {
1071 return inputStream.read(buffer, offset, length);
1072 }
1073 catch (SocketTimeoutException ste2)
1074 {
1075 debugException(Level.FINEST, ste2);
1076 }
1077 }
1078 }
1079 else
1080 {
1081 throw ste;
1082 }
1083 }
1084 }
1085
1086
1087
1088 /**
1089 * Sets the SASL client to use to unwrap any data read over this ASN.1 stream
1090 * reader.
1091 *
1092 * @param saslClient The SASL client to use to unwrap any data read over
1093 * this ASN.1 stream reader.
1094 */
1095 void setSASLClient(final SaslClient saslClient)
1096 {
1097 this.saslClient = saslClient;
1098 }
1099
1100
1101
1102 /**
1103 * Reads data from the underlying input stream, unwraps it using the
1104 * configured SASL client, and makes the result available in a byte array
1105 * input stream that will be used for subsequent reads.
1106 *
1107 * @param firstByte The first byte that has already been read. This should
1108 * only be used if the value is greater than or equal to
1109 * zero.
1110 *
1111 * @throws IOException If a problem is encountered while reading from the
1112 * underlying input stream or decoding the data that
1113 * has been read.
1114 */
1115 private void readAndDecodeSASLData(final int firstByte)
1116 throws IOException
1117 {
1118 // The first four bytes must be the number of bytes of data to unwrap.
1119 int numWrappedBytes = 0;
1120 int numLengthBytes = 4;
1121 if (firstByte >= 0)
1122 {
1123 numLengthBytes = 3;
1124 numWrappedBytes = firstByte;
1125 }
1126
1127 for (int i=0; i < numLengthBytes; i++)
1128 {
1129 final int b = inputStream.read();
1130 if (b < 0)
1131 {
1132 if ((i == 0) && (firstByte < 0))
1133 {
1134 // This means that we hit the end of the input stream without
1135 // reading any data. This is fine and just means that the end of
1136 // the input stream has been reached.
1137 saslInputStream = new ByteArrayInputStream(NO_BYTES);
1138 }
1139 else
1140 {
1141 // This means that we hit the end of the input stream after having
1142 // read a portion of the number of wrapped bytes. This is an error.
1143 throw new IOException(
1144 ERR_STREAM_READER_EOS_READING_SASL_LENGTH.get(i));
1145 }
1146 }
1147 else
1148 {
1149 numWrappedBytes = (numWrappedBytes << 8) | (b & 0xFF);
1150 }
1151 }
1152
1153 if ((maxElementSize > 0) && (numWrappedBytes > maxElementSize))
1154 {
1155 throw new IOException(ERR_READ_SASL_LENGTH_EXCEEDS_MAX.get(
1156 numWrappedBytes, maxElementSize));
1157 }
1158
1159 int wrappedDataPos = 0;
1160 final byte[] wrappedData = new byte[numWrappedBytes];
1161 while (true)
1162 {
1163 final int numBytesRead = inputStream.read(wrappedData, wrappedDataPos,
1164 (numWrappedBytes - wrappedDataPos));
1165 if (numBytesRead < 0)
1166 {
1167 throw new IOException(ERR_STREAM_READER_EOS_READING_SASL_DATA.get(
1168 wrappedDataPos, numWrappedBytes));
1169 }
1170
1171 wrappedDataPos += numBytesRead;
1172 if (wrappedDataPos >= numWrappedBytes)
1173 {
1174 break;
1175 }
1176 }
1177
1178 final byte[] unwrappedData =
1179 saslClient.unwrap(wrappedData, 0, numWrappedBytes);
1180 saslInputStream = new ByteArrayInputStream(unwrappedData, 0,
1181 unwrappedData.length);
1182 }
1183 }