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