001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.protocols.ldap;
028    import org.opends.messages.Message;
029    
030    
031    
032    import static org.opends.server.loggers.AccessLogger.logConnect;
033    import static org.opends.server.loggers.ErrorLogger.logError;
034    import static org.opends.server.loggers.debug.DebugLogger.*;
035    import org.opends.server.loggers.debug.DebugTracer;
036    import static org.opends.messages.ProtocolMessages.*;
037    
038    import static org.opends.server.util.ServerConstants.*;
039    import static org.opends.server.util.StaticUtils.*;
040    
041    import java.io.IOException;
042    import java.net.InetAddress;
043    import java.net.InetSocketAddress;
044    import java.nio.channels.SelectionKey;
045    import java.nio.channels.Selector;
046    import java.nio.channels.ServerSocketChannel;
047    import java.nio.channels.SocketChannel;
048    import java.util.ArrayList;
049    import java.util.Collection;
050    import java.util.Iterator;
051    import java.util.LinkedHashMap;
052    import java.util.LinkedList;
053    import java.util.List;
054    import java.util.Set;
055    
056    import org.opends.server.admin.server.ConfigurationChangeListener;
057    import org.opends.server.admin.std.server.ConnectionHandlerCfg;
058    import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
059    import org.opends.server.api.AlertGenerator;
060    import org.opends.server.api.ClientConnection;
061    import org.opends.server.api.ConnectionHandler;
062    import org.opends.server.api.ConnectionSecurityProvider;
063    import org.opends.server.api.ServerShutdownListener;
064    import org.opends.server.api.plugin.PluginResult;
065    import org.opends.server.config.ConfigException;
066    import org.opends.server.core.DirectoryServer;
067    import org.opends.server.core.PluginConfigManager;
068    import org.opends.server.extensions.NullConnectionSecurityProvider;
069    import org.opends.server.extensions.TLSConnectionSecurityProvider;
070    import org.opends.server.types.AddressMask;
071    import org.opends.server.types.ConfigChangeResult;
072    import org.opends.server.types.DN;
073    import org.opends.server.types.DebugLogLevel;
074    import org.opends.server.types.DisconnectReason;
075    
076    
077    import org.opends.server.types.HostPort;
078    import org.opends.server.types.InitializationException;
079    import org.opends.server.types.ResultCode;
080    import org.opends.server.types.SSLClientAuthPolicy;
081    import org.opends.server.util.StaticUtils;
082    
083    
084    
085    /**
086     * This class defines a connection handler that will be used for
087     * communicating with clients over LDAP. It is actually implemented in
088     * two parts: as a connection handler and one or more request
089     * handlers. The connection handler is responsible for accepting new
090     * connections and registering each of them with a request handler.
091     * The request handlers then are responsible for reading requests from
092     * the clients and parsing them as operations. A single request
093     * handler may be used, but having multiple handlers might provide
094     * better performance in a multi-CPU system.
095     */
096    public final class LDAPConnectionHandler extends
097        ConnectionHandler<LDAPConnectionHandlerCfg> implements
098        ConfigurationChangeListener<LDAPConnectionHandlerCfg>,
099        ServerShutdownListener, AlertGenerator {
100    
101      /**
102       * The tracer object for the debug logger.
103       */
104      private static final DebugTracer TRACER = getTracer();
105    
106      /**
107       * The fully-qualified name of this class.
108       */
109      private static final String CLASS_NAME =
110        "org.opends.server.protocols.ldap.LDAPConnectionHandler";
111    
112      // The current configuration state.
113      private LDAPConnectionHandlerCfg currentConfig;
114    
115      /* Properties that cannot be modified dynamically */
116    
117      // The set of addresses on which to listen for new connections.
118      private Set<InetAddress> listenAddresses;
119    
120      // The port on which this connection handler should listen for
121      // requests.
122      private int listenPort;
123    
124      // The SSL client auth policy used by this connection handler.
125      private SSLClientAuthPolicy sslClientAuthPolicy;
126    
127      // The backlog that will be used for the accept queue.
128      private int backlog;
129    
130      // Indicates whether to allow the reuse address socket option.
131      private boolean allowReuseAddress;
132    
133      // The number of request handlers that should be used for this
134      // connection handler.
135      private int numRequestHandlers;
136    
137      // Indicates whether the Directory Server is in the process of
138      // shutting down.
139      private boolean shutdownRequested;
140    
141      /* Internal LDAP connection handler state */
142    
143      // Indicates whether this connection handler is enabled.
144      private boolean enabled;
145    
146      // The set of clients that are explicitly allowed access to the
147      // server.
148      private AddressMask[] allowedClients;
149    
150      // The set of clients that have been explicitly denied access to the
151      // server.
152      private AddressMask[] deniedClients;
153    
154      // The set of SSL cipher suites that should be allowed.
155      private String[] enabledSSLCipherSuites;
156    
157      // The set of SSL protocols that should be allowed.
158      private String[] enabledSSLProtocols;
159    
160      // The index to the request handler that will be used for the next
161      // connection accepted by the server.
162      private int requestHandlerIndex;
163    
164      // The set of listeners for this connection handler.
165      private LinkedList<HostPort> listeners;
166    
167      // The set of request handlers that are associated with this
168      // connection handler.
169      private LDAPRequestHandler[] requestHandlers;
170    
171      // The set of statistics collected for this connection handler.
172      private LDAPStatistics statTracker;
173    
174      // The selector that will be used to multiplex connection acceptance
175      // across multiple sockets by a single thread.
176      private Selector selector;
177    
178      // The unique name assigned to this connection handler.
179      private String handlerName;
180    
181      // The protocol used by this connection handler.
182      private String protocol;
183    
184      // The connection security provider that will be used by default for
185      // new client connections.
186      private ConnectionSecurityProvider securityProvider;
187    
188    
189    
190      /**
191       * Creates a new instance of this LDAP connection handler. It must
192       * be initialized before it may be used.
193       */
194      public LDAPConnectionHandler() {
195        super("LDAP Connection Handler Thread");
196    
197        // No real implementation is required. Do all the work in the
198        // initializeConnectionHandler method.
199      }
200    
201    
202    
203      /**
204       * Indicates whether this connection handler should allow
205       * interaction with LDAPv2 clients.
206       *
207       * @return <CODE>true</CODE> if LDAPv2 is allowed, or <CODE>false</CODE>
208       *         if not.
209       */
210      public boolean allowLDAPv2() {
211        return currentConfig.isAllowLDAPV2();
212      }
213    
214    
215    
216      /**
217       * Indicates whether this connection handler should allow the use of
218       * the StartTLS extended operation.
219       *
220       * @return <CODE>true</CODE> if StartTLS is allowed, or <CODE>false</CODE>
221       *         if not.
222       */
223      public boolean allowStartTLS() {
224        if (currentConfig.isAllowStartTLS()) {
225          if (currentConfig.isUseSSL()) {
226            return false;
227          } else {
228            return true;
229          }
230        } else {
231          return false;
232        }
233      }
234    
235    
236    
237      /**
238       * {@inheritDoc}
239       */
240      public ConfigChangeResult applyConfigurationChange(
241          LDAPConnectionHandlerCfg config) {
242        // Create variables to include in the response.
243        ResultCode resultCode = ResultCode.SUCCESS;
244        boolean adminActionRequired = false;
245        ArrayList<Message> messages = new ArrayList<Message>();
246    
247        // Note that the following properties cannot be modified:
248        //
249        // * listen port and addresses
250        // * use ssl
251        // * ssl policy
252        // * ssl cert nickname
253        // * accept backlog
254        // * tcp reuse address
255        // * num request handler
256    
257        // Start/clear the stat tracker if LDAPv2 is being enabled.
258        if (currentConfig.isAllowLDAPV2() != config.isAllowLDAPV2()) {
259          if (config.isAllowLDAPV2()) {
260            if (statTracker == null) {
261              statTracker = new LDAPStatistics(handlerName
262                  + " Statistics");
263            } else {
264              statTracker.clearStatistics();
265            }
266          }
267        }
268    
269        // Apply the changes.
270        currentConfig = config;
271        enabled = config.isEnabled();
272        allowedClients = config.getAllowedClient().toArray(
273            new AddressMask[0]);
274        deniedClients = config.getDeniedClient().toArray(
275            new AddressMask[0]);
276    
277        // Get the supported SSL ciphers and protocols.
278        Set<String> ciphers = config.getSSLCipherSuite();
279        if (ciphers.isEmpty()) {
280          enabledSSLCipherSuites = null;
281        } else {
282          enabledSSLCipherSuites = ciphers.toArray(new String[0]);
283        }
284    
285        Set<String> protocols = config.getSSLProtocol();
286        if (protocols.isEmpty()) {
287          enabledSSLProtocols = null;
288        } else {
289          enabledSSLProtocols = protocols.toArray(new String[0]);
290        }
291    
292        if (config.isAllowLDAPV2())
293        {
294          DirectoryServer.registerSupportedLDAPVersion(2, this);
295        }
296        else
297        {
298          DirectoryServer.deregisterSupportedLDAPVersion(2, this);
299        }
300    
301        return new ConfigChangeResult(resultCode, adminActionRequired,
302            messages);
303      }
304    
305    
306    
307      /**
308       * Closes this connection handler so that it will no longer accept
309       * new client connections. It may or may not disconnect existing
310       * client connections based on the provided flag. Note, however,
311       * that some connection handler implementations may not have any way
312       * to continue processing requests from existing connections, in
313       * which case they should always be closed regardless of the value
314       * of the <CODE>closeConnections</CODE> flag.
315       *
316       * @param finalizeReason
317       *          The reason that this connection handler should be
318       *          finalized.
319       * @param closeConnections
320       *          Indicates whether any established client connections
321       *          associated with the connection handler should also be
322       *          closed.
323       */
324      public void finalizeConnectionHandler(Message finalizeReason,
325          boolean closeConnections) {
326        shutdownRequested = true;
327        currentConfig.removeLDAPChangeListener(this);
328    
329        DirectoryServer.deregisterSupportedLDAPVersion(2, this);
330        DirectoryServer.deregisterSupportedLDAPVersion(3, this);
331    
332        try {
333          selector.wakeup();
334        } catch (Exception e) {
335          if (debugEnabled())
336          {
337            TRACER.debugCaught(DebugLogLevel.ERROR, e);
338          }
339        }
340    
341        if (closeConnections) {
342          for (LDAPRequestHandler requestHandler : requestHandlers) {
343            requestHandler.processServerShutdown(finalizeReason);
344          }
345        } else {
346          for (LDAPRequestHandler requestHandler : requestHandlers) {
347            requestHandler.registerShutdownListener();
348          }
349        }
350      }
351    
352    
353    
354      /**
355       * Retrieves information about the set of alerts that this generator
356       * may produce. The map returned should be between the notification
357       * type for a particular notification and the human-readable
358       * description for that notification. This alert generator must not
359       * generate any alerts with types that are not contained in this
360       * list.
361       *
362       * @return Information about the set of alerts that this generator
363       *         may produce.
364       */
365      public LinkedHashMap<String, String> getAlerts() {
366        LinkedHashMap<String, String> alerts = new LinkedHashMap<String, String>();
367    
368        alerts
369            .put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
370                ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES);
371        alerts.put(ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR,
372            ALERT_DESCRIPTION_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR);
373    
374        return alerts;
375      }
376    
377    
378    
379      /**
380       * Retrieves the fully-qualified name of the Java class for this
381       * alert generator implementation.
382       *
383       * @return The fully-qualified name of the Java class for this alert
384       *         generator implementation.
385       */
386      public String getClassName() {
387        return CLASS_NAME;
388      }
389    
390    
391    
392      /**
393       * Retrieves the set of active client connections that have been
394       * established through this connection handler.
395       *
396       * @return The set of active client connections that have been
397       *         established through this connection handler.
398       */
399      public Collection<ClientConnection> getClientConnections() {
400        LinkedList<ClientConnection> connectionList =
401          new LinkedList<ClientConnection>();
402        for (LDAPRequestHandler requestHandler : requestHandlers) {
403          connectionList.addAll(requestHandler.getClientConnections());
404        }
405    
406        return connectionList;
407      }
408    
409    
410    
411      /**
412       * Retrieves the DN of the configuration entry with which this alert
413       * generator is associated.
414       *
415       * @return The DN of the configuration entry with which this alert
416       *         generator is associated.
417       */
418      public DN getComponentEntryDN() {
419        return currentConfig.dn();
420      }
421    
422    
423    
424      /**
425       * {@inheritDoc}
426       */
427      public String getConnectionHandlerName() {
428        return handlerName;
429      }
430    
431    
432    
433      /**
434       * Retrieves the set of enabled SSL cipher suites configured for
435       * this connection handler.
436       *
437       * @return The set of enabled SSL cipher suites configured for this
438       *         connection handler.
439       */
440      public String[] getEnabledSSLCipherSuites() {
441        return enabledSSLCipherSuites;
442      }
443    
444    
445    
446      /**
447       * Retrieves the set of enabled SSL protocols configured for this
448       * connection handler.
449       *
450       * @return The set of enabled SSL protocols configured for this
451       *         connection handler.
452       */
453      public String[] getEnabledSSLProtocols() {
454        return enabledSSLProtocols;
455      }
456    
457    
458    
459      /**
460       * Retrieves the DN of the key manager provider that should be used
461       * for operations associated with this connection handler which need
462       * access to a key manager.
463       *
464       * @return The DN of the key manager provider that should be used
465       *         for operations associated with this connection handler
466       *         which need access to a key manager, or {@code null} if no
467       *         key manager provider has been configured for this
468       *         connection handler.
469       */
470      public DN getKeyManagerProviderDN() {
471        return currentConfig.getKeyManagerProviderDN();
472      }
473    
474    
475    
476      /**
477       * {@inheritDoc}
478       */
479      public Collection<HostPort> getListeners() {
480        return listeners;
481      }
482    
483    
484    
485      /**
486       * Retrieves the port on which this connection handler is listening
487       * for client connections.
488       *
489       * @return The port on which this connection handler is listening
490       *         for client connections.
491       */
492      public int getListenPort() {
493        return listenPort;
494      }
495    
496    
497    
498      /**
499       * Retrieves the maximum length of time in milliseconds that attempts to write
500       * to LDAP client connections should be allowed to block.
501       *
502       * @return  The maximum length of time in milliseconds that attempts to write
503       *          to LDAP client connections should be allowed to block, or zero if
504       *          there should not be any limit imposed.
505       */
506      public long getMaxBlockedWriteTimeLimit() {
507        return currentConfig.getMaxBlockedWriteTimeLimit();
508      }
509    
510    
511    
512      /**
513       * Retrieves the maximum ASN.1 element value length that will be
514       * allowed by this connection handler.
515       *
516       * @return The maximum ASN.1 element value length that will be
517       *         allowed by this connection handler.
518       */
519      public int getMaxRequestSize() {
520        return (int) currentConfig.getMaxRequestSize();
521      }
522    
523    
524    
525      /**
526       * {@inheritDoc}
527       */
528      public String getProtocol() {
529        return protocol;
530      }
531    
532    
533    
534      /**
535       * {@inheritDoc}
536       */
537      public String getShutdownListenerName() {
538        return handlerName;
539      }
540    
541    
542    
543      /**
544       * Retrieves the nickname of the server certificate that should be
545       * used in conjunction with this LDAP connection handler.
546       *
547       * @return The nickname of the server certificate that should be
548       *         used in conjunction with this LDAP connection handler.
549       */
550      public String getSSLServerCertNickname() {
551        return currentConfig.getSSLCertNickname();
552      }
553    
554    
555    
556      /**
557       * Retrieves the SSL client authentication policy for this
558       * connection handler.
559       *
560       * @return The SSL client authentication policy for this connection
561       *         handler.
562       */
563      public SSLClientAuthPolicy getSSLClientAuthPolicy() {
564        return sslClientAuthPolicy;
565      }
566    
567    
568    
569      /**
570       * Retrieves the set of statistics maintained by this connection
571       * handler.
572       *
573       * @return The set of statistics maintained by this connection
574       *         handler.
575       */
576      public LDAPStatistics getStatTracker() {
577        return statTracker;
578      }
579    
580    
581    
582      /**
583       * Retrieves the DN of the trust manager provider that should be
584       * used for operations associated with this connection handler which
585       * need access to a trust manager.
586       *
587       * @return The DN of the trust manager provider that should be used
588       *         for operations associated with this connection handler
589       *         which need access to a trust manager, or {@code null} if
590       *         no trust manager provider has been configured for this
591       *         connection handler.
592       */
593      public DN getTrustManagerProviderDN() {
594        return currentConfig.getTrustManagerProviderDN();
595      }
596    
597    
598    
599      /**
600       * {@inheritDoc}
601       */
602      public void initializeConnectionHandler(LDAPConnectionHandlerCfg config)
603             throws ConfigException, InitializationException
604      {
605        // Open the selector.
606        try {
607          selector = Selector.open();
608        } catch (Exception e) {
609          if (debugEnabled())
610          {
611            TRACER.debugCaught(DebugLogLevel.ERROR, e);
612          }
613    
614          Message message = ERR_LDAP_CONNHANDLER_OPEN_SELECTOR_FAILED.get(
615              String.valueOf(config.dn()), stackTraceToSingleLineString(e));
616          throw new InitializationException(message, e);
617        }
618    
619        // Get the SSL auth policy.
620        switch (config.getSSLClientAuthPolicy()) {
621        case DISABLED:
622          sslClientAuthPolicy = SSLClientAuthPolicy.DISABLED;
623          break;
624        case REQUIRED:
625          sslClientAuthPolicy = SSLClientAuthPolicy.REQUIRED;
626          break;
627        default:
628          sslClientAuthPolicy = SSLClientAuthPolicy.OPTIONAL;
629          break;
630        }
631    
632        // Get the supported SSL ciphers and protocols.
633        Set<String> ciphers = config.getSSLCipherSuite();
634        if (ciphers.isEmpty()) {
635          enabledSSLCipherSuites = null;
636        } else {
637          enabledSSLCipherSuites = ciphers.toArray(new String[0]);
638        }
639    
640        Set<String> protocols = config.getSSLProtocol();
641        if (protocols.isEmpty()) {
642          enabledSSLProtocols = null;
643        } else {
644          enabledSSLProtocols = protocols.toArray(new String[0]);
645        }
646    
647        // Initialize the security provider.
648        if (config.isUseSSL()) {
649          TLSConnectionSecurityProvider tlsProvider =
650            new TLSConnectionSecurityProvider();
651          tlsProvider.initializeConnectionSecurityProvider(null);
652          tlsProvider.setSSLClientAuthPolicy(sslClientAuthPolicy);
653          tlsProvider.setEnabledProtocols(enabledSSLProtocols);
654          tlsProvider.setEnabledCipherSuites(enabledSSLCipherSuites);
655    
656          // FIXME -- Need to do something with the requested cert
657          // nickname.
658    
659          securityProvider = tlsProvider;
660        } else {
661          securityProvider = new NullConnectionSecurityProvider();
662          securityProvider.initializeConnectionSecurityProvider(null);
663        }
664    
665        // Save this configuration for future reference.
666        currentConfig = config;
667        enabled = config.isEnabled();
668        requestHandlerIndex = 0;
669        allowedClients = config.getAllowedClient().toArray(
670            new AddressMask[0]);
671        deniedClients = config.getDeniedClient().toArray(
672            new AddressMask[0]);
673    
674        // Save properties that cannot be dynamically modified.
675        allowReuseAddress = config.isAllowTCPReuseAddress();
676        backlog = config.getAcceptBacklog();
677        listenAddresses = config.getListenAddress();
678        listenPort = config.getListenPort();
679        numRequestHandlers = config.getNumRequestHandlers();
680    
681        // Construct a unique name for this connection handler, and put
682        // together the
683        // set of listeners.
684        listeners = new LinkedList<HostPort>();
685        StringBuilder nameBuffer = new StringBuilder();
686        nameBuffer.append("LDAP Connection Handler");
687        for (InetAddress a : listenAddresses) {
688          listeners.add(new HostPort(a.getHostAddress(), listenPort));
689          nameBuffer.append(" ");
690          nameBuffer.append(a.getHostAddress());
691        }
692        nameBuffer.append(" port ");
693        nameBuffer.append(listenPort);
694        handlerName = nameBuffer.toString();
695    
696        // Set the protocol for this connection handler.
697        if (config.isUseSSL()) {
698          protocol = "LDAP+SSL";
699        } else {
700          protocol = "LDAP";
701        }
702    
703        // Perform any additional initialization that might be required.
704        statTracker = new LDAPStatistics(handlerName + " Statistics");
705    
706        // Attempt to bind to the listen port on all configured addresses to
707        // verify whether the connection handler will be able to start.
708        for (InetAddress a : listenAddresses) {
709          try {
710            if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) {
711              throw new IOException(
712                ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
713            }
714          } catch (IOException e) {
715            if (debugEnabled()) {
716              TRACER.debugCaught(DebugLogLevel.ERROR, e);
717            }
718    
719            Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get(
720              String.valueOf(config.dn()), a.getHostAddress(),
721              listenPort, getExceptionMessage(e));
722            logError(message);
723            throw new InitializationException(message);
724          }
725        }
726    
727        // Create and start the request handlers.
728        requestHandlers = new LDAPRequestHandler[numRequestHandlers];
729        for (int i = 0; i < numRequestHandlers; i++) {
730          requestHandlers[i] = new LDAPRequestHandler(this, i);
731        }
732    
733        for (int i = 0; i < numRequestHandlers; i++) {
734          requestHandlers[i].start();
735        }
736    
737        // Register the set of supported LDAP versions.
738        DirectoryServer.registerSupportedLDAPVersion(3, this);
739        if (config.isAllowLDAPV2())
740        {
741          DirectoryServer.registerSupportedLDAPVersion(2, this);
742        }
743    
744        // Register this as a change listener.
745        config.addLDAPChangeListener(this);
746      }
747    
748    
749    
750      /**
751       * {@inheritDoc}
752       */
753      @Override()
754      public boolean isConfigurationAcceptable(ConnectionHandlerCfg configuration,
755                                               List<Message> unacceptableReasons)
756      {
757        LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
758    
759        // Attempt to bind to the listen port on all configured addresses to
760        // verify whether the connection handler will be able to start.
761        if ((currentConfig == null) ||
762          (!currentConfig.isEnabled() && config.isEnabled())) {
763          for (InetAddress a : config.getListenAddress()) {
764            try {
765              if (StaticUtils.isAddressInUse(a, config.getListenPort(),
766                config.isAllowTCPReuseAddress())) {
767                throw new IOException(
768                  ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
769              }
770            } catch (IOException e) {
771              if (debugEnabled()) {
772                TRACER.debugCaught(DebugLogLevel.ERROR, e);
773              }
774    
775              Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get(
776                String.valueOf(config.dn()), a.getHostAddress(),
777                config.getListenPort(), getExceptionMessage(e));
778              unacceptableReasons.add(message);
779              return false;
780            }
781          }
782        }
783    
784        return isConfigurationChangeAcceptable(config, unacceptableReasons);
785      }
786    
787    
788    
789      /**
790       * {@inheritDoc}
791       */
792      public boolean isConfigurationChangeAcceptable(
793          LDAPConnectionHandlerCfg config,
794          List<Message> unacceptableReasons) {
795        // All validation is performed by the admin framework.
796        return true;
797      }
798    
799    
800    
801      /**
802       * Indicates whether this connection handler should maintain usage
803       * statistics.
804       *
805       * @return <CODE>true</CODE> if this connection handler should
806       *         maintain usage statistics, or <CODE>false</CODE> if
807       *         not.
808       */
809      public boolean keepStats() {
810        return currentConfig.isKeepStats();
811      }
812    
813    
814    
815      /**
816       * {@inheritDoc}
817       */
818      public void processServerShutdown(Message reason) {
819        shutdownRequested = true;
820    
821        try {
822          for (LDAPRequestHandler requestHandler : requestHandlers) {
823            try {
824              requestHandler.processServerShutdown(reason);
825            } catch (Exception e) {
826            }
827          }
828        } catch (Exception e) {
829        }
830      }
831    
832    
833    
834      /**
835       * Operates in a loop, accepting new connections and ensuring that
836       * requests on those connections are handled properly.
837       */
838      public void run() {
839        setName(handlerName);
840        boolean listening = false;
841    
842        while (!shutdownRequested) {
843          // If this connection handler is not enabled, then just sleep
844          // for a bit and check again.
845          if (!enabled) {
846            if (listening) {
847              cleanUpSelector();
848              listening = false;
849    
850              logError(ERR_LDAP_CONNHANDLER_STOPPED_LISTENING.get(handlerName));
851            }
852    
853            try {
854              Thread.sleep(1000);
855            } catch (Exception e) {
856            }
857    
858            continue;
859          }
860    
861          // If we have gotten here, then we are about to start listening
862          // for the first time since startup or since we were previously
863          // disabled. Make sure to start with a clean selector and then
864          // create all the listeners.
865          try {
866            cleanUpSelector();
867    
868            int numRegistered = 0;
869            for (InetAddress a : listenAddresses) {
870              try {
871                ServerSocketChannel channel = ServerSocketChannel.open();
872                channel.socket().setReuseAddress(allowReuseAddress);
873                channel.socket().bind(
874                    new InetSocketAddress(a, listenPort), backlog);
875                channel.configureBlocking(false);
876                channel.register(selector, SelectionKey.OP_ACCEPT);
877                numRegistered++;
878    
879                logError(ERR_LDAP_CONNHANDLER_STARTED_LISTENING.get(handlerName));
880              } catch (Exception e) {
881                if (debugEnabled())
882                {
883                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
884                }
885    
886                logError(ERR_LDAP_CONNHANDLER_CREATE_CHANNEL_FAILED.
887                    get(String.valueOf(currentConfig.dn()), a.getHostAddress(),
888                        listenPort, stackTraceToSingleLineString(e)));
889              }
890            }
891    
892            // If none of the listeners were created successfully, then
893            // consider the connection handler disabled and require
894            // administrative action before trying again.
895            if (numRegistered == 0) {
896              logError(ERR_LDAP_CONNHANDLER_NO_ACCEPTORS.get(
897                      String.valueOf(currentConfig.dn())));
898    
899              enabled = false;
900              continue;
901            }
902    
903            listening = true;
904    
905            // Enter a loop, waiting for new connections to arrive and
906            // then accepting them as they come in.
907            boolean lastIterationFailed = false;
908            while (enabled && (!shutdownRequested)) {
909              try {
910                if (selector.select() > 0) {
911                  Iterator<SelectionKey> iterator = selector
912                      .selectedKeys().iterator();
913    
914                  while (iterator.hasNext()) {
915                    SelectionKey key = iterator.next();
916                    if (key.isAcceptable()) {
917                      // Accept the new client connection.
918                      ServerSocketChannel serverChannel = (ServerSocketChannel) key
919                          .channel();
920                      SocketChannel clientChannel = serverChannel
921                          .accept();
922                      LDAPClientConnection clientConnection =
923                        new LDAPClientConnection(this, clientChannel);
924    
925                      // Check to see if the core server rejected the
926                      // connection (e.g., already too many connections
927                      // established).
928                      if (clientConnection.getConnectionID() < 0) {
929                        // The connection will have already been closed.
930                        iterator.remove();
931                        continue;
932                      }
933    
934                      InetAddress clientAddr = clientConnection
935                          .getRemoteAddress();
936                      // Check to see if the client is on the denied list.
937                      // If so, then reject it immediately.
938                      if ((deniedClients.length > 0)
939                          && AddressMask.maskListContains(clientAddr
940                              .getAddress(), clientAddr.getHostName(),
941                              deniedClients)) {
942                        clientConnection.disconnect(
943                            DisconnectReason.CONNECTION_REJECTED,
944                            currentConfig.isSendRejectionNotice(),
945                            ERR_LDAP_CONNHANDLER_DENIED_CLIENT.get(
946                              clientConnection.getClientHostPort(),
947                              clientConnection.getServerHostPort()));
948    
949                        iterator.remove();
950                        continue;
951                      }
952                      // Check to see if there is an allowed list and if
953                      // there is whether the client is on that list. If
954                      // not, then reject the connection.
955                      if ((allowedClients.length > 0)
956                          && (!AddressMask.maskListContains(clientAddr
957                              .getAddress(), clientAddr.getHostName(),
958                              allowedClients))) {
959                        clientConnection.disconnect(
960                            DisconnectReason.CONNECTION_REJECTED,
961                            currentConfig.isSendRejectionNotice(),
962                            ERR_LDAP_CONNHANDLER_DISALLOWED_CLIENT.get(
963                              clientConnection.getClientHostPort(),
964                              clientConnection.getServerHostPort()));
965                        iterator.remove();
966                        continue;
967                      }
968                      clientChannel.socket().setKeepAlive(
969                          currentConfig.isUseTCPKeepAlive());
970                      clientChannel.socket().setTcpNoDelay(
971                          currentConfig.isUseTCPNoDelay());
972    
973                      try
974                      {
975                        ConnectionSecurityProvider connectionSecurityProvider =
976                             securityProvider.newInstance(clientConnection,
977                                                          clientChannel);
978                        clientConnection.setConnectionSecurityProvider(
979                             connectionSecurityProvider);
980                      }
981                      catch (Exception e)
982                      {
983                        if (debugEnabled())
984                        {
985                          TRACER.debugCaught(DebugLogLevel.ERROR, e);
986                        }
987    
988                        clientConnection.disconnect(
989                             DisconnectReason.SECURITY_PROBLEM, false,
990                             ERR_LDAP_CONNHANDLER_CANNOT_SET_SECURITY_PROVIDER.get(
991                              String.valueOf(e)));
992                        iterator.remove();
993                        continue;
994                      }
995    
996                      // If we've gotten here, then we'll take the
997                      // connection so invoke the post-connect plugins and
998                      // register the client connection with a request
999                      // handler.
1000                      try {
1001                        PluginConfigManager pluginManager = DirectoryServer
1002                            .getPluginConfigManager();
1003                        PluginResult.PostConnect pluginResult = pluginManager
1004                            .invokePostConnectPlugins(clientConnection);
1005                        if (!pluginResult.continueProcessing()) {
1006                          clientConnection.disconnect(
1007                              pluginResult.getDisconnectReason(),
1008                              pluginResult.sendDisconnectNotification(),
1009                              pluginResult.getErrorMessage());
1010    
1011                          iterator.remove();
1012                          continue;
1013                        }
1014    
1015                        LDAPRequestHandler requestHandler =
1016                          requestHandlers[requestHandlerIndex++];
1017                        if (requestHandlerIndex >= numRequestHandlers) {
1018                          requestHandlerIndex = 0;
1019                        }
1020    
1021                        if (requestHandler
1022                            .registerClient(clientConnection)) {
1023                          logConnect(clientConnection);
1024                        } else {
1025                          iterator.remove();
1026                          continue;
1027                        }
1028                      } catch (Exception e) {
1029                        if (debugEnabled())
1030                        {
1031                          TRACER.debugCaught(DebugLogLevel.ERROR, e);
1032                        }
1033    
1034                        Message message =
1035                          INFO_LDAP_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.
1036                              get(clientConnection.getClientHostPort(),
1037                                  clientConnection.getServerHostPort(),
1038                                  getExceptionMessage(e));
1039                        logError(message);
1040    
1041                        clientConnection.disconnect(
1042                            DisconnectReason.SERVER_ERROR, currentConfig
1043                                .isSendRejectionNotice(), message);
1044    
1045                        iterator.remove();
1046                        continue;
1047                      }
1048                    }
1049    
1050                    iterator.remove();
1051                  }
1052                } else {
1053                  if (shutdownRequested) {
1054                    cleanUpSelector();
1055                    selector.close();
1056                    listening = false;
1057                    enabled = false;
1058                    continue;
1059                  }
1060                }
1061    
1062                lastIterationFailed = false;
1063              } catch (Exception e) {
1064                if (debugEnabled())
1065                {
1066                  TRACER.debugCaught(DebugLogLevel.ERROR, e);
1067                }
1068    
1069                logError(ERR_LDAP_CONNHANDLER_CANNOT_ACCEPT_CONNECTION.get(
1070                    String.valueOf(currentConfig.dn()), getExceptionMessage(e)));
1071    
1072                if (lastIterationFailed) {
1073                  // The last time through the accept loop we also
1074                  // encountered a failure. Rather than enter a potential
1075                  // infinite loop of failures, disable this acceptor and
1076                  // log an error.
1077                  Message message =
1078                    ERR_LDAP_CONNHANDLER_CONSECUTIVE_ACCEPT_FAILURES.
1079                        get(String.valueOf(currentConfig.dn()),
1080                            stackTraceToSingleLineString(e));
1081                  logError(message);
1082    
1083                  DirectoryServer
1084                      .sendAlertNotification(
1085                          this,
1086                          ALERT_TYPE_LDAP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES,
1087                              message);
1088    
1089                  enabled = false;
1090    
1091                  try {
1092                    cleanUpSelector();
1093                  } catch (Exception e2) {
1094                  }
1095                } else {
1096                  lastIterationFailed = true;
1097                }
1098              }
1099            }
1100          } catch (Exception e) {
1101            if (debugEnabled())
1102            {
1103              TRACER.debugCaught(DebugLogLevel.ERROR, e);
1104            }
1105    
1106            // This is very bad because we failed outside the loop. The
1107            // only thing we can do here is log a message, send an alert,
1108            // and disable the selector until an administrator can figure
1109            // out what's going on.
1110            Message message = ERR_LDAP_CONNHANDLER_UNCAUGHT_ERROR.
1111                get(String.valueOf(currentConfig.dn()),
1112                    stackTraceToSingleLineString(e));
1113            logError(message);
1114    
1115            DirectoryServer.sendAlertNotification(this,
1116                ALERT_TYPE_LDAP_CONNECTION_HANDLER_UNCAUGHT_ERROR,
1117                    message);
1118    
1119            try {
1120              cleanUpSelector();
1121            } catch (Exception e2) {
1122            }
1123    
1124            enabled = false;
1125          }
1126        }
1127      }
1128    
1129    
1130    
1131      /**
1132       * Appends a string representation of this connection handler to the
1133       * provided buffer.
1134       *
1135       * @param buffer
1136       *          The buffer to which the information should be appended.
1137       */
1138      public void toString(StringBuilder buffer) {
1139        buffer.append(handlerName);
1140      }
1141    
1142    
1143    
1144      /**
1145       * Indicates whether this connection handler should use SSL to
1146       * communicate with clients.
1147       *
1148       * @return {@code true} if this connection handler should use SSL to
1149       *         communicate with clients, or {@code false} if not.
1150       */
1151      public boolean useSSL() {
1152        return currentConfig.isUseSSL();
1153      }
1154    
1155    
1156    
1157      /**
1158       * Cleans up the contents of the selector, closing any server socket
1159       * channels that might be associated with it. Any connections that
1160       * might have been established through those channels should not be
1161       * impacted.
1162       */
1163      private void cleanUpSelector() {
1164        try {
1165          Iterator<SelectionKey> iterator = selector.keys().iterator();
1166          while (iterator.hasNext()) {
1167            SelectionKey key = iterator.next();
1168    
1169            try {
1170              key.cancel();
1171            } catch (Exception e) {
1172              if (debugEnabled())
1173              {
1174                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1175              }
1176            }
1177    
1178            try {
1179              key.channel().close();
1180            } catch (Exception e) {
1181              if (debugEnabled())
1182              {
1183                TRACER.debugCaught(DebugLogLevel.ERROR, e);
1184              }
1185            }
1186          }
1187        } catch (Exception e) {
1188          if (debugEnabled())
1189          {
1190            TRACER.debugCaught(DebugLogLevel.ERROR, e);
1191          }
1192        }
1193      }
1194    }