001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.protocols.ldap; 028 029 030 031 import java.net.InetAddress; 032 import java.nio.ByteBuffer; 033 import java.nio.channels.Selector; 034 import java.nio.channels.SocketChannel; 035 import java.util.ArrayList; 036 import java.util.Collection; 037 import java.util.Iterator; 038 import java.util.List; 039 import java.util.concurrent.ConcurrentHashMap; 040 import java.util.concurrent.atomic.AtomicLong; 041 import java.util.concurrent.atomic.AtomicReference; 042 043 import org.opends.messages.Message; 044 import org.opends.messages.MessageBuilder; 045 import org.opends.server.api.ClientConnection; 046 import org.opends.server.api.ConnectionHandler; 047 import org.opends.server.api.ConnectionSecurityProvider; 048 import org.opends.server.core.AbandonOperationBasis; 049 import org.opends.server.core.AddOperationBasis; 050 import org.opends.server.core.BindOperationBasis; 051 import org.opends.server.core.CompareOperationBasis; 052 import org.opends.server.core.DeleteOperationBasis; 053 import org.opends.server.core.DirectoryServer; 054 import org.opends.server.core.ExtendedOperationBasis; 055 import org.opends.server.core.ModifyDNOperationBasis; 056 import org.opends.server.core.ModifyOperationBasis; 057 import org.opends.server.core.PersistentSearch; 058 import org.opends.server.core.PluginConfigManager; 059 import org.opends.server.core.SearchOperation; 060 import org.opends.server.core.SearchOperationBasis; 061 import org.opends.server.core.UnbindOperationBasis; 062 import org.opends.server.extensions.NullConnectionSecurityProvider; 063 import org.opends.server.extensions.TLSCapableConnection; 064 import org.opends.server.extensions.TLSConnectionSecurityProvider; 065 import org.opends.server.loggers.debug.DebugTracer; 066 import org.opends.server.protocols.asn1.ASN1Element; 067 import org.opends.server.protocols.asn1.ASN1OctetString; 068 import org.opends.server.protocols.asn1.ASN1Sequence; 069 import org.opends.server.types.AbstractOperation; 070 import org.opends.server.types.CancelRequest; 071 import org.opends.server.types.CancelResult; 072 import org.opends.server.types.Control; 073 import org.opends.server.types.DN; 074 import org.opends.server.types.DebugLogLevel; 075 import org.opends.server.types.DirectoryException; 076 import org.opends.server.types.DisconnectReason; 077 import org.opends.server.types.IntermediateResponse; 078 import org.opends.server.types.Operation; 079 import org.opends.server.types.ResultCode; 080 import org.opends.server.types.SearchResultEntry; 081 import org.opends.server.types.SearchResultReference; 082 import org.opends.server.util.TimeThread; 083 084 import static org.opends.messages.ProtocolMessages.*; 085 import static org.opends.server.loggers.AccessLogger.logDisconnect; 086 import static org.opends.server.loggers.ErrorLogger.logError; 087 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; 088 import static org.opends.server.loggers.debug.DebugLogger.getTracer; 089 import static org.opends.server.protocols.ldap.LDAPConstants.*; 090 import static org.opends.server.util.StaticUtils.getExceptionMessage; 091 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; 092 093 094 095 /** 096 * This class defines an LDAP client connection, which is a type of client 097 * connection that will be accepted by an instance of the LDAP connection 098 * handler and have its requests decoded by an LDAP request handler. 099 */ 100 public class LDAPClientConnection 101 extends ClientConnection 102 implements TLSCapableConnection 103 { 104 /** 105 * The tracer object for the debug logger. 106 */ 107 private static final DebugTracer TRACER = getTracer(); 108 109 110 111 // The time that the last operation was completed. 112 private AtomicLong lastCompletionTime; 113 114 // The next operation ID that should be used for this connection. 115 private AtomicLong nextOperationID; 116 117 // The selector that may be used for write operations. 118 private AtomicReference<Selector> writeSelector; 119 120 // Indicates whether the Directory Server believes this connection to be 121 // valid and available for communication. 122 private boolean connectionValid; 123 124 // Indicates whether this connection is about to be closed. This will be used 125 // to prevent accepting new requests while a disconnect is in progress. 126 private boolean disconnectRequested; 127 128 // Indicates whether the connection should keep statistics regarding the 129 // operations that it is performing. 130 private boolean keepStats; 131 132 // The BER type for the ASN.1 element that is in the process of being read. 133 private byte elementType; 134 135 // The encoded value for the ASN.1 element that is in the process of being 136 // read. 137 private byte[] elementValue; 138 139 // The set of all operations currently in progress on this connection. 140 private ConcurrentHashMap<Integer,AbstractOperation> operationsInProgress; 141 142 // The connection security provider that was in use for the client connection 143 // before switching to a TLS-based provider. 144 private ConnectionSecurityProvider clearSecurityProvider; 145 146 // The connection security provider for this client connection. 147 private ConnectionSecurityProvider securityProvider; 148 149 // The port on the client from which this connection originated. 150 private int clientPort; 151 152 // The number of bytes contained in the value for the ASN.1 element that is in 153 // the process of being read. 154 private int elementLength; 155 156 // The number of bytes in the multi-byte length that are still needed to fully 157 // decode the length of the ASN.1 element in process. 158 private int elementLengthBytesNeeded; 159 160 // The current state for the data read for the ASN.1 element in progress. 161 private int elementReadState; 162 163 // The number of bytes that have already been read for the ASN.1 element 164 // value in progress. 165 private int elementValueBytesRead; 166 167 // The number of bytes that are still needed to fully decode the value of the 168 // ASN.1 element in progress. 169 private int elementValueBytesNeeded; 170 171 // The LDAP version that the client is using to communicate with the server. 172 private int ldapVersion; 173 174 // The port on the server to which this client has connected. 175 private int serverPort; 176 177 // The reference to the connection handler that accepted this connection. 178 private LDAPConnectionHandler connectionHandler; 179 180 // The reference to the request handler with which this connection is 181 // associated. 182 private LDAPRequestHandler requestHandler; 183 184 // The statistics tracker associated with this client connection. 185 private LDAPStatistics statTracker; 186 187 // The connection ID assigned to this connection. 188 private long connectionID; 189 190 // The lock used to provide threadsafe access to the set of operations in 191 // progress. 192 private Object opsInProgressLock; 193 194 // The lock used to provide threadsafe access when sending data to the client. 195 private Object transmitLock; 196 197 // The socket channel with which this client connection is associated. 198 private SocketChannel clientChannel; 199 200 // The string representation of the address of the client. 201 private String clientAddress; 202 203 // The name of the protocol that the client is using to communicate with the 204 // server. 205 private String protocol; 206 207 // The string representation of the address of the server to which the client 208 // has connected. 209 private String serverAddress; 210 211 // The TLS connection security provider that may be used for this connection 212 // if StartTLS is requested. 213 private TLSConnectionSecurityProvider tlsSecurityProvider; 214 215 216 217 /** 218 * Creates a new LDAP client connection with the provided information. 219 * 220 * @param connectionHandler The connection handler that accepted this 221 * connection. 222 * @param clientChannel The socket channel that may be used to 223 * communicate with the client. 224 */ 225 public LDAPClientConnection(LDAPConnectionHandler connectionHandler, 226 SocketChannel clientChannel) 227 { 228 super(); 229 230 231 this.connectionHandler = connectionHandler; 232 this.clientChannel = clientChannel; 233 this.securityProvider = null; 234 this.clearSecurityProvider = null; 235 236 opsInProgressLock = new Object(); 237 transmitLock = new Object(); 238 239 elementReadState = ELEMENT_READ_STATE_NEED_TYPE; 240 elementType = 0x00; 241 elementLength = 0; 242 elementLengthBytesNeeded = 0; 243 elementValue = null; 244 elementValueBytesRead = 0; 245 elementValueBytesNeeded = 0; 246 247 ldapVersion = 3; 248 requestHandler = null; 249 lastCompletionTime = new AtomicLong(TimeThread.getTime()); 250 nextOperationID = new AtomicLong(0); 251 connectionValid = true; 252 disconnectRequested = false; 253 operationsInProgress = new ConcurrentHashMap<Integer,AbstractOperation>(); 254 keepStats = connectionHandler.keepStats(); 255 protocol = "LDAP"; 256 writeSelector = new AtomicReference<Selector>(); 257 258 clientAddress = clientChannel.socket().getInetAddress().getHostAddress(); 259 clientPort = clientChannel.socket().getPort(); 260 serverAddress = clientChannel.socket().getLocalAddress().getHostAddress(); 261 serverPort = clientChannel.socket().getLocalPort(); 262 263 LDAPStatistics parentTracker = connectionHandler.getStatTracker(); 264 String instanceName = parentTracker.getMonitorInstanceName() + 265 " for " + toString(); 266 statTracker = new LDAPStatistics(instanceName, parentTracker); 267 268 if (keepStats) 269 { 270 statTracker.updateConnect(); 271 } 272 273 connectionID = DirectoryServer.newConnectionAccepted(this); 274 if (connectionID < 0) 275 { 276 disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true, 277 ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get()); 278 } 279 } 280 281 282 283 /** 284 * Retrieves the connection ID assigned to this connection. 285 * 286 * @return The connection ID assigned to this connection. 287 */ 288 public long getConnectionID() 289 { 290 return connectionID; 291 } 292 293 294 295 /** 296 * Retrieves the connection handler that accepted this client connection. 297 * 298 * @return The connection handler that accepted this client connection. 299 */ 300 public ConnectionHandler getConnectionHandler() 301 { 302 return connectionHandler; 303 } 304 305 306 307 /** 308 * Retrieves the request handler that will read requests for this client 309 * connection. 310 * 311 * @return The request handler that will read requests for this client 312 * connection, or <CODE>null</CODE> if none has been assigned yet. 313 */ 314 public LDAPRequestHandler getRequestHandler() 315 { 316 return requestHandler; 317 } 318 319 320 321 /** 322 * Specifies the request handler that will read requests for this client 323 * connection. 324 * 325 * @param requestHandler The request handler that will read requests for 326 * this client connection. 327 */ 328 public void setRequestHandler(LDAPRequestHandler requestHandler) 329 { 330 this.requestHandler = requestHandler; 331 } 332 333 334 335 /** 336 * Retrieves the socket channel that can be used to communicate with the 337 * client. 338 * 339 * @return The socket channel that can be used to communicate with the 340 * client. 341 */ 342 public SocketChannel getSocketChannel() 343 { 344 return clientChannel; 345 } 346 347 348 349 /** 350 * Retrieves the protocol that the client is using to communicate with the 351 * Directory Server. 352 * 353 * @return The protocol that the client is using to communicate with the 354 * Directory Server. 355 */ 356 public String getProtocol() 357 { 358 return protocol; 359 } 360 361 362 363 /** 364 * Retrieves a string representation of the address of the client. 365 * 366 * @return A string representation of the address of the client. 367 */ 368 public String getClientAddress() 369 { 370 return clientAddress; 371 } 372 373 374 375 /** 376 * Retrieves the port number for this connection on the client system. 377 * 378 * @return The port number for this connection on the client system. 379 */ 380 public int getClientPort() 381 { 382 return clientPort; 383 } 384 385 386 387 /** 388 * Retrieves the address and port of the client system, separated by a colon. 389 * 390 * @return The address and port of the client system, separated by a colon. 391 */ 392 public String getClientHostPort() 393 { 394 return clientAddress + ":" + clientPort; 395 } 396 397 398 399 /** 400 * Retrieves a string representation of the address on the server to which the 401 * client connected. 402 * 403 * @return A string representation of the address on the server to which the 404 * client connected. 405 */ 406 public String getServerAddress() 407 { 408 return serverAddress; 409 } 410 411 412 413 /** 414 * Retrieves the port number for this connection on the server system. 415 * 416 * @return The port number for this connection on the server system. 417 */ 418 public int getServerPort() 419 { 420 return serverPort; 421 } 422 423 424 425 /** 426 * Retrieves the address and port of the server system, separated by a colon. 427 * 428 * @return The address and port of the server system, separated by a colon. 429 */ 430 public String getServerHostPort() 431 { 432 return serverAddress + ":" + serverPort; 433 } 434 435 436 437 /** 438 * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the remote 439 * client system. 440 * 441 * @return The <CODE>java.net.InetAddress</CODE> associated with the remote 442 * client system. It may be <CODE>null</CODE> if the client is not 443 * connected over an IP-based connection. 444 */ 445 public InetAddress getRemoteAddress() 446 { 447 return clientChannel.socket().getInetAddress(); 448 } 449 450 451 452 /** 453 * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory Server 454 * system to which the client has established the connection. 455 * 456 * @return The <CODE>java.net.InetAddress</CODE> for the Directory Server 457 * system to which the client has established the connection. It may 458 * be <CODE>null</CODE> if the client is not connected over an 459 * IP-based connection. 460 */ 461 public InetAddress getLocalAddress() 462 { 463 return clientChannel.socket().getLocalAddress(); 464 } 465 466 467 468 /** 469 * Indicates whether this client connection is currently using a secure 470 * mechanism to communicate with the server. Note that this may change over 471 * time based on operations performed by the client or server (e.g., it may go 472 * from <CODE>false</CODE> to <CODE>true</CODE> if the client uses the 473 * StartTLS extended operation). 474 * 475 * @return <CODE>true</CODE> if the client connection is currently using a 476 * secure mechanism to communicate with the server, or 477 * <CODE>false</CODE> if not. 478 */ 479 public boolean isSecure() 480 { 481 return securityProvider.isSecure(); 482 } 483 484 485 486 /** 487 * Retrieves the connection security provider for this client connection. 488 * 489 * @return The connection security provider for this client connection. 490 */ 491 public ConnectionSecurityProvider getConnectionSecurityProvider() 492 { 493 return securityProvider; 494 } 495 496 497 498 /** 499 * Specifies the connection security provider for this client connection. 500 * 501 * @param securityProvider The connection security provider to use for 502 * communication on this client connection. 503 */ 504 public void setConnectionSecurityProvider(ConnectionSecurityProvider 505 securityProvider) 506 { 507 this.securityProvider = securityProvider; 508 509 if (securityProvider.isSecure()) 510 { 511 protocol = "LDAP+" + securityProvider.getSecurityMechanismName(); 512 } 513 else 514 { 515 protocol = "LDAP"; 516 } 517 } 518 519 520 521 /** 522 * Retrieves the human-readable name of the security mechanism that is used to 523 * protect communication with this client. 524 * 525 * @return The human-readable name of the security mechanism that is used to 526 * protect communication with this client, or <CODE>null</CODE> if no 527 * security is in place. 528 */ 529 public String getSecurityMechanism() 530 { 531 return securityProvider.getSecurityMechanismName(); 532 } 533 534 535 536 /** 537 * Retrieves the next operation ID that should be used for this connection. 538 * 539 * @return The next operation ID that should be used for this connection. 540 */ 541 public long nextOperationID() 542 { 543 return nextOperationID.getAndIncrement(); 544 } 545 546 547 548 /** 549 * Sends a response to the client based on the information in the provided 550 * operation. 551 * 552 * @param operation The operation for which to send the response. 553 */ 554 public void sendResponse(Operation operation) 555 { 556 // Since this is the final response for this operation, we can go ahead and 557 // remove it from the "operations in progress" list. It can't be canceled 558 // after this point, and this will avoid potential race conditions in which 559 // the client immediately sends another request with the same message ID as 560 // was used for this operation. 561 removeOperationInProgress(operation.getMessageID()); 562 563 LDAPMessage message = operationToResponseLDAPMessage(operation); 564 if (message != null) 565 { 566 sendLDAPMessage(securityProvider, message); 567 } 568 } 569 570 571 572 /** 573 * Retrieves an LDAPMessage containing a response generated from the provided 574 * operation. 575 * 576 * @param operation The operation to use to generate the response 577 * LDAPMessage. 578 * 579 * @return An LDAPMessage containing a response generated from the provided 580 * operation. 581 */ 582 private LDAPMessage operationToResponseLDAPMessage(Operation operation) 583 { 584 ResultCode resultCode = operation.getResultCode(); 585 if (resultCode == null) 586 { 587 // This must mean that the operation has either not yet completed or that 588 // it completed without a result for some reason. In any case, log a 589 // message and set the response to "operations error". 590 logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE. 591 get(operation.getOperationType().toString(), 592 operation.getConnectionID(), operation.getOperationID())); 593 resultCode = DirectoryServer.getServerErrorResultCode(); 594 } 595 596 597 MessageBuilder errorMessage = operation.getErrorMessage(); 598 DN matchedDN = operation.getMatchedDN(); 599 600 601 // Referrals are not allowed for LDAPv2 clients. 602 List<String> referralURLs; 603 if (ldapVersion == 2) 604 { 605 referralURLs = null; 606 607 if (resultCode == ResultCode.REFERRAL) 608 { 609 resultCode = ResultCode.CONSTRAINT_VIOLATION; 610 errorMessage.append(ERR_LDAPV2_REFERRAL_RESULT_CHANGED.get()); 611 } 612 613 List<String> opReferrals = operation.getReferralURLs(); 614 if ((opReferrals != null) && (! opReferrals.isEmpty())) 615 { 616 StringBuilder referralsStr = new StringBuilder(); 617 Iterator<String> iterator = opReferrals.iterator(); 618 referralsStr.append(iterator.next()); 619 620 while (iterator.hasNext()) 621 { 622 referralsStr.append(", "); 623 referralsStr.append(iterator.next()); 624 } 625 626 errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get( 627 String.valueOf(referralsStr))); 628 } 629 } 630 else 631 { 632 referralURLs = operation.getReferralURLs(); 633 } 634 635 ProtocolOp protocolOp; 636 switch (operation.getOperationType()) 637 { 638 case ADD: 639 protocolOp = new AddResponseProtocolOp(resultCode.getIntValue(), 640 errorMessage.toMessage(), 641 matchedDN, referralURLs); 642 break; 643 case BIND: 644 ASN1OctetString serverSASLCredentials = 645 ((BindOperationBasis) operation).getServerSASLCredentials(); 646 protocolOp = new BindResponseProtocolOp(resultCode.getIntValue(), 647 errorMessage.toMessage(), matchedDN, 648 referralURLs, serverSASLCredentials); 649 break; 650 case COMPARE: 651 protocolOp = new CompareResponseProtocolOp(resultCode.getIntValue(), 652 errorMessage.toMessage(), 653 matchedDN, referralURLs); 654 break; 655 case DELETE: 656 protocolOp = new DeleteResponseProtocolOp(resultCode.getIntValue(), 657 errorMessage.toMessage(), 658 matchedDN, referralURLs); 659 break; 660 case EXTENDED: 661 // If this an LDAPv2 client, then we can't send this. 662 if (ldapVersion == 2) 663 { 664 logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get( 665 getConnectionID(), operation.getOperationID(), 666 String.valueOf(operation))); 667 return null; 668 } 669 670 ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation; 671 protocolOp = new ExtendedResponseProtocolOp(resultCode.getIntValue(), 672 errorMessage.toMessage(), matchedDN, referralURLs, 673 extOp.getResponseOID(), extOp.getResponseValue()); 674 break; 675 case MODIFY: 676 protocolOp = new ModifyResponseProtocolOp(resultCode.getIntValue(), 677 errorMessage.toMessage(), 678 matchedDN, referralURLs); 679 break; 680 case MODIFY_DN: 681 protocolOp = new ModifyDNResponseProtocolOp(resultCode.getIntValue(), 682 errorMessage.toMessage(), 683 matchedDN, referralURLs); 684 break; 685 case SEARCH: 686 protocolOp = new SearchResultDoneProtocolOp(resultCode.getIntValue(), 687 errorMessage.toMessage(), 688 matchedDN, referralURLs); 689 break; 690 default: 691 // This must be a type of operation that doesn't have a response. This 692 // shouldn't happen, so log a message and return. 693 logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get( 694 String.valueOf(operation.getOperationType()), 695 getConnectionID(), 696 operation.getOperationID(), 697 String.valueOf(operation))); 698 return null; 699 } 700 701 702 // Controls are not allowed for LDAPv2 clients. 703 ArrayList<LDAPControl> controls; 704 if (ldapVersion == 2) 705 { 706 controls = null; 707 } 708 else 709 { 710 List<Control> responseControls = operation.getResponseControls(); 711 if ((responseControls == null) || responseControls.isEmpty()) 712 { 713 controls = null; 714 } 715 else 716 { 717 controls = new ArrayList<LDAPControl>(responseControls.size()); 718 for (Control c : responseControls) 719 { 720 controls.add(new LDAPControl(c)); 721 } 722 } 723 } 724 725 return new LDAPMessage(operation.getMessageID(), protocolOp, controls); 726 } 727 728 729 730 /** 731 * Sends the provided search result entry to the client. 732 * 733 * @param searchOperation The search operation with which the entry is 734 * associated. 735 * @param searchEntry The search result entry to be sent to the client. 736 */ 737 public void sendSearchEntry(SearchOperation searchOperation, 738 SearchResultEntry searchEntry) 739 { 740 SearchResultEntryProtocolOp protocolOp = 741 new SearchResultEntryProtocolOp(searchEntry); 742 743 List<Control> entryControls = searchEntry.getControls(); 744 ArrayList<LDAPControl> controls; 745 if ((entryControls == null) || entryControls.isEmpty()) 746 { 747 controls = null; 748 } 749 else 750 { 751 controls = new ArrayList<LDAPControl>(entryControls.size()); 752 for (Control c : entryControls) 753 { 754 controls.add(new LDAPControl(c)); 755 } 756 } 757 758 sendLDAPMessage(securityProvider, 759 new LDAPMessage(searchOperation.getMessageID(), protocolOp, 760 controls)); 761 } 762 763 764 765 /** 766 * Sends the provided search result reference to the client. 767 * 768 * @param searchOperation The search operation with which the reference is 769 * associated. 770 * @param searchReference The search result reference to be sent to the 771 * client. 772 * 773 * @return <CODE>true</CODE> if the client is able to accept referrals, or 774 * <CODE>false</CODE> if the client cannot handle referrals and no 775 * more attempts should be made to send them for the associated 776 * search operation. 777 */ 778 public boolean sendSearchReference(SearchOperation searchOperation, 779 SearchResultReference searchReference) 780 { 781 // Make sure this is not an LDAPv2 client. If it is, then they can't see 782 // referrals so we'll not send anything. Also, throw an exception so that 783 // the core server will know not to try sending any more referrals to this 784 // client for the rest of the operation. 785 if (ldapVersion == 2) 786 { 787 Message message = ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE. 788 get(getConnectionID(), searchOperation.getOperationID(), 789 String.valueOf(searchReference)); 790 logError(message); 791 return false; 792 } 793 794 SearchResultReferenceProtocolOp protocolOp = 795 new SearchResultReferenceProtocolOp(searchReference); 796 797 List<Control> referenceControls = searchReference.getControls(); 798 ArrayList<LDAPControl> controls; 799 if ((referenceControls == null) || referenceControls.isEmpty()) 800 { 801 controls = null; 802 } 803 else 804 { 805 controls = new ArrayList<LDAPControl>(referenceControls.size()); 806 for (Control c : referenceControls) 807 { 808 controls.add(new LDAPControl(c)); 809 } 810 } 811 812 sendLDAPMessage(securityProvider, 813 new LDAPMessage(searchOperation.getMessageID(), protocolOp, 814 controls)); 815 return true; 816 } 817 818 819 820 821 /** 822 * Sends the provided intermediate response message to the client. 823 * 824 * @param intermediateResponse The intermediate response message to be sent. 825 * 826 * @return <CODE>true</CODE> if processing on the associated operation should 827 * continue, or <CODE>false</CODE> if not. 828 */ 829 protected boolean sendIntermediateResponseMessage( 830 IntermediateResponse intermediateResponse) 831 { 832 IntermediateResponseProtocolOp protocolOp = 833 new IntermediateResponseProtocolOp(intermediateResponse.getOID(), 834 intermediateResponse.getValue()); 835 836 Operation operation = intermediateResponse.getOperation(); 837 838 List<Control> controls = intermediateResponse.getControls(); 839 ArrayList<LDAPControl> ldapControls = 840 new ArrayList<LDAPControl>(controls.size()); 841 for (Control c : controls) 842 { 843 ldapControls.add(new LDAPControl(c)); 844 } 845 846 847 LDAPMessage message = new LDAPMessage(operation.getMessageID(), protocolOp, 848 ldapControls); 849 sendLDAPMessage(securityProvider, message); 850 851 852 // The only reason we shouldn't continue processing is if the connection is 853 // closed. 854 return connectionValid; 855 } 856 857 858 859 /** 860 * Sends the provided LDAP message to the client. 861 * 862 * @param secProvider The connection security provider to use to handle any 863 * necessary security translation. 864 * @param message The LDAP message to send to the client. 865 */ 866 public void sendLDAPMessage(ConnectionSecurityProvider secProvider, 867 LDAPMessage message) 868 { 869 ASN1Element messageElement = message.encode(); 870 871 ByteBuffer messageBuffer = ByteBuffer.wrap(messageElement.encode()); 872 873 874 // Make sure that we can only send one message at a time. This locking will 875 // not have any impact on the ability to read requests from the client. 876 synchronized (transmitLock) 877 { 878 try 879 { 880 try 881 { 882 int bytesWritten = messageBuffer.limit() - messageBuffer.position(); 883 if (! secProvider.writeData(messageBuffer)) 884 { 885 return; 886 } 887 888 TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, message); 889 TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, messageElement); 890 891 messageBuffer.rewind(); 892 if (debugEnabled()) 893 { 894 TRACER.debugData(DebugLogLevel.VERBOSE, messageBuffer); 895 } 896 897 if (keepStats) 898 { 899 statTracker.updateMessageWritten(message, bytesWritten); 900 } 901 } 902 catch (@Deprecated Exception e) 903 { 904 if (debugEnabled()) 905 { 906 TRACER.debugCaught(DebugLogLevel.ERROR, e); 907 } 908 909 // We were unable to send the message due to some other internal 910 // problem. Disconnect from the client and return. 911 disconnect(DisconnectReason.SERVER_ERROR, true, null); 912 return; 913 } 914 } 915 catch (Exception e) 916 { 917 if (debugEnabled()) 918 { 919 TRACER.debugCaught(DebugLogLevel.ERROR, e); 920 } 921 922 // FIXME -- Log a message or something 923 disconnect(DisconnectReason.SERVER_ERROR, true, null); 924 return; 925 } 926 } 927 } 928 929 930 931 /** 932 * Closes the connection to the client, optionally sending it a message 933 * indicating the reason for the closure. Note that the ability to send a 934 * notice of disconnection may not be available for all protocols or under all 935 * circumstances. 936 * 937 * @param disconnectReason The disconnect reason that provides the generic 938 * cause for the disconnect. 939 * @param sendNotification Indicates whether to try to provide notification 940 * to the client that the connection will be closed. 941 * @param message The message to include in the disconnect 942 * notification response. It may be 943 * <CODE>null</CODE> if no message is to be sent. 944 */ 945 public void disconnect(DisconnectReason disconnectReason, 946 boolean sendNotification, 947 Message message) 948 { 949 // Set a flag indicating that the connection is being terminated so that no 950 // new requests will be accepted. Also cancel all operations in progress. 951 synchronized (opsInProgressLock) 952 { 953 // If we are already in the middle of a disconnect, then don't 954 // do anything. 955 if (disconnectRequested) 956 { 957 return; 958 } 959 960 disconnectRequested = true; 961 } 962 963 964 if (keepStats) 965 { 966 statTracker.updateDisconnect(); 967 } 968 969 if (connectionID >= 0) 970 { 971 DirectoryServer.connectionClosed(this); 972 } 973 974 975 // Indicate that this connection is no longer valid. 976 connectionValid = false; 977 978 MessageBuilder msgBuilder = new MessageBuilder(); 979 msgBuilder.append(disconnectReason.getClosureMessage()); 980 msgBuilder.append(": "); 981 msgBuilder.append(message); 982 cancelAllOperations(new CancelRequest(true, msgBuilder.toMessage())); 983 finalizeConnectionInternal(); 984 985 986 // If there is a write selector for this connection, then close it. 987 Selector selector = writeSelector.get(); 988 if (selector != null) 989 { 990 try 991 { 992 selector.close(); 993 } catch (Exception e) {} 994 } 995 996 997 // See if we should send a notification to the client. If so, then 998 // construct and send a notice of disconnection unsolicited response. 999 // Note that we cannot send this notification to an LDAPv2 client. 1000 if (sendNotification && (ldapVersion != 2)) 1001 { 1002 try 1003 { 1004 int resultCode; 1005 switch (disconnectReason) 1006 { 1007 case PROTOCOL_ERROR: 1008 resultCode = LDAPResultCode.PROTOCOL_ERROR; 1009 break; 1010 case SERVER_SHUTDOWN: 1011 resultCode = LDAPResultCode.UNAVAILABLE; 1012 break; 1013 case SERVER_ERROR: 1014 resultCode = 1015 DirectoryServer.getServerErrorResultCode().getIntValue(); 1016 break; 1017 case ADMIN_LIMIT_EXCEEDED: 1018 case IDLE_TIME_LIMIT_EXCEEDED: 1019 case MAX_REQUEST_SIZE_EXCEEDED: 1020 case IO_TIMEOUT: 1021 resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED; 1022 break; 1023 case CONNECTION_REJECTED: 1024 resultCode = LDAPResultCode.CONSTRAINT_VIOLATION; 1025 break; 1026 default: 1027 resultCode = LDAPResultCode.OTHER; 1028 break; 1029 } 1030 1031 1032 Message errMsg; 1033 if (message == null) 1034 { 1035 errMsg = INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get(); 1036 } 1037 else 1038 { 1039 errMsg = message; 1040 } 1041 1042 1043 ExtendedResponseProtocolOp notificationOp = 1044 new ExtendedResponseProtocolOp(resultCode, errMsg, null, null, 1045 OID_NOTICE_OF_DISCONNECTION, null); 1046 byte[] messageBytes = 1047 new LDAPMessage(0, notificationOp, null).encode().encode(); 1048 ByteBuffer buffer = ByteBuffer.wrap(messageBytes); 1049 try 1050 { 1051 securityProvider.writeData(buffer); 1052 } catch (Exception e) {} 1053 } 1054 catch (Exception e) 1055 { 1056 // NYI -- Log a message indicating that we couldn't send the notice of 1057 // disconnection. 1058 } 1059 } 1060 1061 1062 // Close the connection to the client. 1063 try 1064 { 1065 securityProvider.disconnect(sendNotification); 1066 } 1067 catch (Exception e) 1068 { 1069 // In general, we don't care about any exception that might be thrown 1070 // here. 1071 if (debugEnabled()) 1072 { 1073 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1074 } 1075 } 1076 1077 try 1078 { 1079 clientChannel.close(); 1080 } 1081 catch (Exception e) 1082 { 1083 // In general, we don't care about any exception that might be thrown 1084 // here. 1085 if (debugEnabled()) 1086 { 1087 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1088 } 1089 } 1090 1091 1092 // NYI -- Deregister the client connection from any server components that 1093 // might know about it. 1094 1095 1096 // Log a disconnect message. 1097 logDisconnect(this, disconnectReason, message); 1098 1099 1100 try 1101 { 1102 PluginConfigManager pluginManager = 1103 DirectoryServer.getPluginConfigManager(); 1104 pluginManager.invokePostDisconnectPlugins(this, disconnectReason, 1105 message); 1106 } 1107 catch (Exception e) 1108 { 1109 if (debugEnabled()) 1110 { 1111 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1112 } 1113 } 1114 } 1115 1116 1117 1118 /** 1119 * Retrieves the set of operations in progress for this client connection. 1120 * This list must not be altered by any caller. 1121 * 1122 * @return The set of operations in progress for this client connection. 1123 */ 1124 public Collection<AbstractOperation> getOperationsInProgress() 1125 { 1126 return operationsInProgress.values(); 1127 } 1128 1129 1130 1131 /** 1132 * Retrieves the operation in progress with the specified message ID. 1133 * 1134 * @param messageID The message ID for the operation to retrieve. 1135 * 1136 * @return The operation in progress with the specified message ID, or 1137 * <CODE>null</CODE> if no such operation could be found. 1138 */ 1139 public AbstractOperation getOperationInProgress(int messageID) 1140 { 1141 return operationsInProgress.get(messageID); 1142 } 1143 1144 1145 1146 /** 1147 * Adds the provided operation to the set of operations in progress for this 1148 * client connection. 1149 * 1150 * @param operation The operation to add to the set of operations in 1151 * progress for this client connection. 1152 * 1153 * @throws DirectoryException If the operation is not added for some reason 1154 * (e.g., the client already has reached the 1155 * maximum allowed concurrent requests). 1156 */ 1157 public void addOperationInProgress(AbstractOperation operation) 1158 throws DirectoryException 1159 { 1160 int messageID = operation.getMessageID(); 1161 1162 // We need to grab a lock to ensure that no one else can add operations to 1163 // the queue while we are performing some preliminary checks. 1164 synchronized (opsInProgressLock) 1165 { 1166 try 1167 { 1168 // If we're already in the process of disconnecting the client, then 1169 // reject the operation. 1170 if (disconnectRequested) 1171 { 1172 Message message = WARN_LDAP_CLIENT_DISCONNECT_IN_PROGRESS.get(); 1173 throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, 1174 message); 1175 } 1176 1177 1178 // See if there is already an operation in progress with the same 1179 // message ID. If so, then we can't allow it. 1180 AbstractOperation op = operationsInProgress.get(messageID); 1181 if (op != null) 1182 { 1183 Message message = 1184 WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID); 1185 throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); 1186 } 1187 1188 1189 // Add the operation to the list of operations in progress for this 1190 // connection. 1191 operationsInProgress.put(messageID, operation); 1192 1193 1194 // Try to add the operation to the work queue. 1195 DirectoryServer.enqueueRequest(operation); 1196 } 1197 catch (DirectoryException de) 1198 { 1199 if (debugEnabled()) 1200 { 1201 TRACER.debugCaught(DebugLogLevel.ERROR, de); 1202 } 1203 1204 operationsInProgress.remove(messageID); 1205 lastCompletionTime.set(TimeThread.getTime()); 1206 1207 throw de; 1208 } 1209 catch (Exception e) 1210 { 1211 if (debugEnabled()) 1212 { 1213 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1214 } 1215 1216 Message message = 1217 WARN_LDAP_CLIENT_CANNOT_ENQUEUE.get(getExceptionMessage(e)); 1218 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 1219 message, e); 1220 } 1221 } 1222 } 1223 1224 1225 1226 /** 1227 * Removes the provided operation from the set of operations in progress for 1228 * this client connection. Note that this does not make any attempt to 1229 * cancel any processing that may already be in progress for the operation. 1230 * 1231 * @param messageID The message ID of the operation to remove from the set 1232 * of operations in progress. 1233 * 1234 * @return <CODE>true</CODE> if the operation was found and removed from the 1235 * set of operations in progress, or <CODE>false</CODE> if not. 1236 */ 1237 public boolean removeOperationInProgress(int messageID) 1238 { 1239 AbstractOperation operation = operationsInProgress.remove(messageID); 1240 if (operation == null) 1241 { 1242 return false; 1243 } 1244 else 1245 { 1246 lastCompletionTime.set(TimeThread.getTime()); 1247 return true; 1248 } 1249 } 1250 1251 1252 1253 /** 1254 * Attempts to cancel the specified operation. 1255 * 1256 * @param messageID The message ID of the operation to cancel. 1257 * @param cancelRequest An object providing additional information about how 1258 * the cancel should be processed. 1259 * 1260 * @return A cancel result that either indicates that the cancel was 1261 * successful or provides a reason that it was not. 1262 */ 1263 public CancelResult cancelOperation(int messageID, 1264 CancelRequest cancelRequest) 1265 { 1266 AbstractOperation op = operationsInProgress.get(messageID); 1267 if (op == null) 1268 { 1269 // See if the operation is in the list of persistent searches. 1270 for (PersistentSearch ps : getPersistentSearches()) 1271 { 1272 if (ps.getSearchOperation().getMessageID() == messageID) 1273 { 1274 CancelResult cancelResult = 1275 ps.getSearchOperation().cancel(cancelRequest); 1276 1277 if (keepStats && (cancelResult.getResultCode() == 1278 ResultCode.CANCELED)) 1279 { 1280 statTracker.updateAbandonedOperation(); 1281 } 1282 1283 return cancelResult; 1284 } 1285 } 1286 1287 return new CancelResult(ResultCode.NO_SUCH_OPERATION, null); 1288 } 1289 else 1290 { 1291 CancelResult cancelResult = op.cancel(cancelRequest); 1292 if (keepStats && (cancelResult.getResultCode() == ResultCode.CANCELED)) 1293 { 1294 statTracker.updateAbandonedOperation(); 1295 } 1296 1297 return op.cancel(cancelRequest); 1298 } 1299 } 1300 1301 1302 1303 /** 1304 * Attempts to cancel all operations in progress on this connection. 1305 * 1306 * @param cancelRequest An object providing additional information about how 1307 * the cancel should be processed. 1308 */ 1309 public void cancelAllOperations(CancelRequest cancelRequest) 1310 { 1311 // Make sure that no one can add any new operations. 1312 synchronized (opsInProgressLock) 1313 { 1314 try 1315 { 1316 for (AbstractOperation o : operationsInProgress.values()) 1317 { 1318 try 1319 { 1320 o.abort(cancelRequest); 1321 1322 // TODO: Assume its cancelled? 1323 if (keepStats) 1324 { 1325 statTracker.updateAbandonedOperation(); 1326 } 1327 } 1328 catch (Exception e) 1329 { 1330 if (debugEnabled()) 1331 { 1332 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1333 } 1334 } 1335 } 1336 1337 if (! (operationsInProgress.isEmpty() && 1338 getPersistentSearches().isEmpty())) 1339 { 1340 lastCompletionTime.set(TimeThread.getTime()); 1341 } 1342 1343 operationsInProgress.clear(); 1344 1345 1346 for (PersistentSearch persistentSearch : getPersistentSearches()) 1347 { 1348 DirectoryServer.deregisterPersistentSearch(persistentSearch); 1349 } 1350 } 1351 catch (Exception e) 1352 { 1353 if (debugEnabled()) 1354 { 1355 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1356 } 1357 } 1358 } 1359 } 1360 1361 1362 1363 /** 1364 * Attempts to cancel all operations in progress on this connection except the 1365 * operation with the specified message ID. 1366 * 1367 * @param cancelRequest An object providing additional information about how 1368 * the cancel should be processed. 1369 * @param messageID The message ID of the operation that should not be 1370 * canceled. 1371 */ 1372 public void cancelAllOperationsExcept(CancelRequest cancelRequest, 1373 int messageID) 1374 { 1375 // Make sure that no one can add any new operations. 1376 synchronized (opsInProgressLock) 1377 { 1378 try 1379 { 1380 for (int msgID : operationsInProgress.keySet()) 1381 { 1382 if (msgID == messageID) 1383 { 1384 continue; 1385 } 1386 1387 AbstractOperation o = operationsInProgress.get(msgID); 1388 if (o != null) 1389 { 1390 try 1391 { 1392 o.abort(cancelRequest); 1393 1394 // TODO: Assume its cancelled? 1395 if (keepStats) 1396 { 1397 statTracker.updateAbandonedOperation(); 1398 } 1399 } 1400 catch (Exception e) 1401 { 1402 if (debugEnabled()) 1403 { 1404 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1405 } 1406 } 1407 } 1408 1409 operationsInProgress.remove(msgID); 1410 lastCompletionTime.set(TimeThread.getTime()); 1411 } 1412 1413 1414 for (PersistentSearch persistentSearch : getPersistentSearches()) 1415 { 1416 DirectoryServer.deregisterPersistentSearch(persistentSearch); 1417 lastCompletionTime.set(TimeThread.getTime()); 1418 } 1419 } 1420 catch (Exception e) 1421 { 1422 if (debugEnabled()) 1423 { 1424 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1425 } 1426 } 1427 } 1428 } 1429 1430 1431 1432 /** 1433 * {@inheritDoc} 1434 */ 1435 @Override() 1436 public Selector getWriteSelector() 1437 { 1438 Selector selector = writeSelector.get(); 1439 if (selector == null) 1440 { 1441 try 1442 { 1443 selector = Selector.open(); 1444 if (! writeSelector.compareAndSet(null, selector)) 1445 { 1446 selector.close(); 1447 selector = writeSelector.get(); 1448 } 1449 } 1450 catch (Exception e) 1451 { 1452 if (debugEnabled()) 1453 { 1454 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1455 } 1456 } 1457 } 1458 1459 return selector; 1460 } 1461 1462 1463 1464 /** 1465 * {@inheritDoc} 1466 */ 1467 @Override() 1468 public long getMaxBlockedWriteTimeLimit() 1469 { 1470 return connectionHandler.getMaxBlockedWriteTimeLimit(); 1471 } 1472 1473 1474 1475 /** 1476 * Process the information contained in the provided byte buffer as an ASN.1 1477 * element. It may take several calls to this method in order to get all the 1478 * information necessary to decode a single ASN.1 element, but it may also be 1479 * possible that there are multiple elements (or at least fragments of 1480 * multiple elements) in a single buffer. This will fully process whatever 1481 * the client provided and set up the appropriate state information to make it 1482 * possible to pick up in the right place the next time around. 1483 * 1484 * @param buffer The buffer containing the data to be processed. It must be 1485 * ready for reading (i.e., it should have been flipped by the 1486 * caller), and the data provided must be unencrypted (e.g., 1487 * if the client is communicating over SSL, then the 1488 * decryption should happen before calling this method). 1489 * 1490 * @return <CODE>true</CODE> if all the data in the provided buffer was 1491 * processed and the client connection can remain established, or 1492 * <CODE>false</CODE> if a decoding error occurred and requests from 1493 * this client should no longer be processed. Note that if this 1494 * method does return <CODE>false</CODE>, then it must have already 1495 * disconnected the client, and upon returning the request handler 1496 * should remove it from the associated selector. 1497 */ 1498 public boolean processDataRead(ByteBuffer buffer) 1499 { 1500 if (debugEnabled()) 1501 { 1502 TRACER.debugData(DebugLogLevel.VERBOSE, buffer); 1503 } 1504 1505 1506 int bytesAvailable = buffer.limit() - buffer.position(); 1507 1508 if (keepStats) 1509 { 1510 statTracker.updateBytesRead(bytesAvailable); 1511 } 1512 1513 while (bytesAvailable > 0) 1514 { 1515 switch (elementReadState) 1516 { 1517 case ELEMENT_READ_STATE_NEED_TYPE: 1518 // Read just the type and then loop again to see if there is more. 1519 elementType = buffer.get(); 1520 bytesAvailable--; 1521 elementReadState = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; 1522 continue; 1523 1524 1525 case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE: 1526 // Get the first length byte and see if it is a single-byte or 1527 // multi-byte length. 1528 byte firstLengthByte = buffer.get(); 1529 bytesAvailable--; 1530 elementLengthBytesNeeded = (firstLengthByte & 0x7F); 1531 if (elementLengthBytesNeeded == firstLengthByte) 1532 { 1533 elementLength = firstLengthByte; 1534 1535 // If the length is zero, then it cannot be a valid LDAP message. 1536 if (elementLength == 0) 1537 { 1538 disconnect(DisconnectReason.PROTOCOL_ERROR, true, 1539 ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); 1540 return false; 1541 } 1542 1543 // Make sure that the element is not larger than the maximum allowed 1544 // message size. 1545 if ((connectionHandler.getMaxRequestSize() > 0) && 1546 (elementLength > connectionHandler.getMaxRequestSize())) 1547 { 1548 Message m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( 1549 elementLength, connectionHandler.getMaxRequestSize()); 1550 disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, m); 1551 return false; 1552 } 1553 1554 elementValue = new byte[elementLength]; 1555 elementValueBytesRead = 0; 1556 elementValueBytesNeeded = elementLength; 1557 elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; 1558 continue; 1559 } 1560 else 1561 { 1562 if (elementLengthBytesNeeded > 4) 1563 { 1564 // We cannot handle multi-byte lengths in which more than four 1565 // bytes are used to encode the length. 1566 Message m = ERR_LDAP_CLIENT_DECODE_INVALID_MULTIBYTE_LENGTH.get( 1567 elementLengthBytesNeeded); 1568 disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); 1569 return false; 1570 } 1571 1572 elementLength = 0x00; 1573 if (elementLengthBytesNeeded <= bytesAvailable) 1574 { 1575 // We can read the entire length, so do it. 1576 while (elementLengthBytesNeeded > 0) 1577 { 1578 elementLength = (elementLength << 8) | (buffer.get() & 0xFF); 1579 bytesAvailable--; 1580 elementLengthBytesNeeded--; 1581 } 1582 1583 // If the length is zero, then it cannot be a valid LDAP message. 1584 if (elementLength == 0) 1585 { 1586 disconnect(DisconnectReason.PROTOCOL_ERROR, true, 1587 ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); 1588 return false; 1589 } 1590 1591 // Make sure that the element is not larger than the maximum 1592 // allowed message size. 1593 if ((connectionHandler.getMaxRequestSize() > 0) && 1594 (elementLength > connectionHandler.getMaxRequestSize())) 1595 { 1596 disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, 1597 ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( 1598 elementLength, 1599 connectionHandler.getMaxRequestSize())); 1600 return false; 1601 } 1602 1603 elementValue = new byte[elementLength]; 1604 elementValueBytesRead = 0; 1605 elementValueBytesNeeded = elementLength; 1606 elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; 1607 continue; 1608 } 1609 else 1610 { 1611 // We can't read the entire length, so just read what is 1612 // available. 1613 while (bytesAvailable > 0) 1614 { 1615 elementLength = (elementLength << 8) | (buffer.get() & 0xFF); 1616 bytesAvailable--; 1617 elementLengthBytesNeeded--; 1618 } 1619 1620 return true; 1621 } 1622 } 1623 1624 1625 case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES: 1626 if (bytesAvailable >= elementLengthBytesNeeded) 1627 { 1628 // We have enough data available to be able to read the entire 1629 // length. Do so. 1630 while (elementLengthBytesNeeded > 0) 1631 { 1632 elementLength = (elementLength << 8) | (buffer.get() & 0xFF); 1633 bytesAvailable--; 1634 elementLengthBytesNeeded--; 1635 } 1636 1637 // If the length is zero, then it cannot be a valid LDAP message. 1638 if (elementLength == 0) 1639 { 1640 disconnect(DisconnectReason.PROTOCOL_ERROR, true, 1641 ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); 1642 return false; 1643 } 1644 1645 // Make sure that the element is not larger than the maximum allowed 1646 // message size. 1647 if ((connectionHandler.getMaxRequestSize() > 0) && 1648 (elementLength > connectionHandler.getMaxRequestSize())) 1649 { 1650 disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, 1651 ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( 1652 elementLength, 1653 connectionHandler.getMaxRequestSize())); 1654 return false; 1655 } 1656 1657 elementValue = new byte[elementLength]; 1658 elementValueBytesRead = 0; 1659 elementValueBytesNeeded = elementLength; 1660 elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; 1661 continue; 1662 } 1663 else 1664 { 1665 // We still don't have enough data to complete the length, so just 1666 // read as much as possible. 1667 while (bytesAvailable > 0) 1668 { 1669 elementLength = (elementLength << 8) | (buffer.get() & 0xFF); 1670 bytesAvailable--; 1671 elementLengthBytesNeeded--; 1672 } 1673 1674 return true; 1675 } 1676 1677 1678 case ELEMENT_READ_STATE_NEED_VALUE_BYTES: 1679 if (bytesAvailable >= elementValueBytesNeeded) 1680 { 1681 // We have enough data available to fully read the value. Finish 1682 // reading the information and convert it to an ASN.1 element. Then 1683 // decode that as an LDAP message. 1684 buffer.get(elementValue, elementValueBytesRead, 1685 elementValueBytesNeeded); 1686 elementValueBytesRead += elementValueBytesNeeded; 1687 bytesAvailable -= elementValueBytesNeeded; 1688 elementReadState = ELEMENT_READ_STATE_NEED_TYPE; 1689 1690 ASN1Sequence requestSequence; 1691 try 1692 { 1693 requestSequence = ASN1Sequence.decodeAsSequence(elementType, 1694 elementValue); 1695 TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, 1696 requestSequence); 1697 } 1698 catch (Exception e) 1699 { 1700 if (debugEnabled()) 1701 { 1702 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1703 } 1704 Message m = ERR_LDAP_CLIENT_DECODE_ASN1_FAILED.get( 1705 String.valueOf(e)); 1706 disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); 1707 return false; 1708 } 1709 1710 LDAPMessage requestMessage; 1711 try 1712 { 1713 requestMessage = LDAPMessage.decode(requestSequence); 1714 TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, 1715 requestMessage); 1716 } 1717 catch (Exception e) 1718 { 1719 if (debugEnabled()) 1720 { 1721 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1722 } 1723 Message m = ERR_LDAP_CLIENT_DECODE_LDAP_MESSAGE_FAILED.get( 1724 String.valueOf(e)); 1725 disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); 1726 return false; 1727 } 1728 1729 if (processLDAPMessage(requestMessage)) 1730 { 1731 continue; 1732 } 1733 else 1734 { 1735 return false; 1736 } 1737 } 1738 else 1739 { 1740 // We can't read all the value, so just read as much as we have 1741 // available and pick it up again the next time around. 1742 buffer.get(elementValue, elementValueBytesRead, bytesAvailable); 1743 elementValueBytesRead += bytesAvailable; 1744 elementValueBytesNeeded -= bytesAvailable; 1745 return true; 1746 } 1747 1748 1749 default: 1750 // This should never happen. There is an invalid internal read state. 1751 // The only recourse that we have is to log a message and disconnect 1752 // the client. 1753 Message message = 1754 ERR_LDAP_CLIENT_INVALID_DECODE_STATE.get(elementReadState); 1755 logError(message); 1756 disconnect(DisconnectReason.SERVER_ERROR, true, message); 1757 return false; 1758 } 1759 } 1760 1761 1762 // If we've gotten here, then all of the data must have been processed 1763 // properly so we can return true. 1764 return true; 1765 } 1766 1767 1768 1769 /** 1770 * Processes the provided LDAP message read from the client and takes 1771 * whatever action is appropriate. For most requests, this will include 1772 * placing the operation in the work queue. Certain requests (in particular, 1773 * abandons and unbinds) will be processed directly. 1774 * 1775 * @param message The LDAP message to process. 1776 * 1777 * @return <CODE>true</CODE> if the appropriate action was taken for the 1778 * request, or <CODE>false</CODE> if there was a fatal error and 1779 * the client has been disconnected as a result, or if the client 1780 * unbound from the server. 1781 */ 1782 private boolean processLDAPMessage(LDAPMessage message) 1783 { 1784 if (keepStats) 1785 { 1786 statTracker.updateMessageRead(message); 1787 } 1788 1789 ArrayList<Control> opControls; 1790 ArrayList<LDAPControl> ldapControls = message.getControls(); 1791 if ((ldapControls == null) || ldapControls.isEmpty()) 1792 { 1793 opControls = null; 1794 } 1795 else 1796 { 1797 opControls = new ArrayList<Control>(ldapControls.size()); 1798 for (LDAPControl c : ldapControls) 1799 { 1800 opControls.add(c.getControl()); 1801 } 1802 } 1803 1804 1805 // FIXME -- See if there is a bind in progress. If so, then deny most 1806 // kinds of operations. 1807 1808 1809 // Figure out what type of operation we're dealing with based on the LDAP 1810 // message. Abandon and unbind requests will be processed here. All other 1811 // types of requests will be encapsulated into operations and put into the 1812 // work queue to be picked up by a worker thread. Any other kinds of 1813 // LDAP messages (e.g., response messages) are illegal and will result in 1814 // the connection being terminated. 1815 try 1816 { 1817 switch (message.getProtocolOpType()) 1818 { 1819 case OP_TYPE_ABANDON_REQUEST: 1820 return processAbandonRequest(message, opControls); 1821 case OP_TYPE_ADD_REQUEST: 1822 return processAddRequest(message, opControls); 1823 case OP_TYPE_BIND_REQUEST: 1824 return processBindRequest(message, opControls); 1825 case OP_TYPE_COMPARE_REQUEST: 1826 return processCompareRequest(message, opControls); 1827 case OP_TYPE_DELETE_REQUEST: 1828 return processDeleteRequest(message, opControls); 1829 case OP_TYPE_EXTENDED_REQUEST: 1830 return processExtendedRequest(message, opControls); 1831 case OP_TYPE_MODIFY_REQUEST: 1832 return processModifyRequest(message, opControls); 1833 case OP_TYPE_MODIFY_DN_REQUEST: 1834 return processModifyDNRequest(message, opControls); 1835 case OP_TYPE_SEARCH_REQUEST: 1836 return processSearchRequest(message, opControls); 1837 case OP_TYPE_UNBIND_REQUEST: 1838 return processUnbindRequest(message, opControls); 1839 default: 1840 Message msg = ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get( 1841 message.getProtocolOpName(), message.getMessageID()); 1842 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 1843 return false; 1844 } 1845 } 1846 catch (Exception e) 1847 { 1848 if (debugEnabled()) 1849 { 1850 TRACER.debugCaught(DebugLogLevel.ERROR, e); 1851 } 1852 1853 Message msg = ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get( 1854 message.getProtocolOpName(), 1855 message.getMessageID(), String.valueOf(e)); 1856 disconnect(DisconnectReason.SERVER_ERROR, true, msg); 1857 return false; 1858 } 1859 } 1860 1861 1862 1863 /** 1864 * Processes the provided LDAP message as an abandon request. 1865 * 1866 * @param message The LDAP message containing the abandon request to 1867 * process. 1868 * @param controls The set of pre-decoded request controls contained in the 1869 * message. 1870 * 1871 * @return <CODE>true</CODE> if the request was processed successfully, or 1872 * <CODE>false</CODE> if not and the connection has been closed as a 1873 * result (it is the responsibility of this method to close the 1874 * connection). 1875 */ 1876 private boolean processAbandonRequest(LDAPMessage message, 1877 ArrayList<Control> controls) 1878 { 1879 AbandonRequestProtocolOp protocolOp = message.getAbandonRequestProtocolOp(); 1880 AbandonOperationBasis abandonOp = 1881 new AbandonOperationBasis(this, nextOperationID.getAndIncrement(), 1882 message.getMessageID(), controls, 1883 protocolOp.getIDToAbandon()); 1884 1885 abandonOp.run(); 1886 if (keepStats && (abandonOp.getResultCode() == ResultCode.CANCELED)) 1887 { 1888 statTracker.updateAbandonedOperation(); 1889 } 1890 1891 return connectionValid; 1892 } 1893 1894 1895 1896 /** 1897 * Processes the provided LDAP message as an add request. 1898 * 1899 * @param message The LDAP message containing the add request to process. 1900 * @param controls The set of pre-decoded request controls contained in the 1901 * message. 1902 * 1903 * @return <CODE>true</CODE> if the request was processed successfully, or 1904 * <CODE>false</CODE> if not and the connection has been closed as a 1905 * result (it is the responsibility of this method to close the 1906 * connection). 1907 */ 1908 private boolean processAddRequest(LDAPMessage message, 1909 ArrayList<Control> controls) 1910 { 1911 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 1912 { 1913 // LDAPv2 clients aren't allowed to send controls. 1914 AddResponseProtocolOp responseOp = 1915 new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 1916 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1917 sendLDAPMessage(securityProvider, 1918 new LDAPMessage(message.getMessageID(), responseOp)); 1919 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 1920 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 1921 return false; 1922 } 1923 1924 // Create the add operation and add it into the work queue. 1925 AddRequestProtocolOp protocolOp = message.getAddRequestProtocolOp(); 1926 AddOperationBasis addOp = 1927 new AddOperationBasis(this, nextOperationID.getAndIncrement(), 1928 message.getMessageID(), controls, protocolOp.getDN(), 1929 protocolOp.getAttributes()); 1930 1931 try 1932 { 1933 addOperationInProgress(addOp); 1934 } 1935 catch (DirectoryException de) 1936 { 1937 if (debugEnabled()) 1938 { 1939 TRACER.debugCaught(DebugLogLevel.ERROR, de); 1940 } 1941 1942 AddResponseProtocolOp responseOp = 1943 new AddResponseProtocolOp(de.getResultCode().getIntValue(), 1944 de.getMessageObject(), de.getMatchedDN(), 1945 de.getReferralURLs()); 1946 1947 List<Control> responseControls = addOp.getResponseControls(); 1948 ArrayList<LDAPControl> responseLDAPControls = 1949 new ArrayList<LDAPControl>(responseControls.size()); 1950 for (Control c : responseControls) 1951 { 1952 responseLDAPControls.add(new LDAPControl(c)); 1953 } 1954 1955 sendLDAPMessage(securityProvider, 1956 new LDAPMessage(message.getMessageID(), responseOp, 1957 responseLDAPControls)); 1958 } 1959 1960 1961 return connectionValid; 1962 } 1963 1964 1965 1966 /** 1967 * Processes the provided LDAP message as a bind request. 1968 * 1969 * @param message The LDAP message containing the bind request to process. 1970 * @param controls The set of pre-decoded request controls contained in the 1971 * message. 1972 * 1973 * @return <CODE>true</CODE> if the request was processed successfully, or 1974 * <CODE>false</CODE> if not and the connection has been closed as a 1975 * result (it is the responsibility of this method to close the 1976 * connection). 1977 */ 1978 private boolean processBindRequest(LDAPMessage message, 1979 ArrayList<Control> controls) 1980 { 1981 BindRequestProtocolOp protocolOp = message.getBindRequestProtocolOp(); 1982 1983 // See if this is an LDAPv2 bind request, and if so whether that should be 1984 // allowed. 1985 String versionString; 1986 switch (ldapVersion = protocolOp.getProtocolVersion()) 1987 { 1988 case 2: 1989 versionString = "2"; 1990 1991 if (! connectionHandler.allowLDAPv2()) 1992 { 1993 BindResponseProtocolOp responseOp = 1994 new BindResponseProtocolOp( 1995 LDAPResultCode.INAPPROPRIATE_AUTHENTICATION, 1996 ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); 1997 sendLDAPMessage(securityProvider, 1998 new LDAPMessage(message.getMessageID(), responseOp)); 1999 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2000 ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); 2001 return false; 2002 } 2003 2004 if ((controls != null) && (! controls.isEmpty())) 2005 { 2006 // LDAPv2 clients aren't allowed to send controls. 2007 BindResponseProtocolOp responseOp = 2008 new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2009 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2010 sendLDAPMessage(securityProvider, 2011 new LDAPMessage(message.getMessageID(), responseOp)); 2012 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2013 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2014 return false; 2015 } 2016 2017 break; 2018 case 3: 2019 versionString = "3"; 2020 break; 2021 default: 2022 versionString = String.valueOf(ldapVersion); 2023 break; 2024 } 2025 2026 2027 ASN1OctetString bindDN = protocolOp.getDN(); 2028 2029 BindOperationBasis bindOp; 2030 switch (protocolOp.getAuthenticationType()) 2031 { 2032 case SIMPLE: 2033 bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(), 2034 message.getMessageID(), controls, 2035 versionString, bindDN, 2036 protocolOp.getSimplePassword()); 2037 break; 2038 case SASL: 2039 bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(), 2040 message.getMessageID(), controls, 2041 versionString, bindDN, 2042 protocolOp.getSASLMechanism(), 2043 protocolOp.getSASLCredentials()); 2044 break; 2045 default: 2046 // This is an invalid authentication type, and therefore a protocol 2047 // error. As per RFC 2251, a protocol error in a bind request must 2048 // result in terminating the connection. 2049 Message msg = 2050 ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(), 2051 String.valueOf(protocolOp.getAuthenticationType())); 2052 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 2053 return false; 2054 } 2055 2056 // Add the operation into the work queue. 2057 try 2058 { 2059 addOperationInProgress(bindOp); 2060 } 2061 catch (DirectoryException de) 2062 { 2063 if (debugEnabled()) 2064 { 2065 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2066 } 2067 2068 BindResponseProtocolOp responseOp = 2069 new BindResponseProtocolOp(de.getResultCode().getIntValue(), 2070 de.getMessageObject(), de.getMatchedDN(), 2071 de.getReferralURLs()); 2072 2073 List<Control> responseControls = bindOp.getResponseControls(); 2074 ArrayList<LDAPControl> responseLDAPControls = 2075 new ArrayList<LDAPControl>(responseControls.size()); 2076 for (Control c : responseControls) 2077 { 2078 responseLDAPControls.add(new LDAPControl(c)); 2079 } 2080 2081 sendLDAPMessage(securityProvider, 2082 new LDAPMessage(message.getMessageID(), responseOp, 2083 responseLDAPControls)); 2084 2085 // If it was a protocol error, then terminate the connection. 2086 if (de.getResultCode() == ResultCode.PROTOCOL_ERROR) 2087 { 2088 Message msg = ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get( 2089 message.getMessageID(), de.getMessageObject()); 2090 disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); 2091 } 2092 } 2093 2094 2095 return connectionValid; 2096 } 2097 2098 2099 2100 /** 2101 * Processes the provided LDAP message as a compare request. 2102 * 2103 * @param message The LDAP message containing the compare request to 2104 * process. 2105 * @param controls The set of pre-decoded request controls contained in the 2106 * message. 2107 * 2108 * @return <CODE>true</CODE> if the request was processed successfully, or 2109 * <CODE>false</CODE> if not and the connection has been closed as a 2110 * result (it is the responsibility of this method to close the 2111 * connection). 2112 */ 2113 private boolean processCompareRequest(LDAPMessage message, 2114 ArrayList<Control> controls) 2115 { 2116 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 2117 { 2118 // LDAPv2 clients aren't allowed to send controls. 2119 CompareResponseProtocolOp responseOp = 2120 new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2121 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2122 sendLDAPMessage(securityProvider, 2123 new LDAPMessage(message.getMessageID(), responseOp)); 2124 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2125 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2126 return false; 2127 } 2128 2129 CompareRequestProtocolOp protocolOp = message.getCompareRequestProtocolOp(); 2130 CompareOperationBasis compareOp = 2131 new CompareOperationBasis(this, nextOperationID.getAndIncrement(), 2132 message.getMessageID(), controls, 2133 protocolOp.getDN(), protocolOp.getAttributeType(), 2134 protocolOp.getAssertionValue()); 2135 2136 // Add the operation into the work queue. 2137 try 2138 { 2139 addOperationInProgress(compareOp); 2140 } 2141 catch (DirectoryException de) 2142 { 2143 if (debugEnabled()) 2144 { 2145 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2146 } 2147 2148 CompareResponseProtocolOp responseOp = 2149 new CompareResponseProtocolOp(de.getResultCode().getIntValue(), 2150 de.getMessageObject(), 2151 de.getMatchedDN(), 2152 de.getReferralURLs()); 2153 2154 List<Control> responseControls = compareOp.getResponseControls(); 2155 ArrayList<LDAPControl> responseLDAPControls = 2156 new ArrayList<LDAPControl>(responseControls.size()); 2157 for (Control c : responseControls) 2158 { 2159 responseLDAPControls.add(new LDAPControl(c)); 2160 } 2161 2162 sendLDAPMessage(securityProvider, 2163 new LDAPMessage(message.getMessageID(), responseOp, 2164 responseLDAPControls)); 2165 } 2166 2167 2168 return connectionValid; 2169 } 2170 2171 2172 2173 /** 2174 * Processes the provided LDAP message as a delete request. 2175 * 2176 * @param message The LDAP message containing the delete request to 2177 * process. 2178 * @param controls The set of pre-decoded request controls contained in the 2179 * message. 2180 * 2181 * @return <CODE>true</CODE> if the request was processed successfully, or 2182 * <CODE>false</CODE> if not and the connection has been closed as a 2183 * result (it is the responsibility of this method to close the 2184 * connection). 2185 */ 2186 private boolean processDeleteRequest(LDAPMessage message, 2187 ArrayList<Control> controls) 2188 { 2189 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 2190 { 2191 // LDAPv2 clients aren't allowed to send controls. 2192 DeleteResponseProtocolOp responseOp = 2193 new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2194 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2195 sendLDAPMessage(securityProvider, 2196 new LDAPMessage(message.getMessageID(), responseOp)); 2197 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2198 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2199 return false; 2200 } 2201 2202 DeleteRequestProtocolOp protocolOp = message.getDeleteRequestProtocolOp(); 2203 DeleteOperationBasis deleteOp = 2204 new DeleteOperationBasis(this, nextOperationID.getAndIncrement(), 2205 message.getMessageID(), controls, 2206 protocolOp.getDN()); 2207 2208 // Add the operation into the work queue. 2209 try 2210 { 2211 addOperationInProgress(deleteOp); 2212 } 2213 catch (DirectoryException de) 2214 { 2215 if (debugEnabled()) 2216 { 2217 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2218 } 2219 2220 DeleteResponseProtocolOp responseOp = 2221 new DeleteResponseProtocolOp(de.getResultCode().getIntValue(), 2222 de.getMessageObject(), 2223 de.getMatchedDN(), 2224 de.getReferralURLs()); 2225 2226 List<Control> responseControls = deleteOp.getResponseControls(); 2227 ArrayList<LDAPControl> responseLDAPControls = 2228 new ArrayList<LDAPControl>(responseControls.size()); 2229 for (Control c : responseControls) 2230 { 2231 responseLDAPControls.add(new LDAPControl(c)); 2232 } 2233 2234 sendLDAPMessage(securityProvider, 2235 new LDAPMessage(message.getMessageID(), responseOp, 2236 responseLDAPControls)); 2237 } 2238 2239 2240 return connectionValid; 2241 } 2242 2243 2244 2245 /** 2246 * Processes the provided LDAP message as an extended request. 2247 * 2248 * @param message The LDAP message containing the extended request to 2249 * process. 2250 * @param controls The set of pre-decoded request controls contained in the 2251 * message. 2252 * 2253 * @return <CODE>true</CODE> if the request was processed successfully, or 2254 * <CODE>false</CODE> if not and the connection has been closed as a 2255 * result (it is the responsibility of this method to close the 2256 * connection). 2257 */ 2258 private boolean processExtendedRequest(LDAPMessage message, 2259 ArrayList<Control> controls) 2260 { 2261 // See if this is an LDAPv2 client. If it is, then they should not be 2262 // issuing extended requests. We can't send a response that we can be sure 2263 // they can understand, so we have no choice but to close the connection. 2264 if (ldapVersion == 2) 2265 { 2266 Message msg = ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get( 2267 getConnectionID(), message.getMessageID()); 2268 logError(msg); 2269 disconnect(DisconnectReason.PROTOCOL_ERROR, false, msg); 2270 return false; 2271 } 2272 2273 2274 // FIXME -- Do we need to handle certain types of request here? 2275 // -- StartTLS requests 2276 // -- Cancel requests 2277 2278 2279 ExtendedRequestProtocolOp protocolOp = 2280 message.getExtendedRequestProtocolOp(); 2281 ExtendedOperationBasis extendedOp = 2282 new ExtendedOperationBasis(this, nextOperationID.getAndIncrement(), 2283 message.getMessageID(), controls, 2284 protocolOp.getOID(), protocolOp.getValue()); 2285 2286 // Add the operation into the work queue. 2287 try 2288 { 2289 addOperationInProgress(extendedOp); 2290 } 2291 catch (DirectoryException de) 2292 { 2293 if (debugEnabled()) 2294 { 2295 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2296 } 2297 2298 ExtendedResponseProtocolOp responseOp = 2299 new ExtendedResponseProtocolOp(de.getResultCode().getIntValue(), 2300 de.getMessageObject(), 2301 de.getMatchedDN(), 2302 de.getReferralURLs()); 2303 2304 List<Control> responseControls = extendedOp.getResponseControls(); 2305 ArrayList<LDAPControl> responseLDAPControls = 2306 new ArrayList<LDAPControl>(responseControls.size()); 2307 for (Control c : responseControls) 2308 { 2309 responseLDAPControls.add(new LDAPControl(c)); 2310 } 2311 2312 sendLDAPMessage(securityProvider, 2313 new LDAPMessage(message.getMessageID(), responseOp, 2314 responseLDAPControls)); 2315 } 2316 2317 2318 return connectionValid; 2319 } 2320 2321 2322 2323 /** 2324 * Processes the provided LDAP message as a modify request. 2325 * 2326 * @param message The LDAP message containing the modify request to 2327 * process. 2328 * @param controls The set of pre-decoded request controls contained in the 2329 * message. 2330 * 2331 * @return <CODE>true</CODE> if the request was processed successfully, or 2332 * <CODE>false</CODE> if not and the connection has been closed as a 2333 * result (it is the responsibility of this method to close the 2334 * connection). 2335 */ 2336 private boolean processModifyRequest(LDAPMessage message, 2337 ArrayList<Control> controls) 2338 { 2339 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 2340 { 2341 // LDAPv2 clients aren't allowed to send controls. 2342 ModifyResponseProtocolOp responseOp = 2343 new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2344 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2345 sendLDAPMessage(securityProvider, 2346 new LDAPMessage(message.getMessageID(), responseOp)); 2347 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2348 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2349 return false; 2350 } 2351 2352 ModifyRequestProtocolOp protocolOp = message.getModifyRequestProtocolOp(); 2353 ModifyOperationBasis modifyOp = 2354 new ModifyOperationBasis(this, nextOperationID.getAndIncrement(), 2355 message.getMessageID(), controls, 2356 protocolOp.getDN(), protocolOp.getModifications()); 2357 2358 // Add the operation into the work queue. 2359 try 2360 { 2361 addOperationInProgress(modifyOp); 2362 } 2363 catch (DirectoryException de) 2364 { 2365 if (debugEnabled()) 2366 { 2367 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2368 } 2369 2370 ModifyResponseProtocolOp responseOp = 2371 new ModifyResponseProtocolOp(de.getResultCode().getIntValue(), 2372 de.getMessageObject(), 2373 de.getMatchedDN(), 2374 de.getReferralURLs()); 2375 2376 List<Control> responseControls = modifyOp.getResponseControls(); 2377 ArrayList<LDAPControl> responseLDAPControls = 2378 new ArrayList<LDAPControl>(responseControls.size()); 2379 for (Control c : responseControls) 2380 { 2381 responseLDAPControls.add(new LDAPControl(c)); 2382 } 2383 2384 sendLDAPMessage(securityProvider, 2385 new LDAPMessage(message.getMessageID(), responseOp, 2386 responseLDAPControls)); 2387 } 2388 2389 2390 return connectionValid; 2391 } 2392 2393 2394 2395 /** 2396 * Processes the provided LDAP message as a modify DN request. 2397 * 2398 * @param message The LDAP message containing the modify DN request to 2399 * process. 2400 * @param controls The set of pre-decoded request controls contained in the 2401 * message. 2402 * 2403 * @return <CODE>true</CODE> if the request was processed successfully, or 2404 * <CODE>false</CODE> if not and the connection has been closed as a 2405 * result (it is the responsibility of this method to close the 2406 * connection). 2407 */ 2408 private boolean processModifyDNRequest(LDAPMessage message, 2409 ArrayList<Control> controls) 2410 { 2411 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 2412 { 2413 // LDAPv2 clients aren't allowed to send controls. 2414 ModifyDNResponseProtocolOp responseOp = 2415 new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2416 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2417 sendLDAPMessage(securityProvider, 2418 new LDAPMessage(message.getMessageID(), responseOp)); 2419 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2420 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2421 return false; 2422 } 2423 2424 ModifyDNRequestProtocolOp protocolOp = 2425 message.getModifyDNRequestProtocolOp(); 2426 ModifyDNOperationBasis modifyDNOp = 2427 new ModifyDNOperationBasis(this, nextOperationID.getAndIncrement(), 2428 message.getMessageID(), controls, 2429 protocolOp.getEntryDN(), protocolOp.getNewRDN(), 2430 protocolOp.deleteOldRDN(), 2431 protocolOp.getNewSuperior()); 2432 2433 // Add the operation into the work queue. 2434 try 2435 { 2436 addOperationInProgress(modifyDNOp); 2437 } 2438 catch (DirectoryException de) 2439 { 2440 if (debugEnabled()) 2441 { 2442 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2443 } 2444 2445 ModifyDNResponseProtocolOp responseOp = 2446 new ModifyDNResponseProtocolOp(de.getResultCode().getIntValue(), 2447 de.getMessageObject(), 2448 de.getMatchedDN(), 2449 de.getReferralURLs()); 2450 2451 List<Control> responseControls = modifyDNOp.getResponseControls(); 2452 ArrayList<LDAPControl> responseLDAPControls = 2453 new ArrayList<LDAPControl>(responseControls.size()); 2454 for (Control c : responseControls) 2455 { 2456 responseLDAPControls.add(new LDAPControl(c)); 2457 } 2458 2459 sendLDAPMessage(securityProvider, 2460 new LDAPMessage(message.getMessageID(), responseOp, 2461 responseLDAPControls)); 2462 } 2463 2464 2465 return connectionValid; 2466 } 2467 2468 2469 2470 /** 2471 * Processes the provided LDAP message as a search request. 2472 * 2473 * @param message The LDAP message containing the search request to 2474 * process. 2475 * @param controls The set of pre-decoded request controls contained in the 2476 * message. 2477 * 2478 * @return <CODE>true</CODE> if the request was processed successfully, or 2479 * <CODE>false</CODE> if not and the connection has been closed as a 2480 * result (it is the responsibility of this method to close the 2481 * connection). 2482 */ 2483 private boolean processSearchRequest(LDAPMessage message, 2484 ArrayList<Control> controls) 2485 { 2486 if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) 2487 { 2488 // LDAPv2 clients aren't allowed to send controls. 2489 SearchResultDoneProtocolOp responseOp = 2490 new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR, 2491 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2492 sendLDAPMessage(securityProvider, 2493 new LDAPMessage(message.getMessageID(), responseOp)); 2494 disconnect(DisconnectReason.PROTOCOL_ERROR, false, 2495 ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); 2496 return false; 2497 } 2498 2499 SearchRequestProtocolOp protocolOp = message.getSearchRequestProtocolOp(); 2500 SearchOperationBasis searchOp = 2501 new SearchOperationBasis(this, nextOperationID.getAndIncrement(), 2502 message.getMessageID(), controls, 2503 protocolOp.getBaseDN(), protocolOp.getScope(), 2504 protocolOp.getDereferencePolicy(), 2505 protocolOp.getSizeLimit(), 2506 protocolOp.getTimeLimit(), 2507 protocolOp.getTypesOnly(), protocolOp.getFilter(), 2508 protocolOp.getAttributes()); 2509 2510 // Add the operation into the work queue. 2511 try 2512 { 2513 addOperationInProgress(searchOp); 2514 } 2515 catch (DirectoryException de) 2516 { 2517 if (debugEnabled()) 2518 { 2519 TRACER.debugCaught(DebugLogLevel.ERROR, de); 2520 } 2521 2522 SearchResultDoneProtocolOp responseOp = 2523 new SearchResultDoneProtocolOp(de.getResultCode().getIntValue(), 2524 de.getMessageObject(), 2525 de.getMatchedDN(), 2526 de.getReferralURLs()); 2527 2528 List<Control> responseControls = searchOp.getResponseControls(); 2529 ArrayList<LDAPControl> responseLDAPControls = 2530 new ArrayList<LDAPControl>(responseControls.size()); 2531 for (Control c : responseControls) 2532 { 2533 responseLDAPControls.add(new LDAPControl(c)); 2534 } 2535 2536 sendLDAPMessage(securityProvider, 2537 new LDAPMessage(message.getMessageID(), responseOp, 2538 responseLDAPControls)); 2539 } 2540 2541 2542 return connectionValid; 2543 } 2544 2545 2546 2547 /** 2548 * Processes the provided LDAP message as an unbind request. 2549 * 2550 * @param message The LDAP message containing the unbind request to 2551 * process. 2552 * @param controls The set of pre-decoded request controls contained in the 2553 * message. 2554 * 2555 * @return <CODE>true</CODE> if the request was processed successfully, or 2556 * <CODE>false</CODE> if not and the connection has been closed as a 2557 * result (it is the responsibility of this method to close the 2558 * connection). 2559 */ 2560 private boolean processUnbindRequest(LDAPMessage message, 2561 ArrayList<Control> controls) 2562 { 2563 UnbindOperationBasis unbindOp = 2564 new UnbindOperationBasis(this, nextOperationID.getAndIncrement(), 2565 message.getMessageID(), controls); 2566 2567 unbindOp.run(); 2568 2569 // The client connection will never be valid after an unbind. 2570 return false; 2571 } 2572 2573 2574 2575 /** 2576 * {@inheritDoc} 2577 */ 2578 public String getMonitorSummary() 2579 { 2580 StringBuilder buffer = new StringBuilder(); 2581 buffer.append("connID=\""); 2582 buffer.append(connectionID); 2583 buffer.append("\" connectTime=\""); 2584 buffer.append(getConnectTimeString()); 2585 buffer.append("\" source=\""); 2586 buffer.append(clientAddress); 2587 buffer.append(":"); 2588 buffer.append(clientPort); 2589 buffer.append("\" destination=\""); 2590 buffer.append(serverAddress); 2591 buffer.append(":"); 2592 buffer.append(connectionHandler.getListenPort()); 2593 buffer.append("\" ldapVersion=\""); 2594 buffer.append(ldapVersion); 2595 buffer.append("\" authDN=\""); 2596 2597 DN authDN = getAuthenticationInfo().getAuthenticationDN(); 2598 if (authDN != null) 2599 { 2600 authDN.toString(buffer); 2601 } 2602 2603 buffer.append("\" security=\""); 2604 if (securityProvider.isSecure()) 2605 { 2606 buffer.append(securityProvider.getSecurityMechanismName()); 2607 } 2608 else 2609 { 2610 buffer.append("none"); 2611 } 2612 2613 buffer.append("\" opsInProgress=\""); 2614 buffer.append(operationsInProgress.size()); 2615 buffer.append("\""); 2616 2617 return buffer.toString(); 2618 } 2619 2620 2621 2622 /** 2623 * Appends a string representation of this client connection to the provided 2624 * buffer. 2625 * 2626 * @param buffer The buffer to which the information should be appended. 2627 */ 2628 public void toString(StringBuilder buffer) 2629 { 2630 buffer.append("LDAP client connection from "); 2631 buffer.append(clientAddress); 2632 buffer.append(":"); 2633 buffer.append(clientPort); 2634 buffer.append(" to "); 2635 buffer.append(serverAddress); 2636 buffer.append(":"); 2637 buffer.append(serverPort); 2638 } 2639 2640 2641 2642 /** 2643 * Indicates whether TLS protection is actually available for the underlying 2644 * client connection. If there is any reason that TLS protection cannot be 2645 * enabled on this client connection, then it should be appended to the 2646 * provided buffer. 2647 * 2648 * @param unavailableReason The buffer used to hold the reason that TLS is 2649 * not available on the underlying client 2650 * connection. 2651 * 2652 * @return <CODE>true</CODE> if TLS is available on the underlying client 2653 * connection, or <CODE>false</CODE> if it is not. 2654 */ 2655 public boolean tlsProtectionAvailable(MessageBuilder unavailableReason) 2656 { 2657 // Make sure that this client connection does not already have some other 2658 // security provider enabled. 2659 if (! (securityProvider instanceof NullConnectionSecurityProvider)) 2660 { 2661 2662 unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER.get( 2663 securityProvider.getSecurityMechanismName())); 2664 return false; 2665 } 2666 2667 2668 // Make sure that the connection handler allows the use of the StartTLS 2669 // operation. 2670 if (! connectionHandler.allowStartTLS()) 2671 { 2672 2673 unavailableReason.append(ERR_LDAP_TLS_STARTTLS_NOT_ALLOWED.get()); 2674 return false; 2675 } 2676 2677 2678 // Make sure that the TLS security provider is available. 2679 if (tlsSecurityProvider == null) 2680 { 2681 try 2682 { 2683 TLSConnectionSecurityProvider tlsProvider = 2684 new TLSConnectionSecurityProvider(); 2685 tlsProvider.initializeConnectionSecurityProvider(null); 2686 tlsProvider.setSSLClientAuthPolicy( 2687 connectionHandler.getSSLClientAuthPolicy()); 2688 tlsProvider.setEnabledProtocols( 2689 connectionHandler.getEnabledSSLProtocols()); 2690 tlsProvider.setEnabledCipherSuites( 2691 connectionHandler.getEnabledSSLCipherSuites()); 2692 2693 tlsSecurityProvider = (TLSConnectionSecurityProvider) 2694 tlsProvider.newInstance(this, clientChannel); 2695 } 2696 catch (Exception e) 2697 { 2698 if (debugEnabled()) 2699 { 2700 TRACER.debugCaught(DebugLogLevel.ERROR, e); 2701 } 2702 2703 tlsSecurityProvider = null; 2704 2705 2706 unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER.get( 2707 stackTraceToSingleLineString(e))); 2708 return false; 2709 } 2710 } 2711 2712 2713 // If we've gotten here, then everything looks OK. 2714 return true; 2715 } 2716 2717 2718 2719 /** 2720 * Installs the TLS connection security provider on this client connection. 2721 * If an error occurs in the process, then the underlying client connection 2722 * must be terminated and an exception must be thrown to indicate the 2723 * underlying cause. 2724 * 2725 * @throws DirectoryException If the TLS connection security provider could 2726 * not be enabled and the underlying connection 2727 * has been closed. 2728 */ 2729 public void enableTLSConnectionSecurityProvider() 2730 throws DirectoryException 2731 { 2732 if (tlsSecurityProvider == null) 2733 { 2734 Message message = ERR_LDAP_TLS_NO_PROVIDER.get(); 2735 2736 disconnect(DisconnectReason.OTHER, false, message); 2737 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 2738 message); 2739 } 2740 2741 clearSecurityProvider = securityProvider; 2742 setConnectionSecurityProvider(tlsSecurityProvider); 2743 } 2744 2745 2746 2747 /** 2748 * Disables the TLS connection security provider on this client connection. 2749 * This must also eliminate any authentication that had been performed on the 2750 * client connection so that it is in an anonymous state. If a problem occurs 2751 * while attempting to revert the connection to a non-TLS-protected state, 2752 * then an exception must be thrown and the client connection must be 2753 * terminated. 2754 * 2755 * @throws DirectoryException If TLS protection cannot be reverted and the 2756 * underlying client connection has been closed. 2757 */ 2758 public void disableTLSConnectionSecurityProvider() 2759 throws DirectoryException 2760 { 2761 Message message = ERR_LDAP_TLS_CLOSURE_NOT_ALLOWED.get(); 2762 2763 disconnect(DisconnectReason.OTHER, false, message); 2764 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 2765 message); 2766 } 2767 2768 2769 2770 /** 2771 * Sends a response to the client in the clear rather than through the 2772 * encrypted channel. This should only be used when processing the StartTLS 2773 * extended operation to send the response in the clear after the TLS 2774 * negotiation has already been initiated. 2775 * 2776 * @param operation The operation for which to send the response in the 2777 * clear. 2778 * 2779 * 2780 * @throws DirectoryException If a problem occurs while sending the response 2781 * in the clear. 2782 */ 2783 public void sendClearResponse(Operation operation) 2784 throws DirectoryException 2785 { 2786 if (clearSecurityProvider == null) 2787 { 2788 Message message = ERR_LDAP_NO_CLEAR_SECURITY_PROVIDER.get(toString()); 2789 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 2790 message); 2791 } 2792 2793 sendLDAPMessage(clearSecurityProvider, 2794 operationToResponseLDAPMessage(operation)); 2795 } 2796 2797 2798 2799 /** 2800 * {@inheritDoc} 2801 */ 2802 public DN getKeyManagerProviderDN() 2803 { 2804 return connectionHandler.getKeyManagerProviderDN(); 2805 } 2806 2807 2808 2809 /** 2810 * {@inheritDoc} 2811 */ 2812 public DN getTrustManagerProviderDN() 2813 { 2814 return connectionHandler.getTrustManagerProviderDN(); 2815 } 2816 2817 2818 2819 /** 2820 * Retrieves the alias of the server certificate that should be used 2821 * for operations requiring a server certificate. The default 2822 * implementation returns {@code null} to indicate that any alias is 2823 * acceptable. 2824 * 2825 * @return The alias of the server certificate that should be used 2826 * for operations requring a server certificate, or 2827 * {@code null} if any alias is acceptable. 2828 */ 2829 public String getCertificateAlias() 2830 { 2831 return connectionHandler.getSSLServerCertNickname(); 2832 } 2833 2834 2835 2836 /** 2837 * Retrieves the length of time in milliseconds that this client 2838 * connection has been idle. 2839 * <BR><BR> 2840 * Note that the default implementation will always return zero. 2841 * Subclasses associated with connection handlers should override 2842 * this method if they wish to provided idle time limit 2843 * functionality. 2844 * 2845 * @return The length of time in milliseconds that this client 2846 * connection has been idle. 2847 */ 2848 public long getIdleTime() 2849 { 2850 if (operationsInProgress.isEmpty() && getPersistentSearches().isEmpty()) 2851 { 2852 return (TimeThread.getTime() - lastCompletionTime.get()); 2853 } 2854 else 2855 { 2856 // There's at least one operation in progress, so it's not idle. 2857 return 0L; 2858 } 2859 } 2860 } 2861