Advanced InfoNameSystem

This commit is contained in:
Finn
2025-12-11 01:22:58 +01:00
parent fecee6043c
commit acacff60ff
24 changed files with 562 additions and 856 deletions

View File

@@ -7,21 +7,18 @@ import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
import org.openautonomousconnection.protocol.exceptions.UnsupportedProtocolException;
import org.openautonomousconnection.protocol.packets.OACPacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.GetDestinationPacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.ValidateInfoNamePacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.classic.Classic_DomainPacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.classic.Classic_PingPacket;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.INSQueryPacket;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseCode;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.InfoName;
import org.openautonomousconnection.protocol.versions.v1_0_0.classic.objects.Classic_RequestDomain;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.List;
/**
* Abstract class defining the client-side protocol operations and interactions with INS and web servers.
@@ -38,23 +35,15 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider {
*/
@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;
/**
* The reference to the ProtocolBridge Object
*/
@Getter
private final ProtocolBridge protocolBridge;
/**
* Stores the protocol version of the connected server.
*/
private ProtocolVersion serverVersion = null;
/**
* Initializes the ProtocolClient, setting up certificate folders and the INS client connection.
@@ -85,33 +74,6 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider {
return clientToINS;
}
/**
* Creates a web connection to the specified InfoName and ports.
*
* @param infoName the target InfoName 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(InfoName infoName, int pipelinePort, int webPort) throws Exception {
// Ensure the protocol supports web connections
if (!protocolBridge.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.getLogger().exception("Failed to close connection to web server", e);
return;
}
}
// Verify necessary certificate files exist
webClient = new WebClient(infoName, pipelinePort, webPort, this);
}
/**
* Checks if the required certificate files exist in the specified folder.
*
@@ -172,15 +134,6 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider {
public final void onINSDisconnect(ClientDisconnectedEvent event) {
// Reset server version on INS disconnect
serverVersion = null;
// Close web client connection if it exists
if (webClient != null) {
try {
webClient.closeConnection();
} catch (IOException e) {
protocolBridge.getLogger().exception("Failed to close connection to web server", e);
}
}
}
/**
@@ -305,55 +258,46 @@ public abstract class ProtocolClient extends DefaultMethodsOverrider {
}
/**
* Validates the specified InfoName by sending a validation request to the INS server.
* Sends an INS query to the INS server.
* <p>
* The client requests a specific record type for a given TLN, name and optional subname.
* After sending the packet, {@link #onQuerySent(String, String, String, INSRecordType)} is invoked.
*
* @param infoName the InfoName 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.
* @param tln The TLN.
* @param name The InfoName.
* @param sub Optional subname or {@code null}.
* @param type The requested record type.
* @throws IOException If sending the packet fails.
* @throws ClassNotFoundException If packet serialization fails.
*/
public final void validateInfoName(InfoName infoName) throws IOException, ClassNotFoundException {
// Send Classic_PingPacket if classic protocol is supported
Classic_PingPacket cPingPacket = new Classic_PingPacket(new Classic_RequestDomain(infoName.getName(), infoName.getTopLevelName(), infoName.getPath(), protocolBridge), null, false, protocolBridge);
if (protocolBridge.isClassicSupported()) clientToINS.sendPacket(cPingPacket);
public final void sendINSQuery(String tln, String name, String sub, INSRecordType type) throws IOException, ClassNotFoundException {
if (!protocolBridge.isRunningAsClient()) return;
// Send ValidateInfoNamePacket
clientToINS.sendPacket(new ValidateInfoNamePacket(infoName, protocolBridge));
getClientINSConnection().sendPacket(new INSQueryPacket(tln, name, sub, type, getClientINSConnection().getClientID()));
onQuerySent(tln, name, sub, type);
}
/**
* Requests the destination for the specified InfoName from the INS server.
* Called when the client receives an INS response from the server.
* <p>
*
* @param infoName the InfoName for which to request the destination.
* @param responseCode the expected INSResponseCode 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.
* @param status The response status from the server.
* @param records The list of records returned by the server.
*/
public final void requestInfoName(InfoName infoName, INSResponseCode responseCode) throws IOException, ClassNotFoundException {
// Send Classic_DomainPacket if classic protocol is supported
Classic_DomainPacket cDomainPacket = new Classic_DomainPacket(0, new Classic_RequestDomain(infoName.getName(), infoName.getTopLevelName(), infoName.getPath(), protocolBridge), null, protocolBridge);
if (protocolBridge.isClassicSupported()) clientToINS.sendPacket(cDomainPacket);
public abstract void onResponse(INSResponseStatus status, List<INSRecord> records);
// Send GetDestinationPacket
clientToINS.sendPacket(new GetDestinationPacket(infoName, responseCode, protocolBridge));
/**
* Called after a query was sent to the INS server.
* <p>
*
* @param tln The TLN requested.
* @param name The InfoName.
* @param sub Optional subname.
* @param type The requested record type.
*/
public void onQuerySent(String tln, String name, String sub, INSRecordType type) {
}
/**
* Callback method invoked when InfoName validation is completed.
*
* @param infoName the InfoName that was validated.
* @param responseCode the INSResponseCode resulting from the validation.
*/
public abstract void validationCompleted(InfoName infoName, INSResponseCode responseCode);
/**
* Callback method invoked when the destination retrieval is completed.
*
* @param infoName the InfoName for which the destination was requested.
* @param destination the retrieved destination as a string.
* @param validationResponse the INSResponseCode resulting from the destination retrieval.
*/
public abstract void getDestinationCompleted(InfoName infoName, String destination, INSResponseCode validationResponse);
/**
* Manages the folder structure for client certificates.
*/

View File

@@ -1,239 +0,0 @@
package org.openautonomousconnection.protocol.side.client;
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
import lombok.Getter;
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.InfoName;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ConnectException;
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.INS)
public final class WebClient {
/**
* NetworkClient instance for managing the pipeline connection to the web server.
*/
private final NetworkClient clientToWebPipeline;
/**
* The Protocol Client associated with this protocol client.
*/
@Getter
private final ProtocolClient protocolClient;
/**
* 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;
/**
* Constructs a WebClient instance and establishes a secure connection to the web server.
*
* @param infoName The InfoName information for the web server.
* @param pipelinePort The port for the pipeline connection.
* @param webPort The port for the web server connection.
* @param protocolClient The Protocol Client associated with this protocol client.
* @throws Exception If an error occurs during connection setup.
*/
public WebClient(InfoName infoName, int pipelinePort, int webPort, ProtocolClient protocolClient) throws Exception {
this.protocolClient = protocolClient;
// Initialize and connect the pipeline client
clientToWebPipeline = new NetworkClient.ClientBuilder().
// Set logger from ProtocolBridge
setLogger(protocolClient.getProtocolBridge().getLogger()).
// Set the destination and port for the pipeline connection
setHost(infoName.getDestination(protocolClient.getProtocolBridge())).setPort(pipelinePort).
// Configure packet handler and event manager
setPacketHandler(protocolClient.getProtocolBridge().getProtocolSettings().packetHandler).
setEventManager(protocolClient.getProtocolBridge().getProtocolSettings().eventManager).
// Set proxy and ssl parameters from INS connection settings
setProxy(protocolClient.getProtocolBridge().getProtocolClient().getClientINSConnection().getProxy()).
setSSLParameters(protocolClient.getProtocolBridge().getProtocolClient().getClientINSConnection().getSocket().getSSLParameters()).
// Set certificates and folders for SSL
setRootCAFolder(protocolClient.getProtocolBridge().getProtocolClient().getFolderStructure().publicCAFolder).
setClientCertificatesFolder(protocolClient.getProtocolBridge().getProtocolClient().getFolderStructure().publicClientFolder,
protocolClient.getProtocolBridge().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;
// Create SSL socket factory using client certificates
SSLSocketFactory sslSocketFactory = NetworkClient.ClientBuilder.
createSSLSocketFactory(protocolClient.getProtocolBridge().getProtocolClient().getFolderStructure().publicCAFolder,
protocolClient.getProtocolBridge().getProtocolClient().getFolderStructure().publicClientFolder,
protocolClient.getProtocolBridge().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(infoName.getDestination(protocolClient.getProtocolBridge()), webPort), 0);
tempSocket = (SSLSocket) sslSocketFactory.createSocket(rawSocket, infoName.getDestination(protocolClient.getProtocolBridge()), webPort, true);
} else {
tempSocket = (SSLSocket) sslSocketFactory.createSocket(infoName.getDestination(protocolClient.getProtocolBridge()), webPort);
}
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);
try {
this.clientToWebServer.startHandshake();
} catch (Exception handshakeEx) {
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;
} /**
* Thread for receiving data from the web server.
*/
private final Thread receiveThread = new Thread(this::receive);
/**
* 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() &&
protocolClient.getProtocolBridge().getProtocolClient().getClientINSConnection().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()) {
Object received = this.inputStream.readObject();
}
} catch (Exception var2) {
try {
this.closeConnection();
} catch (IOException exception) {
protocolClient.getProtocolBridge().getLogger().exception("Failed to close connection to web server", var2);
}
}
}
/**
* 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;
} else {
clientToWebPipeline.disconnect();
try {
// Interrupt the receive thread
this.receiveThread.interrupt();
// Close streams and socket
if (this.outputStream != null) {
this.outputStream.close();
}
if (this.inputStream != null) {
this.inputStream.close();
}
if (this.clientToWebServer != null) {
this.clientToWebServer.close();
}
} finally {
// Nullify references to help with garbage collection
this.clientToWebServer = null;
this.outputStream = null;
this.inputStream = null;
}
return true;
}
}
}

View File

@@ -8,8 +8,8 @@ import lombok.Getter;
import org.openautonomousconnection.protocol.ProtocolBridge;
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseCode;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.InfoName;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
import java.io.File;
import java.io.FileNotFoundException;
@@ -33,24 +33,21 @@ public abstract class ProtocolINSServer extends DefaultMethodsOverrider {
* The configuration manager for handling server configurations.
*/
private final ConfigurationManager configurationManager;
/**
* List of connected protocol clients.
*/
@Getter
private List<ConnectedProtocolClient> clients;
/**
* The folder structure for server certificates.
*/
@Getter
private ServerCertificateFolderStructure folderStructure;
/**
* The reference to the ProtocolBridge Object
*/
@Getter
private final ProtocolBridge protocolBridge;
/**
* List of connected protocol clients.
*/
@Getter
private List<ConnectedProtocolClient> clients;
/**
* The folder structure for server certificates.
*/
@Getter
private ServerCertificateFolderStructure folderStructure;
/**
* Constructs a ProtocolINSServer with the specified configuration file.
@@ -164,6 +161,61 @@ public abstract class ProtocolINSServer extends DefaultMethodsOverrider {
return configurationManager.getString("server.site.info");
}
/**
* Resolves a request for an INS record based on TLN, name, subname and record type.
* <p>
* The implementation should:
* <ul>
* <li>Locate the corresponding InfoName in storage</li>
* <li>Return all matching {@link INSRecord} entries</li>
* <li>Handle CNAME recursion, TTL rules, and filtering for the requested type</li>
* </ul>
*
* @param tln The top-level name.
* @param name The InfoName.
* @param sub The optional subname , may be {@code null}.
* @param type The INS record type being requested.
* @return A list of resolved INS records. May be empty if no record exists.
*/
public abstract List<INSRecord> resolve(String tln, String name, String sub, INSRecordType type);
/**
* Callback fired whenever the INS server receives a query packet.
* <p>This method is optional.</p>
*
* @param tln The top-level name of the request.
* @param name The InfoName being queried.
* @param sub An optional subname, or {@code null}.
* @param type The record type requested.
*/
public void onQueryReceived(String tln, String name, String sub, INSRecordType type) {
}
/**
* Callback fired after an INS response was successfully sent to the client.
*
* @param tln The requested TLN.
* @param name The InfoName.
* @param sub Optional subname or {@code null}.
* @param type The requested record type.
* @param results The records returned to the client.
*/
public void onResponseSent(String tln, String name, String sub, INSRecordType type, List<INSRecord> results) {
}
/**
* Callback fired when an INS response could not be delivered to the client.
*
* @param tln The requested TLN.
* @param name The InfoName.
* @param sub Optional subname.
* @param type The record type requested.
* @param results The records that would have been sent.
* @param exception The exception describing the failure.
*/
public void onResponseSentFailed(String tln, String name, String sub, INSRecordType type, List<INSRecord> results, Exception exception) {
}
/**
* Gets the INS registration site URL from the configuration.
*
@@ -173,65 +225,6 @@ public abstract class ProtocolINSServer extends DefaultMethodsOverrider {
return configurationManager.getString("server.site.register");
}
/**
* Abstract method to retrieve the list of InfoNames managed by the INS server.
*
* @return A list of InfoName objects.
*/
public abstract List<InfoName> getInfoNames();
/**
* @param infoName The InfoName to look up.
* @return The destination associated with the InfoName.
* @see InfoName#getDestination(ProtocolBridge)
* Abstract method to get the destination for a given InfoName.
*/
public abstract String getInfoNameDestination(InfoName infoName);
/**
* @param infoName The parent InfoName.
* @param subname The subname to look up.
* @return The destination associated with the subname.
* @see InfoName#getDestination(ProtocolBridge)
* Abstract method to get the destination for a given subname under a specific InfoName.
*/
public abstract String getSubnameDestination(InfoName infoName, String subname);
/**
* @param topLevelName The top-level name.
* @return The information site URL for the specified top-level name.
* @see InfoName#getDestination(ProtocolBridge)
* Abstract method to get the top-level name information site URL.
*/
public abstract String getTLNInfoSite(String topLevelName);
/**
* Abstract method to validate a requested InfoName.
*
* @param requestedInfoName The InfoName to validate.
* @return A INSResponseCode indicating the result of the validation.
*/
public abstract INSResponseCode validateInfoName(InfoName requestedInfoName);
/**
* Abstract method called when a validation packet fails to send.
*
* @param infoName The InfoName associated with the validation.
* @param client The connected protocol client.
* @param exception The exception that occurred during sending.
*/
public abstract void validationPacketSendFailed(InfoName infoName, ConnectedProtocolClient client, Exception exception);
/**
* Abstract method called when a InfoName destination packet fails to send.
*
* @param client The connected protocol client.
* @param infoName The InfoName associated with the packet.
* @param validationResponse The INS response code from validation.
* @param exception The exception that occurred during sending.
*/
public abstract void infoNameDestinationPacketFailedSend(ConnectedProtocolClient client, InfoName infoName, INSResponseCode validationResponse, Exception exception);
/**
* Class representing the folder structure for server certificates.
*/

View File

@@ -68,10 +68,7 @@ public final class ConnectedWebClient {
*/
public ConnectedWebClient(ConnectionHandler pipelineConnection) {
this.pipelineConnection = pipelineConnection;
} /**
* Thread for receiving data from the client.
*/
private final Thread receiveThread = new Thread(this::receive);
}
/**
* Sends an HTTP redirect response to the client.
@@ -96,7 +93,10 @@ public final class ConnectedWebClient {
// End of headers
out.write("\r\n".getBytes());
out.flush();
}
} /**
* Thread for receiving data from the client.
*/
private final Thread receiveThread = new Thread(this::receive);
/**
* Parses POST parameters from the input stream.

View File

@@ -62,6 +62,11 @@ public final class ProtocolWebServer {
* The configuration file for the web server.
*/
private final File configFile;
/**
* The reference to the ProtocolBridge Object
*/
@Getter
private final ProtocolBridge protocolBridge;
/**
* The network server handling pipeline connections.
*/
@@ -83,12 +88,6 @@ public final class ProtocolWebServer {
@Getter
private String uniqueSessionString;
/**
* The reference to the ProtocolBridge Object
*/
@Getter
private final ProtocolBridge protocolBridge;
/**
* Initializes the web server with the given configuration, authentication, and rules files.
*