From 9af5bd326500773d89ce30311af5e3cac2a9b3f5 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Tue, 29 Sep 2026 06:39:04 +0000
Subject: [PATCH] [#1112] Listen on the configured listen-address in the HTTP connection handler and the JMX RMI connector (#1113)

---
 opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java |   82 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 80 insertions(+), 2 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
index 3b7f82e..626f94d 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/jmx/RmiConnector.java
@@ -22,9 +22,11 @@
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.rmi.RemoteException;
 import java.rmi.registry.LocateRegistry;
 import java.rmi.registry.Registry;
+import java.rmi.server.RMIServerSocketFactory;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.SortedSet;
@@ -168,6 +170,18 @@
    */
   private String rmiVersion;
 
+  /**
+   * The system property that names the host the stubs of the objects exported
+   * through RMI advertise.
+   */
+  private static final String RMI_SERVER_HOSTNAME = "java.rmi.server.hostname";
+
+  /**
+   * The value this server gave to {@code java.rmi.server.hostname}, or
+   * {@code null} if the server did not set it.
+   */
+  private static String rmiServerHostnameSetByServer;
+
   // ===================================================================
   // CONSTRUCTOR
   // ===================================================================
@@ -319,8 +333,10 @@
       // ---------------------
       // init an ssl context
       // ---------------------
+      // Both server socket factories listen on the configured listen address:
+      // the default RMI factory would listen on every interface.
       SslRMIClientSocketFactory rmiClientSockeyFactory = null;
-      DirectoryRMIServerSocketFactory rmiServerSockeyFactory = null;
+      RMIServerSocketFactory rmiServerSockeyFactory;
       if (jmxConnectionHandler.isUseSSL())
       {
         if (logger.isTraceEnabled())
@@ -355,7 +371,8 @@
         SSLSocketFactory ssf = ctx.getSocketFactory();
 
         // set the Server socket factory in the JMX map
-        rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(ssf, false);
+        rmiServerSockeyFactory = new DirectoryRMIServerSocketFactory(
+            ssf, false, jmxConnectionHandler.getListenAddress());
         env.put(
             "jmx.remote.rmi.server.socket.factory",
             rmiServerSockeyFactory);
@@ -376,6 +393,8 @@
         {
           logger.trace("UNSECURE CONNECTION");
         }
+        rmiServerSockeyFactory = new OpendsRmiServerSocketFactory(
+            jmxConnectionHandler.getListenAddress());
       }
 
       // specify the rmi JMX authenticator to be used
@@ -399,6 +418,7 @@
       {
         logger.trace("Create and start the JMX RMI connector");
       }
+      advertiseListenAddress(jmxConnectionHandler.getListenAddress());
       OpendsRMIJRMPServerImpl opendsRmiConnectorServer =
           new OpendsRMIJRMPServerImpl(jmxConnectionHandler.getRmiPort(),
               rmiClientSockeyFactory, rmiServerSockeyFactory, env);
@@ -426,6 +446,64 @@
 
   }
 
+  /**
+   * Makes the stub of the RMI connector advertise the listen address.
+   * <p>
+   * The stub that a client gets from the RMI registry advertises the host that
+   * {@code java.rmi.server.hostname} names, or else the address of the local host,
+   * whatever address the connector listens on: a connector bound to another
+   * address refuses the client. The JDK reads the property again at each export.
+   * A value that this server did not set is the operator's, and is kept.
+   *
+   * @param listenAddress
+   *          the address the connector listens on
+   */
+  private static synchronized void advertiseListenAddress(InetAddress listenAddress)
+  {
+    final String hostname = System.getProperty(RMI_SERVER_HOSTNAME);
+    if (hostname != null && !hostname.equals(rmiServerHostnameSetByServer))
+    {
+      return;
+    }
+    if (listenAddress.isAnyLocalAddress())
+    {
+      // Cleared, the property would leave the JDK advertising the host it last
+      // read: the address a connector of this server listened on before, which
+      // a remote client cannot reach if it was a loopback one. Advertise the
+      // local host instead, as the JDK does by default.
+      final InetAddress localHost = getLocalHostOrNull();
+      if (rmiServerHostnameSetByServer != null
+          && localHost != null && !localHost.isLoopbackAddress())
+      {
+        rmiServerHostnameSetByServer = localHost.getHostAddress();
+        System.setProperty(RMI_SERVER_HOSTNAME, rmiServerHostnameSetByServer);
+      }
+      else
+      {
+        System.clearProperty(RMI_SERVER_HOSTNAME);
+        rmiServerHostnameSetByServer = null;
+      }
+    }
+    else
+    {
+      rmiServerHostnameSetByServer = listenAddress.getHostAddress();
+      System.setProperty(RMI_SERVER_HOSTNAME, rmiServerHostnameSetByServer);
+    }
+  }
+
+  private static InetAddress getLocalHostOrNull()
+  {
+    try
+    {
+      return InetAddress.getLocalHost();
+    }
+    catch (UnknownHostException e)
+    {
+      logger.traceException(e);
+      return null;
+    }
+  }
+
   static void configureJmxDeserializationProtection(Map<String, Object> env)
   {
     // Tightly constrain the credentials object exchanged during authentication

--
Gitblit v1.10.0