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