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