From e18f7bb4e5ce243bdc8b9471108f1990c6dbaca6 Mon Sep 17 00:00:00 2001 From: ghidra1 Date: Tue, 5 May 2026 13:35:15 -0400 Subject: [PATCH] GP-1 Revised Ghidra Server interface version and compatibility checks. --- .../ghidra/server/remote/GhidraServer.java | 21 +++++-- .../client/RepositoryServerAdapter.java | 6 +- .../framework/client/ServerConnectTask.java | 27 ++++---- .../framework/remote/GhidraServerHandle.java | 63 +++++++++++++++---- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java index 927caf738e..f788c33b9e 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java @@ -243,15 +243,15 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan } @Override - public void checkCompatibility(int minServerInterfaceVersion) throws RemoteException { - if (minServerInterfaceVersion > INTERFACE_VERSION) { + public void checkCompatibility(int clientInterfaceVersion) throws RemoteException { + if (clientInterfaceVersion > SERVER_INTERFACE_VERSION) { throw new RemoteException( "Incompatible server interface, a newer Ghidra Server version is required."); } - else if (minServerInterfaceVersion < MINIMUM_INTERFACE_VERSION) { + else if (clientInterfaceVersion < SERVER_MIN_CLIENT_INTERFACE_VERSION) { throw new RemoteException( "Incompatible server interface, the minimum supported Ghidra version is " + - MIN_GHIDRA_VERSION); + ALT_GHIDRA_BIND_VERSION); } } @@ -871,9 +871,20 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan Registry registry = LocateRegistry.createRegistry( ServerPortFactory.getRMIRegistryPort(), clientSocketFactory, serverSocketFactory); + + StringBuilder bindVersions = new StringBuilder(" ("); + bindVersions.append(GHIDRA_BIND_VERSION); registry.bind(BIND_NAME, svr); - log.info("Registered Ghidra Server."); + if (!BIND_NAME.equals(ALT_BIND_NAME)) { + // Include alternate binding in support of older Ghidra client versions + bindVersions.append(", "); + bindVersions.append(ALT_GHIDRA_BIND_VERSION); + registry.bind(ALT_BIND_NAME, svr); + } + bindVersions.append(")"); + + log.info("Registered Ghidra Server" + bindVersions); } catch (Throwable t) { diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/RepositoryServerAdapter.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/RepositoryServerAdapter.java index 30c7471d8c..963dc5fdd4 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/RepositoryServerAdapter.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/RepositoryServerAdapter.java @@ -199,7 +199,7 @@ public class RepositoryServerAdapter { lastConnectError = t; } Msg.showError(this, null, "Server Error", - "An error occurred on the server (" + serverInfoStr + ").\n" + msg, e); + "An error occurred on the server (" + serverInfoStr + ").\n" + msg); } catch (IOException e) { String err = e.getMessage(); @@ -208,8 +208,8 @@ public class RepositoryServerAdapter { } String msg = err != null ? err : e.toString(); Msg.showError(this, null, "Server Error", - "An error occurred while connecting to the server (" + serverInfoStr + ").\n" + msg, - e); + "An error occurred while connecting to the server (" + serverInfoStr + ").\n" + + msg); } throw new NotConnectedException("Not connected to repository server", lastConnectError); } diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/ServerConnectTask.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/ServerConnectTask.java index a7353c6864..87de89dc07 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/ServerConnectTask.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/client/ServerConnectTask.java @@ -175,7 +175,7 @@ class ServerConnectTask extends Task { gsh = (GhidraServerHandle) reg.lookup(GhidraServerHandle.BIND_NAME); // Check interface compatibility with the minimum supported version - gsh.checkCompatibility(GhidraServerHandle.MINIMUM_INTERFACE_VERSION); + gsh.checkCompatibility(GhidraServerHandle.MIN_CLIENT_INTERFACE_VERSION); } catch (NotBoundException e) { throw new IOException(e.getMessage()); @@ -420,29 +420,32 @@ class ServerConnectTask extends Task { } } - private static void checkServerBindNames(Registry reg) throws RemoteException { + private static void checkServerBindNames(Registry reg) throws IOException { - String requiredVersion = GhidraServerHandle.MIN_GHIDRA_VERSION; + String requiredVersion = GhidraServerHandle.GHIDRA_BIND_VERSION; if (!Application.getApplicationVersion().startsWith(requiredVersion)) { requiredVersion = requiredVersion + " - " + Application.getApplicationVersion(); } + requiredVersion += " (or possibly newer)"; String[] regList = reg.list(); - RemoteException exc = null; + IOException exc = null; int badVerCount = 0; + String version = null; for (String name : regList) { if (name.equals(GhidraServerHandle.BIND_NAME)) { return; // found it } else if (name.startsWith(GhidraServerHandle.BIND_NAME_PREFIX)) { - String version = name.substring(GhidraServerHandle.BIND_NAME_PREFIX.length()); + // NOTE: We only report one version even if server has multiple bindings + version = name.substring(GhidraServerHandle.BIND_NAME_PREFIX.length()); if (version.length() == 0) { version = "4.3.x (or older)"; } - exc = new RemoteException( - "Incompatible Ghidra Server interface, detected interface version " + version + - ",\nthis client requires server version " + requiredVersion); + exc = new IOException( + "Incompatible Ghidra Server - detected interface version " + version + + ".\nThis client requires server version " + requiredVersion); ++badVerCount; } } @@ -450,11 +453,11 @@ class ServerConnectTask extends Task { if (badVerCount == 1) { throw exc; } - throw new RemoteException("Incompatible Ghidra Server interface, detected " + - badVerCount + " incompatible server versions" + - ",\nthis client requires server version " + requiredVersion); + throw new IOException("Incompatible Ghidra Server - detected " + + badVerCount + " incompatible server versions." + + "\nThis client requires server version " + requiredVersion); } - throw new RemoteException("Ghidra Server not found."); + throw new IOException("Ghidra Server not found."); } } diff --git a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/remote/GhidraServerHandle.java b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/remote/GhidraServerHandle.java index f1e6e0c10b..b2672867db 100644 --- a/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/remote/GhidraServerHandle.java +++ b/Ghidra/Framework/FileSystem/src/main/java/ghidra/framework/remote/GhidraServerHandle.java @@ -52,23 +52,53 @@ public interface GhidraServerHandle extends Remote { * unchanged allowing 9.1 clients to connect to 9.0 server. * 12: Revised RepositoryFile serialization to facilitate support for text-data used * for link-file storage (12.0). + * 13: Client-side serialization filters have been implemented and server-side thrown + * exceptions reduced to be compliant with client-side filters. Server still + * supports older clients back to interface version 11. Server may now BIND + * to the RMI registery with two different names if needed. */ /** - * The server interface version that the server will use and is the maximum version that the - * client can operate with. + * The server interface version that the server implements. This corresponds to the maximum + * supported client interface version. */ - public static final int INTERFACE_VERSION = 12; + public static final int SERVER_INTERFACE_VERSION = 13; /** * The minimum server interface version that the client can operate with. */ - public static final int MINIMUM_INTERFACE_VERSION = 11; + public static final int MIN_CLIENT_INTERFACE_VERSION = 13; /** - * Minimum version of Ghidra which utilized the current INTERFACE_VERSION + * The minimum interface version that the server will support for older client versions. + * When this version is less than {@link #MIN_CLIENT_INTERFACE_VERSION} it allows the following: + * + * When this version differs from {@link #MIN_CLIENT_INTERFACE_VERSION} the server will bind two + * both {@link #BIND_NAME} and {@value #ALT_BIND_NAME}. + *

+ * NOTE: It is important that the server authentication interface not be modified between this + * version and {@value #MIN_CLIENT_INTERFACE_VERSION}. */ - public static final String MIN_GHIDRA_VERSION = "9.0"; + public static final int SERVER_MIN_CLIENT_INTERFACE_VERSION = 11; + + /** + * The server BIND version which the Ghidra client can communicate with. + * This corresponds to {@value #MIN_CLIENT_INTERFACE_VERSION}. + */ + public static final String GHIDRA_BIND_VERSION = "12.0.5"; + + /** + * Minimum version of a Ghidra client release which can communicate with the current + * Ghidra Server. This corresponds to {@value #SERVER_MIN_CLIENT_INTERFACE_VERSION} + * and {@link #ALT_BIND_NAME}. + *

+ * This version is used only by the server only in publishing an alternate BIND name and + * identifies the oldest Ghidra client version that may connect. + */ + public static final String ALT_GHIDRA_BIND_VERSION = "9.0"; /** * Default RMI base port for Ghidra Server @@ -81,13 +111,21 @@ public interface GhidraServerHandle extends Remote { static final String BIND_NAME_PREFIX = "GhidraServer"; /** - * RMI registry binding name for the supported version of the remote GhidraServerHandle object. + * Primary RMI registry binding name for the remote GhidraServerHandle object. + * This BIND name is used by both the server and client. */ - static final String BIND_NAME = BIND_NAME_PREFIX + MIN_GHIDRA_VERSION; + static final String BIND_NAME = BIND_NAME_PREFIX + GHIDRA_BIND_VERSION; + + /** + * Alternate RMI registry binding name for the remote GhidraServerHandle object. + * This alternate BIND name is used only by the server in support of older Ghidra clients + * and corresponds to {@link #SERVER_MIN_CLIENT_INTERFACE_VERSION}. + */ + static final String ALT_BIND_NAME = BIND_NAME_PREFIX + ALT_GHIDRA_BIND_VERSION; /** * Returns user authentication proxy object. - * @throws RemoteException + * @throws RemoteException if failure occurs while generating authentication callbacks * @return authentication callbacks which must be satisfied or null if authentication not * required. */ @@ -107,11 +145,10 @@ public interface GhidraServerHandle extends Remote { throws FailedLoginException, RemoteException; /** - * Check server interface compatibility - * @param serverInterfaceVersion client/server interface version + * Check server interface compatibility with the specified client interface version. + * @param clientInterfaceVersion client/server interface version * @throws RemoteException if requested server interface version not available - * @see #INTERFACE_VERSION */ - void checkCompatibility(int serverInterfaceVersion) throws RemoteException; + void checkCompatibility(int clientInterfaceVersion) throws RemoteException; }