jscanner

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

commit 05cc7ca4b808686a34ddd1bc359745130452cc96
parent 4b74b6c6ea4e1ac2675fed1087df26a005f417dc
Author: Matthias Jaros <jaros@mailbox.org>
Date:   Mon, 23 Mar 2026 12:30:57 +0100

Added feature to distinguish between blocked by firewall and open ports

Diffstat:
Msrc/main/java/com/it_jaros/network_scanner/App.java | 21+++++++++++++++------
Asrc/main/java/com/it_jaros/network_scanner/PortResult.java | 4++++
Asrc/main/java/com/it_jaros/network_scanner/PortState.java | 7+++++++
Msrc/main/java/com/it_jaros/network_scanner/Progress.java | 4++--
Msrc/main/java/com/it_jaros/network_scanner/ProgressBar.java | 5+++--
Msrc/main/java/com/it_jaros/network_scanner/ScanResult.java | 2+-
Msrc/main/java/com/it_jaros/network_scanner/Scanner.java | 61+++++++++++++++++++++++++++++--------------------------------
7 files changed, 61 insertions(+), 43 deletions(-)

diff --git a/src/main/java/com/it_jaros/network_scanner/App.java b/src/main/java/com/it_jaros/network_scanner/App.java @@ -1,5 +1,6 @@ package com.it_jaros.network_scanner; +import java.util.BitSet; import java.util.List; import java.util.stream.Collectors; @@ -24,13 +25,21 @@ public class App { } private static void printResult(ScanResult result) { - String ports = result.openPorts().stream().mapToObj(String::valueOf).collect(Collectors.joining(",")); - String output = "[" + result.target() + "]:"; - if (ports.isEmpty()) { + if (result.openPorts().isEmpty() && result.blockedPorts().isEmpty()) { return; - } else { - output = output + ports; } - System.out.print(output + "\n"); + + System.out.println(result.target()); + if (!result.openPorts().isEmpty()) { + System.out.println("\topen:\t" + map(result.openPorts())); + } + if (!result.blockedPorts().isEmpty()) { + System.out.println("\tblocked:\t" + map(result.blockedPorts())); + } + System.out.println(); + } + + private static String map(BitSet ports) { + return ports.stream().mapToObj(String::valueOf).collect(Collectors.joining(",")); } } diff --git a/src/main/java/com/it_jaros/network_scanner/PortResult.java b/src/main/java/com/it_jaros/network_scanner/PortResult.java @@ -0,0 +1,4 @@ +package com.it_jaros.network_scanner; + +public record PortResult(int port, PortState state) { +} diff --git a/src/main/java/com/it_jaros/network_scanner/PortState.java b/src/main/java/com/it_jaros/network_scanner/PortState.java @@ -0,0 +1,7 @@ +package com.it_jaros.network_scanner; + +public enum PortState { + OPEN, + CLOSED, + BLOCKED +} diff --git a/src/main/java/com/it_jaros/network_scanner/Progress.java b/src/main/java/com/it_jaros/network_scanner/Progress.java @@ -2,4 +2,4 @@ package com.it_jaros.network_scanner; import java.util.concurrent.atomic.AtomicInteger; -public record Progress(String host, int total, AtomicInteger done, AtomicInteger open) {} -\ No newline at end of file +public record Progress(String host, int total, AtomicInteger done, AtomicInteger open, AtomicInteger blocked) {} +\ No newline at end of file diff --git a/src/main/java/com/it_jaros/network_scanner/ProgressBar.java b/src/main/java/com/it_jaros/network_scanner/ProgressBar.java @@ -59,11 +59,12 @@ public class ProgressBar { System.out.println(createBar( progress.host(), progress.open().get(), + progress.blocked().get(), progress.done().get(), progress.total())); } - public String createBar(String label, int open, int done, int total) { + public String createBar(String label, int open, int blocked, int done, int total) { int width = 30; double pct = total == 0 ? 1.0 : (done / (double) total); int filled = (int) (pct * width); @@ -71,7 +72,7 @@ public class ProgressBar { String brackets = "[" + "#".repeat(filled) + "-".repeat(width - filled) + "]"; int percent = (int) (pct * 100); - return String.format("%-40s open=%s %s %3d%% (%d/%d)", label, open, brackets, percent, done, total); + return String.format("%-40s open: %s blocked: %s %s %3d%% (%d/%d)", label, open, blocked, brackets, percent, done, total); } public void stop() { diff --git a/src/main/java/com/it_jaros/network_scanner/ScanResult.java b/src/main/java/com/it_jaros/network_scanner/ScanResult.java @@ -2,4 +2,4 @@ package com.it_jaros.network_scanner; import java.util.BitSet; -public record ScanResult(String target, BitSet openPorts) {} +public record ScanResult(String target, BitSet openPorts, BitSet blockedPorts) {} diff --git a/src/main/java/com/it_jaros/network_scanner/Scanner.java b/src/main/java/com/it_jaros/network_scanner/Scanner.java @@ -1,19 +1,11 @@ package com.it_jaros.network_scanner; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.SocketException; -import java.net.SocketTimeoutException; +import java.net.*; import java.util.ArrayList; import java.util.BitSet; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.LockSupport; @@ -69,7 +61,11 @@ public class Scanner { } 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("--------------------"); return results; } @@ -78,23 +74,26 @@ public class Scanner { AtomicLong portSlotFactory = new AtomicLong(System.nanoTime()); Semaphore maxWorkers = new Semaphore(maxWorkersPerHost); BitSet openPorts = new BitSet(PortRange.MAX_PORT); + BitSet blockedPorts = new BitSet(PortRange.MAX_PORT); - Progress progress = new Progress(target, portRange.getTotal(), new AtomicInteger(), new AtomicInteger()); + Progress progress = new Progress(target, portRange.getTotal(), new AtomicInteger(), new AtomicInteger(), new AtomicInteger() ); progressBar.submit(progress); try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { - List<Future<Integer>> futures = new ArrayList<>(); + List<Future<PortResult>> futures = new ArrayList<>(); while (portRange.hasNext()) { maxWorkers.acquireUninterruptibly(); final int currentPort = portRange.next(); futures.add(executor.submit(() -> { try { waitForSlot(portSlotFactory); - int port = scanPort(target, currentPort); - if (port != -1) { + PortState state = getPortState(target, currentPort); + if (state.equals(PortState.OPEN)) { progress.open().incrementAndGet(); + } else if (state.equals(PortState.BLOCKED)) { + progress.blocked().incrementAndGet(); } - return port; + return new PortResult(currentPort, state); } finally { maxWorkers.release(); progress.done().incrementAndGet(); @@ -103,12 +102,12 @@ public class Scanner { } List<Throwable> errors = new ArrayList<>(); - for (Future<Integer> f : futures) { + for (Future<PortResult> f : futures) { try { - int port = f.get(); - if (port != -1) { - // openPorts is not threadsafe, so we do not update it outside the worker threads - openPorts.set(port); + PortResult portResult = f.get(); + switch (portResult.state()) { + case OPEN -> openPorts.set(portResult.port()); + case BLOCKED -> blockedPorts.set(portResult.port()); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -122,11 +121,7 @@ public class Scanner { } } - return new ScanResult(target, openPorts); - } - - private int scanPort(String target, final int currentPort) { - return isTargetPortOpen(target, currentPort) ? currentPort : -1; + return new ScanResult(target, openPorts, blockedPorts); } private void waitForSlot(AtomicLong scanSlotFactory) { @@ -138,21 +133,23 @@ public class Scanner { } } - private boolean isTargetPortOpen(String target, int port) { + private PortState getPortState(String target, int port) { socketLimit.acquireUninterruptibly(); counter.inc(); try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(target, port), timeoutInMillis); - return true; - } catch (SocketException | SocketTimeoutException ignored) { + return PortState.OPEN; + } catch (NoRouteToHostException ignored) { + // this can be safely ignored because the port is closed if a host is unreachable + } catch (ConnectException ignored) { + return PortState.BLOCKED; + } catch (IOException ignored) { // Will happen a lot when scanning for open ports, so not needed - } catch (IOException e) { - System.out.printf("%s -> %s", e.getClass().getSimpleName(), e.getMessage()); - } finally { + } finally { counter.dec(); socketLimit.release(); } - return false; + return PortState.CLOSED; } }