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