mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

abobrov
13.23.2008 7b0becc5365373eb2004cd89f9e82fae0298c7fa
- [Issue 2877] : Issue with creation of connection handler:
refactor "address in use" test from connection handled implementations into a standalone isAddressInUse() StaticUtils method.
invoke isAddressInUse() when validating connection handler config and connection handler initialization, both LDAP and JMX.

3 files modified
249 ■■■■■ changed files
opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java 69 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java 100 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 80 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
@@ -36,8 +36,6 @@
import static org.opends.server.util.StaticUtils.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
@@ -60,6 +58,7 @@
import org.opends.server.types.HostPort;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.util.StaticUtils;
@@ -328,48 +327,16 @@
    // Configuration is ok.
    currentConfig = config;
    // Attempt to bind to the listen port to verify whether the connection
    // handler will be able to start.
    ServerSocket s = null;
    try
    {
      // HACK:
      // With dual stacks we can have a situation when INADDR_ANY/PORT
      // is bound in TCP4 space but available in TCP6 space and since
      // JavaServerSocket implemantation will always use TCP46 on dual
      // stacks the bind below will always succeed in such cases thus
      // shadowing anything that is already bound to INADDR_ANY/PORT.
      // While technically correct, with IPv4 and IPv6 being separate
      // address spaces, it presents a problem to end users because a
      // common case scenario is to have a single service serving both
      // address spaces ie listening to the same port in both spaces
      // on wildcard addresses 0 and ::. ServerSocket implemantation
      // does not provide any means of working with each address space
      // separately such as doing TCP4 or TCP6 only binds thus we have
      // to do a dummy connect to INADDR_ANY/PORT to check if it is
      // bound to something already. This is only needed for wildcard
      // addresses as specific IPv4 or IPv6 addresses will always be
      // handled in their respective address space.
      Socket clientSocket = new Socket();
      try {
        // This might fail on some stacks but this is the best we
        // can do. No need for explicit timeout since it is local
        // address and we have to know for sure unless it fails.
        clientSocket.connect(new InetSocketAddress(
          config.getListenPort()));
      } catch (IOException e) {
        // Expected, ignore.
      }
      if (clientSocket.isConnected()) {
        clientSocket.close();
      if (StaticUtils.isAddressInUse(
        new InetSocketAddress(config.getListenPort()).getAddress(),
        config.getListenPort(), true)) {
        throw new IOException(
          ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
      }
      s = new ServerSocket();
      s.setReuseAddress(true);
      s.bind(new InetSocketAddress(config.getListenPort()));
    }
    catch (Exception e)
    {
@@ -379,13 +346,6 @@
      logError(message);
      throw new InitializationException(message);
    }
    finally
    {
      try
      {
        s.close();
      } catch (Exception e) {}
    }
    if (config.isUseSSL()) {
      protocol = "JMX+SSL";
@@ -441,6 +401,27 @@
                                           List<Message> unacceptableReasons)
  {
    JMXConnectionHandlerCfg config = (JMXConnectionHandlerCfg) configuration;
    // Attempt to bind to the listen port to verify whether the connection
    // handler will be able to start.
    try
    {
      if (StaticUtils.isAddressInUse(
        new InetSocketAddress(config.getListenPort()).getAddress(),
        config.getListenPort(), true)) {
        throw new IOException(
          ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
      }
    }
    catch (Exception e)
    {
      Message message = ERR_JMX_CONNHANDLER_CANNOT_BIND.
          get(String.valueOf(config.dn()), config.getListenPort(),
              getExceptionMessage(e));
      unacceptableReasons.add(message);
      return false;
    }
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
  }
opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -41,8 +41,6 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
@@ -80,6 +78,7 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SSLClientAuthPolicy;
import org.opends.server.util.StaticUtils;
@@ -704,81 +703,26 @@
    // Perform any additional initialization that might be required.
    statTracker = new LDAPStatistics(handlerName + " Statistics");
    // Attempt to bind to the listen port on all configured addresses to
    // verify whether the connection handler will be able to start.
    LinkedList<ServerSocket> testListenSockets = new LinkedList<ServerSocket>();
    try
    {
      for (InetAddress a : listenAddresses)
      {
        try
        {
          // HACK:
          // With dual stacks we can have a situation when INADDR_ANY/PORT
          // is bound in TCP4 space but available in TCP6 space and since
          // JavaServerSocket implemantation will always use TCP46 on dual
          // stacks the bind below will always succeed in such cases thus
          // shadowing anything that is already bound to INADDR_ANY/PORT.
          // While technically correct, with IPv4 and IPv6 being separate
          // address spaces, it presents a problem to end users because a
          // common case scenario is to have a single service serving both
          // address spaces ie listening to the same port in both spaces
          // on wildcard addresses 0 and ::. ServerSocket implemantation
          // does not provide any means of working with each address space
          // separately such as doing TCP4 or TCP6 only binds thus we have
          // to do a dummy connect to INADDR_ANY/PORT to check if it is
          // bound to something already. This is only needed for wildcard
          // addresses as specific IPv4 or IPv6 addresses will always be
          // handled in their respective address space.
          if (a.isAnyLocalAddress()) {
            Socket s = new Socket();
    for (InetAddress a : listenAddresses) {
            try {
              // This might fail on some stacks but this is the best we
              // can do. No need for explicit timeout since it is local
              // address and we have to know for sure unless it fails.
              s.connect(new InetSocketAddress(a, listenPort));
            } catch (IOException e) {
              // Expected, ignore.
            }
            if (s.isConnected()) {
              s.close();
        if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) {
              throw new IOException(
                ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
            }
          }
          ServerSocket s = new ServerSocket();
          s.setReuseAddress(allowReuseAddress);
          s.bind(new InetSocketAddress(a, listenPort));
          testListenSockets.add(s);
        }
        catch (IOException e)
        {
          if (debugEnabled())
          {
      } catch (IOException e) {
        if (debugEnabled()) {
            TRACER.debugCaught(DebugLogLevel.ERROR, e);
          }
          Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.
              get(String.valueOf(config.dn()), a.getHostAddress(), listenPort,
                  getExceptionMessage(e));
        Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get(
          String.valueOf(config.dn()), a.getHostAddress(),
          listenPort, getExceptionMessage(e));
          logError(message);
          throw new InitializationException(message);
        }
      }
    }
    finally
    {
      for (ServerSocket s : testListenSockets)
      {
        try
        {
          s.close();
        } catch (Exception e) {}
      }
    }
    // Create and start the request handlers.
    requestHandlers = new LDAPRequestHandler[numRequestHandlers];
@@ -790,7 +734,6 @@
      requestHandlers[i].start();
    }
    // Register the set of supported LDAP versions.
    DirectoryServer.registerSupportedLDAPVersion(3, this);
    if (config.isAllowLDAPV2())
@@ -798,7 +741,6 @@
      DirectoryServer.registerSupportedLDAPVersion(2, this);
    }
    // Register this as a change listener.
    config.addLDAPChangeListener(this);
  }
@@ -813,6 +755,32 @@
                                           List<Message> unacceptableReasons)
  {
    LDAPConnectionHandlerCfg config = (LDAPConnectionHandlerCfg) configuration;
    // Attempt to bind to the listen port on all configured addresses to
    // verify whether the connection handler will be able to start.
    listenPort = config.getListenPort();
    listenAddresses = config.getListenAddress();
    allowReuseAddress = config.isAllowTCPReuseAddress();
    for (InetAddress a : listenAddresses) {
      try {
        if (StaticUtils.isAddressInUse(a, listenPort, allowReuseAddress)) {
          throw new IOException(
            ERR_CONNHANDLER_ADDRESS_INUSE.get().toString());
        }
      } catch (IOException e) {
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, e);
        }
        Message message = ERR_LDAP_CONNHANDLER_CANNOT_BIND.get(
          String.valueOf(config.dn()), a.getHostAddress(),
          listenPort, getExceptionMessage(e));
        unacceptableReasons.add(message);
        return false;
      }
    }
    return isConfigurationChangeAcceptable(config, unacceptableReasons);
  }
