diff --git a/src/main/java/org/metricshub/winrm/light/CipherGen.java b/src/main/java/org/metricshub/winrm/light/CipherGen.java index dbf90c8..413f4fd 100644 --- a/src/main/java/org/metricshub/winrm/light/CipherGen.java +++ b/src/main/java/org/metricshub/winrm/light/CipherGen.java @@ -519,6 +519,10 @@ private static byte[] lmv2Hash(final String domain, final String user, final byt * Responses. */ private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash) throws NtlmException { + // CPD-OFF — near-duplicate of lmv2Hash() by design: both mirror the reference NTLM + // implementation (Apache HttpClient's NTLMEngineImpl), kept line-for-line comparable with + // upstream rather than factored through a shared helper. The one divergence is deliberate + // and easy to miss: LMv2 upper-cases the domain, NTLMv2 keeps its case. if (NTLMEngineUtils.UNICODE_LITTLE_UNMARKED == null) { throw new NtlmException("Unicode not supported"); } @@ -529,6 +533,7 @@ private static byte[] ntlmv2Hash(final String domain, final String user, final b hmacMD5.update(domain.getBytes(NTLMEngineUtils.UNICODE_LITTLE_UNMARKED)); } return hmacMD5.getOutput(); + // CPD-ON } /** diff --git a/src/main/java/org/metricshub/winrm/light/LightWinRMService.java b/src/main/java/org/metricshub/winrm/light/LightWinRMService.java index 458f12c..8086a21 100644 --- a/src/main/java/org/metricshub/winrm/light/LightWinRMService.java +++ b/src/main/java/org/metricshub/winrm/light/LightWinRMService.java @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -167,24 +168,17 @@ public List> executeWql(final String wqlQuery, final long ti // Enforce the caller's timeout as a wall-clock deadline (throwing TimeoutException), matching // the CXF WinRMService and bounding the WSMan Pull loop. - try { - return Utils.execute( - () -> { - final List> rows = client.wql(winRMEndpoint.getNamespace(), wqlQuery); - final List> result = new ArrayList<>(rows.size()); - for (final Map row : rows) { - result.add(new LinkedHashMap<>(row)); - } - return result; - }, - timeout - ); - } catch (final InterruptedException | ExecutionException e) { - if (e.getCause() != null) { - throw new WinRMException(e.getCause(), e.getCause().getMessage()); - } - throw new WinRMException(e); - } + return executeWithTimeout( + () -> { + final List> rows = client.wql(winRMEndpoint.getNamespace(), wqlQuery); + final List> result = new ArrayList<>(rows.size()); + for (final Map row : rows) { + result.add(new LinkedHashMap<>(row)); + } + return result; + }, + timeout + ); } @Override @@ -200,16 +194,34 @@ public WindowsRemoteCommandResult executeCommand( // Enforce the caller's timeout as a wall-clock deadline (throwing TimeoutException), matching // the CXF WinRMService and bounding the WSMan Receive loop. + return executeWithTimeout( + () -> { + final long start = Utils.getCurrentTimeMillis(); + final WsmanClient.CommandOutput output = client.executeCommand(command, workingDirectory, charset); + final float executionTime = (Utils.getCurrentTimeMillis() - start) / 1000.0f; + return new WindowsRemoteCommandResult(output.stdout, output.stderr, executionTime, output.exitCode); + }, + timeout + ); + } + + /** + * Run the task through {@link Utils#execute(Callable, long)} under the caller's wall-clock + * timeout, converting the executor's checked exceptions into {@link WinRMException} — the + * task's own failure is unwrapped from {@link ExecutionException} so the caller sees the real + * cause and its message. + * + * @param task the operation to run + * @param timeout timeout in milliseconds + * @param the task's result type + * @return the task's result + * @throws TimeoutException when the deadline elapses first + * @throws WinRMException when the task fails or the wait is interrupted + */ + private static T executeWithTimeout(final Callable task, final long timeout) + throws TimeoutException, WinRMException { try { - return Utils.execute( - () -> { - final long start = Utils.getCurrentTimeMillis(); - final WsmanClient.CommandOutput output = client.executeCommand(command, workingDirectory, charset); - final float executionTime = (Utils.getCurrentTimeMillis() - start) / 1000.0f; - return new WindowsRemoteCommandResult(output.stdout, output.stderr, executionTime, output.exitCode); - }, - timeout - ); + return Utils.execute(task, timeout); } catch (final InterruptedException | ExecutionException e) { if (e.getCause() != null) { throw new WinRMException(e.getCause(), e.getCause().getMessage());