001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.protocol; 037 038 039 040import java.io.InterruptedIOException; 041import java.io.IOException; 042import java.io.Serializable; 043import java.net.SocketTimeoutException; 044import java.util.ArrayList; 045import java.util.Arrays; 046import java.util.Collections; 047import java.util.Iterator; 048import java.util.List; 049 050import com.unboundid.asn1.ASN1Buffer; 051import com.unboundid.asn1.ASN1BufferSequence; 052import com.unboundid.asn1.ASN1Element; 053import com.unboundid.asn1.ASN1Integer; 054import com.unboundid.asn1.ASN1Sequence; 055import com.unboundid.asn1.ASN1StreamReader; 056import com.unboundid.asn1.ASN1StreamReaderSequence; 057import com.unboundid.ldap.sdk.Control; 058import com.unboundid.ldap.sdk.InternalSDKHelper; 059import com.unboundid.ldap.sdk.LDAPException; 060import com.unboundid.ldap.sdk.ResultCode; 061import com.unboundid.ldap.sdk.schema.Schema; 062import com.unboundid.util.Debug; 063import com.unboundid.util.InternalUseOnly; 064import com.unboundid.util.NotMutable; 065import com.unboundid.util.NotNull; 066import com.unboundid.util.Nullable; 067import com.unboundid.util.StaticUtils; 068import com.unboundid.util.ThreadSafety; 069import com.unboundid.util.ThreadSafetyLevel; 070 071import static com.unboundid.ldap.protocol.ProtocolMessages.*; 072 073 074 075/** 076 * This class provides a data structure that may be used to represent LDAP 077 * protocol messages. Each LDAP message contains a message ID, a protocol op, 078 * and an optional set of controls. 079 */ 080@InternalUseOnly() 081@NotMutable() 082@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 083public final class LDAPMessage 084 implements Serializable 085{ 086 /** 087 * The BER type to use for the bind request protocol op. 088 */ 089 public static final byte PROTOCOL_OP_TYPE_BIND_REQUEST = 0x60; 090 091 092 093 /** 094 * The BER type to use for the bind response protocol op. 095 */ 096 public static final byte PROTOCOL_OP_TYPE_BIND_RESPONSE = 0x61; 097 098 099 100 /** 101 * The BER type to use for the unbind request protocol op. 102 */ 103 public static final byte PROTOCOL_OP_TYPE_UNBIND_REQUEST = 0x42; 104 105 106 107 /** 108 * The BER type to use for the search request protocol op. 109 */ 110 public static final byte PROTOCOL_OP_TYPE_SEARCH_REQUEST = 0x63; 111 112 113 114 /** 115 * The BER type to use for the search result entry protocol op. 116 */ 117 public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY = 0x64; 118 119 120 121 /** 122 * The BER type to use for the search result reference protocol op. 123 */ 124 public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73; 125 126 127 128 /** 129 * The BER type to use for the search result done protocol op. 130 */ 131 public static final byte PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE = 0x65; 132 133 134 135 /** 136 * The BER type to use for the modify request protocol op. 137 */ 138 public static final byte PROTOCOL_OP_TYPE_MODIFY_REQUEST = 0x66; 139 140 141 142 /** 143 * The BER type to use for the modify response protocol op. 144 */ 145 public static final byte PROTOCOL_OP_TYPE_MODIFY_RESPONSE = 0x67; 146 147 148 149 /** 150 * The BER type to use for the add request protocol op. 151 */ 152 public static final byte PROTOCOL_OP_TYPE_ADD_REQUEST = 0x68; 153 154 155 156 /** 157 * The BER type to use for the add response protocol op. 158 */ 159 public static final byte PROTOCOL_OP_TYPE_ADD_RESPONSE = 0x69; 160 161 162 163 /** 164 * The BER type to use for the delete request protocol op. 165 */ 166 public static final byte PROTOCOL_OP_TYPE_DELETE_REQUEST = 0x4A; 167 168 169 170 /** 171 * The BER type to use for the delete response protocol op. 172 */ 173 public static final byte PROTOCOL_OP_TYPE_DELETE_RESPONSE = 0x6B; 174 175 176 177 /** 178 * The BER type to use for the modify DN request protocol op. 179 */ 180 public static final byte PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST = 0x6C; 181 182 183 184 /** 185 * The BER type to use for the modify DN response protocol op. 186 */ 187 public static final byte PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE = 0x6D; 188 189 190 191 /** 192 * The BER type to use for the compare request protocol op. 193 */ 194 public static final byte PROTOCOL_OP_TYPE_COMPARE_REQUEST = 0x6E; 195 196 197 198 /** 199 * The BER type to use for the compare response protocol op. 200 */ 201 public static final byte PROTOCOL_OP_TYPE_COMPARE_RESPONSE = 0x6F; 202 203 204 205 /** 206 * The BER type to use for the abandon request protocol op. 207 */ 208 public static final byte PROTOCOL_OP_TYPE_ABANDON_REQUEST = 0x50; 209 210 211 212 /** 213 * The BER type to use for the extended request protocol op. 214 */ 215 public static final byte PROTOCOL_OP_TYPE_EXTENDED_REQUEST = 0x77; 216 217 218 219 /** 220 * The BER type to use for the extended response protocol op. 221 */ 222 public static final byte PROTOCOL_OP_TYPE_EXTENDED_RESPONSE = 0x78; 223 224 225 226 /** 227 * The BER type to use for the intermediate response protocol op. 228 */ 229 public static final byte PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE = 0x79; 230 231 232 233 /** 234 * The BER type to use for the set of controls. 235 */ 236 public static final byte MESSAGE_TYPE_CONTROLS = (byte) 0xA0; 237 238 239 240 /** 241 * The serial version UID for this serializable class. 242 */ 243 private static final long serialVersionUID = 909272448857832592L; 244 245 246 247 // The message ID for this LDAP message. 248 private final int messageID; 249 250 // The protocol op for this LDAP message. 251 @NotNull private final ProtocolOp protocolOp; 252 253 // The set of controls for this LDAP message. 254 @NotNull private final List<Control> controls; 255 256 257 258 /** 259 * Creates a new LDAP message with the provided information. 260 * 261 * @param messageID The message ID for this LDAP message. 262 * @param protocolOp The protocol op for this LDAP message. It must not be 263 * {@code null}. 264 * @param controls The set of controls for this LDAP message. It may be 265 * {@code null} or empty if no controls are required. 266 */ 267 public LDAPMessage(final int messageID, @NotNull final ProtocolOp protocolOp, 268 @Nullable final Control... controls) 269 { 270 this.messageID = messageID; 271 this.protocolOp = protocolOp; 272 273 if (controls == null) 274 { 275 this.controls = Collections.emptyList(); 276 } 277 else 278 { 279 this.controls = Collections.unmodifiableList(Arrays.asList(controls)); 280 } 281 } 282 283 284 285 /** 286 * Creates a new LDAP message with the provided information. 287 * 288 * @param messageID The message ID for this LDAP message. 289 * @param protocolOp The protocol op for this LDAP message. It must not be 290 * {@code null}. 291 * @param controls The set of controls for this LDAP message. It may be 292 * {@code null} or empty if no controls are required. 293 */ 294 public LDAPMessage(final int messageID, @NotNull final ProtocolOp protocolOp, 295 @Nullable final List<Control> controls) 296 { 297 this.messageID = messageID; 298 this.protocolOp = protocolOp; 299 300 if (controls == null) 301 { 302 this.controls = Collections.emptyList(); 303 } 304 else 305 { 306 this.controls = Collections.unmodifiableList(controls); 307 } 308 } 309 310 311 312 /** 313 * Retrieves the message ID for this LDAP message. 314 * 315 * @return The message ID for this LDAP message. 316 */ 317 public int getMessageID() 318 { 319 return messageID; 320 } 321 322 323 324 /** 325 * Retrieves the protocol op for this LDAP message. 326 * 327 * @return The protocol op for this LDAP message. 328 */ 329 @NotNull() 330 public ProtocolOp getProtocolOp() 331 { 332 return protocolOp; 333 } 334 335 336 337 /** 338 * Retrieves the BER type for the protocol op contained in this LDAP message. 339 * 340 * @return The BER type for the protocol op contained in this LDAP message. 341 */ 342 public byte getProtocolOpType() 343 { 344 return protocolOp.getProtocolOpType(); 345 } 346 347 348 349 /** 350 * Retrieves the abandon request protocol op from this LDAP message. This may 351 * only be used if this LDAP message was obtained using the {@link #readFrom} 352 * method. 353 * 354 * @return The abandon request protocol op from this LDAP message. 355 * 356 * @throws ClassCastException If the protocol op for this LDAP message is 357 * not an abandon request protocol op. 358 */ 359 @NotNull() 360 public AbandonRequestProtocolOp getAbandonRequestProtocolOp() 361 throws ClassCastException 362 { 363 return (AbandonRequestProtocolOp) protocolOp; 364 } 365 366 367 368 /** 369 * Retrieves the add request protocol op from this LDAP message. This may 370 * only be used if this LDAP message was obtained using the {@link #readFrom} 371 * method. 372 * 373 * @return The add request protocol op from this LDAP message. 374 * 375 * @throws ClassCastException If the protocol op for this LDAP message is 376 * not an add request protocol op. 377 */ 378 @NotNull() 379 public AddRequestProtocolOp getAddRequestProtocolOp() 380 throws ClassCastException 381 { 382 return (AddRequestProtocolOp) protocolOp; 383 } 384 385 386 387 /** 388 * Retrieves the add response protocol op from this LDAP message. This may 389 * only be used if this LDAP message was obtained using the {@link #readFrom} 390 * method. 391 * 392 * @return The add response protocol op from this LDAP message. 393 * 394 * @throws ClassCastException If the protocol op for this LDAP message is 395 * not an add response protocol op. 396 */ 397 @NotNull() 398 public AddResponseProtocolOp getAddResponseProtocolOp() 399 throws ClassCastException 400 { 401 return (AddResponseProtocolOp) protocolOp; 402 } 403 404 405 406 /** 407 * Retrieves the bind request protocol op from this LDAP message. This may 408 * only be used if this LDAP message was obtained using the {@link #readFrom} 409 * method. 410 * 411 * @return The bind request protocol op from this LDAP message. 412 * 413 * @throws ClassCastException If the protocol op for this LDAP message is 414 * not a bind request protocol op. 415 */ 416 @NotNull() 417 public BindRequestProtocolOp getBindRequestProtocolOp() 418 throws ClassCastException 419 { 420 return (BindRequestProtocolOp) protocolOp; 421 } 422 423 424 425 /** 426 * Retrieves the bind response protocol op from this LDAP message. This may 427 * only be used if this LDAP message was obtained using the {@link #readFrom} 428 * method. 429 * 430 * @return The bind response protocol op from this LDAP message. 431 * 432 * @throws ClassCastException If the protocol op for this LDAP message is 433 * not a bind response protocol op. 434 */ 435 @NotNull() 436 public BindResponseProtocolOp getBindResponseProtocolOp() 437 throws ClassCastException 438 { 439 return (BindResponseProtocolOp) protocolOp; 440 } 441 442 443 444 /** 445 * Retrieves the compare request protocol op from this LDAP message. This may 446 * only be used if this LDAP message was obtained using the {@link #readFrom} 447 * method. 448 * 449 * @return The compare request protocol op from this LDAP message. 450 * 451 * @throws ClassCastException If the protocol op for this LDAP message is 452 * not a compare request protocol op. 453 */ 454 @NotNull() 455 public CompareRequestProtocolOp getCompareRequestProtocolOp() 456 throws ClassCastException 457 { 458 return (CompareRequestProtocolOp) protocolOp; 459 } 460 461 462 463 /** 464 * Retrieves the compare response protocol op from this LDAP message. This 465 * may only be used if this LDAP message was obtained using the 466 * {@link #readFrom} method. 467 * 468 * @return The compare response protocol op from this LDAP message. 469 * 470 * @throws ClassCastException If the protocol op for this LDAP message is 471 * not a compare response protocol op. 472 */ 473 @NotNull() 474 public CompareResponseProtocolOp getCompareResponseProtocolOp() 475 throws ClassCastException 476 { 477 return (CompareResponseProtocolOp) protocolOp; 478 } 479 480 481 482 /** 483 * Retrieves the delete request protocol op from this LDAP message. This may 484 * only be used if this LDAP message was obtained using the {@link #readFrom} 485 * method. 486 * 487 * @return The delete request protocol op from this LDAP message. 488 * 489 * @throws ClassCastException If the protocol op for this LDAP message is 490 * not a delete request protocol op. 491 */ 492 @NotNull() 493 public DeleteRequestProtocolOp getDeleteRequestProtocolOp() 494 throws ClassCastException 495 { 496 return (DeleteRequestProtocolOp) protocolOp; 497 } 498 499 500 501 /** 502 * Retrieves the delete response protocol op from this LDAP message. This may 503 * only be used if this LDAP message was obtained using the {@link #readFrom} 504 * method. 505 * 506 * @return The delete response protocol op from this LDAP message. 507 * 508 * @throws ClassCastException If the protocol op for this LDAP message is 509 * not a delete response protocol op. 510 */ 511 @NotNull() 512 public DeleteResponseProtocolOp getDeleteResponseProtocolOp() 513 throws ClassCastException 514 { 515 return (DeleteResponseProtocolOp) protocolOp; 516 } 517 518 519 520 /** 521 * Retrieves the extended request protocol op from this LDAP message. This 522 * may only be used if this LDAP message was obtained using the 523 * {@link #readFrom} method. 524 * 525 * @return The extended request protocol op from this LDAP message. 526 * 527 * @throws ClassCastException If the protocol op for this LDAP message is 528 * not an extended request protocol op. 529 */ 530 @NotNull() 531 public ExtendedRequestProtocolOp getExtendedRequestProtocolOp() 532 throws ClassCastException 533 { 534 return (ExtendedRequestProtocolOp) protocolOp; 535 } 536 537 538 539 /** 540 * Retrieves the extended response protocol op from this LDAP message. This 541 * may only be used if this LDAP message was obtained using the 542 * {@link #readFrom} method. 543 * 544 * @return The extended response protocol op from this LDAP message. 545 * 546 * @throws ClassCastException If the protocol op for this LDAP message is 547 * not an extended response protocol op. 548 */ 549 @NotNull() 550 public ExtendedResponseProtocolOp getExtendedResponseProtocolOp() 551 throws ClassCastException 552 { 553 return (ExtendedResponseProtocolOp) protocolOp; 554 } 555 556 557 558 /** 559 * Retrieves the modify request protocol op from this LDAP message. This may 560 * only be used if this LDAP message was obtained using the {@link #readFrom} 561 * method. 562 * 563 * @return The modify request protocol op from this LDAP message. 564 * 565 * @throws ClassCastException If the protocol op for this LDAP message is 566 * not a modify request protocol op. 567 */ 568 @NotNull() 569 public ModifyRequestProtocolOp getModifyRequestProtocolOp() 570 throws ClassCastException 571 { 572 return (ModifyRequestProtocolOp) protocolOp; 573 } 574 575 576 577 /** 578 * Retrieves the modify response protocol op from this LDAP message. This may 579 * only be used if this LDAP message was obtained using the {@link #readFrom} 580 * method. 581 * 582 * @return The modify response protocol op from this LDAP message. 583 * 584 * @throws ClassCastException If the protocol op for this LDAP message is 585 * not a modify response protocol op. 586 */ 587 @NotNull() 588 public ModifyResponseProtocolOp getModifyResponseProtocolOp() 589 throws ClassCastException 590 { 591 return (ModifyResponseProtocolOp) protocolOp; 592 } 593 594 595 596 /** 597 * Retrieves the modify DN request protocol op from this LDAP message. This 598 * may only be used if this LDAP message was obtained using the 599 * {@link #readFrom} method. 600 * 601 * @return The modify DN request protocol op from this LDAP message. 602 * 603 * @throws ClassCastException If the protocol op for this LDAP message is 604 * not a modify DN request protocol op. 605 */ 606 @NotNull() 607 public ModifyDNRequestProtocolOp getModifyDNRequestProtocolOp() 608 throws ClassCastException 609 { 610 return (ModifyDNRequestProtocolOp) protocolOp; 611 } 612 613 614 615 /** 616 * Retrieves the modify DN response protocol op from this LDAP message. This 617 * may only be used if this LDAP message was obtained using the 618 * {@link #readFrom} method. 619 * 620 * @return The modify DN response protocol op from this LDAP message. 621 * 622 * @throws ClassCastException If the protocol op for this LDAP message is 623 * not a modify DN response protocol op. 624 */ 625 @NotNull() 626 public ModifyDNResponseProtocolOp getModifyDNResponseProtocolOp() 627 throws ClassCastException 628 { 629 return (ModifyDNResponseProtocolOp) protocolOp; 630 } 631 632 633 634 /** 635 * Retrieves the search request protocol op from this LDAP message. This 636 * may only be used if this LDAP message was obtained using the 637 * {@link #readFrom} method. 638 * 639 * @return The search request protocol op from this LDAP message. 640 * 641 * @throws ClassCastException If the protocol op for this LDAP message is 642 * not a search request protocol op. 643 */ 644 @NotNull() 645 public SearchRequestProtocolOp getSearchRequestProtocolOp() 646 throws ClassCastException 647 { 648 return (SearchRequestProtocolOp) protocolOp; 649 } 650 651 652 653 /** 654 * Retrieves the search result entry protocol op from this LDAP message. This 655 * may only be used if this LDAP message was obtained using the 656 * {@link #readFrom} method. 657 * 658 * @return The search result entry protocol op from this LDAP message. 659 * 660 * @throws ClassCastException If the protocol op for this LDAP message is 661 * not a search result entry protocol op. 662 */ 663 @NotNull() 664 public SearchResultEntryProtocolOp getSearchResultEntryProtocolOp() 665 throws ClassCastException 666 { 667 return (SearchResultEntryProtocolOp) protocolOp; 668 } 669 670 671 672 /** 673 * Retrieves the search result reference protocol op from this LDAP message. 674 * This may only be used if this LDAP message was obtained using the 675 * {@link #readFrom} method. 676 * 677 * @return The search result reference protocol op from this LDAP message. 678 * 679 * @throws ClassCastException If the protocol op for this LDAP message is 680 * not a search result reference protocol op. 681 */ 682 @NotNull() 683 public SearchResultReferenceProtocolOp getSearchResultReferenceProtocolOp() 684 throws ClassCastException 685 { 686 return (SearchResultReferenceProtocolOp) protocolOp; 687 } 688 689 690 691 /** 692 * Retrieves the search result done protocol op from this LDAP message. This 693 * may only be used if this LDAP message was obtained using the 694 * {@link #readFrom} method. 695 * 696 * @return The search result done protocol op from this LDAP message. 697 * 698 * @throws ClassCastException If the protocol op for this LDAP message is 699 * not a search result done protocol op. 700 */ 701 @NotNull() 702 public SearchResultDoneProtocolOp getSearchResultDoneProtocolOp() 703 throws ClassCastException 704 { 705 return (SearchResultDoneProtocolOp) protocolOp; 706 } 707 708 709 710 /** 711 * Retrieves the unbind request protocol op from this LDAP message. This may 712 * only be used if this LDAP message was obtained using the {@link #readFrom} 713 * method. 714 * 715 * @return The unbind request protocol op from this LDAP message. 716 * 717 * @throws ClassCastException If the protocol op for this LDAP message is 718 * not an unbind request protocol op. 719 */ 720 @NotNull() 721 public UnbindRequestProtocolOp getUnbindRequestProtocolOp() 722 throws ClassCastException 723 { 724 return (UnbindRequestProtocolOp) protocolOp; 725 } 726 727 728 729 /** 730 * Retrieves the intermediate response protocol op from this LDAP message. 731 * This may only be used if this LDAP message was obtained using the 732 * {@link #readFrom} method. 733 * 734 * @return The intermediate response protocol op from this LDAP message. 735 * 736 * @throws ClassCastException If the protocol op for this LDAP message is 737 * not an intermediate response protocol op. 738 */ 739 @NotNull() 740 public IntermediateResponseProtocolOp getIntermediateResponseProtocolOp() 741 throws ClassCastException 742 { 743 return (IntermediateResponseProtocolOp) protocolOp; 744 } 745 746 747 748 /** 749 * Retrieves the set of controls for this LDAP message. 750 * 751 * @return The set of controls for this LDAP message. 752 */ 753 @NotNull() 754 public List<Control> getControls() 755 { 756 return controls; 757 } 758 759 760 761 /** 762 * Encodes this LDAP message to an ASN.1 element. 763 * 764 * @return The ASN.1 element containing the encoded representation of this 765 * LDAP message. 766 */ 767 @NotNull() 768 public ASN1Element encode() 769 { 770 if (controls.isEmpty()) 771 { 772 return new ASN1Sequence( 773 new ASN1Integer(messageID), 774 protocolOp.encodeProtocolOp()); 775 } 776 else 777 { 778 final Control[] controlArray = new Control[controls.size()]; 779 controls.toArray(controlArray); 780 781 return new ASN1Sequence( 782 new ASN1Integer(messageID), 783 protocolOp.encodeProtocolOp(), 784 Control.encodeControls(controlArray)); 785 } 786 } 787 788 789 790 /** 791 * Decodes the provided ASN.1 element as an LDAP message. 792 * 793 * @param element The ASN.1 element to be decoded. 794 * 795 * @return The LDAP message decoded from the provided ASN.1 element. 796 * 797 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 798 * a valid LDAP message. 799 */ 800 @NotNull() 801 public static LDAPMessage decode(@NotNull final ASN1Element element) 802 throws LDAPException 803 { 804 try 805 { 806 final ASN1Element[] elements = 807 ASN1Sequence.decodeAsSequence(element).elements(); 808 if ((elements.length < 2) || (elements.length > 3)) 809 { 810 throw new LDAPException(ResultCode.DECODING_ERROR, 811 ERR_MESSAGE_DECODE_VALUE_SEQUENCE_INVALID_ELEMENT_COUNT.get( 812 elements.length)); 813 } 814 815 final int messageID = ASN1Integer.decodeAsInteger(elements[0]).intValue(); 816 817 final ProtocolOp protocolOp; 818 switch (elements[1].getType()) 819 { 820 case PROTOCOL_OP_TYPE_ABANDON_REQUEST: 821 protocolOp = AbandonRequestProtocolOp.decodeProtocolOp(elements[1]); 822 break; 823 case PROTOCOL_OP_TYPE_ADD_REQUEST: 824 protocolOp = AddRequestProtocolOp.decodeProtocolOp(elements[1]); 825 break; 826 case PROTOCOL_OP_TYPE_ADD_RESPONSE: 827 protocolOp = AddResponseProtocolOp.decodeProtocolOp(elements[1]); 828 break; 829 case PROTOCOL_OP_TYPE_BIND_REQUEST: 830 protocolOp = BindRequestProtocolOp.decodeProtocolOp(elements[1]); 831 break; 832 case PROTOCOL_OP_TYPE_BIND_RESPONSE: 833 protocolOp = BindResponseProtocolOp.decodeProtocolOp(elements[1]); 834 break; 835 case PROTOCOL_OP_TYPE_COMPARE_REQUEST: 836 protocolOp = CompareRequestProtocolOp.decodeProtocolOp(elements[1]); 837 break; 838 case PROTOCOL_OP_TYPE_COMPARE_RESPONSE: 839 protocolOp = CompareResponseProtocolOp.decodeProtocolOp(elements[1]); 840 break; 841 case PROTOCOL_OP_TYPE_DELETE_REQUEST: 842 protocolOp = DeleteRequestProtocolOp.decodeProtocolOp(elements[1]); 843 break; 844 case PROTOCOL_OP_TYPE_DELETE_RESPONSE: 845 protocolOp = DeleteResponseProtocolOp.decodeProtocolOp(elements[1]); 846 break; 847 case PROTOCOL_OP_TYPE_EXTENDED_REQUEST: 848 protocolOp = ExtendedRequestProtocolOp.decodeProtocolOp(elements[1]); 849 break; 850 case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 851 protocolOp = ExtendedResponseProtocolOp.decodeProtocolOp(elements[1]); 852 break; 853 case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE: 854 protocolOp = 855 IntermediateResponseProtocolOp.decodeProtocolOp(elements[1]); 856 break; 857 case PROTOCOL_OP_TYPE_MODIFY_REQUEST: 858 protocolOp = ModifyRequestProtocolOp.decodeProtocolOp(elements[1]); 859 break; 860 case PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 861 protocolOp = ModifyResponseProtocolOp.decodeProtocolOp(elements[1]); 862 break; 863 case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST: 864 protocolOp = ModifyDNRequestProtocolOp.decodeProtocolOp(elements[1]); 865 break; 866 case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 867 protocolOp = ModifyDNResponseProtocolOp.decodeProtocolOp(elements[1]); 868 break; 869 case PROTOCOL_OP_TYPE_SEARCH_REQUEST: 870 protocolOp = SearchRequestProtocolOp.decodeProtocolOp(elements[1]); 871 break; 872 case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE: 873 protocolOp = SearchResultDoneProtocolOp.decodeProtocolOp(elements[1]); 874 break; 875 case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY: 876 protocolOp = 877 SearchResultEntryProtocolOp.decodeProtocolOp(elements[1]); 878 break; 879 case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE: 880 protocolOp = 881 SearchResultReferenceProtocolOp.decodeProtocolOp(elements[1]); 882 break; 883 case PROTOCOL_OP_TYPE_UNBIND_REQUEST: 884 protocolOp = UnbindRequestProtocolOp.decodeProtocolOp(elements[1]); 885 break; 886 default: 887 throw new LDAPException(ResultCode.DECODING_ERROR, 888 ERR_MESSAGE_DECODE_INVALID_PROTOCOL_OP_TYPE.get( 889 StaticUtils.toHex(elements[1].getType()))); 890 } 891 892 final Control[] controls; 893 if (elements.length == 3) 894 { 895 controls = 896 Control.decodeControls(ASN1Sequence.decodeAsSequence(elements[2])); 897 } 898 else 899 { 900 controls = null; 901 } 902 903 return new LDAPMessage(messageID, protocolOp, controls); 904 } 905 catch (final LDAPException le) 906 { 907 Debug.debugException(le); 908 throw le; 909 } 910 catch (final Exception e) 911 { 912 Debug.debugException(e); 913 throw new LDAPException(ResultCode.DECODING_ERROR, 914 ERR_MESSAGE_DECODE_ERROR.get(StaticUtils.getExceptionMessage(e)), 915 e); 916 } 917 } 918 919 920 921 /** 922 * Writes an encoded representation of this LDAP message to the provided ASN.1 923 * buffer. 924 * 925 * @param buffer The ASN.1 buffer to which the encoded representation should 926 * be written. 927 */ 928 public void writeTo(@NotNull final ASN1Buffer buffer) 929 { 930 final ASN1BufferSequence messageSequence = buffer.beginSequence(); 931 buffer.addInteger(messageID); 932 protocolOp.writeTo(buffer); 933 934 if (! controls.isEmpty()) 935 { 936 final ASN1BufferSequence controlsSequence = 937 buffer.beginSequence(MESSAGE_TYPE_CONTROLS); 938 for (final Control c : controls) 939 { 940 c.writeTo(buffer); 941 } 942 controlsSequence.end(); 943 } 944 messageSequence.end(); 945 } 946 947 948 949 /** 950 * Reads an LDAP message from the provided ASN.1 stream reader. 951 * 952 * @param reader The ASN.1 stream reader from which the LDAP 953 * message should be read. 954 * @param ignoreSocketTimeout Indicates whether to ignore socket timeout 955 * exceptions caught during processing. This 956 * should be {@code true} when the associated 957 * connection is operating in asynchronous mode, 958 * and {@code false} when operating in 959 * synchronous mode. In either case, exceptions 960 * will not be ignored for the first read, since 961 * that will be handled by the connection reader. 962 * 963 * @return The decoded LDAP message, or {@code null} if the end of the input 964 * stream has been reached. 965 * 966 * @throws LDAPException If an error occurs while attempting to read or 967 * decode the LDAP message. 968 */ 969 @Nullable() 970 public static LDAPMessage readFrom(@NotNull final ASN1StreamReader reader, 971 final boolean ignoreSocketTimeout) 972 throws LDAPException 973 { 974 final ASN1StreamReaderSequence messageSequence; 975 try 976 { 977 reader.setIgnoreSocketTimeout(false, ignoreSocketTimeout); 978 messageSequence = reader.beginSequence(); 979 if (messageSequence == null) 980 { 981 return null; 982 } 983 } 984 catch (final IOException ioe) 985 { 986 if (! ((ioe instanceof SocketTimeoutException) || 987 (ioe instanceof InterruptedIOException))) 988 { 989 Debug.debugException(ioe); 990 } 991 992 throw new LDAPException(ResultCode.SERVER_DOWN, 993 ERR_MESSAGE_IO_ERROR.get(StaticUtils.getExceptionMessage(ioe)), ioe); 994 } 995 catch (final Exception e) 996 { 997 Debug.debugException(e); 998 999 throw new LDAPException(ResultCode.DECODING_ERROR, 1000 ERR_MESSAGE_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), 1001 e); 1002 } 1003 1004 try 1005 { 1006 1007 reader.setIgnoreSocketTimeout(ignoreSocketTimeout, ignoreSocketTimeout); 1008 final int messageID = reader.readInteger(); 1009 1010 final ProtocolOp protocolOp; 1011 final byte protocolOpType = (byte) reader.peek(); 1012 switch (protocolOpType) 1013 { 1014 case PROTOCOL_OP_TYPE_BIND_REQUEST: 1015 protocolOp = new BindRequestProtocolOp(reader); 1016 break; 1017 case PROTOCOL_OP_TYPE_BIND_RESPONSE: 1018 protocolOp = new BindResponseProtocolOp(reader); 1019 break; 1020 case PROTOCOL_OP_TYPE_UNBIND_REQUEST: 1021 protocolOp = new UnbindRequestProtocolOp(reader); 1022 break; 1023 case PROTOCOL_OP_TYPE_SEARCH_REQUEST: 1024 protocolOp = new SearchRequestProtocolOp(reader); 1025 break; 1026 case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY: 1027 protocolOp = new SearchResultEntryProtocolOp(reader); 1028 break; 1029 case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE: 1030 protocolOp = new SearchResultReferenceProtocolOp(reader); 1031 break; 1032 case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE: 1033 protocolOp = new SearchResultDoneProtocolOp(reader); 1034 break; 1035 case PROTOCOL_OP_TYPE_MODIFY_REQUEST: 1036 protocolOp = new ModifyRequestProtocolOp(reader); 1037 break; 1038 case PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 1039 protocolOp = new ModifyResponseProtocolOp(reader); 1040 break; 1041 case PROTOCOL_OP_TYPE_ADD_REQUEST: 1042 protocolOp = new AddRequestProtocolOp(reader); 1043 break; 1044 case PROTOCOL_OP_TYPE_ADD_RESPONSE: 1045 protocolOp = new AddResponseProtocolOp(reader); 1046 break; 1047 case PROTOCOL_OP_TYPE_DELETE_REQUEST: 1048 protocolOp = new DeleteRequestProtocolOp(reader); 1049 break; 1050 case PROTOCOL_OP_TYPE_DELETE_RESPONSE: 1051 protocolOp = new DeleteResponseProtocolOp(reader); 1052 break; 1053 case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST: 1054 protocolOp = new ModifyDNRequestProtocolOp(reader); 1055 break; 1056 case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 1057 protocolOp = new ModifyDNResponseProtocolOp(reader); 1058 break; 1059 case PROTOCOL_OP_TYPE_COMPARE_REQUEST: 1060 protocolOp = new CompareRequestProtocolOp(reader); 1061 break; 1062 case PROTOCOL_OP_TYPE_COMPARE_RESPONSE: 1063 protocolOp = new CompareResponseProtocolOp(reader); 1064 break; 1065 case PROTOCOL_OP_TYPE_ABANDON_REQUEST: 1066 protocolOp = new AbandonRequestProtocolOp(reader); 1067 break; 1068 case PROTOCOL_OP_TYPE_EXTENDED_REQUEST: 1069 protocolOp = new ExtendedRequestProtocolOp(reader); 1070 break; 1071 case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 1072 protocolOp = new ExtendedResponseProtocolOp(reader); 1073 break; 1074 case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE: 1075 protocolOp = new IntermediateResponseProtocolOp(reader); 1076 break; 1077 default: 1078 throw new LDAPException(ResultCode.DECODING_ERROR, 1079 ERR_MESSAGE_INVALID_PROTOCOL_OP_TYPE.get( 1080 StaticUtils.toHex(protocolOpType))); 1081 } 1082 1083 final ArrayList<Control> controls = new ArrayList<>(5); 1084 if (messageSequence.hasMoreElements()) 1085 { 1086 final ASN1StreamReaderSequence controlSequence = reader.beginSequence(); 1087 while (controlSequence.hasMoreElements()) 1088 { 1089 controls.add(Control.readFrom(reader)); 1090 } 1091 } 1092 1093 return new LDAPMessage(messageID, protocolOp, controls); 1094 } 1095 catch (final LDAPException le) 1096 { 1097 Debug.debugException(le); 1098 throw le; 1099 } 1100 catch (final IOException ioe) 1101 { 1102 Debug.debugException(ioe); 1103 1104 if ((ioe instanceof SocketTimeoutException) || 1105 (ioe instanceof InterruptedIOException)) 1106 { 1107 // We don't want to provide this exception as the cause because we want 1108 // to ensure that a failure in the middle of the response causes the 1109 // connection to be terminated. 1110 throw new LDAPException(ResultCode.DECODING_ERROR, 1111 ERR_MESSAGE_CANNOT_DECODE.get(StaticUtils. 1112 getExceptionMessage(ioe))); 1113 } 1114 else 1115 { 1116 throw new LDAPException(ResultCode.SERVER_DOWN, 1117 ERR_MESSAGE_IO_ERROR.get(StaticUtils.getExceptionMessage(ioe)), 1118 ioe); 1119 } 1120 } 1121 catch (final Exception e) 1122 { 1123 Debug.debugException(e); 1124 1125 throw new LDAPException(ResultCode.DECODING_ERROR, 1126 ERR_MESSAGE_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), 1127 e); 1128 } 1129 } 1130 1131 1132 1133 /** 1134 * Reads {@link LDAPResponse} object from the provided ASN.1 stream reader. 1135 * 1136 * @param reader The ASN.1 stream reader from which the LDAP 1137 * message should be read. 1138 * @param ignoreSocketTimeout Indicates whether to ignore socket timeout 1139 * exceptions caught during processing. This 1140 * should be {@code true} when the associated 1141 * connection is operating in asynchronous mode, 1142 * and {@code false} when operating in 1143 * synchronous mode. In either case, exceptions 1144 * will not be ignored for the first read, since 1145 * that will be handled by the connection reader. 1146 * 1147 * @return The decoded LDAP message, or {@code null} if the end of the input 1148 * stream has been reached. 1149 * 1150 * @throws LDAPException If an error occurs while attempting to read or 1151 * decode the LDAP message. 1152 */ 1153 @Nullable() 1154 public static LDAPResponse readLDAPResponseFrom( 1155 @NotNull final ASN1StreamReader reader, 1156 final boolean ignoreSocketTimeout) 1157 throws LDAPException 1158 { 1159 return readLDAPResponseFrom(reader, ignoreSocketTimeout, null); 1160 } 1161 1162 1163 1164 /** 1165 * Reads {@link LDAPResponse} object from the provided ASN.1 stream reader. 1166 * 1167 * @param reader The ASN.1 stream reader from which the LDAP 1168 * message should be read. 1169 * @param ignoreSocketTimeout Indicates whether to ignore socket timeout 1170 * exceptions caught during processing. This 1171 * should be {@code true} when the associated 1172 * connection is operating in asynchronous mode, 1173 * and {@code false} when operating in 1174 * synchronous mode. In either case, exceptions 1175 * will not be ignored for the first read, since 1176 * that will be handled by the connection reader. 1177 * @param schema The schema to use to select the appropriate 1178 * matching rule for attributes included in the 1179 * response. 1180 * 1181 * @return The decoded LDAP message, or {@code null} if the end of the input 1182 * stream has been reached. 1183 * 1184 * @throws LDAPException If an error occurs while attempting to read or 1185 * decode the LDAP message. 1186 */ 1187 @Nullable() 1188 public static LDAPResponse readLDAPResponseFrom( 1189 @NotNull final ASN1StreamReader reader, 1190 final boolean ignoreSocketTimeout, 1191 @Nullable final Schema schema) 1192 throws LDAPException 1193 { 1194 final ASN1StreamReaderSequence messageSequence; 1195 try 1196 { 1197 reader.setIgnoreSocketTimeout(false, ignoreSocketTimeout); 1198 messageSequence = reader.beginSequence(); 1199 if (messageSequence == null) 1200 { 1201 return null; 1202 } 1203 } 1204 catch (final IOException ioe) 1205 { 1206 final ResultCode resultCode; 1207 if (ioe instanceof SocketTimeoutException) 1208 { 1209 resultCode = ResultCode.TIMEOUT; 1210 } 1211 else 1212 { 1213 Debug.debugException(ioe); 1214 resultCode = ResultCode.SERVER_DOWN; 1215 } 1216 1217 throw new LDAPException(resultCode, 1218 ERR_MESSAGE_IO_ERROR.get(StaticUtils.getExceptionMessage(ioe)), ioe); 1219 } 1220 catch (final Exception e) 1221 { 1222 Debug.debugException(e); 1223 1224 throw new LDAPException(ResultCode.DECODING_ERROR, 1225 ERR_MESSAGE_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), 1226 e); 1227 } 1228 1229 try 1230 { 1231 reader.setIgnoreSocketTimeout(ignoreSocketTimeout, ignoreSocketTimeout); 1232 final int messageID = reader.readInteger(); 1233 1234 final byte protocolOpType = (byte) reader.peek(); 1235 switch (protocolOpType) 1236 { 1237 case PROTOCOL_OP_TYPE_ADD_RESPONSE: 1238 case PROTOCOL_OP_TYPE_DELETE_RESPONSE: 1239 case PROTOCOL_OP_TYPE_MODIFY_RESPONSE: 1240 case PROTOCOL_OP_TYPE_MODIFY_DN_RESPONSE: 1241 return InternalSDKHelper.readLDAPResultFrom(messageID, 1242 messageSequence, reader); 1243 1244 case PROTOCOL_OP_TYPE_BIND_RESPONSE: 1245 return InternalSDKHelper.readBindResultFrom(messageID, 1246 messageSequence, reader); 1247 1248 case PROTOCOL_OP_TYPE_COMPARE_RESPONSE: 1249 return InternalSDKHelper.readCompareResultFrom(messageID, 1250 messageSequence, reader); 1251 1252 case PROTOCOL_OP_TYPE_EXTENDED_RESPONSE: 1253 return InternalSDKHelper.readExtendedResultFrom(messageID, 1254 messageSequence, reader); 1255 1256 case PROTOCOL_OP_TYPE_SEARCH_RESULT_ENTRY: 1257 return InternalSDKHelper.readSearchResultEntryFrom(messageID, 1258 messageSequence, reader, schema); 1259 1260 case PROTOCOL_OP_TYPE_SEARCH_RESULT_REFERENCE: 1261 return InternalSDKHelper.readSearchResultReferenceFrom(messageID, 1262 messageSequence, reader); 1263 1264 case PROTOCOL_OP_TYPE_SEARCH_RESULT_DONE: 1265 return InternalSDKHelper.readSearchResultFrom(messageID, 1266 messageSequence, reader); 1267 1268 case PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE: 1269 return InternalSDKHelper.readIntermediateResponseFrom(messageID, 1270 messageSequence, reader); 1271 1272 case PROTOCOL_OP_TYPE_ABANDON_REQUEST: 1273 case PROTOCOL_OP_TYPE_ADD_REQUEST: 1274 case PROTOCOL_OP_TYPE_BIND_REQUEST: 1275 case PROTOCOL_OP_TYPE_COMPARE_REQUEST: 1276 case PROTOCOL_OP_TYPE_DELETE_REQUEST: 1277 case PROTOCOL_OP_TYPE_EXTENDED_REQUEST: 1278 case PROTOCOL_OP_TYPE_MODIFY_REQUEST: 1279 case PROTOCOL_OP_TYPE_MODIFY_DN_REQUEST: 1280 case PROTOCOL_OP_TYPE_SEARCH_REQUEST: 1281 case PROTOCOL_OP_TYPE_UNBIND_REQUEST: 1282 throw new LDAPException(ResultCode.DECODING_ERROR, 1283 ERR_MESSAGE_PROTOCOL_OP_TYPE_NOT_RESPONSE.get( 1284 StaticUtils.toHex(protocolOpType))); 1285 1286 default: 1287 throw new LDAPException(ResultCode.DECODING_ERROR, 1288 ERR_MESSAGE_INVALID_PROTOCOL_OP_TYPE.get( 1289 StaticUtils.toHex(protocolOpType))); 1290 } 1291 } 1292 catch (final LDAPException le) 1293 { 1294 Debug.debugException(le); 1295 throw le; 1296 } 1297 catch (final IOException ioe) 1298 { 1299 Debug.debugException(ioe); 1300 1301 if ((ioe instanceof SocketTimeoutException) || 1302 (ioe instanceof InterruptedIOException)) 1303 { 1304 // We don't want to provide this exception as the cause because we want 1305 // to ensure that a failure in the middle of the response causes the 1306 // connection to be terminated. 1307 throw new LDAPException(ResultCode.DECODING_ERROR, 1308 ERR_MESSAGE_CANNOT_DECODE.get( 1309 StaticUtils.getExceptionMessage(ioe))); 1310 } 1311 else 1312 { 1313 throw new LDAPException(ResultCode.SERVER_DOWN, 1314 ERR_MESSAGE_IO_ERROR.get(StaticUtils.getExceptionMessage(ioe)), 1315 ioe); 1316 } 1317 } 1318 catch (final Exception e) 1319 { 1320 Debug.debugException(e); 1321 1322 throw new LDAPException(ResultCode.DECODING_ERROR, 1323 ERR_MESSAGE_CANNOT_DECODE.get(StaticUtils.getExceptionMessage(e)), 1324 e); 1325 } 1326 } 1327 1328 1329 1330 /** 1331 * Retrieves a string representation of this LDAP message. 1332 * 1333 * @return A string representation of this LDAP message. 1334 */ 1335 @Override() 1336 @NotNull() 1337 public String toString() 1338 { 1339 final StringBuilder buffer = new StringBuilder(); 1340 toString(buffer); 1341 return buffer.toString(); 1342 } 1343 1344 1345 1346 /** 1347 * Appends a string representation of this LDAP message to the provided 1348 * buffer. 1349 * 1350 * @param buffer The buffer to which the string representation should be 1351 * appended. 1352 */ 1353 public void toString(@NotNull final StringBuilder buffer) 1354 { 1355 buffer.append("LDAPMessage(msgID="); 1356 buffer.append(messageID); 1357 buffer.append(", protocolOp="); 1358 protocolOp.toString(buffer); 1359 1360 if (! controls.isEmpty()) 1361 { 1362 buffer.append(", controls={"); 1363 final Iterator<Control> iterator = controls.iterator(); 1364 while (iterator.hasNext()) 1365 { 1366 iterator.next().toString(buffer); 1367 if (iterator.hasNext()) 1368 { 1369 buffer.append(','); 1370 } 1371 } 1372 buffer.append('}'); 1373 } 1374 1375 buffer.append(')'); 1376 } 1377}