From 17a39e7d6c987b8f65d315c9818541d82fa34bd5 Mon Sep 17 00:00:00 2001 From: Tobias Grothe Date: Sun, 14 Dec 2025 20:35:55 +0100 Subject: [PATCH 1/2] Default update at 2025-12-14-20-35 --- src/main/java/Main.java | 67 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index ef76dc4..c7dd797 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -6,7 +6,7 @@ import java.util.LinkedList; public class Main { - private static final int DEFAULT_TIMEOUT = 1000; // milliseconds + private static final int TCP_TIMEOUT = 1000; // milliseconds private static void printUsageAndExit() { System.out.println( @@ -20,58 +20,61 @@ public static void main(String[] args) { if (args.length == 0) { printUsageAndExit(); } - // Collect parameters... + + // Default parameters... int delay = 100; // default delay String host = "localhost"; // default host + + // Parse arguments... LinkedList> knockSequence = new LinkedList<>(); for (String arg : args) { String[] parts = arg.split("=", 2); - if (parts.length == 2) { - String key = parts[0]; - String value = parts[1]; - switch (key) { - case "delay" -> { - try { - int d = Integer.parseInt(value); - if (d < 0 || d > 60_000) { - printUsageAndExit(); - } - delay = d; - } catch (NumberFormatException e) { + if (parts.length != 2) { + printUsageAndExit(); + } + String key = parts[0]; + String value = parts[1]; + switch (key) { + case "host" -> host = value; + case "delay" -> { + try { + int d = Integer.parseInt(value); + if (d < 0 || d > 60_000) { printUsageAndExit(); } + delay = d; + } catch (NumberFormatException e) { + printUsageAndExit(); } - case "host" -> host = value; - case "u", "t" -> { - try { - int port = Integer.parseInt(value); - if (port < 1 || port > 65535) { - printUsageAndExit(); - } - knockSequence.add(new AbstractMap.SimpleEntry<>(key, port)); - } catch (NumberFormatException e) { + } + case "u", "t" -> { + try { + int port = Integer.parseInt(value); + if (port < 1 || port > 65535) { printUsageAndExit(); } + knockSequence.add(new AbstractMap.SimpleEntry<>(key, port)); + } catch (NumberFormatException e) { + printUsageAndExit(); } - default -> printUsageAndExit(); } - } else { - printUsageAndExit(); + default -> printUsageAndExit(); } } if (knockSequence.isEmpty()) { printUsageAndExit(); } + // Perform knocking sequence... for (AbstractMap.SimpleEntry entry : knockSequence) { String protocol = entry.getKey(); int port = entry.getValue(); - if (protocol.equals("u")) { - System.out.printf("Knocking UDP %s:%d ... ", host, port); - System.out.println(knockUDP(host, port) ? "Success" : "Failed"); - } else if (protocol.equals("t")) { - System.out.printf("Knocking TCP %s:%d ... ", host, port); - System.out.println(knockTCP(host, port, DEFAULT_TIMEOUT) ? "Success" : "Failed"); + System.out.printf("Knocking %s %s:%d ... ", protocol.equals("u") ? "UDP" : "TCP", host, port); + switch (protocol) { + case "u" -> System.out.println(knockUDP(host, port) ? "Success" : "Failed"); + case "t" -> System.out.println(knockTCP(host, port, TCP_TIMEOUT) ? "Success" : "Failed"); + default -> // This should never happen due to prior validation + System.out.println("Unknown protocol"); } try { Thread.sleep(delay); From 520445f0c6902d87567f56dfc49f78b97a1feab5 Mon Sep 17 00:00:00 2001 From: Tobias Grothe Date: Sun, 14 Dec 2025 21:15:34 +0100 Subject: [PATCH 2/2] Update at 2025-12-14-21-15 --- src/main/java/Main.java | 43 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index c7dd797..068480d 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -6,6 +6,19 @@ import java.util.LinkedList; public class Main { + private enum Mode { + UDP, + TCP; + + public static Mode fromString(String s) { + return switch (s) { + case "u" -> UDP; + case "t" -> TCP; + default -> throw new IllegalArgumentException("Unknown mode: " + s); + }; + } + } + private static final int TCP_TIMEOUT = 1000; // milliseconds private static void printUsageAndExit() { @@ -26,7 +39,7 @@ public static void main(String[] args) { String host = "localhost"; // default host // Parse arguments... - LinkedList> knockSequence = new LinkedList<>(); + LinkedList> knockSequence = new LinkedList<>(); for (String arg : args) { String[] parts = arg.split("=", 2); if (parts.length != 2) { @@ -53,7 +66,7 @@ public static void main(String[] args) { if (port < 1 || port > 65535) { printUsageAndExit(); } - knockSequence.add(new AbstractMap.SimpleEntry<>(key, port)); + knockSequence.add(new AbstractMap.SimpleEntry<>(Mode.fromString(key), port)); } catch (NumberFormatException e) { printUsageAndExit(); } @@ -66,15 +79,13 @@ public static void main(String[] args) { } // Perform knocking sequence... - for (AbstractMap.SimpleEntry entry : knockSequence) { - String protocol = entry.getKey(); + for (AbstractMap.SimpleEntry entry : knockSequence) { + Mode mode = entry.getKey(); int port = entry.getValue(); - System.out.printf("Knocking %s %s:%d ... ", protocol.equals("u") ? "UDP" : "TCP", host, port); - switch (protocol) { - case "u" -> System.out.println(knockUDP(host, port) ? "Success" : "Failed"); - case "t" -> System.out.println(knockTCP(host, port, TCP_TIMEOUT) ? "Success" : "Failed"); - default -> // This should never happen due to prior validation - System.out.println("Unknown protocol"); + System.out.printf("Knocking %s %s:%d ... ", mode.name(), host, port); + switch (mode) { + case UDP -> System.out.println(knockUDP(host, port) ? "Success" : "Failed"); + case TCP -> System.out.println(knockTCP(host, port, TCP_TIMEOUT) ? "Success" : "Failed"); } try { Thread.sleep(delay); @@ -86,15 +97,12 @@ public static void main(String[] args) { } /** - * Knocks on a UDP port by attempting to create a UDP socket connection. Note: UDP is - * connectionless, so this method only validates that a DatagramSocket can be created and - * connected to the address. It will succeed even if nothing is listening on the target port, as - * UDP does not establish a true connection like TCP. + * Knocks on a UDP port by attempting to create a UDP socket, and sending a datagram. The datagram + * content is not important, it has a fixed size of 2048 bytes. * * @param host the hostname or IP address to connect to * @param port the UDP port to knock on - * @return true if the socket was successfully created and connected to the address, false - * otherwise + * @return true if the datagram was sent successfully, false otherwise */ public static boolean knockUDP(String host, int port) { try (DatagramSocket socket = new DatagramSocket()) { @@ -107,7 +115,8 @@ public static boolean knockUDP(String host, int port) { } /** - * Knocks on a TCP port by attempting to establish a TCP connection. + * Knocks on a TCP port by attempting to establish a TCP connection. Returning false + * does not necessarily mean that the port is closed; it may also be filtered by a firewall. * * @param host the hostname or IP address to connect to * @param port the TCP port to knock on