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