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