diff --git a/README.MD b/README.MD index 317a1aa..c082761 100644 --- a/README.MD +++ b/README.MD @@ -32,7 +32,7 @@ This project (OAC) is licensed under the [Open Autonomous Public License (OAPL)] ### Repository: ``` - repounlegitdqrk + oac https://repo.open-autonomous-connection.org/api/packages/open-autonomous-connection/maven true diff --git a/pom.xml b/pom.xml index 9c7d897..edde05f 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ dev.unlegitdqrk unlegitlibrary - 1.6.2 + 1.6.5 org.projectlombok diff --git a/src/main/java/org/openautonomousconnection/protocol/ProtocolBridge.java b/src/main/java/org/openautonomousconnection/protocol/ProtocolBridge.java index 062e64e..80051a0 100644 --- a/src/main/java/org/openautonomousconnection/protocol/ProtocolBridge.java +++ b/src/main/java/org/openautonomousconnection/protocol/ProtocolBridge.java @@ -17,134 +17,168 @@ import org.openautonomousconnection.protocol.side.client.ProtocolClient; import org.openautonomousconnection.protocol.side.dns.ProtocolDNSServer; import org.openautonomousconnection.protocol.side.web.ProtocolWebServer; import org.openautonomousconnection.protocol.versions.ProtocolVersion; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.ClassicHandlerClient; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.ClassicHandlerDNSServer; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.ClassicHandlerWebServer; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_ClientListener; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerClient; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerDNSServer; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerWebServer; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ClientListener; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +/** + * The main bridge class for the protocol connection. + * It manages the protocol settings, version, and side instances. + */ public class ProtocolBridge { + /** + * The singleton instance of the ProtocolBridge class + */ @Getter private static ProtocolBridge instance; + + /** + * The protocol settings for the current connection + */ @Getter private final ProtocolSettings protocolSettings; + + /** + * The protocol version for the current connection + */ @Getter private final ProtocolVersion protocolVersion; + + /** + * The logger instance for logging events and errors + */ @Getter - private final Logger logger; + private Logger logger; + + /** + * The protocol side instances + */ @Getter private ProtocolDNSServer protocolDNSServer; + + /** + * The protocol side instances + */ @Getter private ProtocolClient protocolClient; + + /** + * The protocol side instances + */ @Getter private ProtocolWebServer protocolWebServer; + + /** + * The classic protocol handlers for dns server side + */ @Getter @Setter private ClassicHandlerDNSServer classicHandlerDNSServer; + + /** + * The classic protocol handlers for web server side + */ @Getter @Setter private ClassicHandlerWebServer classicHandlerWebServer; + + /** + * The classic protocol handlers for client side + */ @Getter @Setter private ClassicHandlerClient classicHandlerClient; + /** + * Initialize the ProtocolBridge instance for the DNS server side + * @param protocolDNSServer The ProtocolDNSServer instance + * @param protocolSettings The ProtocolSettings instance + * @param protocolVersion The ProtocolVersion instance + * @param logFolder The folder to store the log files + * @throws Exception if an error occurs while initializing the ProtocolBridge + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) - public ProtocolBridge(ProtocolDNSServer protocolDNSServer, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { + public ProtocolBridge(ProtocolDNSServer protocolDNSServer, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws Exception { + // Assign the parameters to the class fields this.protocolDNSServer = protocolDNSServer; this.protocolSettings = protocolSettings; this.protocolVersion = protocolVersion; - Logger tmpLogger = null; - try { - tmpLogger = new Logger(logFolder, false, true); - } catch (IOException | NoSuchFieldException | IllegalAccessException exception) { - exception.printStackTrace(); - tmpLogger = null; - System.exit(1); - } + // Initialize the logger and protocol version + initializeLogger(logFolder); + initializeProtocolVersion(); - this.logger = tmpLogger; - protocolSettings.eventManager.registerListener(new DNSServerListener()); - protocolSettings.eventManager.unregisterListener(new WebServerListener()); - protocolSettings.eventManager.unregisterListener(new ClientListener()); - - if (!validateProtocolSide()) { - this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!"); - System.exit(1); - } - - if (isClassicSupported()) protocolSettings.eventManager.unregisterListener(new Classic_ClientListener()); + // Register the appropriate listeners and packets + registerListeners(); registerPackets(); + // Set the static instance to this instance instance = this; } + /** + * Initialize the ProtocolBridge instance for the web server side + * @param protocolWebServer The ProtocolWebServer instance + * @param protocolSettings The ProtocolSettings instance + * @param protocolVersion The ProtocolVersion instance + * @param logFolder The folder to store the log files + * @throws Exception if an error occurs while initializing the ProtocolBridge + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) - public ProtocolBridge(ProtocolWebServer protocolWebServer, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { + public ProtocolBridge(ProtocolWebServer protocolWebServer, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws Exception { + // Assign the parameters to the class fields this.protocolWebServer = protocolWebServer; this.protocolSettings = protocolSettings; this.protocolVersion = protocolVersion; - Logger tmpLogger = null; - try { - tmpLogger = new Logger(logFolder, false, true); - } catch (IOException | NoSuchFieldException | IllegalAccessException exception) { - exception.printStackTrace(); - tmpLogger = null; - System.exit(1); - } + // Initialize the logger and protocol version + initializeLogger(logFolder); + initializeProtocolVersion(); - this.logger = tmpLogger; - protocolSettings.eventManager.unregisterListener(new DNSServerListener()); - protocolSettings.eventManager.registerListener(new WebServerListener()); - protocolSettings.eventManager.unregisterListener(new ClientListener()); - - if (!validateProtocolSide()) { - this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!"); - System.exit(1); - } - - if (isClassicSupported()) protocolSettings.eventManager.unregisterListener(new Classic_ClientListener()); + // Register the appropriate listeners and packets + registerListeners(); registerPackets(); + // Set the static instance to this instance instance = this; } + /** + * Initialize the ProtocolBridge instance for the client side + * @param protocolClient The ProtocolClient instance + * @param protocolSettings The ProtocolSettings instance + * @param protocolVersion The ProtocolVersion instance + * @param logFolder The folder to store the log files + * @throws Exception if an error occurs while initializing the ProtocolBridge + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT) - public ProtocolBridge(ProtocolClient protocolClient, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { + public ProtocolBridge(ProtocolClient protocolClient, ProtocolSettings protocolSettings, ProtocolVersion protocolVersion, File logFolder) throws Exception { + // Assign the parameters to the class fields this.protocolClient = protocolClient; this.protocolSettings = protocolSettings; this.protocolVersion = protocolVersion; - Logger tmpLogger = null; - try { - tmpLogger = new Logger(logFolder, false, true); - } catch (IOException | NoSuchFieldException | IllegalAccessException exception) { - exception.printStackTrace(); - tmpLogger = null; - System.exit(1); - } + // Initialize the logger and protocol version + initializeLogger(logFolder); + initializeProtocolVersion(); - this.logger = tmpLogger; - protocolSettings.eventManager.registerListener(new ClientListener()); - protocolSettings.eventManager.unregisterListener(new WebServerListener()); - protocolSettings.eventManager.unregisterListener(new DNSServerListener()); - - if (!validateProtocolSide()) { - this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!"); - System.exit(1); - } - - if (isClassicSupported()) protocolSettings.eventManager.registerListener(new Classic_ClientListener()); + // Register the appropriate listeners and packets + registerListeners(); registerPackets(); + // Set the static instance to this instance instance = this; } + /** + * Register the appropriate packets based on the current protocol version + */ private void registerPackets() { // Classic packets Classic_DomainPacket cDomainPacket = new Classic_DomainPacket(); @@ -162,50 +196,132 @@ public class ProtocolBridge { GetDestinationPacket v100bGetDestinationPacket = new GetDestinationPacket(); if (isPacketSupported(v100bAuthPath)) protocolSettings.packetHandler.registerPacket(v100bAuthPath); - if (isPacketSupported(v100bUnsupportedClassicPacket)) - protocolSettings.packetHandler.registerPacket(v100bUnsupportedClassicPacket); - if (isPacketSupported(v100bValidateDomainPacket)) - protocolSettings.packetHandler.registerPacket(v100bValidateDomainPacket); - if (isPacketSupported(v100bGetDestinationPacket)) - protocolSettings.packetHandler.registerPacket(v100bGetDestinationPacket); + if (isPacketSupported(v100bUnsupportedClassicPacket)) protocolSettings.packetHandler.registerPacket(v100bUnsupportedClassicPacket); + if (isPacketSupported(v100bValidateDomainPacket)) protocolSettings.packetHandler.registerPacket(v100bValidateDomainPacket); + if (isPacketSupported(v100bGetDestinationPacket)) protocolSettings.packetHandler.registerPacket(v100bGetDestinationPacket); } - public final boolean isPacketSupported(OACPacket packet) { - return isVersionSupported(packet.getProtocolVersion()); + /** + * Register the appropriate listeners based on the current side + * @throws Exception if an error occurs while registering the listeners + */ + private void registerListeners() throws Exception { + // Classic listeners + if (isClassicSupported()) protocolSettings.eventManager.registerListener(Classic_ClientListener.class); + else protocolSettings.eventManager.unregisterListener(Classic_ClientListener.class); + + // DNS Listeners + if (isRunningAsDNSServer()) { + protocolSettings.eventManager.registerListener(DNSServerListener.class); + protocolSettings.eventManager.unregisterListener(WebServerListener.class); + protocolSettings.eventManager.unregisterListener(ClientListener.class); + } + + // Web Listeners + if (isRunningAsWebServer()) { + protocolSettings.eventManager.registerListener(WebServerListener.class); + protocolSettings.eventManager.unregisterListener(DNSServerListener.class); + protocolSettings.eventManager.unregisterListener(ClientListener.class); + } + + // Client Listeners + if (isRunningAsClient()) { + protocolSettings.eventManager.registerListener(ClientListener.class); + protocolSettings.eventManager.unregisterListener(DNSServerListener.class); + protocolSettings.eventManager.unregisterListener(WebServerListener.class); + } } + /** + * Initialize the logger instance + * @param logFolder The folder to store the log files + */ + private void initializeLogger(File logFolder) { + // Create a temporary logger instance to avoid final field issues + Logger tmpLogger = null; + + try { + // Initialize temporary logger + tmpLogger = new Logger(logFolder, false, true); + } catch (IOException | NoSuchFieldException | IllegalAccessException exception) { + exception.printStackTrace(); + System.exit(1); + } + + // Assign the temporary logger to the final field + this.logger = tmpLogger; + } + + /** + * Initialize the protocol version + * Validate if the protocol version is valid for the current side + * If not, log an error and exit the application + */ + private void initializeProtocolVersion() { + // Check if the protocol version is valid for the current side + // If not, log an error and exit the application + if (!validateProtocolSide()) { + this.logger.error("Invalid protocol version '" + protocolVersion.toString() + "'!"); + System.exit(1); + } + } + + /** + * Check if the classic protocol is supported by the current protocol version + * @return true if the classic protocol is supported, false otherwise + */ public final boolean isClassicSupported() { boolean yes = false; for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) { + // Check if the compatible version is classic yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; if (yes) break; } + // Check if the current protocol version is classic or if it is supported by any of the compatible versions return protocolVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC || yes; } + /** + * Check if the target protocol is supported by the current protocol version + * @param protocol The target protocol to check + * @return true If the target protocol is supported, false otherwise + */ public final boolean isProtocolSupported(ProtocolVersion.Protocol protocol) { boolean yes = false; for (ProtocolVersion compatibleVersion : protocolVersion.getCompatibleVersions()) { + // Check if the compatible version supports the target protocol yes = compatibleVersion.getSupportedProtocols().contains(protocol); if (yes) break; } + // Check if the current protocol version supports the target protocol or if it is supported by any of the compatible versions return protocolVersion.getSupportedProtocols().contains(protocol) || yes; } - public final boolean isRunningAsDNSServer() { - return protocolDNSServer != null; + /** + * Check if the target packet is supported by the current protocol version + * @param packet The target packet to check + * @return true if the target packet is supported, false otherwise + */ + public final boolean isPacketSupported(OACPacket packet) { + return isVersionSupported(packet.getProtocolVersion()); } - public final boolean isRunningAsClient() { - return protocolClient != null; - } - - public final boolean isRunningAsWebServer() { - return protocolWebServer != null; + /** + * Check if the target protocol version is supported by the current protocol version + * @param targetVersion The target protocol version to check + * @return true if the target protocol version is supported, false otherwise + */ + public final boolean isVersionSupported(ProtocolVersion targetVersion) { + // Check if the target protocol version is the same as the current protocol version or if it is in the list of compatible versions + return protocolVersion == targetVersion || protocolVersion.getCompatibleVersions().contains(targetVersion); } + /** + * Validate if the protocol version is valid for the current side + * @return true if the protocol version is valid for the current side, false otherwise + */ private boolean validateProtocolSide() { return (isRunningAsClient() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.CLIENT) || @@ -224,7 +340,27 @@ public class ProtocolBridge { (isRunningAsDNSServer() && protocolVersion.getProtocolSide() == ProtocolVersion.ProtocolSide.ALL); } - public final boolean isVersionSupported(ProtocolVersion targetVersion) { - return protocolVersion == targetVersion || protocolVersion.getCompatibleVersions().contains(targetVersion); + /** + * Check if the current instance is running as a DNS server + * @return true if the current instance is running as a DNS server, false otherwise + */ + public final boolean isRunningAsDNSServer() { + return protocolDNSServer != null; + } + + /** + * Check if the current instance is running as a client + * @return true if the current instance is running as a client, false otherwise + */ + public final boolean isRunningAsClient() { + return protocolClient != null; + } + + /** + * Check if the current instance is running as a web server + * @return true if the current instance is running as a web server, false otherwise + */ + public final boolean isRunningAsWebServer() { + return protocolWebServer != null; } } diff --git a/src/main/java/org/openautonomousconnection/protocol/ProtocolSettings.java b/src/main/java/org/openautonomousconnection/protocol/ProtocolSettings.java index 6a9c2fc..df259a0 100644 --- a/src/main/java/org/openautonomousconnection/protocol/ProtocolSettings.java +++ b/src/main/java/org/openautonomousconnection/protocol/ProtocolSettings.java @@ -4,11 +4,29 @@ import dev.unlegitdqrk.unlegitlibrary.event.EventManager; import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider; +/** + * Settings for the protocol connection. + */ public class ProtocolSettings extends DefaultMethodsOverrider { + /** + * The host to connect to. + */ public String host; + + /** + * The port to connect to. + */ public int port; + + /** + * The protocol version to use. + */ public PacketHandler packetHandler; + + /** + * The event manager to use. + */ public EventManager eventManager; } diff --git a/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java b/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java index 24235d8..e908cf3 100644 --- a/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java +++ b/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java @@ -2,8 +2,16 @@ package org.openautonomousconnection.protocol.annotations; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Annotation to provide metadata about protocol handlers or classes. + */ public @interface ProtocolInfo { + /** + * Specifies the side of the protocol that the annotated class or method is associated with. + * Default is ALL, indicating that it can be used on any side. + * @return The protocol side. + */ ProtocolVersion.ProtocolSide protocolSide() default ProtocolVersion.ProtocolSide.ALL; } diff --git a/src/main/java/org/openautonomousconnection/protocol/exceptions/UnsupportedProtocolException.java b/src/main/java/org/openautonomousconnection/protocol/exceptions/UnsupportedProtocolException.java index 427f89a..a3bf9b7 100644 --- a/src/main/java/org/openautonomousconnection/protocol/exceptions/UnsupportedProtocolException.java +++ b/src/main/java/org/openautonomousconnection/protocol/exceptions/UnsupportedProtocolException.java @@ -1,5 +1,8 @@ package org.openautonomousconnection.protocol.exceptions; +/** + * Exception thrown when an unsupported protocol is encountered. + */ public class UnsupportedProtocolException extends RuntimeException { public UnsupportedProtocolException() { diff --git a/src/main/java/org/openautonomousconnection/protocol/listeners/ClientListener.java b/src/main/java/org/openautonomousconnection/protocol/listeners/ClientListener.java index 2bff565..d3ec9d0 100644 --- a/src/main/java/org/openautonomousconnection/protocol/listeners/ClientListener.java +++ b/src/main/java/org/openautonomousconnection/protocol/listeners/ClientListener.java @@ -11,19 +11,32 @@ import org.openautonomousconnection.protocol.versions.ProtocolVersion; import java.io.IOException; +/** + * Listener for client-side events such as connection and disconnection. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT) public class ClientListener extends EventListener { + /** + * Handles the event when a client connects. + * Sends an authentication packet to the server. + * @param event The client connected event. + */ @Listener public void onConnect(ClientConnectedEvent event) { try { - event.client.sendPacket(new AuthPacket()); + event.getClient().sendPacket(new AuthPacket()); } catch (IOException | ClassNotFoundException exception) { ProtocolBridge.getInstance().getLogger().exception("Failed to send auth packet", exception); - event.client.disconnect(); + event.getClient().disconnect(); } } + /** + * Handles the event when a client disconnects. + * Notifies the protocol client of the disconnection. + * @param event The client disconnected event. + */ @Listener public void onDisconnect(ClientDisconnectedEvent event) { ProtocolBridge.getInstance().getProtocolClient().onDNSDisconnect(event); diff --git a/src/main/java/org/openautonomousconnection/protocol/listeners/DNSServerListener.java b/src/main/java/org/openautonomousconnection/protocol/listeners/DNSServerListener.java index 11ed27a..e545665 100644 --- a/src/main/java/org/openautonomousconnection/protocol/listeners/DNSServerListener.java +++ b/src/main/java/org/openautonomousconnection/protocol/listeners/DNSServerListener.java @@ -9,17 +9,31 @@ import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Listener for DNS server connection events. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public class DNSServerListener extends EventListener { + /** + * Handles the event when a connection handler connects to the DNS server. + * Adds the connected client to the ProtocolBridge's DNS server client list. + * @param event The connection handler connected event. + */ @Listener public void onConnect(ConnectionHandlerConnectedEvent event) { - ProtocolBridge.getInstance().getProtocolDNSServer().getClients().add(new ConnectedProtocolClient(event.connectionHandler)); + ProtocolBridge.getInstance().getProtocolDNSServer().getClients().add(new ConnectedProtocolClient(event.getConnectionHandler())); } + /** + * Handles the event when a connection handler disconnects from the DNS server. + * Removes the disconnected client from the ProtocolBridge's DNS server client list. + * @param event The connection handler disconnected event. + */ @Listener public void onDisconnect(ConnectionHandlerDisconnectedEvent event) { - ProtocolBridge.getInstance().getProtocolDNSServer().getClients().removeIf(client -> client.getConnectionHandler().getClientID() == -1); + ProtocolBridge.getInstance().getProtocolDNSServer().getClients().removeIf(client -> + client.getConnectionHandler().getClientID() == -1); } } diff --git a/src/main/java/org/openautonomousconnection/protocol/listeners/WebServerListener.java b/src/main/java/org/openautonomousconnection/protocol/listeners/WebServerListener.java index 9d63994..2109252 100644 --- a/src/main/java/org/openautonomousconnection/protocol/listeners/WebServerListener.java +++ b/src/main/java/org/openautonomousconnection/protocol/listeners/WebServerListener.java @@ -9,14 +9,27 @@ import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.web.ConnectedWebClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Listener for web server connection events. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public class WebServerListener extends EventListener { + /** + * Handles the event when a connection is established. + * Adds the connected client to the protocol web server's client list. + * @param event The connection handler connected event. + */ @Listener public void onConnect(ConnectionHandlerConnectedEvent event) { - ProtocolBridge.getInstance().getProtocolWebServer().getClients().add(new ConnectedWebClient(event.connectionHandler)); + ProtocolBridge.getInstance().getProtocolWebServer().getClients().add(new ConnectedWebClient(event.getConnectionHandler())); } + /** + * Handles the event when a connection is disconnected. + * Removes the disconnected client from the protocol web server's client list. + * @param event The connection handler disconnected event. + */ @Listener public void onDisconnect(ConnectionHandlerDisconnectedEvent event) { ProtocolBridge.getInstance().getProtocolWebServer().getClients().removeIf(client -> client.getPipelineConnection().getClientID() == -1); diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/OACPacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/OACPacket.java index 2e1e764..340c7a5 100644 --- a/src/main/java/org/openautonomousconnection/protocol/packets/OACPacket.java +++ b/src/main/java/org/openautonomousconnection/protocol/packets/OACPacket.java @@ -10,45 +10,101 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +/** + * Abstract class representing a packet in the Open Autonomous Connection (OAC) protocol. + * This class extends the base Packet class and includes additional functionality specific to OAC. + */ public abstract class OACPacket extends Packet { + /** + * The protocol version associated with this packet. + */ @Getter private final ProtocolVersion protocolVersion; + /** + * The response code for the packet, defaulting to RESPONSE_NOT_REQUIRED. + */ private DNSResponseCode responseCode = DNSResponseCode.RESPONSE_NOT_REQUIRED; + /** + * Constructor for OACPacket. + * @param id The unique identifier for the packet. + * @param protocolVersion The protocol version associated with this packet. + */ public OACPacket(int id, ProtocolVersion protocolVersion) { super(id); this.protocolVersion = protocolVersion; } + /** + * Gets the response code for the packet. + * @return The DNSResponseCode associated with the packet. + */ protected final DNSResponseCode getResponseCode() { return responseCode; } + /** + * Sets the response code for the packet. + * @param responseCode The DNSResponseCode to set for the packet. + */ protected final void setResponseCode(DNSResponseCode responseCode) { this.responseCode = responseCode; } + /** + * Writes the packet data to the output stream. + * @param packetHandler The packet handler managing the packet. + * @param objectOutputStream The output stream to write the packet data to. + * @throws IOException If an I/O error occurs. + * @throws ClassNotFoundException If a class cannot be found during serialization. + */ @Override public final void write(PacketHandler packetHandler, ObjectOutputStream objectOutputStream) throws IOException, ClassNotFoundException { + // Write the specific packet data onWrite(packetHandler, objectOutputStream); + + // Write the response code if the protocol version is not classic if (protocolVersion != ProtocolVersion.PV_1_0_0_CLASSIC) objectOutputStream.writeObject(responseCode); } @Override public final void read(PacketHandler packetHandler, ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { + // Read the specific packet data onRead(packetHandler, objectInputStream); - if (protocolVersion != ProtocolVersion.PV_1_0_0_CLASSIC) { - responseCode = (DNSResponseCode) objectInputStream.readObject(); - onResponseCodeRead(packetHandler, objectInputStream); - } + + // Read the response code if the protocol version is not classic + if (protocolVersion != ProtocolVersion.PV_1_0_0_CLASSIC) responseCode = (DNSResponseCode) objectInputStream.readObject(); + else responseCode = DNSResponseCode.RESPONSE_NOT_REQUIRED; + + // Call the response code read handler + onResponseCodeRead(packetHandler, objectInputStream); } + /** + * Abstract method to be implemented by subclasses for writing specific packet data. + * @param packetHandler The packet handler managing the packet. + * @param objectOutputStream The output stream to write the packet data to. + * @throws IOException If an I/O error occurs. + * @throws ClassNotFoundException If a class cannot be found during serialization. + */ public abstract void onWrite(PacketHandler packetHandler, ObjectOutputStream objectOutputStream) throws IOException, ClassNotFoundException; + /** + * Abstract method to be implemented by subclasses for reading specific packet data. + * @param packetHandler The packet handler managing the packet. + * @param objectInputStream The input stream to read the packet data from. + * @throws IOException If an I/O error occurs. + * @throws ClassNotFoundException If a class cannot be found during deserialization. + */ public abstract void onRead(PacketHandler packetHandler, ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException; - protected void onResponseCodeRead(PacketHandler packetHandler, ObjectInputStream objectInputStream) { - } + /** + * Method called after the response code has been read from the input stream. + * Subclasses can override this method to handle any additional logic based on the response code. + * @param packetHandler The packet handler managing the packet. + * @param objectInputStream The input stream from which the response code was read. + */ + protected void onResponseCodeRead(PacketHandler packetHandler, ObjectInputStream objectInputStream) {} } diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java index 2d6987b..cb6e29f 100644 --- a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java +++ b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/beta/AuthPacket.java @@ -6,7 +6,7 @@ import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler; import dev.unlegitdqrk.unlegitlibrary.network.utils.NetworkUtils; import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.packets.OACPacket; -import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolServer; +import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolDNSServerEvent; import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient; import org.openautonomousconnection.protocol.side.dns.events.ConnectedProtocolClientEvent; import org.openautonomousconnection.protocol.side.web.ConnectedWebClient; @@ -130,7 +130,7 @@ public class AuthPacket extends OACPacket { } ProtocolBridge.getInstance().getProtocolClient().setServerVersion(serverVersion); - ProtocolBridge.getInstance().getProtocolSettings().eventManager.executeEvent(new ConnectedToProtocolServer()); + ProtocolBridge.getInstance().getProtocolSettings().eventManager.executeEvent(new ConnectedToProtocolDNSServerEvent()); } } } diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_DomainPacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_DomainPacket.java index c0b82ce..2ea61cf 100644 --- a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_DomainPacket.java +++ b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_DomainPacket.java @@ -5,10 +5,10 @@ import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.packets.OACPacket; import org.openautonomousconnection.protocol.packets.v1_0_0.beta.UnsupportedClassicPacket; import org.openautonomousconnection.protocol.versions.ProtocolVersion; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_Domain; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_DomainPacketReceivedEvent; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_ProtocolVersion; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_RequestDomain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.events.Classic_DomainPacketReceivedEvent; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; import java.io.IOException; import java.io.ObjectInputStream; diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_MessagePacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_MessagePacket.java index dad624a..0037eaf 100644 --- a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_MessagePacket.java +++ b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_MessagePacket.java @@ -4,7 +4,7 @@ import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.packets.OACPacket; import org.openautonomousconnection.protocol.versions.ProtocolVersion; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; import java.io.IOException; import java.io.ObjectInputStream; diff --git a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_PingPacket.java b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_PingPacket.java index 8dbdadc..6c8cfb4 100644 --- a/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_PingPacket.java +++ b/src/main/java/org/openautonomousconnection/protocol/packets/v1_0_0/classic/Classic_PingPacket.java @@ -6,10 +6,10 @@ import org.openautonomousconnection.protocol.packets.OACPacket; import org.openautonomousconnection.protocol.packets.v1_0_0.beta.UnsupportedClassicPacket; import org.openautonomousconnection.protocol.versions.ProtocolVersion; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.DNSResponseCode; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_Domain; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_PingPacketReceivedEvent; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_ProtocolVersion; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_RequestDomain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.events.Classic_PingPacketReceivedEvent; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; import java.io.IOException; import java.io.ObjectInputStream; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/client/ProtocolClient.java b/src/main/java/org/openautonomousconnection/protocol/side/client/ProtocolClient.java index 22cb007..7a099e9 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/client/ProtocolClient.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/client/ProtocolClient.java @@ -16,24 +16,50 @@ import org.openautonomousconnection.protocol.packets.v1_0_0.classic.Classic_Ping import org.openautonomousconnection.protocol.versions.ProtocolVersion; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.DNSResponseCode; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.Domain; -import org.openautonomousconnection.protocol.versions.v1_0_0.classic.Classic_RequestDomain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.security.cert.CertificateException; +/** + * Abstract class defining the client-side protocol operations and interactions with DNS and web servers. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public abstract class ProtocolClient extends DefaultMethodsOverrider { - private final NetworkClient clientToDNS; // Handles everything with DNS-Connection + /** + * Handles everything with DNS-Connection. + */ + private final NetworkClient clientToDNS; + + /** + * Manages the folder structure for client certificates. + */ @Getter private final ClientCertificateFolderStructure folderStructure; + + /** + * Manages the web connection to the destination server. + */ + @Getter private WebClient webClient; + /** + * Stores the protocol version of the connected server. + */ private ProtocolVersion serverVersion = null; + + /** + * Initializes the ProtocolClient, setting up certificate folders and the DNS client connection. + * @throws CertificateException if there are issues with the certificates. + * @throws IOException if there are I/O issues during initialization. + */ public ProtocolClient() throws CertificateException, IOException { + // Initialize and verify certificate folders and files folderStructure = new ClientCertificateFolderStructure(); + // Initialize connection to DNS server clientToDNS = new NetworkClient.ClientBuilder().setLogger(ProtocolBridge.getInstance().getLogger()). setHost(ProtocolBridge.getInstance().getProtocolSettings().host).setPort(ProtocolBridge.getInstance().getProtocolSettings().port). setPacketHandler(ProtocolBridge.getInstance().getProtocolSettings().packetHandler).setEventManager(ProtocolBridge.getInstance().getProtocolSettings().eventManager). @@ -41,52 +67,98 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider { build(); } + /** + * Gets the DNS connection client. + * @return the NetworkClient handling the DNS connection. + */ public final NetworkClient getClientDNSConnection() { return clientToDNS; } + /** + * Creates a web connection to the specified domain and ports. + * @param domain the target domain for the web connection. + * @param pipelinePort the port used for the pipeline connection. + * @param webPort the port used for the web connection. + * @throws Exception if there are issues creating the web connection or if the protocol is unsupported. + */ public final void createWebConnection(Domain domain, int pipelinePort, int webPort) throws Exception { + // Ensure the protocol supports web connections if (!ProtocolBridge.getInstance().isProtocolSupported(ProtocolVersion.Protocol.OAC)) throw new UnsupportedProtocolException(); + // Check if web client is already connected and close it if (webClient != null) { try { webClient.closeConnection(); } catch (IOException e) { ProtocolBridge.getInstance().getLogger().exception("Failed to close connection to web server", e); + return; } } + // Verify necessary certificate files exist webClient = new WebClient(domain, pipelinePort, webPort); } + /** + * Checks if the required certificate files exist in the specified folder. + * @param folder the folder to check for certificate files. + * @param prefix the prefix of the certificate files. + * @param extension the extension of the certificate files. + * @throws CertificateException if any required certificate file is missing or invalid. + * @throws IOException if there are I/O issues during the check. + */ private final void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException { boolean found = false; + // Check if folder exists if (folder == null) throw new FileNotFoundException("Folder does not exist"); + // List files in the folder File[] files = folder.listFiles(); + + // Check if folder is empty if (files == null || files.length == 0) throw new FileNotFoundException("Folder " + folder.getAbsolutePath() + " is empty"); + // Validate each file in the folder for (File file : files) { if (!file.getName().startsWith(prefix) || !file.getName().endsWith(extension)) throw new CertificateException(file.getAbsolutePath() + " is not valid"); + + // Check for specific files if (!found) found = file.getName().equalsIgnoreCase(prefix + NetworkUtils.getPublicIPAddress() + extension); } + // If the specific file is not found, throw an exception if (!found) throw new CertificateException("Missing " + prefix + NetworkUtils.getPublicIPAddress() + extension); } + /** + * Gets the protocol version of the connected server. + * @return the ProtocolVersion of the server, or PV_1_0_0_CLASSIC if not set. + */ public final ProtocolVersion getServerVersion() { return serverVersion == null ? ProtocolVersion.PV_1_0_0_CLASSIC : serverVersion; } + /** + * Sets the protocol version of the connected server. + * @param serverVersion the ProtocolVersion to set for the server. + */ public final void setServerVersion(ProtocolVersion serverVersion) { if (serverVersion == null) this.serverVersion = serverVersion; } + /** + * Handles DNS disconnection events, resetting the server version and closing the web client connection if necessary. + * @param event the ClientDisconnectedEvent triggered on DNS disconnection. + */ public final void onDNSDisconnect(ClientDisconnectedEvent event) { + // Reset server version on DNS disconnect serverVersion = null; + + // Close web client connection if it exists if (webClient != null) { try { webClient.closeConnection(); @@ -96,82 +168,167 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider { } } + /** + * Checks if the connected server is a stable server. + * @return true if the server is stable, false otherwise. + */ public final boolean isStableServer() { + // Check if the server version is stable return !isBetaServer() && !isClassicServer(); } + /** + * Checks if the connected server or its compatible versions support stable protocol. + * @return true if stable protocol is supported, false otherwise. + */ public final boolean supportServerStable() { boolean yes = false; for (ProtocolVersion compatibleVersion : getServerVersion().getCompatibleVersions()) { + // Check if compatible version is stable yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.STABLE; if (yes) break; } + // Check if the server version is stable return isStableServer() || yes; } + /** + * Checks if the connected server is a beta server. + * @return true if the server is beta, false otherwise. + */ public final boolean isBetaServer() { + // Check if the server version is beta return getServerVersion().getProtocolType() == ProtocolVersion.ProtocolType.BETA; } + /** + * Checks if the connected server or its compatible versions support beta protocol. + * @return true if beta protocol is supported, false otherwise. + */ public final boolean supportServerBeta() { boolean yes = false; for (ProtocolVersion compatibleVersion : getServerVersion().getCompatibleVersions()) { + // Check if compatible version is beta yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.BETA; if (yes) break; } + // Check if the server version is beta return isBetaServer() || yes; } + /** + * Checks if the connected server is a classic server. + * @return true if the server is classic, false otherwise. + */ public final boolean isClassicServer() { + // Check if the server version is classic return getServerVersion().getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; } + /** + * Checks if the connected server or its compatible versions support classic protocol. + * @return true if classic protocol is supported, false otherwise. + */ public final boolean supportServerClassic() { boolean yes = false; for (ProtocolVersion compatibleVersion : getServerVersion().getCompatibleVersions()) { + // Check if compatible version is classic yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; if (yes) break; } + // Check if the server version is classic return isClassicServer() || yes; } + /** + * Checks if the connected server supports the protocol version of the given packet. + * @param packet the OACPacket to check against the server's supported protocol version. + * @return true if the server supports the packet's protocol version, false otherwise. + */ public final boolean supportServerPacket(OACPacket packet) { + // Check if the server supports the protocol version of the packet return supportServerVersion(packet.getProtocolVersion()); } + /** + * Checks if the connected server or its compatible versions support the specified protocol version. + * @param targetVersion the ProtocolVersion to check for support. + * @return true if the server or its compatible versions support the target version, false otherwise. + */ public final boolean supportServerVersion(ProtocolVersion targetVersion) { + // Directly check if the server version matches or is in the list of compatible versions return getServerVersion() == targetVersion || getServerVersion().getCompatibleVersions().contains(targetVersion); } + /** + * Checks if the connected server or its compatible versions support the specified protocol. + * @param protocol the Protocol to check for support. + * @return true if the server or its compatible versions support the protocol, false otherwise. + */ public final boolean supportServerProtocol(ProtocolVersion.Protocol protocol) { boolean yes = false; for (ProtocolVersion compatibleVersion : getServerVersion().getCompatibleVersions()) { + // Check if compatible version supports the protocol yes = compatibleVersion.getSupportedProtocols().contains(protocol); if (yes) break; } + // Check if the server version supports the protocol return getServerVersion().getSupportedProtocols().contains(protocol) || yes; } + /** + * Validates the specified domain by sending a validation request to the DNS server. + * @param domain the Domain to validate. + * @throws IOException if there are I/O issues during the validation process. + * @throws ClassNotFoundException if there are issues with class loading during packet handling. + */ public final void validateDomain(Domain domain) throws IOException, ClassNotFoundException { + // Send Classic_PingPacket if classic protocol is supported Classic_PingPacket cPingPacket = new Classic_PingPacket(new Classic_RequestDomain(domain.getName(), domain.getTopLevelName(), domain.getPath()), null, false); - if (ProtocolBridge.getInstance().isPacketSupported(cPingPacket)) clientToDNS.sendPacket(cPingPacket); + if (ProtocolBridge.getInstance().isClassicSupported()) clientToDNS.sendPacket(cPingPacket); + + // Send ValidateDomainPacket clientToDNS.sendPacket(new ValidateDomainPacket(domain)); } + /** + * Requests the destination for the specified domain from the DNS server. + * @param domain the Domain for which to request the destination. + * @param responseCode the expected DNSResponseCode for the request. + * @throws IOException if there are I/O issues during the request process. + * @throws ClassNotFoundException if there are issues with class loading during packet handling. + */ public final void requestDestination(Domain domain, DNSResponseCode responseCode) throws IOException, ClassNotFoundException { + // Send Classic_DomainPacket if classic protocol is supported Classic_DomainPacket cDomainPacket = new Classic_DomainPacket(0, new Classic_RequestDomain(domain.getName(), domain.getTopLevelName(), domain.getPath()), null); - if (ProtocolBridge.getInstance().isPacketSupported(cDomainPacket)) clientToDNS.sendPacket(cDomainPacket); + if (ProtocolBridge.getInstance().isClassicSupported()) clientToDNS.sendPacket(cDomainPacket); + + // Send GetDestinationPacket clientToDNS.sendPacket(new GetDestinationPacket(domain, responseCode)); } + /** + * Callback method invoked when domain validation is completed. + * @param domain the Domain that was validated. + * @param responseCode the DNSResponseCode resulting from the validation. + */ public abstract void validationCompleted(Domain domain, DNSResponseCode responseCode); + /** + * Callback method invoked when the destination retrieval is completed. + * @param domain the Domain for which the destination was requested. + * @param destination the retrieved destination as a string. + * @param validationResponse the DNSResponseCode resulting from the destination retrieval. + */ public abstract void getDestinationCompleted(Domain domain, String destination, DNSResponseCode validationResponse); + /** + * Manages the folder structure for client certificates. + */ public final class ClientCertificateFolderStructure { public final File certificatesFolder; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/client/WebClient.java b/src/main/java/org/openautonomousconnection/protocol/side/client/WebClient.java index 8e0ca05..3884a5b 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/client/WebClient.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/client/WebClient.java @@ -17,29 +17,89 @@ import java.net.InetSocketAddress; import java.net.Proxy; import java.net.Socket; +/** + * WebClient handles secure connections to web servers through a pipeline connection. + * It manages SSL/TLS handshakes and data transmission over the established connection. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public final class WebClient { - private final NetworkClient clientToWebPipeline; // Handles everything with Pipeline-Connection - private SSLSocket clientToWebServer; // Handles everything with Web-Connection - private ObjectOutputStream outputStream; private final Thread receiveThread = new Thread(this::receive); + /** + * NetworkClient instance for managing the pipeline connection to the web server. + */ + private final NetworkClient clientToWebPipeline; + + /** + * SSLSocket for secure communication with the web server. + */ + private SSLSocket clientToWebServer; + + /** + * Streams for object serialization over the SSL connection. + */ + private ObjectOutputStream outputStream; + /** + * Streams for object serialization over the SSL connection. + */ private ObjectInputStream inputStream; + + /** + * Thread for receiving data from the web server. + */ + private final Thread receiveThread = new Thread(this::receive); + + /** + * Constructs a WebClient instance and establishes a secure connection to the web server. + * @param domain The domain information for the web server. + * @param pipelinePort The port for the pipeline connection. + * @param webPort The port for the web server connection. + * @throws Exception If an error occurs during connection setup. + */ public WebClient(Domain domain, int pipelinePort, int webPort) throws Exception { - clientToWebPipeline = new NetworkClient.ClientBuilder().setLogger(ProtocolBridge.getInstance().getLogger()). + // Initialize and connect the pipeline client + clientToWebPipeline = new NetworkClient.ClientBuilder(). + // Set logger from ProtocolBridge + setLogger(ProtocolBridge.getInstance().getLogger()). + // Set the destination and port for the pipeline connection setHost(domain.getDestination()).setPort(pipelinePort). - setPacketHandler(ProtocolBridge.getInstance().getProtocolSettings().packetHandler).setEventManager(ProtocolBridge.getInstance().getProtocolSettings().eventManager). - setRootCAFolder(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicCAFolder).setClientCertificatesFolder(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicClientFolder, ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().privateClientFolder). + + // Configure packet handler and event manager + setPacketHandler(ProtocolBridge.getInstance().getProtocolSettings().packetHandler). + setEventManager(ProtocolBridge.getInstance().getProtocolSettings().eventManager). + + // Set proxy and ssl parameters from DNS connection settings + setProxy(ProtocolBridge.getInstance().getProtocolClient().getClientDNSConnection().getProxy()). + setSSLParameters(ProtocolBridge.getInstance().getProtocolClient().getClientDNSConnection().getSocket().getSSLParameters()). + + // Set certificates and folders for SSL + setRootCAFolder(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicCAFolder). + setClientCertificatesFolder(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicClientFolder, + ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().privateClientFolder). + + + // Finalize the client setup build(); + // Connect to the pipeline clientToWebPipeline.connect(); + // Wait until the pipeline connection is established while (!clientToWebPipeline.isConnected()) if (clientToWebPipeline.isConnected()) break; - SSLSocketFactory sslSocketFactory = NetworkClient.ClientBuilder.createSSLSocketFactory(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicCAFolder, ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicClientFolder, ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().privateClientFolder); + // Create SSL socket factory using client certificates + SSLSocketFactory sslSocketFactory = NetworkClient.ClientBuilder. + createSSLSocketFactory(ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicCAFolder, + ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().publicClientFolder, + ProtocolBridge.getInstance().getProtocolClient().getFolderStructure().privateClientFolder); + + // Get proxy settings from the pipeline client Proxy proxy = clientToWebPipeline.getProxy(); + + // Establish SSL connection to the web server SSLSocket tempSocket; if (sslSocketFactory == null) { throw new ConnectException("SSL socket factory not set. Client certificate required!"); } else { + // Create raw socket and wrap it in SSL socket if (proxy != null) { Socket rawSocket = new Socket(proxy); rawSocket.connect(new InetSocketAddress(domain.getDestination(), webPort), 0); @@ -49,16 +109,20 @@ public final class WebClient { } clientToWebServer = tempSocket; + + // Configure SSL parameters SSLParameters sslParameters = clientToWebPipeline.getSocket().getSSLParameters(); if (sslParameters != null) { clientToWebServer.setSSLParameters(sslParameters); } else { + // Set default to TLSv1.3 if no parameters are provided SSLParameters defaultParams = clientToWebServer.getSSLParameters(); defaultParams.setProtocols(new String[]{"TLSv1.3"}); clientToWebServer.setSSLParameters(defaultParams); } + // Configure socket options clientToWebServer.setTcpNoDelay(true); clientToWebServer.setSoTimeout(0); @@ -68,26 +132,46 @@ public final class WebClient { throw new ConnectException("Handshake failed: " + handshakeEx.getMessage()); } + // Initialize object streams for communication this.outputStream = new ObjectOutputStream(clientToWebServer.getOutputStream()); this.inputStream = new ObjectInputStream(clientToWebServer.getInputStream()); + + // Start the receive thread this.receiveThread.start(); } } + /** + * Gets the NetworkClient used for the pipeline connection to the web server. + * @return The NetworkClient connected to the web server pipeline. + */ public NetworkClient getClientPipelineConnection() { return clientToWebPipeline; } + /** + * Gets the SSLSocket used for communication with the web server. + * @return The SSLSocket connected to the web server. + */ public SSLSocket getClientWebConnection() { return clientToWebServer; } + /** + * Checks if the WebClient is currently connected to the web server. + * @return true if connected, false otherwise. + */ public boolean isConnected() { return this.clientToWebServer != null && this.clientToWebServer.isConnected() && !this.clientToWebServer.isClosed() && this.receiveThread.isAlive() && !this.receiveThread.isInterrupted() && ProtocolBridge.getInstance().getProtocolClient().getClientDNSConnection().isConnected() && clientToWebPipeline.isConnected(); } + /** + * Continuously receives data from the web server. + * This method runs in a separate thread and handles incoming objects. + * If an error occurs, it attempts to close the connection. + */ private void receive() { try { while (this.isConnected()) { @@ -102,6 +186,11 @@ public final class WebClient { } } + /** + * Closes the connection to the web server and releases resources. + * @return true if the connection was successfully closed, false if it was already closed. + * @throws IOException If an I/O error occurs during closure. + */ public synchronized boolean closeConnection() throws IOException { if (!this.isConnected()) { return false; @@ -109,7 +198,10 @@ public final class WebClient { clientToWebPipeline.disconnect(); try { + // Interrupt the receive thread this.receiveThread.interrupt(); + + // Close streams and socket if (this.outputStream != null) { this.outputStream.close(); } @@ -122,6 +214,7 @@ public final class WebClient { this.clientToWebServer.close(); } } finally { + // Nullify references to help with garbage collection this.clientToWebServer = null; this.outputStream = null; this.inputStream = null; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolServer.java b/src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolDNSServerEvent.java similarity index 68% rename from src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolServer.java rename to src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolDNSServerEvent.java index 4cbfd36..2695cec 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolServer.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/client/events/ConnectedToProtocolDNSServerEvent.java @@ -4,6 +4,9 @@ import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Event triggered when a client successfully connects to a DNS protocol server. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) -public class ConnectedToProtocolServer extends Event { +public class ConnectedToProtocolDNSServerEvent extends Event { } diff --git a/src/main/java/org/openautonomousconnection/protocol/side/dns/ConnectedProtocolClient.java b/src/main/java/org/openautonomousconnection/protocol/side/dns/ConnectedProtocolClient.java index 66d2d7e..417be26 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/dns/ConnectedProtocolClient.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/dns/ConnectedProtocolClient.java @@ -6,83 +6,153 @@ import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.packets.OACPacket; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Represents a connected protocol client on the DNS server side. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public final class ConnectedProtocolClient { + /** + * The connection handler associated with this protocol client. + */ @Getter private final ConnectionHandler connectionHandler; + /** + * The protocol version of the connected client. + */ private ProtocolVersion clientVersion = null; public ConnectedProtocolClient(ConnectionHandler connectionHandler) { this.connectionHandler = connectionHandler; } + /** + * Gets the protocol version of the connected client. + * Defaults to PV_1_0_0_CLASSIC if not set. + * @return The protocol version of the client. + */ public ProtocolVersion getClientVersion() { return clientVersion == null ? ProtocolVersion.PV_1_0_0_CLASSIC : clientVersion; } + /** + * Sets the protocol version of the connected client. + * @param clientVersion The protocol version to set. + */ public void setClientVersion(ProtocolVersion clientVersion) { if (clientVersion == null) this.clientVersion = clientVersion; } + /** + * Checks if the connected client is a stable client. + * @return True if the client is stable, false otherwise. + */ public boolean isStableClient() { + // Check if the server version is stable return !isBetaClient() && !isClassicClient(); } + /** + * Checks if the connected client supports stable protocol versions. + * @return True if the client supports stable versions, false otherwise. + */ public boolean supportClientStable() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is stable yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.STABLE; if (yes) break; } + // Check if the client version is stable return isStableClient() || yes; } + /** + * Checks if the connected client is a beta client. + * @return True if the client is beta, false otherwise. + */ public boolean isBetaClient() { + // Check if the server version is beta return getClientVersion().getProtocolType() == ProtocolVersion.ProtocolType.BETA; } + /** + * Checks if the connected client supports beta protocol versions. + * @return True if the client supports beta versions, false otherwise. + */ public boolean supportClientBeta() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is beta yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.BETA; if (yes) break; } + // Check if the client version is beta return isBetaClient() || yes; } + /** + * Checks if the connected client is a classic client. + * @return True if the client is classic, false otherwise. + */ public boolean isClassicClient() { + // Check if the server version is classic return getClientVersion().getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; } + + /** + * Checks if the connected client supports classic protocol versions. + * @return True if the client supports classic versions, false otherwise. + */ public boolean supportClientClassic() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is classic yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; if (yes) break; } + // Check if the client version is classic return isClassicClient() || yes; } + /** + * Checks if the connected client supports the given packet's protocol version. + * @param packet The packet to check. + * @return True if the client supports the packet's protocol version, false otherwise. + */ public boolean supportClientPacket(OACPacket packet) { return supportClientVersion(packet.getProtocolVersion()); } + /** + * Checks if the connected client supports the given protocol version. + * @param targetVersion The protocol version to check. + * @return True if the client supports the target version, false otherwise. + */ public boolean supportClientVersion(ProtocolVersion targetVersion) { + // Check if the client version matches or is compatible with the target version return getClientVersion() == targetVersion || getClientVersion().getCompatibleVersions().contains(targetVersion); } + /** + * Checks if the connected client supports the given protocol. + * @param protocol The protocol to check. + * @return True if the client supports the protocol, false otherwise. + */ public boolean supportClientProtocol(ProtocolVersion.Protocol protocol) { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version supports the protocol yes = compatibleVersion.getSupportedProtocols().contains(protocol); if (yes) break; } + // Check if the client version supports the protocol return getClientVersion().getSupportedProtocols().contains(protocol) || yes; } } diff --git a/src/main/java/org/openautonomousconnection/protocol/side/dns/ProtocolDNSServer.java b/src/main/java/org/openautonomousconnection/protocol/side/dns/ProtocolDNSServer.java index 104a589..3e77132 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/dns/ProtocolDNSServer.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/dns/ProtocolDNSServer.java @@ -18,23 +18,50 @@ import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; +/** + * Abstract class representing a DNS server in the protocol. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public abstract class ProtocolDNSServer extends DefaultMethodsOverrider { + /** + * The network server instance. + */ @Getter private final NetworkServer networkServer; + + /** + * The configuration manager for handling server configurations. + */ private final ConfigurationManager configurationManager; + + /** + * List of connected protocol clients. + */ @Getter private List clients; + /** + * The folder structure for server certificates. + */ @Getter private ServerCertificateFolderStructure folderStructure; + /** + * Constructs a ProtocolDNSServer with the specified configuration file. + * + * @param configFile The configuration file for the DNS server. + * @throws IOException If an I/O error occurs. + * @throws CertificateException If a certificate error occurs. + */ public ProtocolDNSServer(File configFile) throws IOException, CertificateException { + // Ensure the configuration file exists if (!configFile.exists()) configFile.createNewFile(); + // Load the configuration properties configurationManager = new ConfigurationManager(configFile); configurationManager.loadProperties(); + // Set default values for configuration properties if not already set if (!configurationManager.isSet("server.site.info")) { configurationManager.set("server.site.info", "DNS-SERVER INFO SITE IP"); configurationManager.saveProperties(); @@ -45,8 +72,10 @@ public abstract class ProtocolDNSServer extends DefaultMethodsOverrider { configurationManager.saveProperties(); } + // Initialize the folder structure folderStructure = new ServerCertificateFolderStructure(); + // Check for the existence of necessary certificate files checkFileExists(folderStructure.publicCAFolder, folderStructure.caPrefix, ".pem"); checkFileExists(folderStructure.publicCAFolder, folderStructure.caPrefix, ".srl"); checkFileExists(folderStructure.privateCAFolder, folderStructure.caPrefix, ".key"); @@ -54,13 +83,17 @@ public abstract class ProtocolDNSServer extends DefaultMethodsOverrider { checkFileExists(folderStructure.publicServerFolder, folderStructure.certPrefix, ".crt"); checkFileExists(folderStructure.privateServerFolder, folderStructure.certPrefix, ".key"); + // Define the certificate and key files based on the public IP address File certFile = new File(folderStructure.publicServerFolder, folderStructure.certPrefix + NetworkUtils.getPublicIPAddress() + ".crt"); File keyFile = new File(folderStructure.privateServerFolder, folderStructure.certPrefix + NetworkUtils.getPublicIPAddress() + ".key"); + // Initialize the protocol bridge and clients list ProtocolBridge protocolBridge = ProtocolBridge.getInstance(); this.clients = new ArrayList<>(); - this.networkServer = new NetworkServer.ServerBuilder().setLogger(protocolBridge.getLogger()). + // Build the network server with the specified settings + this.networkServer = new NetworkServer.ServerBuilder(). + setLogger(protocolBridge.getLogger()). setEventManager(protocolBridge.getProtocolSettings().eventManager). setPacketHandler(protocolBridge.getProtocolSettings().packetHandler). setPort(protocolBridge.getProtocolSettings().port). @@ -68,51 +101,126 @@ public abstract class ProtocolDNSServer extends DefaultMethodsOverrider { build(); } + + /** + * Checks if the required certificate files exist in the specified folder. + * @param folder The folder to check for certificate files. + * @param prefix The prefix of the certificate files. + * @param extension The extension of the certificate files. + * @throws CertificateException If a certificate error occurs. + * @throws IOException If an I/O error occurs. + */ private final void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException { boolean found = false; + + // Check if the folder exists if (folder == null) throw new FileNotFoundException("Folder does not exist"); + // List all files in the folder File[] files = folder.listFiles(); + + // Check if the folder is empty if (files == null || files.length == 0) throw new FileNotFoundException("Folder " + folder.getAbsolutePath() + " is empty"); + // Validate each file in the folder for (File file : files) { if (!file.getName().startsWith(prefix) || !file.getName().endsWith(extension)) throw new CertificateException(file.getAbsolutePath() + " is not valid"); + + // Check if the file matches the expected naming convention if (!found) found = file.getName().equalsIgnoreCase(prefix + NetworkUtils.getPublicIPAddress() + extension); } + // If the required file is not found, throw an exception if (!found) throw new CertificateException("Missing " + prefix + NetworkUtils.getPublicIPAddress() + extension); } + /** + * Retrieves a connected protocol client by its client ID. + * @param clientID The ID of the client to retrieve. + * @return The ConnectedProtocolClient with the specified ID, or null if not found. + */ public final ConnectedProtocolClient getClientByID(int clientID) { for (ConnectedProtocolClient client : clients) if (client.getConnectionHandler().getClientID() == clientID) return client; return null; } + /** + * Gets the DNS information site URL from the configuration. + * @return The DNS information site URL. + */ public final String getDNSInfoSite() { return configurationManager.getString("server.site.info"); } + /** + * Gets the DNS registration site URL from the configuration. + * @return The DNS registration site URL. + */ public final String getDNSRegisterSite() { return configurationManager.getString("server.site.register"); } + /** + * Abstract method to retrieve the list of domains managed by the DNS server. + * @return A list of Domain objects. + */ public abstract List getDomains(); + /** + * @see Domain#getDestination() + * Abstract method to get the destination for a given domain. + * @param domain The domain to look up. + * @return The destination associated with the domain. + */ public abstract String getDomainDestination(Domain domain); + /** + * @see Domain#getDestination() + * Abstract method to get the destination for a given subname under a specific domain. + * @param domain The parent domain. + * @param subname The subname to look up. + * @return The destination associated with the subname. + */ public abstract String getSubnameDestination(Domain domain, String subname); + /** + * @see Domain#getDestination() + * Abstract method to get the top-level domain information site URL. + * @param topLevelName The top-level domain name. + * @return The information site URL for the specified top-level domain. + */ public abstract String getTLNInfoSite(String topLevelName); + /** + * Abstract method to validate a requested domain. + * @param requestedDomain The domain to validate. + * @return A DNSResponseCode indicating the result of the validation. + */ public abstract DNSResponseCode validateDomain(Domain requestedDomain); + /** + * Abstract method called when a validation packet fails to send. + * @param domain The domain associated with the validation. + * @param client The connected protocol client. + * @param exception The exception that occurred during sending. + */ public abstract void validationPacketSendFailed(Domain domain, ConnectedProtocolClient client, Exception exception); + /** + * Abstract method called when a domain destination packet fails to send. + * @param client The connected protocol client. + * @param domain The domain associated with the packet. + * @param validationResponse The DNS response code from validation. + * @param exception The exception that occurred during sending. + */ public abstract void domainDestinationPacketFailedSend(ConnectedProtocolClient client, Domain domain, DNSResponseCode validationResponse, Exception exception); + /** + * Class representing the folder structure for server certificates. + */ public final class ServerCertificateFolderStructure { public final File certificatesFolder; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/dns/events/ConnectedProtocolClientEvent.java b/src/main/java/org/openautonomousconnection/protocol/side/dns/events/ConnectedProtocolClientEvent.java index f5a618c..ef76840 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/dns/events/ConnectedProtocolClientEvent.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/dns/events/ConnectedProtocolClientEvent.java @@ -6,6 +6,9 @@ import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Event triggered when a protocol client connects to the DNS server. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public final class ConnectedProtocolClientEvent extends Event { diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/ConnectedWebClient.java b/src/main/java/org/openautonomousconnection/protocol/side/web/ConnectedWebClient.java index 30954d0..be99fb6 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/ConnectedWebClient.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/ConnectedWebClient.java @@ -18,64 +18,140 @@ import java.nio.file.Files; import java.util.HashMap; import java.util.Map; +/** + * Represents a connected web client. + * Manages the connection, handles HTTP requests, and serves files. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public final class ConnectedWebClient { + /** + * The connection handler associated with this web client. + */ @Getter private final ConnectionHandler pipelineConnection; + /** + * The SSL socket for the web client connection. + */ @Getter private SSLSocket webSocket; + + /** + * The output stream for sending data to the client. + */ private ObjectOutputStream outputStream; + + /** + * The input stream for receiving data from the client. + */ private ObjectInputStream inputStream; - private ProtocolVersion clientVersion = null; private final Thread receiveThread = new Thread(this::receive); + + /** + * The protocol version of the connected client. + */ + private ProtocolVersion clientVersion = null; + + /** + * Thread for receiving data from the client. + */ + private final Thread receiveThread = new Thread(this::receive); + + /** + * Indicates if the client version has been loaded. + */ @Getter private boolean clientVersionLoaded = false; + /** + * Constructs a ConnectedWebClient with the given connection handler. + * @param pipelineConnection The connection handler for the web client. + */ public ConnectedWebClient(ConnectionHandler pipelineConnection) { this.pipelineConnection = pipelineConnection; } + /** + * Sends an HTTP redirect response to the client. + * @param out The output stream to send the response to. + * @param location The URL to redirect to. + * @param cookies Optional cookies to set in the response. + * @throws IOException If an I/O error occurs. + */ private static void sendRedirect(OutputStream out, String location, Map cookies) throws IOException { - out.write(("HTTP/1.1 302 Found\r\n").getBytes()); + // Send HTTP 302 Found response with Location header + out.write(("OAC 302 Found\r\n").getBytes()); out.write(("Location: " + location + "\r\n").getBytes()); + + // Set cookies if provided if (cookies != null) { for (var entry : cookies.entrySet()) { out.write((entry.getKey() + ": " + entry.getValue() + "\r\n").getBytes()); } } + + // End of headers out.write("\r\n".getBytes()); out.flush(); } + /** + * Parses POST parameters from the input stream. + * @param in The input stream to read from. + * @return A map of POST parameter names to values. + * @throws IOException If an I/O error occurs. + */ private static Map parsePostParams(InputStream in) throws IOException { + // Read the entire input stream into a string BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); StringBuilder sb = new StringBuilder(); while (reader.ready()) { sb.append((char) reader.read()); } + + // Split the string into key-value pairs and decode them Map map = new HashMap<>(); String[] pairs = sb.toString().split("&"); for (String p : pairs) { + // Split each pair into key and value String[] kv = p.split("=", 2); if (kv.length == 2) + // Decode and store in the map map.put(URLDecoder.decode(kv[0], StandardCharsets.UTF_8), URLDecoder.decode(kv[1], StandardCharsets.UTF_8)); } return map; } + /** + * Normalizes a file path to prevent directory traversal attacks. + * @param path The raw file path. + * @return The normalized file path. + */ private static String normalizePath(String path) { + // Replace backslashes with forward slashes and remove ".." segments path = path.replace("/", File.separator).replace("\\", "/"); + // Remove any ".." segments to prevent directory traversal while (path.contains("..")) path = path.replace("..", ""); + + // Remove leading slashes if (path.startsWith("/")) path = path.substring(1); + return path; } + /** + * Parses query parameters from a raw URL path. + * @param rawPath The raw URL path containing query parameters. + * @return A map of query parameter names to values. + */ private static Map parseQueryParams(String rawPath) { + // Extract query parameters from the URL path Map map = new HashMap<>(); if (rawPath.contains("?")) { + // Split the query string into key-value pairs String[] params = rawPath.substring(rawPath.indexOf("?") + 1).split("&"); for (String p : params) { + // Split each pair into key and value String[] kv = p.split("="); if (kv.length == 2) map.put(kv[0], kv[1]); } @@ -83,17 +159,32 @@ public final class ConnectedWebClient { return map; } + /** + * Checks if the request is a multipart/form-data request. + * @param headers The HTTP headers of the request. + * @return True if the request is multipart/form-data, false otherwise. + */ private static boolean isMultipart(Map headers) { String contentType = headers.get("content-type"); return contentType != null && contentType.startsWith("multipart/form-data"); } + /** + * Handles a multipart/form-data request, saving uploaded files to the specified directory. + * @param in The input stream to read the request body from. + * @param headers The HTTP headers of the request. + * @param uploadDir The directory to save uploaded files to. + * @throws IOException If an I/O error occurs. + */ private static void handleMultipart(InputStream in, Map headers, File uploadDir) throws IOException { + // Ensure the upload directory exists if (!uploadDir.exists()) uploadDir.mkdirs(); + // Extract the boundary from the Content-Type header String contentType = headers.get("content-type"); String boundary = "--" + contentType.split("boundary=")[1]; + // Read the entire request body into a buffer ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] lineBuffer = new byte[8192]; int read; @@ -102,14 +193,17 @@ public final class ConnectedWebClient { if (buffer.size() > 10 * 1024 * 1024) break; // 10 MB max } + // Parse the multipart data String data = buffer.toString(StandardCharsets.UTF_8); String[] parts = data.split(boundary); + // Process each part for (String part : parts) { if (part.contains("Content-Disposition")) { String name = null; String filename = null; + // Extract headers from the part for (String headerLine : part.split("\r\n")) { if (headerLine.startsWith("Content-Disposition")) { if (headerLine.contains("filename=\"")) { @@ -125,6 +219,7 @@ public final class ConnectedWebClient { } } + // Save the file if a filename is provided if (filename != null && !filename.isEmpty()) { int headerEnd = part.indexOf("\r\n\r\n"); byte[] fileData = part.substring(headerEnd + 4).getBytes(StandardCharsets.UTF_8); @@ -135,54 +230,89 @@ public final class ConnectedWebClient { } } - private static String renderPHP(File file) throws IOException, InterruptedException { - ProcessBuilder pb = new ProcessBuilder("php", file.getAbsolutePath()); - pb.redirectErrorStream(true); - Process p = pb.start(); - ByteArrayOutputStream output = new ByteArrayOutputStream(); - InputStream processIn = p.getInputStream(); - byte[] buf = new byte[8192]; - int read; - while ((read = processIn.read(buf)) != -1) { - output.write(buf, 0, read); - } - p.waitFor(); - return output.toString(StandardCharsets.UTF_8); - } - + /** + * Sends an response to the client. + * @param out + * @param code + * @param file + * @param headers + * @throws IOException + */ private static void sendResponse(OutputStream out, int code, File file, Map headers) throws IOException { byte[] body = Files.readAllBytes(file.toPath()); sendResponse(out, code, body, "text/html", headers); } + /** + * Sends an response to the client. + * @param out The output stream to send the response to. + * @param code The HTTP status code. + * @param file The file to read the response body from. + * @throws IOException If an I/O error occurs. + */ private static void sendResponse(OutputStream out, int code, File file) throws IOException { sendResponse(out, code, Files.readString(file.toPath()), "text/html"); } + /** + * Sends an response to the client. + * @param out The output stream to send the response to. + * @param code The HTTP status code. + * @param body The response body as a string. + * @param contentType The content type of the response. + * @throws IOException If an I/O error occurs. + */ private static void sendResponse(OutputStream out, int code, String body, String contentType) throws IOException { sendResponse(out, code, body.getBytes(StandardCharsets.UTF_8), contentType, null); } + /** + * Sends an response to the client. + * @param out The output stream to send the response to. + * @param code The HTTP status code. + * @param file The file to read the response body from. + * @param contentType The content type of the response. + * @throws IOException If an I/O error occurs. + */ private static void sendResponse(OutputStream out, int code, File file, String contentType) throws IOException { byte[] bytes = Files.readAllBytes(file.toPath()); sendResponse(out, code, bytes, contentType, null); } + /** + * Sends an response to the client. + * @param out The output stream to send the response to. + * @param code The HTTP status code. + * @param body The response body as a byte array. + * @param contentType The content type of the response. + * @param headers Additional headers to include in the response. + * @throws IOException If an I/O error occurs. + */ private static void sendResponse(OutputStream out, int code, byte[] body, String contentType, Map headers) throws IOException { - out.write(("HTTP/1.1 " + code + " " + getStatusText(code) + "\r\n").getBytes()); + // Send response status line and headers + out.write(("OAC " + code + " " + getStatusText(code) + "\r\n").getBytes()); out.write(("Content-Type: " + contentType + "\r\n").getBytes()); out.write(("Content-Length: " + body.length + "\r\n").getBytes()); + + // Write additional headers if provided if (headers != null) headers.forEach((k, v) -> { try { out.write((k + ": " + v + "\r\n").getBytes()); } catch (IOException ignored) { } }); + + // End of headers out.write("\r\n".getBytes()); out.write(body); out.flush(); } + /** + * Returns the standard status text for a given status code. + * @param code The status code. + * @return The corresponding status text. + */ private static String getStatusText(int code) { return switch (code) { case 200 -> "OK"; @@ -197,6 +327,11 @@ public final class ConnectedWebClient { }; } + /** + * Returns the content type based on the file extension. + * @param name The file name. + * @return The corresponding content type. + */ private static String getContentType(String name) { return switch (name.substring(name.lastIndexOf('.') + 1).toLowerCase()) { case "html", "php" -> "text/html"; @@ -213,11 +348,20 @@ public final class ConnectedWebClient { }; } + /** + * Renders a PHP file by executing it with the PHP interpreter and captures cookies. + * @param file The PHP file to render. + * @return A PHPResponse containing the output and cookies. + * @throws IOException If an I/O error occurs. + * @throws InterruptedException If the process is interrupted. + */ private static PHPResponse renderPHPWithCookies(File file) throws IOException, InterruptedException { + // Execute the PHP file using the PHP interpreter ProcessBuilder pb = new ProcessBuilder("php", file.getAbsolutePath()); pb.redirectErrorStream(true); Process p = pb.start(); + // Capture the output of the PHP process ByteArrayOutputStream output = new ByteArrayOutputStream(); InputStream processIn = p.getInputStream(); byte[] buf = new byte[8192]; @@ -227,14 +371,19 @@ public final class ConnectedWebClient { } p.waitFor(); + // Parse the output to separate headers and body, and extract cookies String fullOutput = output.toString(StandardCharsets.UTF_8); Map cookies = new HashMap<>(); + // Split headers and body String[] parts = fullOutput.split("\r\n\r\n", 2); String body; if (parts.length == 2) { + // Get headers and body String headers = parts[0]; body = parts[1]; + + // Extract cookies from headers for (String headerLine : headers.split("\r\n")) { if (headerLine.toLowerCase().startsWith("set-cookie:")) { String cookie = headerLine.substring("set-cookie:".length()).trim(); @@ -244,114 +393,194 @@ public final class ConnectedWebClient { } } } else { + // No headers, only body body = fullOutput; } return new PHPResponse(body, cookies); } + /** + * Sets the SSL socket for the web client and starts the receive thread. + * @param webSocket The SSL socket to set. + */ public void setWebSocket(SSLSocket webSocket) { if (webSocket != null) this.webSocket = webSocket; this.receiveThread.start(); } + /** + * Checks if the web client is currently connected. + * @return True if connected, false otherwise. + */ public boolean isConnected() { return this.webSocket != null && this.webSocket.isConnected() && !this.webSocket.isClosed() && this.receiveThread.isAlive() && pipelineConnection.isConnected(); } + /** + * Disconnects the web client, closing streams and the socket. + * @return True if disconnection was successful, false if already disconnected. + */ public synchronized boolean disconnect() { if (!this.isConnected()) { return false; } else { + // Disconnect the underlying connection handler pipelineConnection.disconnect(); + // Interrupt the receive thread if it's still alive if (this.receiveThread.isAlive()) { this.receiveThread.interrupt(); } try { + // Close streams and the socket this.outputStream.close(); this.inputStream.close(); this.webSocket.close(); } catch (IOException var2) { } + // Nullify references this.webSocket = null; this.outputStream = null; this.inputStream = null; + return true; } } + /** + * Gets the protocol version of the connected client. + * @return The protocol version of the client, defaults to PV_1_0_0_CLASSIC if not set. + */ public ProtocolVersion getClientVersion() { return clientVersion == null ? ProtocolVersion.PV_1_0_0_CLASSIC : clientVersion; } + /** + * Sets the protocol version of the connected client. + * @param clientVersion The protocol version to set. + */ public void setClientVersion(ProtocolVersion clientVersion) { if (!clientVersionLoaded) clientVersionLoaded = true; if (clientVersion == null) this.clientVersion = clientVersion; } + /** + * Checks if the connected client is a stable client. + * @return True if the client is stable, false otherwise. + */ public boolean isStableClient() { + // Check if the server version is stable return !isBetaClient() && !isClassicClient(); } + /** + * Checks if the connected client supports stable protocol versions. + * @return True if the client supports stable versions, false otherwise. + */ public boolean supportClientStable() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is stable yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.STABLE; if (yes) break; } + // Check if the client version is stable return isStableClient() || yes; } + /** + * Checks if the connected client is a beta client. + * @return True if the client is beta, false otherwise. + */ public boolean isBetaClient() { + // Check if the server version is beta return getClientVersion().getProtocolType() == ProtocolVersion.ProtocolType.BETA; } + /** + * Checks if the connected client supports beta protocol versions. + * @return True if the client supports beta versions, false otherwise. + */ public boolean supportClientBeta() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is beta yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.BETA; if (yes) break; } + // Check if the client version is beta return isBetaClient() || yes; } + /** + * Checks if the connected client is a classic client. + * @return True if the client is classic, false otherwise. + */ public boolean isClassicClient() { return getClientVersion().getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; } + /** + * Checks if the connected client supports classic protocol versions. + * @return True if the client supports classic versions, false otherwise. + */ public boolean supportClientClassic() { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version is classic yes = compatibleVersion.getProtocolType() == ProtocolVersion.ProtocolType.CLASSIC; if (yes) break; } + // Check if the client version is classic return isClassicClient() || yes; } + /** + * Checks if the connected client supports the protocol version of the given packet. + * @param packet The packet to check support for. + * @return True if the client supports the packet's protocol version, false otherwise. + */ public boolean supportClientPacket(OACPacket packet) { return supportClientVersion(packet.getProtocolVersion()); } + /** + * Checks if the connected client supports the given protocol version. + * @param targetVersion The protocol version to check support for. + * @return True if the client supports the target version, false otherwise. + */ public boolean supportClientVersion(ProtocolVersion targetVersion) { + // Check if the client version matches the target version or is compatible return getClientVersion() == targetVersion || getClientVersion().getCompatibleVersions().contains(targetVersion); } + /** + * Checks if the connected client supports the given protocol. + * @param protocol The protocol to check support for. + * @return True if the client supports the protocol, false otherwise. + */ public boolean supportClientProtocol(ProtocolVersion.Protocol protocol) { boolean yes = false; for (ProtocolVersion compatibleVersion : getClientVersion().getCompatibleVersions()) { + // Check if compatible version supports the protocol yes = compatibleVersion.getSupportedProtocols().contains(protocol); if (yes) break; } + // Check if the client version supports the protocol return getClientVersion().getSupportedProtocols().contains(protocol) || yes; } + /** + * Receives and processes requests from the client. + * Handles authentication, file serving, and PHP rendering. + */ private void receive() { try { while (this.isConnected()) { @@ -447,6 +676,9 @@ public final class ConnectedWebClient { } } + /** + * Represents the response from a PHP script, including body and cookies. + */ private static class PHPResponse { String body; Map cookies; @@ -457,5 +689,4 @@ public final class ConnectedWebClient { } } - } diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/ProtocolWebServer.java b/src/main/java/org/openautonomousconnection/protocol/side/web/ProtocolWebServer.java index 678c49e..bdf6a40 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/ProtocolWebServer.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/ProtocolWebServer.java @@ -20,43 +20,96 @@ import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.List; +/** + * Represents the web server for the protocol. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public class ProtocolWebServer { + /** + * Folder for web content. + */ @Getter private final File contentFolder; + + /** + * Folder for error pages. + */ @Getter private final File errorsFolder; + + /** + * Structure for server. + */ @Getter private final ServerCertificateFolderStructure folderStructure; + + /** + * Configuration manager for server settings. + */ private final ConfigurationManager configurationManager; + + /** + * Certificate files for SSL. + */ private final File certFile; + /** + * Certificate files for SSL. + */ private final File keyFile; + + + /** + * The network server handling pipeline connections. + */ @Getter private NetworkServer pipelineServer; + + /** + * The SSL server socket for web connections. + */ @Getter private SSLServerSocket webServer; + + /** + * List of connected web clients. + */ @Getter private List clients; + /** + * Initializes the web server with the given configuration, authentication, and rules files. + * @param configFile The configuration file. + * @param authFile The authentication file. + * @param rulesFile The rules file. + * @throws Exception If an error occurs during initialization. + */ public ProtocolWebServer(File configFile, File authFile, File rulesFile) throws Exception { + // Initialize the list of connected clients this.clients = new ArrayList<>(); + // Set up folder structure for certificates folderStructure = new ServerCertificateFolderStructure(); + // Check for necessary certificate files checkFileExists(folderStructure.publicServerFolder, folderStructure.certPrefix, ".crt"); checkFileExists(folderStructure.privateServerFolder, folderStructure.certPrefix, ".key"); + // Load configuration settings this.configurationManager = getConfigurationManager(configFile); + // Set up content and error folders contentFolder = new File("content"); errorsFolder = new File("errors"); + // Create folders if they don't exist if (!contentFolder.exists()) contentFolder.mkdir(); if (!errorsFolder.exists()) errorsFolder.mkdir(); + // Set up certificate files based on public IP address this.certFile = new File(folderStructure.publicServerFolder, folderStructure.certPrefix + NetworkUtils.getPublicIPAddress() + ".crt"); this.keyFile = new File(folderStructure.privateServerFolder, folderStructure.certPrefix + NetworkUtils.getPublicIPAddress() + ".key"); + // Create auth and rules files with default content if they don't exist if (!authFile.exists()) { authFile.createNewFile(); FileUtils.writeFile(authFile, """ @@ -65,6 +118,7 @@ public class ProtocolWebServer { """); } + // Create default rules file if it doesn't exist if (!rulesFile.exists()) { rulesFile.createNewFile(); FileUtils.writeFile(rulesFile, """ @@ -85,9 +139,11 @@ public class ProtocolWebServer { """); } + // Load authentication and rules AuthManager.loadAuthFile(authFile); RuleManager.loadRules(rulesFile); + // Initialize the pipeline server pipelineServer = new NetworkServer.ServerBuilder(). setPort(configurationManager.getInt("port.pipeline")).setTimeout(0). setPacketHandler(ProtocolBridge.getInstance().getProtocolSettings().packetHandler).setEventManager(ProtocolBridge.getInstance().getProtocolSettings().eventManager). @@ -96,13 +152,26 @@ public class ProtocolWebServer { build(); } + /** + * Retrieves a connected web client by its client ID. + * @param clientID The client ID to search for. + * @return The connected web client with the specified ID, or null if not found. + */ public final ConnectedWebClient getClientByID(int clientID) { for (ConnectedWebClient client : clients) if (client.getPipelineConnection().getClientID() == clientID) return client; return null; } + /** + * Starts the web server to accept and handle client connections. + * @throws Exception If an error occurs while starting the server. + */ public final void startWebServer() throws Exception { + // Start the pipeline server + pipelineServer.start(); + + // Create the SSL server socket for web connections webServer = (SSLServerSocket) NetworkServer.ServerBuilder. createSSLServerSocketFactory(folderStructure.publicCAFolder, certFile, keyFile). createServerSocket(configurationManager.getInt("port")); @@ -112,8 +181,10 @@ public class ProtocolWebServer { // Stop WebServer if pipelineServer dies new Thread(() -> { while (true) { + // Check if the pipeline server is still running if (pipelineServer == null || !pipelineServer.getServerSocket().isBound()) { try { + // Stop the web server onPipelineStop(); } catch (IOException e) { pipelineServer.getLogger().exception("Failed to stop WebServer", e); @@ -122,6 +193,11 @@ public class ProtocolWebServer { Thread.currentThread().interrupt(); break; } + + try { + // Sleep for a while before checking again + Thread.sleep(1000); + } catch (InterruptedException ignored) {} } }).start(); @@ -129,40 +205,68 @@ public class ProtocolWebServer { new Thread(() -> { while (true) { try { + // Accept incoming client connections SSLSocket client = (SSLSocket) webServer.accept(); + for (ConnectedWebClient connectedWebClient : clients) { if (connectedWebClient.getPipelineConnection().getClientID() != -1 && connectedWebClient.isClientVersionLoaded()) { + // Assign socket to an existing connected client connectedWebClient.setWebSocket(client); } } } catch (IOException e) { - e.printStackTrace(); + pipelineServer.getLogger().exception("Failed to accept WebClient", e); } } }).start(); } + /** + * Handles the shutdown of the web server when the pipeline server stops. + * @throws IOException If an I/O error occurs while closing the web server. + */ private void onPipelineStop() throws IOException { webServer.close(); } + /** + * Checks if the required certificate files exist in the specified folder. + * @param folder The folder to check for certificate files. + * @param prefix The prefix of the certificate files. + * @param extension The extension of the certificate files. + * @throws CertificateException If a required certificate file is missing or invalid. + * @throws IOException If an I/O error occurs while checking the files. + */ private void checkFileExists(File folder, String prefix, String extension) throws CertificateException, IOException { boolean found = false; + + // Ensure the folder exists if (folder == null) throw new FileNotFoundException("Folder does not exist"); + // List all files in the folder File[] files = folder.listFiles(); if (files == null || files.length == 0) throw new FileNotFoundException("Folder " + folder.getAbsolutePath() + " is empty"); + // Check for the required certificate file for (File file : files) { if (!file.getName().startsWith(prefix) || !file.getName().endsWith(extension)) throw new CertificateException(file.getAbsolutePath() + " is not valid"); + + // Check for file matching the public IP address if (!found) found = file.getName().equalsIgnoreCase(prefix + NetworkUtils.getPublicIPAddress() + extension); } + // Throw exception if the required file is not found if (!found) throw new CertificateException("Missing " + prefix + NetworkUtils.getPublicIPAddress() + extension); } + /** + * Loads and initializes the configuration manager with default settings if necessary. + * @param configFile The configuration file to load. + * @return The initialized configuration manager. + * @throws IOException If an I/O error occurs while loading or saving the configuration. + */ private ConfigurationManager getConfigurationManager(File configFile) throws IOException { if (!configFile.exists()) configFile.createNewFile(); @@ -192,6 +296,9 @@ public class ProtocolWebServer { return configurationManager; } + /** + * Represents the folder structure for server certificates. + */ public final class ServerCertificateFolderStructure { public final File certificatesFolder; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/events/ConnectedWebClientEvent.java b/src/main/java/org/openautonomousconnection/protocol/side/web/events/ConnectedWebClientEvent.java index 2353e8c..6a2cfd4 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/events/ConnectedWebClientEvent.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/events/ConnectedWebClientEvent.java @@ -6,9 +6,15 @@ import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.web.ConnectedWebClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +/** + * Event triggered when a web client connects to the web server. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public final class ConnectedWebClientEvent extends Event { + /** + * The connected web client. + */ @Getter private final ConnectedWebClient webClient; diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/AuthManager.java b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/AuthManager.java index 8a88b7b..0cc228a 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/AuthManager.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/AuthManager.java @@ -11,16 +11,34 @@ import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; +/** + * Manages authentication for web clients. + * Loads user credentials from a file and verifies login attempts. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public class AuthManager { + /** + * Map of usernames to their SHA-256 hashed passwords + */ private static final Map users = new HashMap<>(); + /** + * Loads the authentication file and populates the users map. + * The file should contain lines in the format: username:hashed_password + * Lines starting with '#' are treated as comments and ignored. + * @param authFile The authentication file to load. + * @throws IOException If an I/O error occurs reading from the file. + */ public static void loadAuthFile(File authFile) throws IOException { + // Create the file if it doesn't exist if (!authFile.exists()) authFile.createNewFile(); for (String line : Files.readAllLines(authFile.toPath(), StandardCharsets.UTF_8)) { + // Trim whitespace and ignore comments/empty lines line = line.trim(); if (line.isEmpty() || line.startsWith("#")) continue; + + // Split the line into username and hashed password String[] parts = line.split(":", 2); if (parts.length == 2) { users.put(parts[0], parts[1]); @@ -28,19 +46,33 @@ public class AuthManager { } } + /** + * Checks if the provided login and password are valid. + * @param login The username to check. + * @param password The password to verify. + * @return True if the credentials are valid, false otherwise. + */ public static boolean checkAuth(String login, String password) { - + // Retrieve the stored hashed password for the given username String storedHash = users.get(login); if (storedHash == null) return false; + // Hash the provided password and compare it to the stored hash String hash = sha256(password); return storedHash.equalsIgnoreCase(hash); } + /** + * Computes the SHA-256 hash of the given input string. + * @param input The input string to hash. + * @return The hexadecimal representation of the SHA-256 hash. + */ private static String sha256(String input) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8)); + + // Convert the byte array to a hexadecimal string StringBuilder sb = new StringBuilder(); for (byte b : digest) sb.append(String.format("%02x", b)); return sb.toString(); diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/RuleManager.java b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/RuleManager.java index bbc3a3a..0d02392 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/RuleManager.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/RuleManager.java @@ -10,33 +10,79 @@ import java.nio.file.Files; import java.util.List; import java.util.Map; +/** + * Manages access rules for web resources. + * Loads allow, deny, and auth rules from a JSON file and provides methods to check access. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public class RuleManager { + /** + * Lists of path patterns for allow, deny, and auth rules + */ private static List allow; + + /** + * Lists of path patterns for allow, deny, and auth rules + */ private static List deny; + + /** + * Lists of path patterns for allow, deny, and auth rules + */ private static List auth; + /** + * Loads rules from a JSON file. + * The JSON should have the structure: + * { + * "allow": ["pattern1", "pattern2", ...], + * "deny": ["pattern1", "pattern2", ...], + * "auth": ["pattern1", "pattern2", ...] + * } + * @param rulesFile The JSON file containing the rules. + * @throws Exception If an error occurs reading the file or parsing JSON. + */ public static void loadRules(File rulesFile) throws Exception { + // Load and parse the JSON file String json = new String(Files.readAllBytes(rulesFile.toPath())); - Map> map = new Gson().fromJson(json, new TypeToken>>() { - }.getType()); + Map> map = new Gson().fromJson(json, new TypeToken>>() {}.getType()); + + // Default to empty lists if keys are missing allow = map.getOrDefault("allow", List.of()); deny = map.getOrDefault("deny", List.of()); auth = map.getOrDefault("auth", List.of()); } + /** Checks if the given path is allowed based on the allow rules. + * @param path The path to check. + * @return True if the path is allowed, false otherwise. + */ public static boolean isAllowed(String path) { return allow.stream().anyMatch(p -> pathMatches(path, p)); } + /** Checks if the given path is denied based on the deny rules. + * @param path The path to check. + * @return True if the path is denied, false otherwise. + */ public static boolean isDenied(String path) { return deny.stream().anyMatch(p -> pathMatches(path, p)); } + /** Checks if the given path requires authentication based on the auth rules. + * @param path The path to check. + * @return True if the path requires authentication, false otherwise. + */ public static boolean requiresAuth(String path) { return auth.stream().anyMatch(p -> pathMatches(path, p)); } + /** Helper method to check if a path matches a pattern. + * Patterns can include '*' as a wildcard. + * @param path The path to check. + * @param pattern The pattern to match against. + * @return True if the path matches the pattern, false otherwise. + */ private static boolean pathMatches(String path, String pattern) { pattern = pattern.replace("/", File.separator).replace("*", ".*"); return path.matches(pattern); diff --git a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/SessionManager.java b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/SessionManager.java index ad6dde7..6a8b936 100644 --- a/src/main/java/org/openautonomousconnection/protocol/side/web/managers/SessionManager.java +++ b/src/main/java/org/openautonomousconnection/protocol/side/web/managers/SessionManager.java @@ -10,49 +10,106 @@ import java.util.Base64; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +/** + * Manages user sessions for web clients. + * Provides methods to create, validate, and invalidate sessions. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public class SessionManager { + /** + * Map of session IDs to Session objects. + */ private static final Map sessions = new ConcurrentHashMap<>(); + + /** + * Secure random number generator for session ID creation. + */ private static final SecureRandom secureRandom = new SecureRandom(); + /** + * Creates a new session for the given user. + * @param login The username associated with the session. + * @param ip The IP address of the client. + * @param userAgent The User-Agent string of the client. + * @return The generated session ID. + * @throws IOException If an I/O error occurs. + */ public static String create(String login, String ip, String userAgent) throws IOException { + // Generate a secure random session ID byte[] bytes = new byte[32]; secureRandom.nextBytes(bytes); + + // Encode the bytes to a URL-safe Base64 string String sessionId = Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); + + // Create and store the new session sessions.put(sessionId, new Session(login, ip, userAgent)); return sessionId; } + /** + * Validates a session ID against the provided IP and User-Agent. + * @param sessionId The session ID to validate. + * @param ip The IP address of the client. + * @param userAgent The User-Agent string of the client. + * @return True if the session is valid, false otherwise. + * @throws IOException If an I/O error occurs. + */ public static boolean isValid(String sessionId, String ip, String userAgent) throws IOException { + // Retrieve the session associated with the session ID Session session = sessions.get(sessionId); + + // Check if the session exists, is not expired, and matches the IP and User-Agent if (session == null || session.isExpired() || !session.matches(ip, userAgent)) { sessions.remove(sessionId); return false; } + // Refresh the session expiration time session.refresh(); return true; } + /** + * Invalidates a session, removing it from the active sessions. + * @param sessionId The session ID to invalidate. + */ public static void invalidate(String sessionId) { sessions.remove(sessionId); } + /** + * Retrieves the username associated with a valid session ID. + * @param sessionId The session ID to look up. + * @return The username if the session is valid, null otherwise. + */ public static String getUser(String sessionId) { + // Retrieve the session associated with the session ID Session session = sessions.get(sessionId); + + // Check if the session exists and is not expired if (session == null || session.isExpired()) { sessions.remove(sessionId); return null; } + + // Return the username associated with the session return session.getLogin(); } + /** + * Cleans up expired sessions from the session map. + * This method should be called periodically to prevent memory leaks. + */ public static void cleanupExpiredSessions() { long now = System.currentTimeMillis(); sessions.entrySet().removeIf(entry -> entry.getValue().isExpired()); } + /** + * Represents a user session with associated metadata. + */ private static class Session { @Getter String login; @@ -67,14 +124,28 @@ public class SessionManager { this.expiresAt = System.currentTimeMillis() + Main.getConfigurationManager().getInt("sessionexpireminutes") * 60 * 1000; } + /** + * Checks if the session has expired. + * @return True if the session is expired, false otherwise. + */ boolean isExpired() { return System.currentTimeMillis() > expiresAt; } + /** + * Checks if the session matches the given IP and User-Agent. + * @param ip The IP address to check. + * @param userAgent The User-Agent string to check. + * @return True if both match, false otherwise. + */ boolean matches(String ip, String userAgent) { return this.ip.equals(ip) && this.userAgent.equals(userAgent); } + /** + * Refreshes the session's expiration time. + * @throws IOException If an I/O error occurs. + */ void refresh() throws IOException { this.expiresAt = System.currentTimeMillis() + Main.getConfigurationManager().getInt("sessionexpireminutes") * 60 * 1000; } diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/ProtocolVersion.java b/src/main/java/org/openautonomousconnection/protocol/versions/ProtocolVersion.java index 5234077..13620a1 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/ProtocolVersion.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/ProtocolVersion.java @@ -7,21 +7,59 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +/** + * Enum representing different protocol versions, their types, sides, and compatibility. + */ public enum ProtocolVersion implements Serializable { + /** + * Support for old OAC-Project => *_old + */ PV_1_0_0_CLASSIC("1.0.0", ProtocolType.CLASSIC, ProtocolSide.WEB_DNS, List.of(Protocol.HTTP)), + + /** + * First Beta Version of OAC-Protocol + */ PV_1_0_0_BETA("1.0.0", ProtocolType.BETA, ProtocolSide.ALL, List.of(Protocol.OAC), PV_1_0_0_CLASSIC); + /** + * The version string of the protocol version. + */ @Getter private final String version; + + /** + * The type of the protocol version. + */ @Getter private final ProtocolType protocolType; + + /** + * The side(s) the protocol version is intended for. + */ @Getter private final ProtocolSide protocolSide; + + /** + * List of protocol versions that are compatible with this version. + */ @Getter private final List compatibleVersions; + + /** + * List of supported protocols. + */ @Getter private final List supportedProtocols; + /** + * Constructor for ProtocolVersion enum. + * + * @param version The version string. + * @param protocolType The type of the protocol. + * @param protocolSide The side(s) the protocol is intended for. + * @param supportedProtocols List of supported protocols. + * @param compatibleVersions Varargs of compatible protocol versions. + */ ProtocolVersion(String version, ProtocolType protocolType, ProtocolSide protocolSide, List supportedProtocols, ProtocolVersion... compatibleVersions) { this.version = version; this.protocolType = protocolType; @@ -31,57 +69,123 @@ public enum ProtocolVersion implements Serializable { this.supportedProtocols = supportedProtocols; } + /** + * Returns a string representation of the protocol version, including its version, type, side, supported protocols, and compatible versions. + * @return a string representation of the protocol version. + */ @Override public final String toString() { StringBuilder compatible = new StringBuilder("["); StringBuilder supported = new StringBuilder("["); + for (ProtocolVersion compatibleVersion : compatibleVersions) compatible.append(compatibleVersion.buildName()); for (Protocol supportedProtocol : supportedProtocols) supported.append(supportedProtocol.toString()); + compatible.append("]"); supported.append("]"); return "{version=" + version + ";type=" + protocolType.toString() + ";side=" + protocolSide.toString() + ";supportedProtocols=" + supported + ";compatibleVersions=" + compatible + "}"; } + /** + * Builds a name for the protocol version combining its version and type. + * @return a string representing the name of the protocol version. + */ public final String buildName() { return version + "-" + protocolType.toString(); } + /** + * Enum representing different protocols. + */ public enum Protocol implements Serializable { HTTP, HTTPS, OAC; + /** + * Returns the name of the protocol in uppercase. + * @return the name of the protocol in uppercase. + */ @Override public final String toString() { return name().toUpperCase(); } } + /** + * Enum representing different types of protocol versions. + */ public enum ProtocolType implements Serializable { - CLASSIC, // -> See "_old" Projects https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/ + /** + * Classic Protocol Type, see old OAC-Project: *_old + */ + CLASSIC, + + /** + * Beta Protocol Type, may be unstable and subject to change. + */ BETA, + + /** + * Stable Protocol Type, recommended for production use. + */ STABLE; + /** + * Returns the name of the protocol in uppercase. + * @return the name of the protocol in uppercase. + */ @Override public final String toString() { return name().toUpperCase(); } } + /** + * Enum representing different sides where the protocol version can be used. + */ public enum ProtocolSide implements Serializable { + /** + * Client Side only + */ CLIENT, // Protocol version can only used on Client - DNS, // Protocol version can only used on DNS Server - WEB, // Protocol version can only used on Web Server - WEB_DNS, // Protocol version can only used on DNS and WebSerber + /** + * DNS Server Side only + */ + DNS, - CLIENT_DNS, // Protocol version can only used on DNS and Client - CLIENT_WEB, // Protocol version can only used on WebServer and Client + /** + * Web Server Side only + */ + WEB, - ALL // Protocol version can used on all Sides + /** + * Both DNS and Web Server Side + */ + WEB_DNS, + + /** + * Both Client and DNS Server Side + */ + CLIENT_DNS, + + /** + * Both Client and Web Server Side + */ + CLIENT_WEB, + + /** + * All Sides + */ + ALL ; + /** + * Returns the name of the protocol in uppercase. + * @return the name of the protocol in uppercase. + */ @Override public final String toString() { return name().toUpperCase(); diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/DNSResponseCode.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/DNSResponseCode.java index 9169563..8bf2a20 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/DNSResponseCode.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/DNSResponseCode.java @@ -4,42 +4,79 @@ import lombok.Getter; import java.io.Serializable; +/** + * Enum representing various DNS response codes and their descriptions. + */ public enum DNSResponseCode implements Serializable { + /** + * General Responses + */ RESPONSE_NOT_REQUIRED(0, "Response code not required"), RESPONSE_INVALID_REQUEST(1, "Invalid request"), + /** + * Authentication Responses + */ RESPONSE_AUTH_SUCCESS(4, "Auth success"), RESPONSE_AUTH_FAILED(5, "Auth failed"), + /** + * Domain Responses + */ RESPONSE_DOMAIN_NAME_EXIST(100, "Domainname exist"), RESPONSE_DOMAIN_NAME_NOT_EXIST(101, "Domainname does not exist"), RESPONSE_DOMAIN_NAME_CREATED(105, "Domainname created"), RESPONSE_DOMAIN_NAME_DELETED(106, "Domainname deleted"), + /** + * Top Level Name Responses + */ RESPONSE_DOMAIN_TLN_EXIST(110, "TopLevelName exist"), RESPONSE_DOMAIN_TLN_NOT_EXIST(111, "TopLevelName does not exist"), RESPONSE_DOMAIN_TLN_CREATED(115, "TopLevelName created"), RESPONSE_DOMAIN_TLN_DELETED(116, "TopLevelName deleted"), + /** + * Subname Responses + */ RESPONSE_DOMAIN_SUBNAME_EXIST(120, "Subname exist"), RESPONSE_DOMAIN_SUBNAME_NOT_EXIST(121, "Subname does not exist"), RESPONSE_DOMAIN_SUBNAME_CREATED(125, "Subname created"), RESPONSE_DOMAIN_SUBNAME_DELETED(126, "Subname deleted"), + /** + * Full Domain Responses + */ RESPONSE_DOMAIN_FULLY_EXIST(130, "Full domain exist"), RESPONSE_DOMAIN_FULLY_NOT_EXIST(131, "Full domain does not exist"); + /** + * The numeric code representing the DNS response. + */ @Getter private final int code; + /** + * A brief description of the DNS response code. + */ @Getter private final String description; + /** + * Constructor for DNSResponseCode enum. + * + * @param code The numeric code of the response. + * @param description A brief description of the response. + */ DNSResponseCode(int code, String description) { this.code = code; this.description = description; } + /** + * Returns a string representation of the DNS response code, including its code and description. + * @return a string representation of the DNS response code. + */ @Override public String toString() { return "{code=" + code + ";description=" + description + "}"; diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/Domain.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/Domain.java index d190bcf..ee0c550 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/Domain.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/beta/Domain.java @@ -9,22 +9,58 @@ import java.io.Serializable; import java.util.Arrays; import java.util.List; +/** + * Class representing a domain with its components such as subname, name, top-level name, path, query, fragment, and protocol. + */ public class Domain implements Serializable { + /** + * The subname of the domain (e.g., "sub" in "sub.example.com"). + */ @Getter private final String subname; + + /** + * The main name of the domain (e.g., "example" in "sub.example.com"). + */ @Getter private final String name; + + /** + * The top-level name of the domain (e.g., "com" in "sub.example.com"). + */ @Getter private final String topLevelName; + + /** + * The path component of the domain (e.g., "path/to/resource" in "example.com/path/to/resource"). + */ @Getter private String path; + + /** + * The query component of the domain (e.g., "key=value" in "example.com/path?key=value"). + */ @Getter private String query; + + /** + * The fragment component of the domain (e.g., "section1" in "example.com/path#section1"). + */ @Getter private String fragment; + + /** + * The protocol of the domain (e.g., "oac" in "oac://example.com"). + */ @Getter private String protocol; + /** + * Constructs a Domain object by parsing the provided full domain string. + * + * @param fullDomain The full domain string to parse. + * @throws IllegalArgumentException if the domain is invalid. + */ public Domain(String fullDomain) { // Remove protocol String domainWithPath = fullDomain.contains("://") ? fullDomain.split("://", 2)[1] : fullDomain; @@ -34,17 +70,21 @@ public class Domain implements Serializable { // Cut path String[] domainPartsAndPath = domainWithPath.split("/", 2); - String host = domainPartsAndPath[0]; // z.B. hello.world.com + + // Get host and full path + String host = domainPartsAndPath[0]; String fullPath = domainPartsAndPath.length > 1 ? "/" + domainPartsAndPath[1] : ""; // Split domain in labels List labels = Arrays.asList(host.split("\\.")); if (labels.size() < 2) throw new IllegalArgumentException("Invalid domain: " + host); + // Get subname, name and top-level name this.topLevelName = labels.getLast(); this.name = labels.get(labels.size() - 2); this.subname = labels.size() > 2 ? String.join(".", labels.subList(0, labels.size() - 2)) : null; + // Split fragment if (fullPath.contains("#")) { this.fragment = "#" + Arrays.stream(fullPath.split("#")).toList().getLast(); fullPath = fullPath.substring(0, fullPath.length() - ("#" + fragment).length()); @@ -60,6 +100,7 @@ public class Domain implements Serializable { this.query = ""; } + // Clean up path, query and fragment if (this.path.startsWith("/")) this.path = this.path.substring(1); if (this.path.endsWith("/")) this.path = this.path.substring(0, this.path.length() - 1); @@ -67,23 +108,41 @@ public class Domain implements Serializable { if (this.fragment.startsWith("#")) this.fragment = this.fragment.substring(1); } + /** + * Checks if the domain has a subname. + * @return true if the domain has a subname, false otherwise. + */ public final boolean hasSubname() { return subname != null; } + /** + * Checks if this domain is equal to another object. + * Two domains are considered equal if their subname, name, top-level name, and protocol are equal (case-insensitive). + * @return true if the domains are equal, false otherwise. + */ @Override public final boolean equals(Object obj) { + // Check if the object is an instance of Domain if (!(obj instanceof Domain domain)) return false; + // Compare subname, name, top-level name, and protocol (case-insensitive) return domain.getSubname().equalsIgnoreCase(this.subname) && domain.getName().equalsIgnoreCase(this.name) && domain.getTopLevelName().equalsIgnoreCase(this.topLevelName) && domain.getProtocol().equalsIgnoreCase(this.protocol); } + /** + * Returns the destination associated with this domain. + * The destination is determined based on the domain's components and the current protocol context. + * @return the destination as a string. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public final String getDestination() { + // If running as client or web server, return invalid request if (ProtocolBridge.getInstance().isRunningAsClient() || ProtocolBridge.getInstance().isRunningAsWebServer()) return DNSResponseCode.RESPONSE_INVALID_REQUEST.toString(); + // Handle special default domains if (this.equals(DefaultDomains.DNS_INFO_SITE)) return ProtocolBridge.getInstance().getProtocolDNSServer().getDNSInfoSite(); if (this.equals(DefaultDomains.DNS_REGISTER_SITE)) @@ -91,9 +150,14 @@ public class Domain implements Serializable { if (this.name.equalsIgnoreCase("about") && this.protocol.equalsIgnoreCase("oac")) return ProtocolBridge.getInstance().getProtocolDNSServer().getTLNInfoSite(topLevelName); + // Return destination based on whether subname exists return !hasSubname() ? ProtocolBridge.getInstance().getProtocolDNSServer().getDomainDestination(this) : ProtocolBridge.getInstance().getProtocolDNSServer().getSubnameDestination(this, subname); } + /** + * Returns a string representation of the domain, including its protocol, subname, name, top-level name, path, query, and fragment. + * @return a string representation of the domain. + */ public static class DefaultDomains { public static final Domain DNS_INFO_SITE = new Domain("oac://about.oac/"); public static final Domain DNS_REGISTER_SITE = new Domain("oac://register.oac/"); diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_SiteType.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_SiteType.java deleted file mode 100644 index 56816dc..0000000 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_SiteType.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; - -import java.io.Serializable; - -enum Classic_SiteType implements Serializable { - CLIENT("oac-client"), SERVER("oac-server"), - PUBLIC("oac"), PROTOCOL("oac-protocol"), LOCAL("oac-local"); - - public final String name; - - Classic_SiteType(String name) { - this.name = name; - } -} diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainPacketReceivedEvent.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_DomainPacketReceivedEvent.java similarity index 55% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainPacketReceivedEvent.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_DomainPacketReceivedEvent.java index 8da1907..a237687 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainPacketReceivedEvent.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_DomainPacketReceivedEvent.java @@ -1,7 +1,19 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.events; import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import org.openautonomousconnection.protocol.versions.ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; +/** + * This event is fired when a classic domain packet is received. + * This event is deprecated and will be marked for removal in future versions. + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerDNSServer + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerClient + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerWebServer + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_DomainPacketReceivedEvent extends Event { public final Classic_ProtocolVersion protocolVersion; diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_PingPacketReceivedEvent.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_PingPacketReceivedEvent.java similarity index 59% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_PingPacketReceivedEvent.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_PingPacketReceivedEvent.java index fdb1f76..56af978 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_PingPacketReceivedEvent.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/events/Classic_PingPacketReceivedEvent.java @@ -1,9 +1,19 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.events; import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; +/** + * This event is fired when a classic ping packet is received. + * This event is deprecated and will be marked for removal in future versions. + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerDNSServer + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerClient + * @see org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers.ClassicHandlerWebServer + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_PingPacketReceivedEvent extends Event { - public final Classic_ProtocolVersion protocolVersion; public final Classic_Domain domain; public final Classic_RequestDomain requestDomain; diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerClient.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerClient.java similarity index 72% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerClient.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerClient.java index 21dcbfb..4e420cc 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerClient.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerClient.java @@ -1,13 +1,18 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers; import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.packets.v1_0_0.classic.Classic_MessagePacket; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.site.Classic_SiteType; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; import java.io.IOException; - +/** + * Abstract class defining the client-side handler for Classic protocol operations. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.CLIENT) public abstract class ClassicHandlerClient { diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerDNSServer.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerDNSServer.java similarity index 68% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerDNSServer.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerDNSServer.java index 7632954..72bd937 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerDNSServer.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerDNSServer.java @@ -1,11 +1,17 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers; import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain; import java.sql.SQLException; +/** + * Abstract class defining the DNS server-side handler for Classic protocol operations. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.DNS) public abstract class ClassicHandlerDNSServer { public abstract void handleMessage(ConnectedProtocolClient client, String message, Classic_ProtocolVersion protocolVersion); diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerWebServer.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerWebServer.java similarity index 75% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerWebServer.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerWebServer.java index d43e94f..4457047 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicHandlerWebServer.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/handlers/ClassicHandlerWebServer.java @@ -1,11 +1,13 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.handlers; import org.openautonomousconnection.protocol.annotations.ProtocolInfo; import org.openautonomousconnection.protocol.side.dns.ConnectedProtocolClient; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils.Classic_ProtocolVersion; -import java.sql.SQLException; - +/** + * Abstract class defining the web server-side handler for Classic protocol operations. + */ @ProtocolInfo(protocolSide = ProtocolVersion.ProtocolSide.WEB) public abstract class ClassicHandlerWebServer { public abstract void handleMessage(ConnectedProtocolClient client, String message, Classic_ProtocolVersion protocolVersion); diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_Domain.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_Domain.java similarity index 53% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_Domain.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_Domain.java index f5491f6..acfe6dd 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_Domain.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_Domain.java @@ -1,15 +1,46 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects; import lombok.Getter; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.Domain; import java.io.Serializable; +/** + * Classic_Domain is an old representation of a domain, maintained for backward compatibility. + * It encapsulates the domain's name, top-level domain, path, and destination. + * This class is deprecated and users are encouraged to use the Domain class instead. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_Domain implements Serializable { + + /** + * The name of the domain (e.g., "example" in "example.com"). + */ + @Deprecated(forRemoval = false, since = "1.0.0-BETA.3 | Will be replaced with a getter") public final String name; + + /** + * The top-level domain (e.g., "com" in "example.com"). + */ + @Deprecated(forRemoval = false, since = "1.0.0-BETA.3 | Will be replaced with a getter") public final String topLevelDomain; + + + /** + * The path component of the domain (e.g., "/path/to/resource" in "example.com/path/to/resource"). + */ + @Deprecated(forRemoval = false, since = "1.0.0-BETA.3 | Will be replaced with a getter") public final String path; + + /** + * The destination of the domain, typically the full URL or address. + */ + @Deprecated(forRemoval = false, since = "1.0.0-BETA.3 | Will be replaced with a getter") private final String destination; + + /** + * The encapsulated Domain object for modern usage. + */ @Getter private final Domain domain; diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_LocalDomain.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_LocalDomain.java similarity index 52% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_LocalDomain.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_LocalDomain.java index cccd78e..671b3cd 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_LocalDomain.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_LocalDomain.java @@ -1,5 +1,10 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects; +/** + * Class representing a local domain in the Classic protocol. + * This class extends Classic_Domain and is used for local domain representation. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_LocalDomain extends Classic_Domain { public Classic_LocalDomain(String name, String endName, String path) { super(name, endName, null, path); diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_RequestDomain.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_RequestDomain.java similarity index 57% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_RequestDomain.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_RequestDomain.java index ce3a8fe..b6d3fb3 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_RequestDomain.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/objects/Classic_RequestDomain.java @@ -1,7 +1,12 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects; import java.io.Serializable; +/** + * Class representing a request for a domain in the Classic protocol. + * This class extends Classic_Domain and is used for requesting domain information. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_RequestDomain extends Classic_Domain implements Serializable { public Classic_RequestDomain(String name, String topLevelDomain, String path) { diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_SiteType.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_SiteType.java new file mode 100644 index 0000000..78c4547 --- /dev/null +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_SiteType.java @@ -0,0 +1,44 @@ +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.site; + +import java.io.Serializable; + +/** + * Enum representing different types of sites in the Classic protocol. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") +public enum Classic_SiteType implements Serializable { + /** + * Client site type. + */ + CLIENT("oac-client"), + + /** + * Web server site type. + */ + SERVER("oac-server"), + + /** + * DNS server site type. + */ + PUBLIC("oac"), + + /** + * Protocol site type. + */ + PROTOCOL("oac-protocol"), + + /** + * Local site type. + */ + LOCAL("oac-local"); + + /** + * The name of the site type. + */ + @Deprecated(forRemoval = false, since = "1.0.0-BETA.3 | Will be replaced with a getter") + public final String name; + + Classic_SiteType(String name) { + this.name = name; + } +} diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_WebsitesContent.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_WebsitesContent.java similarity index 93% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_WebsitesContent.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_WebsitesContent.java index fada924..d83b234 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_WebsitesContent.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/site/Classic_WebsitesContent.java @@ -1,7 +1,11 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.site; import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider; +/** + * This class contains predefined HTML content for various website responses in the Classic protocol. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_WebsitesContent extends DefaultMethodsOverrider { public static final String DOMAIN_NOT_FOUND = """ diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicConverter.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/ClassicConverter.java similarity index 50% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicConverter.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/ClassicConverter.java index 85b0e4b..b5264b9 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/ClassicConverter.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/ClassicConverter.java @@ -1,23 +1,51 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils; import org.openautonomousconnection.protocol.versions.ProtocolVersion; import org.openautonomousconnection.protocol.versions.v1_0_0.beta.Domain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_Domain; +/** + * Utility class for converting between Classic protocol objects and new protocol objects. + */ public class ClassicConverter { + /** + * Converts a Classic_Domain object to a Domain object. + * @param classicDomain the Classic_Domain object to convert + * @return the converted Domain object + */ + @SuppressWarnings(value = "deprecation") public static Domain classicDomainToNewDomain(Classic_Domain classicDomain) { return new Domain(classicDomain.name + "." + classicDomain.topLevelDomain + (classicDomain.path.startsWith("/") ? classicDomain.path : "/" + classicDomain.path)); } + /** + * Converts a Domain object to a Classic_Domain object. + * @param newDomain the Domain object to convert + * @return the converted Classic_Domain object + */ + @SuppressWarnings(value = "deprecation") public static Classic_Domain newDomainToClassicDomain(Domain newDomain) { return new Classic_Domain(newDomain.getName(), newDomain.getTopLevelName(), newDomain.getDestination(), newDomain.getPath()); } + /** + * Converts a Classic_ProtocolVersion to a ProtocolVersion. + * @param classicProtocolVersion the Classic_ProtocolVersion to convert + * @return the converted ProtocolVersion + */ + @SuppressWarnings(value = "deprecation") public static ProtocolVersion classicProtocolVersionToNewProtocolVersion(Classic_ProtocolVersion classicProtocolVersion) { if (classicProtocolVersion == Classic_ProtocolVersion.PV_1_0_0) return ProtocolVersion.PV_1_0_0_CLASSIC; return null; } + /** + * Converts a ProtocolVersion to a Classic_ProtocolVersion. + * @param newProtocolVersion the ProtocolVersion to convert + * @return the converted Classic_ProtocolVersion + */ + @SuppressWarnings(value = "deprecation") public static Classic_ProtocolVersion newProtocolVersionToClassicProtocolVersion(ProtocolVersion newProtocolVersion) { return Classic_ProtocolVersion.PV_1_0_0; } diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ClientListener.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ClientListener.java similarity index 65% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ClientListener.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ClientListener.java index 09c783a..dfd47c5 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ClientListener.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ClientListener.java @@ -1,9 +1,14 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils; import dev.unlegitdqrk.unlegitlibrary.event.EventListener; import dev.unlegitdqrk.unlegitlibrary.event.Listener; import org.openautonomousconnection.protocol.ProtocolBridge; import org.openautonomousconnection.protocol.packets.v1_0_0.classic.Classic_PingPacket; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.events.Classic_DomainPacketReceivedEvent; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.events.Classic_PingPacketReceivedEvent; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_LocalDomain; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.site.Classic_SiteType; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.site.Classic_WebsitesContent; import java.io.BufferedReader; import java.io.IOException; @@ -11,48 +16,77 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +/** + * This class listens for events related to Classic protocol operations on the client side. + * It handles domain resolution and ping responses, facilitating communication with the DNS server + * and web content retrieval. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public class Classic_ClientListener extends EventListener { + /** + * Handles the event when a domain packet is received. + * It checks if the domain exists and sends a ping request to the DNS server. + * If the domain does not exist, it handles the error accordingly. + * @param event The event containing domain information. + */ @Listener public void onDomain(Classic_DomainPacketReceivedEvent event) { + // Check if the domain exists boolean exists = event.domain != null; if (exists) { try { + // Send a ping request to the DNS server if (!ProtocolBridge.getInstance().getProtocolClient().getClientDNSConnection().sendPacket(new Classic_PingPacket(event.requestDomain, event.domain, false))) { + // If sending the packet fails, handle the error ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PROTOCOL, new Classic_LocalDomain("error-occurred", "html", ""), Classic_WebsitesContent.ERROR_OCCURRED(event.domain + "/" + event.domain.path)); } } catch (IOException | ClassNotFoundException e) { + // Handle any exceptions that occur during the process ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PROTOCOL, new Classic_LocalDomain("error-occurred", "html", ""), Classic_WebsitesContent.ERROR_OCCURRED(event.domain + "/" + event.domain.path + ":\n" + e.getMessage())); } } else + // If the domain does not exist, handle the error ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PROTOCOL, new Classic_LocalDomain("domain-not-found", "html", ""), Classic_WebsitesContent.DOMAIN_NOT_FOUND); } + /** + * Handles the event when a ping packet is received. + * If the domain is reachable, it fetches the HTML content from the domain. + * If not reachable, it handles the error accordingly. + * @param event The event containing ping response information. + */ @Listener public void onPing(Classic_PingPacketReceivedEvent event) { + // If the domain is reachable, fetch the HTML content if (event.reachable) { String destination = event.domain.getDomain().getDestination(); try { + // Create a URL object URL url = new URL(destination); HttpURLConnection connection2 = (HttpURLConnection) url.openConnection(); connection2.setRequestMethod("GET"); + // Read the response StringBuilder content = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection2.getInputStream()))) { String line; while ((line = reader.readLine()) != null) content.append(line); } + // Handle the HTML content ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PUBLIC, event.domain, content.toString()); } catch (IOException exception) { + // Handle any exceptions that occur during the process ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PROTOCOL, new Classic_LocalDomain("error-occurred", "html", ""), Classic_WebsitesContent.ERROR_OCCURRED(exception.getMessage().replace(event.domain.getDomain().getDestination(), event.domain + "/" + event.domain.path))); } } else + // If the domain is not reachable, handle the error ProtocolBridge.getInstance().getClassicHandlerClient().handleHTMLContent(Classic_SiteType.PROTOCOL, new Classic_LocalDomain("error-not-reached", "html", ""), Classic_WebsitesContent.DOMAIN_NOT_REACHABLE); } diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainUtils.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_DomainUtils.java similarity index 76% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainUtils.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_DomainUtils.java index 9f9b018..26de946 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_DomainUtils.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_DomainUtils.java @@ -1,14 +1,25 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils; import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider; +import org.openautonomousconnection.protocol.versions.v1_0_0.classic.site.Classic_SiteType; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +/** + * Utility class for domain-related operations in the Classic protocol. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") class Classic_DomainUtils extends DefaultMethodsOverrider { + /** + * Extracts the top-level domain (TLD) from a given URL. + * @param url The URL from which to extract the TLD. + * @return The top-level domain as a string. + * @throws MalformedURLException If the URL is malformed. + */ public static String getTopLevelDomain(String url) throws MalformedURLException { URL uri = null; String tldString = null; @@ -33,6 +44,13 @@ class Classic_DomainUtils extends DefaultMethodsOverrider { return tldString; } + /** + * Extracts the domain name (excluding the TLD) from a given URL. + * @param url The URL from which to extract the domain name. + * @return The domain name as a string. + * @throws URISyntaxException If the URL syntax is incorrect. + * @throws MalformedURLException If the URL is malformed. + */ public static String getDomainName(String url) throws URISyntaxException, MalformedURLException { if (url.startsWith(Classic_SiteType.PUBLIC.name + "://")) url = url.substring((Classic_SiteType.PUBLIC.name + "://").length()); @@ -48,11 +66,14 @@ class Classic_DomainUtils extends DefaultMethodsOverrider { if (!url.startsWith("https://") && !url.startsWith("http://")) url = "https://" + url; URI uri = new URI(url); - String domain = uri.getHost().replace("." + getTopLevelDomain(url), ""); - return domain; + return uri.getHost().replace("." + getTopLevelDomain(url), ""); } - + /** + * Extracts the path component from a given URL. + * @param url The URL from which to extract the path. + * @return The path as a string. + */ public static String getPath(String url) { if (!url.startsWith(Classic_SiteType.PUBLIC.name + "://") && !url.startsWith(Classic_SiteType.CLIENT.name + "://") && !url.startsWith(Classic_SiteType.SERVER.name + "://") && !url.startsWith(Classic_SiteType.PROTOCOL.name + "://") && diff --git a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ProtocolVersion.java b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ProtocolVersion.java similarity index 67% rename from src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ProtocolVersion.java rename to src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ProtocolVersion.java index e308033..4e6fc20 100644 --- a/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/Classic_ProtocolVersion.java +++ b/src/main/java/org/openautonomousconnection/protocol/versions/v1_0_0/classic/utils/Classic_ProtocolVersion.java @@ -1,7 +1,11 @@ -package org.openautonomousconnection.protocol.versions.v1_0_0.classic; +package org.openautonomousconnection.protocol.versions.v1_0_0.classic.utils; import java.io.Serializable; +/** + * Enum representing the protocol versions for the Classic protocol. + */ +@Deprecated(forRemoval = false, since = "1.0.0-BETA.3") public enum Classic_ProtocolVersion implements Serializable { PV_1_0_0("1.0.0"); public final String version;