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