diff --git a/README.MD b/README.MD
index 2a4f7b5..fd1fa3f 100644
--- a/README.MD
+++ b/README.MD
@@ -9,4 +9,61 @@ Open Autonomous Public License (OAPL)
A special exception applies exclusively to the project Open Autonomous Connection (OAC).
Within OAC, the UnlegitLibrary is also licensed under the OAPL.
In this context, OAPL terms take precedence.
-→ https://github.com/Open-Autonomous-Connection/OAPL
\ No newline at end of file
+→ https://github.com/Open-Autonomous-Connection/OAPL
+
+## Include in own projects
+````
+
+
+ github
+ https://maven.pkg.github.com/unlegitdqrk/unlegitlibrary
+
+ true
+
+
+
+
+
+ me.finn.unlegitlibrary
+ unlegitlibrary
+ 1.5.15
+
+
+````
+
+## Certificate generation for NetworkSystem
+### Creating Root-CA:
+````
+openssl genrsa -out myCA.key 4096
+openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.pem
+
+myCA.key = private Key for CA (keep secret)
+myCA.pem = public Root-Certificate for signing server and client certificates
+````
+### Creating Server Certificate based on Root-CA:
+````
+openssl genrsa -out server.key 2048
+openssl req -new -key server.key -out server.csr
+openssl x509 -req -in server.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out server.crt -days 825 -sha256
+
+server.key = private Key for Server
+server.crt = Server-Certificate signed by Root-CA
+````
+### Optional: Creating Client Certificate based on Root-CA:
+````
+openssl genrsa -out client.key 2048
+openssl req -new -key client.key -out client.csr
+openssl x509 -req -in client.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out client.crt -days 825 -sha256
+
+client.key = private Key for Client
+client.crt = Client-Certificate signed by Root-CA
+````
+
+1. Generate a Root-CA. Every client and server NEED this Root-CA *.pem-File. Keep the *.key file private
+2. Generate a Server-Certificate
+3. Optional: Generate a Client-Certificate
+4. Put the Root-CA on your server and client in "certificates/ca"-Folder
+5. Put the Server-Certificate-Key in "certificates/key"-Folder
+6. Put the Server-Certificate in "certificates/server"-Folder
+7. Optional: Put the Client-Certificate-Key in "certificates/key"-Folder
+8. Optional: Put the Client-Certificate in "certificates/client"-Folder
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d3918e2..a3969a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
me.finn.unlegitlibrary
unlegitlibrary
- 1.5.15
+ 1.6.0
https://unlegitdqrk.dev/UnlegitLibrary/
Just a big library
@@ -66,8 +66,8 @@
maven-compiler-plugin
3.8.1
- 1.8
- 1.8
+ 16
+ 16
diff --git a/src/main/java/me/finn/unlegitlibrary/network/system/client/NetworkClient.java b/src/main/java/me/finn/unlegitlibrary/network/system/client/NetworkClient.java
index a0f96e2..a9b2399 100644
--- a/src/main/java/me/finn/unlegitlibrary/network/system/client/NetworkClient.java
+++ b/src/main/java/me/finn/unlegitlibrary/network/system/client/NetworkClient.java
@@ -1,11 +1,3 @@
-/*
- * Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
- *
- * You are unauthorized to remove this copyright.
- * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
- * See LICENSE-File if exists
- */
-
package me.finn.unlegitlibrary.network.system.client;
import me.finn.unlegitlibrary.event.EventManager;
@@ -13,15 +5,16 @@ import me.finn.unlegitlibrary.network.system.client.events.*;
import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
+import me.finn.unlegitlibrary.network.utils.PemUtils;
import me.finn.unlegitlibrary.utils.DefaultMethodsOverrider;
import me.finn.unlegitlibrary.utils.Logger;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+import javax.net.ssl.*;
+import java.io.*;
import java.net.ConnectException;
-import java.net.Socket;
-import java.net.SocketException;
+import java.net.Proxy;
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
public final class NetworkClient {
private final String host;
@@ -29,50 +22,33 @@ public final class NetworkClient {
private final PacketHandler packetHandler;
private final EventManager eventManager;
private final Logger logger;
- private final int maxReconnectAttempts;
- private final int reconnectDelay;
- private Socket socket;
private final int timeout;
+ private SSLSocket socket;
private ObjectOutputStream outputStream;
private ObjectInputStream inputStream;
- private int clientID;
- private int currentAttempts;
- private NetworkClient(String host, int port, PacketHandler packetHandler, EventManager eventManager, Logger logger, int reconnectAttempts, int reconnectDelay, int timeout) {
- this.host = host;
- this.port = port;
- this.clientID = -1;
- this.timeout = timeout;
+ private final SSLSocketFactory sslSocketFactory;
+ private final SSLParameters sslParameters;
- this.packetHandler = packetHandler;
- this.eventManager = eventManager;
- this.logger = logger;
+ private final Thread receiveThread = new Thread(this::receive);
- this.maxReconnectAttempts = reconnectAttempts;
- this.reconnectDelay = reconnectDelay;
- this.currentAttempts = 0;
-
- this.packetHandler.setClientInstance(this);
- this.packetHandler.registerPacket(new ClientIDPacket());
- }
-
- public int getClientID() {
- return clientID;
- } private final Thread receiveThread = new Thread(this::receive);
-
- public void setClientID(int clientID) {
- if (this.clientID == -1) this.clientID = clientID;
- }
-
- public Socket getSocket() {
- return socket;
+ public PacketHandler getPacketHandler() {
+ return packetHandler;
}
public EventManager getEventManager() {
return eventManager;
}
- public PacketHandler getPacketHandler() {
- return packetHandler;
+ public ObjectInputStream getInputStream() {
+ return inputStream;
+ }
+
+ public SSLSocket getSocket() {
+ return socket;
+ }
+
+ public ObjectOutputStream getOutputStream() {
+ return outputStream;
}
public Logger getLogger() {
@@ -87,200 +63,188 @@ public final class NetworkClient {
return host;
}
- public boolean isConnected() {
- return socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
- && receiveThread.isAlive() && !receiveThread.isInterrupted();
+ private NetworkClient(String host, int port, PacketHandler packetHandler,
+ EventManager eventManager, Logger logger,
+ int timeout, SSLSocketFactory sslSocketFactory,
+ SSLParameters sslParameters) {
+ this.host = host;
+ this.port = port;
+ this.packetHandler = packetHandler;
+ this.eventManager = eventManager;
+ this.logger = logger;
+ this.timeout = timeout;
+ this.sslSocketFactory = sslSocketFactory;
+ this.sslParameters = sslParameters;
+
+ this.packetHandler.setClientInstance(this);
+ this.packetHandler.registerPacket(new ClientIDPacket());
}
- public boolean isAutoReconnectEnabled() {
- return maxReconnectAttempts != 0;
+ public boolean isConnected() {
+ return socket != null && socket.isConnected() && !socket.isClosed()
+ && receiveThread.isAlive() && !receiveThread.isInterrupted();
}
public synchronized boolean connect() throws ConnectException {
if (isConnected()) return false;
- if (logger == null) System.out.println("Trying to connect to " + host + ":" + port + "...");
- else logger.info("Trying to connect to " + host + ":" + port + "...");
+ if (logger != null) logger.info("Trying to connect to " + host + ":" + port + "...");
+ else System.out.println("Trying to connect to " + host + ":" + port + "...");
try {
- socket = new Socket(host, port);
+ if (sslSocketFactory == null) throw new ConnectException("SSL socket factory not set. Client certificate required!");
+
+ socket = (SSLSocket) sslSocketFactory.createSocket(host, port);
+
+ if (sslParameters != null) socket.setSSLParameters(sslParameters);
+ else {
+ SSLParameters defaultParams = socket.getSSLParameters();
+ defaultParams.setProtocols(new String[]{"TLSv1.3"});
+ socket.setSSLParameters(defaultParams);
+ }
+
socket.setTcpNoDelay(true);
socket.setSoTimeout(timeout);
+ try {
+ socket.startHandshake();
+ } catch (Exception handshakeEx) {
+ throw new ConnectException("Handshake failed: " + handshakeEx.getMessage());
+ }
outputStream = new ObjectOutputStream(socket.getOutputStream());
inputStream = new ObjectInputStream(socket.getInputStream());
receiveThread.start();
-
- if (currentAttempts == 0) currentAttempts++;
- if (logger == null)
- System.out.println("Connected to " + host + ":" + port + " (Attempts: " + currentAttempts + ")");
- else logger.info("Connected to " + host + ":" + port + " (Attempts: " + currentAttempts + ")");
-
eventManager.executeEvent(new ClientConnectedEvent(this));
-
- currentAttempts = 0;
+ if (logger != null) logger.info("Connected to " + host + ":" + port);
+ else System.out.println("Connected to " + host + ":" + port);
return true;
- } catch (IOException exception) {
- if (isAutoReconnectEnabled()) {
- try {
- Thread.sleep(reconnectDelay);
- } catch (InterruptedException sleepThreadException) {
- if (logger == null) System.err.println("Reconnect exception: " + sleepThreadException.getMessage());
- else logger.exception("Reconnect exception", sleepThreadException);
- }
-
- currentAttempts++;
- if (currentAttempts < maxReconnectAttempts || maxReconnectAttempts < 0) return connect();
- }
+ } catch (Exception e) {
+ throw new ConnectException("Failed to connect: " + e.getMessage());
}
-
- throw new ConnectException("Failed to connect to " + host + ":" + port);
}
private void receive() {
- if (!isConnected()) return;
-
- while (isConnected()) {
- try {
+ try {
+ while (isConnected()) {
Object received = inputStream.readObject();
-
- if (received instanceof Integer) {
- int packetID = (Integer) received;
- if (packetHandler.isPacketIDRegistered(packetID)) {
- Packet packet = packetHandler.getPacketByID(packetID);
- if (packetHandler.handlePacket(packetID, packet, inputStream))
- eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
- else
- eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
- } else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
- } else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
- } catch (SocketException ignored) {
- try {
- disconnect();
- } catch (ConnectException exception) {
- exception.printStackTrace();
- }
- } catch (Exception exception) {
- exception.printStackTrace();
- return;
+ handleReceived(received);
}
+ } catch (Exception e) { disconnect(); }
+ }
+
+ private void handleReceived(Object received) throws IOException, ClassNotFoundException {
+ if (received instanceof Integer id) {
+ if (packetHandler.isPacketIDRegistered(id)) {
+ Packet packet = packetHandler.getPacketByID(id);
+ boolean handled = packetHandler.handlePacket(id, packet, inputStream);
+ if (handled) eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
+ else eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
+ } else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
+ } else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
+ }
+
+ public synchronized boolean disconnect() {
+ if (!isConnected()) return false;
+ try {
+ receiveThread.interrupt();
+ if (outputStream != null) outputStream.close();
+ if (inputStream != null) inputStream.close();
+ if (socket != null) socket.close();
+ } catch (IOException e) {
+ if (logger != null) logger.exception("Error closing connection", e);
+ else System.err.println("Error closing connection: " + e.getMessage());
+ } finally {
+ socket = null;
+ outputStream = null;
+ inputStream = null;
+ eventManager.executeEvent(new ClientDisconnectedEvent(this));
}
+ return true;
}
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
if (!isConnected()) return false;
-
- try {
- if (packetHandler.sendPacket(packet, outputStream)) {
- eventManager.executeEvent(new C_PacketSendEvent(this, packet));
- return true;
- } else {
- eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
- return false;
- }
- } catch (IOException | ClassNotFoundException exception) {
- throw exception;
- }
- }
-
- public synchronized boolean disconnect() throws ConnectException {
- boolean wasConnected = isConnected();
-
- if (wasConnected) {
- if (logger == null) System.out.println("Disconnecting from server...");
- else logger.info("Disconnecting from server...");
- }
-
- if (receiveThread.isAlive() && !receiveThread.isInterrupted()) receiveThread.interrupt();
-
- if (wasConnected) {
- try {
- outputStream.close();
- inputStream.close();
- socket.close();
- } catch (IOException exception) {
- if (logger == null) System.err.println("Failed to close socket: " + exception.getMessage());
- else logger.exception("Failed to close socket", exception);
- }
- }
-
- outputStream = null;
- inputStream = null;
- socket = null;
-
- currentAttempts = 0;
-
- if (wasConnected) {
- if (logger == null) System.out.println("Disconnected from server");
- else logger.info("Disconnected from server");
- }
-
- eventManager.executeEvent(new ClientDisconnectedEvent(this));
-
- clientID = -1;
- if (isAutoReconnectEnabled() && (currentAttempts < maxReconnectAttempts || maxReconnectAttempts < 0))
- return connect();
-
- return true;
+ boolean sent = packetHandler.sendPacket(packet, outputStream);
+ if (sent) eventManager.executeEvent(new C_PacketSendEvent(this, packet));
+ else eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
+ return sent;
}
+ // --- Builder ---
public static class ClientBuilder extends DefaultMethodsOverrider {
private String host;
private int port;
-
private PacketHandler packetHandler;
private EventManager eventManager;
private Logger logger;
+ private int timeout = 5000;
+ private SSLSocketFactory sslSocketFactory;
+ private SSLParameters sslParameters;
+ private File caFolder;
+ private File clientFolder;
+ private File keyFolder;
- private int maxReconnectAttempts = 0;
- private int reconnectDelay = 3000;
- private int timeout = 0;
+ public ClientBuilder setHost(String host) { this.host = host; return this; }
+ public ClientBuilder setPort(int port) { this.port = port; return this; }
+ public ClientBuilder setPacketHandler(PacketHandler handler) { this.packetHandler = handler; return this; }
+ public ClientBuilder setEventManager(EventManager manager) { this.eventManager = manager; return this; }
+ public ClientBuilder setLogger(Logger logger) { this.logger = logger; return this; }
+ public ClientBuilder setTimeout(int timeout) { this.timeout = timeout; return this; }
+ public ClientBuilder setSSLSocketFactory(SSLSocketFactory factory) { this.sslSocketFactory = factory; return this; }
+ public ClientBuilder setSSLParameters(SSLParameters params) { this.sslParameters = params; return this; }
+ public ClientBuilder setRootCAFolder(File folder) { this.caFolder = folder; return this; }
+ public ClientBuilder setClientCertificatesFolder(File clientFolder, File keyFolder) { this.clientFolder = clientFolder; this.keyFolder = keyFolder; return this; }
- public final NetworkClient build() {
- return new NetworkClient(host, port, packetHandler, eventManager, logger, maxReconnectAttempts, reconnectDelay, timeout);
+ public NetworkClient build() {
+ if (sslSocketFactory == null && caFolder != null) {
+ try { sslSocketFactory = createSSLSocketFactory(caFolder, clientFolder, keyFolder); }
+ catch (Exception e) { throw new RuntimeException("Failed to create SSLFactory", e); }
+ }
+
+ return new NetworkClient(host, port, packetHandler, eventManager, logger,
+ timeout, sslSocketFactory, sslParameters);
}
- public final ClientBuilder setEventManager(EventManager eventManager) {
- this.eventManager = eventManager;
- return this;
- }
+ public static SSLSocketFactory createSSLSocketFactory(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
+ // TrustStore (Root-CAs)
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, null);
- public final ClientBuilder setHost(String host) {
- this.host = host;
- return this;
- }
+ int caIndex = 1;
+ for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
+ try (FileInputStream fis = new FileInputStream(caFile)) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ java.security.cert.Certificate caCert = cf.generateCertificate(fis);
+ trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
+ }
+ }
- public final ClientBuilder setLogger(Logger logger) {
- this.logger = logger;
- return this;
- }
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
- public final ClientBuilder setMaxReconnectAttempts(int maxReconnectAttempts) {
- this.maxReconnectAttempts = maxReconnectAttempts;
- return this;
- }
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+ keyStore.load(null, null); // Kein Passwort nötig
- public final ClientBuilder setPacketHandler(PacketHandler packetHandler) {
- this.packetHandler = packetHandler;
- return this;
- }
+ int clientIndex = 1;
+ for (File certFile : clientCertFolder.listFiles((f) -> f.getName().endsWith(".crt"))) {
+ String baseName = certFile.getName().replace(".crt", "");
+ File keyFile = new File(clientKeyFolder, baseName + ".key");
+ if (!keyFile.exists()) continue;
- public final ClientBuilder setPort(int port) {
- this.port = port;
- return this;
- }
+ java.security.PrivateKey key = PemUtils.loadPrivateKey(keyFile);
+ java.security.cert.Certificate cert = PemUtils.loadCertificate(certFile);
- public final ClientBuilder setReconnectDelay(int reconnectDelay) {
- this.reconnectDelay = reconnectDelay;
- return this;
- }
+ keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
+ }
- public final ClientBuilder setTimeout(int timeout) {
- this.timeout = timeout;
- return this;
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, null);
+
+ // SSLContext
+ SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ return sslContext.getSocketFactory();
}
}
-
-
}
diff --git a/src/main/java/me/finn/unlegitlibrary/network/system/server/ConnectionHandler.java b/src/main/java/me/finn/unlegitlibrary/network/system/server/ConnectionHandler.java
index 12bafdb..4387cc8 100644
--- a/src/main/java/me/finn/unlegitlibrary/network/system/server/ConnectionHandler.java
+++ b/src/main/java/me/finn/unlegitlibrary/network/system/server/ConnectionHandler.java
@@ -1,35 +1,42 @@
-/*
- * Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
- *
- * You are unauthorized to remove this copyright.
- * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
- * See LICENSE-File if exists
- */
-
package me.finn.unlegitlibrary.network.system.server;
import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
import me.finn.unlegitlibrary.network.system.server.events.*;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.net.Socket;
import java.net.SocketException;
+import java.security.cert.X509Certificate;
public class ConnectionHandler {
-
- public final NetworkServer networkServer;
- private Socket socket;
+ private SSLSocket socket;
private int clientID;
-
private ObjectOutputStream outputStream;
private ObjectInputStream inputStream;
- public ConnectionHandler(NetworkServer server, Socket socket, int clientID) throws IOException, ClassNotFoundException {
- this.networkServer = server;
- this.socket = socket;
- this.clientID = clientID;
+ private final NetworkServer server;
+
+ public SSLSocket getSocket() {
+ return socket;
+ }
+
+ public ObjectOutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ public ObjectInputStream getInputStream() {
+ return inputStream;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+
+ public ConnectionHandler(NetworkServer server, SSLSocket socket, int clientID) throws IOException, ClassNotFoundException {
+ this.server = server; this.socket = socket; this.clientID = clientID;
outputStream = new ObjectOutputStream(socket.getOutputStream());
inputStream = new ObjectInputStream(socket.getInputStream());
@@ -37,97 +44,57 @@ public class ConnectionHandler {
receiveThread.start();
sendPacket(new ClientIDPacket());
- networkServer.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
- } public final Thread receiveThread = new Thread(this::receive);
-
- public int getClientID() {
- return clientID;
+ server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
}
- public final boolean isConnected() {
- return networkServer.isRunning() && socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
- && receiveThread.isAlive() && !receiveThread.isInterrupted();
- }
+ public final Thread receiveThread = new Thread(this::receive);
+
+ public int getClientID() { return clientID; }
+ public boolean isConnected() { return socket != null && socket.isConnected() && !socket.isClosed() && receiveThread.isAlive(); }
public synchronized boolean disconnect() {
- boolean wasConnected = isConnected();
+ if (!isConnected()) return false;
+ if (receiveThread.isAlive()) receiveThread.interrupt();
- if (wasConnected) {
- if (networkServer.getLogger() == null)
- System.out.println("Client ID '" + clientID + "' is disconnecting from server...");
- else networkServer.getLogger().info("Client ID '" + clientID + "' is disconnecting from server...");
- }
+ try { outputStream.close(); inputStream.close(); socket.close(); } catch (IOException ignored) {}
+ socket = null; outputStream = null; inputStream = null; clientID = -1;
- if (receiveThread.isAlive() && !receiveThread.isInterrupted()) receiveThread.interrupt();
+ server.getConnectionHandlers().remove(this);
+ server.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this));
- if (wasConnected) {
- try {
- outputStream.close();
- inputStream.close();
- socket.close();
- } catch (IOException exception) {
- if (networkServer.getLogger() == null)
- System.err.println("Client ID '" + clientID + "' failed to close socket: " + exception.getMessage());
- else
- networkServer.getLogger().exception("Client ID '" + clientID + "' failed to close socket", exception);
- }
- }
-
- outputStream = null;
- inputStream = null;
- socket = null;
-
- networkServer.getConnectionHandlers().remove(this);
-
- if (wasConnected) {
- if (networkServer.getLogger() == null)
- System.out.println("Client ID '" + clientID + "' disconnected from server");
- else networkServer.getLogger().info("Client ID '" + clientID + "' disconnected from server");
- }
-
- networkServer.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this));
- clientID = -1;
return true;
}
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
if (!isConnected()) return false;
+ boolean sent = server.getPacketHandler().sendPacket(packet, outputStream);
- if (networkServer.getPacketHandler().sendPacket(packet, outputStream)) {
- networkServer.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
- return true;
- } else {
- networkServer.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
- return false;
- }
+ if (sent) server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
+ else server.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
+
+ return sent;
}
private void receive() {
- if (!isConnected()) return;
-
while (isConnected()) {
try {
Object received = inputStream.readObject();
-
if (received instanceof Integer) {
- int packetID = (Integer) received;
- if (networkServer.getPacketHandler().isPacketIDRegistered(packetID)) {
- Packet packet = networkServer.getPacketHandler().getPacketByID(packetID);
- if (networkServer.getPacketHandler().handlePacket(packetID, packet, inputStream))
- networkServer.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
- else
- networkServer.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
- } else
- networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
- } else networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
- } catch (SocketException ignored) {
+ int id = (Integer) received;
+
+ if (server.getPacketHandler().isPacketIDRegistered(id)) {
+ Packet packet = server.getPacketHandler().getPacketByID(id);
+
+ if (server.getPacketHandler().handlePacket(id, packet, inputStream)) server.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
+ else server.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
+ } else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
+ } else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
+ } catch (SocketException se) { disconnect(); }
+ catch (Exception ex) {
+ if (server.getLogger() != null) server.getLogger().exception("Receive thread exception for client " + clientID, ex);
+ else System.err.println("Receive thread exception for client " + clientID + ": " + ex.getMessage());
disconnect();
- } catch (Exception exception) {
- exception.printStackTrace();
- return;
}
}
}
-
-
-}
+}
\ No newline at end of file
diff --git a/src/main/java/me/finn/unlegitlibrary/network/system/server/NetworkServer.java b/src/main/java/me/finn/unlegitlibrary/network/system/server/NetworkServer.java
index 86f7348..39cfbd6 100644
--- a/src/main/java/me/finn/unlegitlibrary/network/system/server/NetworkServer.java
+++ b/src/main/java/me/finn/unlegitlibrary/network/system/server/NetworkServer.java
@@ -1,27 +1,21 @@
-/*
- * Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
- *
- * You are unauthorized to remove this copyright.
- * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
- * See LICENSE-File if exists
- */
-
package me.finn.unlegitlibrary.network.system.server;
import me.finn.unlegitlibrary.event.EventManager;
-import me.finn.unlegitlibrary.network.system.packets.Packet;
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
import me.finn.unlegitlibrary.network.system.server.events.IncomingConnectionEvent;
+import me.finn.unlegitlibrary.network.utils.PemUtils;
import me.finn.unlegitlibrary.utils.DefaultMethodsOverrider;
import me.finn.unlegitlibrary.utils.Logger;
-import java.io.IOException;
-import java.net.ServerSocket;
+import javax.net.ssl.*;
+import java.io.File;
+import java.io.FileInputStream;
import java.net.Socket;
+import java.security.KeyStore;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
public final class NetworkServer {
private final int port;
@@ -29,33 +23,14 @@ public final class NetworkServer {
private final EventManager eventManager;
private final Logger logger;
private final int timeout;
- private final int maxRestartAttempts;
- private final int restartDelay;
+ private SSLServerSocket serverSocket;
+ private final SSLServerSocketFactory sslServerSocketFactory;
+
private final List connectionHandlers = new ArrayList<>();
- private int currentAttempts;
- private ServerSocket serverSocket;
- private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager, Logger logger, int maxRestartAttempts, int restartDelay, int timeout) {
- this.port = port;
- this.timeout = timeout;
+ private final Thread incomingThread = new Thread(this::incomingConnections);
- this.packetHandler = packetHandler;
- this.eventManager = eventManager;
- this.logger = logger;
-
- this.maxRestartAttempts = maxRestartAttempts;
- this.restartDelay = restartDelay;
- this.currentAttempts = 0;
-
- this.packetHandler.setServerInstance(this);
- this.packetHandler.registerPacket(new ClientIDPacket());
- } public final Thread incomingConnectionThread = new Thread(this::incomingConnection);
-
- public Logger getLogger() {
- return logger;
- }
-
- public EventManager getEventManager() {
- return eventManager;
+ public List getConnectionHandlers() {
+ return connectionHandlers;
}
public int getPort() {
@@ -66,164 +41,133 @@ public final class NetworkServer {
return packetHandler;
}
- public List getConnectionHandlers() {
- return connectionHandlers;
+ public Logger getLogger() {
+ return logger;
}
- public boolean isAutoRestartEnabled() {
- return maxRestartAttempts != 0;
+ public SSLServerSocket getServerSocket() {
+ return serverSocket;
}
- public boolean isRunning() {
- return serverSocket != null && !serverSocket.isClosed() && serverSocket.isBound() &&
- incomingConnectionThread.isAlive() && !incomingConnectionThread.isInterrupted();
+ public EventManager getEventManager() {
+ return eventManager;
}
- public ConnectionHandler getConnectionHandlerByID(int clientID) {
- for (ConnectionHandler connectionHandler : connectionHandlers)
- if (connectionHandler.getClientID() == clientID) return connectionHandler;
- return null;
+ private boolean requireClientCert;
+
+ private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager,
+ Logger logger, int timeout, SSLServerSocketFactory factory, boolean requireClientCert) {
+ this.port = port; this.packetHandler = packetHandler; this.eventManager = eventManager;
+ this.logger = logger; this.timeout = timeout; this.sslServerSocketFactory = factory;
+
+ this.packetHandler.setServerInstance(this);
+ this.packetHandler.registerPacket(new ClientIDPacket());
+ this.requireClientCert = requireClientCert;
}
- public synchronized boolean start() {
- if (isRunning()) return false;
-
- if (logger == null) System.out.println("Trying to start on port " + port + "...");
- else logger.info("Trying to start on port " + port + "...");
-
+ public boolean start() {
try {
- serverSocket = new ServerSocket(port);
+ serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
+ serverSocket.setNeedClientAuth(requireClientCert);
serverSocket.setSoTimeout(timeout);
-
- incomingConnectionThread.start();
-
- if (currentAttempts == 0) currentAttempts++;
- if (logger == null) System.out.println("Started at port " + port + " (Attempts: " + currentAttempts + ")");
- else logger.info("Started at port " + port + " (Attempts: " + currentAttempts + ")");
-
- currentAttempts = 0;
+ serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
+ incomingThread.start();
+ if (logger != null) logger.log("Server started on port " + port);
+ else System.out.println("Server started on port " + port);
return true;
- } catch (IOException exception) {
- if (maxRestartAttempts != 0) {
- try {
- Thread.sleep(restartDelay);
- } catch (InterruptedException sleepThreadException) {
- if (logger == null) System.err.println("Restart exception: " + sleepThreadException.getMessage());
- else logger.exception("Restart exception", sleepThreadException);
- }
-
- currentAttempts++;
- if (currentAttempts <= maxRestartAttempts || maxRestartAttempts < 0) return start();
- }
-
- if (logger == null) System.err.println("Failed to start on port " + port + ": " + exception.getMessage());
- else logger.exception("Failed to start on port " + port, exception);
- }
-
- return false;
+ } catch (Exception e) {if (logger != null) logger.exception("Failed to start", e);
+ else System.err.println("Failed to start: " + e.getMessage()); return false; }
}
- public boolean broadcastPacket(Packet packet) {
- AtomicBoolean toReturn = new AtomicBoolean(false);
- connectionHandlers.forEach(connectionHandler -> {
- try {
- if (!toReturn.get()) return;
- toReturn.set(connectionHandler.sendPacket(packet));
- } catch (IOException | ClassNotFoundException e) {
- toReturn.set(false);
- }
- });
-
- return toReturn.get();
- }
-
- private void incomingConnection() {
- if (!isRunning()) return;
-
+ private void incomingConnections() {
try {
- while (isRunning()) {
+ while (!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
- socket.setTcpNoDelay(true);
- socket.setSoTimeout(timeout);
-
- if (logger == null) System.out.println("Accepted connection from " + socket.getRemoteSocketAddress());
- else logger.info("Accepted connection from " + socket.getRemoteSocketAddress());
-
- IncomingConnectionEvent incomingConnectionEvent = new IncomingConnectionEvent(this, socket);
-
- eventManager.executeEvent(incomingConnectionEvent);
-
- if (incomingConnectionEvent.isCancelled()) {
- socket.close();
- return;
+ if (!(socket instanceof SSLSocket ssl)) { socket.close(); continue; }
+ ssl.setTcpNoDelay(true);
+ ssl.setSoTimeout(timeout);
+ try { ssl.startHandshake(); }
+ catch (Exception handshakeEx) {
+ if (logger != null) logger.exception("Handshake failed", handshakeEx);
+ else System.err.println("Handshake failed: " + handshakeEx.getMessage());
+ ssl.close();
+ continue;
}
- ConnectionHandler connectionHandler = new ConnectionHandler(this, socket, connectionHandlers.size() + 1);
- connectionHandlers.add(connectionHandler);
+ IncomingConnectionEvent event = new IncomingConnectionEvent(this, ssl);
+ eventManager.executeEvent(event);
+ if (event.isCancelled()) { ssl.close(); continue; }
+
+ try {
+ ConnectionHandler connectionHandler = new ConnectionHandler(this, ssl, connectionHandlers.size() + 1);
+ connectionHandlers.add(connectionHandler);
+ } catch (Exception exception) {
+ ssl.close();
+ continue;
+ }
}
- } catch (IOException | ClassNotFoundException exception) {
- exception.printStackTrace();
- }
- }
-
- public boolean sendPacket(int clientID, Packet packet) throws IOException, ClassNotFoundException {
- return getConnectionHandlerByID(clientID).sendPacket(packet);
- }
-
- public boolean sendPacket(Packet packet, int clientID) throws IOException, ClassNotFoundException {
- return sendPacket(clientID, packet);
+ } catch (Exception e) { e.printStackTrace(); }
}
+ // --- Builder ---
public static class ServerBuilder extends DefaultMethodsOverrider {
private int port;
-
private PacketHandler packetHandler;
private EventManager eventManager;
private Logger logger;
+ private int timeout = 5000;
+ private SSLServerSocketFactory factory;
+ private boolean requireClientCert;
+ private File caFolder;
+ private File serverCertFile;
+ private File serverKeyFile;
- private int maxRestartAttempts = 0;
- private int restartDelay = 3000;
- private int timeout = 0;
+ public ServerBuilder setPort(int port) { this.port = port; return this; }
+ public ServerBuilder setPacketHandler(PacketHandler handler) { this.packetHandler = handler; return this; }
+ public ServerBuilder setEventManager(EventManager manager) { this.eventManager = manager; return this; }
+ public ServerBuilder setLogger(Logger logger) { this.logger = logger; return this; }
+ public ServerBuilder setTimeout(int timeout) { this.timeout = timeout; return this; }
+ public ServerBuilder setSSLServerSocketFactory(SSLServerSocketFactory factory) { this.factory = factory; return this; }
+ public ServerBuilder setRequireClientCertificate(boolean requireClientCertificate) { this.requireClientCert = requireClientCertificate; return this; }
+ public ServerBuilder setRootCAFolder(File folder) { this.caFolder = folder; return this; }
+ public ServerBuilder setServerCertificate(File certFile, File keyFile) { this.serverCertFile = certFile; this.serverKeyFile = keyFile; return this; }
- public final NetworkServer build() {
- return new NetworkServer(port, packetHandler, eventManager, logger, maxRestartAttempts, restartDelay, timeout);
+ public NetworkServer build() {
+ if (factory == null && caFolder != null && serverCertFile != null && serverKeyFile != null) {
+ try { factory = createSSLServerSocketFactory(caFolder, serverCertFile, serverKeyFile); }
+ catch (Exception e) { throw new RuntimeException("Failed to create SSLServerSocketFactory", e); }
+ }
+ return new NetworkServer(port, packetHandler, eventManager, logger, timeout, factory, requireClientCert);
}
- public final ServerBuilder setEventManager(EventManager eventManager) {
- this.eventManager = eventManager;
- return this;
- }
+ public static SSLServerSocketFactory createSSLServerSocketFactory(File caFolder, File serverCert, File serverKey) throws Exception {
+ // TrustStore (Root-CAs)
+ KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ trustStore.load(null, null);
- public final ServerBuilder setLogger(Logger logger) {
- this.logger = logger;
- return this;
- }
+ int caIndex = 1;
+ for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
+ try (FileInputStream fis = new FileInputStream(caFile)) {
+ java.security.cert.Certificate cert = PemUtils.loadCertificate(caFile);
+ trustStore.setCertificateEntry("ca" + (caIndex++), cert);
+ }
+ }
- public final ServerBuilder setMaxReconnectAttempts(int maxRestartAttempts) {
- this.maxRestartAttempts = maxRestartAttempts;
- return this;
- }
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
- public final ServerBuilder setPacketHandler(PacketHandler packetHandler) {
- this.packetHandler = packetHandler;
- return this;
- }
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+ keyStore.load(null, null);
+ java.security.PrivateKey key = PemUtils.loadPrivateKey(serverKey);
+ java.security.cert.Certificate cert = PemUtils.loadCertificate(serverCert);
+ keyStore.setKeyEntry("server", key, null, new java.security.cert.Certificate[]{cert});
- public final ServerBuilder setPort(int port) {
- this.port = port;
- return this;
- }
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, null);
- public final ServerBuilder setReconnectDelay(int reconnectDelay) {
- this.restartDelay = reconnectDelay;
- return this;
- }
-
- public final ServerBuilder setTimeout(int timeout) {
- this.timeout = timeout;
- return this;
+ SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ return sslContext.getServerSocketFactory();
}
}
-
-
}
diff --git a/src/main/java/me/finn/unlegitlibrary/network/system/server/events/IncomingConnectionEvent.java b/src/main/java/me/finn/unlegitlibrary/network/system/server/events/IncomingConnectionEvent.java
index 42fa801..685c669 100644
--- a/src/main/java/me/finn/unlegitlibrary/network/system/server/events/IncomingConnectionEvent.java
+++ b/src/main/java/me/finn/unlegitlibrary/network/system/server/events/IncomingConnectionEvent.java
@@ -11,13 +11,16 @@ package me.finn.unlegitlibrary.network.system.server.events;
import me.finn.unlegitlibrary.event.impl.CancellableEvent;
import me.finn.unlegitlibrary.network.system.server.NetworkServer;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
import java.net.Socket;
+import java.security.cert.X509Certificate;
public class IncomingConnectionEvent extends CancellableEvent {
public final NetworkServer server;
- public final Socket socket;
+ public final SSLSocket socket;
- public IncomingConnectionEvent(NetworkServer server, Socket socket) {
+ public IncomingConnectionEvent(NetworkServer server, SSLSocket socket) {
this.server = server;
this.socket = socket;
}
diff --git a/src/main/java/me/finn/unlegitlibrary/network/utils/PemUtils.java b/src/main/java/me/finn/unlegitlibrary/network/utils/PemUtils.java
new file mode 100644
index 0000000..ba6342c
--- /dev/null
+++ b/src/main/java/me/finn/unlegitlibrary/network/utils/PemUtils.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
+ *
+ * You are unauthorized to remove this copyright.
+ * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+package me.finn.unlegitlibrary.network.utils;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+
+public class PemUtils {
+ public static PrivateKey loadPrivateKey(File keyFile) throws Exception {
+ String keyPem = new String(Files.readAllBytes(keyFile.toPath()))
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replaceAll("\\s", "");
+ byte[] keyBytes = Base64.getDecoder().decode(keyPem);
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
+ KeyFactory kf = KeyFactory.getInstance("RSA"); // oder "EC" je nach Key
+ return kf.generatePrivate(spec);
+ }
+
+ public static X509Certificate loadCertificate(File certFile) throws Exception {
+ try (FileInputStream fis = new FileInputStream(certFile)) {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(fis);
+ }
+ }
+}