- Very big NetworkSystem updates
This commit is contained in:
57
README.MD
57
README.MD
@@ -10,3 +10,60 @@ A special exception applies exclusively to the project Open Autonomous Connectio
|
|||||||
Within OAC, the UnlegitLibrary is also licensed under the OAPL.<br />
|
Within OAC, the UnlegitLibrary is also licensed under the OAPL.<br />
|
||||||
In this context, OAPL terms take precedence.<br />
|
In this context, OAPL terms take precedence.<br />
|
||||||
→ https://github.com/Open-Autonomous-Connection/OAPL
|
→ https://github.com/Open-Autonomous-Connection/OAPL
|
||||||
|
|
||||||
|
## Include in own projects
|
||||||
|
````
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>github</id>
|
||||||
|
<url>https://maven.pkg.github.com/unlegitdqrk/unlegitlibrary</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.finn.unlegitlibrary</groupId>
|
||||||
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
|
<version>1.5.15</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
````
|
||||||
|
|
||||||
|
## 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<br />
|
||||||
|
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
|
6
pom.xml
6
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>me.finn.unlegitlibrary</groupId>
|
<groupId>me.finn.unlegitlibrary</groupId>
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
<version>1.5.15</version>
|
<version>1.6.0</version>
|
||||||
<url>https://unlegitdqrk.dev/UnlegitLibrary/</url>
|
<url>https://unlegitdqrk.dev/UnlegitLibrary/</url>
|
||||||
<description>Just a big library</description>
|
<description>Just a big library</description>
|
||||||
|
|
||||||
@@ -66,8 +66,8 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.8</source>
|
<source>16</source>
|
||||||
<target>1.8</target>
|
<target>16</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
@@ -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;
|
package me.finn.unlegitlibrary.network.system.client;
|
||||||
|
|
||||||
import me.finn.unlegitlibrary.event.EventManager;
|
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.Packet;
|
||||||
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
|
import me.finn.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
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.DefaultMethodsOverrider;
|
||||||
import me.finn.unlegitlibrary.utils.Logger;
|
import me.finn.unlegitlibrary.utils.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
import javax.net.ssl.*;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.*;
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.Socket;
|
import java.net.Proxy;
|
||||||
import java.net.SocketException;
|
import java.security.KeyStore;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
|
||||||
public final class NetworkClient {
|
public final class NetworkClient {
|
||||||
private final String host;
|
private final String host;
|
||||||
@@ -29,50 +22,33 @@ public final class NetworkClient {
|
|||||||
private final PacketHandler packetHandler;
|
private final PacketHandler packetHandler;
|
||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final int maxReconnectAttempts;
|
|
||||||
private final int reconnectDelay;
|
|
||||||
private Socket socket;
|
|
||||||
private final int timeout;
|
private final int timeout;
|
||||||
|
private SSLSocket socket;
|
||||||
private ObjectOutputStream outputStream;
|
private ObjectOutputStream outputStream;
|
||||||
private ObjectInputStream inputStream;
|
private ObjectInputStream inputStream;
|
||||||
private int clientID;
|
private final SSLSocketFactory sslSocketFactory;
|
||||||
private int currentAttempts;
|
private final SSLParameters sslParameters;
|
||||||
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;
|
|
||||||
|
|
||||||
this.packetHandler = packetHandler;
|
private final Thread receiveThread = new Thread(this::receive);
|
||||||
this.eventManager = eventManager;
|
|
||||||
this.logger = logger;
|
|
||||||
|
|
||||||
this.maxReconnectAttempts = reconnectAttempts;
|
public PacketHandler getPacketHandler() {
|
||||||
this.reconnectDelay = reconnectDelay;
|
return packetHandler;
|
||||||
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 EventManager getEventManager() {
|
public EventManager getEventManager() {
|
||||||
return eventManager;
|
return eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketHandler getPacketHandler() {
|
public ObjectInputStream getInputStream() {
|
||||||
return packetHandler;
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSLSocket getSocket() {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectOutputStream getOutputStream() {
|
||||||
|
return outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger getLogger() {
|
public Logger getLogger() {
|
||||||
@@ -87,200 +63,188 @@ public final class NetworkClient {
|
|||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
private NetworkClient(String host, int port, PacketHandler packetHandler,
|
||||||
return socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
|
EventManager eventManager, Logger logger,
|
||||||
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
|
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() {
|
public boolean isConnected() {
|
||||||
return maxReconnectAttempts != 0;
|
return socket != null && socket.isConnected() && !socket.isClosed()
|
||||||
|
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean connect() throws ConnectException {
|
public synchronized boolean connect() throws ConnectException {
|
||||||
if (isConnected()) return false;
|
if (isConnected()) return false;
|
||||||
|
|
||||||
if (logger == null) System.out.println("Trying to connect to " + host + ":" + port + "...");
|
if (logger != null) logger.info("Trying to connect to " + host + ":" + port + "...");
|
||||||
else logger.info("Trying to connect to " + host + ":" + port + "...");
|
else System.out.println("Trying to connect to " + host + ":" + port + "...");
|
||||||
|
|
||||||
try {
|
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.setTcpNoDelay(true);
|
||||||
socket.setSoTimeout(timeout);
|
socket.setSoTimeout(timeout);
|
||||||
|
try {
|
||||||
|
socket.startHandshake();
|
||||||
|
} catch (Exception handshakeEx) {
|
||||||
|
throw new ConnectException("Handshake failed: " + handshakeEx.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
||||||
inputStream = new ObjectInputStream(socket.getInputStream());
|
inputStream = new ObjectInputStream(socket.getInputStream());
|
||||||
|
|
||||||
receiveThread.start();
|
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));
|
eventManager.executeEvent(new ClientConnectedEvent(this));
|
||||||
|
if (logger != null) logger.info("Connected to " + host + ":" + port);
|
||||||
currentAttempts = 0;
|
else System.out.println("Connected to " + host + ":" + port);
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException exception) {
|
} catch (Exception e) {
|
||||||
if (isAutoReconnectEnabled()) {
|
throw new ConnectException("Failed to connect: " + e.getMessage());
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ConnectException("Failed to connect to " + host + ":" + port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receive() {
|
private void receive() {
|
||||||
if (!isConnected()) return;
|
try {
|
||||||
|
|
||||||
while (isConnected()) {
|
while (isConnected()) {
|
||||||
try {
|
|
||||||
Object received = inputStream.readObject();
|
Object received = inputStream.readObject();
|
||||||
|
handleReceived(received);
|
||||||
|
}
|
||||||
|
} catch (Exception e) { disconnect(); }
|
||||||
|
}
|
||||||
|
|
||||||
if (received instanceof Integer) {
|
private void handleReceived(Object received) throws IOException, ClassNotFoundException {
|
||||||
int packetID = (Integer) received;
|
if (received instanceof Integer id) {
|
||||||
if (packetHandler.isPacketIDRegistered(packetID)) {
|
if (packetHandler.isPacketIDRegistered(id)) {
|
||||||
Packet packet = packetHandler.getPacketByID(packetID);
|
Packet packet = packetHandler.getPacketByID(id);
|
||||||
if (packetHandler.handlePacket(packetID, packet, inputStream))
|
boolean handled = packetHandler.handlePacket(id, packet, inputStream);
|
||||||
eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
|
if (handled) eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
|
||||||
else
|
else eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
|
||||||
eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
|
|
||||||
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
||||||
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
||||||
} catch (SocketException ignored) {
|
}
|
||||||
|
|
||||||
|
public synchronized boolean disconnect() {
|
||||||
|
if (!isConnected()) return false;
|
||||||
try {
|
try {
|
||||||
disconnect();
|
receiveThread.interrupt();
|
||||||
} catch (ConnectException exception) {
|
if (outputStream != null) outputStream.close();
|
||||||
exception.printStackTrace();
|
if (inputStream != null) inputStream.close();
|
||||||
}
|
if (socket != null) socket.close();
|
||||||
} catch (Exception exception) {
|
} catch (IOException e) {
|
||||||
exception.printStackTrace();
|
if (logger != null) logger.exception("Error closing connection", e);
|
||||||
return;
|
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 {
|
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
if (!isConnected()) return false;
|
if (!isConnected()) return false;
|
||||||
|
boolean sent = packetHandler.sendPacket(packet, outputStream);
|
||||||
try {
|
if (sent) eventManager.executeEvent(new C_PacketSendEvent(this, packet));
|
||||||
if (packetHandler.sendPacket(packet, outputStream)) {
|
else eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
|
||||||
eventManager.executeEvent(new C_PacketSendEvent(this, packet));
|
return sent;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Builder ---
|
||||||
public static class ClientBuilder extends DefaultMethodsOverrider {
|
public static class ClientBuilder extends DefaultMethodsOverrider {
|
||||||
private String host;
|
private String host;
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private PacketHandler packetHandler;
|
private PacketHandler packetHandler;
|
||||||
private EventManager eventManager;
|
private EventManager eventManager;
|
||||||
private Logger logger;
|
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;
|
public ClientBuilder setHost(String host) { this.host = host; return this; }
|
||||||
private int reconnectDelay = 3000;
|
public ClientBuilder setPort(int port) { this.port = port; return this; }
|
||||||
private int timeout = 0;
|
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() {
|
public NetworkClient build() {
|
||||||
return new NetworkClient(host, port, packetHandler, eventManager, logger, maxReconnectAttempts, reconnectDelay, timeout);
|
if (sslSocketFactory == null && caFolder != null) {
|
||||||
|
try { sslSocketFactory = createSSLSocketFactory(caFolder, clientFolder, keyFolder); }
|
||||||
|
catch (Exception e) { throw new RuntimeException("Failed to create SSLFactory", e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ClientBuilder setEventManager(EventManager eventManager) {
|
return new NetworkClient(host, port, packetHandler, eventManager, logger,
|
||||||
this.eventManager = eventManager;
|
timeout, sslSocketFactory, sslParameters);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ClientBuilder setHost(String host) {
|
public static SSLSocketFactory createSSLSocketFactory(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
|
||||||
this.host = host;
|
// TrustStore (Root-CAs)
|
||||||
return this;
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
}
|
trustStore.load(null, null);
|
||||||
|
|
||||||
public final ClientBuilder setLogger(Logger logger) {
|
int caIndex = 1;
|
||||||
this.logger = logger;
|
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
|
||||||
return this;
|
try (FileInputStream fis = new FileInputStream(caFile)) {
|
||||||
}
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
java.security.cert.Certificate caCert = cf.generateCertificate(fis);
|
||||||
public final ClientBuilder setMaxReconnectAttempts(int maxReconnectAttempts) {
|
trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
|
||||||
this.maxReconnectAttempts = maxReconnectAttempts;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ClientBuilder setPacketHandler(PacketHandler packetHandler) {
|
|
||||||
this.packetHandler = packetHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ClientBuilder setPort(int port) {
|
|
||||||
this.port = port;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ClientBuilder setReconnectDelay(int reconnectDelay) {
|
|
||||||
this.reconnectDelay = reconnectDelay;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ClientBuilder setTimeout(int timeout) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(trustStore);
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
|
keyStore.load(null, null); // Kein Passwort nötig
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
java.security.PrivateKey key = PemUtils.loadPrivateKey(keyFile);
|
||||||
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(certFile);
|
||||||
|
|
||||||
|
keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
package me.finn.unlegitlibrary.network.system.server;
|
||||||
|
|
||||||
import me.finn.unlegitlibrary.network.system.packets.Packet;
|
import me.finn.unlegitlibrary.network.system.packets.Packet;
|
||||||
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
||||||
import me.finn.unlegitlibrary.network.system.server.events.*;
|
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.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
public class ConnectionHandler {
|
public class ConnectionHandler {
|
||||||
|
private SSLSocket socket;
|
||||||
public final NetworkServer networkServer;
|
|
||||||
private Socket socket;
|
|
||||||
private int clientID;
|
private int clientID;
|
||||||
|
|
||||||
private ObjectOutputStream outputStream;
|
private ObjectOutputStream outputStream;
|
||||||
private ObjectInputStream inputStream;
|
private ObjectInputStream inputStream;
|
||||||
public ConnectionHandler(NetworkServer server, Socket socket, int clientID) throws IOException, ClassNotFoundException {
|
private final NetworkServer server;
|
||||||
this.networkServer = server;
|
|
||||||
this.socket = socket;
|
public SSLSocket getSocket() {
|
||||||
this.clientID = clientID;
|
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());
|
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
||||||
inputStream = new ObjectInputStream(socket.getInputStream());
|
inputStream = new ObjectInputStream(socket.getInputStream());
|
||||||
@@ -37,97 +44,57 @@ public class ConnectionHandler {
|
|||||||
receiveThread.start();
|
receiveThread.start();
|
||||||
|
|
||||||
sendPacket(new ClientIDPacket());
|
sendPacket(new ClientIDPacket());
|
||||||
networkServer.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
|
server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
|
||||||
} public final Thread receiveThread = new Thread(this::receive);
|
|
||||||
|
|
||||||
public int getClientID() {
|
|
||||||
return clientID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isConnected() {
|
public final Thread receiveThread = new Thread(this::receive);
|
||||||
return networkServer.isRunning() && socket != null && socket.isConnected() && !socket.isClosed() && socket.isBound()
|
|
||||||
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
|
public int getClientID() { return clientID; }
|
||||||
}
|
public boolean isConnected() { return socket != null && socket.isConnected() && !socket.isClosed() && receiveThread.isAlive(); }
|
||||||
|
|
||||||
public synchronized boolean disconnect() {
|
public synchronized boolean disconnect() {
|
||||||
boolean wasConnected = isConnected();
|
if (!isConnected()) return false;
|
||||||
|
if (receiveThread.isAlive()) receiveThread.interrupt();
|
||||||
|
|
||||||
if (wasConnected) {
|
try { outputStream.close(); inputStream.close(); socket.close(); } catch (IOException ignored) {}
|
||||||
if (networkServer.getLogger() == null)
|
socket = null; outputStream = null; inputStream = null; clientID = -1;
|
||||||
System.out.println("Client ID '" + clientID + "' is disconnecting from server...");
|
|
||||||
else networkServer.getLogger().info("Client ID '" + clientID + "' is disconnecting from server...");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
|
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
if (!isConnected()) return false;
|
if (!isConnected()) return false;
|
||||||
|
boolean sent = server.getPacketHandler().sendPacket(packet, outputStream);
|
||||||
|
|
||||||
if (networkServer.getPacketHandler().sendPacket(packet, outputStream)) {
|
if (sent) server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
|
||||||
networkServer.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
|
else server.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
|
||||||
return true;
|
|
||||||
} else {
|
return sent;
|
||||||
networkServer.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receive() {
|
private void receive() {
|
||||||
if (!isConnected()) return;
|
|
||||||
|
|
||||||
while (isConnected()) {
|
while (isConnected()) {
|
||||||
try {
|
try {
|
||||||
Object received = inputStream.readObject();
|
Object received = inputStream.readObject();
|
||||||
|
|
||||||
if (received instanceof Integer) {
|
if (received instanceof Integer) {
|
||||||
int packetID = (Integer) received;
|
int id = (Integer) received;
|
||||||
if (networkServer.getPacketHandler().isPacketIDRegistered(packetID)) {
|
|
||||||
Packet packet = networkServer.getPacketHandler().getPacketByID(packetID);
|
if (server.getPacketHandler().isPacketIDRegistered(id)) {
|
||||||
if (networkServer.getPacketHandler().handlePacket(packetID, packet, inputStream))
|
Packet packet = server.getPacketHandler().getPacketByID(id);
|
||||||
networkServer.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
|
|
||||||
else
|
if (server.getPacketHandler().handlePacket(id, packet, inputStream)) server.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
|
||||||
networkServer.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
|
else server.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
|
||||||
} else
|
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
||||||
networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
||||||
} else networkServer.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
} catch (SocketException se) { disconnect(); }
|
||||||
} catch (SocketException ignored) {
|
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();
|
disconnect();
|
||||||
} catch (Exception exception) {
|
|
||||||
exception.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@@ -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;
|
package me.finn.unlegitlibrary.network.system.server;
|
||||||
|
|
||||||
import me.finn.unlegitlibrary.event.EventManager;
|
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.PacketHandler;
|
||||||
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
import me.finn.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
||||||
import me.finn.unlegitlibrary.network.system.server.events.IncomingConnectionEvent;
|
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.DefaultMethodsOverrider;
|
||||||
import me.finn.unlegitlibrary.utils.Logger;
|
import me.finn.unlegitlibrary.utils.Logger;
|
||||||
|
|
||||||
import java.io.IOException;
|
import javax.net.ssl.*;
|
||||||
import java.net.ServerSocket;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public final class NetworkServer {
|
public final class NetworkServer {
|
||||||
private final int port;
|
private final int port;
|
||||||
@@ -29,33 +23,14 @@ public final class NetworkServer {
|
|||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final int timeout;
|
private final int timeout;
|
||||||
private final int maxRestartAttempts;
|
private SSLServerSocket serverSocket;
|
||||||
private final int restartDelay;
|
private final SSLServerSocketFactory sslServerSocketFactory;
|
||||||
|
|
||||||
private final List<ConnectionHandler> connectionHandlers = new ArrayList<>();
|
private final List<ConnectionHandler> connectionHandlers = new ArrayList<>();
|
||||||
private int currentAttempts;
|
private final Thread incomingThread = new Thread(this::incomingConnections);
|
||||||
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;
|
|
||||||
|
|
||||||
this.packetHandler = packetHandler;
|
public List<ConnectionHandler> getConnectionHandlers() {
|
||||||
this.eventManager = eventManager;
|
return connectionHandlers;
|
||||||
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 int getPort() {
|
public int getPort() {
|
||||||
@@ -66,164 +41,133 @@ public final class NetworkServer {
|
|||||||
return packetHandler;
|
return packetHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ConnectionHandler> getConnectionHandlers() {
|
public Logger getLogger() {
|
||||||
return connectionHandlers;
|
return logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAutoRestartEnabled() {
|
public SSLServerSocket getServerSocket() {
|
||||||
return maxRestartAttempts != 0;
|
return serverSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
public EventManager getEventManager() {
|
||||||
return serverSocket != null && !serverSocket.isClosed() && serverSocket.isBound() &&
|
return eventManager;
|
||||||
incomingConnectionThread.isAlive() && !incomingConnectionThread.isInterrupted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandlerByID(int clientID) {
|
private boolean requireClientCert;
|
||||||
for (ConnectionHandler connectionHandler : connectionHandlers)
|
|
||||||
if (connectionHandler.getClientID() == clientID) return connectionHandler;
|
private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager,
|
||||||
return null;
|
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() {
|
public 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 + "...");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverSocket = new ServerSocket(port);
|
serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
|
||||||
|
serverSocket.setNeedClientAuth(requireClientCert);
|
||||||
serverSocket.setSoTimeout(timeout);
|
serverSocket.setSoTimeout(timeout);
|
||||||
|
serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
|
||||||
incomingConnectionThread.start();
|
incomingThread.start();
|
||||||
|
if (logger != null) logger.log("Server started on port " + port);
|
||||||
if (currentAttempts == 0) currentAttempts++;
|
else System.out.println("Server started on port " + port);
|
||||||
if (logger == null) System.out.println("Started at port " + port + " (Attempts: " + currentAttempts + ")");
|
|
||||||
else logger.info("Started at port " + port + " (Attempts: " + currentAttempts + ")");
|
|
||||||
|
|
||||||
currentAttempts = 0;
|
|
||||||
return true;
|
return true;
|
||||||
} catch (IOException exception) {
|
} catch (Exception e) {if (logger != null) logger.exception("Failed to start", e);
|
||||||
if (maxRestartAttempts != 0) {
|
else System.err.println("Failed to start: " + e.getMessage()); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void incomingConnections() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(restartDelay);
|
while (!serverSocket.isClosed()) {
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (isRunning()) {
|
|
||||||
Socket socket = serverSocket.accept();
|
Socket socket = serverSocket.accept();
|
||||||
socket.setTcpNoDelay(true);
|
if (!(socket instanceof SSLSocket ssl)) { socket.close(); continue; }
|
||||||
socket.setSoTimeout(timeout);
|
ssl.setTcpNoDelay(true);
|
||||||
|
ssl.setSoTimeout(timeout);
|
||||||
if (logger == null) System.out.println("Accepted connection from " + socket.getRemoteSocketAddress());
|
try { ssl.startHandshake(); }
|
||||||
else logger.info("Accepted connection from " + socket.getRemoteSocketAddress());
|
catch (Exception handshakeEx) {
|
||||||
|
if (logger != null) logger.exception("Handshake failed", handshakeEx);
|
||||||
IncomingConnectionEvent incomingConnectionEvent = new IncomingConnectionEvent(this, socket);
|
else System.err.println("Handshake failed: " + handshakeEx.getMessage());
|
||||||
|
ssl.close();
|
||||||
eventManager.executeEvent(incomingConnectionEvent);
|
continue;
|
||||||
|
|
||||||
if (incomingConnectionEvent.isCancelled()) {
|
|
||||||
socket.close();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionHandler connectionHandler = new ConnectionHandler(this, socket, connectionHandlers.size() + 1);
|
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);
|
connectionHandlers.add(connectionHandler);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
ssl.close();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} catch (IOException | ClassNotFoundException exception) {
|
|
||||||
exception.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) { e.printStackTrace(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sendPacket(int clientID, Packet packet) throws IOException, ClassNotFoundException {
|
// --- Builder ---
|
||||||
return getConnectionHandlerByID(clientID).sendPacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean sendPacket(Packet packet, int clientID) throws IOException, ClassNotFoundException {
|
|
||||||
return sendPacket(clientID, packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ServerBuilder extends DefaultMethodsOverrider {
|
public static class ServerBuilder extends DefaultMethodsOverrider {
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private PacketHandler packetHandler;
|
private PacketHandler packetHandler;
|
||||||
private EventManager eventManager;
|
private EventManager eventManager;
|
||||||
private Logger logger;
|
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;
|
public ServerBuilder setPort(int port) { this.port = port; return this; }
|
||||||
private int restartDelay = 3000;
|
public ServerBuilder setPacketHandler(PacketHandler handler) { this.packetHandler = handler; return this; }
|
||||||
private int timeout = 0;
|
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() {
|
public NetworkServer build() {
|
||||||
return new NetworkServer(port, packetHandler, eventManager, logger, maxRestartAttempts, restartDelay, timeout);
|
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) {
|
public static SSLServerSocketFactory createSSLServerSocketFactory(File caFolder, File serverCert, File serverKey) throws Exception {
|
||||||
this.eventManager = eventManager;
|
// TrustStore (Root-CAs)
|
||||||
return this;
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
}
|
trustStore.load(null, null);
|
||||||
|
|
||||||
public final ServerBuilder setLogger(Logger logger) {
|
int caIndex = 1;
|
||||||
this.logger = logger;
|
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
|
||||||
return this;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ServerBuilder setPacketHandler(PacketHandler packetHandler) {
|
|
||||||
this.packetHandler = packetHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ServerBuilder setPort(int port) {
|
|
||||||
this.port = port;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ServerBuilder setReconnectDelay(int reconnectDelay) {
|
|
||||||
this.restartDelay = reconnectDelay;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ServerBuilder setTimeout(int timeout) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(trustStore);
|
||||||
|
|
||||||
|
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});
|
||||||
|
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
|
kmf.init(keyStore, null);
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
|
||||||
|
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
|
return sslContext.getServerSocketFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,13 +11,16 @@ package me.finn.unlegitlibrary.network.system.server.events;
|
|||||||
import me.finn.unlegitlibrary.event.impl.CancellableEvent;
|
import me.finn.unlegitlibrary.event.impl.CancellableEvent;
|
||||||
import me.finn.unlegitlibrary.network.system.server.NetworkServer;
|
import me.finn.unlegitlibrary.network.system.server.NetworkServer;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
public class IncomingConnectionEvent extends CancellableEvent {
|
public class IncomingConnectionEvent extends CancellableEvent {
|
||||||
public final NetworkServer server;
|
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.server = server;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user