diff options
| author | 2026-03-23 12:30:57 +0100 | |
|---|---|---|
| committer | 2026-07-09 09:59:28 +0200 | |
| commit | bb02319a3ab4c62c263415042795e0ced97b6d7a (patch) | |
| tree | bad95578de4bb83f2aa3eb4dce7e3e60bd95fe0a /src/main/java/com/it_jaros/network_scanner | |
| parent | 1ec785cf30afe9e83a81cf1b7e7f402fda295bbf (diff) | |
Added feature to distinguish between blocked by firewall and open ports
Diffstat (limited to 'src/main/java/com/it_jaros/network_scanner')
7 files changed, 60 insertions, 42 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 index bde3f3b..dcb73dd 100644 --- 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 new file mode 100644 index 0000000..d5ebf7c --- /dev/null +++ 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 new file mode 100644 index 0000000..1ef9787 --- /dev/null +++ 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 index 24ea343..d0432c8 100644 --- 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 index 72f762a..44ac808 100644 --- 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 index b7cd8b1..024e843 100644 --- 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 index 7ee0276..2153fe6 100644 --- 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; } } |
