LDAPClientConnection.java revision ca669ae54f86dbeea277280690584d9f591c7571
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2010-2015 ForgeRock AS.
*/
/**
* 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 final class LDAPClientConnection extends ClientConnection implements
{
/**
* A runnable whose task is to close down all IO related channels
* associated with a client connection after a small delay.
*/
private static final class ConnectionFinalizerJob implements Runnable
{
/** The client connection ASN1 reader. */
private final ASN1ByteChannelReader asn1Reader;
/** The client connection socket channel. */
private final SocketChannel socketChannel;
/** Creates a new connection finalizer job. */
{
this.asn1Reader = asn1Reader;
this.socketChannel = socketChannel;
}
/**
* {@inheritDoc}
*/
public void run()
{
try
{
asn1Reader.close();
}
catch (Exception e)
{
// In general, we don't care about any exception that might be
// thrown here.
logger.traceException(e);
}
try
{
}
catch (Exception e)
{
// In general, we don't care about any exception that might be
// thrown here.
logger.traceException(e);
}
}
}
/**
* Channel that writes the contents of the provided buffer to the client,
* throwing an exception if the write is unsuccessful for too
* long (e.g., if the client is unresponsive or there is a network
* problem). If possible, it will attempt to use the selector returned
* by the {@code ClientConnection.getWriteSelector} method, but it is
* capable of working even if that method returns {@code null}. <BR>
*
* Note that the original position and limit values will not be
* preserved, so if that is important to the caller, then it should
* record them before calling this method and restore them after it
* returns.
*/
private class TimeoutWriteByteChannel implements ByteChannel
{
/** Synchronize concurrent writes to the same connection. */
{
{
}
return bytesRead;
}
public boolean isOpen()
{
return clientChannel.isOpen();
}
public void close() throws IOException
{
}
{
try
{
{
}
if (!byteBuffer.hasRemaining())
{
return bytesToWrite;
}
long waitTime = getMaxBlockedWriteTimeLimit();
if (waitTime <= 0)
{
// We won't support an infinite time limit, so fall back to using
// five minutes, which is a very long timeout given that we're
// blocking a worker thread.
waitTime = 300000L;
}
{
// The client connection does not provide a selector, so we'll
// fall back to a more inefficient way that will work without a
// selector.
while (byteBuffer.hasRemaining()
{
if (bytesWritten < 0)
{
// The client connection has been closed.
throw new ClosedChannelException();
}
{
}
}
if (byteBuffer.hasRemaining())
{
// If we've gotten here, then the write timed out.
throw new ClosedChannelException();
}
return bytesToWrite;
}
// Register with the selector for handling write operations.
try
{
while (byteBuffer.hasRemaining())
{
if (currentTime >= stopTime)
{
// We've been blocked for too long.
throw new ClosedChannelException();
}
else
{
}
.iterator();
{
if (k.isWritable())
{
if (bytesWritten < 0)
{
// The client connection has been closed.
throw new ClosedChannelException();
}
{
}
}
}
if (byteBuffer.hasRemaining())
{
}
}
return bytesToWrite;
}
finally
{
{
}
}
}
finally
{
}
}
}
/** The tracer object for the debug logger. */
/**
* Thread local ASN1Writer and buffer.
*/
private static final class ASN1WriterHolder implements Closeable
{
private final ASN1Writer writer;
private final ByteStringBuilder buffer;
private final int maxBufferSize;
private ASN1WriterHolder()
{
this.buffer = new ByteStringBuilder();
this.maxBufferSize = getMaxInternalBufferSize();
}
/** {@inheritDoc} */
public void close() throws IOException
{
}
}
/**
* Cached ASN1 writer: a thread can only write to one connection at a time.
*/
new ThreadLocal<ASN1WriterHolder>()
{
/**
* {@inheritDoc}
*/
protected ASN1WriterHolder initialValue()
{
return new ASN1WriterHolder();
}
};
private ASN1WriterHolder getASN1Writer()
{
{
// Setting has changed, so recreate the holder.
holder = new ASN1WriterHolder();
}
return holder;
}
/** The time that the last operation was completed. */
private final AtomicLong lastCompletionTime;
/** The next operation ID that should be used for this connection. */
private final AtomicLong nextOperationID;
/** The selector that may be used for write operations. */
/**
* Indicates whether the Directory Server believes this connection to be valid
* and available for communication.
*/
private volatile 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.
*/
private boolean disconnectRequested;
/**
* 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. */
/**
* The number of operations performed on this connection. Used to compare with
* the resource limits of the network group.
*/
private final AtomicLong operationsPerformed;
/** The port on the client from which this connection originated. */
private final int clientPort;
/**
* 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 final int serverPort;
/** The reference to the connection handler that accepted this connection. */
private final LDAPConnectionHandler connectionHandler;
/** The statistics tracker associated with this client connection. */
private final LDAPStatistics statTracker;
private boolean useNanoTime;
/** The connection ID assigned to this connection. */
private final long connectionID;
/**
* The lock used to provide threadsafe access to the set of operations in
* progress.
*/
private final Object opsInProgressLock;
/** The socket channel with which this client connection is associated. */
private final SocketChannel clientChannel;
/** The byte channel used for blocking writes with time out. */
private final ByteChannel timeoutClientChannel;
/** The string representation of the address of the client. */
private final String clientAddress;
/**
* The name of the protocol that the client is using to communicate with the
* server.
*/
/**
* The string representation of the address of the server to which the client
* has connected.
*/
private final String serverAddress;
private ASN1ByteChannelReader asn1Reader;
private final int bufferSize;
private final RedirectingByteChannel saslChannel;
private final RedirectingByteChannel tlsChannel;
private volatile ConnectionSecurityProvider saslActiveProvider;
private volatile ConnectionSecurityProvider tlsActiveProvider;
private volatile ConnectionSecurityProvider saslPendingProvider;
private volatile ConnectionSecurityProvider tlsPendingProvider;
/**
* 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 protocol String representing the protocol (LDAP or LDAP+SSL).
* @throws DirectoryException If SSL initialisation fails.
*/
{
this.connectionHandler = connectionHandler;
this.clientChannel = clientChannel;
opsInProgressLock = new Object();
ldapVersion = 3;
connectionValid = true;
disconnectRequested = false;
if (keepStats)
{
}
this.asn1Reader = new ASN1ByteChannelReader(saslChannel, bufferSize, connectionHandler.getMaxRequestSize());
if (connectionHandler.useSSL())
{
}
}
/**
* Retrieves the connection ID assigned to this connection.
*
* @return The connection ID assigned to this connection.
*/
public long getConnectionID()
{
return connectionID;
}
/**
* Retrieves the connection handler that accepted this client
* connection.
*
* @return The connection handler that accepted this client
* connection.
*/
public ConnectionHandler<?> getConnectionHandler()
{
return connectionHandler;
}
/**
* 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.
*/
public SocketChannel getSocketChannel()
{
return clientChannel;
}
/**
* 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.
*/
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.
*/
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.
*/
public int getClientPort()
{
return clientPort;
}
/**
* 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.
*/
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.
*/
public int getServerPort()
{
return serverPort;
}
/**
* 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.
*/
public InetAddress getRemoteAddress()
{
}
/**
* 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.
*/
public InetAddress getLocalAddress()
{
}
/** {@inheritDoc} */
public boolean isConnectionValid()
{
return this.connectionValid;
}
/**
* 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.
*/
public boolean isSecure()
{
boolean secure = false;
if (tlsActiveProvider != null)
{
}
{
}
return secure;
}
/**
* Sends a response to the client based on the information in the
* provided operation.
*
* @param operation
* The operation for which to send the response.
*/
{
// 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.
if (keepStats) {
long time;
if (useNanoTime) {
} else {
}
time);
}
// Avoid sending the response if one has already been sent. This may happen
// if operation processing encounters a run-time exception after sending the
// response: the worker thread exception handling code will attempt to send
// an error result to the client indicating that a problem occurred.
{
{
}
}
}
/**
* 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.
*/
{
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".
}
// Referrals are not allowed for LDAPv2 clients.
if (ldapVersion == 2)
{
referralURLs = null;
{
}
{
{
}
}
}
else
{
}
switch (operation.getOperationType())
{
case ADD:
break;
case BIND:
break;
case COMPARE:
break;
case DELETE:
break;
case EXTENDED:
// If this an LDAPv2 client, then we can't send this.
if (ldapVersion == 2)
{
return null;
}
break;
case MODIFY:
break;
case MODIFY_DN:
break;
case SEARCH:
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.
logger.error(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP, operation.getOperationType(), getConnectionID(),
return null;
}
// Controls are not allowed for LDAPv2 clients.
if (ldapVersion == 2)
{
}
else
{
}
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.
*/
{
}
/**
* 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.
*/
{
// 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)
{
return false;
}
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.
*/
protected boolean sendIntermediateResponseMessage(
{
// 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 message
* The LDAP message to send to the client.
*/
{
// Use a thread local writer.
try
{
if (logger.isTraceEnabled())
{
}
if (keepStats)
{
}
}
catch (Exception e)
{
logger.traceException(e);
// FIXME -- Log a message or something
return;
}
finally
{
// Clear and reset all of the internal buffers ready for the next usage.
// The ASN1Writer is based on a ByteStringBuilder so closing will cause
// the internal buffers to be resized if needed.
}
}
/**
* 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.
*/
{
// 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
// do anything.
if (disconnectRequested)
{
return;
}
disconnectRequested = true;
}
if (keepStats)
{
}
if (connectionID >= 0)
{
DirectoryServer.connectionClosed(this);
}
// Indicate that this connection is no longer valid.
connectionValid = false;
{
.toMessage()));
}
else
{
.getClosureMessage()));
}
// If there is a write selector for this connection, then close it.
// 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.
{
try
{
int resultCode;
switch (disconnectReason)
{
case PROTOCOL_ERROR:
break;
case SERVER_SHUTDOWN:
break;
case SERVER_ERROR:
break;
case ADMIN_LIMIT_EXCEEDED:
case IDLE_TIME_LIMIT_EXCEEDED:
case IO_TIMEOUT:
break;
case CONNECTION_REJECTED:
break;
case INVALID_CREDENTIALS:
break;
default:
break;
}
{
errMsg =
}
else
{
}
}
catch (Exception e)
{
// NYI -- Log a message indicating that we couldn't send the
// notice of disconnection.
logger.traceException(e);
}
}
// Enqueue the connection channels for closing by the finalizer.
// NYI -- Deregister the client connection from any server components that
// might know about it.
// Log a disconnect message.
try
{
message);
}
catch (Exception e)
{
logger.traceException(e);
}
}
/**
* 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 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.
*/
{
}
/**
* 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).
*/
throws DirectoryException
{
// 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.
try
{
synchronized (opsInProgressLock)
{
// If we're already in the process of disconnecting the client,
// then reject the operation.
if (disconnectRequested)
{
message);
}
// Add the operation to the list of operations in progress for
// this connection.
// See if there is already an operation in progress with the
// same message ID. If so, then we can't allow it.
{
message);
}
}
// Try to add the operation to the work queue,
// or run it synchronously (typically for the administration
// connector)
}
catch (DirectoryException de)
{
throw de;
}
catch (Exception e)
{
logger.traceException(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.
*
* @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.
*/
public boolean removeOperationInProgress(int messageID)
{
{
return false;
}
&& keepStats
{
}
return true;
}
/**
* 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.
*/
{
{
// See if the operation is in the list of persistent searches.
{
{
// We only need to find the first persistent search
// associated with the provided message ID. The persistent
// search will ensure that all other related persistent
// searches are cancelled.
}
}
}
else
{
}
}
/**
* Attempts to cancel all operations in progress on this connection.
*
* @param cancelRequest
* An object providing additional information about how the
* cancel should be processed.
*/
{
// Make sure that no one can add any new operations.
synchronized (opsInProgressLock)
{
try
{
{
try
{
o.abort(cancelRequest);
// TODO: Assume its cancelled?
if (keepStats)
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
}
if (!operationsInProgress.isEmpty()
|| !getPersistentSearches().isEmpty())
{
}
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
/**
* 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.
*/
int messageID)
{
// Make sure that no one can add any new operations.
synchronized (opsInProgressLock)
{
try
{
{
{
continue;
}
if (o != null)
{
try
{
o.abort(cancelRequest);
// TODO: Assume its cancelled?
if (keepStats)
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
{
{
continue;
}
}
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
/**
* {@inheritDoc}
*/
public Selector getWriteSelector()
{
{
try
{
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
}
return selector;
}
/**
* {@inheritDoc}
*/
public long getMaxBlockedWriteTimeLimit()
{
return connectionHandler.getMaxBlockedWriteTimeLimit();
}
/**
* Returns the total number of operations initiated on this
* connection.
*
* @return the total number of operations on this connection
*/
public long getNumberOfOperations()
{
return operationsPerformed.get();
}
/**
* Returns the ASN1 reader for this connection.
*
* @return the ASN1 reader for this connection
*/
{
return asn1Reader;
}
/**
* Process data read.
*
* @return number of bytes read if this connection is still valid
* or negative integer to indicate an error otherwise
*/
int processDataRead()
{
if (bindOrStartTLSInProgress.get())
{
// We should wait for the bind or startTLS to finish before
// reading any more data off the socket.
return 0;
}
try
{
if (result < 0)
{
// The connection has been closed by the client. Disconnect
// and return.
return -1;
}
return result;
}
catch (Exception e)
{
logger.traceException(e);
{
// The connection failed, but there was an unread partial message so
// interpret this as an IO error.
}
else
{
// The connection failed and there was no unread data, so interpret this
// as indicating that the client aborted (reset) the connection. This
// happens when a client configures closes a connection which has been
// configured with SO_LINGER set to 0.
}
return -1;
}
}
/**
* 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.
*
* @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.
*/
{
if (keepStats)
{
}
// 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 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
{
if(bindOrStartTLSInProgress.get() ||
(saslBindInProgress.get() &&
{
}
boolean result;
switch (message.getProtocolOpType())
{
case OP_TYPE_ABANDON_REQUEST:
return result;
case OP_TYPE_ADD_REQUEST:
return result;
case OP_TYPE_BIND_REQUEST:
bindOrStartTLSInProgress.set(true);
{
saslBindInProgress.set(true);
}
if(!result)
{
bindOrStartTLSInProgress.set(false);
{
saslBindInProgress.set(false);
}
}
return result;
case OP_TYPE_COMPARE_REQUEST:
return result;
case OP_TYPE_DELETE_REQUEST:
return result;
case OP_TYPE_EXTENDED_REQUEST:
{
bindOrStartTLSInProgress.set(true);
}
if(!result &&
{
bindOrStartTLSInProgress.set(false);
}
return result;
case OP_TYPE_MODIFY_REQUEST:
return result;
return result;
case OP_TYPE_SEARCH_REQUEST:
return result;
case OP_TYPE_UNBIND_REQUEST:
return result;
default:
return false;
}
}
catch (Exception e)
{
logger.traceException(e);
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
return false;
}
// Create the abandon operation and add it into the work queue.
new AbandonOperationBasis(this, nextOperationID
try
{
}
catch (DirectoryException de)
{
// Don't send an error response since abandon operations
// don't have a response.
}
return connectionValid;
}
/**
* 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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
// Create the add operation and add it into the work queue.
try
{
}
catch (DirectoryException de)
{
.getReferralURLs());
}
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).
*/
{
// See if this is an LDAPv2 bind request, and if so whether that
// should be allowed.
{
case 2:
versionString = "2";
if (!connectionHandler.allowLDAPv2())
{
responseOp));
return false;
}
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
break;
case 3:
versionString = "3";
break;
default:
// Unsupported protocol version. RFC4511 states that we MUST send
// a protocol error back to the client.
responseOp));
return false;
}
switch (protocolOp.getAuthenticationType())
{
case SIMPLE:
bindOp =
new BindOperationBasis(this, nextOperationID
break;
case SASL:
bindOp =
new BindOperationBasis(this, nextOperationID
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.
return false;
}
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
.getReferralURLs());
// If it was a protocol error, then terminate the connection.
{
}
}
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
new CompareOperationBasis(this, nextOperationID
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
new DeleteOperationBasis(this, nextOperationID
protocolOp.getDN());
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
// 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)
{
return false;
}
// FIXME -- Do we need to handle certain types of request here?
// -- StartTLS requests
// -- Cancel requests
new ExtendedOperationBasis(this, nextOperationID
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
new ModifyOperationBasis(this, nextOperationID
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
new ModifyDNOperationBasis(this, nextOperationID
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
{
// LDAPv2 clients aren't allowed to send controls.
responseOp));
return false;
}
new SearchOperationBasis(this, nextOperationID
// Add the operation into the work queue.
try
{
}
catch (DirectoryException de)
{
}
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).
*/
{
new UnbindOperationBasis(this, nextOperationID
// The client connection will never be valid after an unbind.
return false;
}
/**
* {@inheritDoc}
*/
public String getMonitorSummary()
{
{
}
if (isSecure())
{
if (tlsActiveProvider != null)
{
}
if (saslActiveProvider != null)
{
if (tlsActiveProvider != null)
{
}
}
}
else
{
}
if (countPSearch > 0)
{
}
}
/**
* Appends a string representation of this client connection to the
* provided buffer.
*
* @param buffer
* The buffer to which the information should be appended.
*/
{
}
/**
* {@inheritDoc}
*/
{
if (tlsActiveProvider != null)
{
return false;
}
// Make sure that the connection handler allows the use of the
// StartTLS operation.
if (!connectionHandler.allowStartTLS())
{
return false;
}
try
{
}
catch (DirectoryException de)
{
return false;
}
return true;
}
/**
* Retrieves the length of time in milliseconds that this client
* 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.
*
* @return The length of time in milliseconds that this client
* connection has been idle.
*/
public long getIdleTime()
{
if (operationsInProgress.isEmpty()
&& getPersistentSearches().isEmpty())
{
}
else
{
// There's at least one operation in progress, so it's not idle.
return 0L;
}
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
}
/**
* Enable the provider that is inactive.
*/
private void enableTLS()
{
}
/**
* Set the security provider to the specified provider.
*
* @param sslProvider
* The provider to set the security provider to.
*/
{
}
/**
* Enable the SASL provider that is currently inactive or pending.
*/
private void enableSASL()
{
}
/**
* Return the certificate chain array associated with a connection.
*
* @return The array of certificates associated with a connection.
*/
public Certificate[] getClientCertificateChain()
{
if (tlsActiveProvider != null)
{
return tlsActiveProvider.getClientCertificateChain();
}
if (saslActiveProvider != null)
{
return saslActiveProvider.getClientCertificateChain();
}
return new Certificate[0];
}
/**
* Retrieves the TLS redirecting byte channel used in a LDAP client
* connection.
*
* @return The TLS redirecting byte channel.
*/
public ByteChannel getChannel() {
return this.tlsChannel;
}
/**
* {@inheritDoc}
*/
public int getSSF()
{
}
/**
* {@inheritDoc}
*/
public void finishBindOrStartTLS()
{
if(this.tlsPendingProvider != null)
{
enableTLS();
}
if (this.saslPendingProvider != null)
{
enableSASL();
}
super.finishBindOrStartTLS();
}
}