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