From 129e812ad23e6963874c1ddc80841969ab07ab3b Mon Sep 17 00:00:00 2001
From: abobrov <abobrov@localhost>
Date: Wed, 13 Feb 2008 13:23:54 +0000
Subject: [PATCH] - [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.

---
 opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java                     |   80 ++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java   |   69 +++++--------
 opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java |  114 ++++++++--------------
 3 files changed, 146 insertions(+), 117 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
index 3b0cfd6..27f62e6 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
+++ b/opendj-sdk/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);
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index 201c7d0..b565302 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opendj-sdk/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();
-            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();
-              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);
+    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));
-          logError(message);
-          throw new InitializationException(message);
+      } 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));
+        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);
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java b/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
index d48716f..3be3a1c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/StaticUtils.java
+++ b/opendj-sdk/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

--
Gitblit v1.10.0