001    /*
002     * Copyright 2009-2014 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2014 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          return (value != 0);
548        }
549        else
550        {
551          skip(length);
552          throw new ASN1Exception(ERR_BOOLEAN_INVALID_LENGTH.get());
553        }
554      }
555    
556    
557    
558      /**
559       * Reads an ASN.1 enumerated element from the input stream and returns the
560       * value as an {@code Integer}.
561       *
562       * @return  The {@code Integer} value of the ASN.1 enumerated element read, or
563       *          {@code null} if the end of the input stream was reached before any
564       *          data could be read.  If {@code null} is returned, then the input
565       *          stream will have been closed.
566       *
567       * @throws  IOException  If a problem occurs while reading from the input
568       *                       stream, if the end of the input stream is reached in
569       *                       the middle of the element, or or if an attempt is
570       *                       made to read an element larger than the maximum
571       *                       allowed size.
572       *
573       * @throws  ASN1Exception  If the data read cannot be parsed as an ASN.1
574       *                         enumerated element.
575       */
576      public Integer readEnumerated()
577             throws IOException, ASN1Exception
578      {
579        return readInteger();
580      }
581    
582    
583    
584      /**
585       * Reads an ASN.1 integer element from the input stream and returns the value
586       * as an {@code Integer}.
587       *
588       * @return  The {@code Integer} value of the ASN.1 integer element read, or
589       *          {@code null} if the end of the input stream was reached before any
590       *          data could be read.  If {@code null} is returned, then the input
591       *          stream will have been closed.
592       *
593       * @throws  IOException  If a problem occurs while reading from the input
594       *                       stream, if the end of the input stream is reached in
595       *                       the middle of the element, or or if an attempt is
596       *                       made to read an element larger than the maximum
597       *                       allowed size.
598       *
599       * @throws  ASN1Exception  If the data read cannot be parsed as an ASN.1
600       *                         integer element.
601       */
602      public Integer readInteger()
603             throws IOException, ASN1Exception
604      {
605        final int type = readType();
606        if (type < 0)
607        {
608          return null;
609        }
610    
611        final int length = readLength();
612        if ((length == 0) || (length > 4))
613        {
614          skip(length);
615          throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(length));
616        }
617    
618        boolean negative = false;
619        int intValue = 0;
620        for (int i=0; i < length; i++)
621        {
622          final int byteRead = read(false);
623          if (byteRead < 0)
624          {
625            throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
626          }
627    
628          if (i == 0)
629          {
630            negative = ((byteRead & 0x80) != 0x00);
631          }
632    
633          intValue <<= 8;
634          intValue |= (byteRead & 0xFF);
635        }
636    
637        if (negative)
638        {
639          switch (length)
640          {
641            case 1:
642              intValue |= 0xFFFFFF00;
643              break;
644            case 2:
645              intValue |= 0xFFFF0000;
646              break;
647            case 3:
648              intValue |= 0xFF000000;
649              break;
650          }
651        }
652    
653        totalBytesRead += length;
654        return intValue;
655      }
656    
657    
658    
659      /**
660       * Reads an ASN.1 integer element from the input stream and returns the value
661       * as a {@code Long}.
662       *
663       * @return  The {@code Long} value of the ASN.1 integer element read, or
664       *          {@code null} if the end of the input stream was reached before any
665       *          data could be read.  If {@code null} is returned, then the input
666       *          stream will have been closed.
667       *
668       * @throws  IOException  If a problem occurs while reading from the input
669       *                       stream, if the end of the input stream is reached in
670       *                       the middle of the element, or or if an attempt is
671       *                       made to read an element larger than the maximum
672       *                       allowed size.
673       *
674       * @throws  ASN1Exception  If the data read cannot be parsed as an ASN.1
675       *                         integer element.
676       */
677      public Long readLong()
678             throws IOException, ASN1Exception
679      {
680        final int type = readType();
681        if (type < 0)
682        {
683          return null;
684        }
685    
686        final int length = readLength();
687        if ((length == 0) || (length > 8))
688        {
689          skip(length);
690          throw new ASN1Exception(ERR_LONG_INVALID_LENGTH.get(length));
691        }
692    
693        boolean negative = false;
694        long longValue = 0;
695        for (int i=0; i < length; i++)
696        {
697          final int byteRead = read(false);
698          if (byteRead < 0)
699          {
700            throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
701          }
702    
703          if (i == 0)
704          {
705            negative = ((byteRead & 0x80) != 0x00);
706          }
707    
708          longValue <<= 8;
709          longValue |= (byteRead & 0xFFL);
710        }
711    
712        if (negative)
713        {
714          switch (length)
715          {
716            case 1:
717              longValue |= 0xFFFFFFFFFFFFFF00L;
718              break;
719            case 2:
720              longValue |= 0xFFFFFFFFFFFF0000L;
721              break;
722            case 3:
723              longValue |= 0xFFFFFFFFFF000000L;
724              break;
725            case 4:
726              longValue |= 0xFFFFFFFF00000000L;
727              break;
728            case 5:
729              longValue |= 0xFFFFFF0000000000L;
730              break;
731            case 6:
732              longValue |= 0xFFFF000000000000L;
733              break;
734            case 7:
735              longValue |= 0xFF00000000000000L;
736              break;
737          }
738        }
739    
740        totalBytesRead += length;
741        return longValue;
742      }
743    
744    
745    
746      /**
747       * Reads an ASN.1 null element from the input stream.  No value will be
748       * returned but the null element will be consumed.
749       *
750       * @throws  IOException  If a problem occurs while reading from the input
751       *                       stream, if the end of the input stream is reached in
752       *                       the middle of the element, or or if an attempt is
753       *                       made to read an element larger than the maximum
754       *                       allowed size.
755       *
756       * @throws  ASN1Exception  If the data read cannot be parsed as an ASN.1 null
757       *                         element.
758       */
759      public void readNull()
760             throws IOException, ASN1Exception
761      {
762        final int type = readType();
763        if (type < 0)
764        {
765          return;
766        }
767    
768        final int length = readLength();
769    
770        if (length != 0)
771        {
772          skip(length);
773          throw new ASN1Exception(ERR_NULL_HAS_VALUE.get());
774        }
775      }
776    
777    
778    
779      /**
780       * Reads an ASN.1 octet string element from the input stream and returns the
781       * value as a byte array.
782       *
783       * @return  The byte array value of the ASN.1 octet string element read, or
784       *          {@code null} if the end of the input stream was reached before any
785       *          data could be read.  If {@code null} is returned, then the input
786       *          stream will have been closed.
787       *
788       * @throws  IOException  If a problem occurs while reading from the input
789       *                       stream, if the end of the input stream is reached in
790       *                       the middle of the element, or or if an attempt is
791       *                       made to read an element larger than the maximum
792       *                       allowed size.
793       */
794      public byte[] readBytes()
795             throws IOException
796      {
797        final int type = readType();
798        if (type < 0)
799        {
800          return null;
801        }
802    
803        final int length = readLength();
804    
805        int valueBytesRead = 0;
806        int bytesRemaining = length;
807        final byte[] value = new byte[length];
808        while (valueBytesRead < length)
809        {
810          final int bytesRead = read(value, valueBytesRead, bytesRemaining);
811          if (bytesRead < 0)
812          {
813            throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
814          }
815    
816          valueBytesRead += bytesRead;
817          bytesRemaining -= bytesRead;
818        }
819    
820        totalBytesRead += length;
821        return value;
822      }
823    
824    
825    
826      /**
827       * Reads an ASN.1 octet string element from the input stream and returns the
828       * value as a {@code String} using the UTF-8 encoding.
829       *
830       * @return  The {@code String} value of the ASN.1 octet string element read,
831       *          or {@code null} if the end of the input stream was reached before
832       *          any data could be read.  If {@code null} is returned, then the
833       *          input stream will have been closed.
834       *
835       * @throws  IOException  If a problem occurs while reading from the input
836       *                       stream, if the end of the input stream is reached in
837       *                       the middle of the element, or or if an attempt is
838       *                       made to read an element larger than the maximum
839       *                       allowed size.
840       */
841      public String readString()
842             throws IOException
843      {
844        final int type = readType();
845        if (type < 0)
846        {
847          return null;
848        }
849    
850        final int length = readLength();
851    
852        int valueBytesRead = 0;
853        int bytesRemaining = length;
854        final byte[] value = new byte[length];
855        while (valueBytesRead < length)
856        {
857          final int bytesRead = read(value, valueBytesRead, bytesRemaining);
858          if (bytesRead < 0)
859          {
860            throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
861          }
862    
863          valueBytesRead += bytesRead;
864          bytesRemaining -= bytesRead;
865        }
866    
867        totalBytesRead += length;
868        return toUTF8String(value);
869      }
870    
871    
872    
873      /**
874       * Reads the beginning of an ASN.1 sequence from the input stream and
875       * returns a value that can be used to determine when the end of the sequence
876       * has been reached.  Elements which are part of the sequence may be read from
877       * this ASN.1 stream reader until the
878       * {@code ASN1StreamReaderSequence#hasMoreElements} method returns
879       * {@code false}.
880       *
881       * @return  An object which may be used to determine when the end of the
882       *          sequence has been reached, or {@code null} if the end of the input
883       *          stream was reached before any data could be read.  If {@code null}
884       *          is returned, then the input stream will have been closed.
885       *
886       * @throws  IOException  If a problem occurs while reading from the input
887       *                       stream, if the end of the input stream is reached in
888       *                       the middle of the element, or or if an attempt is
889       *                       made to read an element larger than the maximum
890       *                       allowed size.
891       */
892      public ASN1StreamReaderSequence beginSequence()
893             throws IOException
894      {
895        final int type = readType();
896        if (type < 0)
897        {
898          return null;
899        }
900    
901        final int length = readLength();
902    
903        return new ASN1StreamReaderSequence(this, (byte) type, length);
904      }
905    
906    
907    
908      /**
909       * Reads the beginning of an ASN.1 set from the input stream and returns a
910       * value that can be used to determine when the end of the set has been
911       * reached.  Elements which are part of the set may be read from this ASN.1
912       * stream reader until the {@code ASN1StreamReaderSet#hasMoreElements} method
913       * returns {@code false}.
914       *
915       * @return  An object which may be used to determine when the end of the set
916       *          has been reached, or {@code null} if the end of the input stream
917       *          was reached before any data could be read.  If {@code null} is
918       *          returned, then the input stream will have been closed.
919       *
920       * @throws  IOException  If a problem occurs while reading from the input
921       *                       stream, if the end of the input stream is reached in
922       *                       the middle of the element, or or if an attempt is
923       *                       made to read an element larger than the maximum
924       *                       allowed size.
925       */
926      public ASN1StreamReaderSet beginSet()
927             throws IOException
928      {
929        final int type = readType();
930        if (type < 0)
931        {
932          return null;
933        }
934    
935        final int length = readLength();
936    
937        return new ASN1StreamReaderSet(this, (byte) type, length);
938      }
939    
940    
941    
942      /**
943       * Reads a byte of data from the underlying input stream, optionally ignoring
944       * socket timeout exceptions.
945       *
946       * @param  initial  Indicates whether this is the initial read for an element.
947       *
948       * @return  The byte read from the input stream, or -1 if the end of the
949       *          input stream was reached.
950       *
951       * @throws  IOException  If a problem occurs while reading data.
952       */
953      private int read(final boolean initial)
954              throws IOException
955      {
956        if (saslClient != null)
957        {
958          if (saslInputStream != null)
959          {
960            final int b = saslInputStream.read();
961            if (b >= 0)
962            {
963              return b;
964            }
965          }
966    
967          readAndDecodeSASLData(-1);
968          return saslInputStream.read();
969        }
970    
971        try
972        {
973          final int b = inputStream.read();
974          if ((saslClient == null) || (b < 0))
975          {
976            return b;
977          }
978          else
979          {
980            // This should only happen the first time after the SASL client has been
981            // installed.
982            readAndDecodeSASLData(b);
983            return saslInputStream.read();
984          }
985        }
986        catch (SocketTimeoutException ste)
987        {
988          debugException(Level.FINEST, ste);
989    
990          if ((initial && ignoreInitialSocketTimeout) ||
991              ((! initial) && ignoreSubsequentSocketTimeout))
992          {
993            while (true)
994            {
995              try
996              {
997                return inputStream.read();
998              }
999              catch (SocketTimeoutException ste2)
1000              {
1001                debugException(Level.FINEST, ste2);
1002              }
1003            }
1004          }
1005          else
1006          {
1007            throw ste;
1008          }
1009        }
1010      }
1011    
1012    
1013    
1014      /**
1015       * Reads data from the underlying input stream, optionally ignoring socket
1016       * timeout exceptions.
1017       *
1018       * @param  buffer   The buffer into which the data should be read.
1019       * @param  offset   The position at which to start placing the data that was
1020       *                  read.
1021       * @param  length   The maximum number of bytes to read.
1022       *
1023       * @return  The number of bytes read, or -1 if the end of the input stream
1024       *          was reached.
1025       *
1026       * @throws  IOException  If a problem occurs while reading data.
1027       */
1028      private int read(final byte[] buffer, final int offset, final int length)
1029              throws IOException
1030      {
1031        if (saslClient != null)
1032        {
1033          if (saslInputStream != null)
1034          {
1035            final int bytesRead = saslInputStream.read(buffer, offset, length);
1036            if (bytesRead > 0)
1037            {
1038              return bytesRead;
1039            }
1040          }
1041    
1042          readAndDecodeSASLData(-1);
1043          return saslInputStream.read(buffer, offset, length);
1044        }
1045    
1046        try
1047        {
1048          return inputStream.read(buffer, offset, length);
1049        }
1050        catch (SocketTimeoutException ste)
1051        {
1052          debugException(Level.FINEST, ste);
1053          if (ignoreSubsequentSocketTimeout)
1054          {
1055            while (true)
1056            {
1057              try
1058              {
1059                return inputStream.read(buffer, offset, length);
1060              }
1061              catch (SocketTimeoutException ste2)
1062              {
1063                debugException(Level.FINEST, ste2);
1064              }
1065            }
1066          }
1067          else
1068          {
1069            throw ste;
1070          }
1071        }
1072      }
1073    
1074    
1075    
1076      /**
1077       * Sets the SASL client to use to unwrap any data read over this ASN.1 stream
1078       * reader.
1079       *
1080       * @param  saslClient  The SASL client to use to unwrap any data read over
1081       *                     this ASN.1 stream reader.
1082       */
1083      void setSASLClient(final SaslClient saslClient)
1084      {
1085        this.saslClient = saslClient;
1086      }
1087    
1088    
1089    
1090      /**
1091       * Reads data from the underlying input stream, unwraps it using the
1092       * configured SASL client, and makes the result available in a byte array
1093       * input stream that will be used for subsequent reads.
1094       *
1095       * @param  firstByte  The first byte that has already been read.  This should
1096       *                    only be used if the value is greater than or equal to
1097       *                    zero.
1098       *
1099       * @throws  IOException  If a problem is encountered while reading from the
1100       *                       underlying input stream or  decoding the data that
1101       *                       has been read.
1102       */
1103      private void readAndDecodeSASLData(final int firstByte)
1104              throws IOException
1105      {
1106        // The first four bytes must be the number of bytes of data to unwrap.
1107        int numWrappedBytes = 0;
1108        int numLengthBytes = 4;
1109        if (firstByte >= 0)
1110        {
1111          numLengthBytes = 3;
1112          numWrappedBytes = firstByte;
1113        }
1114    
1115        for (int i=0; i < numLengthBytes; i++)
1116        {
1117          final int b = inputStream.read();
1118          if (b < 0)
1119          {
1120            if ((i == 0) && (firstByte < 0))
1121            {
1122              // This means that we hit the end of the input stream without
1123              // reading any data.  This is fine and just means that the end of
1124              // the input stream has been reached.
1125              saslInputStream = new ByteArrayInputStream(NO_BYTES);
1126            }
1127            else
1128            {
1129              // This means that we hit the end of the input stream after having
1130              // read a portion of the number of wrapped bytes.  This is an error.
1131              throw new IOException(
1132                   ERR_STREAM_READER_EOS_READING_SASL_LENGTH.get(i));
1133            }
1134          }
1135          else
1136          {
1137            numWrappedBytes = (numWrappedBytes << 8) | (b & 0xFF);
1138          }
1139        }
1140    
1141        if ((maxElementSize > 0) && (numWrappedBytes > maxElementSize))
1142        {
1143          throw new IOException(ERR_READ_SASL_LENGTH_EXCEEDS_MAX.get(
1144               numWrappedBytes, maxElementSize));
1145        }
1146    
1147        int wrappedDataPos = 0;
1148        final byte[] wrappedData = new byte[numWrappedBytes];
1149        while (true)
1150        {
1151          final int numBytesRead = inputStream.read(wrappedData, wrappedDataPos,
1152               (numWrappedBytes - wrappedDataPos));
1153          if (numBytesRead < 0)
1154          {
1155            throw new IOException(ERR_STREAM_READER_EOS_READING_SASL_DATA.get(
1156                 wrappedDataPos, numWrappedBytes));
1157          }
1158    
1159          wrappedDataPos += numBytesRead;
1160          if (wrappedDataPos >= numWrappedBytes)
1161          {
1162            break;
1163          }
1164        }
1165    
1166        final byte[] unwrappedData =
1167             saslClient.unwrap(wrappedData, 0, numWrappedBytes);
1168        saslInputStream = new ByteArrayInputStream(unwrappedData, 0,
1169             unwrappedData.length);
1170      }
1171    }