jscanner

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

commit 05d1676032a732c72fb03ac358f8e3cdbb498a95
parent c344fa59bdcf3bfae588b4d9626dc282bb9999f8
Author: Matthias Jaros <jaros@mailbox.org>
Date:   Thu, 22 Jan 2026 21:05:16 +0100

Working version with semaphore counter and rate limiter

Diffstat:
A.gitignore | 2++
A.mvn/jvm.config | 0
A.mvn/maven.config | 0
Apom.xml | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/com/it_jaros/networkScanner/App.java | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/main/java/com/it_jaros/networkScanner/Counter.java | 25+++++++++++++++++++++++++
Asrc/main/java/com/it_jaros/networkScanner/FdCounter.java | 37+++++++++++++++++++++++++++++++++++++
Asrc/main/java/com/it_jaros/networkScanner/Scanner.java | 50++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +# java +/target diff --git a/.mvn/jvm.config b/.mvn/jvm.config diff --git a/.mvn/maven.config b/.mvn/maven.config diff --git a/pom.xml b/pom.xml @@ -0,0 +1,97 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>com.it-jaros</groupId> + <artifactId>networkScanner</artifactId> + <version>1.0-SNAPSHOT</version> + + <name>networkScanner</name> + <!-- FIXME change it to the project's website --> + <url>http://www.example.com</url> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven.compiler.release>21</maven.compiler.release> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.junit</groupId> + <artifactId>junit-bom</artifactId> + <version>5.11.0</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <!-- Optionally: parameterized tests support --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> + <plugins> + <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <version>3.4.0</version> + </plugin> + <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>3.3.1</version> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.13.0</version> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.3.0</version> + </plugin> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <version>3.4.2</version> + <configuration> + <archive> + <manifest> + <mainClass>com.it_jaros.networkScanner.App</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <version>3.1.2</version> + </plugin> + <plugin> + <artifactId>maven-deploy-plugin</artifactId> + <version>3.1.2</version> + </plugin> + <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <version>3.12.1</version> + </plugin> + <plugin> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>3.6.1</version> + </plugin> + </plugins> + </pluginManagement> + </build> +</project> diff --git a/src/main/java/com/it_jaros/networkScanner/App.java b/src/main/java/com/it_jaros/networkScanner/App.java @@ -0,0 +1,41 @@ +package com.it_jaros.networkScanner; + +import java.util.List; + +public class App { + public static void main(String[] args) { + Scanner scanner = new Scanner(); + if (args.length < 1 || args[0] == "") { + usageAndExit(); + } + String target = args[0]; + System.out.printf( + "Scanning host=%s \n", + target); + + try (FdCounter fd = new FdCounter()) { + fd.start(20); // sample every 20ms + List<Integer> openPorts = scanner.scanPorts(target, 10000); + printPorts(target, openPorts); + System.out.println("FD limit: " + fd.maxFds()); + System.out.println("Peak open FDs during scan: " + fd.peakOpenFds()); + } + } + + public static void printPorts(String address, List<Integer> openPorts) { + System.out.println("Address:" + address); + openPorts.forEach((p) -> System.out.println("Open port: " + p)); + } + + private static void usageAndExit() { + System.err.println(""" + Usage: + java -jar scanner.jar <host> + + Examples: + java -jar scanner.jar localhost + java -jar scanner.jar fd00::1 + """); + System.exit(1); + } +} diff --git a/src/main/java/com/it_jaros/networkScanner/Counter.java b/src/main/java/com/it_jaros/networkScanner/Counter.java @@ -0,0 +1,25 @@ +package com.it_jaros.networkScanner; + +import java.util.concurrent.atomic.AtomicInteger; + +public final class Counter { + private final AtomicInteger current = new AtomicInteger(0); + private final AtomicInteger max = new AtomicInteger(0); + + public void inc() { + int now = current.incrementAndGet(); + max.accumulateAndGet(now, Math::max); + } + + public void dec() { + current.decrementAndGet(); + } + + public int current() { + return current.get(); + } + + public int max() { + return max.get(); + } +} diff --git a/src/main/java/com/it_jaros/networkScanner/FdCounter.java b/src/main/java/com/it_jaros/networkScanner/FdCounter.java @@ -0,0 +1,37 @@ +package com.it_jaros.networkScanner; + +import java.lang.management.ManagementFactory; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import com.sun.management.UnixOperatingSystemMXBean; + +public final class FdCounter implements AutoCloseable { + private final UnixOperatingSystemMXBean os = + (UnixOperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + private final ScheduledExecutorService ses = + Executors.newSingleThreadScheduledExecutor(Thread.ofPlatform().daemon().factory()); + private final AtomicLong peak = new AtomicLong(0); + + public void start(long everyMillis) { + ses.scheduleAtFixedRate(() -> { + long now = os.getOpenFileDescriptorCount(); + peak.accumulateAndGet(now, Math::max); + }, 0, everyMillis, TimeUnit.MILLISECONDS); + } + + public long peakOpenFds() { + return peak.get(); + } + + public long maxFds() { + return os.getMaxFileDescriptorCount(); + } + + @Override public void close() { + ses.shutdownNow(); + } +} + diff --git a/src/main/java/com/it_jaros/networkScanner/Scanner.java b/src/main/java/com/it_jaros/networkScanner/Scanner.java @@ -0,0 +1,50 @@ +package com.it_jaros.networkScanner; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; + +public class Scanner { + + private Counter counter = new Counter(); + private final int socketLimitDefault = 1024; + + public Scanner() {} + + public List<Integer> scanPorts(String address) { + return scanPorts(address, socketLimitDefault); + } + + public List<Integer> scanPorts(String address, int socketLimit) { + Queue<Integer> openPorts = new ConcurrentLinkedQueue<>(); + Semaphore socketCap = new Semaphore(socketLimit); + try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { + for (int port = 1; port < 65536; port++) { + final int currentPort = port; + executor.submit(() -> { + socketCap.acquireUninterruptibly(); + counter.inc(); + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(address, currentPort)); + openPorts.add(currentPort); + } catch (ConnectException ignored) { + } catch (IOException e) { + System.out.println(e.getClass().getSimpleName() + ": " + e.getMessage()); + } finally { + counter.dec(); + socketCap.release(); + } + }); + } + } + System.out.println("Peak concurrent connects: " + counter.max()); + return openPorts.stream().toList(); + } +}