From a8f541436eb175d569c73b72319cd3fd2683388f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Apr 2026 18:09:06 +0200 Subject: [PATCH] Client: after STARTTLS claimed success, verify it by an innocent query [networkupstools/nut#3387] Signed-off-by: Jim Klimov --- .../java/org/networkupstools/jnut/Client.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/jNut/src/main/java/org/networkupstools/jnut/Client.java b/jNut/src/main/java/org/networkupstools/jnut/Client.java index f6d8a1c..f0dc912 100644 --- a/jNut/src/main/java/org/networkupstools/jnut/Client.java +++ b/jNut/src/main/java/org/networkupstools/jnut/Client.java @@ -283,11 +283,59 @@ public void connect() throws IOException, UnknownHostException, NutException } else if (sslConfig.isForceSSL()) { throw new NutException("STARTTLS-FAILED", "Server does not support SSL but it is required"); } + + // Make sure handshake succeeded or abort early + // (there is currently no way for the server to + // report its fault to the client when connection + // is half-way secure): + if (!isValidProtocolVersion()) { + if (sslConfig.isForceSSL()) { + throw new NutException("STARTTLS-FAILED", "SSL setup failed but it is required"); + } + } } authenticate(); } + /** Query the (already established) connection to UPSD for its version. + * @return true if it is an "X(.Y)" number, false otherwise. + * @throws IOException + * @throws NutException + */ + public boolean isValidProtocolVersion() throws IOException, NutException + { + return isValidProtocolVersion(null); + } + + /** Query the (already established) connection to UPSD for its version + * and check it against given expectations. + * @param regex Regular expression to match the version against (defaults + * to an "X(.Y)" number "^\\d+(?:\\.\\d+)?$" if NULL). + * @return true if it matches "regex", false otherwise. + * @throws IOException + * @throws NutException + */ + public boolean isValidProtocolVersion(String regex) throws IOException, NutException + { + String res; + + try { + res = query("PROTVER"); + } catch (NutException e) { + // Deprecated and hidden, but may be what ancient NUT servers say + // May throw if the error is due to (non-)connection + res = query("NETVER"); + } + + if (regex == null) { + // Is it an X(.Y) number? + regex = "^\\d+(?:\\.\\d+)?$"; + } + + return res.matches(regex); + } + /** * Intend to authenticate with a specified login and password, overriding * already defined ones (remembers them as class instance fields).