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