001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 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-2024 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}