001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.protocols.ldap;
028
029import static org.opends.messages.ProtocolMessages.*;
030import static org.opends.server.util.ServerConstants.*;
031import static org.opends.server.util.StaticUtils.*;
032
033import java.io.IOException;
034import java.net.InetAddress;
035import java.net.InetSocketAddress;
036import java.net.SocketException;
037import java.nio.channels.*;
038import java.util.*;
039import java.util.concurrent.Executors;
040import java.util.concurrent.ScheduledExecutorService;
041import java.util.concurrent.TimeUnit;
042
043import javax.net.ssl.KeyManager;
044import javax.net.ssl.SSLContext;
045import javax.net.ssl.SSLEngine;
046
047import org.forgerock.i18n.LocalizableMessage;
048import org.forgerock.i18n.slf4j.LocalizedLogger;
049import org.forgerock.opendj.config.server.ConfigChangeResult;
050import org.forgerock.opendj.config.server.ConfigException;
051import org.forgerock.opendj.ldap.AddressMask;
052import org.forgerock.opendj.ldap.ResultCode;
053import org.opends.server.admin.server.ConfigurationChangeListener;
054import org.opends.server.admin.std.server.ConnectionHandlerCfg;
055import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
056import org.opends.server.api.*;
057import org.opends.server.api.plugin.PluginResult;
058import org.opends.server.core.DirectoryServer;
059import org.opends.server.core.PluginConfigManager;
060import org.opends.server.core.QueueingStrategy;
061import org.opends.server.core.WorkQueueStrategy;
062import org.opends.server.extensions.NullKeyManagerProvider;
063import org.opends.server.extensions.NullTrustManagerProvider;
064import org.opends.server.extensions.TLSByteChannel;
065import org.opends.server.monitors.ClientConnectionMonitorProvider;
066import org.opends.server.types.*;
067import org.opends.server.util.SelectableCertificateKeyManager;
068import org.opends.server.util.StaticUtils;
069
070/**
071 * This class defines a connection handler that will be used for communicating
072 * with clients over LDAP. It is actually implemented in two parts: as a
073 * connection handler and one or more request handlers. The connection handler
074 * is responsible for accepting new connections and registering each of them
075 * with a request handler. The request handlers then are responsible for reading
076 * requests from the clients and parsing them as operations. A single request
077 * handler may be used, but having multiple handlers might provide better
078 * performance in a multi-CPU system.
079 */
080public final class LDAPConnectionHandler extends
081    ConnectionHandler<LDAPConnectionHandlerCfg> implements
082    ConfigurationChangeListener<LDAPConnectionHandlerCfg>,
083    ServerShutdownListener, AlertGenerator
084{
085
086  /**
087   * Task run periodically by the connection finalizer.
088   */
089  private final class ConnectionFinalizerRunnable implements Runnable
090  {
091    @Override
092    public void run()
093    {
094      if (!connectionFinalizerActiveJobQueue.isEmpty())
095      {
096        for (Runnable r : connectionFinalizerActiveJobQueue)
097        {
098          r.run();
099        }
100        connectionFinalizerActiveJobQueue.clear();
101      }
102
103      // Switch the lists.
104      synchronized (connectionFinalizerLock)
105      {
106        List<Runnable> tmp = connectionFinalizerActiveJobQueue;
107        connectionFinalizerActiveJobQueue = connectionFinalizerPendingJobQueue;
108        connectionFinalizerPendingJobQueue = tmp;
109      }
110
111    }
112  }
113  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
114
115  /**
116   * Default friendly name for the LDAP connection handler.
117   */
118  private static final String DEFAULT_FRIENDLY_NAME = "LDAP Connection Handler";
119
120  /** SSL instance name used in context creation. */
121  private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
122
123  /** The current configuration state. */
124  private LDAPConnectionHandlerCfg currentConfig;
125
126  /* Properties that cannot be modified dynamically */
127
128  /** The set of addresses on which to listen for new connections. */
129  private Set<InetAddress> listenAddresses;
130
131  /** The port on which this connection handler should listen for requests. */
132  private int listenPort;
133
134  /** The SSL client auth policy used by this connection handler. */
135  private SSLClientAuthPolicy sslClientAuthPolicy;
136
137  /** The backlog that will be used for the accept queue. */
138  private int backlog;
139
140  /** Indicates whether to allow the reuse address socket option. */
141  private boolean allowReuseAddress;
142
143  /**
144   * The number of request handlers that should be used for this connection
145   * handler.
146   */
147  private int numRequestHandlers;
148
149  /**
150   * Indicates whether the Directory Server is in the process of shutting down.
151   */
152  private volatile boolean shutdownRequested;
153
154  /* Internal LDAP connection handler state */
155
156  /** Indicates whether this connection handler is enabled. */
157  private boolean enabled;
158
159  /** The set of clients that are explicitly allowed access to the server. */
160  private Collection<AddressMask> allowedClients;
161
162  /**
163   * The set of clients that have been explicitly denied access to the server.
164   */
165  private Collection<AddressMask> deniedClients;
166
167  /**
168   * The index to the request handler that will be used for the next connection
169   * accepted by the server.
170   */
171  private int requestHandlerIndex;
172
173  /** The set of listeners for this connection handler. */
174  private List<HostPort> listeners;
175
176  /**
177   * The set of request handlers that are associated with this connection
178   * handler.
179   */
180  private LDAPRequestHandler[] requestHandlers;
181
182  /** The set of statistics collected for this connection handler. */
183  private LDAPStatistics statTracker;
184
185  /**
186   * The client connection monitor provider associated with this connection
187   * handler.
188   */
189  private ClientConnectionMonitorProvider connMonitor;
190
191  /**
192   * The selector that will be used to multiplex connection acceptance across
193   * multiple sockets by a single thread.
194   */
195  private Selector selector;
196
197  /** The unique name assigned to this connection handler. */
198  private String handlerName;
199
200  /** The protocol used by this connection handler. */
201  private String protocol;
202
203  /** Queueing strategy. */
204  private final QueueingStrategy queueingStrategy;
205
206  /**
207   * The condition variable that will be used by the start method to wait for
208   * the socket port to be opened and ready to process requests before
209   * returning.
210   */
211  private final Object waitListen = new Object();
212
213  /** The friendly name of this connection handler. */
214  private String friendlyName;
215
216  /**
217   * SSL context.
218   *
219   * @see LDAPConnectionHandler#sslEngine
220   */
221  private SSLContext sslContext;
222
223  /** The SSL engine is used for obtaining default SSL parameters. */
224  private SSLEngine sslEngine;
225
226  /**
227   * Connection finalizer thread.
228   * <p>
229   * This thread is defers closing clients for approximately 100ms. This gives
230   * the client a chance to close the connection themselves before the server
231   * thus avoiding leaving the server side in the TIME WAIT state.
232   */
233  private final Object connectionFinalizerLock = new Object();
234  private ScheduledExecutorService connectionFinalizer;
235  private List<Runnable> connectionFinalizerActiveJobQueue;
236  private List<Runnable> connectionFinalizerPendingJobQueue;
237
238
239
240  /**
241   * Creates a new instance of this LDAP connection handler. It must be
242   * initialized before it may be used.
243   */
244  public LDAPConnectionHandler()
245  {
246    this(new WorkQueueStrategy(), null); // Use name from configuration.
247  }
248
249
250
251  /**
252   * Creates a new instance of this LDAP connection handler, using a queueing
253   * strategy. It must be initialized before it may be used.
254   *
255   * @param strategy
256   *          Request handling strategy.
257   * @param friendlyName
258   *          The name of of this connection handler, or {@code null} if the
259   *          name should be taken from the configuration.
260   */
261  public LDAPConnectionHandler(QueueingStrategy strategy, String friendlyName)
262  {
263    super(friendlyName != null ? friendlyName : DEFAULT_FRIENDLY_NAME
264        + " Thread");
265
266    this.friendlyName = friendlyName;
267    this.queueingStrategy = strategy;
268
269    // No real implementation is required. Do all the work in the
270    // initializeConnectionHandler method.
271  }
272
273
274
275  /**
276   * Indicates whether this connection handler should allow interaction with
277   * LDAPv2 clients.
278   *
279   * @return <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE> if
280   *         not.
281   */
282  public boolean allowLDAPv2()
283  {
284    return currentConfig.isAllowLDAPV2();
285  }
286
287
288
289  /**
290   * Indicates whether this connection handler should allow the use of the
291   * StartTLS extended operation.
292   *
293   * @return <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE> if
294   *         not.
295   */
296  public boolean allowStartTLS()
297  {
298    return currentConfig.isAllowStartTLS() && !currentConfig.isUseSSL();
299  }
300
301
302
303  /** {@inheritDoc} */
304  @Override
305  public ConfigChangeResult applyConfigurationChange(
306      LDAPConnectionHandlerCfg config)
307  {
308    final ConfigChangeResult ccr = new ConfigChangeResult();
309
310    // Note that the following properties cannot be modified:
311    //
312    // * listen port and addresses
313    // * use ssl
314    // * ssl policy
315    // * ssl cert nickname
316    // * accept backlog
317    // * tcp reuse address
318    // * num request handler
319
320    // Clear the stat tracker if LDAPv2 is being enabled.
321    if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2()
322        && config.isAllowLDAPV2())
323    {
324      statTracker.clearStatistics();
325    }
326
327    // Apply the changes.
328    currentConfig = config;
329    enabled = config.isEnabled();
330    allowedClients = config.getAllowedClient();
331    deniedClients = config.getDeniedClient();
332
333    // Reconfigure SSL if needed.
334    try
335    {
336      configureSSL(config);
337    }
338    catch (DirectoryException e)
339    {
340      logger.traceException(e);
341      ccr.setResultCode(e.getResultCode());
342      ccr.addMessage(e.getMessageObject());
343      return ccr;
344    }
345
346    if (config.isAllowLDAPV2())
347    {
348      DirectoryServer.registerSupportedLDAPVersion(2, this);
349    }
350    else
351    {
352      DirectoryServer.deregisterSupportedLDAPVersion(2, this);
353    }
354
355    return ccr;
356  }
357
358  private void configureSSL(LDAPConnectionHandlerCfg config)
359      throws DirectoryException
360  {
361    protocol = config.isUseSSL() ? "LDAPS" : "LDAP";
362    if (config.isUseSSL() || config.isAllowStartTLS())
363    {
364      sslContext = createSSLContext(config);
365      sslEngine = createSSLEngine(config, sslContext);
366    }
367    else
368    {
369      sslContext = null;
370      sslEngine = null;
371    }
372  }
373
374
375  /** {@inheritDoc} */
376  @Override
377  public void finalizeConnectionHandler(LocalizableMessage finalizeReason)
378  {
379    shutdownRequested = true;
380    currentConfig.removeLDAPChangeListener(this);
381
382    if (connMonitor != null)
383    {
384      DirectoryServer.deregisterMonitorProvider(connMonitor);
385    }
386
387    if (statTracker != null)
388    {
389      DirectoryServer.deregisterMonitorProvider(statTracker);
390    }
391
392    DirectoryServer.deregisterSupportedLDAPVersion(2, this);
393    DirectoryServer.deregisterSupportedLDAPVersion(3, this);
394
395    try
396    {
397      selector.wakeup();
398    }
399    catch (Exception e)
400    {
401      logger.traceException(e);
402    }
403
404    for (LDAPRequestHandler requestHandler : requestHandlers)
405    {
406      requestHandler.processServerShutdown(finalizeReason);
407    }
408
409    // Shutdown the connection finalizer and ensure that any pending
410    // unclosed connections are closed.
411    synchronized (connectionFinalizerLock)
412    {
413      connectionFinalizer.shutdown();
414      connectionFinalizer = null;
415
416      Runnable r = new ConnectionFinalizerRunnable();
417      r.run(); // Flush active queue.
418      r.run(); // Flush pending queue.
419    }
420  }
421
422
423
424  /**
425   * Retrieves information about the set of alerts that this generator may
426   * produce. The map returned should be between the notification type for a
427   * particular notification and the human-readable description for that
428   * notification. This alert generator must not generate any alerts with types
429   * that are not contained in this list.
430   *
431   * @return Information about the set of alerts that this generator may
432   *         produce.
433   */
434  @Override
435  public Map<String, String> getAlerts()
436  {
437    Map<String, String> alerts = new LinkedHashMap<>();
438
439    alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
440        ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
441    alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR,
442        ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR);
443
444    return alerts;
445  }
446
447
448
449  /**
450   * Retrieves the fully-qualified name of the Java class for this alert
451   * generator implementation.
452   *
453   * @return The fully-qualified name of the Java class for this alert generator
454   *         implementation.
455   */
456  @Override
457  public String getClassName()
458  {
459    return LDAPConnectionHandler.class.getName();
460  }
461
462
463
464  /**
465   * Retrieves the set of active client connections that have been established
466   * through this connection handler.
467   *
468   * @return The set of active client connections that have been established
469   *         through this connection handler.
470   */
471  @Override
472  public Collection<ClientConnection> getClientConnections()
473  {
474    List<ClientConnection> connectionList = new LinkedList<>();
475    for (LDAPRequestHandler requestHandler : requestHandlers)
476    {
477      connectionList.addAll(requestHandler.getClientConnections());
478    }
479    return connectionList;
480  }
481
482
483
484  /**
485   * Retrieves the DN of the configuration entry with which this alert generator
486   * is associated.
487   *
488   * @return The DN of the configuration entry with which this alert generator
489   *         is associated.
490   */
491  @Override
492  public DN getComponentEntryDN()
493  {
494    return currentConfig.dn();
495  }
496
497
498
499  /** {@inheritDoc} */
500  @Override
501  public String getConnectionHandlerName()
502  {
503    return handlerName;
504  }
505
506
507
508  /** {@inheritDoc} */
509  @Override
510  public Collection<String> getEnabledSSLCipherSuites()
511  {
512    final SSLEngine engine = sslEngine;
513    if (engine != null)
514    {
515      return Arrays.asList(engine.getEnabledCipherSuites());
516    }
517    return super.getEnabledSSLCipherSuites();
518  }
519
520
521
522  /** {@inheritDoc} */
523  @Override
524  public Collection<String> getEnabledSSLProtocols()
525  {
526    final SSLEngine engine = sslEngine;
527    if (engine != null)
528    {
529      return Arrays.asList(engine.getEnabledProtocols());
530    }
531    return super.getEnabledSSLProtocols();
532  }
533
534
535
536  /** {@inheritDoc} */
537  @Override
538  public Collection<HostPort> getListeners()
539  {
540    return listeners;
541  }
542
543
544
545  /**
546   * Retrieves the port on which this connection handler is listening for client
547   * connections.
548   *
549   * @return The port on which this connection handler is listening for client
550   *         connections.
551   */
552  public int getListenPort()
553  {
554    return listenPort;
555  }
556
557
558
559  /**
560   * Retrieves the maximum length of time in milliseconds that attempts to write
561   * to LDAP client connections should be allowed to block.
562   *
563   * @return The maximum length of time in milliseconds that attempts to write
564   *         to LDAP client connections should be allowed to block, or zero if
565   *         there should not be any limit imposed.
566   */
567  public long getMaxBlockedWriteTimeLimit()
568  {
569    return currentConfig.getMaxBlockedWriteTimeLimit();
570  }
571
572
573
574  /**
575   * Retrieves the maximum ASN.1 element value length that will be allowed by
576   * this connection handler.
577   *
578   * @return The maximum ASN.1 element value length that will be allowed by this
579   *         connection handler.
580   */
581  public int getMaxRequestSize()
582  {
583    return (int) currentConfig.getMaxRequestSize();
584  }
585
586
587
588  /**
589   * Retrieves the size in bytes of the LDAP response message write buffer
590   * defined for this connection handler.
591   *
592   * @return The size in bytes of the LDAP response message write buffer.
593   */
594  public int getBufferSize()
595  {
596    return (int) currentConfig.getBufferSize();
597  }
598
599
600
601  /** {@inheritDoc} */
602  @Override
603  public String getProtocol()
604  {
605    return protocol;
606  }
607
608
609
610  /** {@inheritDoc} */
611  @Override
612  public String getShutdownListenerName()
613  {
614    return handlerName;
615  }
616
617
618
619  /**
620   * Retrieves the SSL client authentication policy for this connection handler.
621   *
622   * @return The SSL client authentication policy for this connection handler.
623   */
624  public SSLClientAuthPolicy getSSLClientAuthPolicy()
625  {
626    return sslClientAuthPolicy;
627  }
628
629
630
631  /**
632   * Retrieves the set of statistics maintained by this connection handler.
633   *
634   * @return The set of statistics maintained by this connection handler.
635   */
636  public LDAPStatistics getStatTracker()
637  {
638    return statTracker;
639  }
640
641
642
643  /** {@inheritDoc} */
644  @Override
645  public void initializeConnectionHandler(LDAPConnectionHandlerCfg config)
646      throws ConfigException, InitializationException
647  {
648    if (friendlyName == null)
649    {
650      friendlyName = config.dn().rdn().getAttributeValue(0).toString();
651    }
652
653    // Open the selector.
654    try
655    {
656      selector = Selector.open();
657    }
658    catch (Exception e)
659    {
660      logger.traceException(e);
661
662      LocalizableMessage message = ERR_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED.get(
663          config.dn(), stackTraceToSingleLineString(e));
664      throw new InitializationException(message, e);
665    }
666
667    // Save this configuration for future reference.
668    currentConfig = config;
669    enabled = config.isEnabled();
670    requestHandlerIndex = 0;
671    allowedClients = config.getAllowedClient();
672    deniedClients = config.getDeniedClient();
673
674    // Configure SSL if needed.
675    try
676    {
677      // This call may disable the connector if wrong SSL settings
678      configureSSL(config);
679    }
680    catch (DirectoryException e)
681    {
682      logger.traceException(e);
683      throw new InitializationException(e.getMessageObject());
684    }
685
686    // Save properties that cannot be dynamically modified.
687    allowReuseAddress = config.isAllowTCPReuseAddress();
688    backlog = config.getAcceptBacklog();
689    listenAddresses = config.getListenAddress();
690    listenPort = config.getListenPort();
691    numRequestHandlers =
692        getNumRequestHandlers(config.getNumRequestHandlers(), friendlyName);
693
694    // Construct a unique name for this connection handler, and put
695    // together the set of listeners.
696    listeners = new LinkedList<>();
697    StringBuilder nameBuffer = new StringBuilder();
698    nameBuffer.append(friendlyName);
699    for (InetAddress a : listenAddresses)
700    {
701      listeners.add(new HostPort(a.getHostAddress(), listenPort));
702      nameBuffer.append(" ");
703      nameBuffer.append(a.getHostAddress());
704    }
705    nameBuffer.append(" port ");
706    nameBuffer.append(listenPort);
707    handlerName = nameBuffer.toString();
708
709    // Attempt to bind to the listen port on all configured addresses to
710    // verify whether the connection handler will be able to start.
711    LocalizableMessage errorMessage =
712        checkAnyListenAddressInUse(listenAddresses, listenPort,
713            allowReuseAddress, config.dn());
714    if (errorMessage != null)
715    {
716      logger.error(errorMessage);
717      throw new InitializationException(errorMessage);
718    }
719
720    // Create a system property to store the LDAP(S) port the server is
721    // listening to. This information can be displayed with jinfo.
722    System.setProperty(protocol + "_port", String.valueOf(listenPort));
723
724    // Create and start a connection finalizer thread for this
725    // connection handler.
726    connectionFinalizer = Executors
727        .newSingleThreadScheduledExecutor(new DirectoryThread.Factory(
728            "LDAP Connection Finalizer for connection handler " + toString()));
729
730    connectionFinalizerActiveJobQueue = new ArrayList<>();
731    connectionFinalizerPendingJobQueue = new ArrayList<>();
732
733    connectionFinalizer.scheduleWithFixedDelay(
734        new ConnectionFinalizerRunnable(), 100, 100, TimeUnit.MILLISECONDS);
735
736    // Create and start the request handlers.
737    requestHandlers = new LDAPRequestHandler[numRequestHandlers];
738    for (int i = 0; i < numRequestHandlers; i++)
739    {
740      requestHandlers[i] = new LDAPRequestHandler(this, i);
741    }
742
743    for (int i = 0; i < numRequestHandlers; i++)
744    {
745      requestHandlers[i].start();
746    }
747
748    // Register the set of supported LDAP versions.
749    DirectoryServer.registerSupportedLDAPVersion(3, this);
750    if (config.isAllowLDAPV2())
751    {
752      DirectoryServer.registerSupportedLDAPVersion(2, this);
753    }
754
755    // Create and register monitors.
756    statTracker = new LDAPStatistics(handlerName + " Statistics");
757    DirectoryServer.registerMonitorProvider(statTracker);
758
759    connMonitor = new ClientConnectionMonitorProvider(this);
760    DirectoryServer.registerMonitorProvider(connMonitor);
761
762    // Register this as a change listener.
763    config.addLDAPChangeListener(this);
764  }
765
766
767
768  /** {@inheritDoc} */
769  @Override
770  public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration,
771      List<LocalizableMessage> unacceptableReasons)
772  {
773    LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
774
775    if (currentConfig == null
776        || (!currentConfig.isEnabled() && config.isEnabled()))
777    {
778      // Attempt to bind to the listen port on all configured addresses to
779      // verify whether the connection handler will be able to start.
780      LocalizableMessage errorMessage =
781          checkAnyListenAddressInUse(config.getListenAddress(), config
782              .getListenPort(), config.isAllowTCPReuseAddress(), config.dn());
783      if (errorMessage != null)
784      {
785        unacceptableReasons.add(errorMessage);
786        return false;
787      }
788    }
789
790    if (config.isEnabled()
791        // Check that the SSL configuration is valid.
792        && (config.isUseSSL() || config.isAllowStartTLS()))
793    {
794      try
795      {
796        createSSLEngine(config, createSSLContext(config));
797      }
798      catch (DirectoryException e)
799      {
800        logger.traceException(e);
801
802        unacceptableReasons.add(e.getMessageObject());
803        return false;
804      }
805    }
806
807    return true;
808  }
809
810  /**
811   * Checks whether any listen address is in use for the given port. The check
812   * is performed by binding to each address and port.
813   *
814   * @param listenAddresses
815   *          the listen {@link InetAddress} to test
816   * @param listenPort
817   *          the listen port to test
818   * @param allowReuseAddress
819   *          whether addresses can be reused
820   * @param configEntryDN
821   *          the configuration entry DN
822   * @return an error message if at least one of the address is already in use,
823   *         null otherwise.
824   */
825  private LocalizableMessage checkAnyListenAddressInUse(
826      Collection<InetAddress> listenAddresses, int listenPort,
827      boolean allowReuseAddress, DN configEntryDN)
828  {
829    for (InetAddress a : listenAddresses)
830    {
831      try
832      {
833        if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress))
834        {
835          throw new IOException(ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
836        }
837      }
838      catch (IOException e)
839      {
840        logger.traceException(e);
841        return ERR_CONNHANDLER_CANNOT_BIND.get("LDAP", configEntryDN, a.getHostAddress(), listenPort,
842            getExceptionMessage(e));
843      }
844    }
845    return null;
846  }
847
848
849  /** {@inheritDoc} */
850  @Override
851  public boolean isConfigurationChangeAcceptable(
852      LDAPConnectionHandlerCfg config, List<LocalizableMessage> unacceptableReasons)
853  {
854    return isConfigurationAcceptable(config, unacceptableReasons);
855  }
856
857
858
859  /**
860   * Indicates whether this connection handler should maintain usage statistics.
861   *
862   * @return <CODE>true</CODE> if this connection handler should maintain usage
863   *         statistics, or <CODE>false</CODE> if not.
864   */
865  public boolean keepStats()
866  {
867    return currentConfig.isKeepStats();
868  }
869
870
871
872  /** {@inheritDoc} */
873  @Override
874  public void processServerShutdown(LocalizableMessage reason)
875  {
876    shutdownRequested = true;
877
878    try
879    {
880      for (LDAPRequestHandler requestHandler : requestHandlers)
881      {
882        try
883        {
884          requestHandler.processServerShutdown(reason);
885        }
886        catch (Exception ignored)
887        {
888        }
889      }
890    }
891    catch (Exception ignored)
892    {
893    }
894  }
895
896
897
898  /** {@inheritDoc} */
899  @Override
900  public void start()
901  {
902    // The Directory Server start process should only return
903    // when the connection handlers port are fully opened
904    // and working. The start method therefore needs to wait for
905    // the created thread to
906    synchronized (waitListen)
907    {
908      super.start();
909
910      try
911      {
912        waitListen.wait();
913      }
914      catch (InterruptedException e)
915      {
916        // If something interrupted the start its probably better
917        // to return ASAP.
918      }
919    }
920  }
921
922
923
924  /**
925   * Operates in a loop, accepting new connections and ensuring that requests on
926   * those connections are handled properly.
927   */
928  @Override
929  public void run()
930  {
931    setName(handlerName);
932    boolean listening = false;
933    boolean starting = true;
934
935    while (!shutdownRequested)
936    {
937      // If this connection handler is not enabled, then just sleep
938      // for a bit and check again.
939      if (!enabled)
940      {
941        if (listening)
942        {
943          cleanUpSelector();
944          listening = false;
945
946          logger.info(NOTE_CONNHANDLER_STOPPED_LISTENING, handlerName);
947        }
948
949        if (starting)
950        {
951          // This may happen if there was an initialisation error
952          // which led to disable the connector.
953          // The main thread is waiting for the connector to listen
954          // on its port, which will not occur yet,
955          // so notify here to allow the server startup to complete.
956          synchronized (waitListen)
957          {
958            starting = false;
959            waitListen.notify();
960          }
961        }
962
963        StaticUtils.sleep(1000);
964        continue;
965      }
966
967      // If we have gotten here, then we are about to start listening
968      // for the first time since startup or since we were previously
969      // disabled. Make sure to start with a clean selector and then
970      // create all the listeners.
971      try
972      {
973        cleanUpSelector();
974
975        int numRegistered = registerChannels();
976
977        // At this point, the connection Handler either started
978        // correctly or failed to start but the start process
979        // should be notified and resume its work in any cases.
980        synchronized (waitListen)
981        {
982          waitListen.notify();
983        }
984
985        // If none of the listeners were created successfully, then
986        // consider the connection handler disabled and require
987        // administrative action before trying again.
988        if (numRegistered == 0)
989        {
990          logger.error(ERR_LDAP_CONNHANDLER_NO_ACCEPTORS, currentConfig.dn());
991
992          enabled = false;
993          continue;
994        }
995
996        listening = true;
997
998        // Enter a loop, waiting for new connections to arrive and
999        // then accepting them as they come in.
1000        boolean lastIterationFailed = false;
1001        while (enabled && !shutdownRequested)
1002        {
1003          try
1004          {
1005            serveIncomingConnections();
1006
1007            lastIterationFailed = false;
1008          }
1009          catch (Exception e)
1010          {
1011            logger.traceException(e);
1012            logger.error(ERR_CONNHANDLER_CANNOT_ACCEPT_CONNECTION, friendlyName,
1013                currentConfig.dn(), getExceptionMessage(e));
1014
1015            if (lastIterationFailed)
1016            {
1017              // The last time through the accept loop we also
1018              // encountered a failure. Rather than enter a potential
1019              // infinite loop of failures, disable this acceptor and
1020              // log an error.
1021              LocalizableMessage message =
1022                  ERR_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.get(friendlyName,
1023                      currentConfig.dn(), stackTraceToSingleLineString(e));
1024              logger.error(message);
1025
1026              DirectoryServer.sendAlertNotification(this,
1027                  ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
1028                  message);
1029
1030              cleanUpSelector();
1031              enabled = false;
1032            }
1033            else
1034            {
1035              lastIterationFailed = true;
1036            }
1037          }
1038        }
1039
1040        if (shutdownRequested)
1041        {
1042          cleanUpSelector();
1043          selector.close();
1044          listening = false;
1045          enabled = false;
1046        }
1047
1048      }
1049      catch (Exception e)
1050      {
1051        logger.traceException(e);
1052
1053        // This is very bad because we failed outside the loop. The
1054        // only thing we can do here is log a message, send an alert,
1055        // and disable the selector until an administrator can figure
1056        // out what's going on.
1057        LocalizableMessage message =
1058            ERR_LDAP_CONNHANDLER_UNCAUGHT_ERROR.get(currentConfig.dn(), stackTraceToSingleLineString(e));
1059        logger.error(message);
1060
1061        DirectoryServer.sendAlertNotification(this,
1062            ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR, message);
1063
1064        cleanUpSelector();
1065        enabled = false;
1066      }
1067    }
1068  }
1069
1070
1071  /**
1072   * Serves the incoming connections.
1073   *
1074   * @throws IOException
1075   * @throws DirectoryException
1076   */
1077  private void serveIncomingConnections() throws IOException, DirectoryException
1078  {
1079    int selectorState = selector.select();
1080
1081    // We can't rely on return value of select to determine if any keys
1082    // are ready.
1083    // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373
1084    for (Iterator<SelectionKey> iterator =
1085        selector.selectedKeys().iterator(); iterator.hasNext();)
1086    {
1087      SelectionKey key = iterator.next();
1088      iterator.remove();
1089      if (key.isAcceptable())
1090      {
1091        // Accept the new client connection.
1092        ServerSocketChannel serverChannel = (ServerSocketChannel) key
1093            .channel();
1094        SocketChannel clientChannel = serverChannel.accept();
1095        if (clientChannel != null)
1096        {
1097          acceptConnection(clientChannel);
1098        }
1099      }
1100
1101      if (selectorState == 0 && enabled && !shutdownRequested
1102          && logger.isTraceEnabled())
1103      {
1104        // Selected keys was non empty but select() returned 0.
1105        // Log warning and hope it blocks on the next select() call.
1106        logger.trace("Selector.select() returned 0. "
1107            + "Selected Keys: %d, Interest Ops: %d, Ready Ops: %d ",
1108            selector.selectedKeys().size(), key.interestOps(),
1109            key.readyOps());
1110      }
1111    }
1112  }
1113
1114
1115  /**
1116   * Open channels for each listen address and register them against this
1117   * ConnectionHandler's {@link Selector}.
1118   *
1119   * @return the number of successfully registered channel
1120   */
1121  private int registerChannels()
1122  {
1123    int numRegistered = 0;
1124    for (InetAddress a : listenAddresses)
1125    {
1126      try
1127      {
1128        ServerSocketChannel channel = ServerSocketChannel.open();
1129        channel.socket().setReuseAddress(allowReuseAddress);
1130        channel.socket()
1131            .bind(new InetSocketAddress(a, listenPort), backlog);
1132        channel.configureBlocking(false);
1133        channel.register(selector, SelectionKey.OP_ACCEPT);
1134        numRegistered++;
1135
1136        logger.info(NOTE_CONNHANDLER_STARTED_LISTENING, handlerName);
1137      }
1138      catch (Exception e)
1139      {
1140        logger.traceException(e);
1141
1142        logger.error(ERR_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED, currentConfig.dn(), a.getHostAddress(), listenPort,
1143            stackTraceToSingleLineString(e));
1144      }
1145    }
1146    return numRegistered;
1147  }
1148
1149
1150
1151  private void acceptConnection(SocketChannel clientChannel)
1152      throws DirectoryException
1153  {
1154    try
1155    {
1156      clientChannel.socket().setKeepAlive(currentConfig.isUseTCPKeepAlive());
1157      clientChannel.socket().setTcpNoDelay(currentConfig.isUseTCPNoDelay());
1158    }
1159    catch (SocketException se)
1160    {
1161      // TCP error occurred because connection reset/closed? In any case,
1162      // just close it and ignore.
1163      // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6378870
1164      close(clientChannel);
1165    }
1166
1167    // Check to see if the core server rejected the
1168    // connection (e.g., already too many connections
1169    // established).
1170    LDAPClientConnection clientConnection = new LDAPClientConnection(this,
1171        clientChannel, getProtocol());
1172    if (clientConnection.getConnectionID() < 0)
1173    {
1174      clientConnection.disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true,
1175          ERR_CONNHANDLER_REJECTED_BY_SERVER.get());
1176      return;
1177    }
1178
1179    InetAddress clientAddr = clientConnection.getRemoteAddress();
1180    // Check to see if the client is on the denied list.
1181    // If so, then reject it immediately.
1182    if (!deniedClients.isEmpty()
1183        && AddressMask.matchesAny(deniedClients, clientAddr))
1184    {
1185      clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED,
1186          currentConfig.isSendRejectionNotice(), ERR_CONNHANDLER_DENIED_CLIENT
1187              .get(clientConnection.getClientHostPort(), clientConnection
1188                  .getServerHostPort()));
1189      return;
1190    }
1191    // Check to see if there is an allowed list and if
1192    // there is whether the client is on that list. If
1193    // not, then reject the connection.
1194    if (!allowedClients.isEmpty()
1195        && !AddressMask.matchesAny(allowedClients, clientAddr))
1196    {
1197      clientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED,
1198          currentConfig.isSendRejectionNotice(),
1199          ERR_CONNHANDLER_DISALLOWED_CLIENT.get(clientConnection
1200              .getClientHostPort(), clientConnection.getServerHostPort()));
1201      return;
1202    }
1203
1204    // If we've gotten here, then we'll take the
1205    // connection so invoke the post-connect plugins and
1206    // register the client connection with a request
1207    // handler.
1208    try
1209    {
1210      PluginConfigManager pluginManager = DirectoryServer
1211          .getPluginConfigManager();
1212      PluginResult.PostConnect pluginResult = pluginManager
1213          .invokePostConnectPlugins(clientConnection);
1214      if (!pluginResult.continueProcessing())
1215      {
1216        clientConnection.disconnect(pluginResult.getDisconnectReason(),
1217            pluginResult.sendDisconnectNotification(),
1218            pluginResult.getErrorMessage());
1219        return;
1220      }
1221
1222      LDAPRequestHandler requestHandler =
1223          requestHandlers[requestHandlerIndex++];
1224      if (requestHandlerIndex >= numRequestHandlers)
1225      {
1226        requestHandlerIndex = 0;
1227      }
1228      requestHandler.registerClient(clientConnection);
1229    }
1230    catch (Exception e)
1231    {
1232      logger.traceException(e);
1233
1234      LocalizableMessage message =
1235          INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(clientConnection
1236              .getClientHostPort(), clientConnection.getServerHostPort(),
1237              getExceptionMessage(e));
1238      logger.debug(message);
1239
1240      clientConnection.disconnect(DisconnectReason.SERVER_ERROR,
1241          currentConfig.isSendRejectionNotice(), message);
1242    }
1243  }
1244
1245
1246
1247  /**
1248   * Appends a string representation of this connection handler to the provided
1249   * buffer.
1250   *
1251   * @param buffer
1252   *          The buffer to which the information should be appended.
1253   */
1254  @Override
1255  public void toString(StringBuilder buffer)
1256  {
1257    buffer.append(handlerName);
1258  }
1259
1260
1261
1262  /**
1263   * Indicates whether this connection handler should use SSL to communicate
1264   * with clients.
1265   *
1266   * @return {@code true} if this connection handler should use SSL to
1267   *         communicate with clients, or {@code false} if not.
1268   */
1269  public boolean useSSL()
1270  {
1271    return currentConfig.isUseSSL();
1272  }
1273
1274
1275
1276  /**
1277   * Cleans up the contents of the selector, closing any server socket channels
1278   * that might be associated with it. Any connections that might have been
1279   * established through those channels should not be impacted.
1280   */
1281  private void cleanUpSelector()
1282  {
1283    try
1284    {
1285      for (SelectionKey key : selector.keys())
1286      {
1287        try
1288        {
1289          key.cancel();
1290        }
1291        catch (Exception e)
1292        {
1293          logger.traceException(e);
1294        }
1295
1296        try
1297        {
1298          key.channel().close();
1299        }
1300        catch (Exception e)
1301        {
1302          logger.traceException(e);
1303        }
1304      }
1305    }
1306    catch (Exception e)
1307    {
1308      logger.traceException(e);
1309    }
1310  }
1311
1312
1313
1314  /**
1315   * Get the queueing strategy.
1316   *
1317   * @return The queueing strategy.
1318   */
1319  public QueueingStrategy getQueueingStrategy()
1320  {
1321    return queueingStrategy;
1322  }
1323
1324
1325
1326  /**
1327   * Creates a TLS Byte Channel instance using the specified socket channel.
1328   *
1329   * @param channel
1330   *          The socket channel to use in the creation.
1331   * @return A TLS Byte Channel instance.
1332   * @throws DirectoryException
1333   *           If the channel cannot be created.
1334   */
1335  public TLSByteChannel getTLSByteChannel(ByteChannel channel)
1336      throws DirectoryException
1337  {
1338    SSLEngine sslEngine = createSSLEngine(currentConfig, sslContext);
1339    return new TLSByteChannel(channel, sslEngine);
1340  }
1341
1342
1343
1344  private SSLEngine createSSLEngine(LDAPConnectionHandlerCfg config,
1345      SSLContext sslContext) throws DirectoryException
1346  {
1347    try
1348    {
1349      SSLEngine sslEngine = sslContext.createSSLEngine();
1350      sslEngine.setUseClientMode(false);
1351
1352      final Set<String> protocols = config.getSSLProtocol();
1353      if (!protocols.isEmpty())
1354      {
1355        sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
1356      }
1357
1358      final Set<String> ciphers = config.getSSLCipherSuite();
1359      if (!ciphers.isEmpty())
1360      {
1361        sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
1362      }
1363
1364      switch (config.getSSLClientAuthPolicy())
1365      {
1366      case DISABLED:
1367        sslEngine.setNeedClientAuth(false);
1368        sslEngine.setWantClientAuth(false);
1369        break;
1370      case REQUIRED:
1371        sslEngine.setWantClientAuth(true);
1372        sslEngine.setNeedClientAuth(true);
1373        break;
1374      case OPTIONAL:
1375      default:
1376        sslEngine.setNeedClientAuth(false);
1377        sslEngine.setWantClientAuth(true);
1378        break;
1379      }
1380
1381      return sslEngine;
1382    }
1383    catch (Exception e)
1384    {
1385      logger.traceException(e);
1386      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
1387      LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
1388          .get(getExceptionMessage(e));
1389      throw new DirectoryException(resCode, message, e);
1390    }
1391  }
1392
1393
1394
1395  private void disableAndWarnIfUseSSL(LDAPConnectionHandlerCfg config)
1396  {
1397    if (config.isUseSSL())
1398    {
1399      logger.warn(INFO_DISABLE_CONNECTION, friendlyName);
1400      enabled = false;
1401    }
1402  }
1403
1404  private SSLContext createSSLContext(LDAPConnectionHandlerCfg config)
1405      throws DirectoryException
1406  {
1407    try
1408    {
1409      DN keyMgrDN = config.getKeyManagerProviderDN();
1410      KeyManagerProvider<?> keyManagerProvider = DirectoryServer
1411          .getKeyManagerProvider(keyMgrDN);
1412      if (keyManagerProvider == null)
1413      {
1414        logger.error(ERR_NULL_KEY_PROVIDER_MANAGER, keyMgrDN, friendlyName);
1415        disableAndWarnIfUseSSL(config);
1416        keyManagerProvider = new NullKeyManagerProvider();
1417        // The SSL connection is unusable without a key manager provider
1418      }
1419      else if (! keyManagerProvider.containsAtLeastOneKey())
1420      {
1421        logger.error(ERR_INVALID_KEYSTORE, friendlyName);
1422        disableAndWarnIfUseSSL(config);
1423      }
1424
1425      String alias = config.getSSLCertNickname();
1426      KeyManager[] keyManagers;
1427      if (alias == null)
1428      {
1429        keyManagers = keyManagerProvider.getKeyManagers();
1430      }
1431      else
1432      {
1433        if (!keyManagerProvider.containsKeyWithAlias(alias))
1434        {
1435          logger.error(ERR_KEYSTORE_DOES_NOT_CONTAIN_ALIAS, alias, friendlyName);
1436          disableAndWarnIfUseSSL(config);
1437        }
1438        keyManagers = SelectableCertificateKeyManager.wrap(
1439            keyManagerProvider.getKeyManagers(), alias, friendlyName);
1440      }
1441
1442      DN trustMgrDN = config.getTrustManagerProviderDN();
1443      TrustManagerProvider<?> trustManagerProvider = DirectoryServer
1444          .getTrustManagerProvider(trustMgrDN);
1445      if (trustManagerProvider == null)
1446      {
1447        trustManagerProvider = new NullTrustManagerProvider();
1448      }
1449
1450      SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
1451      sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(),
1452          null);
1453      return sslContext;
1454    }
1455    catch (Exception e)
1456    {
1457      logger.traceException(e);
1458      ResultCode resCode = DirectoryServer.getServerErrorResultCode();
1459      LocalizableMessage message = ERR_CONNHANDLER_SSL_CANNOT_INITIALIZE
1460          .get(getExceptionMessage(e));
1461      throw new DirectoryException(resCode, message, e);
1462    }
1463  }
1464
1465
1466
1467  /**
1468   * Enqueue a connection finalizer which will be invoked after a short delay.
1469   *
1470   * @param r
1471   *          The connection finalizer runnable.
1472   */
1473  void registerConnectionFinalizer(Runnable r)
1474  {
1475    synchronized (connectionFinalizerLock)
1476    {
1477      if (connectionFinalizer != null)
1478      {
1479        connectionFinalizerPendingJobQueue.add(r);
1480      }
1481      else
1482      {
1483        // Already finalized - invoked immediately.
1484        r.run();
1485      }
1486    }
1487  }
1488
1489}