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