From 2624f889d5adadc592c015e31deeb32a8d0e3a23 Mon Sep 17 00:00:00 2001 From: Matthias Jaros Date: Thu, 2 Jul 2026 11:29:17 +0200 Subject: Added new Class ScanState in order to make Scanner stateless during scan process. Not regarding config which is given to the constructor --- src/main/java/com/it_jaros/jscanner/Scanner.java | 58 ++++++++++++++---------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'src/main/java/com/it_jaros/jscanner/Scanner.java') diff --git a/src/main/java/com/it_jaros/jscanner/Scanner.java b/src/main/java/com/it_jaros/jscanner/Scanner.java index 4d951f5..5a6ccbb 100644 --- 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; 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 scanHosts(Stream 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 completionService = new ExecutorCompletionService<>(executor); Queue 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 f = completionService.poll(10, TimeUnit.MILLISECONDS); - if (f == null) { + Future 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(); } -- cgit v1.3.1