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