GP-1 Revised Ghidra Server interface version and compatibility checks.

This commit is contained in:
ghidra1
2026-05-05 13:35:15 -04:00
parent daa0bc243a
commit e18f7bb4e5
4 changed files with 84 additions and 33 deletions
@@ -243,15 +243,15 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
} }
@Override @Override
public void checkCompatibility(int minServerInterfaceVersion) throws RemoteException { public void checkCompatibility(int clientInterfaceVersion) throws RemoteException {
if (minServerInterfaceVersion > INTERFACE_VERSION) { if (clientInterfaceVersion > SERVER_INTERFACE_VERSION) {
throw new RemoteException( throw new RemoteException(
"Incompatible server interface, a newer Ghidra Server version is required."); "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( throw new RemoteException(
"Incompatible server interface, the minimum supported Ghidra version is " + "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( Registry registry = LocateRegistry.createRegistry(
ServerPortFactory.getRMIRegistryPort(), clientSocketFactory, serverSocketFactory); ServerPortFactory.getRMIRegistryPort(), clientSocketFactory, serverSocketFactory);
StringBuilder bindVersions = new StringBuilder(" (");
bindVersions.append(GHIDRA_BIND_VERSION);
registry.bind(BIND_NAME, svr); 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) { catch (Throwable t) {
@@ -199,7 +199,7 @@ public class RepositoryServerAdapter {
lastConnectError = t; lastConnectError = t;
} }
Msg.showError(this, null, "Server Error", 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) { catch (IOException e) {
String err = e.getMessage(); String err = e.getMessage();
@@ -208,8 +208,8 @@ public class RepositoryServerAdapter {
} }
String msg = err != null ? err : e.toString(); String msg = err != null ? err : e.toString();
Msg.showError(this, null, "Server Error", Msg.showError(this, null, "Server Error",
"An error occurred while connecting to the server (" + serverInfoStr + ").\n" + msg, "An error occurred while connecting to the server (" + serverInfoStr + ").\n" +
e); msg);
} }
throw new NotConnectedException("Not connected to repository server", lastConnectError); throw new NotConnectedException("Not connected to repository server", lastConnectError);
} }
@@ -175,7 +175,7 @@ class ServerConnectTask extends Task {
gsh = (GhidraServerHandle) reg.lookup(GhidraServerHandle.BIND_NAME); gsh = (GhidraServerHandle) reg.lookup(GhidraServerHandle.BIND_NAME);
// Check interface compatibility with the minimum supported version // Check interface compatibility with the minimum supported version
gsh.checkCompatibility(GhidraServerHandle.MINIMUM_INTERFACE_VERSION); gsh.checkCompatibility(GhidraServerHandle.MIN_CLIENT_INTERFACE_VERSION);
} }
catch (NotBoundException e) { catch (NotBoundException e) {
throw new IOException(e.getMessage()); 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)) { if (!Application.getApplicationVersion().startsWith(requiredVersion)) {
requiredVersion = requiredVersion + " - " + Application.getApplicationVersion(); requiredVersion = requiredVersion + " - " + Application.getApplicationVersion();
} }
requiredVersion += " (or possibly newer)";
String[] regList = reg.list(); String[] regList = reg.list();
RemoteException exc = null; IOException exc = null;
int badVerCount = 0; int badVerCount = 0;
String version = null;
for (String name : regList) { for (String name : regList) {
if (name.equals(GhidraServerHandle.BIND_NAME)) { if (name.equals(GhidraServerHandle.BIND_NAME)) {
return; // found it return; // found it
} }
else if (name.startsWith(GhidraServerHandle.BIND_NAME_PREFIX)) { 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) { if (version.length() == 0) {
version = "4.3.x (or older)"; version = "4.3.x (or older)";
} }
exc = new RemoteException( exc = new IOException(
"Incompatible Ghidra Server interface, detected interface version " + version + "Incompatible Ghidra Server - detected interface version " + version +
",\nthis client requires server version " + requiredVersion); ".\nThis client requires server version " + requiredVersion);
++badVerCount; ++badVerCount;
} }
} }
@@ -450,11 +453,11 @@ class ServerConnectTask extends Task {
if (badVerCount == 1) { if (badVerCount == 1) {
throw exc; throw exc;
} }
throw new RemoteException("Incompatible Ghidra Server interface, detected " + throw new IOException("Incompatible Ghidra Server - detected " +
badVerCount + " incompatible server versions" + badVerCount + " incompatible server versions." +
",\nthis client requires server version " + requiredVersion); "\nThis client requires server version " + requiredVersion);
} }
throw new RemoteException("Ghidra Server not found."); throw new IOException("Ghidra Server not found.");
} }
} }
@@ -52,23 +52,53 @@ public interface GhidraServerHandle extends Remote {
* unchanged allowing 9.1 clients to connect to 9.0 server. * unchanged allowing 9.1 clients to connect to 9.0 server.
* 12: Revised RepositoryFile serialization to facilitate support for text-data used * 12: Revised RepositoryFile serialization to facilitate support for text-data used
* for link-file storage (12.0). * 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 * The server interface version that the server implements. This corresponds to the maximum
* client can operate with. * 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. * 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:
* <ul>
* <li>Older ghidra client versions can continue using the current server version, while</li>
* <li>Current ghidra clients version cannot use an older version server.</li>
* </ul>
* When this version differs from {@link #MIN_CLIENT_INTERFACE_VERSION} the server will bind two
* both {@link #BIND_NAME} and {@value #ALT_BIND_NAME}.
* <p>
* 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}.
* <p>
* 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 * Default RMI base port for Ghidra Server
@@ -81,13 +111,21 @@ public interface GhidraServerHandle extends Remote {
static final String BIND_NAME_PREFIX = "GhidraServer"; 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. * 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 * @return authentication callbacks which must be satisfied or null if authentication not
* required. * required.
*/ */
@@ -107,11 +145,10 @@ public interface GhidraServerHandle extends Remote {
throws FailedLoginException, RemoteException; throws FailedLoginException, RemoteException;
/** /**
* Check server interface compatibility * Check server interface compatibility with the specified client interface version.
* @param serverInterfaceVersion client/server interface version * @param clientInterfaceVersion client/server interface version
* @throws RemoteException if requested server interface version not available * @throws RemoteException if requested server interface version not available
* @see #INTERFACE_VERSION
*/ */
void checkCompatibility(int serverInterfaceVersion) throws RemoteException; void checkCompatibility(int clientInterfaceVersion) throws RemoteException;
} }