jscanner

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

commit a17a156f18e86a79572fee147f2237f6ef8202b2
parent 05a47b70919628cf439751ea5bad9fb955a9c065
Author: Matthias Jaros <jaros@mailbox.org>
Date:   Thu,  2 Jul 2026 11:29:17 +0200

Added new Class ScanState in order to make Scanner
stateless during scan process. Not regarding config
which is given to the constructor

Diffstat:
Msrc/main/java/com/it_jaros/jscanner/ProgressBar.java | 15+++++++++------
Asrc/main/java/com/it_jaros/jscanner/ScanState.java | 5+++++
Msrc/main/java/com/it_jaros/jscanner/Scanner.java | 58+++++++++++++++++++++++++++++++++-------------------------
3 files changed, 47 insertions(+), 31 deletions(-)

diff --git a/src/main/java/com/it_jaros/jscanner/ProgressBar.java b/src/main/java/com/it_jaros/jscanner/ProgressBar.java @@ -15,11 +15,15 @@ import java.util.regex.Pattern; public class ProgressBar { - private final ScheduledExecutorService ui = Executors.newSingleThreadScheduledExecutor(); + private List<Progress> hosts = new ArrayList<>(); private final ReentrantLock lock = new ReentrantLock(); + private final ScheduledExecutorService ui = Executors.newSingleThreadScheduledExecutor(); + private final boolean quiet; private final int columnWidth = ProgressBar.getColumnWidth(); - private List<Progress> hosts; + public ProgressBar(boolean quiet) { + this.quiet = quiet; + } private static int getColumnWidth() { try { @@ -42,10 +46,9 @@ public class ProgressBar { return 120; } - public void start(boolean quiet) { - this.hosts = new ArrayList<>(); - System.out.print("Scanning targets...\n\n"); + public void start() { if (quiet) { return; } + System.out.print("Scanning targets...\n\n"); ui.scheduleAtFixedRate(() -> { lock.lock(); drawProgress(false); @@ -106,7 +109,7 @@ public class ProgressBar { return String.format("%s%s%s %s %s", label, " ".repeat(spacesBetween),statsLeft, brackets, statsRight); } - public void stop(boolean quiet) { + public void stop() { ui.shutdown(); try { ui.awaitTermination(10, TimeUnit.SECONDS); diff --git a/src/main/java/com/it_jaros/jscanner/ScanState.java b/src/main/java/com/it_jaros/jscanner/ScanState.java @@ -0,0 +1,5 @@ +package com.it_jaros.jscanner; + +import java.util.List; + +public record ScanState(Counter counter, ProgressBar progressBar, List<ScanResult> scanResult) {} diff --git a/src/main/java/com/it_jaros/jscanner/Scanner.java b/src/main/java/com/it_jaros/jscanner/Scanner.java @@ -21,11 +21,8 @@ public class Scanner implements AutoCloseable { private static final int READ_BUFFER_SIZE = 1024; - private ArrayList<ScanResult> scanResult; private volatile boolean cancelled = false; - private final Counter counter = new Counter(); - private final ProgressBar progressBar = new ProgressBar(); private final Semaphore socketLimit; private final boolean disableOnlineCheck; private final boolean quiet; @@ -34,14 +31,22 @@ public class Scanner implements AutoCloseable { private final int timeoutInMillis; private final long delayInNanos; - public Scanner(int socketLimit, int timeoutInMillis, int delayInMillis, int maxWorkersPerHost, int maxHostsLimit, boolean disableOnlineCheck, boolean quiet) { - this.timeoutInMillis = timeoutInMillis; + public Scanner( + int socketLimit, + int timeoutInMillis, + int delayInMillis, + int maxWorkersPerHost, + int maxHostsLimit, + boolean disableOnlineCheck, + boolean quiet + ) { this.delayInNanos = TimeUnit.MILLISECONDS.toNanos(Math.max(0, delayInMillis)); - this.socketLimit = new Semaphore(socketLimit); - this.maxWorkersPerHost = maxWorkersPerHost; - this.maxHostsLimit = maxHostsLimit; this.disableOnlineCheck = disableOnlineCheck; + this.maxHostsLimit = maxHostsLimit; + this.maxWorkersPerHost = maxWorkersPerHost; this.quiet = quiet; + this.socketLimit = new Semaphore(socketLimit); + this.timeoutInMillis = timeoutInMillis; } public Scanner(ScanOptions options) { @@ -78,8 +83,8 @@ public class Scanner implements AutoCloseable { } public List<ScanResult> scanHosts(Stream<String> hosts, String ports) throws IOException { - this.scanResult = new ArrayList<>(); - progressBar.start(quiet); + ScanState scanState = new ScanState(new Counter(), new ProgressBar(quiet), new ArrayList<>()); + scanState.progressBar().start(); try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { CompletionService<ScanResult> completionService = new ExecutorCompletionService<>(executor); Queue<String> hostsInQueue = hosts.collect(Collectors.toCollection(ArrayDeque::new)); @@ -91,7 +96,7 @@ public class Scanner implements AutoCloseable { while (!hostsInQueue.isEmpty() && activeHostWorkers <= maxHostsLimit && !cancelled) { activeHostWorkers++; final String host = hostsInQueue.poll(); - completionService.submit(() -> scanHost(host, ports)); + completionService.submit(() -> scanHost(host, ports, scanState)); } try { @@ -101,7 +106,7 @@ public class Scanner implements AutoCloseable { } activeHostWorkers--; hostsToProcess--; - this.scanResult.add(finishedHost.get()); + scanState.scanResult().add(finishedHost.get()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { @@ -114,16 +119,16 @@ public class Scanner implements AutoCloseable { } } - progressBar.stop(quiet); + scanState.progressBar().stop(); System.out.println("--------------------"); System.out.println("Finished scan!"); System.out.println("Stats:"); - System.out.println("Peak concurrent connects: " + counter.max()); + System.out.println("Peak concurrent connects: " + scanState.counter().max()); System.out.println("--------------------"); - return scanResult; + return scanState.scanResult(); } - private ScanResult scanHost(String host, String ports) { + private ScanResult scanHost(String host, String ports, ScanState scanState) { PortRange portRange = new PortRange(ports); AtomicLong portSlotFactory = new AtomicLong(System.nanoTime()); BitSet openPorts = new BitSet(PortRange.MAX_PORT); @@ -132,7 +137,7 @@ public class Scanner implements AutoCloseable { // Give progressbar the current progress object which is then updated in the sub virtual threads Progress progress = new Progress(host, portRange.getTotal(), new AtomicInteger(), new AtomicInteger(), new AtomicInteger()); - progressBar.submit(progress); + scanState.progressBar().submit(progress); if (!disableOnlineCheck && !checkHostOnline(host)) { // Visually show that this host is basically done @@ -151,13 +156,13 @@ public class Scanner implements AutoCloseable { final int currentPort = portRange.next(); completionService.submit(() -> { waitForSlot(portSlotFactory); - return checkPort(host, currentPort); + return checkPort(host, currentPort, scanState); }); } try { - Future<PortResult> f = completionService.poll(10, TimeUnit.MILLISECONDS); - if (f == null) { + Future<PortResult> portResultFuture = completionService.poll(10, TimeUnit.MILLISECONDS); + if (portResultFuture == null) { continue; } activePerHostWorkers--; @@ -166,7 +171,7 @@ public class Scanner implements AutoCloseable { // bitset is not thread-safe, so it is set // outside the other virtual threads that update progress - PortResult portResult = f.get(); + PortResult portResult = portResultFuture.get(); switch (portResult.getState()) { case OPEN -> { openPorts.set(portResult.getPort()); @@ -212,12 +217,15 @@ public class Scanner implements AutoCloseable { } } - private PortResult checkPort(String host, int port) { - socketLimit.acquireUninterruptibly(); - counter.inc(); + private PortResult checkPort(String host, int port, ScanState scanState) { PortResult result = new PortResult(); result.setPort(port); result.setState(PortState.UNKNOWN); + + // don't allow more sockets then specified + socketLimit.acquireUninterruptibly(); + // count how many ports are concurrently checked + scanState.counter().inc(); try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(host, port), timeoutInMillis); result.setBanner(getBanner(socket)); @@ -230,7 +238,7 @@ public class Scanner implements AutoCloseable { // 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 } finally { - counter.dec(); + scanState.counter().dec(); socketLimit.release(); }