| | |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2006-2008 Sun Microsystems, Inc. |
| | | * Copyright 2006-2009 Sun Microsystems, Inc. |
| | | */ |
| | | package org.opends.server.protocols.ldap; |
| | | |
| | | |
| | | |
| | | import static org.opends.messages.ProtocolMessages.*; |
| | | import static org.opends.server.loggers.AccessLogger.*; |
| | | import static org.opends.server.loggers.ErrorLogger.*; |
| | | import static org.opends.server.loggers.debug.DebugLogger.*; |
| | | import static org.opends.server.protocols.ldap.LDAPConstants.*; |
| | | import static org.opends.server.types.OperationType.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.nio.ByteBuffer; |
| | | import java.nio.channels.Selector; |
| | | import java.nio.channels.SocketChannel; |
| | | import java.util.ArrayList; |
| | | import java.security.cert.Certificate; |
| | | import java.util.Collection; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | |
| | | import org.opends.messages.MessageBuilder; |
| | | import org.opends.server.api.ClientConnection; |
| | | import org.opends.server.api.ConnectionHandler; |
| | | import org.opends.server.api.ConnectionSecurityProvider; |
| | | import org.opends.server.core.AbandonOperationBasis; |
| | | import org.opends.server.core.AddOperationBasis; |
| | | import org.opends.server.core.BindOperationBasis; |
| | |
| | | import org.opends.server.core.SearchOperationBasis; |
| | | import org.opends.server.core.UnbindOperationBasis; |
| | | import org.opends.server.core.networkgroups.NetworkGroup; |
| | | import org.opends.server.extensions.NullConnectionSecurityProvider; |
| | | import org.opends.server.extensions.ConnectionSecurityProvider; |
| | | import org.opends.server.extensions.RedirectingByteChannel; |
| | | import org.opends.server.extensions.TLSByteChannel; |
| | | import org.opends.server.extensions.TLSCapableConnection; |
| | | import org.opends.server.extensions.TLSConnectionSecurityProvider; |
| | | import org.opends.server.loggers.debug.DebugTracer; |
| | | import org.opends.server.monitors.OperationMonitor; |
| | | import org.opends.server.protocols.asn1.ASN1Element; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.protocols.asn1.ASN1Sequence; |
| | | import org.opends.server.protocols.asn1.ASN1; |
| | | import org.opends.server.protocols.asn1.ASN1ByteChannelReader; |
| | | import org.opends.server.protocols.asn1.ASN1Writer; |
| | | import org.opends.server.types.AbstractOperation; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.ByteStringBuilder; |
| | | import org.opends.server.types.CancelRequest; |
| | | import org.opends.server.types.CancelResult; |
| | | import org.opends.server.types.Control; |
| | |
| | | import org.opends.server.types.SearchResultReference; |
| | | import org.opends.server.util.TimeThread; |
| | | |
| | | import static org.opends.messages.ProtocolMessages.*; |
| | | import static org.opends.server.loggers.AccessLogger.logDisconnect; |
| | | import static org.opends.server.loggers.ErrorLogger.logError; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | | import static org.opends.server.loggers.debug.DebugLogger.getTracer; |
| | | import static org.opends.server.protocols.ldap.LDAPConstants.*; |
| | | import static org.opends.server.util.StaticUtils.getExceptionMessage; |
| | | import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; |
| | | import static org.opends.server.types.OperationType.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class defines an LDAP client connection, which is a type of client |
| | | * connection that will be accepted by an instance of the LDAP connection |
| | | * handler and have its requests decoded by an LDAP request handler. |
| | | * This class defines an LDAP client connection, which is a type of |
| | | * client connection that will be accepted by an instance of the LDAP |
| | | * connection handler and have its requests decoded by an LDAP request |
| | | * handler. |
| | | */ |
| | | public class LDAPClientConnection |
| | | extends ClientConnection |
| | | implements TLSCapableConnection |
| | | |
| | | public class LDAPClientConnection extends ClientConnection implements |
| | | TLSCapableConnection |
| | | { |
| | | /** |
| | | * The tracer object for the debug logger. |
| | | */ |
| | | private static final DebugTracer TRACER = getTracer(); |
| | | |
| | | |
| | | |
| | | // The time that the last operation was completed. |
| | | private AtomicLong lastCompletionTime; |
| | | private final AtomicLong lastCompletionTime; |
| | | |
| | | // The next operation ID that should be used for this connection. |
| | | private AtomicLong nextOperationID; |
| | | private final AtomicLong nextOperationID; |
| | | |
| | | // The selector that may be used for write operations. |
| | | private AtomicReference<Selector> writeSelector; |
| | | private final AtomicReference<Selector> writeSelector; |
| | | |
| | | // Indicates whether the Directory Server believes this connection to be |
| | | // valid and available for communication. |
| | | // Indicates whether the Directory Server believes this connection to |
| | | // be valid and available for communication. |
| | | private boolean connectionValid; |
| | | |
| | | // Indicates whether this connection is about to be closed. This will be used |
| | | // to prevent accepting new requests while a disconnect is in progress. |
| | | // Indicates whether this connection is about to be closed. This will |
| | | // be used to prevent accepting new requests while a disconnect is in |
| | | // progress. |
| | | private boolean disconnectRequested; |
| | | |
| | | // Indicates whether the connection should keep statistics regarding the |
| | | // operations that it is performing. |
| | | private boolean keepStats; |
| | | |
| | | // The BER type for the ASN.1 element that is in the process of being read. |
| | | private byte elementType; |
| | | |
| | | // The encoded value for the ASN.1 element that is in the process of being |
| | | // read. |
| | | private byte[] elementValue; |
| | | // Indicates whether the connection should keep statistics regarding |
| | | // the operations that it is performing. |
| | | private final boolean keepStats; |
| | | |
| | | // The set of all operations currently in progress on this connection. |
| | | private ConcurrentHashMap<Integer,Operation> operationsInProgress; |
| | | private final ConcurrentHashMap<Integer, Operation> operationsInProgress; |
| | | |
| | | // The number of operations performed on this connection. |
| | | // Used to compare with the resource limits of the network group. |
| | | private long operationsPerformed; |
| | | |
| | | // Lock on the number of operations |
| | | private Object operationsPerformedLock; |
| | | |
| | | // The connection security provider that was in use for the client connection |
| | | // before switching to a TLS-based provider. |
| | | private ConnectionSecurityProvider clearSecurityProvider; |
| | | |
| | | // The connection security provider for this client connection. |
| | | private ConnectionSecurityProvider securityProvider; |
| | | private final Object operationsPerformedLock; |
| | | |
| | | // The port on the client from which this connection originated. |
| | | private int clientPort; |
| | | private final int clientPort; |
| | | |
| | | // The number of bytes contained in the value for the ASN.1 element that is in |
| | | // the process of being read. |
| | | private int elementLength; |
| | | |
| | | // The number of bytes in the multi-byte length that are still needed to fully |
| | | // decode the length of the ASN.1 element in process. |
| | | private int elementLengthBytesNeeded; |
| | | |
| | | // The current state for the data read for the ASN.1 element in progress. |
| | | private int elementReadState; |
| | | |
| | | // The number of bytes that have already been read for the ASN.1 element |
| | | // value in progress. |
| | | private int elementValueBytesRead; |
| | | |
| | | // The number of bytes that are still needed to fully decode the value of the |
| | | // ASN.1 element in progress. |
| | | private int elementValueBytesNeeded; |
| | | |
| | | // The LDAP version that the client is using to communicate with the server. |
| | | // The LDAP version that the client is using to communicate with the |
| | | // server. |
| | | private int ldapVersion; |
| | | |
| | | // The port on the server to which this client has connected. |
| | | private int serverPort; |
| | | private final int serverPort; |
| | | |
| | | // The reference to the connection handler that accepted this connection. |
| | | private LDAPConnectionHandler connectionHandler; |
| | | // The reference to the connection handler that accepted this |
| | | // connection. |
| | | private final LDAPConnectionHandler connectionHandler; |
| | | |
| | | // The reference to the request handler with which this connection is |
| | | // associated. |
| | | private LDAPRequestHandler requestHandler; |
| | | private final LDAPRequestHandler requestHandler; |
| | | |
| | | // The statistics tracker associated with this client connection. |
| | | private LDAPStatistics statTracker; |
| | | private final LDAPStatistics statTracker; |
| | | |
| | | // The connectionHandler statistic tracker. |
| | | private LDAPStatistics parentTracker; |
| | | private final LDAPStatistics parentTracker; |
| | | |
| | | // The connection ID assigned to this connection. |
| | | private long connectionID; |
| | | private final long connectionID; |
| | | |
| | | // The lock used to provide threadsafe access to the set of operations in |
| | | // progress. |
| | | private Object opsInProgressLock; |
| | | // The lock used to provide threadsafe access to the set of operations |
| | | // in progress. |
| | | private final Object opsInProgressLock; |
| | | |
| | | // The lock used to provide threadsafe access when sending data to the client. |
| | | private Object transmitLock; |
| | | // The lock used to provide threadsafe access when sending data to the |
| | | // client. |
| | | private final Object transmitLock; |
| | | |
| | | // The socket channel with which this client connection is associated. |
| | | private SocketChannel clientChannel; |
| | | private final SocketChannel clientChannel; |
| | | |
| | | // The string representation of the address of the client. |
| | | private String clientAddress; |
| | | private final String clientAddress; |
| | | |
| | | // The name of the protocol that the client is using to communicate with the |
| | | // server. |
| | | private String protocol; |
| | | // The name of the protocol that the client is using to communicate |
| | | // with the server. |
| | | private final String protocol; |
| | | |
| | | // The string representation of the address of the server to which the client |
| | | // has connected. |
| | | private String serverAddress; |
| | | // The string representation of the address of the server to which the |
| | | // client has connected. |
| | | private final String serverAddress; |
| | | |
| | | // The TLS connection security provider that may be used for this connection |
| | | // if StartTLS is requested. |
| | | private TLSConnectionSecurityProvider tlsSecurityProvider; |
| | | private final ThreadLocal<WriterBuffer> cachedBuffers; |
| | | |
| | | //The SASL connection provider used if confidentiality/integrity is negotiated |
| | | //during a SASL bind (GSSAPI and DIGEST-MD5 only). |
| | | private ConnectionSecurityProvider saslSecurityProvider; |
| | | private ASN1ByteChannelReader asn1Reader; |
| | | |
| | | private final RedirectingByteChannel saslChannel; |
| | | private final RedirectingByteChannel tlsChannel; |
| | | private ConnectionSecurityProvider activeProvider = null; |
| | | private ConnectionSecurityProvider tlsPendingProvider = null; |
| | | private ConnectionSecurityProvider saslPendingProvider = null; |
| | | |
| | | // Statistics for the processed operations |
| | | private OperationMonitor addMonitor; |
| | |
| | | private OperationMonitor moddnMonitor; |
| | | private OperationMonitor unbindMonitor; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class wraps the byte string buffer and ASN1 writer. |
| | | */ |
| | | private static class WriterBuffer |
| | | { |
| | | ASN1Writer writer; |
| | | ByteStringBuilder buffer; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Creates a new LDAP client connection with the provided information. |
| | | * |
| | | * @param connectionHandler The connection handler that accepted this |
| | | * connection. |
| | | * @param clientChannel The socket channel that may be used to |
| | | * communicate with the client. |
| | | * @param connectionHandler |
| | | * The connection handler that accepted this connection. |
| | | * @param clientChannel |
| | | * The socket channel that may be used to communicate with |
| | | * the client. |
| | | */ |
| | | public LDAPClientConnection(LDAPConnectionHandler connectionHandler, |
| | | SocketChannel clientChannel) |
| | | SocketChannel clientChannel) |
| | | { |
| | | super(); |
| | | |
| | | |
| | | this.connectionHandler = connectionHandler; |
| | | if (connectionHandler.isAdminConnectionHandler()) { |
| | | this.connectionHandler = connectionHandler; |
| | | if (connectionHandler.isAdminConnectionHandler()) |
| | | { |
| | | setNetworkGroup(NetworkGroup.getAdminNetworkGroup()); |
| | | } |
| | | |
| | | this.clientChannel = clientChannel; |
| | | this.securityProvider = null; |
| | | this.clearSecurityProvider = null; |
| | | |
| | | this.clientChannel = clientChannel; |
| | | opsInProgressLock = new Object(); |
| | | transmitLock = new Object(); |
| | | |
| | | elementReadState = ELEMENT_READ_STATE_NEED_TYPE; |
| | | elementType = 0x00; |
| | | elementLength = 0; |
| | | elementLengthBytesNeeded = 0; |
| | | elementValue = null; |
| | | elementValueBytesRead = 0; |
| | | elementValueBytesNeeded = 0; |
| | | |
| | | ldapVersion = 3; |
| | | requestHandler = null; |
| | | lastCompletionTime = new AtomicLong(TimeThread.getTime()); |
| | | nextOperationID = new AtomicLong(0); |
| | | connectionValid = true; |
| | | disconnectRequested = false; |
| | | operationsInProgress = new ConcurrentHashMap<Integer,Operation>(); |
| | | transmitLock = new Object(); |
| | | ldapVersion = 3; |
| | | requestHandler = null; |
| | | lastCompletionTime = new AtomicLong(TimeThread.getTime()); |
| | | nextOperationID = new AtomicLong(0); |
| | | connectionValid = true; |
| | | disconnectRequested = false; |
| | | operationsInProgress = new ConcurrentHashMap<Integer, Operation>(); |
| | | operationsPerformed = 0; |
| | | operationsPerformedLock = new Object(); |
| | | keepStats = connectionHandler.keepStats(); |
| | | protocol = "LDAP"; |
| | | writeSelector = new AtomicReference<Selector>(); |
| | | |
| | | clientAddress = clientChannel.socket().getInetAddress().getHostAddress(); |
| | | clientPort = clientChannel.socket().getPort(); |
| | | serverAddress = clientChannel.socket().getLocalAddress().getHostAddress(); |
| | | serverPort = clientChannel.socket().getLocalPort(); |
| | | |
| | | keepStats = connectionHandler.keepStats(); |
| | | protocol = "LDAP"; |
| | | writeSelector = new AtomicReference<Selector>(); |
| | | clientAddress = |
| | | clientChannel.socket().getInetAddress().getHostAddress(); |
| | | clientPort = clientChannel.socket().getPort(); |
| | | serverAddress = |
| | | clientChannel.socket().getLocalAddress().getHostAddress(); |
| | | serverPort = clientChannel.socket().getLocalPort(); |
| | | parentTracker = connectionHandler.getStatTracker(); |
| | | String instanceName = parentTracker.getMonitorInstanceName() + |
| | | " for " + toString(); |
| | | String instanceName = |
| | | parentTracker.getMonitorInstanceName() + " for " + toString(); |
| | | this.initializeOperationMonitors(); |
| | | statTracker = new LDAPStatistics(connectionHandler, |
| | | instanceName, parentTracker); |
| | | statTracker = |
| | | new LDAPStatistics(connectionHandler, instanceName, |
| | | parentTracker); |
| | | |
| | | if (keepStats) |
| | | { |
| | |
| | | if (connectionID < 0) |
| | | { |
| | | disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true, |
| | | ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get()); |
| | | ERR_LDAP_CONNHANDLER_REJECTED_BY_SERVER.get()); |
| | | } |
| | | cachedBuffers = new ThreadLocal<WriterBuffer>(); |
| | | tlsChannel = |
| | | RedirectingByteChannel.getRedirectingByteChannel(clientChannel); |
| | | saslChannel = |
| | | RedirectingByteChannel.getRedirectingByteChannel(tlsChannel); |
| | | this.asn1Reader = |
| | | ASN1.getReader(saslChannel, 4096, connectionHandler |
| | | .getMaxRequestSize()); |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Retrieves the connection ID assigned to this connection. |
| | | * |
| | | * @return The connection ID assigned to this connection. |
| | | * @return The connection ID assigned to this connection. |
| | | */ |
| | | @Override |
| | | public long getConnectionID() |
| | | { |
| | | return connectionID; |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the connection handler that accepted this client connection. |
| | | * Retrieves the connection handler that accepted this client |
| | | * connection. |
| | | * |
| | | * @return The connection handler that accepted this client connection. |
| | | * @return The connection handler that accepted this client |
| | | * connection. |
| | | */ |
| | | @Override |
| | | public ConnectionHandler<?> getConnectionHandler() |
| | | { |
| | | return connectionHandler; |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the request handler that will read requests for this client |
| | | * connection. |
| | | * Retrieves the request handler that will read requests for this |
| | | * client connection. |
| | | * |
| | | * @return The request handler that will read requests for this client |
| | | * connection, or <CODE>null</CODE> if none has been assigned yet. |
| | | * @return The request handler that will read requests for this client |
| | | * connection, or <CODE>null</CODE> if none has been assigned |
| | | * yet. |
| | | */ |
| | | public LDAPRequestHandler getRequestHandler() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Specifies the request handler that will read requests for this client |
| | | * connection. |
| | | * Retrieves the socket channel that can be used to communicate with |
| | | * the client. |
| | | * |
| | | * @param requestHandler The request handler that will read requests for |
| | | * this client connection. |
| | | */ |
| | | public void setRequestHandler(LDAPRequestHandler requestHandler) |
| | | { |
| | | this.requestHandler = requestHandler; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the socket channel that can be used to communicate with the |
| | | * client. |
| | | * |
| | | * @return The socket channel that can be used to communicate with the |
| | | * client. |
| | | * @return The socket channel that can be used to communicate with the |
| | | * client. |
| | | */ |
| | | public SocketChannel getSocketChannel() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the protocol that the client is using to communicate with the |
| | | * Directory Server. |
| | | * Retrieves the protocol that the client is using to communicate with |
| | | * the Directory Server. |
| | | * |
| | | * @return The protocol that the client is using to communicate with the |
| | | * Directory Server. |
| | | * @return The protocol that the client is using to communicate with |
| | | * the Directory Server. |
| | | */ |
| | | @Override |
| | | public String getProtocol() |
| | | { |
| | | return protocol; |
| | |
| | | /** |
| | | * Retrieves a string representation of the address of the client. |
| | | * |
| | | * @return A string representation of the address of the client. |
| | | * @return A string representation of the address of the client. |
| | | */ |
| | | @Override |
| | | public String getClientAddress() |
| | | { |
| | | return clientAddress; |
| | |
| | | /** |
| | | * Retrieves the port number for this connection on the client system. |
| | | * |
| | | * @return The port number for this connection on the client system. |
| | | * @return The port number for this connection on the client system. |
| | | */ |
| | | @Override |
| | | public int getClientPort() |
| | | { |
| | | return clientPort; |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a string representation of the address on the server to which the |
| | | * client connected. |
| | | * Retrieves a string representation of the address on the server to |
| | | * which the client connected. |
| | | * |
| | | * @return A string representation of the address on the server to which the |
| | | * client connected. |
| | | * @return A string representation of the address on the server to |
| | | * which the client connected. |
| | | */ |
| | | @Override |
| | | public String getServerAddress() |
| | | { |
| | | return serverAddress; |
| | |
| | | /** |
| | | * Retrieves the port number for this connection on the server system. |
| | | * |
| | | * @return The port number for this connection on the server system. |
| | | * @return The port number for this connection on the server system. |
| | | */ |
| | | @Override |
| | | public int getServerPort() |
| | | { |
| | | return serverPort; |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the remote |
| | | * client system. |
| | | * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the |
| | | * remote client system. |
| | | * |
| | | * @return The <CODE>java.net.InetAddress</CODE> associated with the remote |
| | | * client system. It may be <CODE>null</CODE> if the client is not |
| | | * connected over an IP-based connection. |
| | | * @return The <CODE>java.net.InetAddress</CODE> associated with the |
| | | * remote client system. It may be <CODE>null</CODE> if the |
| | | * client is not connected over an IP-based connection. |
| | | */ |
| | | @Override |
| | | public InetAddress getRemoteAddress() |
| | | { |
| | | return clientChannel.socket().getInetAddress(); |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory Server |
| | | * system to which the client has established the connection. |
| | | * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory |
| | | * Server system to which the client has established the connection. |
| | | * |
| | | * @return The <CODE>java.net.InetAddress</CODE> for the Directory Server |
| | | * system to which the client has established the connection. It may |
| | | * be <CODE>null</CODE> if the client is not connected over an |
| | | * IP-based connection. |
| | | * @return The <CODE>java.net.InetAddress</CODE> for the Directory |
| | | * Server system to which the client has established the |
| | | * connection. It may be <CODE>null</CODE> if the client is |
| | | * not connected over an IP-based connection. |
| | | */ |
| | | @Override |
| | | public InetAddress getLocalAddress() |
| | | { |
| | | return clientChannel.socket().getLocalAddress(); |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this client connection is currently using a secure |
| | | * mechanism to communicate with the server. Note that this may change over |
| | | * time based on operations performed by the client or server (e.g., it may go |
| | | * from <CODE>false</CODE> to <CODE>true</CODE> if the client uses the |
| | | * StartTLS extended operation). |
| | | * Indicates whether this client connection is currently using a |
| | | * secure mechanism to communicate with the server. Note that this may |
| | | * change over time based on operations performed by the client or |
| | | * server (e.g., it may go from <CODE>false</CODE> to |
| | | * <CODE>true</CODE> if the client uses the StartTLS extended |
| | | * operation). |
| | | * |
| | | * @return <CODE>true</CODE> if the client connection is currently using a |
| | | * secure mechanism to communicate with the server, or |
| | | * <CODE>false</CODE> if not. |
| | | * @return <CODE>true</CODE> if the client connection is currently |
| | | * using a secure mechanism to communicate with the server, or |
| | | * <CODE>false</CODE> if not. |
| | | */ |
| | | @Override |
| | | public boolean isSecure() |
| | | { |
| | | return securityProvider.isSecure(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the connection security provider for this client connection. |
| | | * |
| | | * @return The connection security provider for this client connection. |
| | | */ |
| | | public ConnectionSecurityProvider getConnectionSecurityProvider() |
| | | { |
| | | if(saslSecurityProvider != null && saslSecurityProvider.isActive()) |
| | | securityProvider = saslSecurityProvider; |
| | | return securityProvider; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Set the security provider to be used to process SASL (DIGEST-MD5, GSSAPI) |
| | | * confidentiality/integrity messages. |
| | | * |
| | | * @param secProvider The security provider to use. |
| | | */ |
| | | public void |
| | | setSASLConnectionSecurityProvider(ConnectionSecurityProvider secProvider) { |
| | | saslSecurityProvider = secProvider; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Specifies the connection security provider for this client connection. |
| | | * |
| | | * @param securityProvider The connection security provider to use for |
| | | * communication on this client connection. |
| | | */ |
| | | public void setConnectionSecurityProvider(ConnectionSecurityProvider |
| | | securityProvider) |
| | | { |
| | | this.securityProvider = securityProvider; |
| | | |
| | | if (securityProvider.isSecure()) |
| | | { |
| | | protocol = "LDAP+" + securityProvider.getSecurityMechanismName(); |
| | | } |
| | | if (activeProvider != null) |
| | | return activeProvider.isSecure(); |
| | | else |
| | | { |
| | | protocol = "LDAP"; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the human-readable name of the security mechanism that is used to |
| | | * protect communication with this client. |
| | | * Retrieves the next operation ID that should be used for this |
| | | * connection. |
| | | * |
| | | * @return The human-readable name of the security mechanism that is used to |
| | | * protect communication with this client, or <CODE>null</CODE> if no |
| | | * security is in place. |
| | | */ |
| | | public String getSecurityMechanism() |
| | | { |
| | | return securityProvider.getSecurityMechanismName(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the next operation ID that should be used for this connection. |
| | | * |
| | | * @return The next operation ID that should be used for this connection. |
| | | * @return The next operation ID that should be used for this |
| | | * connection. |
| | | */ |
| | | public long nextOperationID() |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Sends a response to the client based on the information in the provided |
| | | * operation. |
| | | * Sends a response to the client based on the information in the |
| | | * provided operation. |
| | | * |
| | | * @param operation The operation for which to send the response. |
| | | * @param operation |
| | | * The operation for which to send the response. |
| | | */ |
| | | @Override |
| | | public void sendResponse(Operation operation) |
| | | { |
| | | // Since this is the final response for this operation, we can go ahead and |
| | | // remove it from the "operations in progress" list. It can't be canceled |
| | | // after this point, and this will avoid potential race conditions in which |
| | | // the client immediately sends another request with the same message ID as |
| | | // was used for this operation. |
| | | // Since this is the final response for this operation, we can go |
| | | // ahead and remove it from the "operations in progress" list. It |
| | | // can't be canceled after this point, and this will avoid potential |
| | | // race conditions in which the client immediately sends another |
| | | // request with the same message ID as was used for this operation. |
| | | removeOperationInProgress(operation.getMessageID()); |
| | | |
| | | LDAPMessage message = operationToResponseLDAPMessage(operation); |
| | | if (message != null) |
| | | { |
| | | sendLDAPMessage(securityProvider, message); |
| | | sendLDAPMessage(message); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves an LDAPMessage containing a response generated from the provided |
| | | * operation. |
| | | * Retrieves an LDAPMessage containing a response generated from the |
| | | * provided operation. |
| | | * |
| | | * @param operation The operation to use to generate the response |
| | | * LDAPMessage. |
| | | * |
| | | * @return An LDAPMessage containing a response generated from the provided |
| | | * operation. |
| | | * @param operation |
| | | * The operation to use to generate the response LDAPMessage. |
| | | * @return An LDAPMessage containing a response generated from the |
| | | * provided operation. |
| | | */ |
| | | private LDAPMessage operationToResponseLDAPMessage(Operation operation) |
| | | { |
| | | ResultCode resultCode = operation.getResultCode(); |
| | | if (resultCode == null) |
| | | { |
| | | // This must mean that the operation has either not yet completed or that |
| | | // it completed without a result for some reason. In any case, log a |
| | | // message and set the response to "operations error". |
| | | logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE. |
| | | get(operation.getOperationType().toString(), |
| | | operation.getConnectionID(), operation.getOperationID())); |
| | | // This must mean that the operation has either not yet completed |
| | | // or that it completed without a result for some reason. In any |
| | | // case, log a message and set the response to "operations error". |
| | | logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE.get( |
| | | operation.getOperationType().toString(), operation |
| | | .getConnectionID(), operation.getOperationID())); |
| | | resultCode = DirectoryServer.getServerErrorResultCode(); |
| | | } |
| | | |
| | | |
| | | MessageBuilder errorMessage = operation.getErrorMessage(); |
| | | DN matchedDN = operation.getMatchedDN(); |
| | | |
| | | DN matchedDN = operation.getMatchedDN(); |
| | | |
| | | // Referrals are not allowed for LDAPv2 clients. |
| | | List<String> referralURLs; |
| | |
| | | } |
| | | |
| | | List<String> opReferrals = operation.getReferralURLs(); |
| | | if ((opReferrals != null) && (! opReferrals.isEmpty())) |
| | | if ((opReferrals != null) && (!opReferrals.isEmpty())) |
| | | { |
| | | StringBuilder referralsStr = new StringBuilder(); |
| | | Iterator<String> iterator = opReferrals.iterator(); |
| | |
| | | referralsStr.append(iterator.next()); |
| | | } |
| | | |
| | | errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get( |
| | | String.valueOf(referralsStr))); |
| | | errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get(String |
| | | .valueOf(referralsStr))); |
| | | } |
| | | } |
| | | else |
| | |
| | | ProtocolOp protocolOp; |
| | | switch (operation.getOperationType()) |
| | | { |
| | | case ADD: |
| | | protocolOp = new AddResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | case BIND: |
| | | ASN1OctetString serverSASLCredentials = |
| | | ((BindOperationBasis) operation).getServerSASLCredentials(); |
| | | protocolOp = new BindResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, |
| | | referralURLs, serverSASLCredentials); |
| | | break; |
| | | case COMPARE: |
| | | protocolOp = new CompareResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | case DELETE: |
| | | protocolOp = new DeleteResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | case EXTENDED: |
| | | // If this an LDAPv2 client, then we can't send this. |
| | | if (ldapVersion == 2) |
| | | { |
| | | logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get( |
| | | getConnectionID(), operation.getOperationID(), |
| | | String.valueOf(operation))); |
| | | return null; |
| | | } |
| | | |
| | | ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation; |
| | | protocolOp = new ExtendedResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs, |
| | | extOp.getResponseOID(), extOp.getResponseValue()); |
| | | break; |
| | | case MODIFY: |
| | | protocolOp = new ModifyResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | case MODIFY_DN: |
| | | protocolOp = new ModifyDNResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | case SEARCH: |
| | | protocolOp = new SearchResultDoneProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), |
| | | matchedDN, referralURLs); |
| | | break; |
| | | default: |
| | | // This must be a type of operation that doesn't have a response. This |
| | | // shouldn't happen, so log a message and return. |
| | | logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get( |
| | | String.valueOf(operation.getOperationType()), |
| | | getConnectionID(), |
| | | operation.getOperationID(), |
| | | String.valueOf(operation))); |
| | | case ADD: |
| | | protocolOp = |
| | | new AddResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | case BIND: |
| | | ByteString serverSASLCredentials = |
| | | ((BindOperationBasis) operation).getServerSASLCredentials(); |
| | | protocolOp = |
| | | new BindResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs, |
| | | serverSASLCredentials); |
| | | break; |
| | | case COMPARE: |
| | | protocolOp = |
| | | new CompareResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | case DELETE: |
| | | protocolOp = |
| | | new DeleteResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | case EXTENDED: |
| | | // If this an LDAPv2 client, then we can't send this. |
| | | if (ldapVersion == 2) |
| | | { |
| | | logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get( |
| | | getConnectionID(), operation.getOperationID(), String |
| | | .valueOf(operation))); |
| | | return null; |
| | | } |
| | | |
| | | ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation; |
| | | protocolOp = |
| | | new ExtendedResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs, extOp |
| | | .getResponseOID(), extOp.getResponseValue()); |
| | | break; |
| | | case MODIFY: |
| | | protocolOp = |
| | | new ModifyResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | case MODIFY_DN: |
| | | protocolOp = |
| | | new ModifyDNResponseProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | case SEARCH: |
| | | protocolOp = |
| | | new SearchResultDoneProtocolOp(resultCode.getIntValue(), |
| | | errorMessage.toMessage(), matchedDN, referralURLs); |
| | | break; |
| | | default: |
| | | // This must be a type of operation that doesn't have a response. |
| | | // This shouldn't happen, so log a message and return. |
| | | logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get(String |
| | | .valueOf(operation.getOperationType()), getConnectionID(), |
| | | operation.getOperationID(), String.valueOf(operation))); |
| | | return null; |
| | | } |
| | | |
| | | |
| | | // Controls are not allowed for LDAPv2 clients. |
| | | ArrayList<LDAPControl> controls; |
| | | List<Control> controls; |
| | | if (ldapVersion == 2) |
| | | { |
| | | controls = null; |
| | | } |
| | | else |
| | | { |
| | | List<Control> responseControls = operation.getResponseControls(); |
| | | if ((responseControls == null) || responseControls.isEmpty()) |
| | | { |
| | | controls = null; |
| | | } |
| | | else |
| | | { |
| | | controls = new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | controls.add(new LDAPControl(c)); |
| | | } |
| | | } |
| | | controls = operation.getResponseControls(); |
| | | } |
| | | |
| | | return new LDAPMessage(operation.getMessageID(), protocolOp, controls); |
| | | return new LDAPMessage(operation.getMessageID(), protocolOp, |
| | | controls); |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Sends the provided search result entry to the client. |
| | | * |
| | | * @param searchOperation The search operation with which the entry is |
| | | * associated. |
| | | * @param searchEntry The search result entry to be sent to the client. |
| | | * @param searchOperation |
| | | * The search operation with which the entry is associated. |
| | | * @param searchEntry |
| | | * The search result entry to be sent to the client. |
| | | */ |
| | | @Override |
| | | public void sendSearchEntry(SearchOperation searchOperation, |
| | | SearchResultEntry searchEntry) |
| | | SearchResultEntry searchEntry) |
| | | { |
| | | SearchResultEntryProtocolOp protocolOp = |
| | | new SearchResultEntryProtocolOp(searchEntry,ldapVersion); |
| | | new SearchResultEntryProtocolOp(searchEntry, ldapVersion); |
| | | |
| | | List<Control> entryControls = searchEntry.getControls(); |
| | | ArrayList<LDAPControl> controls; |
| | | if ((entryControls == null) || entryControls.isEmpty()) |
| | | { |
| | | controls = null; |
| | | } |
| | | else |
| | | { |
| | | controls = new ArrayList<LDAPControl>(entryControls.size()); |
| | | for (Control c : entryControls) |
| | | { |
| | | controls.add(new LDAPControl(c)); |
| | | } |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(searchOperation.getMessageID(), protocolOp, |
| | | controls)); |
| | | sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(), |
| | | protocolOp, searchEntry.getControls())); |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Sends the provided search result reference to the client. |
| | | * |
| | | * @param searchOperation The search operation with which the reference is |
| | | * associated. |
| | | * @param searchReference The search result reference to be sent to the |
| | | * client. |
| | | * |
| | | * @return <CODE>true</CODE> if the client is able to accept referrals, or |
| | | * <CODE>false</CODE> if the client cannot handle referrals and no |
| | | * more attempts should be made to send them for the associated |
| | | * search operation. |
| | | * @param searchOperation |
| | | * The search operation with which the reference is |
| | | * associated. |
| | | * @param searchReference |
| | | * The search result reference to be sent to the client. |
| | | * @return <CODE>true</CODE> if the client is able to accept |
| | | * referrals, or <CODE>false</CODE> if the client cannot |
| | | * handle referrals and no more attempts should be made to |
| | | * send them for the associated search operation. |
| | | */ |
| | | @Override |
| | | public boolean sendSearchReference(SearchOperation searchOperation, |
| | | SearchResultReference searchReference) |
| | | SearchResultReference searchReference) |
| | | { |
| | | // Make sure this is not an LDAPv2 client. If it is, then they can't see |
| | | // referrals so we'll not send anything. Also, throw an exception so that |
| | | // the core server will know not to try sending any more referrals to this |
| | | // client for the rest of the operation. |
| | | // Make sure this is not an LDAPv2 client. If it is, then they can't |
| | | // see referrals so we'll not send anything. Also, throw an |
| | | // exception so that the core server will know not to try sending |
| | | // any more referrals to this client for the rest of the operation. |
| | | if (ldapVersion == 2) |
| | | { |
| | | Message message = ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE. |
| | | get(getConnectionID(), searchOperation.getOperationID(), |
| | | String.valueOf(searchReference)); |
| | | Message message = |
| | | ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE.get(getConnectionID(), |
| | | searchOperation.getOperationID(), String |
| | | .valueOf(searchReference)); |
| | | logError(message); |
| | | return false; |
| | | } |
| | | |
| | | SearchResultReferenceProtocolOp protocolOp = |
| | | new SearchResultReferenceProtocolOp(searchReference); |
| | | new SearchResultReferenceProtocolOp(searchReference); |
| | | |
| | | List<Control> referenceControls = searchReference.getControls(); |
| | | ArrayList<LDAPControl> controls; |
| | | if ((referenceControls == null) || referenceControls.isEmpty()) |
| | | { |
| | | controls = null; |
| | | } |
| | | else |
| | | { |
| | | controls = new ArrayList<LDAPControl>(referenceControls.size()); |
| | | for (Control c : referenceControls) |
| | | { |
| | | controls.add(new LDAPControl(c)); |
| | | } |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(searchOperation.getMessageID(), protocolOp, |
| | | controls)); |
| | | sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(), |
| | | protocolOp, searchReference.getControls())); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sends the provided intermediate response message to the client. |
| | | * |
| | | * @param intermediateResponse The intermediate response message to be sent. |
| | | * |
| | | * @return <CODE>true</CODE> if processing on the associated operation should |
| | | * continue, or <CODE>false</CODE> if not. |
| | | * @param intermediateResponse |
| | | * The intermediate response message to be sent. |
| | | * @return <CODE>true</CODE> if processing on the associated operation |
| | | * should continue, or <CODE>false</CODE> if not. |
| | | */ |
| | | @Override |
| | | protected boolean sendIntermediateResponseMessage( |
| | | IntermediateResponse intermediateResponse) |
| | | IntermediateResponse intermediateResponse) |
| | | { |
| | | IntermediateResponseProtocolOp protocolOp = |
| | | new IntermediateResponseProtocolOp(intermediateResponse.getOID(), |
| | | intermediateResponse.getValue()); |
| | | new IntermediateResponseProtocolOp(intermediateResponse |
| | | .getOID(), intermediateResponse.getValue()); |
| | | |
| | | Operation operation = intermediateResponse.getOperation(); |
| | | |
| | | List<Control> controls = intermediateResponse.getControls(); |
| | | ArrayList<LDAPControl> ldapControls = |
| | | new ArrayList<LDAPControl>(controls.size()); |
| | | for (Control c : controls) |
| | | { |
| | | ldapControls.add(new LDAPControl(c)); |
| | | } |
| | | LDAPMessage message = |
| | | new LDAPMessage(operation.getMessageID(), protocolOp, |
| | | intermediateResponse.getControls()); |
| | | sendLDAPMessage(message); |
| | | |
| | | |
| | | LDAPMessage message = new LDAPMessage(operation.getMessageID(), protocolOp, |
| | | ldapControls); |
| | | sendLDAPMessage(securityProvider, message); |
| | | |
| | | |
| | | // The only reason we shouldn't continue processing is if the connection is |
| | | // closed. |
| | | // The only reason we shouldn't continue processing is if the |
| | | // connection is closed. |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Sends the provided LDAP message to the client. |
| | | * |
| | | * @param secProvider The connection security provider to use to handle any |
| | | * necessary security translation. |
| | | * @param message The LDAP message to send to the client. |
| | | * @param message |
| | | * The LDAP message to send to the client. |
| | | */ |
| | | public void sendLDAPMessage(ConnectionSecurityProvider secProvider, |
| | | LDAPMessage message) |
| | | public void sendLDAPMessage(LDAPMessage message) |
| | | { |
| | | ASN1Element messageElement = message.encode(); |
| | | |
| | | ByteBuffer messageBuffer = ByteBuffer.wrap(messageElement.encode()); |
| | | |
| | | |
| | | // Make sure that we can only send one message at a time. This locking will |
| | | // not have any impact on the ability to read requests from the client. |
| | | synchronized (transmitLock) |
| | | // Get the buffer used by this thread. |
| | | WriterBuffer writerBuffer = cachedBuffers.get(); |
| | | if (writerBuffer == null) |
| | | { |
| | | try |
| | | writerBuffer = new WriterBuffer(); |
| | | // TODO SASLPhase2 maybe don't want to cache these |
| | | if (isSecure()) |
| | | { |
| | | try |
| | | int appBufSize = activeProvider.getAppBufSize(); |
| | | writerBuffer.writer = ASN1.getWriter(saslChannel, appBufSize); |
| | | } |
| | | else |
| | | writerBuffer.writer = ASN1.getWriter(saslChannel, 4096); |
| | | cachedBuffers.set(writerBuffer); |
| | | } |
| | | try |
| | | { |
| | | // Make sure that we can only send one message at a time. This |
| | | // locking will not have any impact on the ability to read |
| | | // requests from the client. |
| | | synchronized (transmitLock) |
| | | { |
| | | message.write(writerBuffer.writer); |
| | | writerBuffer.writer.flush(); |
| | | if(debugEnabled()) |
| | | { |
| | | int bytesWritten = messageBuffer.limit() - messageBuffer.position(); |
| | | if (! secProvider.writeData(messageBuffer)) |
| | | { |
| | | return; |
| | | } |
| | | TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, |
| | | message.toString()); |
| | | |
| | | TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, message); |
| | | TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, messageElement); |
| | | |
| | | messageBuffer.rewind(); |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugData(DebugLogLevel.VERBOSE, messageBuffer); |
| | | } |
| | | |
| | | if (keepStats) |
| | | { |
| | | statTracker.updateMessageWritten(message, bytesWritten); |
| | | } |
| | | // TODO SASLPhase2 message buffer? |
| | | // TRACER.debugData(DebugLogLevel.VERBOSE, messageBuffer); |
| | | } |
| | | catch (@Deprecated Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // We were unable to send the message due to some other internal |
| | | // problem. Disconnect from the client and return. |
| | | disconnect(DisconnectReason.SERVER_ERROR, true, null); |
| | | return; |
| | | if (keepStats) |
| | | { |
| | | // TODO SASLPhase2 hard-coded for now, flush probably needs to |
| | | // return how many bytes were flushed. |
| | | statTracker.updateMessageWritten(message, 4096); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | // writerBuffer.buffer.clear(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // FIXME -- Log a message or something |
| | | disconnect(DisconnectReason.SERVER_ERROR, true, null); |
| | | return; |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // FIXME -- Log a message or something |
| | | disconnect(DisconnectReason.SERVER_ERROR, true, null); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Closes the connection to the client, optionally sending it a message |
| | | * indicating the reason for the closure. Note that the ability to send a |
| | | * notice of disconnection may not be available for all protocols or under all |
| | | * circumstances. |
| | | * Closes the connection to the client, optionally sending it a |
| | | * message indicating the reason for the closure. Note that the |
| | | * ability to send a notice of disconnection may not be available for |
| | | * all protocols or under all circumstances. |
| | | * |
| | | * @param disconnectReason The disconnect reason that provides the generic |
| | | * cause for the disconnect. |
| | | * @param sendNotification Indicates whether to try to provide notification |
| | | * to the client that the connection will be closed. |
| | | * @param message The message to include in the disconnect |
| | | * notification response. It may be |
| | | * <CODE>null</CODE> if no message is to be sent. |
| | | * @param disconnectReason |
| | | * The disconnect reason that provides the generic cause for |
| | | * the disconnect. |
| | | * @param sendNotification |
| | | * Indicates whether to try to provide notification to the |
| | | * client that the connection will be closed. |
| | | * @param message |
| | | * The message to include in the disconnect notification |
| | | * response. It may be <CODE>null</CODE> if no message is to |
| | | * be sent. |
| | | */ |
| | | @Override |
| | | public void disconnect(DisconnectReason disconnectReason, |
| | | boolean sendNotification, |
| | | Message message) |
| | | boolean sendNotification, Message message) |
| | | { |
| | | // Set a flag indicating that the connection is being terminated so that no |
| | | // new requests will be accepted. Also cancel all operations in progress. |
| | | // Set a flag indicating that the connection is being terminated so |
| | | // that no new requests will be accepted. Also cancel all operations |
| | | // in progress. |
| | | synchronized (opsInProgressLock) |
| | | { |
| | | // If we are already in the middle of a disconnect, then don't |
| | |
| | | disconnectRequested = true; |
| | | } |
| | | |
| | | |
| | | if (keepStats) |
| | | { |
| | | statTracker.updateDisconnect(); |
| | |
| | | DirectoryServer.connectionClosed(this); |
| | | } |
| | | |
| | | |
| | | // Indicate that this connection is no longer valid. |
| | | connectionValid = false; |
| | | |
| | | if(message != null) |
| | | if (message != null) |
| | | { |
| | | MessageBuilder msgBuilder = new MessageBuilder(); |
| | | msgBuilder.append(disconnectReason.getClosureMessage()); |
| | | msgBuilder.append(": "); |
| | | msgBuilder.append(message); |
| | | cancelAllOperations(new CancelRequest(true, msgBuilder.toMessage())); |
| | | cancelAllOperations(new CancelRequest(true, msgBuilder |
| | | .toMessage())); |
| | | } |
| | | else |
| | | { |
| | | cancelAllOperations(new CancelRequest(true, |
| | | disconnectReason.getClosureMessage())); |
| | | cancelAllOperations(new CancelRequest(true, disconnectReason |
| | | .getClosureMessage())); |
| | | } |
| | | finalizeConnectionInternal(); |
| | | |
| | | |
| | | // If there is a write selector for this connection, then close it. |
| | | Selector selector = writeSelector.get(); |
| | | if (selector != null) |
| | |
| | | try |
| | | { |
| | | selector.close(); |
| | | } catch (Exception e) {} |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | } |
| | | } |
| | | |
| | | |
| | | // See if we should send a notification to the client. If so, then |
| | | // construct and send a notice of disconnection unsolicited response. |
| | | // Note that we cannot send this notification to an LDAPv2 client. |
| | | // See if we should send a notification to the client. If so, then |
| | | // construct and send a notice of disconnection unsolicited |
| | | // response. Note that we cannot send this notification to an LDAPv2 |
| | | // client. |
| | | if (sendNotification && (ldapVersion != 2)) |
| | | { |
| | | try |
| | |
| | | int resultCode; |
| | | switch (disconnectReason) |
| | | { |
| | | case PROTOCOL_ERROR: |
| | | resultCode = LDAPResultCode.PROTOCOL_ERROR; |
| | | break; |
| | | case SERVER_SHUTDOWN: |
| | | resultCode = LDAPResultCode.UNAVAILABLE; |
| | | break; |
| | | case SERVER_ERROR: |
| | | resultCode = |
| | | DirectoryServer.getServerErrorResultCode().getIntValue(); |
| | | break; |
| | | case ADMIN_LIMIT_EXCEEDED: |
| | | case IDLE_TIME_LIMIT_EXCEEDED: |
| | | case MAX_REQUEST_SIZE_EXCEEDED: |
| | | case IO_TIMEOUT: |
| | | resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED; |
| | | break; |
| | | case CONNECTION_REJECTED: |
| | | resultCode = LDAPResultCode.CONSTRAINT_VIOLATION; |
| | | break; |
| | | default: |
| | | resultCode = LDAPResultCode.OTHER; |
| | | break; |
| | | case PROTOCOL_ERROR: |
| | | resultCode = LDAPResultCode.PROTOCOL_ERROR; |
| | | break; |
| | | case SERVER_SHUTDOWN: |
| | | resultCode = LDAPResultCode.UNAVAILABLE; |
| | | break; |
| | | case SERVER_ERROR: |
| | | resultCode = |
| | | DirectoryServer.getServerErrorResultCode().getIntValue(); |
| | | break; |
| | | case ADMIN_LIMIT_EXCEEDED: |
| | | case IDLE_TIME_LIMIT_EXCEEDED: |
| | | case MAX_REQUEST_SIZE_EXCEEDED: |
| | | case IO_TIMEOUT: |
| | | resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED; |
| | | break; |
| | | case CONNECTION_REJECTED: |
| | | resultCode = LDAPResultCode.CONSTRAINT_VIOLATION; |
| | | break; |
| | | default: |
| | | resultCode = LDAPResultCode.OTHER; |
| | | break; |
| | | } |
| | | |
| | | |
| | | Message errMsg; |
| | | if (message == null) |
| | | { |
| | | errMsg = INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get(); |
| | | errMsg = |
| | | INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get(); |
| | | } |
| | | else |
| | | { |
| | | errMsg = message; |
| | | } |
| | | |
| | | |
| | | ExtendedResponseProtocolOp notificationOp = |
| | | new ExtendedResponseProtocolOp(resultCode, errMsg, null, null, |
| | | OID_NOTICE_OF_DISCONNECTION, null); |
| | | byte[] messageBytes = |
| | | new LDAPMessage(0, notificationOp, null).encode().encode(); |
| | | ByteBuffer buffer = ByteBuffer.wrap(messageBytes); |
| | | try |
| | | { |
| | | securityProvider.writeData(buffer); |
| | | } catch (Exception e) {} |
| | | new ExtendedResponseProtocolOp(resultCode, errMsg, null, |
| | | null, OID_NOTICE_OF_DISCONNECTION, null); |
| | | |
| | | sendLDAPMessage(new LDAPMessage(0, notificationOp, null)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // NYI -- Log a message indicating that we couldn't send the notice of |
| | | // disconnection. |
| | | // NYI -- Log a message indicating that we couldn't send the |
| | | // notice of disconnection. |
| | | } |
| | | } |
| | | |
| | | |
| | | // Close the connection to the client. |
| | | try |
| | | { |
| | | securityProvider.disconnect(sendNotification); |
| | | asn1Reader.close(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // In general, we don't care about any exception that might be thrown |
| | | // here. |
| | | // In general, we don't care about any exception that might be |
| | | // thrown here. |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // In general, we don't care about any exception that might be thrown |
| | | // here. |
| | | // In general, we don't care about any exception that might be |
| | | // thrown here. |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | |
| | | // Remove the thread local buffers. |
| | | cachedBuffers.remove(); |
| | | |
| | | // NYI -- Deregister the client connection from any server components that |
| | | // might know about it. |
| | | |
| | | |
| | | // Log a disconnect message. |
| | | logDisconnect(this, disconnectReason, message); |
| | | |
| | | |
| | | try |
| | | { |
| | | PluginConfigManager pluginManager = |
| | | DirectoryServer.getPluginConfigManager(); |
| | | DirectoryServer.getPluginConfigManager(); |
| | | pluginManager.invokePostDisconnectPlugins(this, disconnectReason, |
| | | message); |
| | | message); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the set of operations in progress for this client connection. |
| | | * This list must not be altered by any caller. |
| | | * Retrieves the set of operations in progress for this client |
| | | * connection. This list must not be altered by any caller. |
| | | * |
| | | * @return The set of operations in progress for this client connection. |
| | | * @return The set of operations in progress for this client |
| | | * connection. |
| | | */ |
| | | @Override |
| | | public Collection<Operation> getOperationsInProgress() |
| | | { |
| | | return operationsInProgress.values(); |
| | |
| | | /** |
| | | * Retrieves the operation in progress with the specified message ID. |
| | | * |
| | | * @param messageID The message ID for the operation to retrieve. |
| | | * |
| | | * @return The operation in progress with the specified message ID, or |
| | | * <CODE>null</CODE> if no such operation could be found. |
| | | * @param messageID |
| | | * The message ID for the operation to retrieve. |
| | | * @return The operation in progress with the specified message ID, or |
| | | * <CODE>null</CODE> if no such operation could be found. |
| | | */ |
| | | @Override |
| | | public Operation getOperationInProgress(int messageID) |
| | | { |
| | | return operationsInProgress.get(messageID); |
| | |
| | | |
| | | |
| | | /** |
| | | * Adds the provided operation to the set of operations in progress for this |
| | | * client connection. |
| | | * Adds the provided operation to the set of operations in progress |
| | | * for this client connection. |
| | | * |
| | | * @param operation The operation to add to the set of operations in |
| | | * progress for this client connection. |
| | | * |
| | | * @throws DirectoryException If the operation is not added for some reason |
| | | * (e.g., the client already has reached the |
| | | * maximum allowed concurrent requests). |
| | | * @param operation |
| | | * The operation to add to the set of operations in progress |
| | | * for this client connection. |
| | | * @throws DirectoryException |
| | | * If the operation is not added for some reason (e.g., the |
| | | * client already has reached the maximum allowed concurrent |
| | | * requests). |
| | | */ |
| | | public void addOperationInProgress(AbstractOperation operation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | int messageID = operation.getMessageID(); |
| | | |
| | | // We need to grab a lock to ensure that no one else can add operations to |
| | | // the queue while we are performing some preliminary checks. |
| | | // We need to grab a lock to ensure that no one else can add |
| | | // operations to the queue while we are performing some preliminary |
| | | // checks. |
| | | synchronized (opsInProgressLock) |
| | | { |
| | | try |
| | | { |
| | | // If we're already in the process of disconnecting the client, then |
| | | // reject the operation. |
| | | // If we're already in the process of disconnecting the client, |
| | | // then reject the operation. |
| | | if (disconnectRequested) |
| | | { |
| | | Message message = WARN_LDAP_CLIENT_DISCONNECT_IN_PROGRESS.get(); |
| | | Message message = |
| | | WARN_LDAP_CLIENT_DISCONNECT_IN_PROGRESS.get(); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message); |
| | | message); |
| | | } |
| | | |
| | | |
| | | // See if there is already an operation in progress with the same |
| | | // message ID. If so, then we can't allow it. |
| | | // See if there is already an operation in progress with the |
| | | // same message ID. If so, then we can't allow it. |
| | | Operation op = operationsInProgress.get(messageID); |
| | | if (op != null) |
| | | { |
| | | Message message = |
| | | WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID); |
| | | throw new DirectoryException(ResultCode.PROTOCOL_ERROR, message); |
| | | WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID); |
| | | throw new DirectoryException(ResultCode.PROTOCOL_ERROR, |
| | | message); |
| | | } |
| | | |
| | | |
| | | // Add the operation to the list of operations in progress for this |
| | | // connection. |
| | | // Add the operation to the list of operations in progress for |
| | | // this connection. |
| | | operationsInProgress.put(messageID, operation); |
| | | |
| | | |
| | | // Try to add the operation to the work queue, |
| | | // or run it synchronously (typically for the administration connector) |
| | | connectionHandler.getQueueingStrategy().enqueueRequest(operation); |
| | | // or run it synchronously (typically for the administration |
| | | // connector) |
| | | connectionHandler.getQueueingStrategy().enqueueRequest( |
| | | operation); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | |
| | | |
| | | Message message = |
| | | WARN_LDAP_CLIENT_CANNOT_ENQUEUE.get(getExceptionMessage(e)); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message, e); |
| | | throw new DirectoryException(DirectoryServer |
| | | .getServerErrorResultCode(), message, e); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Removes the provided operation from the set of operations in progress for |
| | | * this client connection. Note that this does not make any attempt to |
| | | * cancel any processing that may already be in progress for the operation. |
| | | * Removes the provided operation from the set of operations in |
| | | * progress for this client connection. Note that this does not make |
| | | * any attempt to cancel any processing that may already be in |
| | | * progress for the operation. |
| | | * |
| | | * @param messageID The message ID of the operation to remove from the set |
| | | * of operations in progress. |
| | | * |
| | | * @return <CODE>true</CODE> if the operation was found and removed from the |
| | | * set of operations in progress, or <CODE>false</CODE> if not. |
| | | * @param messageID |
| | | * The message ID of the operation to remove from the set of |
| | | * operations in progress. |
| | | * @return <CODE>true</CODE> if the operation was found and removed |
| | | * from the set of operations in progress, or |
| | | * <CODE>false</CODE> if not. |
| | | */ |
| | | @Override |
| | | public boolean removeOperationInProgress(int messageID) |
| | | { |
| | | Operation operation = operationsInProgress.remove(messageID); |
| | |
| | | /** |
| | | * Attempts to cancel the specified operation. |
| | | * |
| | | * @param messageID The message ID of the operation to cancel. |
| | | * @param cancelRequest An object providing additional information about how |
| | | * the cancel should be processed. |
| | | * |
| | | * @return A cancel result that either indicates that the cancel was |
| | | * successful or provides a reason that it was not. |
| | | * @param messageID |
| | | * The message ID of the operation to cancel. |
| | | * @param cancelRequest |
| | | * An object providing additional information about how the |
| | | * cancel should be processed. |
| | | * @return A cancel result that either indicates that the cancel was |
| | | * successful or provides a reason that it was not. |
| | | */ |
| | | @Override |
| | | public CancelResult cancelOperation(int messageID, |
| | | CancelRequest cancelRequest) |
| | | CancelRequest cancelRequest) |
| | | { |
| | | Operation op = operationsInProgress.get(messageID); |
| | | if (op == null) |
| | |
| | | /** |
| | | * Attempts to cancel all operations in progress on this connection. |
| | | * |
| | | * @param cancelRequest An object providing additional information about how |
| | | * the cancel should be processed. |
| | | * @param cancelRequest |
| | | * An object providing additional information about how the |
| | | * cancel should be processed. |
| | | */ |
| | | @Override |
| | | public void cancelAllOperations(CancelRequest cancelRequest) |
| | | { |
| | | // Make sure that no one can add any new operations. |
| | |
| | | } |
| | | } |
| | | |
| | | if (! (operationsInProgress.isEmpty() && |
| | | getPersistentSearches().isEmpty())) |
| | | if (!(operationsInProgress.isEmpty() && getPersistentSearches() |
| | | .isEmpty())) |
| | | { |
| | | lastCompletionTime.set(TimeThread.getTime()); |
| | | } |
| | | |
| | | operationsInProgress.clear(); |
| | | |
| | | |
| | | for (PersistentSearch persistentSearch : getPersistentSearches()) |
| | | { |
| | | persistentSearch.cancel(); |
| | |
| | | |
| | | |
| | | /** |
| | | * Attempts to cancel all operations in progress on this connection except the |
| | | * operation with the specified message ID. |
| | | * Attempts to cancel all operations in progress on this connection |
| | | * except the operation with the specified message ID. |
| | | * |
| | | * @param cancelRequest An object providing additional information about how |
| | | * the cancel should be processed. |
| | | * @param messageID The message ID of the operation that should not be |
| | | * canceled. |
| | | * @param cancelRequest |
| | | * An object providing additional information about how the |
| | | * cancel should be processed. |
| | | * @param messageID |
| | | * The message ID of the operation that should not be |
| | | * canceled. |
| | | */ |
| | | @Override |
| | | public void cancelAllOperationsExcept(CancelRequest cancelRequest, |
| | | int messageID) |
| | | int messageID) |
| | | { |
| | | // Make sure that no one can add any new operations. |
| | | synchronized (opsInProgressLock) |
| | |
| | | lastCompletionTime.set(TimeThread.getTime()); |
| | | } |
| | | |
| | | |
| | | for (PersistentSearch persistentSearch : getPersistentSearches()) |
| | | { |
| | | if (persistentSearch.getMessageID() == messageID) |
| | |
| | | try |
| | | { |
| | | selector = Selector.open(); |
| | | if (! writeSelector.compareAndSet(null, selector)) |
| | | if (!writeSelector.compareAndSet(null, selector)) |
| | | { |
| | | selector.close(); |
| | | selector = writeSelector.get(); |
| | |
| | | |
| | | |
| | | /** |
| | | * Returns the total number of operations initiated on this connection. |
| | | * Returns the total number of operations initiated on this |
| | | * connection. |
| | | * |
| | | * @return the total number of operations on this connection |
| | | */ |
| | | public long getNumberOfOperations() { |
| | | @Override |
| | | public long getNumberOfOperations() |
| | | { |
| | | long tmpNumberOfOperations; |
| | | synchronized (operationsPerformedLock) { |
| | | synchronized (operationsPerformedLock) |
| | | { |
| | | tmpNumberOfOperations = operationsPerformed; |
| | | } |
| | | return tmpNumberOfOperations; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Process the information contained in the provided byte buffer as an ASN.1 |
| | | * element. It may take several calls to this method in order to get all the |
| | | * information necessary to decode a single ASN.1 element, but it may also be |
| | | * possible that there are multiple elements (or at least fragments of |
| | | * multiple elements) in a single buffer. This will fully process whatever |
| | | * the client provided and set up the appropriate state information to make it |
| | | * possible to pick up in the right place the next time around. |
| | | * Process data read. |
| | | * |
| | | * @param buffer The buffer containing the data to be processed. It must be |
| | | * ready for reading (i.e., it should have been flipped by the |
| | | * caller), and the data provided must be unencrypted (e.g., |
| | | * if the client is communicating over SSL, then the |
| | | * decryption should happen before calling this method). |
| | | * |
| | | * @return <CODE>true</CODE> if all the data in the provided buffer was |
| | | * processed and the client connection can remain established, or |
| | | * <CODE>false</CODE> if a decoding error occurred and requests from |
| | | * this client should no longer be processed. Note that if this |
| | | * method does return <CODE>false</CODE>, then it must have already |
| | | * disconnected the client, and upon returning the request handler |
| | | * should remove it from the associated selector. |
| | | * @return {@code true} if this connection is still valid. |
| | | */ |
| | | public boolean processDataRead(ByteBuffer buffer) |
| | | public boolean processDataRead() |
| | | { |
| | | if (debugEnabled()) |
| | | if (this.saslPendingProvider != null) |
| | | { |
| | | TRACER.debugData(DebugLogLevel.VERBOSE, buffer); |
| | | enableSASL(); |
| | | } |
| | | |
| | | |
| | | int bytesAvailable = buffer.limit() - buffer.position(); |
| | | |
| | | if (keepStats) |
| | | while (true) |
| | | { |
| | | statTracker.updateBytesRead(bytesAvailable); |
| | | } |
| | | |
| | | while (bytesAvailable > 0) |
| | | { |
| | | switch (elementReadState) |
| | | try |
| | | { |
| | | case ELEMENT_READ_STATE_NEED_TYPE: |
| | | // Read just the type and then loop again to see if there is more. |
| | | elementType = buffer.get(); |
| | | bytesAvailable--; |
| | | elementReadState = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; |
| | | continue; |
| | | |
| | | |
| | | case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE: |
| | | // Get the first length byte and see if it is a single-byte or |
| | | // multi-byte length. |
| | | byte firstLengthByte = buffer.get(); |
| | | bytesAvailable--; |
| | | elementLengthBytesNeeded = (firstLengthByte & 0x7F); |
| | | if (elementLengthBytesNeeded == firstLengthByte) |
| | | { |
| | | elementLength = firstLengthByte; |
| | | |
| | | // If the length is zero, then it cannot be a valid LDAP message. |
| | | if (elementLength == 0) |
| | | { |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, |
| | | ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); |
| | | return false; |
| | | } |
| | | |
| | | // Make sure that the element is not larger than the maximum allowed |
| | | // message size. |
| | | if ((connectionHandler.getMaxRequestSize() > 0) && |
| | | (elementLength > connectionHandler.getMaxRequestSize())) |
| | | { |
| | | Message m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( |
| | | elementLength, connectionHandler.getMaxRequestSize()); |
| | | disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, m); |
| | | return false; |
| | | } |
| | | |
| | | elementValue = new byte[elementLength]; |
| | | elementValueBytesRead = 0; |
| | | elementValueBytesNeeded = elementLength; |
| | | elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | if (elementLengthBytesNeeded > 4) |
| | | { |
| | | // We cannot handle multi-byte lengths in which more than four |
| | | // bytes are used to encode the length. |
| | | Message m = ERR_LDAP_CLIENT_DECODE_INVALID_MULTIBYTE_LENGTH.get( |
| | | elementLengthBytesNeeded); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); |
| | | return false; |
| | | } |
| | | |
| | | elementLength = 0x00; |
| | | if (elementLengthBytesNeeded <= bytesAvailable) |
| | | { |
| | | // We can read the entire length, so do it. |
| | | while (elementLengthBytesNeeded > 0) |
| | | { |
| | | elementLength = (elementLength << 8) | (buffer.get() & 0xFF); |
| | | bytesAvailable--; |
| | | elementLengthBytesNeeded--; |
| | | } |
| | | |
| | | // If the length is zero, then it cannot be a valid LDAP message. |
| | | if (elementLength == 0) |
| | | { |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, |
| | | ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); |
| | | return false; |
| | | } |
| | | |
| | | // Make sure that the element is not larger than the maximum |
| | | // allowed message size. |
| | | if ((connectionHandler.getMaxRequestSize() > 0) && |
| | | (elementLength > connectionHandler.getMaxRequestSize())) |
| | | { |
| | | disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, |
| | | ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( |
| | | elementLength, |
| | | connectionHandler.getMaxRequestSize())); |
| | | return false; |
| | | } |
| | | |
| | | elementValue = new byte[elementLength]; |
| | | elementValueBytesRead = 0; |
| | | elementValueBytesNeeded = elementLength; |
| | | elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | // We can't read the entire length, so just read what is |
| | | // available. |
| | | while (bytesAvailable > 0) |
| | | { |
| | | elementLength = (elementLength << 8) | (buffer.get() & 0xFF); |
| | | bytesAvailable--; |
| | | elementLengthBytesNeeded--; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | |
| | | case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES: |
| | | if (bytesAvailable >= elementLengthBytesNeeded) |
| | | { |
| | | // We have enough data available to be able to read the entire |
| | | // length. Do so. |
| | | while (elementLengthBytesNeeded > 0) |
| | | { |
| | | elementLength = (elementLength << 8) | (buffer.get() & 0xFF); |
| | | bytesAvailable--; |
| | | elementLengthBytesNeeded--; |
| | | } |
| | | |
| | | // If the length is zero, then it cannot be a valid LDAP message. |
| | | if (elementLength == 0) |
| | | { |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, |
| | | ERR_LDAP_CLIENT_DECODE_ZERO_BYTE_VALUE.get()); |
| | | return false; |
| | | } |
| | | |
| | | // Make sure that the element is not larger than the maximum allowed |
| | | // message size. |
| | | if ((connectionHandler.getMaxRequestSize() > 0) && |
| | | (elementLength > connectionHandler.getMaxRequestSize())) |
| | | { |
| | | disconnect(DisconnectReason.MAX_REQUEST_SIZE_EXCEEDED, true, |
| | | ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get( |
| | | elementLength, |
| | | connectionHandler.getMaxRequestSize())); |
| | | return false; |
| | | } |
| | | |
| | | elementValue = new byte[elementLength]; |
| | | elementValueBytesRead = 0; |
| | | elementValueBytesNeeded = elementLength; |
| | | elementReadState = ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | // We still don't have enough data to complete the length, so just |
| | | // read as much as possible. |
| | | while (bytesAvailable > 0) |
| | | { |
| | | elementLength = (elementLength << 8) | (buffer.get() & 0xFF); |
| | | bytesAvailable--; |
| | | elementLengthBytesNeeded--; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | case ELEMENT_READ_STATE_NEED_VALUE_BYTES: |
| | | if (bytesAvailable >= elementValueBytesNeeded) |
| | | { |
| | | // We have enough data available to fully read the value. Finish |
| | | // reading the information and convert it to an ASN.1 element. Then |
| | | // decode that as an LDAP message. |
| | | buffer.get(elementValue, elementValueBytesRead, |
| | | elementValueBytesNeeded); |
| | | elementValueBytesRead += elementValueBytesNeeded; |
| | | bytesAvailable -= elementValueBytesNeeded; |
| | | elementReadState = ELEMENT_READ_STATE_NEED_TYPE; |
| | | |
| | | ASN1Sequence requestSequence; |
| | | try |
| | | { |
| | | requestSequence = ASN1Sequence.decodeAsSequence(elementType, |
| | | elementValue); |
| | | TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, |
| | | requestSequence); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | Message m = ERR_LDAP_CLIENT_DECODE_ASN1_FAILED.get( |
| | | String.valueOf(e)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); |
| | | return false; |
| | | } |
| | | |
| | | LDAPMessage requestMessage; |
| | | try |
| | | { |
| | | requestMessage = LDAPMessage.decode(requestSequence); |
| | | TRACER.debugProtocolElement(DebugLogLevel.VERBOSE, |
| | | requestMessage); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | Message m = ERR_LDAP_CLIENT_DECODE_LDAP_MESSAGE_FAILED.get( |
| | | String.valueOf(e)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); |
| | | return false; |
| | | } |
| | | |
| | | if (processLDAPMessage(requestMessage)) |
| | | { |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // We can't read all the value, so just read as much as we have |
| | | // available and pick it up again the next time around. |
| | | buffer.get(elementValue, elementValueBytesRead, bytesAvailable); |
| | | elementValueBytesRead += bytesAvailable; |
| | | elementValueBytesNeeded -= bytesAvailable; |
| | | return true; |
| | | } |
| | | |
| | | |
| | | default: |
| | | // This should never happen. There is an invalid internal read state. |
| | | // The only recourse that we have is to log a message and disconnect |
| | | // the client. |
| | | Message message = |
| | | ERR_LDAP_CLIENT_INVALID_DECODE_STATE.get(elementReadState); |
| | | logError(message); |
| | | disconnect(DisconnectReason.SERVER_ERROR, true, message); |
| | | int result = asn1Reader.processChannelData(); |
| | | if (result < 0) |
| | | { |
| | | // The connection has been closed by the client. Disconnect |
| | | // and return. |
| | | disconnect(DisconnectReason.CLIENT_DISCONNECT, false, null); |
| | | return false; |
| | | } |
| | | if (result == 0) |
| | | { |
| | | // We have read all the data that there is to read right now |
| | | // (or there wasn't any in the first place). Just return and |
| | | // wait for future notification. |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | // Decode all complete elements from the last read |
| | | while (asn1Reader.elementAvailable()) |
| | | { |
| | | processLDAPMessage(LDAPReader.readMessage(asn1Reader)); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | Message m = |
| | | ERR_LDAP_CLIENT_DECODE_LDAP_MESSAGE_FAILED.get(String |
| | | .valueOf(e)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, m); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then all of the data must have been processed |
| | | // properly so we can return true. |
| | | |
| | | /** |
| | | * Process the information contained in the provided byte buffer as an |
| | | * ASN.1 element. It may take several calls to this method in order to |
| | | * get all the information necessary to decode a single ASN.1 element, |
| | | * but it may also be possible that there are multiple elements (or at |
| | | * least fragments of multiple elements) in a single buffer. This will |
| | | * fully process whatever the client provided and set up the |
| | | * appropriate state information to make it possible to pick up in the |
| | | * right place the next time around. |
| | | * |
| | | * @param buffer |
| | | * The buffer containing the data to be processed. It must be |
| | | * ready for reading (i.e., it should have been flipped by |
| | | * the caller), and the data provided must be unencrypted |
| | | * (e.g., if the client is communicating over SSL, then the |
| | | * decryption should happen before calling this method). |
| | | * @return <CODE>true</CODE> if all the data in the provided buffer |
| | | * was processed and the client connection can remain |
| | | * established, or <CODE>false</CODE> if a decoding error |
| | | * occurred and requests from this client should no longer be |
| | | * processed. Note that if this method does return |
| | | * <CODE>false</CODE>, then it must have already disconnected |
| | | * the client, and upon returning the request handler should |
| | | * remove it from the associated selector. |
| | | */ |
| | | @Override |
| | | public boolean processDataRead(ByteBuffer buffer) |
| | | { |
| | | // this is no longer used. |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | /** |
| | | * Processes the provided LDAP message read from the client and takes |
| | | * whatever action is appropriate. For most requests, this will include |
| | | * placing the operation in the work queue. Certain requests (in particular, |
| | | * abandons and unbinds) will be processed directly. |
| | | * whatever action is appropriate. For most requests, this will |
| | | * include placing the operation in the work queue. Certain requests |
| | | * (in particular, abandons and unbinds) will be processed directly. |
| | | * |
| | | * @param message The LDAP message to process. |
| | | * |
| | | * @return <CODE>true</CODE> if the appropriate action was taken for the |
| | | * request, or <CODE>false</CODE> if there was a fatal error and |
| | | * the client has been disconnected as a result, or if the client |
| | | * unbound from the server. |
| | | * @param message |
| | | * The LDAP message to process. |
| | | * @return <CODE>true</CODE> if the appropriate action was taken for |
| | | * the request, or <CODE>false</CODE> if there was a fatal |
| | | * error and the client has been disconnected as a result, or |
| | | * if the client unbound from the server. |
| | | */ |
| | | private boolean processLDAPMessage(LDAPMessage message) |
| | | { |
| | |
| | | statTracker.updateMessageRead(message); |
| | | this.getNetworkGroup().updateMessageRead(message); |
| | | } |
| | | synchronized (operationsPerformedLock) { |
| | | synchronized (operationsPerformedLock) |
| | | { |
| | | operationsPerformed++; |
| | | } |
| | | |
| | | ArrayList<Control> opControls; |
| | | ArrayList<LDAPControl> ldapControls = message.getControls(); |
| | | if ((ldapControls == null) || ldapControls.isEmpty()) |
| | | { |
| | | opControls = null; |
| | | } |
| | | else |
| | | { |
| | | opControls = new ArrayList<Control>(ldapControls.size()); |
| | | for (LDAPControl c : ldapControls) |
| | | { |
| | | opControls.add(c.getControl()); |
| | | } |
| | | } |
| | | List<Control> opControls = message.getControls(); |
| | | |
| | | // FIXME -- See if there is a bind in progress. If so, then deny |
| | | // most kinds of operations. |
| | | |
| | | // FIXME -- See if there is a bind in progress. If so, then deny most |
| | | // kinds of operations. |
| | | |
| | | |
| | | // Figure out what type of operation we're dealing with based on the LDAP |
| | | // message. Abandon and unbind requests will be processed here. All other |
| | | // types of requests will be encapsulated into operations and put into the |
| | | // work queue to be picked up by a worker thread. Any other kinds of |
| | | // LDAP messages (e.g., response messages) are illegal and will result in |
| | | // the connection being terminated. |
| | | // Figure out what type of operation we're dealing with based on the |
| | | // LDAP message. Abandon and unbind requests will be processed here. |
| | | // All other types of requests will be encapsulated into operations |
| | | // and append into the work queue to be picked up by a worker |
| | | // thread. Any other kinds of LDAP messages (e.g., response |
| | | // messages) are illegal and will result in the connection being |
| | | // terminated. |
| | | try |
| | | { |
| | | boolean result; |
| | | switch (message.getProtocolOpType()) |
| | | { |
| | | case OP_TYPE_ABANDON_REQUEST: |
| | | if (keepStats) this.abandonMonitor.start(); |
| | | result=processAbandonRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.abandonMonitor.stop(); |
| | | this.abandonMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_ADD_REQUEST: |
| | | if (keepStats) this.addMonitor.start(); |
| | | result=processAddRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.addMonitor.stop(); |
| | | this.addMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_BIND_REQUEST: |
| | | if (keepStats) this.bindMonitor.start(); |
| | | result=processBindRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.bindMonitor.stop(); |
| | | this.bindMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_COMPARE_REQUEST: |
| | | if (keepStats) this.compareMonitor.start(); |
| | | result=processCompareRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.compareMonitor.stop(); |
| | | this.compareMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_DELETE_REQUEST: |
| | | if (keepStats) this.delMonitor.start(); |
| | | result=processDeleteRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.delMonitor.stop(); |
| | | this.delMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_EXTENDED_REQUEST: |
| | | if (keepStats) this.extendedMonitor.start(); |
| | | result=processExtendedRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.extendedMonitor.stop(); |
| | | this.extendedMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_MODIFY_REQUEST: |
| | | if (keepStats) this.modMonitor.start(); |
| | | result=processModifyRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.modMonitor.stop(); |
| | | this.modMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_MODIFY_DN_REQUEST: |
| | | if (keepStats) this.moddnMonitor.start(); |
| | | result=processModifyDNRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.moddnMonitor.stop(); |
| | | this.moddnMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_SEARCH_REQUEST: |
| | | if (keepStats) this.searchMonitor.start(); |
| | | result=processSearchRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.searchMonitor.stop(); |
| | | this.searchMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_UNBIND_REQUEST: |
| | | if (keepStats) this.unbindMonitor.start(); |
| | | result=processUnbindRequest(message, opControls); |
| | | if (keepStats) { |
| | | this.unbindMonitor.stop(); |
| | | this.unbindMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | default: |
| | | Message msg = ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get( |
| | | message.getProtocolOpName(), message.getMessageID()); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); |
| | | return false; |
| | | case OP_TYPE_ABANDON_REQUEST: |
| | | if (keepStats) this.abandonMonitor.start(); |
| | | result = processAbandonRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.abandonMonitor.stop(); |
| | | this.abandonMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_ADD_REQUEST: |
| | | if (keepStats) this.addMonitor.start(); |
| | | result = processAddRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.addMonitor.stop(); |
| | | this.addMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_BIND_REQUEST: |
| | | if (keepStats) this.bindMonitor.start(); |
| | | result = processBindRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.bindMonitor.stop(); |
| | | this.bindMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_COMPARE_REQUEST: |
| | | if (keepStats) this.compareMonitor.start(); |
| | | result = processCompareRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.compareMonitor.stop(); |
| | | this.compareMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_DELETE_REQUEST: |
| | | if (keepStats) this.delMonitor.start(); |
| | | result = processDeleteRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.delMonitor.stop(); |
| | | this.delMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_EXTENDED_REQUEST: |
| | | if (keepStats) this.extendedMonitor.start(); |
| | | result = processExtendedRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.extendedMonitor.stop(); |
| | | this.extendedMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_MODIFY_REQUEST: |
| | | if (keepStats) this.modMonitor.start(); |
| | | result = processModifyRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.modMonitor.stop(); |
| | | this.modMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_MODIFY_DN_REQUEST: |
| | | if (keepStats) this.moddnMonitor.start(); |
| | | result = processModifyDNRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.moddnMonitor.stop(); |
| | | this.moddnMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_SEARCH_REQUEST: |
| | | if (keepStats) this.searchMonitor.start(); |
| | | result = processSearchRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.searchMonitor.stop(); |
| | | this.searchMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | case OP_TYPE_UNBIND_REQUEST: |
| | | if (keepStats) this.unbindMonitor.start(); |
| | | result = processUnbindRequest(message, opControls); |
| | | if (keepStats) |
| | | { |
| | | this.unbindMonitor.stop(); |
| | | this.unbindMonitor.updateMonitorProvider(statTracker); |
| | | } |
| | | return result; |
| | | default: |
| | | Message msg = |
| | | ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get(message |
| | | .getProtocolOpName(), message.getMessageID()); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); |
| | | return false; |
| | | } |
| | | } |
| | | catch (Exception e) |
| | |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | Message msg = ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get( |
| | | message.getProtocolOpName(), |
| | | message.getMessageID(), String.valueOf(e)); |
| | | Message msg = |
| | | ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get(message |
| | | .getProtocolOpName(), message.getMessageID(), String |
| | | .valueOf(e)); |
| | | disconnect(DisconnectReason.SERVER_ERROR, true, msg); |
| | | return false; |
| | | } |
| | |
| | | /** |
| | | * Processes the provided LDAP message as an abandon request. |
| | | * |
| | | * @param message The LDAP message containing the abandon request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the abandon request to |
| | | * process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processAbandonRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | AbandonRequestProtocolOp protocolOp = message.getAbandonRequestProtocolOp(); |
| | | AbandonRequestProtocolOp protocolOp = |
| | | message.getAbandonRequestProtocolOp(); |
| | | AbandonOperationBasis abandonOp = |
| | | new AbandonOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getIDToAbandon()); |
| | | new AbandonOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getIDToAbandon()); |
| | | |
| | | abandonOp.run(); |
| | | if (keepStats && (abandonOp.getResultCode() == ResultCode.CANCELED)) |
| | |
| | | /** |
| | | * Processes the provided LDAP message as an add request. |
| | | * |
| | | * @param message The LDAP message containing the add request to process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the add request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processAddRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | AddResponseProtocolOp responseOp = |
| | | new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | // Create the add operation and add it into the work queue. |
| | | AddRequestProtocolOp protocolOp = message.getAddRequestProtocolOp(); |
| | | AddOperationBasis addOp = |
| | | new AddOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, protocolOp.getDN(), |
| | | protocolOp.getAttributes()); |
| | | new AddOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, protocolOp.getDN(), |
| | | protocolOp.getAttributes()); |
| | | |
| | | try |
| | | { |
| | |
| | | } |
| | | |
| | | AddResponseProtocolOp responseOp = |
| | | new AddResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new AddResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), de.getMatchedDN(), de |
| | | .getReferralURLs()); |
| | | |
| | | List<Control> responseControls = addOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, addOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a bind request. |
| | | * |
| | | * @param message The LDAP message containing the bind request to process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the bind request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processBindRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | BindRequestProtocolOp protocolOp = message.getBindRequestProtocolOp(); |
| | | BindRequestProtocolOp protocolOp = |
| | | message.getBindRequestProtocolOp(); |
| | | |
| | | // See if this is an LDAPv2 bind request, and if so whether that should be |
| | | // allowed. |
| | | // See if this is an LDAPv2 bind request, and if so whether that |
| | | // should be allowed. |
| | | String versionString; |
| | | switch (ldapVersion = protocolOp.getProtocolVersion()) |
| | | { |
| | | case 2: |
| | | versionString = "2"; |
| | | case 2: |
| | | versionString = "2"; |
| | | |
| | | if (! connectionHandler.allowLDAPv2()) |
| | | { |
| | | BindResponseProtocolOp responseOp = |
| | | new BindResponseProtocolOp( |
| | | LDAPResultCode.INAPPROPRIATE_AUTHENTICATION, |
| | | ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | if (!connectionHandler.allowLDAPv2()) |
| | | { |
| | | BindResponseProtocolOp responseOp = |
| | | new BindResponseProtocolOp( |
| | | LDAPResultCode.INAPPROPRIATE_AUTHENTICATION, |
| | | ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | if ((controls != null) && (! controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | BindResponseProtocolOp responseOp = |
| | | new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | if ((controls != null) && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | BindResponseProtocolOp responseOp = |
| | | new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | case 3: |
| | | versionString = "3"; |
| | | break; |
| | | default: |
| | | versionString = String.valueOf(ldapVersion); |
| | | break; |
| | | break; |
| | | case 3: |
| | | versionString = "3"; |
| | | break; |
| | | default: |
| | | versionString = String.valueOf(ldapVersion); |
| | | break; |
| | | } |
| | | |
| | | |
| | | ASN1OctetString bindDN = protocolOp.getDN(); |
| | | ByteString bindDN = protocolOp.getDN(); |
| | | |
| | | BindOperationBasis bindOp; |
| | | switch (protocolOp.getAuthenticationType()) |
| | | { |
| | | case SIMPLE: |
| | | bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | versionString, bindDN, |
| | | protocolOp.getSimplePassword()); |
| | | break; |
| | | case SASL: |
| | | bindOp = new BindOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | versionString, bindDN, |
| | | protocolOp.getSASLMechanism(), |
| | | protocolOp.getSASLCredentials()); |
| | | break; |
| | | default: |
| | | // This is an invalid authentication type, and therefore a protocol |
| | | // error. As per RFC 2251, a protocol error in a bind request must |
| | | // result in terminating the connection. |
| | | Message msg = |
| | | ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(), |
| | | String.valueOf(protocolOp.getAuthenticationType())); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); |
| | | return false; |
| | | case SIMPLE: |
| | | bindOp = |
| | | new BindOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | versionString, bindDN, protocolOp.getSimplePassword()); |
| | | break; |
| | | case SASL: |
| | | bindOp = |
| | | new BindOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | versionString, bindDN, protocolOp.getSASLMechanism(), |
| | | protocolOp.getSASLCredentials()); |
| | | break; |
| | | default: |
| | | // This is an invalid authentication type, and therefore a |
| | | // protocol error. As per RFC 2251, a protocol error in a bind |
| | | // request must result in terminating the connection. |
| | | Message msg = |
| | | ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(), |
| | | String.valueOf(protocolOp.getAuthenticationType())); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); |
| | | return false; |
| | | } |
| | | |
| | | // Add the operation into the work queue. |
| | |
| | | } |
| | | |
| | | BindResponseProtocolOp responseOp = |
| | | new BindResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new BindResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), de.getMatchedDN(), de |
| | | .getReferralURLs()); |
| | | |
| | | List<Control> responseControls = bindOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, bindOp.getResponseControls())); |
| | | |
| | | // If it was a protocol error, then terminate the connection. |
| | | if (de.getResultCode() == ResultCode.PROTOCOL_ERROR) |
| | | { |
| | | Message msg = ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get( |
| | | message.getMessageID(), de.getMessageObject()); |
| | | Message msg = |
| | | ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get(message |
| | | .getMessageID(), de.getMessageObject()); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg); |
| | | } |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a compare request. |
| | | * |
| | | * @param message The LDAP message containing the compare request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the compare request to |
| | | * process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processCompareRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | CompareResponseProtocolOp responseOp = |
| | | new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | CompareRequestProtocolOp protocolOp = message.getCompareRequestProtocolOp(); |
| | | CompareRequestProtocolOp protocolOp = |
| | | message.getCompareRequestProtocolOp(); |
| | | CompareOperationBasis compareOp = |
| | | new CompareOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getDN(), protocolOp.getAttributeType(), |
| | | protocolOp.getAssertionValue()); |
| | | new CompareOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getDN(), protocolOp.getAttributeType(), |
| | | protocolOp.getAssertionValue()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | CompareResponseProtocolOp responseOp = |
| | | new CompareResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new CompareResponseProtocolOp(de.getResultCode() |
| | | .getIntValue(), de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = compareOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, compareOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a delete request. |
| | | * |
| | | * @param message The LDAP message containing the delete request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the delete request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processDeleteRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | DeleteResponseProtocolOp responseOp = |
| | | new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | DeleteRequestProtocolOp protocolOp = message.getDeleteRequestProtocolOp(); |
| | | DeleteRequestProtocolOp protocolOp = |
| | | message.getDeleteRequestProtocolOp(); |
| | | DeleteOperationBasis deleteOp = |
| | | new DeleteOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getDN()); |
| | | new DeleteOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getDN()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | DeleteResponseProtocolOp responseOp = |
| | | new DeleteResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new DeleteResponseProtocolOp( |
| | | de.getResultCode().getIntValue(), de.getMessageObject(), |
| | | de.getMatchedDN(), de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = deleteOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, deleteOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as an extended request. |
| | | * |
| | | * @param message The LDAP message containing the extended request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the extended request to |
| | | * process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processExtendedRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | // See if this is an LDAPv2 client. If it is, then they should not be |
| | | // issuing extended requests. We can't send a response that we can be sure |
| | | // they can understand, so we have no choice but to close the connection. |
| | | // See if this is an LDAPv2 client. If it is, then they should not |
| | | // be issuing extended requests. We can't send a response that we |
| | | // can be sure they can understand, so we have no choice but to |
| | | // close the connection. |
| | | if (ldapVersion == 2) |
| | | { |
| | | Message msg = ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get( |
| | | getConnectionID(), message.getMessageID()); |
| | | Message msg = |
| | | ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get( |
| | | getConnectionID(), message.getMessageID()); |
| | | logError(msg); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, msg); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // FIXME -- Do we need to handle certain types of request here? |
| | | // -- StartTLS requests |
| | | // -- Cancel requests |
| | | |
| | | |
| | | ExtendedRequestProtocolOp protocolOp = |
| | | message.getExtendedRequestProtocolOp(); |
| | | message.getExtendedRequestProtocolOp(); |
| | | ExtendedOperationBasis extendedOp = |
| | | new ExtendedOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getOID(), protocolOp.getValue()); |
| | | new ExtendedOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getOID(), protocolOp.getValue()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | ExtendedResponseProtocolOp responseOp = |
| | | new ExtendedResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new ExtendedResponseProtocolOp(de.getResultCode() |
| | | .getIntValue(), de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = extendedOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, extendedOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a modify request. |
| | | * |
| | | * @param message The LDAP message containing the modify request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the modify request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processModifyRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | ModifyResponseProtocolOp responseOp = |
| | | new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | ModifyRequestProtocolOp protocolOp = message.getModifyRequestProtocolOp(); |
| | | ModifyRequestProtocolOp protocolOp = |
| | | message.getModifyRequestProtocolOp(); |
| | | ModifyOperationBasis modifyOp = |
| | | new ModifyOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getDN(), protocolOp.getModifications()); |
| | | new ModifyOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getDN(), protocolOp.getModifications()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | ModifyResponseProtocolOp responseOp = |
| | | new ModifyResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new ModifyResponseProtocolOp( |
| | | de.getResultCode().getIntValue(), de.getMessageObject(), |
| | | de.getMatchedDN(), de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = modifyOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, modifyOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a modify DN request. |
| | | * |
| | | * @param message The LDAP message containing the modify DN request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the modify DN request to |
| | | * process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processModifyDNRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | ModifyDNResponseProtocolOp responseOp = |
| | | new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | ModifyDNRequestProtocolOp protocolOp = |
| | | message.getModifyDNRequestProtocolOp(); |
| | | message.getModifyDNRequestProtocolOp(); |
| | | ModifyDNOperationBasis modifyDNOp = |
| | | new ModifyDNOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getEntryDN(), protocolOp.getNewRDN(), |
| | | protocolOp.deleteOldRDN(), |
| | | protocolOp.getNewSuperior()); |
| | | new ModifyDNOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getEntryDN(), protocolOp.getNewRDN(), protocolOp |
| | | .deleteOldRDN(), protocolOp.getNewSuperior()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | ModifyDNResponseProtocolOp responseOp = |
| | | new ModifyDNResponseProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new ModifyDNResponseProtocolOp(de.getResultCode() |
| | | .getIntValue(), de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = modifyDNOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, modifyDNOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as a search request. |
| | | * |
| | | * @param message The LDAP message containing the search request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the search request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processSearchRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | if ((ldapVersion == 2) && (controls != null) && (! controls.isEmpty())) |
| | | if ((ldapVersion == 2) && (controls != null) |
| | | && (!controls.isEmpty())) |
| | | { |
| | | // LDAPv2 clients aren't allowed to send controls. |
| | | SearchResultDoneProtocolOp responseOp = |
| | | new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp)); |
| | | new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp)); |
| | | disconnect(DisconnectReason.PROTOCOL_ERROR, false, |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | SearchRequestProtocolOp protocolOp = message.getSearchRequestProtocolOp(); |
| | | SearchRequestProtocolOp protocolOp = |
| | | message.getSearchRequestProtocolOp(); |
| | | SearchOperationBasis searchOp = |
| | | new SearchOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls, |
| | | protocolOp.getBaseDN(), protocolOp.getScope(), |
| | | protocolOp.getDereferencePolicy(), |
| | | protocolOp.getSizeLimit(), |
| | | protocolOp.getTimeLimit(), |
| | | protocolOp.getTypesOnly(), protocolOp.getFilter(), |
| | | protocolOp.getAttributes()); |
| | | new SearchOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls, |
| | | protocolOp.getBaseDN(), protocolOp.getScope(), protocolOp |
| | | .getDereferencePolicy(), protocolOp.getSizeLimit(), |
| | | protocolOp.getTimeLimit(), protocolOp.getTypesOnly(), |
| | | protocolOp.getFilter(), protocolOp.getAttributes()); |
| | | |
| | | // Add the operation into the work queue. |
| | | try |
| | |
| | | } |
| | | |
| | | SearchResultDoneProtocolOp responseOp = |
| | | new SearchResultDoneProtocolOp(de.getResultCode().getIntValue(), |
| | | de.getMessageObject(), |
| | | de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | new SearchResultDoneProtocolOp(de.getResultCode() |
| | | .getIntValue(), de.getMessageObject(), de.getMatchedDN(), |
| | | de.getReferralURLs()); |
| | | |
| | | List<Control> responseControls = searchOp.getResponseControls(); |
| | | ArrayList<LDAPControl> responseLDAPControls = |
| | | new ArrayList<LDAPControl>(responseControls.size()); |
| | | for (Control c : responseControls) |
| | | { |
| | | responseLDAPControls.add(new LDAPControl(c)); |
| | | } |
| | | |
| | | sendLDAPMessage(securityProvider, |
| | | new LDAPMessage(message.getMessageID(), responseOp, |
| | | responseLDAPControls)); |
| | | sendLDAPMessage(new LDAPMessage(message.getMessageID(), |
| | | responseOp, searchOp.getResponseControls())); |
| | | } |
| | | |
| | | |
| | | return connectionValid; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Processes the provided LDAP message as an unbind request. |
| | | * |
| | | * @param message The LDAP message containing the unbind request to |
| | | * process. |
| | | * @param controls The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * |
| | | * @return <CODE>true</CODE> if the request was processed successfully, or |
| | | * <CODE>false</CODE> if not and the connection has been closed as a |
| | | * result (it is the responsibility of this method to close the |
| | | * connection). |
| | | * @param message |
| | | * The LDAP message containing the unbind request to process. |
| | | * @param controls |
| | | * The set of pre-decoded request controls contained in the |
| | | * message. |
| | | * @return <CODE>true</CODE> if the request was processed |
| | | * successfully, or <CODE>false</CODE> if not and the |
| | | * connection has been closed as a result (it is the |
| | | * responsibility of this method to close the connection). |
| | | */ |
| | | private boolean processUnbindRequest(LDAPMessage message, |
| | | ArrayList<Control> controls) |
| | | List<Control> controls) |
| | | { |
| | | UnbindOperationBasis unbindOp = |
| | | new UnbindOperationBasis(this, nextOperationID.getAndIncrement(), |
| | | message.getMessageID(), controls); |
| | | new UnbindOperationBasis(this, nextOperationID |
| | | .getAndIncrement(), message.getMessageID(), controls); |
| | | |
| | | unbindOp.run(); |
| | | |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getMonitorSummary() |
| | | { |
| | | StringBuilder buffer = new StringBuilder(); |
| | |
| | | } |
| | | |
| | | buffer.append("\" security=\""); |
| | | if (securityProvider.isSecure()) |
| | | if (isSecure()) |
| | | { |
| | | buffer.append(securityProvider.getSecurityMechanismName()); |
| | | buffer.append(activeProvider.getName()); |
| | | } |
| | | else |
| | | { |
| | |
| | | |
| | | |
| | | /** |
| | | * Appends a string representation of this client connection to the provided |
| | | * buffer. |
| | | * Appends a string representation of this client connection to the |
| | | * provided buffer. |
| | | * |
| | | * @param buffer The buffer to which the information should be appended. |
| | | * @param buffer |
| | | * The buffer to which the information should be appended. |
| | | */ |
| | | @Override |
| | | public void toString(StringBuilder buffer) |
| | | { |
| | | buffer.append("LDAP client connection from "); |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether TLS protection is actually available for the underlying |
| | | * client connection. If there is any reason that TLS protection cannot be |
| | | * enabled on this client connection, then it should be appended to the |
| | | * provided buffer. |
| | | * Indicates whether TLS protection is actually available for the |
| | | * underlying client connection. If there is any reason that TLS |
| | | * protection cannot be enabled on this client connection, then it |
| | | * should be appended to the provided buffer. |
| | | * |
| | | * @param unavailableReason The buffer used to hold the reason that TLS is |
| | | * not available on the underlying client |
| | | * connection. |
| | | * |
| | | * @return <CODE>true</CODE> if TLS is available on the underlying client |
| | | * connection, or <CODE>false</CODE> if it is not. |
| | | * @param unavailableReason |
| | | * The buffer used to hold the reason that TLS is not |
| | | * available on the underlying client connection. |
| | | * @return <CODE>true</CODE> if TLS is available on the underlying |
| | | * client connection, or <CODE>false</CODE> if it is not. |
| | | */ |
| | | public boolean tlsProtectionAvailable(MessageBuilder unavailableReason) |
| | | public boolean isTLSAvailable(MessageBuilder unavailableReason) |
| | | { |
| | | // Make sure that this client connection does not already have some other |
| | | // security provider enabled. |
| | | if (! (securityProvider instanceof NullConnectionSecurityProvider)) |
| | | if (isSecure() && activeProvider.getName().equals("TLS")) |
| | | { |
| | | |
| | | unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER.get( |
| | | securityProvider.getSecurityMechanismName())); |
| | | // TODO SASLPhase2 more general message |
| | | unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER |
| | | .get(activeProvider.getName())); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Make sure that the connection handler allows the use of the StartTLS |
| | | // operation. |
| | | if (! connectionHandler.allowStartTLS()) |
| | | // Make sure that the connection handler allows the use of the |
| | | // StartTLS operation. |
| | | if (!connectionHandler.allowStartTLS()) |
| | | { |
| | | |
| | | unavailableReason.append(ERR_LDAP_TLS_STARTTLS_NOT_ALLOWED.get()); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Make sure that the TLS security provider is available. |
| | | if (tlsSecurityProvider == null) |
| | | try |
| | | { |
| | | try |
| | | { |
| | | TLSConnectionSecurityProvider tlsProvider = |
| | | new TLSConnectionSecurityProvider(); |
| | | tlsProvider.initializeConnectionSecurityProvider(null); |
| | | tlsProvider.setSSLClientAuthPolicy( |
| | | connectionHandler.getSSLClientAuthPolicy()); |
| | | tlsProvider.setEnabledProtocols( |
| | | connectionHandler.getEnabledSSLProtocols()); |
| | | tlsProvider.setEnabledCipherSuites( |
| | | connectionHandler.getEnabledSSLCipherSuites()); |
| | | |
| | | tlsSecurityProvider = (TLSConnectionSecurityProvider) |
| | | tlsProvider.newInstance(this, clientChannel); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | tlsSecurityProvider = null; |
| | | |
| | | |
| | | unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER.get( |
| | | stackTraceToSingleLineString(e))); |
| | | return false; |
| | | } |
| | | TLSByteChannel tlsByteChannel = |
| | | connectionHandler.getTLSByteChannel(this, clientChannel); |
| | | setTLSPendingProvider(tlsByteChannel); |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then everything looks OK. |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER |
| | | .get(stackTraceToSingleLineString(de))); |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Installs the TLS connection security provider on this client connection. |
| | | * If an error occurs in the process, then the underlying client connection |
| | | * must be terminated and an exception must be thrown to indicate the |
| | | * underlying cause. |
| | | * |
| | | * @throws DirectoryException If the TLS connection security provider could |
| | | * not be enabled and the underlying connection |
| | | * has been closed. |
| | | */ |
| | | public void enableTLSConnectionSecurityProvider() |
| | | throws DirectoryException |
| | | { |
| | | if (tlsSecurityProvider == null) |
| | | { |
| | | Message message = ERR_LDAP_TLS_NO_PROVIDER.get(); |
| | | |
| | | disconnect(DisconnectReason.OTHER, false, message); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message); |
| | | } |
| | | |
| | | clearSecurityProvider = securityProvider; |
| | | setConnectionSecurityProvider(tlsSecurityProvider); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Disables the TLS connection security provider on this client connection. |
| | | * This must also eliminate any authentication that had been performed on the |
| | | * client connection so that it is in an anonymous state. If a problem occurs |
| | | * while attempting to revert the connection to a non-TLS-protected state, |
| | | * then an exception must be thrown and the client connection must be |
| | | * terminated. |
| | | * |
| | | * @throws DirectoryException If TLS protection cannot be reverted and the |
| | | * underlying client connection has been closed. |
| | | */ |
| | | public void disableTLSConnectionSecurityProvider() |
| | | throws DirectoryException |
| | | { |
| | | Message message = ERR_LDAP_TLS_CLOSURE_NOT_ALLOWED.get(); |
| | | |
| | | disconnect(DisconnectReason.OTHER, false, message); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Sends a response to the client in the clear rather than through the |
| | | * encrypted channel. This should only be used when processing the StartTLS |
| | | * extended operation to send the response in the clear after the TLS |
| | | * negotiation has already been initiated. |
| | | * encrypted channel. This should only be used when processing the |
| | | * StartTLS extended operation to send the response in the clear after |
| | | * the TLS negotiation has already been initiated. |
| | | * |
| | | * @param operation The operation for which to send the response in the |
| | | * clear. |
| | | * |
| | | * |
| | | * @throws DirectoryException If a problem occurs while sending the response |
| | | * in the clear. |
| | | * @param operation |
| | | * The operation for which to send the response in the clear. |
| | | * @throws DirectoryException |
| | | * If a problem occurs while sending the response in the |
| | | * clear. |
| | | */ |
| | | public void sendClearResponse(Operation operation) |
| | | throws DirectoryException |
| | | throws DirectoryException |
| | | { |
| | | if (clearSecurityProvider == null) |
| | | { |
| | | Message message = ERR_LDAP_NO_CLEAR_SECURITY_PROVIDER.get(toString()); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message); |
| | | } |
| | | |
| | | sendLDAPMessage(clearSecurityProvider, |
| | | operationToResponseLDAPMessage(operation)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public DN getKeyManagerProviderDN() |
| | | { |
| | | return connectionHandler.getKeyManagerProviderDN(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public DN getTrustManagerProviderDN() |
| | | { |
| | | return connectionHandler.getTrustManagerProviderDN(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the alias of the server certificate that should be used |
| | | * for operations requiring a server certificate. The default |
| | | * implementation returns {@code null} to indicate that any alias is |
| | | * acceptable. |
| | | * |
| | | * @return The alias of the server certificate that should be used |
| | | * for operations requring a server certificate, or |
| | | * {@code null} if any alias is acceptable. |
| | | */ |
| | | @Override |
| | | public String getCertificateAlias() |
| | | { |
| | | return connectionHandler.getSSLServerCertNickname(); |
| | | sendLDAPMessage(operationToResponseLDAPMessage(operation)); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the length of time in milliseconds that this client |
| | | * connection has been idle. |
| | | * <BR><BR> |
| | | * connection has been idle. <BR> |
| | | * <BR> |
| | | * Note that the default implementation will always return zero. |
| | | * Subclasses associated with connection handlers should override |
| | | * this method if they wish to provided idle time limit |
| | | * functionality. |
| | | * Subclasses associated with connection handlers should override this |
| | | * method if they wish to provided idle time limit functionality. |
| | | * |
| | | * @return The length of time in milliseconds that this client |
| | | * connection has been idle. |
| | | * @return The length of time in milliseconds that this client |
| | | * connection has been idle. |
| | | */ |
| | | @Override |
| | | public long getIdleTime() |
| | | { |
| | | if (operationsInProgress.isEmpty() && getPersistentSearches().isEmpty()) |
| | | if (operationsInProgress.isEmpty() |
| | | && getPersistentSearches().isEmpty()) |
| | | { |
| | | return (TimeThread.getTime() - lastCompletionTime.get()); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private void initializeOperationMonitors() { |
| | | this.addMonitor = OperationMonitor.getOperationMonitor( |
| | | ADD); |
| | | this.searchMonitor = OperationMonitor.getOperationMonitor( |
| | | SEARCH); |
| | | this.abandonMonitor = OperationMonitor.getOperationMonitor( |
| | | ABANDON); |
| | | this.bindMonitor = OperationMonitor.getOperationMonitor( |
| | | BIND); |
| | | this.compareMonitor = OperationMonitor.getOperationMonitor( |
| | | COMPARE); |
| | | this.delMonitor = OperationMonitor.getOperationMonitor( |
| | | DELETE); |
| | | this.extendedMonitor = OperationMonitor.getOperationMonitor( |
| | | EXTENDED); |
| | | this.modMonitor = OperationMonitor.getOperationMonitor( |
| | | MODIFY); |
| | | this.moddnMonitor = OperationMonitor.getOperationMonitor( |
| | | MODIFY_DN); |
| | | this.unbindMonitor = OperationMonitor.getOperationMonitor( |
| | | UNBIND); |
| | | |
| | | |
| | | /** |
| | | * Set the connection provider that is not in use yet. Used in TLS |
| | | * negotiation when a clear response is needed before the connection |
| | | * provider is active. |
| | | * |
| | | * @param provider |
| | | * The provider that needs to be activated. |
| | | */ |
| | | public void setTLSPendingProvider(ConnectionSecurityProvider provider) |
| | | { |
| | | tlsPendingProvider = provider; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Set the connection provider that is not in use. Used in SASL |
| | | * negotiation when a clear response is needed before the connection |
| | | * provider is active. |
| | | * |
| | | * @param provider |
| | | * The provider that needs to be activated. |
| | | */ |
| | | public void setSASLPendingProvider(ConnectionSecurityProvider provider) |
| | | { |
| | | saslPendingProvider = provider; |
| | | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Enable the provider that is inactive. |
| | | */ |
| | | public void enableTLS() |
| | | { |
| | | this.asn1Reader = |
| | | ASN1.getReader(saslChannel, tlsPendingProvider.getAppBufSize(), |
| | | connectionHandler.getMaxRequestSize()); |
| | | activeProvider = tlsPendingProvider; |
| | | tlsChannel.redirect(tlsPendingProvider); |
| | | tlsPendingProvider = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Set the security provider to the specified provider. |
| | | * |
| | | * @param sslProvider |
| | | * The provider to set the security provider to. |
| | | */ |
| | | public void enableSSL(ConnectionSecurityProvider sslProvider) |
| | | { |
| | | this.asn1Reader = |
| | | ASN1.getReader(saslChannel, sslProvider.getAppBufSize(), |
| | | connectionHandler.getMaxRequestSize()); |
| | | activeProvider = sslProvider; |
| | | tlsChannel.redirect(sslProvider); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Enable the SASL provider that is currently inactive or pending. |
| | | */ |
| | | public void enableSASL() |
| | | { |
| | | activeProvider = saslPendingProvider; |
| | | saslChannel.redirect(saslPendingProvider); |
| | | asn1Reader = |
| | | ASN1.getReader(saslChannel, |
| | | saslPendingProvider.getAppBufSize(), connectionHandler |
| | | .getMaxRequestSize()); |
| | | saslPendingProvider = null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Return the certificate chain array associated with a connection. |
| | | * |
| | | * @return The array of certificates associated with a connection. |
| | | */ |
| | | public Certificate[] getClientCertificateChain() |
| | | { |
| | | if (activeProvider != null) |
| | | { |
| | | return activeProvider.getClientCertificateChain(); |
| | | } |
| | | else |
| | | return new Certificate[0]; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public int getSSF() |
| | | { |
| | | if (activeProvider != null) |
| | | return activeProvider.getSSF(); |
| | | else |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | |
| | | private void initializeOperationMonitors() |
| | | { |
| | | this.addMonitor = OperationMonitor.getOperationMonitor(ADD); |
| | | this.searchMonitor = OperationMonitor.getOperationMonitor(SEARCH); |
| | | this.abandonMonitor = OperationMonitor.getOperationMonitor(ABANDON); |
| | | this.bindMonitor = OperationMonitor.getOperationMonitor(BIND); |
| | | this.compareMonitor = OperationMonitor.getOperationMonitor(COMPARE); |
| | | this.delMonitor = OperationMonitor.getOperationMonitor(DELETE); |
| | | this.extendedMonitor = |
| | | OperationMonitor.getOperationMonitor(EXTENDED); |
| | | this.modMonitor = OperationMonitor.getOperationMonitor(MODIFY); |
| | | this.moddnMonitor = OperationMonitor.getOperationMonitor(MODIFY_DN); |
| | | this.unbindMonitor = OperationMonitor.getOperationMonitor(UNBIND); |
| | | } |
| | | } |
| | | |