0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License, Version 1.0 only
0N/A * (the "License"). You may not use this file except in compliance
0N/A * with the License.
0N/A *
0N/A * You can obtain a copy of the license at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
0N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
0N/A * add the following below this CDDL HEADER, with the fields enclosed
873N/A * by brackets "[]" replaced with your own identifying information:
0N/A * Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A *
0N/A *
5023N/A * Copyright 2006-2010 Sun Microsystems, Inc.
6023N/A * Portions Copyright 2010-2013 ForgeRock AS.
0N/A */
0N/Apackage org.opends.server.protocols.ldap;
2366N/A
2366N/A
1689N/A
6134N/Aimport static org.opends.messages.CoreMessages.*;
4134N/Aimport static org.opends.messages.ProtocolMessages.*;
6134N/Aimport static org.opends.server.core.DirectoryServer.*;
6134N/Aimport static org.opends.server.loggers.AccessLogger.*;
6134N/Aimport static org.opends.server.loggers.ErrorLogger.*;
6128N/Aimport static org.opends.server.loggers.debug.DebugLogger.*;
4134N/Aimport static org.opends.server.protocols.ldap.LDAPConstants.*;
6134N/Aimport static org.opends.server.util.ServerConstants.*;
6128N/Aimport static org.opends.server.util.StaticUtils.*;
4134N/A
4926N/Aimport java.io.IOException;
0N/Aimport java.net.InetAddress;
4926N/Aimport java.nio.ByteBuffer;
4926N/Aimport java.nio.channels.*;
4134N/Aimport java.security.cert.Certificate;
0N/Aimport java.util.Collection;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.List;
0N/Aimport java.util.concurrent.ConcurrentHashMap;
0N/Aimport java.util.concurrent.atomic.AtomicLong;
2366N/Aimport java.util.concurrent.atomic.AtomicReference;
5899N/Aimport java.util.concurrent.locks.Lock;
5899N/Aimport java.util.concurrent.locks.ReentrantLock;
0N/A
6000N/Aimport javax.net.ssl.SSLException;
6000N/A
2366N/Aimport org.opends.messages.Message;
2366N/Aimport org.opends.messages.MessageBuilder;
0N/Aimport org.opends.server.api.ClientConnection;
0N/Aimport org.opends.server.api.ConnectionHandler;
4926N/Aimport org.opends.server.core.*;
3894N/Aimport org.opends.server.core.networkgroups.NetworkGroup;
4134N/Aimport org.opends.server.extensions.ConnectionSecurityProvider;
4134N/Aimport org.opends.server.extensions.RedirectingByteChannel;
4134N/Aimport org.opends.server.extensions.TLSByteChannel;
0N/Aimport org.opends.server.extensions.TLSCapableConnection;
1689N/Aimport org.opends.server.loggers.debug.DebugTracer;
4134N/Aimport org.opends.server.protocols.asn1.ASN1;
4134N/Aimport org.opends.server.protocols.asn1.ASN1ByteChannelReader;
4495N/Aimport org.opends.server.protocols.asn1.ASN1Reader;
4134N/Aimport org.opends.server.protocols.asn1.ASN1Writer;
4426N/Aimport org.opends.server.types.*;
1963N/Aimport org.opends.server.util.TimeThread;
0N/A
0N/A
0N/A/**
4134N/A * This class defines an LDAP client connection, which is a type of
4134N/A * client connection that will be accepted by an instance of the LDAP
4134N/A * connection handler and have its requests decoded by an LDAP request
4134N/A * handler.
0N/A */
5897N/Apublic final class LDAPClientConnection extends ClientConnection implements
4134N/A TLSCapableConnection
0N/A{
5897N/A
1400N/A /**
4495N/A * A runnable whose task is to close down all IO related channels
4495N/A * associated with a client connection after a small delay.
4495N/A */
4495N/A private static final class ConnectionFinalizerJob implements Runnable
4495N/A {
6128N/A /** The client connection ASN1 reader. */
4495N/A private final ASN1Reader asn1Reader;
4495N/A
6128N/A /** The client connection socket channel. */
4495N/A private final SocketChannel socketChannel;
4495N/A
6128N/A /** Creates a new connection finalizer job. */
6128N/A private ConnectionFinalizerJob(ASN1Reader asn1Reader,
6128N/A SocketChannel socketChannel)
4495N/A {
4495N/A this.asn1Reader = asn1Reader;
4495N/A this.socketChannel = socketChannel;
4495N/A }
4495N/A
4495N/A
4495N/A
4495N/A /**
4495N/A * {@inheritDoc}
4495N/A */
6134N/A @Override
4495N/A public void run()
4495N/A {
4495N/A try
4495N/A {
4495N/A asn1Reader.close();
4495N/A }
4495N/A catch (Exception e)
4495N/A {
4495N/A // In general, we don't care about any exception that might be
4495N/A // thrown here.
4495N/A if (debugEnabled())
4495N/A {
4495N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
4495N/A }
4495N/A }
4495N/A
4495N/A try
4495N/A {
4495N/A socketChannel.close();
4495N/A }
4495N/A catch (Exception e)
4495N/A {
4495N/A // In general, we don't care about any exception that might be
4495N/A // thrown here.
4495N/A if (debugEnabled())
4495N/A {
4495N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
4495N/A }
4495N/A }
4495N/A }
4495N/A }
4495N/A
4926N/A /**
4926N/A * Channel that writes the contents of the provided buffer to the client,
4926N/A * throwing an exception if the write is unsuccessful for too
4926N/A * long (e.g., if the client is unresponsive or there is a network
4926N/A * problem). If possible, it will attempt to use the selector returned
4926N/A * by the {@code ClientConnection.getWriteSelector} method, but it is
4926N/A * capable of working even if that method returns {@code null}. <BR>
4926N/A *
4926N/A * Note that the original position and limit values will not be
4926N/A * preserved, so if that is important to the caller, then it should
4926N/A * record them before calling this method and restore them after it
4926N/A * returns.
4926N/A */
4926N/A private class TimeoutWriteByteChannel implements ByteChannel
4926N/A {
6155N/A /** Synchronize concurrent writes to the same connection. */
5899N/A private final Lock writeLock = new ReentrantLock();
5899N/A
6134N/A @Override
4926N/A public int read(ByteBuffer byteBuffer) throws IOException
4926N/A {
5990N/A int bytesRead = clientChannel.read(byteBuffer);
5990N/A if (bytesRead > 0 && keepStats)
5990N/A {
5990N/A statTracker.updateBytesRead(bytesRead);
5990N/A }
5990N/A return bytesRead;
4926N/A }
4926N/A
6134N/A @Override
4926N/A public boolean isOpen()
4926N/A {
4926N/A return clientChannel.isOpen();
4926N/A }
4926N/A
6134N/A @Override
4926N/A public void close() throws IOException
4926N/A {
4926N/A clientChannel.close();
4926N/A }
4926N/A
5899N/A
5899N/A
6134N/A @Override
4926N/A public int write(ByteBuffer byteBuffer) throws IOException
4926N/A {
5899N/A writeLock.lock();
5899N/A try
4926N/A {
5899N/A int bytesToWrite = byteBuffer.remaining();
5990N/A int bytesWritten = clientChannel.write(byteBuffer);
5990N/A if (bytesWritten > 0 && keepStats)
5990N/A {
5990N/A statTracker.updateBytesWritten(bytesWritten);
5990N/A }
5899N/A if (!byteBuffer.hasRemaining())
4926N/A {
5899N/A return bytesToWrite;
4926N/A }
4926N/A
5899N/A long startTime = System.currentTimeMillis();
5899N/A long waitTime = getMaxBlockedWriteTimeLimit();
5899N/A if (waitTime <= 0)
4926N/A {
5899N/A // We won't support an infinite time limit, so fall back to using
5899N/A // five minutes, which is a very long timeout given that we're
5899N/A // blocking a worker thread.
5899N/A waitTime = 300000L;
4926N/A }
5899N/A long stopTime = startTime + waitTime;
4926N/A
5899N/A Selector selector = getWriteSelector();
5899N/A if (selector == null)
4926N/A {
5899N/A // The client connection does not provide a selector, so we'll
5899N/A // fall back to a more inefficient way that will work without a
5899N/A // selector.
5899N/A while (byteBuffer.hasRemaining()
5899N/A && (System.currentTimeMillis() < stopTime))
4926N/A {
5990N/A bytesWritten = clientChannel.write(byteBuffer);
5990N/A if (bytesWritten < 0)
4926N/A {
5899N/A // The client connection has been closed.
5899N/A throw new ClosedChannelException();
4926N/A }
5990N/A if (bytesWritten > 0 && keepStats)
5990N/A {
5990N/A statTracker.updateBytesWritten(bytesWritten);
5990N/A }
4926N/A }
4926N/A
4926N/A if (byteBuffer.hasRemaining())
4926N/A {
5899N/A // If we've gotten here, then the write timed out.
5899N/A throw new ClosedChannelException();
5899N/A }
5899N/A
5899N/A return bytesToWrite;
5899N/A }
5899N/A
5899N/A // Register with the selector for handling write operations.
5899N/A SelectionKey key = clientChannel.register(selector,
5899N/A SelectionKey.OP_WRITE);
5899N/A try
5899N/A {
5899N/A selector.select(waitTime);
5899N/A while (byteBuffer.hasRemaining())
5899N/A {
5899N/A long currentTime = System.currentTimeMillis();
5899N/A if (currentTime >= stopTime)
5899N/A {
5899N/A // We've been blocked for too long.
5899N/A throw new ClosedChannelException();
5899N/A }
5899N/A else
5899N/A {
5899N/A waitTime = stopTime - currentTime;
5899N/A }
5899N/A
5899N/A Iterator<SelectionKey> iterator = selector.selectedKeys()
5899N/A .iterator();
5899N/A while (iterator.hasNext())
5899N/A {
5899N/A SelectionKey k = iterator.next();
5899N/A if (k.isWritable())
5899N/A {
5990N/A bytesWritten = clientChannel.write(byteBuffer);
5899N/A if (bytesWritten < 0)
5899N/A {
5899N/A // The client connection has been closed.
5899N/A throw new ClosedChannelException();
5899N/A }
5990N/A if (bytesWritten > 0 && keepStats)
5990N/A {
5990N/A statTracker.updateBytesWritten(bytesWritten);
5990N/A }
5899N/A
5899N/A iterator.remove();
5899N/A }
5899N/A }
5899N/A
5899N/A if (byteBuffer.hasRemaining())
5899N/A {
5899N/A selector.select(waitTime);
5899N/A }
5899N/A }
5899N/A
5899N/A return bytesToWrite;
5899N/A }
5899N/A finally
5899N/A {
5899N/A if (key.isValid())
5899N/A {
5899N/A key.cancel();
5899N/A selector.selectNow();
4926N/A }
4926N/A }
4926N/A }
4926N/A finally
4926N/A {
5899N/A writeLock.unlock();
4926N/A }
4926N/A }
4926N/A }
4926N/A
4495N/A
6155N/A /** The tracer object for the debug logger. */
1400N/A private static final DebugTracer TRACER = getTracer();
1400N/A
5897N/A /**
5897N/A * Thread local ASN1Writer and buffer.
5897N/A */
5897N/A private static final class ASN1WriterHolder
5897N/A {
5897N/A private final ASN1Writer writer;
5897N/A private final ByteStringBuilder buffer;
5897N/A private final int maxBufferSize;
5897N/A
5897N/A private ASN1WriterHolder()
5897N/A {
5897N/A this.buffer = new ByteStringBuilder();
5897N/A this.maxBufferSize = getMaxInternalBufferSize();
5897N/A this.writer = ASN1.getWriter(buffer, maxBufferSize);
5897N/A }
5897N/A }
5897N/A
6155N/A /**
6155N/A * Cached ASN1 writer: a thread can only write to one connection at a time.
6155N/A */
5897N/A private static final ThreadLocal<ASN1WriterHolder> ASN1_WRITER_CACHE =
5897N/A new ThreadLocal<ASN1WriterHolder>()
5897N/A {
5897N/A /**
5897N/A * {@inheritDoc}
5897N/A */
6134N/A @Override
5897N/A protected ASN1WriterHolder initialValue()
5897N/A {
5897N/A return new ASN1WriterHolder();
5897N/A }
5897N/A };
5897N/A
5897N/A private ASN1WriterHolder getASN1Writer()
5897N/A {
5897N/A ASN1WriterHolder holder = ASN1_WRITER_CACHE.get();
5897N/A if (holder.maxBufferSize != getMaxInternalBufferSize())
5897N/A {
5897N/A // Setting has changed, so recreate the holder.
5897N/A holder = new ASN1WriterHolder();
5897N/A ASN1_WRITER_CACHE.set(holder);
5897N/A }
5897N/A return holder;
5897N/A }
5897N/A
6155N/A /** The time that the last operation was completed. */
4134N/A private final AtomicLong lastCompletionTime;
1963N/A
6155N/A /** The next operation ID that should be used for this connection. */
4134N/A private final AtomicLong nextOperationID;
0N/A
6155N/A /** The selector that may be used for write operations. */
4134N/A private final AtomicReference<Selector> writeSelector;
2366N/A
6155N/A /**
6155N/A * Indicates whether the Directory Server believes this connection to be valid
6155N/A * and available for communication.
6155N/A */
4495N/A private volatile boolean connectionValid;
0N/A
6155N/A /**
6155N/A * Indicates whether this connection is about to be closed. This will be used
6155N/A * to prevent accepting new requests while a disconnect is in progress.
6155N/A */
0N/A private boolean disconnectRequested;
0N/A
6155N/A /**
6155N/A * Indicates whether the connection should keep statistics regarding the
6155N/A * operations that it is performing.
6155N/A */
4134N/A private final boolean keepStats;
0N/A
6155N/A /** The set of all operations currently in progress on this connection. */
4134N/A private final ConcurrentHashMap<Integer, Operation> operationsInProgress;
0N/A
6155N/A /**
6155N/A * The number of operations performed on this connection. Used to compare with
6155N/A * the resource limits of the network group.
6155N/A */
4814N/A private final AtomicLong operationsPerformed;
0N/A
6155N/A /** The port on the client from which this connection originated. */
4134N/A private final int clientPort;
0N/A
6155N/A /**
6155N/A * The LDAP version that the client is using to communicate with the server.
6155N/A */
0N/A private int ldapVersion;
0N/A
6155N/A /** The port on the server to which this client has connected. */
4134N/A private final int serverPort;
0N/A
6155N/A /** The reference to the connection handler that accepted this connection. */
4134N/A private final LDAPConnectionHandler connectionHandler;
0N/A
6155N/A /** The statistics tracker associated with this client connection. */
4134N/A private final LDAPStatistics statTracker;
4635N/A private boolean useNanoTime=false;
0N/A
3999N/A
6155N/A /** The connection ID assigned to this connection. */
4134N/A private final long connectionID;
0N/A
6155N/A /**
6155N/A * The lock used to provide threadsafe access to the set of operations in
6155N/A * progress.
6155N/A */
4134N/A private final Object opsInProgressLock;
0N/A
6155N/A /** The socket channel with which this client connection is associated. */
4134N/A private final SocketChannel clientChannel;
0N/A
6155N/A /** The byte channel used for blocking writes with time out. */
4926N/A private final ByteChannel timeoutClientChannel;
4926N/A
6155N/A /** The string representation of the address of the client. */
4134N/A private final String clientAddress;
0N/A
6155N/A /**
6155N/A * The name of the protocol that the client is using to communicate with the
6155N/A * server.
6155N/A */
4134N/A private final String protocol;
4134N/A
6155N/A /**
6155N/A * The string representation of the address of the server to which the client
6155N/A * has connected.
6155N/A */
4134N/A private final String serverAddress;
629N/A
5897N/A
5023N/A
4495N/A private ASN1ByteChannelReader asn1Reader;
5734N/A private final int bufferSize;
4134N/A private final RedirectingByteChannel saslChannel;
4134N/A private final RedirectingByteChannel tlsChannel;
4426N/A private volatile ConnectionSecurityProvider activeProvider = null;
4426N/A private volatile ConnectionSecurityProvider tlsPendingProvider = null;
4426N/A private volatile ConnectionSecurityProvider saslPendingProvider = null;
0N/A
4134N/A
4134N/A /**
0N/A * Creates a new LDAP client connection with the provided information.
0N/A *
4134N/A * @param connectionHandler
4134N/A * The connection handler that accepted this connection.
4134N/A * @param clientChannel
4134N/A * The socket channel that may be used to communicate with
4134N/A * the client.
4333N/A * @param protocol String representing the protocol (LDAP or LDAP+SSL).
4926N/A * @throws DirectoryException If SSL initialisation fails.
0N/A */
5897N/A LDAPClientConnection(LDAPConnectionHandler connectionHandler,
4926N/A SocketChannel clientChannel, String protocol) throws DirectoryException
0N/A {
4134N/A this.connectionHandler = connectionHandler;
4134N/A if (connectionHandler.isAdminConnectionHandler())
4134N/A {
3894N/A setNetworkGroup(NetworkGroup.getAdminNetworkGroup());
3894N/A }
3894N/A
4134N/A this.clientChannel = clientChannel;
4926N/A timeoutClientChannel = new TimeoutWriteByteChannel();
2464N/A opsInProgressLock = new Object();
4134N/A ldapVersion = 3;
4134N/A lastCompletionTime = new AtomicLong(TimeThread.getTime());
4134N/A nextOperationID = new AtomicLong(0);
4134N/A connectionValid = true;
4134N/A disconnectRequested = false;
4134N/A operationsInProgress = new ConcurrentHashMap<Integer, Operation>();
4814N/A operationsPerformed = new AtomicLong(0);
4134N/A keepStats = connectionHandler.keepStats();
4333N/A this.protocol = protocol;
4134N/A writeSelector = new AtomicReference<Selector>();
4134N/A clientAddress =
4134N/A clientChannel.socket().getInetAddress().getHostAddress();
4134N/A clientPort = clientChannel.socket().getPort();
4134N/A serverAddress =
4134N/A clientChannel.socket().getLocalAddress().getHostAddress();
4134N/A serverPort = clientChannel.socket().getLocalPort();
4635N/A
4635N/A statTracker =
4635N/A this.connectionHandler.getStatTracker();
0N/A
0N/A if (keepStats)
0N/A {
0N/A statTracker.updateConnect();
4635N/A this.useNanoTime=DirectoryServer.getUseNanoTime();
0N/A }
0N/A
5734N/A bufferSize = connectionHandler.getBufferSize();
4814N/A
4134N/A tlsChannel =
4926N/A RedirectingByteChannel.getRedirectingByteChannel(
4926N/A timeoutClientChannel);
4134N/A saslChannel =
4134N/A RedirectingByteChannel.getRedirectingByteChannel(tlsChannel);
4134N/A this.asn1Reader =
5734N/A ASN1.getReader(saslChannel, bufferSize, connectionHandler
4134N/A .getMaxRequestSize());
4926N/A
4926N/A if (connectionHandler.useSSL())
4926N/A {
6000N/A enableSSL(connectionHandler.getTLSByteChannel(timeoutClientChannel));
4926N/A }
4926N/A
4142N/A connectionID = DirectoryServer.newConnectionAccepted(this);
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the connection ID assigned to this connection.
0N/A *
4134N/A * @return The connection ID assigned to this connection.
0N/A */
4134N/A @Override
0N/A public long getConnectionID()
0N/A {
0N/A return connectionID;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the connection handler that accepted this client
4134N/A * connection.
0N/A *
4134N/A * @return The connection handler that accepted this client
4134N/A * connection.
0N/A */
4134N/A @Override
3853N/A public ConnectionHandler<?> getConnectionHandler()
0N/A {
0N/A return connectionHandler;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the socket channel that can be used to communicate with
4134N/A * the client.
0N/A *
4134N/A * @return The socket channel that can be used to communicate with the
4134N/A * client.
0N/A */
4170N/A @Override
0N/A public SocketChannel getSocketChannel()
0N/A {
0N/A return clientChannel;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the protocol that the client is using to communicate with
4134N/A * the Directory Server.
0N/A *
4134N/A * @return The protocol that the client is using to communicate with
4134N/A * the Directory Server.
0N/A */
4134N/A @Override
0N/A public String getProtocol()
0N/A {
629N/A return protocol;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves a string representation of the address of the client.
0N/A *
4134N/A * @return A string representation of the address of the client.
0N/A */
4134N/A @Override
0N/A public String getClientAddress()
0N/A {
0N/A return clientAddress;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the port number for this connection on the client system.
0N/A *
4134N/A * @return The port number for this connection on the client system.
0N/A */
4134N/A @Override
0N/A public int getClientPort()
0N/A {
0N/A return clientPort;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves a string representation of the address on the server to
4134N/A * which the client connected.
0N/A *
4134N/A * @return A string representation of the address on the server to
4134N/A * which the client connected.
0N/A */
4134N/A @Override
0N/A public String getServerAddress()
0N/A {
0N/A return serverAddress;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the port number for this connection on the server system.
0N/A *
4134N/A * @return The port number for this connection on the server system.
0N/A */
4134N/A @Override
0N/A public int getServerPort()
0N/A {
0N/A return serverPort;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the <CODE>java.net.InetAddress</CODE> associated with the
4134N/A * remote client system.
0N/A *
4134N/A * @return The <CODE>java.net.InetAddress</CODE> associated with the
4134N/A * remote client system. It may be <CODE>null</CODE> if the
4134N/A * client is not connected over an IP-based connection.
0N/A */
4134N/A @Override
0N/A public InetAddress getRemoteAddress()
0N/A {
0N/A return clientChannel.socket().getInetAddress();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the <CODE>java.net.InetAddress</CODE> for the Directory
4134N/A * Server system to which the client has established the connection.
0N/A *
4134N/A * @return The <CODE>java.net.InetAddress</CODE> for the Directory
4134N/A * Server system to which the client has established the
4134N/A * connection. It may be <CODE>null</CODE> if the client is
4134N/A * not connected over an IP-based connection.
0N/A */
4134N/A @Override
0N/A public InetAddress getLocalAddress()
0N/A {
0N/A return clientChannel.socket().getLocalAddress();
0N/A }
0N/A
6198N/A /** {@inheritDoc} */
6198N/A @Override
6198N/A public boolean isConnectionValid()
6198N/A {
6198N/A return this.connectionValid;
6198N/A }
0N/A
0N/A /**
4134N/A * Indicates whether this client connection is currently using a
4134N/A * secure mechanism to communicate with the server. Note that this may
4134N/A * change over time based on operations performed by the client or
4134N/A * server (e.g., it may go from <CODE>false</CODE> to
4134N/A * <CODE>true</CODE> if the client uses the StartTLS extended
4134N/A * operation).
0N/A *
4134N/A * @return <CODE>true</CODE> if the client connection is currently
4134N/A * using a secure mechanism to communicate with the server, or
4134N/A * <CODE>false</CODE> if not.
0N/A */
4134N/A @Override
0N/A public boolean isSecure()
0N/A {
4134N/A if (activeProvider != null)
4134N/A return activeProvider.isSecure();
4134N/A else
4134N/A return false;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Sends a response to the client based on the information in the
4134N/A * provided operation.
0N/A *
4134N/A * @param operation
4134N/A * The operation for which to send the response.
0N/A */
4134N/A @Override
0N/A public void sendResponse(Operation operation)
0N/A {
4134N/A // Since this is the final response for this operation, we can go
4134N/A // ahead and remove it from the "operations in progress" list. It
4134N/A // can't be canceled after this point, and this will avoid potential
4134N/A // race conditions in which the client immediately sends another
4134N/A // request with the same message ID as was used for this operation.
4635N/A
4635N/A if (keepStats) {
4635N/A long time;
4635N/A if (useNanoTime) {
4635N/A time = operation.getProcessingNanoTime();
4635N/A } else {
4635N/A time = operation.getProcessingTime();
4635N/A }
4635N/A this.statTracker.updateOperationMonitoringData(
4635N/A operation.getOperationType(),
4635N/A time);
4635N/A }
4635N/A
5494N/A // Avoid sending the response if one has already been sent. This may happen
5494N/A // if operation processing encounters a run-time exception after sending the
5494N/A // response: the worker thread exception handling code will attempt to send
5494N/A // an error result to the client indicating that a problem occurred.
5494N/A if (removeOperationInProgress(operation.getMessageID()))
309N/A {
5494N/A LDAPMessage message = operationToResponseLDAPMessage(operation);
5494N/A if (message != null)
5494N/A {
5494N/A sendLDAPMessage(message);
5494N/A }
309N/A }
309N/A }
309N/A
309N/A
309N/A
309N/A /**
4134N/A * Retrieves an LDAPMessage containing a response generated from the
4134N/A * provided operation.
309N/A *
4134N/A * @param operation
4134N/A * The operation to use to generate the response LDAPMessage.
4134N/A * @return An LDAPMessage containing a response generated from the
4134N/A * provided operation.
309N/A */
309N/A private LDAPMessage operationToResponseLDAPMessage(Operation operation)
309N/A {
0N/A ResultCode resultCode = operation.getResultCode();
0N/A if (resultCode == null)
0N/A {
4134N/A // This must mean that the operation has either not yet completed
4134N/A // or that it completed without a result for some reason. In any
4134N/A // case, log a message and set the response to "operations error".
4134N/A logError(ERR_LDAP_CLIENT_SEND_RESPONSE_NO_RESULT_CODE.get(
4134N/A operation.getOperationType().toString(), operation
4134N/A .getConnectionID(), operation.getOperationID()));
0N/A resultCode = DirectoryServer.getServerErrorResultCode();
0N/A }
0N/A
2086N/A MessageBuilder errorMessage = operation.getErrorMessage();
4134N/A DN matchedDN = operation.getMatchedDN();
0N/A
0N/A // Referrals are not allowed for LDAPv2 clients.
0N/A List<String> referralURLs;
0N/A if (ldapVersion == 2)
0N/A {
0N/A referralURLs = null;
0N/A
0N/A if (resultCode == ResultCode.REFERRAL)
0N/A {
0N/A resultCode = ResultCode.CONSTRAINT_VIOLATION;
2086N/A errorMessage.append(ERR_LDAPV2_REFERRAL_RESULT_CHANGED.get());
0N/A }
0N/A
0N/A List<String> opReferrals = operation.getReferralURLs();
4134N/A if ((opReferrals != null) && (!opReferrals.isEmpty()))
0N/A {
0N/A StringBuilder referralsStr = new StringBuilder();
0N/A Iterator<String> iterator = opReferrals.iterator();
0N/A referralsStr.append(iterator.next());
0N/A
0N/A while (iterator.hasNext())
0N/A {
0N/A referralsStr.append(", ");
0N/A referralsStr.append(iterator.next());
0N/A }
0N/A
4134N/A errorMessage.append(ERR_LDAPV2_REFERRALS_OMITTED.get(String
4134N/A .valueOf(referralsStr)));
0N/A }
0N/A }
0N/A else
0N/A {
0N/A referralURLs = operation.getReferralURLs();
0N/A }
0N/A
0N/A ProtocolOp protocolOp;
0N/A switch (operation.getOperationType())
0N/A {
4134N/A case ADD:
4134N/A protocolOp =
4134N/A new AddResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A case BIND:
4134N/A ByteString serverSASLCredentials =
4134N/A ((BindOperationBasis) operation).getServerSASLCredentials();
4134N/A protocolOp =
4134N/A new BindResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs,
4134N/A serverSASLCredentials);
4134N/A break;
4134N/A case COMPARE:
4134N/A protocolOp =
4134N/A new CompareResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A case DELETE:
4134N/A protocolOp =
4134N/A new DeleteResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A case EXTENDED:
4134N/A // If this an LDAPv2 client, then we can't send this.
4134N/A if (ldapVersion == 2)
4134N/A {
4134N/A logError(ERR_LDAPV2_SKIPPING_EXTENDED_RESPONSE.get(
4134N/A getConnectionID(), operation.getOperationID(), String
4134N/A .valueOf(operation)));
4134N/A return null;
4134N/A }
0N/A
4134N/A ExtendedOperationBasis extOp = (ExtendedOperationBasis) operation;
4134N/A protocolOp =
4134N/A new ExtendedResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs, extOp
4134N/A .getResponseOID(), extOp.getResponseValue());
4134N/A break;
4134N/A case MODIFY:
4134N/A protocolOp =
4134N/A new ModifyResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A case MODIFY_DN:
4134N/A protocolOp =
4134N/A new ModifyDNResponseProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A case SEARCH:
4134N/A protocolOp =
4134N/A new SearchResultDoneProtocolOp(resultCode.getIntValue(),
4134N/A errorMessage.toMessage(), matchedDN, referralURLs);
4134N/A break;
4134N/A default:
4134N/A // This must be a type of operation that doesn't have a response.
4134N/A // This shouldn't happen, so log a message and return.
4134N/A logError(ERR_LDAP_CLIENT_SEND_RESPONSE_INVALID_OP.get(String
4134N/A .valueOf(operation.getOperationType()), getConnectionID(),
4134N/A operation.getOperationID(), String.valueOf(operation)));
4134N/A return null;
0N/A }
0N/A
0N/A // Controls are not allowed for LDAPv2 clients.
4134N/A List<Control> controls;
0N/A if (ldapVersion == 2)
0N/A {
0N/A controls = null;
0N/A }
0N/A else
0N/A {
4134N/A controls = operation.getResponseControls();
0N/A }
0N/A
4134N/A return new LDAPMessage(operation.getMessageID(), protocolOp,
4134N/A controls);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Sends the provided search result entry to the client.
0N/A *
4134N/A * @param searchOperation
4134N/A * The search operation with which the entry is associated.
4134N/A * @param searchEntry
4134N/A * The search result entry to be sent to the client.
0N/A */
4134N/A @Override
0N/A public void sendSearchEntry(SearchOperation searchOperation,
4134N/A SearchResultEntry searchEntry)
0N/A {
0N/A SearchResultEntryProtocolOp protocolOp =
4134N/A new SearchResultEntryProtocolOp(searchEntry, ldapVersion);
0N/A
4134N/A sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(),
4134N/A protocolOp, searchEntry.getControls()));
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Sends the provided search result reference to the client.
0N/A *
4134N/A * @param searchOperation
4134N/A * The search operation with which the reference is
4134N/A * associated.
4134N/A * @param searchReference
4134N/A * The search result reference to be sent to the client.
4134N/A * @return <CODE>true</CODE> if the client is able to accept
4134N/A * referrals, or <CODE>false</CODE> if the client cannot
4134N/A * handle referrals and no more attempts should be made to
4134N/A * send them for the associated search operation.
0N/A */
4134N/A @Override
0N/A public boolean sendSearchReference(SearchOperation searchOperation,
4134N/A SearchResultReference searchReference)
0N/A {
4134N/A // Make sure this is not an LDAPv2 client. If it is, then they can't
4134N/A // see referrals so we'll not send anything. Also, throw an
4134N/A // exception so that the core server will know not to try sending
4134N/A // any more referrals to this client for the rest of the operation.
0N/A if (ldapVersion == 2)
0N/A {
4134N/A Message message =
4134N/A ERR_LDAPV2_SKIPPING_SEARCH_REFERENCE.get(getConnectionID(),
4134N/A searchOperation.getOperationID(), String
4134N/A .valueOf(searchReference));
2086N/A logError(message);
0N/A return false;
0N/A }
0N/A
0N/A SearchResultReferenceProtocolOp protocolOp =
4134N/A new SearchResultReferenceProtocolOp(searchReference);
0N/A
4134N/A sendLDAPMessage(new LDAPMessage(searchOperation.getMessageID(),
4134N/A protocolOp, searchReference.getControls()));
0N/A return true;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Sends the provided intermediate response message to the client.
0N/A *
4134N/A * @param intermediateResponse
4134N/A * The intermediate response message to be sent.
4134N/A * @return <CODE>true</CODE> if processing on the associated operation
4134N/A * should continue, or <CODE>false</CODE> if not.
0N/A */
4134N/A @Override
0N/A protected boolean sendIntermediateResponseMessage(
4134N/A IntermediateResponse intermediateResponse)
0N/A {
0N/A IntermediateResponseProtocolOp protocolOp =
4134N/A new IntermediateResponseProtocolOp(intermediateResponse
4134N/A .getOID(), intermediateResponse.getValue());
0N/A
0N/A Operation operation = intermediateResponse.getOperation();
0N/A
4134N/A LDAPMessage message =
4134N/A new LDAPMessage(operation.getMessageID(), protocolOp,
4134N/A intermediateResponse.getControls());
4134N/A sendLDAPMessage(message);
0N/A
4134N/A // The only reason we shouldn't continue processing is if the
4134N/A // connection is closed.
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Sends the provided LDAP message to the client.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message to send to the client.
0N/A */
5897N/A private void sendLDAPMessage(LDAPMessage message)
0N/A {
5897N/A // Use a thread local writer.
5897N/A final ASN1WriterHolder holder = getASN1Writer();
4134N/A try
4134N/A {
5897N/A message.write(holder.writer);
5897N/A holder.buffer.copyTo(saslChannel);
4814N/A
4814N/A if (debugEnabled())
4134N/A {
4814N/A TRACER.debugProtocolElement(DebugLogLevel.VERBOSE,
4814N/A message.toString());
4814N/A }
4495N/A
4814N/A if (keepStats)
4814N/A {
5990N/A statTracker.updateMessageWritten(message);
0N/A }
4134N/A }
4134N/A catch (Exception e)
4134N/A {
4134N/A if (debugEnabled())
0N/A {
4134N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
4134N/A }
0N/A
4134N/A // FIXME -- Log a message or something
4609N/A disconnect(DisconnectReason.SERVER_ERROR, false, null);
4134N/A return;
0N/A }
5897N/A finally
5897N/A {
5897N/A // Clear and reset all of the internal buffers ready for the next usage.
6128N/A // The ASN1Writer is based on a ByteStringBuilder so closing will cause
6128N/A // the internal buffers to be resized if needed.
6128N/A close(holder.writer);
5897N/A }
5897N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Closes the connection to the client, optionally sending it a
4134N/A * message indicating the reason for the closure. Note that the
4134N/A * ability to send a notice of disconnection may not be available for
4134N/A * all protocols or under all circumstances.
0N/A *
4134N/A * @param disconnectReason
4134N/A * The disconnect reason that provides the generic cause for
4134N/A * the disconnect.
4134N/A * @param sendNotification
4134N/A * Indicates whether to try to provide notification to the
4134N/A * client that the connection will be closed.
4134N/A * @param message
4134N/A * The message to include in the disconnect notification
4134N/A * response. It may be <CODE>null</CODE> if no message is to
4134N/A * be sent.
0N/A */
4134N/A @Override
0N/A public void disconnect(DisconnectReason disconnectReason,
4134N/A boolean sendNotification, Message message)
0N/A {
4134N/A // Set a flag indicating that the connection is being terminated so
4134N/A // that no new requests will be accepted. Also cancel all operations
4134N/A // in progress.
3109N/A synchronized (opsInProgressLock)
0N/A {
3109N/A // If we are already in the middle of a disconnect, then don't
3109N/A // do anything.
3109N/A if (disconnectRequested)
3109N/A {
3109N/A return;
3109N/A }
3109N/A
3109N/A disconnectRequested = true;
0N/A }
0N/A
0N/A if (keepStats)
0N/A {
0N/A statTracker.updateDisconnect();
0N/A }
0N/A
0N/A if (connectionID >= 0)
0N/A {
0N/A DirectoryServer.connectionClosed(this);
0N/A }
0N/A
0N/A // Indicate that this connection is no longer valid.
0N/A connectionValid = false;
0N/A
4134N/A if (message != null)
3679N/A {
3679N/A MessageBuilder msgBuilder = new MessageBuilder();
3679N/A msgBuilder.append(disconnectReason.getClosureMessage());
3679N/A msgBuilder.append(": ");
3679N/A msgBuilder.append(message);
4134N/A cancelAllOperations(new CancelRequest(true, msgBuilder
4134N/A .toMessage()));
3679N/A }
3679N/A else
3679N/A {
4134N/A cancelAllOperations(new CancelRequest(true, disconnectReason
4134N/A .getClosureMessage()));
3679N/A }
773N/A finalizeConnectionInternal();
0N/A
2366N/A // If there is a write selector for this connection, then close it.
2366N/A Selector selector = writeSelector.get();
6128N/A close(selector);
2366N/A
4134N/A // See if we should send a notification to the client. If so, then
4134N/A // construct and send a notice of disconnection unsolicited
4134N/A // response. Note that we cannot send this notification to an LDAPv2
4134N/A // client.
0N/A if (sendNotification && (ldapVersion != 2))
0N/A {
0N/A try
0N/A {
0N/A int resultCode;
0N/A switch (disconnectReason)
0N/A {
4134N/A case PROTOCOL_ERROR:
4134N/A resultCode = LDAPResultCode.PROTOCOL_ERROR;
4134N/A break;
4134N/A case SERVER_SHUTDOWN:
4134N/A resultCode = LDAPResultCode.UNAVAILABLE;
4134N/A break;
4134N/A case SERVER_ERROR:
4134N/A resultCode =
4134N/A DirectoryServer.getServerErrorResultCode().getIntValue();
4134N/A break;
4134N/A case ADMIN_LIMIT_EXCEEDED:
4134N/A case IDLE_TIME_LIMIT_EXCEEDED:
4134N/A case MAX_REQUEST_SIZE_EXCEEDED:
4134N/A case IO_TIMEOUT:
4134N/A resultCode = LDAPResultCode.ADMIN_LIMIT_EXCEEDED;
4134N/A break;
4134N/A case CONNECTION_REJECTED:
4134N/A resultCode = LDAPResultCode.CONSTRAINT_VIOLATION;
4134N/A break;
5545N/A case INVALID_CREDENTIALS:
5545N/A resultCode = LDAPResultCode.INVALID_CREDENTIALS;
5545N/A break;
4134N/A default:
4134N/A resultCode = LDAPResultCode.OTHER;
4134N/A break;
0N/A }
0N/A
2086N/A Message errMsg;
0N/A if (message == null)
0N/A {
4134N/A errMsg =
4134N/A INFO_LDAP_CLIENT_GENERIC_NOTICE_OF_DISCONNECTION.get();
0N/A }
0N/A else
0N/A {
0N/A errMsg = message;
0N/A }
0N/A
4134N/A ExtendedResponseProtocolOp notificationOp =
4134N/A new ExtendedResponseProtocolOp(resultCode, errMsg, null,
4134N/A null, OID_NOTICE_OF_DISCONNECTION, null);
0N/A
4134N/A sendLDAPMessage(new LDAPMessage(0, notificationOp, null));
0N/A }
0N/A catch (Exception e)
0N/A {
4134N/A // NYI -- Log a message indicating that we couldn't send the
4134N/A // notice of disconnection.
6156N/A if (debugEnabled())
6156N/A {
6156N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
6156N/A }
0N/A }
0N/A }
0N/A
4495N/A // Enqueue the connection channels for closing by the finalizer.
6128N/A Runnable r = new ConnectionFinalizerJob(asn1Reader, clientChannel);
4495N/A connectionHandler.registerConnectionFinalizer(r);
0N/A
0N/A // NYI -- Deregister the client connection from any server components that
0N/A // might know about it.
0N/A
0N/A // Log a disconnect message.
2086N/A logDisconnect(this, disconnectReason, message);
0N/A
320N/A try
320N/A {
320N/A PluginConfigManager pluginManager =
4134N/A DirectoryServer.getPluginConfigManager();
320N/A pluginManager.invokePostDisconnectPlugins(this, disconnectReason,
4134N/A message);
320N/A }
320N/A catch (Exception e)
320N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
320N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Retrieves the set of operations in progress for this client
4134N/A * connection. This list must not be altered by any caller.
0N/A *
4134N/A * @return The set of operations in progress for this client
4134N/A * connection.
0N/A */
4134N/A @Override
3902N/A public Collection<Operation> getOperationsInProgress()
0N/A {
0N/A return operationsInProgress.values();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Retrieves the operation in progress with the specified message ID.
0N/A *
4134N/A * @param messageID
4134N/A * The message ID for the operation to retrieve.
4134N/A * @return The operation in progress with the specified message ID, or
4134N/A * <CODE>null</CODE> if no such operation could be found.
0N/A */
4134N/A @Override
3902N/A public Operation getOperationInProgress(int messageID)
0N/A {
0N/A return operationsInProgress.get(messageID);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Adds the provided operation to the set of operations in progress
4134N/A * for this client connection.
0N/A *
4134N/A * @param operation
4134N/A * The operation to add to the set of operations in progress
4134N/A * for this client connection.
4134N/A * @throws DirectoryException
4134N/A * If the operation is not added for some reason (e.g., the
4134N/A * client already has reached the maximum allowed concurrent
4134N/A * requests).
0N/A */
6153N/A private void addOperationInProgress(Operation operation)
4134N/A throws DirectoryException
0N/A {
0N/A int messageID = operation.getMessageID();
0N/A
4134N/A // We need to grab a lock to ensure that no one else can add
4134N/A // operations to the queue while we are performing some preliminary
4134N/A // checks.
5245N/A try
0N/A {
5245N/A synchronized (opsInProgressLock)
0N/A {
4134N/A // If we're already in the process of disconnecting the client,
4134N/A // then reject the operation.
2464N/A if (disconnectRequested)
2464N/A {
6156N/A Message message = WARN_CLIENT_DISCONNECT_IN_PROGRESS.get();
2464N/A throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
4134N/A message);
2464N/A }
2464N/A
4814N/A // Add the operation to the list of operations in progress for
4814N/A // this connection.
4814N/A Operation op = operationsInProgress.putIfAbsent(messageID, operation);
4814N/A
4134N/A // See if there is already an operation in progress with the
4134N/A // same message ID. If so, then we can't allow it.
2464N/A if (op != null)
2464N/A {
2464N/A Message message =
5245N/A WARN_LDAP_CLIENT_DUPLICATE_MESSAGE_ID.get(messageID);
4134N/A throw new DirectoryException(ResultCode.PROTOCOL_ERROR,
4134N/A message);
2464N/A }
0N/A }
5245N/A
5245N/A // Try to add the operation to the work queue,
5245N/A // or run it synchronously (typically for the administration
5245N/A // connector)
5245N/A connectionHandler.getQueueingStrategy().enqueueRequest(
5245N/A operation);
5245N/A }
5245N/A catch (DirectoryException de)
5245N/A {
5245N/A if (debugEnabled())
868N/A {
5245N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
5245N/A }
2464N/A
5245N/A operationsInProgress.remove(messageID);
5245N/A lastCompletionTime.set(TimeThread.getTime());
2464N/A
5245N/A throw de;
5245N/A }
5245N/A catch (Exception e)
5245N/A {
5245N/A if (debugEnabled())
868N/A {
5245N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
5245N/A }
2464N/A
5245N/A Message message =
5245N/A WARN_LDAP_CLIENT_CANNOT_ENQUEUE.get(getExceptionMessage(e));
5245N/A throw new DirectoryException(DirectoryServer
5245N/A .getServerErrorResultCode(), message, e);
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Removes the provided operation from the set of operations in
4134N/A * progress for this client connection. Note that this does not make
4134N/A * any attempt to cancel any processing that may already be in
4134N/A * progress for the operation.
0N/A *
4134N/A * @param messageID
4134N/A * The message ID of the operation to remove from the set of
4134N/A * operations in progress.
4134N/A * @return <CODE>true</CODE> if the operation was found and removed
4134N/A * from the set of operations in progress, or
4134N/A * <CODE>false</CODE> if not.
0N/A */
4134N/A @Override
0N/A public boolean removeOperationInProgress(int messageID)
0N/A {
3902N/A Operation operation = operationsInProgress.remove(messageID);
1963N/A if (operation == null)
1963N/A {
1963N/A return false;
1963N/A }
4348N/A
4348N/A if (operation.getOperationType() == OperationType.ABANDON)
1963N/A {
4348N/A if (keepStats
4348N/A && (operation.getResultCode() == ResultCode.CANCELED))
4348N/A {
4348N/A statTracker.updateAbandonedOperation();
4348N/A }
1963N/A }
4348N/A
4348N/A lastCompletionTime.set(TimeThread.getTime());
4348N/A return true;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Attempts to cancel the specified operation.
0N/A *
4134N/A * @param messageID
4134N/A * The message ID of the operation to cancel.
4134N/A * @param cancelRequest
4134N/A * An object providing additional information about how the
4134N/A * cancel should be processed.
4134N/A * @return A cancel result that either indicates that the cancel was
4134N/A * successful or provides a reason that it was not.
0N/A */
4134N/A @Override
0N/A public CancelResult cancelOperation(int messageID,
4134N/A CancelRequest cancelRequest)
0N/A {
3902N/A Operation op = operationsInProgress.get(messageID);
0N/A if (op == null)
0N/A {
0N/A // See if the operation is in the list of persistent searches.
0N/A for (PersistentSearch ps : getPersistentSearches())
0N/A {
3902N/A if (ps.getMessageID() == messageID)
0N/A {
3916N/A // We only need to find the first persistent search
3916N/A // associated with the provided message ID. The persistent
3916N/A // search will ensure that all other related persistent
3916N/A // searches are cancelled.
3902N/A CancelResult cancelResult = ps.cancel();
650N/A
650N/A return cancelResult;
0N/A }
0N/A }
0N/A
3352N/A return new CancelResult(ResultCode.NO_SUCH_OPERATION, null);
0N/A }
0N/A else
0N/A {
650N/A CancelResult cancelResult = op.cancel(cancelRequest);
3948N/A
3948N/A return cancelResult;
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Attempts to cancel all operations in progress on this connection.
0N/A *
4134N/A * @param cancelRequest
4134N/A * An object providing additional information about how the
4134N/A * cancel should be processed.
0N/A */
4134N/A @Override
0N/A public void cancelAllOperations(CancelRequest cancelRequest)
0N/A {
0N/A // Make sure that no one can add any new operations.
2464N/A synchronized (opsInProgressLock)
0N/A {
2464N/A try
0N/A {
3902N/A for (Operation o : operationsInProgress.values())
0N/A {
2464N/A try
650N/A {
3902N/A o.abort(cancelRequest);
3352N/A
3352N/A // TODO: Assume its cancelled?
3352N/A if (keepStats)
2464N/A {
2464N/A statTracker.updateAbandonedOperation();
2464N/A }
2464N/A }
2464N/A catch (Exception e)
2464N/A {
2464N/A if (debugEnabled())
2464N/A {
2464N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
2464N/A }
650N/A }
0N/A }
2464N/A
4134N/A if (!(operationsInProgress.isEmpty() && getPersistentSearches()
4134N/A .isEmpty()))
0N/A {
2464N/A lastCompletionTime.set(TimeThread.getTime());
2464N/A }
2464N/A
2464N/A operationsInProgress.clear();
2464N/A
2464N/A for (PersistentSearch persistentSearch : getPersistentSearches())
2464N/A {
3902N/A persistentSearch.cancel();
0N/A }
0N/A }
2464N/A catch (Exception e)
0N/A {
2464N/A if (debugEnabled())
2464N/A {
2464N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
2464N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
4134N/A * Attempts to cancel all operations in progress on this connection
4134N/A * except the operation with the specified message ID.
0N/A *
4134N/A * @param cancelRequest
4134N/A * An object providing additional information about how the
4134N/A * cancel should be processed.
4134N/A * @param messageID
4134N/A * The message ID of the operation that should not be
4134N/A * canceled.
0N/A */
4134N/A @Override
0N/A public void cancelAllOperationsExcept(CancelRequest cancelRequest,
4134N/A int messageID)
0N/A {
0N/A // Make sure that no one can add any new operations.
2464N/A synchronized (opsInProgressLock)
0N/A {
2464N/A try
0N/A {
2464N/A for (int msgID : operationsInProgress.keySet())
0N/A {
2464N/A if (msgID == messageID)
2464N/A {
2464N/A continue;
2464N/A }
2464N/A
3902N/A Operation o = operationsInProgress.get(msgID);
2464N/A if (o != null)
0N/A {
2464N/A try
650N/A {
3352N/A o.abort(cancelRequest);
3352N/A
3352N/A // TODO: Assume its cancelled?
3352N/A if (keepStats)
2464N/A {
2464N/A statTracker.updateAbandonedOperation();
2464N/A }
2464N/A }
2464N/A catch (Exception e)
2464N/A {
2464N/A if (debugEnabled())
2464N/A {
2464N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
2464N/A }
650N/A }
0N/A }
2464N/A
2464N/A operationsInProgress.remove(msgID);
2464N/A lastCompletionTime.set(TimeThread.getTime());
0N/A }
0N/A
2464N/A for (PersistentSearch persistentSearch : getPersistentSearches())
2464N/A {
3916N/A if (persistentSearch.getMessageID() == messageID)
3916N/A {
3916N/A continue;
3916N/A }
3916N/A
3902N/A persistentSearch.cancel();
2464N/A lastCompletionTime.set(TimeThread.getTime());
2464N/A }
0N/A }
2464N/A catch (Exception e)
0N/A {
2464N/A if (debugEnabled())
2464N/A {
2464N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
2464N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
2366N/A * {@inheritDoc}
2366N/A */
2366N/A @Override()
2366N/A public Selector getWriteSelector()
2366N/A {
2366N/A Selector selector = writeSelector.get();
2366N/A if (selector == null)
2366N/A {
2366N/A try
2366N/A {
2366N/A selector = Selector.open();
4134N/A if (!writeSelector.compareAndSet(null, selector))
2366N/A {
2366N/A selector.close();
2366N/A selector = writeSelector.get();
2366N/A }
2366N/A }
2366N/A catch (Exception e)
2366N/A {
2366N/A if (debugEnabled())
2366N/A {
2366N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
2366N/A }
2366N/A }
2366N/A }
2366N/A
2366N/A return selector;
2366N/A }
2366N/A
2366N/A
2366N/A
2366N/A /**
2366N/A * {@inheritDoc}
2366N/A */
2366N/A @Override()
2366N/A public long getMaxBlockedWriteTimeLimit()
2366N/A {
2366N/A return connectionHandler.getMaxBlockedWriteTimeLimit();
2366N/A }
2366N/A
2366N/A
2366N/A
2366N/A /**
4134N/A * Returns the total number of operations initiated on this
4134N/A * connection.
3853N/A *
3853N/A * @return the total number of operations on this connection
3853N/A */
4134N/A @Override
4134N/A public long getNumberOfOperations()
4134N/A {
4814N/A return operationsPerformed.get();
4814N/A }
4814N/A
4814N/A
4814N/A
4814N/A /**
4814N/A * Returns the ASN1 reader for this connection.
4814N/A *
4814N/A * @return the ASN1 reader for this connection
4814N/A */
5897N/A ASN1ByteChannelReader getASN1Reader()
4814N/A {
4814N/A return asn1Reader;
3853N/A }
3853N/A
3853N/A
4134N/A
3853N/A /**
4134N/A * Process data read.
0N/A *
4814N/A * @return number of bytes read if this connection is still valid
4814N/A * or negative integer to indicate an error otherwise
0N/A */
5897N/A int processDataRead()
0N/A {
4814N/A if (bindOrStartTLSInProgress.get())
0N/A {
4814N/A // We should wait for the bind or startTLS to finish before
4814N/A // reading any more data off the socket.
4814N/A return 0;
4814N/A }
4426N/A
4814N/A try
4814N/A {
4814N/A int result = asn1Reader.processChannelData();
4814N/A if (result < 0)
0N/A {
4814N/A // The connection has been closed by the client. Disconnect
4814N/A // and return.
4814N/A disconnect(DisconnectReason.CLIENT_DISCONNECT, false, null);
4814N/A return -1;
4134N/A }
4814N/A return result;
4814N/A }
4814N/A catch (Exception e)
4814N/A {
4814N/A if (debugEnabled())
4134N/A {
4814N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
4134N/A }
5588N/A
6000N/A if (asn1Reader.hasRemainingData() || (e instanceof SSLException))
5588N/A {
5588N/A // The connection failed, but there was an unread partial message so
5588N/A // interpret this as an IO error.
5588N/A Message m = ERR_LDAP_CLIENT_IO_ERROR_DURING_READ.get(String
5588N/A .valueOf(e));
5588N/A disconnect(DisconnectReason.IO_ERROR, true, m);
5588N/A }
5588N/A else
5588N/A {
5588N/A // The connection failed and there was no unread data, so interpret this
5588N/A // as indicating that the client aborted (reset) the connection. This
5588N/A // happens when a client configures closes a connection which has been
5588N/A // configured with SO_LINGER set to 0.
5588N/A Message m = ERR_LDAP_CLIENT_IO_ERROR_BEFORE_READ.get();
5588N/A disconnect(DisconnectReason.CLIENT_DISCONNECT, true, m);
5588N/A }
5588N/A
4814N/A return -1;
4134N/A }
4134N/A }
0N/A
0N/A
0N/A
4134N/A /**
0N/A * Processes the provided LDAP message read from the client and takes
4134N/A * whatever action is appropriate. For most requests, this will
4134N/A * include placing the operation in the work queue. Certain requests
4134N/A * (in particular, abandons and unbinds) will be processed directly.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message to process.
4134N/A * @return <CODE>true</CODE> if the appropriate action was taken for
4134N/A * the request, or <CODE>false</CODE> if there was a fatal
4134N/A * error and the client has been disconnected as a result, or
4134N/A * if the client unbound from the server.
0N/A */
5897N/A boolean processLDAPMessage(LDAPMessage message)
0N/A {
0N/A if (keepStats)
0N/A {
0N/A statTracker.updateMessageRead(message);
3895N/A this.getNetworkGroup().updateMessageRead(message);
0N/A }
4814N/A operationsPerformed.getAndIncrement();
0N/A
4134N/A List<Control> opControls = message.getControls();
4134N/A
4134N/A // FIXME -- See if there is a bind in progress. If so, then deny
4134N/A // most kinds of operations.
0N/A
4134N/A // Figure out what type of operation we're dealing with based on the
4134N/A // LDAP message. Abandon and unbind requests will be processed here.
4134N/A // All other types of requests will be encapsulated into operations
4134N/A // and append into the work queue to be picked up by a worker
4134N/A // thread. Any other kinds of LDAP messages (e.g., response
4134N/A // messages) are illegal and will result in the connection being
4134N/A // terminated.
0N/A try
0N/A {
4426N/A if(bindOrStartTLSInProgress.get() ||
4426N/A (saslBindInProgress.get() &&
4426N/A message.getProtocolOpType() != OP_TYPE_BIND_REQUEST))
4426N/A {
4426N/A throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
4426N/A ERR_ENQUEUE_BIND_IN_PROGRESS.get());
4426N/A }
4426N/A
3999N/A boolean result;
0N/A switch (message.getProtocolOpType())
0N/A {
4134N/A case OP_TYPE_ABANDON_REQUEST:
4134N/A result = processAbandonRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_ADD_REQUEST:
4134N/A result = processAddRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_BIND_REQUEST:
4426N/A bindOrStartTLSInProgress.set(true);
4426N/A if(message.getBindRequestProtocolOp().
4426N/A getAuthenticationType() == AuthenticationType.SASL)
4426N/A {
4426N/A saslBindInProgress.set(true);
4426N/A }
4134N/A result = processBindRequest(message, opControls);
4426N/A if(!result)
4426N/A {
4426N/A bindOrStartTLSInProgress.set(false);
4426N/A if(message.getBindRequestProtocolOp().
4426N/A getAuthenticationType() == AuthenticationType.SASL)
4426N/A {
4426N/A saslBindInProgress.set(false);
4426N/A }
4426N/A }
4134N/A return result;
4134N/A case OP_TYPE_COMPARE_REQUEST:
4134N/A result = processCompareRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_DELETE_REQUEST:
4134N/A result = processDeleteRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_EXTENDED_REQUEST:
4426N/A if(message.getExtendedRequestProtocolOp().getOID().equals(
4426N/A OID_START_TLS_REQUEST))
4426N/A {
4426N/A bindOrStartTLSInProgress.set(true);
4426N/A }
4134N/A result = processExtendedRequest(message, opControls);
4426N/A if(!result &&
4426N/A message.getExtendedRequestProtocolOp().getOID().equals(
4426N/A OID_START_TLS_REQUEST))
4426N/A {
4426N/A bindOrStartTLSInProgress.set(false);
4426N/A }
4134N/A return result;
4134N/A case OP_TYPE_MODIFY_REQUEST:
4134N/A result = processModifyRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_MODIFY_DN_REQUEST:
4134N/A result = processModifyDNRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_SEARCH_REQUEST:
4134N/A result = processSearchRequest(message, opControls);
4134N/A return result;
4134N/A case OP_TYPE_UNBIND_REQUEST:
4134N/A result = processUnbindRequest(message, opControls);
4134N/A return result;
4134N/A default:
4134N/A Message msg =
4134N/A ERR_LDAP_DISCONNECT_DUE_TO_INVALID_REQUEST_TYPE.get(message
4134N/A .getProtocolOpName(), message.getMessageID());
4134N/A disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
4134N/A return false;
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A
4134N/A Message msg =
4134N/A ERR_LDAP_DISCONNECT_DUE_TO_PROCESSING_FAILURE.get(message
4134N/A .getProtocolOpName(), message.getMessageID(), String
4134N/A .valueOf(e));
2086N/A disconnect(DisconnectReason.SERVER_ERROR, true, msg);
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as an abandon request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the abandon request to
4134N/A * process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processAbandonRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4348N/A if ((ldapVersion == 2) && (controls != null)
4348N/A && (!controls.isEmpty()))
4348N/A {
4348N/A // LDAPv2 clients aren't allowed to send controls.
4348N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4348N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4348N/A return false;
4348N/A }
4348N/A
4348N/A // Create the abandon operation and add it into the work queue.
4134N/A AbandonRequestProtocolOp protocolOp =
4134N/A message.getAbandonRequestProtocolOp();
1857N/A AbandonOperationBasis abandonOp =
4134N/A new AbandonOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getIDToAbandon());
0N/A
4348N/A try
4348N/A {
4348N/A addOperationInProgress(abandonOp);
4348N/A }
4348N/A catch (DirectoryException de)
650N/A {
4348N/A if (debugEnabled())
4348N/A {
4348N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
4348N/A }
4348N/A
4348N/A // Don't send an error response since abandon operations
4348N/A // don't have a response.
650N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as an add request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the add request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processAddRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A AddResponseProtocolOp responseOp =
4134N/A new AddResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
0N/A // Create the add operation and add it into the work queue.
0N/A AddRequestProtocolOp protocolOp = message.getAddRequestProtocolOp();
1689N/A AddOperationBasis addOp =
4134N/A new AddOperationBasis(this, nextOperationID.getAndIncrement(),
4134N/A message.getMessageID(), controls, protocolOp.getDN(),
4134N/A protocolOp.getAttributes());
0N/A
0N/A try
0N/A {
0N/A addOperationInProgress(addOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A AddResponseProtocolOp responseOp =
4134N/A new AddResponseProtocolOp(de.getResultCode().getIntValue(),
4134N/A de.getMessageObject(), de.getMatchedDN(), de
4134N/A .getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, addOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a bind request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the bind request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processBindRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A BindRequestProtocolOp protocolOp =
4134N/A message.getBindRequestProtocolOp();
0N/A
4134N/A // See if this is an LDAPv2 bind request, and if so whether that
4134N/A // should be allowed.
1079N/A String versionString;
1079N/A switch (ldapVersion = protocolOp.getProtocolVersion())
0N/A {
4134N/A case 2:
4134N/A versionString = "2";
1079N/A
4134N/A if (!connectionHandler.allowLDAPv2())
4134N/A {
4134N/A BindResponseProtocolOp responseOp =
4134N/A new BindResponseProtocolOp(
5260N/A LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
4134N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CLIENTS_NOT_ALLOWED.get());
4134N/A return false;
4134N/A }
1755N/A
4134N/A if ((controls != null) && (!controls.isEmpty()))
4134N/A {
4134N/A // LDAPv2 clients aren't allowed to send controls.
4134N/A BindResponseProtocolOp responseOp =
4134N/A new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
4134N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A return false;
4134N/A }
1079N/A
4134N/A break;
4134N/A case 3:
4134N/A versionString = "3";
4134N/A break;
4134N/A default:
4383N/A // Unsupported protocol version. RFC4511 states that we MUST send
4383N/A // a protocol error back to the client.
4383N/A BindResponseProtocolOp responseOp =
4383N/A new BindResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4383N/A ERR_LDAP_UNSUPPORTED_PROTOCOL_VERSION.get(ldapVersion));
4383N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4383N/A responseOp));
4383N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4383N/A ERR_LDAP_UNSUPPORTED_PROTOCOL_VERSION.get(ldapVersion));
4383N/A return false;
0N/A }
0N/A
4134N/A ByteString bindDN = protocolOp.getDN();
0N/A
1689N/A BindOperationBasis bindOp;
0N/A switch (protocolOp.getAuthenticationType())
0N/A {
4134N/A case SIMPLE:
4134N/A bindOp =
4134N/A new BindOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A versionString, bindDN, protocolOp.getSimplePassword());
4134N/A break;
4134N/A case SASL:
4134N/A bindOp =
4134N/A new BindOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A versionString, bindDN, protocolOp.getSASLMechanism(),
4134N/A protocolOp.getSASLCredentials());
4134N/A break;
4134N/A default:
4134N/A // This is an invalid authentication type, and therefore a
4134N/A // protocol error. As per RFC 2251, a protocol error in a bind
4134N/A // request must result in terminating the connection.
4134N/A Message msg =
4134N/A ERR_LDAP_INVALID_BIND_AUTH_TYPE.get(message.getMessageID(),
4134N/A String.valueOf(protocolOp.getAuthenticationType()));
4134N/A disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
4134N/A return false;
0N/A }
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(bindOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A BindResponseProtocolOp responseOp =
4134N/A new BindResponseProtocolOp(de.getResultCode().getIntValue(),
4134N/A de.getMessageObject(), de.getMatchedDN(), de
4134N/A .getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, bindOp.getResponseControls()));
0N/A
0N/A // If it was a protocol error, then terminate the connection.
0N/A if (de.getResultCode() == ResultCode.PROTOCOL_ERROR)
0N/A {
4134N/A Message msg =
4134N/A ERR_LDAP_DISCONNECT_DUE_TO_BIND_PROTOCOL_ERROR.get(message
4134N/A .getMessageID(), de.getMessageObject());
2086N/A disconnect(DisconnectReason.PROTOCOL_ERROR, true, msg);
0N/A }
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a compare request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the compare request to
4134N/A * process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processCompareRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A CompareResponseProtocolOp responseOp =
4134N/A new CompareResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
4134N/A CompareRequestProtocolOp protocolOp =
4134N/A message.getCompareRequestProtocolOp();
1842N/A CompareOperationBasis compareOp =
4134N/A new CompareOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getDN(), protocolOp.getAttributeType(),
4134N/A protocolOp.getAssertionValue());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(compareOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A CompareResponseProtocolOp responseOp =
4134N/A new CompareResponseProtocolOp(de.getResultCode()
4134N/A .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
4134N/A de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, compareOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a delete request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the delete request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processDeleteRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A DeleteResponseProtocolOp responseOp =
4134N/A new DeleteResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
4134N/A DeleteRequestProtocolOp protocolOp =
4134N/A message.getDeleteRequestProtocolOp();
1689N/A DeleteOperationBasis deleteOp =
4134N/A new DeleteOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getDN());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(deleteOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A DeleteResponseProtocolOp responseOp =
4134N/A new DeleteResponseProtocolOp(
4134N/A de.getResultCode().getIntValue(), de.getMessageObject(),
4134N/A de.getMatchedDN(), de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, deleteOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as an extended request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the extended request to
4134N/A * process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processExtendedRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A // See if this is an LDAPv2 client. If it is, then they should not
4134N/A // be issuing extended requests. We can't send a response that we
4134N/A // can be sure they can understand, so we have no choice but to
4134N/A // close the connection.
0N/A if (ldapVersion == 2)
0N/A {
4134N/A Message msg =
4134N/A ERR_LDAPV2_EXTENDED_REQUEST_NOT_ALLOWED.get(
4134N/A getConnectionID(), message.getMessageID());
2086N/A logError(msg);
2086N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false, msg);
0N/A return false;
0N/A }
0N/A
0N/A // FIXME -- Do we need to handle certain types of request here?
0N/A // -- StartTLS requests
0N/A // -- Cancel requests
0N/A
0N/A ExtendedRequestProtocolOp protocolOp =
4134N/A message.getExtendedRequestProtocolOp();
1864N/A ExtendedOperationBasis extendedOp =
4134N/A new ExtendedOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getOID(), protocolOp.getValue());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(extendedOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A ExtendedResponseProtocolOp responseOp =
4134N/A new ExtendedResponseProtocolOp(de.getResultCode()
4134N/A .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
4134N/A de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, extendedOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a modify request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the modify request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processModifyRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A ModifyResponseProtocolOp responseOp =
4134N/A new ModifyResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
4134N/A ModifyRequestProtocolOp protocolOp =
4134N/A message.getModifyRequestProtocolOp();
1689N/A ModifyOperationBasis modifyOp =
4134N/A new ModifyOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getDN(), protocolOp.getModifications());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(modifyOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A ModifyResponseProtocolOp responseOp =
4134N/A new ModifyResponseProtocolOp(
4134N/A de.getResultCode().getIntValue(), de.getMessageObject(),
4134N/A de.getMatchedDN(), de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, modifyOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a modify DN request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the modify DN request to
4134N/A * process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processModifyDNRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A ModifyDNResponseProtocolOp responseOp =
4134N/A new ModifyDNResponseProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
0N/A ModifyDNRequestProtocolOp protocolOp =
4134N/A message.getModifyDNRequestProtocolOp();
1858N/A ModifyDNOperationBasis modifyDNOp =
4134N/A new ModifyDNOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getEntryDN(), protocolOp.getNewRDN(), protocolOp
4134N/A .deleteOldRDN(), protocolOp.getNewSuperior());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(modifyDNOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A ModifyDNResponseProtocolOp responseOp =
4134N/A new ModifyDNResponseProtocolOp(de.getResultCode()
4134N/A .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
4134N/A de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, modifyDNOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as a search request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the search request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processSearchRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
4134N/A if ((ldapVersion == 2) && (controls != null)
4134N/A && (!controls.isEmpty()))
1755N/A {
1755N/A // LDAPv2 clients aren't allowed to send controls.
1755N/A SearchResultDoneProtocolOp responseOp =
4134N/A new SearchResultDoneProtocolOp(LDAPResultCode.PROTOCOL_ERROR,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp));
1755N/A disconnect(DisconnectReason.PROTOCOL_ERROR, false,
4134N/A ERR_LDAPV2_CONTROLS_NOT_ALLOWED.get());
1755N/A return false;
1755N/A }
1755N/A
4134N/A SearchRequestProtocolOp protocolOp =
4134N/A message.getSearchRequestProtocolOp();
1689N/A SearchOperationBasis searchOp =
4134N/A new SearchOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls,
4134N/A protocolOp.getBaseDN(), protocolOp.getScope(), protocolOp
4134N/A .getDereferencePolicy(), protocolOp.getSizeLimit(),
4134N/A protocolOp.getTimeLimit(), protocolOp.getTypesOnly(),
4134N/A protocolOp.getFilter(), protocolOp.getAttributes());
0N/A
0N/A // Add the operation into the work queue.
0N/A try
0N/A {
0N/A addOperationInProgress(searchOp);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A SearchResultDoneProtocolOp responseOp =
4134N/A new SearchResultDoneProtocolOp(de.getResultCode()
4134N/A .getIntValue(), de.getMessageObject(), de.getMatchedDN(),
4134N/A de.getReferralURLs());
1886N/A
4134N/A sendLDAPMessage(new LDAPMessage(message.getMessageID(),
4134N/A responseOp, searchOp.getResponseControls()));
0N/A }
0N/A
0N/A return connectionValid;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
0N/A * Processes the provided LDAP message as an unbind request.
0N/A *
4134N/A * @param message
4134N/A * The LDAP message containing the unbind request to process.
4134N/A * @param controls
4134N/A * The set of pre-decoded request controls contained in the
4134N/A * message.
4134N/A * @return <CODE>true</CODE> if the request was processed
4134N/A * successfully, or <CODE>false</CODE> if not and the
4134N/A * connection has been closed as a result (it is the
4134N/A * responsibility of this method to close the connection).
0N/A */
0N/A private boolean processUnbindRequest(LDAPMessage message,
4134N/A List<Control> controls)
0N/A {
1859N/A UnbindOperationBasis unbindOp =
4134N/A new UnbindOperationBasis(this, nextOperationID
4134N/A .getAndIncrement(), message.getMessageID(), controls);
0N/A
0N/A unbindOp.run();
0N/A
0N/A // The client connection will never be valid after an unbind.
0N/A return false;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
629N/A * {@inheritDoc}
629N/A */
4134N/A @Override
629N/A public String getMonitorSummary()
629N/A {
629N/A StringBuilder buffer = new StringBuilder();
629N/A buffer.append("connID=\"");
629N/A buffer.append(connectionID);
629N/A buffer.append("\" connectTime=\"");
629N/A buffer.append(getConnectTimeString());
629N/A buffer.append("\" source=\"");
629N/A buffer.append(clientAddress);
629N/A buffer.append(":");
629N/A buffer.append(clientPort);
629N/A buffer.append("\" destination=\"");
629N/A buffer.append(serverAddress);
629N/A buffer.append(":");
629N/A buffer.append(connectionHandler.getListenPort());
629N/A buffer.append("\" ldapVersion=\"");
629N/A buffer.append(ldapVersion);
629N/A buffer.append("\" authDN=\"");
629N/A
629N/A DN authDN = getAuthenticationInfo().getAuthenticationDN();
629N/A if (authDN != null)
629N/A {
629N/A authDN.toString(buffer);
629N/A }
629N/A
629N/A buffer.append("\" security=\"");
4134N/A if (isSecure())
629N/A {
4134N/A buffer.append(activeProvider.getName());
629N/A }
629N/A else
629N/A {
629N/A buffer.append("none");
629N/A }
629N/A
629N/A buffer.append("\" opsInProgress=\"");
629N/A buffer.append(operationsInProgress.size());
629N/A buffer.append("\"");
629N/A
6023N/A int countPSearch = getPersistentSearches().size();
6023N/A if (countPSearch > 0)
6023N/A {
6024N/A buffer.append(" persistentSearches=\"");
6023N/A buffer.append(countPSearch);
6023N/A buffer.append("\"");
6023N/A }
629N/A return buffer.toString();
629N/A }
629N/A
629N/A
629N/A
629N/A /**
4134N/A * Appends a string representation of this client connection to the
4134N/A * provided buffer.
0N/A *
4134N/A * @param buffer
4134N/A * The buffer to which the information should be appended.
0N/A */
4134N/A @Override
0N/A public void toString(StringBuilder buffer)
0N/A {
0N/A buffer.append("LDAP client connection from ");
0N/A buffer.append(clientAddress);
0N/A buffer.append(":");
0N/A buffer.append(clientPort);
0N/A buffer.append(" to ");
0N/A buffer.append(serverAddress);
0N/A buffer.append(":");
0N/A buffer.append(serverPort);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
6129N/A * {@inheritDoc}
0N/A */
6134N/A @Override
6129N/A public boolean prepareTLS(MessageBuilder unavailableReason)
0N/A {
6129N/A if (isSecure() && "TLS".equals(activeProvider.getName()))
0N/A {
4134N/A unavailableReason.append(ERR_LDAP_TLS_EXISTING_SECURITY_PROVIDER
4134N/A .get(activeProvider.getName()));
0N/A return false;
0N/A }
4134N/A // Make sure that the connection handler allows the use of the
4134N/A // StartTLS operation.
4134N/A if (!connectionHandler.allowStartTLS())
0N/A {
2086N/A unavailableReason.append(ERR_LDAP_TLS_STARTTLS_NOT_ALLOWED.get());
0N/A return false;
0N/A }
4134N/A try
0N/A {
4134N/A TLSByteChannel tlsByteChannel =
6000N/A connectionHandler.getTLSByteChannel(timeoutClientChannel);
4134N/A setTLSPendingProvider(tlsByteChannel);
4134N/A }
4134N/A catch (DirectoryException de)
4134N/A {
4134N/A if (debugEnabled())
0N/A {
4134N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
0N/A }
4134N/A unavailableReason.append(ERR_LDAP_TLS_CANNOT_CREATE_TLS_PROVIDER
4134N/A .get(stackTraceToSingleLineString(de)));
4134N/A return false;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
1963N/A * Retrieves the length of time in milliseconds that this client
4134N/A * connection has been idle. <BR>
4134N/A * <BR>
1963N/A * Note that the default implementation will always return zero.
4134N/A * Subclasses associated with connection handlers should override this
4134N/A * method if they wish to provided idle time limit functionality.
1963N/A *
4134N/A * @return The length of time in milliseconds that this client
4134N/A * connection has been idle.
1963N/A */
3853N/A @Override
1963N/A public long getIdleTime()
1963N/A {
4134N/A if (operationsInProgress.isEmpty()
4134N/A && getPersistentSearches().isEmpty())
1963N/A {
1963N/A return (TimeThread.getTime() - lastCompletionTime.get());
1963N/A }
1963N/A else
1963N/A {
1963N/A // There's at least one operation in progress, so it's not idle.
1963N/A return 0L;
1963N/A }
1963N/A }
3999N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Set the connection provider that is not in use yet. Used in TLS
4134N/A * negotiation when a clear response is needed before the connection
4134N/A * provider is active.
4134N/A *
4134N/A * @param provider
4134N/A * The provider that needs to be activated.
4134N/A */
4134N/A public void setTLSPendingProvider(ConnectionSecurityProvider provider)
4134N/A {
4134N/A tlsPendingProvider = provider;
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Set the connection provider that is not in use. Used in SASL
4134N/A * negotiation when a clear response is needed before the connection
4134N/A * provider is active.
4134N/A *
4134N/A * @param provider
4134N/A * The provider that needs to be activated.
4134N/A */
4134N/A public void setSASLPendingProvider(ConnectionSecurityProvider provider)
4134N/A {
4134N/A saslPendingProvider = provider;
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Enable the provider that is inactive.
4134N/A */
5897N/A private void enableTLS()
4134N/A {
4134N/A activeProvider = tlsPendingProvider;
4134N/A tlsChannel.redirect(tlsPendingProvider);
4134N/A tlsPendingProvider = null;
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Set the security provider to the specified provider.
4134N/A *
4134N/A * @param sslProvider
4134N/A * The provider to set the security provider to.
4134N/A */
5897N/A private void enableSSL(ConnectionSecurityProvider sslProvider)
4134N/A {
4134N/A activeProvider = sslProvider;
4134N/A tlsChannel.redirect(sslProvider);
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Enable the SASL provider that is currently inactive or pending.
4134N/A */
5897N/A private void enableSASL()
4134N/A {
4134N/A activeProvider = saslPendingProvider;
4134N/A saslChannel.redirect(saslPendingProvider);
4134N/A saslPendingProvider = null;
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4134N/A * Return the certificate chain array associated with a connection.
4134N/A *
4134N/A * @return The array of certificates associated with a connection.
4134N/A */
4134N/A public Certificate[] getClientCertificateChain()
4134N/A {
4134N/A if (activeProvider != null)
4134N/A {
4134N/A return activeProvider.getClientCertificateChain();
4134N/A }
4134N/A else
4134N/A return new Certificate[0];
4134N/A }
4134N/A
4134N/A
4134N/A
4134N/A /**
4170N/A * Retrieves the TLS redirecting byte channel used in a LDAP client
4170N/A * connection.
4170N/A *
4170N/A * @return The TLS redirecting byte channel.
4170N/A */
4170N/A @Override
5734N/A public ByteChannel getChannel() {
4170N/A return this.tlsChannel;
4170N/A }
4170N/A
4170N/A
4170N/A
4170N/A /**
4134N/A * {@inheritDoc}
4134N/A */
4134N/A @Override
4134N/A public int getSSF()
4134N/A {
4134N/A if (activeProvider != null)
4134N/A return activeProvider.getSSF();
4134N/A else
4134N/A return 0;
4134N/A }
4134N/A
4134N/A
4134N/A
4170N/A /**
4426N/A * {@inheritDoc}
4426N/A */
4469N/A @Override
4426N/A public void finishBindOrStartTLS()
4426N/A {
4426N/A if(this.tlsPendingProvider != null)
4426N/A {
4426N/A enableTLS();
4426N/A }
4426N/A
4426N/A if (this.saslPendingProvider != null)
4426N/A {
4426N/A enableSASL();
4426N/A }
4426N/A
4426N/A super.finishBindOrStartTLS();
4426N/A }
0N/A}