diff --git a/src/main/java/Main.java b/src/main/java/Main.java index ef76dc4..068480d 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -6,7 +6,20 @@ import java.util.LinkedList; public class Main { - private static final int DEFAULT_TIMEOUT = 1000; // milliseconds + 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() { System.out.println( @@ -20,58 +33,59 @@ 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 - LinkedList> knockSequence = new LinkedList<>(); + + // 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<>(Mode.fromString(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(); + for (AbstractMap.SimpleEntry entry : knockSequence) { + Mode mode = 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 ... ", 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); @@ -83,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()) { @@ -104,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