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