opends/src/server/org/opends/server/util/StaticUtils.java
@@ -40,6 +40,10 @@
import java.io.InputStreamReader;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -2624,6 +2628,82 @@
  /**
   * Indicates whether the provided TCP address is already in use.
   *
   * @param  address        IP address of the TCP address for which to make
   *                        the determination.
   * @param  port           TCP port number of the TCP address for which to
   *                        make the determination.
   * @param  allowReuse     Whether or not TCP address reuse is allowed when
   *                        making the determination.
   *
   * @return  <CODE>true</CODE> if the provided TCP address is already in
   *          use, or <CODE>false</CODE> otherwise.
   */
  public static boolean isAddressInUse(
    InetAddress address, int port,
    boolean allowReuse)
  {
    // Return pessimistic.
    boolean isInUse = true;
    Socket clientSocket = null;
    ServerSocket serverSocket = null;
    try {
      // HACK:
      // With dual stacks we can have a situation when INADDR_ANY/PORT
      // is bound in TCP4 space but available in TCP6 space and since
      // JavaServerSocket implemantation will always use TCP46 on dual
      // stacks the bind below will always succeed in such cases thus
      // shadowing anything that is already bound to INADDR_ANY/PORT.
      // While technically correct, with IPv4 and IPv6 being separate
      // address spaces, it presents a problem to end users because a
      // common case scenario is to have a single service serving both
      // address spaces ie listening to the same port in both spaces
      // on wildcard addresses 0 and ::. ServerSocket implemantation
      // does not provide any means of working with each address space
      // separately such as doing TCP4 or TCP6 only binds thus we have
      // to do a dummy connect to INADDR_ANY/PORT to check if it is
      // bound to something already. This is only needed for wildcard
      // addresses as specific IPv4 or IPv6 addresses will always be
      // handled in their respective address space.
      if (address.isAnyLocalAddress()) {
        clientSocket = new Socket();
        try {
          // This might fail on some stacks but this is the best we
          // can do. No need for explicit timeout since it is local
          // address and we have to know for sure unless it fails.
          clientSocket.connect(new InetSocketAddress(address, port));
        } catch (IOException e) {
        // Expected, ignore.
        }
        if (clientSocket.isConnected()) {
          isInUse = true;
        }
      }
      serverSocket = new ServerSocket();
      serverSocket.setReuseAddress(allowReuse);
      serverSocket.bind(new InetSocketAddress(address, port));
      isInUse = false;
    } catch (IOException e) {
      isInUse = true;
    } finally {
      try {
        if (serverSocket != null) {
          serverSocket.close();
        }
      } catch (Exception e) {}
      try {
        if (clientSocket != null) {
          clientSocket.close();
        }
      } catch (Exception e) {}
    }
    return isInUse;
  }
  /**
   * Retrieves a lowercase representation of the given string.  This
   * implementation presumes that the provided string will contain only ASCII
   * characters and is optimized for that case.  However, if a non-ASCII