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