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