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