jscanner

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 987b857c6dd725ea2f675dee5c42a8157ce6a06a
parent 5b4e1909c7f0f3f6398448b438a8c22ef61b4ae0
Author: Matthias Jaros <jaros@mailbox.org>
Date:   Mon,  6 Jul 2026 15:42:44 +0200

Reworked exception handling. Collect now only weird exceptions and print
them before results to stderr

Diffstat:
Asrc/main/java/com/it_jaros/jscanner/ExceptionInfo.java | 14++++++++++++++
Msrc/main/java/com/it_jaros/jscanner/PortResult.java | 11+++++++++++
Msrc/main/java/com/it_jaros/jscanner/ProgressBar.java | 23++++++++++++++++++++++-
Asrc/main/java/com/it_jaros/jscanner/ScanFailure.java | 7+++++++
Msrc/main/java/com/it_jaros/jscanner/ScanResult.java | 3++-
Msrc/main/java/com/it_jaros/jscanner/Scanner.java | 25++++++++++++++-----------
6 files changed, 70 insertions(+), 13 deletions(-)

diff --git a/src/main/java/com/it_jaros/jscanner/ExceptionInfo.java b/src/main/java/com/it_jaros/jscanner/ExceptionInfo.java @@ -0,0 +1,13 @@ +package com.it_jaros.jscanner; + +public record ExceptionInfo( + String type, + String message +) { + public static ExceptionInfo from(Throwable t) { + return new ExceptionInfo( + t.getClass().getName(), + t.getMessage() + ); + } +} +\ No newline at end of file diff --git a/src/main/java/com/it_jaros/jscanner/PortResult.java b/src/main/java/com/it_jaros/jscanner/PortResult.java @@ -1,9 +1,12 @@ package com.it_jaros.jscanner; +import java.io.IOException; + public class PortResult { private int port; private PortState state; private String banner; + private Exception exception; public int getPort() { return port; @@ -28,4 +31,12 @@ public class PortResult { public void setBanner(String banner) { this.banner = banner; } + + public void setException(IOException e) { + exception = e; + } + + public Exception getException() { + return exception; + } } diff --git a/src/main/java/com/it_jaros/jscanner/ProgressBar.java b/src/main/java/com/it_jaros/jscanner/ProgressBar.java @@ -30,15 +30,36 @@ public class ProgressBar { public void printResult(ScanResult result, boolean showFilteredPorts) { synchronized (outputLock) { - if (result.openPorts().isEmpty() && (!showFilteredPorts || result.filteredPorts().isEmpty())) { + if (result.openPorts().isEmpty() + && (!showFilteredPorts || result.filteredPorts().isEmpty()) + && result.errors().isEmpty()) { return; } clearStatusLine(); + printErrors(result); printPortsResults(result, showFilteredPorts); } } + private void printErrors(ScanResult result) { + System.err.printf( + "error: host scan failed | host=%s | errors: %d%n", + result.host(), + result.errors().size() + ); + for (ScanFailure error : result.errors()) { + System.err.printf( + "port: %s: %s %s%n", + error.port(), + error.exception().type(), + error.exception().message() + ); + } + + printStatusLine(scan); + } + private void printPortsResults(ScanResult result, boolean showFilteredPorts) { if (result.openPorts().isEmpty() && (!showFilteredPorts || result.filteredPorts().isEmpty())) { return; diff --git a/src/main/java/com/it_jaros/jscanner/ScanFailure.java b/src/main/java/com/it_jaros/jscanner/ScanFailure.java @@ -0,0 +1,7 @@ +package com.it_jaros.jscanner; + +public record ScanFailure( + Integer port, + ExceptionInfo exception +) { +} diff --git a/src/main/java/com/it_jaros/jscanner/ScanResult.java b/src/main/java/com/it_jaros/jscanner/ScanResult.java @@ -1,6 +1,7 @@ package com.it_jaros.jscanner; import java.util.BitSet; +import java.util.List; public record ScanResult(String host, BitSet openPorts, BitSet filteredPorts, - java.util.HashMap<Integer, ServiceType> bannerRecognition) {} + java.util.HashMap<Integer, ServiceType> bannerRecognition, List<ScanFailure> errors) {} diff --git a/src/main/java/com/it_jaros/jscanner/Scanner.java b/src/main/java/com/it_jaros/jscanner/Scanner.java @@ -107,7 +107,7 @@ public class Scanner implements AutoCloseable { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { - System.out.printf("%s -> %s%n", e.getClass().getSimpleName(), e.getMessage()); + System.err.printf("%s -> %s%n", e.getClass().getSimpleName(), e.getMessage()); } } @@ -129,10 +129,10 @@ public class Scanner implements AutoCloseable { if (!disableOnlineCheck && !checkHostOnline(host)) { // Unreachable host - return new ScanResult(host, openPorts, filteredPorts, serviceTypes); + return new ScanResult(host, openPorts, filteredPorts, serviceTypes, List.of()); } - List<Throwable> errors = new ArrayList<>(); + List<ScanFailure> scanFailures = new ArrayList<>(); final AtomicLong portSlotFactory = new AtomicLong(System.nanoTime()); // producer thread ProducerState<PortResult> state = startProducer( @@ -173,6 +173,13 @@ public class Scanner implements AutoCloseable { // sonarcube glücklich machen } } + Exception e = portResult.getException(); + if (e != null) { + scanFailures.add(new ScanFailure(portResult.getPort(), ExceptionInfo.from(e))); + } + } catch (ExecutionException e) { + // something more serious did not work + System.err.printf("%s -> %s%n", e.getClass().getSimpleName(), e.getMessage()); } finally { scan.getThreadCounter().dec(); state.activeWorkers().release(); @@ -180,16 +187,10 @@ public class Scanner implements AutoCloseable { } } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - errors.add(e); } } - if (!errors.isEmpty()) { - System.out.printf("Errors happened during scan of host %s%nErrors:%s -> %s", host, errors.size(), errors); - } - - return new ScanResult(host, openPorts, filteredPorts, serviceTypes); + return new ScanResult(host, openPorts, filteredPorts, serviceTypes, scanFailures); } /** @@ -295,9 +296,11 @@ public class Scanner implements AutoCloseable { result.setState(PortState.FILTERED); } catch (ConnectException ignored) { result.setState(PortState.CLOSED); - } catch (IOException ignored) { + } catch (NoRouteToHostException ignored) { // NoRouteToHostException: this can be safely ignored because the port is closed if a host is unreachable // Will happen a lot when scanning for open ports, so not needed + } catch (IOException e) { + result.setException(e); } finally { scan.getSocketCounter().dec(); socketLimit.release();