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