diff --git a/README.MD b/README.MD index 769191d..b854482 100644 --- a/README.MD +++ b/README.MD @@ -1,15 +1,27 @@ # UnlegitLibrary +## Overview +UnlegitLibrary is a general-purpose Java utility library that bundles a modular +event system, command framework, addon loader, networking (TCP/UDP with optional TLS), +and a wide set of math/number/string/file/reflection helpers. + +## Modules +- Addon system: loader + lifecycle events +- Event system: listeners, priorities, cancellable events +- Command system: command manager, permissions, execution events +- Network system: TCP/UDP transport, packet handling, optional TLS, UDP encryption +- Utilities: math/number helpers, strings, colors, files, reflection, logging + ## License Information GNU General Public License v3.0 (GPLv3)
The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.
-→ https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE +-> https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE 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://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL +-> https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL ## Include in own projects ```` @@ -31,6 +43,56 @@ In this context, OAPL terms take precedence.
```` +## NetworkSystem (TCP/UDP + TLS) +- TCP is the control channel (handshake, packet routing). +- UDP is optional and encrypted with a symmetric key negotiated over TCP. +- TLS can be enabled or disabled. For TLS, configure KeyStore/TrustStore explicitly. +- mTLS is supported: set client auth mode to REQUIRED and provide a TrustStore on the server. + +### Basic usage +```java +PacketHandler packetHandler = new PacketHandler(); +packetHandler.registerPacket(() -> new TestTextPacket("")); + +NetworkServer server = new NetworkServer(packetHandler); +server.configureSSL(false, ClientAuthMode.NONE); +server.start(25565, 25566); + +NetworkClient client = new NetworkClient(packetHandler); +client.configureSSL(false); +client.connect("127.0.0.1", 25565); +``` + +### TLS with TrustStore (server validation) +```java +KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray()); +KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray()); + +NetworkServer server = new NetworkServer(packetHandler); +server.configureSSL(true, ClientAuthMode.NONE, serverKeyStore, "changeit".toCharArray(), null); +server.start(25565, 25566); + +NetworkClient client = new NetworkClient(packetHandler); +client.configureSSL(true, null, null, clientTrustStore); +client.connect("127.0.0.1", 25565); +``` + +### TLS with Client Certificate (mTLS) +```java +KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray()); +KeyStore serverTrustStore = loadStore("certs/server-trust.p12", "changeit".toCharArray()); +KeyStore clientKeyStore = loadStore("certs/client.p12", "changeit".toCharArray()); +KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray()); + +NetworkServer server = new NetworkServer(packetHandler); +server.configureSSL(true, ClientAuthMode.REQUIRED, serverKeyStore, "changeit".toCharArray(), serverTrustStore); +server.start(25565, 25566); + +NetworkClient client = new NetworkClient(packetHandler); +client.configureSSL(true, clientKeyStore, "changeit".toCharArray(), clientTrustStore); +client.connect("127.0.0.1", 25565); +``` + ## Certificate generation for NetworkSystem ### Creating Root-CA: ```` @@ -66,4 +128,15 @@ client.crt = Client-Certificate signed by Root-CA 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 +8. Optional: Put the Client-Certificate in "certificates/client"-Folder + +### Helper: load PKCS12 stores in Java +```java +private static KeyStore loadStore(String path, char[] password) throws Exception { + KeyStore store = KeyStore.getInstance("PKCS12"); + try (FileInputStream in = new FileInputStream(path)) { + store.load(in, password); + } + return store; +} +``` diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java new file mode 100644 index 0000000..bfc19a8 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client; + +import dev.unlegitdqrk.unlegitlibrary.event.EventManager; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketFailedReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto; + +import javax.crypto.SecretKey; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.security.KeyStore; +import java.util.UUID; + +/** + * TCP/UDP client with optional TLS and UDP encryption. + */ +public class NetworkClient { + + private Socket tcpSocket; + private DatagramChannel udpChannel; + + private DataInputStream inputStream; + private DataOutputStream outputStream; + + private SecretKey udpKey; + + private final PacketHandler packetHandler; + private final EventManager eventManager; + private final Thread tcpReceiveThread; + private final Thread udpReceiveThread; + + private boolean sslEnabled = true; + private volatile UUID uniqueID; + + private String host; + private int tcpPort = -1; + private int udpPort = -1; + private KeyStore keyStore; + private char[] keyPassword; + private KeyStore trustStore; + private long handshakeTimeoutMillis = 5000; + + /** + * Creates a client with a default {@link EventManager}. + * + * @param packetHandler packet handler + */ + public NetworkClient(PacketHandler packetHandler) { + this(packetHandler, new EventManager()); + } + + /** + * Creates a client with a custom {@link EventManager}. + * + * @param packetHandler packet handler + * @param eventManager event manager + */ + public NetworkClient(PacketHandler packetHandler, EventManager eventManager) { + this.packetHandler = packetHandler; + this.eventManager = eventManager; + this.tcpReceiveThread = new Thread(this::tcpReceive); + this.udpReceiveThread = new Thread(this::udpReceive); + } + + /** + * Enables or disables TLS. + * + * @param sslEnabled enable TLS + */ + public void configureSSL(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + } + + /** + * Enables or disables TLS with explicit key and trust stores. + * + * @param sslEnabled enable TLS + * @param keyStore client key store (optional) + * @param keyPassword key store password + * @param trustStore trust store (required when TLS is enabled) + */ + public void configureSSL(boolean sslEnabled, + KeyStore keyStore, + char[] keyPassword, + KeyStore trustStore) { + this.sslEnabled = sslEnabled; + this.keyStore = keyStore; + this.keyPassword = keyPassword; + this.trustStore = trustStore; + } + + /** + * Sets the TCP handshake timeout in milliseconds. + * + * @param handshakeTimeoutMillis timeout in ms + */ + public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) { + this.handshakeTimeoutMillis = handshakeTimeoutMillis; + } + + /** + * Connects to a server. + * + * @param host server host + * @param tcpPort server TCP port + */ + public void connect(String host, int tcpPort) throws Exception { + this.host = host; + this.tcpPort = tcpPort; + + if (sslEnabled) { + if (trustStore == null) { + throw new IllegalStateException("SSL enabled but no TrustStore configured"); + } + SSLContext ctx = SSLContexts.create(keyStore, keyPassword, trustStore); + SSLSocket sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(host, tcpPort); + sslSocket.startHandshake(); + tcpSocket = sslSocket; + } else { + tcpSocket = new Socket(host, tcpPort); + } + + inputStream = new DataInputStream(tcpSocket.getInputStream()); + outputStream = new DataOutputStream(tcpSocket.getOutputStream()); + + tcpReceiveThread.start(); + + long deadline = System.currentTimeMillis() + handshakeTimeoutMillis; + while (uniqueID == null) { + if (System.currentTimeMillis() >= deadline) { + disconnect(); + throw new IOException("TCP handshake timeout (no server response)"); + } + Thread.sleep(5); + } + + Thread.sleep(10); + eventManager.executeEvent(new ClientConnectedEvent(this)); + } + + private void tcpReceive() { + try { + while (true) { + UUID uuid = UUID.fromString(inputStream.readUTF()); + int packetId = inputStream.readInt(); + if (packetId == -1) { + uniqueID = uuid; + udpPort = inputStream.readInt(); + int keyLen = inputStream.readInt(); + byte[] keyBytes = new byte[keyLen]; + inputStream.readFully(keyBytes); + udpKey = UdpCrypto.fromBytes(keyBytes); + connectUDP(); + } else { + Packet packet = null; + if ((packet = packetHandler.readPacket(inputStream, uuid, packetId)) != null) + eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.TCP)); + } + } + } catch (Exception e) { + disconnect(); + } + } + + private void connectUDP() throws Exception { + udpChannel = DatagramChannel.open(); + udpChannel.connect(new InetSocketAddress(host, udpPort)); + udpReceiveThread.start(); + + // initial handshake + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeUTF(uniqueID.toString()); + dos.writeInt(0); + dos.flush(); + + ByteBuffer buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null); + udpChannel.write(buffer); + } + + private void udpReceive() { + ByteBuffer buffer = ByteBuffer.allocate(65536); + int packetId = 0; + Packet packet = null; + while (udpChannel.isConnected()) { + try { + buffer.clear(); + udpChannel.receive(buffer); + buffer.flip(); + + byte[] plaintext = UdpCrypto.decrypt(udpKey, buffer, null); + + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(plaintext)); + UUID uuid = UUID.fromString(dis.readUTF()); + packetId = dis.readInt(); + if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) { + eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.UDP)); + } else eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP)); + + } catch (Exception ignored) { + eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP)); + } + } + } + + /** + * Sends a packet via TCP or UDP. + * + * @param packet packet to send + * @param protocol transport protocol + */ + public void sendPacket(Packet packet, TransportProtocol protocol) throws Exception { + if (protocol == TransportProtocol.TCP) { + packetHandler.sendPacket(outputStream, packet, uniqueID); + eventManager.executeEvent(new C_PacketSendEvent(this, packet, TransportProtocol.TCP)); + } else if (protocol == TransportProtocol.UDP && udpChannel != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + packetHandler.sendPacket(dos, packet, uniqueID); + dos.flush(); + + ByteBuffer buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null); + udpChannel.write(buffer); + eventManager.executeEvent(new C_PacketSendEvent(this, packet, TransportProtocol.UDP)); + } + } + + /** + * Disconnects the client and releases resources. + */ + public void disconnect() { + // Stop threads first + tcpReceiveThread.interrupt(); + udpReceiveThread.interrupt(); + + try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {} + try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {} + try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} + try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} + + inputStream = null; + outputStream = null; + tcpSocket = null; + udpChannel = null; + + eventManager.executeEvent(new ClientDisconnectedEvent(this)); + + uniqueID = null; + udpKey = null; + udpPort = -1; + } + + + /** + * Builder for {@link NetworkClient}. + */ + public static class Builder { + private PacketHandler packetHandler; + private EventManager eventManager; + private boolean sslEnabled = true; + private KeyStore keyStore; + private char[] keyPassword; + private KeyStore trustStore; + private long handshakeTimeoutMillis = 5000; + + /** + * Sets the packet handler (required). + * + * @param packetHandler packet handler + * @return builder + */ + public Builder packetHandler(PacketHandler packetHandler) { + this.packetHandler = packetHandler; + return this; + } + + /** + * Sets a custom event manager. + * + * @param eventManager event manager + * @return builder + */ + public Builder eventManager(EventManager eventManager) { + this.eventManager = eventManager; + return this; + } + + /** + * Enables or disables TLS. + * + * @param sslEnabled enable TLS + * @return builder + */ + public Builder sslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + return this; + } + + /** + * Sets the key store and its password. + * + * @param keyStore key store + * @param keyPassword key store password + * @return builder + */ + public Builder keyStore(KeyStore keyStore, char[] keyPassword) { + this.keyStore = keyStore; + this.keyPassword = keyPassword; + return this; + } + + /** + * Sets the trust store (root CAs). + * + * @param trustStore trust store + * @return builder + */ + public Builder trustStore(KeyStore trustStore) { + this.trustStore = trustStore; + return this; + } + + /** + * Sets the handshake timeout in milliseconds. + * + * @param handshakeTimeoutMillis timeout in ms + * @return builder + */ + public Builder handshakeTimeoutMillis(long handshakeTimeoutMillis) { + this.handshakeTimeoutMillis = handshakeTimeoutMillis; + return this; + } + + /** + * Builds the client instance. + * + * @return network client + */ + public NetworkClient build() { + if (packetHandler == null) { + throw new IllegalStateException("PacketHandler is required"); + } + + NetworkClient client = (eventManager == null) + ? new NetworkClient(packetHandler) + : new NetworkClient(packetHandler, eventManager); + + if (keyStore != null || trustStore != null) { + client.configureSSL(sslEnabled, keyStore, keyPassword, trustStore); + } else { + client.configureSSL(sslEnabled); + } + client.setHandshakeTimeoutMillis(handshakeTimeoutMillis); + + return client; + } + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java new file mode 100644 index 0000000..076c9b6 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class C_PacketFailedReadEvent extends Event { + + private final NetworkClient client; + private final Packet packet; + private final int packetId; + private final TransportProtocol protocol; + + public C_PacketFailedReadEvent(NetworkClient client, Packet packet, int packetId, TransportProtocol protocol) { + this.client = client; + this.packet = packet; + this.packetId = packetId; + this.protocol = protocol; + } + + public Packet getPacket() { + return packet; + } + + public int getPacketId() { + return packetId; + } + + public NetworkClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java new file mode 100644 index 0000000..ad79f7e --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class C_PacketReadEvent extends Event { + private final NetworkClient client; + private final Packet packet; + private final TransportProtocol protocol; + + public C_PacketReadEvent(NetworkClient client, Packet packet, TransportProtocol protocol) { + this.client = client; + this.packet = packet; + this.protocol = protocol; + } + + public Packet getPacket() { + return packet; + } + + public NetworkClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java new file mode 100644 index 0000000..cbd0147 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class C_PacketSendEvent extends Event { + + private final NetworkClient client; + private final Packet packet; + private final TransportProtocol protocol; + + public C_PacketSendEvent(NetworkClient client, Packet packet, TransportProtocol protocol) { + this.client = client; + this.packet = packet; + this.protocol = protocol; + } + + public Packet getPacket() { + return packet; + } + + public NetworkClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java new file mode 100644 index 0000000..2cd1543 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; + +public class ClientConnectedEvent extends Event { + + private final NetworkClient client; + + public ClientConnectedEvent(NetworkClient client) { + this.client = client; + } + + public NetworkClient getClient() { + return client; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java new file mode 100644 index 0000000..8c35eae --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; + +public class ClientDisconnectedEvent extends Event { + + private final NetworkClient client; + + public ClientDisconnectedEvent(NetworkClient client) { + this.client = client; + } + + public NetworkClient getClient() { + return client; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java index 9a19f77..06bbbe9 100644 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java @@ -33,13 +33,13 @@ public class PacketHandler { * @param input DataInputStream to read from * @return Packet instance or null if failed */ - public boolean readPacket(DataInputStream input, UUID clientID, int id) throws IOException { + public Packet readPacket(DataInputStream input, UUID clientID, int id) throws IOException { Supplier factory = factories.get(id); - if (factory == null) return false; + if (factory == null) return null; Packet packet = factory.get(); packet.read(input, clientID); - return true; + return packet; } /** diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java similarity index 60% rename from src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java rename to src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java index f9d83d0..b96b3e7 100644 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java @@ -6,21 +6,24 @@ * See LICENSE-File if exists */ -package dev.unlegitdqrk.unlegitlibrary.network.system.tcp; +package dev.unlegitdqrk.unlegitlibrary.network.system.server; import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client.S_ClientDisconnectedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketFailedReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketSendEvent; import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto; +import javax.crypto.SecretKey; import java.io.*; -import java.lang.reflect.InvocationTargetException; -import java.net.*; +import java.net.SocketAddress; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; -import java.nio.channels.NotYetBoundException; -import java.nio.channels.NotYetConnectedException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.List; +import java.util.UUID; public class ConnectedClient { @@ -30,33 +33,36 @@ public class ConnectedClient { private DataInputStream inputStream; private DataOutputStream outputStream; - private DatagramChannel udpChannel; // für serverseitiges Senden + private DatagramChannel udpChannel; private final Thread tcpReceiveThread; private final NetworkServer server; private int udpPort = -1; - - public Socket getTcpSocket() { - return tcpSocket; - } + private SecretKey udpKey; public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException { this.tcpSocket = tcpSocket; this.uniqueID = uniqueID; this.udpPort = udpPort; this.server = server; - this.tcpReceiveThread = new Thread(this::tcpReceive); outputStream = new DataOutputStream(tcpSocket.getOutputStream()); inputStream = new DataInputStream(tcpSocket.getInputStream()); + this.udpKey = UdpCrypto.generateKey(); + + tcpReceiveThread = new Thread(this::tcpReceive); tcpReceiveThread.start(); server.getConnectedClients().add(this); + // Sende UUID & UDP-Port an Client outputStream.writeUTF(uniqueID.toString()); - outputStream.writeInt(-1); + outputStream.writeInt(-1); // Handshake-Packet outputStream.writeInt(udpPort); + byte[] keyBytes = udpKey.getEncoded(); + outputStream.writeInt(keyBytes.length); + outputStream.write(keyBytes); outputStream.flush(); } @@ -65,20 +71,17 @@ public class ConnectedClient { } public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException { - if (protocol == TransportProtocol.UDP) { - sendPacketUDP(packet); - return; - } - if (protocol == TransportProtocol.TCP) { sendPacketTCP(packet); - return; + } else if (protocol == TransportProtocol.UDP) { + sendPacketUDP(packet); } } private void sendPacketTCP(Packet packet) throws IOException { if (!isTCPConnected()) return; server.getPacketHandler().sendPacket(outputStream, packet, uniqueID); + server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.TCP)); } private void sendPacketUDP(Packet packet) throws IOException { @@ -89,28 +92,30 @@ public class ConnectedClient { server.getPacketHandler().sendPacket(dos, packet, uniqueID); dos.flush(); - ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray()); - - // Hole die gespeicherte UDP-Adresse des Clients - SocketAddress clientAddress = server.getClientUdpAddress(uniqueID); - if (clientAddress == null) { - return; + ByteBuffer buffer; + try { + buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null); + } catch (Exception e) { + throw new IOException("Failed to encrypt UDP packet", e); } - udpChannel.send(buffer, clientAddress); // korrekt an Client senden + SocketAddress clientAddress = server.getClientUdpAddress(uniqueID); + if (clientAddress != null && udpChannel != null && udpChannel.isOpen()) { + udpChannel.send(buffer, clientAddress); + server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.UDP)); + } } - - private void tcpReceive() { try { - while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) { + while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && !tcpSocket.isClosed()) { UUID uuid = UUID.fromString(inputStream.readUTF()); int packetId = inputStream.readInt(); - - if (!server.getPacketHandler().readPacket(inputStream, uuid, packetId)) { - // TODO: UnknownPacketReceivedEvent - } + Packet packet = null; + if ((packet = server.getPacketHandler().readPacket(inputStream, uuid, packetId)) != null) + server.getEventManager().executeEvent(new S_PacketReadEvent(server, this, packet, TransportProtocol.TCP)); + else + server.getEventManager().executeEvent(new S_PacketFailedReadEvent(server, this, packet, packetId, TransportProtocol.TCP)); } } catch (IOException e) { disconnect(); @@ -123,11 +128,15 @@ public class ConnectedClient { try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {} try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {} + try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} tcpSocket = null; + udpChannel = null; + udpPort = -1; inputStream = null; outputStream = null; - udpPort = -1; + + server.getEventManager().executeEvent(new S_ClientDisconnectedEvent(server, this)); uniqueID = null; server.getConnectedClients().remove(this); @@ -152,4 +161,12 @@ public class ConnectedClient { public int getUdpPort() { return udpPort; } + + public Socket getTcpSocket() { + return tcpSocket; + } + + public SecretKey getUdpKey() { + return udpKey; + } } diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java new file mode 100644 index 0000000..c03970e --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server; + +import dev.unlegitdqrk.unlegitlibrary.event.EventManager; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client.S_ClientConnectedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketFailedReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketReadEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.ServerStartedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.ServerStoppedEvent; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto; + +import javax.net.ssl.*; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.security.KeyStore; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +/** + * TCP/UDP server with optional TLS and UDP encryption. + */ +public class NetworkServer { + private ServerSocket tcpSocket; + private Thread tcpThread; + + private DatagramChannel udpChannel; + private Thread udpThread; + + private final Map clientUdpAddresses = new ConcurrentHashMap<>(); + private final PacketHandler packetHandler; + private final EventManager eventManager; + private final List connectedClients; + + private int udpPort = -1; + + /* === TLS CONFIG === */ + private boolean sslEnabled = true; + private ClientAuthMode clientAuthMode = ClientAuthMode.NONE; + private KeyStore keyStore; + private char[] keyPassword; + private KeyStore trustStore; + + /** + * Creates a server with a custom {@link EventManager}. + * + * @param packetHandler packet handler + * @param eventManager event manager + */ + private NetworkServer(PacketHandler packetHandler, EventManager eventManager) { + this.packetHandler = packetHandler; + this.eventManager = eventManager; + this.connectedClients = Collections.synchronizedList(new ArrayList<>()); + } + + /** + * Enables or disables TLS and sets the client authentication mode. + * + * @param sslEnabled enable TLS + * @param clientAuthMode client authentication mode + */ + public void configureSSL(boolean sslEnabled, ClientAuthMode clientAuthMode) { + this.sslEnabled = sslEnabled; + this.clientAuthMode = clientAuthMode; + } + + /** + * Enables or disables TLS with explicit key and trust stores. + * + * @param sslEnabled enable TLS + * @param clientAuthMode client authentication mode + * @param keyStore server key store (required when TLS is enabled) + * @param keyPassword key store password + * @param trustStore trust store (required for client auth) + */ + public void configureSSL(boolean sslEnabled, + ClientAuthMode clientAuthMode, + KeyStore keyStore, + char[] keyPassword, + KeyStore trustStore) { + this.sslEnabled = sslEnabled; + this.clientAuthMode = clientAuthMode; + this.keyStore = keyStore; + this.keyPassword = keyPassword; + this.trustStore = trustStore; + } + + /** + * Starts the server. + * + * @param tcpPort TCP port + * @param udpPort UDP port (-1 to disable UDP) + */ + public void start(int tcpPort, int udpPort) throws IOException, InterruptedException { + this.udpPort = udpPort; + + if (sslEnabled) { + if (keyStore == null) { + throw new IllegalStateException("SSL enabled but no server KeyStore configured"); + } + if (clientAuthMode != ClientAuthMode.NONE && trustStore == null) { + throw new IllegalStateException("Client auth enabled but no TrustStore configured"); + } + + SSLContext sslContext = SSLContexts.create(keyStore, keyPassword, trustStore); + SSLServerSocketFactory factory = sslContext.getServerSocketFactory(); + + tcpSocket = factory.createServerSocket(tcpPort); + SSLServerSocket sslServerSocket = (SSLServerSocket) tcpSocket; + + switch (clientAuthMode) { + case REQUIRED -> sslServerSocket.setNeedClientAuth(true); + case OPTIONAL -> sslServerSocket.setWantClientAuth(true); + case NONE -> { + sslServerSocket.setNeedClientAuth(false); + sslServerSocket.setWantClientAuth(false); + } + } + } else { + tcpSocket = new ServerSocket(tcpPort); + } + + tcpThread = new Thread(this::tcpAcceptLoop); + tcpThread.start(); + + if (isUDPEnabled()) { + udpChannel = DatagramChannel.open(); + udpChannel.bind(new InetSocketAddress(udpPort)); + udpThread = new Thread(this::udpReceiveLoop); + udpThread.start(); + } + + Thread.sleep(10); + eventManager.executeEvent(new ServerStartedEvent(this)); + } + + /** + * Stops the server and disconnects all clients. + */ + public void stop() { + if (tcpThread != null) tcpThread.interrupt(); + if (udpThread != null) udpThread.interrupt(); + + try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} + try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} + + List snapshot; + synchronized (connectedClients) { + snapshot = new ArrayList<>(connectedClients); + } + for (ConnectedClient client : snapshot) { + client.disconnect(); + } + + tcpSocket = null; + udpChannel = null; + tcpThread = null; + udpThread = null; + udpPort = -1; + + eventManager.executeEvent(new ServerStoppedEvent(this)); + } + + private void tcpAcceptLoop() { + while (!Thread.currentThread().isInterrupted()) { + Socket socket = null; + try { + socket = tcpSocket.accept(); + if (socket instanceof SSLSocket sslSocket) { + sslSocket.startHandshake(); + } + + ConnectedClient client = new ConnectedClient(this, socket, UUID.randomUUID(), udpPort); + + if (isUDPEnabled()) client.setUdpChannel(udpChannel); + Thread.sleep(100); + + eventManager.executeEvent(new S_ClientConnectedEvent(this, client)); + } catch (IOException | InterruptedException e) { + break; + } + } + } + + private void udpReceiveLoop() { + ByteBuffer buffer = ByteBuffer.allocate(65536); + while (!Thread.currentThread().isInterrupted()) { + try { + buffer.clear(); + SocketAddress sender = udpChannel.receive(buffer); + if (sender == null) continue; + buffer.flip(); + if (buffer.remaining() < 13) continue; // 12-byte IV + at least 1 byte payload + + UUID mappedUuid = clientUdpAddresses.get(sender); + if (mappedUuid != null) { + ConnectedClient mappedClient = getClientByUuid(mappedUuid); + if (mappedClient != null) { + if (tryHandleUdpPacket(mappedClient, sender, buffer.asReadOnlyBuffer())) { + continue; + } + } + } + + // Fallback: try all clients if mapping missing or decrypt failed (e.g., NAT rebinding) + List snapshot; + synchronized (connectedClients) { + snapshot = new ArrayList<>(connectedClients); + } + for (ConnectedClient client : snapshot) { + if (tryHandleUdpPacket(client, sender, buffer.asReadOnlyBuffer())) { + break; + } + } + } catch (IOException e) { + if (tcpSocket != null) { + try { tcpSocket.close(); } catch (IOException ignored) {} + } + if (Thread.currentThread().isInterrupted()) { + break; + } + } + } + } + + /** + * @return true if UDP is enabled + */ + public boolean isUDPEnabled() { + return udpPort != -1; + } + + /** + * @return packet handler + */ + public PacketHandler getPacketHandler() { + return packetHandler; + } + + /** + * @return connected clients list (synchronized) + */ + public List getConnectedClients() { + return connectedClients; + } + + /** + * Returns the UDP socket address for a client UUID. + * + * @param uuid client UUID + * @return socket address or null + */ + public SocketAddress getClientUdpAddress(UUID uuid) { + AtomicReference socket = new AtomicReference<>(); + clientUdpAddresses.forEach((socketAddress, client) -> { + if (client.equals(uuid)) socket.set(socketAddress); + }); + + return socket.get(); + } + + private ConnectedClient getClientByUuid(UUID uuid) { + synchronized (connectedClients) { + for (ConnectedClient client : connectedClients) { + if (uuid.equals(client.getUniqueID())) return client; + } + } + return null; + } + + private boolean tryHandleUdpPacket(ConnectedClient client, SocketAddress sender, ByteBuffer rawBuffer) { + int packetId = 0; + Packet packet = null; + try { + rawBuffer.rewind(); + byte[] plaintext = UdpCrypto.decrypt(client.getUdpKey(), rawBuffer, null); + + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(plaintext)); + UUID uuid = UUID.fromString(dis.readUTF()); + packetId = dis.readInt(); + + if (!uuid.equals(client.getUniqueID())) return false; + + if (packetId == 0) { + clientUdpAddresses.put(sender, uuid); + return true; + } + + clientUdpAddresses.put(sender, uuid); + if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) { + eventManager.executeEvent(new S_PacketReadEvent(this, client, packet, TransportProtocol.UDP)); + return true; + } + } catch (Exception ignored) {} + + + eventManager.executeEvent(new S_PacketFailedReadEvent(this, client, packet, packetId, TransportProtocol.UDP)); + return false; + } + + /** + * @return event manager + */ + public EventManager getEventManager() { + return eventManager; + } + + + /** + * Builder for {@link NetworkServer}. + */ + public static class Builder { + private PacketHandler packetHandler; + private EventManager eventManager; + private boolean sslEnabled = true; + private ClientAuthMode clientAuthMode = ClientAuthMode.NONE; + private KeyStore keyStore; + private char[] keyPassword; + private KeyStore trustStore; + + /** + * Sets the packet handler (required). + * + * @param packetHandler packet handler + * @return builder + */ + public Builder packetHandler(PacketHandler packetHandler) { + this.packetHandler = packetHandler; + return this; + } + + /** + * Sets a custom event manager. + * + * @param eventManager event manager + * @return builder + */ + public Builder eventManager(EventManager eventManager) { + this.eventManager = eventManager; + return this; + } + + /** + * Enables or disables TLS. + * + * @param sslEnabled enable TLS + * @return builder + */ + public Builder sslEnabled(boolean sslEnabled) { + this.sslEnabled = sslEnabled; + return this; + } + + /** + * Sets the client authentication mode. + * + * @param clientAuthMode client authentication mode + * @return builder + */ + public Builder clientAuthMode(ClientAuthMode clientAuthMode) { + this.clientAuthMode = clientAuthMode; + return this; + } + + /** + * Sets the key store and its password. + * + * @param keyStore key store + * @param keyPassword key store password + * @return builder + */ + public Builder keyStore(KeyStore keyStore, char[] keyPassword) { + this.keyStore = keyStore; + this.keyPassword = keyPassword; + return this; + } + + /** + * Sets the trust store (root CAs). + * + * @param trustStore trust store + * @return builder + */ + public Builder trustStore(KeyStore trustStore) { + this.trustStore = trustStore; + return this; + } + + /** + * Builds the server instance. + * + * @return network server + */ + public NetworkServer build() { + if (packetHandler == null) { + throw new IllegalStateException("PacketHandler is required"); + } + + if (eventManager == null) { + throw new IllegalStateException("EventManager is required"); + } + + NetworkServer server = new NetworkServer(packetHandler, eventManager); + + if (keyStore != null || trustStore != null || clientAuthMode != ClientAuthMode.NONE) { + server.configureSSL(sslEnabled, clientAuthMode, keyStore, keyPassword, trustStore); + } else { + server.configureSSL(sslEnabled, clientAuthMode); + } + + return server; + } + } +} + + diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java new file mode 100644 index 0000000..025a259 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; + +public class S_ClientConnectedEvent extends Event { + + private final NetworkServer server; + private final ConnectedClient client; + + public S_ClientConnectedEvent(NetworkServer server, ConnectedClient client) { + this.server = server; + this.client = client; + } + + public ConnectedClient getClient() { + return client; + } + + public NetworkServer getServer() { + return server; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java new file mode 100644 index 0000000..a0e2064 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; + +public class S_ClientDisconnectedEvent extends Event { + + private final NetworkServer server; + private final ConnectedClient client; + + public S_ClientDisconnectedEvent(NetworkServer server, ConnectedClient client) { + this.server = server; + this.client = client; + } + + public ConnectedClient getClient() { + return client; + } + + public NetworkServer getServer() { + return server; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java new file mode 100644 index 0000000..db339e1 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class S_PacketFailedReadEvent extends Event { + + private final NetworkServer server; + private final ConnectedClient client; + private final Packet packet; + private final int packetId; + private final TransportProtocol protocol; + + public S_PacketFailedReadEvent(NetworkServer server, ConnectedClient client, Packet packet, int packetId, TransportProtocol protocol) { + this.server = server; + this.client = client; + this.packet = packet; + this.packetId = packetId; + this.protocol = protocol; + } + + public NetworkServer getServer() { + return server; + } + + public Packet getPacket() { + return packet; + } + + public int getPacketId() { + return packetId; + } + + public ConnectedClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java new file mode 100644 index 0000000..4f28537 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class S_PacketReadEvent extends Event { + + private final NetworkServer server; + private final ConnectedClient client; + private final Packet packet; + private final TransportProtocol protocol; + + public S_PacketReadEvent(NetworkServer server, ConnectedClient client, Packet packet, TransportProtocol protocol) { + this.server = server; + this.client = client; + this.packet = packet; + this.protocol = protocol; + } + + public NetworkServer getServer() { + return server; + } + + public Packet getPacket() { + return packet; + } + + public ConnectedClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java new file mode 100644 index 0000000..0e83c1a --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class S_PacketSendEvent extends Event { + + private final NetworkServer server; + private final ConnectedClient client; + private final Packet packet; + private final TransportProtocol protocol; + + public S_PacketSendEvent(NetworkServer server, ConnectedClient client, Packet packet, TransportProtocol protocol) { + this.server = server; + this.client = client; + this.packet = packet; + this.protocol = protocol; + } + + public NetworkServer getServer() { + return server; + } + + public Packet getPacket() { + return packet; + } + + public ConnectedClient getClient() { + return client; + } + + public TransportProtocol getProtocol() { + return protocol; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java new file mode 100644 index 0000000..7fb1b0b --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; + +public class ServerStartedEvent extends Event { + + private final NetworkServer server; + + public ServerStartedEvent(NetworkServer server) { + this.server = server; + } + + public NetworkServer getServer() { + return server; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java new file mode 100644 index 0000000..b9fe051 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 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 + */ + +/* + * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state; + +import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; +import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; + +public class ServerStoppedEvent extends Event { + + private final NetworkServer server; + + public ServerStoppedEvent(NetworkServer server) { + this.server = server; + } + + public NetworkServer getServer() { + return server; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java deleted file mode 100644 index de3e846..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2026 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 - */ - -/* - * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.tcp; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; - -import java.io.*; -import java.lang.reflect.InvocationTargetException; -import java.net.InetSocketAddress; -import java.net.PortUnreachableException; -import java.net.Socket; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.ClosedByInterruptException; -import java.nio.channels.DatagramChannel; -import java.util.UUID; - -public class NetworkClient { - - private Socket tcpSocket; - private final Thread tcpReceiveThread; - - private DatagramChannel udpChannel; - private final Thread udpReceiveThread; - - private DataInputStream inputStream; - private DataOutputStream outputStream; - - private volatile UUID uniqueID; - private final PacketHandler packetHandler; - - private int tcpPort = -1; - private String host; - private int udpPort = -1; - - public NetworkClient(PacketHandler packetHandler) { - this.packetHandler = packetHandler; - this.tcpReceiveThread = new Thread(this::tcpReceive); - this.udpReceiveThread = new Thread(this::udpReceive); - } - - public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException { - if (protocol == TransportProtocol.TCP) { - if (!isTCPConnected()) return; - packetHandler.sendPacket(outputStream, packet, uniqueID); - } - - if (protocol == TransportProtocol.UDP) { - if (!isUDPConnected()) return; - try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); - DataOutputStream oos = new DataOutputStream(baos)) { - packetHandler.sendPacket(oos, packet, uniqueID); - - ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray()); - udpChannel.write(buffer); - - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * Connects the client to a server with optional UDP. - * - * @param host Hostname or IP (IPv4/IPv6) - * @param tcpPort TCP port - * @throws IOException - * @throws InterruptedException - */ - public void connect(String host, int tcpPort) throws IOException, InterruptedException { - this.host = host; - this.tcpPort = tcpPort; - - tcpSocket = new Socket(host, tcpPort); - outputStream = new DataOutputStream(tcpSocket.getOutputStream()); - inputStream = new DataInputStream(tcpSocket.getInputStream()); - tcpReceiveThread.start(); - - while (uniqueID == null) Thread.sleep(10); - Thread.sleep(10); - } - - private void tcpReceive() { - try { - while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) { - UUID uuid = UUID.fromString(inputStream.readUTF()); - int packetId = inputStream.readInt(); - - if (!packetHandler.readPacket(inputStream, uuid, packetId)) { - if (packetId == -1) { - uniqueID = uuid; - this.udpPort = inputStream.readInt(); - if (isUDPEnabled()) { - connectUDP(); - } - } - } - } - } catch (IOException | InterruptedException e) { - disconnect(); - } - } - - private void udpReceive() { - if (!isUDPConnected()) return; - - ByteBuffer buffer = ByteBuffer.allocate(65536); - - while (!Thread.currentThread().isInterrupted() && isUDPConnected()) { - try { - buffer.clear(); - SocketAddress sender = udpChannel.receive(buffer); - if (sender == null) continue; - - buffer.flip(); - handleUdpPacket(buffer); - - } catch (PortUnreachableException | AsynchronousCloseException ignored) {} - catch (IOException e) { - e.printStackTrace(); - break; - } - } - } - - private void handleUdpPacket(ByteBuffer buffer) { - try (DataInputStream ois = new DataInputStream( - new ByteArrayInputStream(buffer.array(), 0, buffer.limit()))) { - - UUID uuid = UUID.fromString(ois.readUTF()); - int packetId = ois.readInt(); - if (!packetHandler.readPacket(ois, uuid, packetId)) { - // TODO: UnknownPacketReceivedEvent - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void connectUDP() throws IOException, InterruptedException { - if (!isUDPEnabled()) return; - if (udpReceiveThread.isAlive()) return; - - udpChannel = DatagramChannel.open(); - udpChannel.socket().setReuseAddress(true); - udpChannel.configureBlocking(true); - udpChannel.connect(new InetSocketAddress(host, udpPort)); - udpReceiveThread.start(); - - ByteBuffer buffer = ByteBuffer.wrap(new byte[]{0}); - udpChannel.write(buffer); - } - - public void disconnect() { - tcpReceiveThread.interrupt(); - - try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {} - try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {} - try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} - try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} - - tcpSocket = null; - udpChannel = null; - inputStream = null; - outputStream = null; - - host = null; - tcpPort = -1; - udpPort = -1; - uniqueID = null; - } - - public boolean isTCPConnected() { - return uniqueID != null && tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed(); - } - - public boolean isUDPEnabled() { - return udpPort != -1; - } - - public boolean isUDPConnected() { - return isUDPEnabled() && isTCPConnected() && udpChannel != null && udpChannel.isConnected(); - } - - public UUID getUniqueID() { - return uniqueID; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java deleted file mode 100644 index f5b75d7..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2026 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 dev.unlegitdqrk.unlegitlibrary.network.system.tcp; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; - -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class NetworkServer { - - // Events-Handler (optional) - public abstract static class EventsServer { - public abstract void onConnect(ConnectedClient client); - } - - public EventsServer events; - - private ServerSocket tcpSocket; - private Thread tcpThread; - - private DatagramChannel udpChannel; - private Thread udpThread; - private final Map clientUdpAddresses = new ConcurrentHashMap<>(); - - private final PacketHandler packetHandler; - private final List connectedClients; - - private int udpPort = -1; - - public NetworkServer(PacketHandler packetHandler) { - this.packetHandler = packetHandler; - this.connectedClients = Collections.synchronizedList(new ArrayList<>()); - } - - public void start(int tcpPort, int udpPort) throws IOException { - this.udpPort = udpPort; - - // TCP starten - tcpSocket = new ServerSocket(); - tcpSocket.bind(new InetSocketAddress(tcpPort)); - - tcpThread = new Thread(this::tcpAcceptLoop); - tcpThread.start(); - - // UDP starten, falls aktiviert - if (isUDPEnabled()) { - udpChannel = DatagramChannel.open(); - udpChannel.socket().setReuseAddress(true); - udpChannel.configureBlocking(true); - udpChannel.bind(new InetSocketAddress(udpPort)); - udpThread = new Thread(this::udpReceiveLoop); - udpThread.start(); - } - } - - private void tcpAcceptLoop() { - while (!Thread.currentThread().isInterrupted() && tcpSocket.isBound()) { - try { - Socket clientSocket = tcpSocket.accept(); - ConnectedClient client = new ConnectedClient(this, clientSocket, UUID.randomUUID(), udpPort); - - if (isUDPEnabled()) { - client.setUdpChannel(udpChannel); - } - Thread.sleep(100); - - if (events != null) events.onConnect(client); - - } catch (IOException | InterruptedException e) { - break; - } - } - } - - private void udpReceiveLoop() { - ByteBuffer buffer = ByteBuffer.allocate(65536); - - while (!Thread.currentThread().isInterrupted() && isUDPEnabled()) { - try { - buffer.clear(); - SocketAddress sender = udpChannel.receive(buffer); - if (sender == null) continue; - - buffer.flip(); - - if (buffer.remaining() == 1 && buffer.get(0) == 0) { - // Handshake: wir kennen die UUID über TCP - // Suche den ConnectedClient mit passender TCP-Adresse - Optional clientOpt = connectedClients.stream() - .filter(c -> c.getTcpSocket().getInetAddress().equals(((InetSocketAddress) sender).getAddress())) - .findFirst(); - - if (clientOpt.isPresent()) { - ConnectedClient client = clientOpt.get(); - clientUdpAddresses.put(client.getUniqueID(), sender); - } - - continue; // kein normales Packet parsen - } - - DataInputStream dis = new DataInputStream( - new ByteArrayInputStream(buffer.array(), buffer.position(), buffer.remaining()) - ); - - UUID uuid = UUID.fromString(dis.readUTF()); - int packetId = dis.readInt(); - - // Speichere die UDP-Adresse des Clients - - clientUdpAddresses.put(uuid, sender); - - for (ConnectedClient connectedClient : connectedClients) { - if (connectedClient.getUniqueID().equals(uuid)) { - packetHandler.readPacket(dis, uuid, packetId); - } - } - - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - - public void stop() { - if (tcpThread != null) tcpThread.interrupt(); - if (udpThread != null) udpThread.interrupt(); - - new ArrayList<>(connectedClients).forEach(ConnectedClient::disconnect); - - try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} - try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} - - tcpSocket = null; - udpChannel = null; - udpPort = -1; - } - - public boolean isTCPOnline() { - return tcpSocket != null && tcpSocket.isBound(); - } - - public boolean isUDPEnabled() { - return udpPort != -1; - } - - public boolean isUDPConnected() { - return isUDPEnabled() && isTCPOnline() && udpChannel != null && udpChannel.isOpen(); - } - - public PacketHandler getPacketHandler() { - return packetHandler; - } - - public List getConnectedClients() { - return connectedClients; - } - - public SocketAddress getClientUdpAddress(UUID uuid) { - return clientUdpAddresses.get(uuid); - } - -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java new file mode 100644 index 0000000..5d8be10 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java @@ -0,0 +1,10 @@ +package dev.unlegitdqrk.unlegitlibrary.network.system.utils; + +/** + * Defines client certificate requirements for TLS. + */ +public enum ClientAuthMode { + NONE, + OPTIONAL, + REQUIRED +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java new file mode 100644 index 0000000..d143809 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java @@ -0,0 +1,86 @@ +package dev.unlegitdqrk.unlegitlibrary.network.system.utils; + +import javax.net.ssl.*; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; + +/** + * Utility class for creating SSLContexts. + */ +public final class SSLContexts { + + private SSLContexts() {} + + /** + * Creates an SSLContext. + * + * @param trustAll If true, disables certificate validation (SSL still active) + * @param keyStore Optional keystore for client authentication + * @param keyPassword Keystore password + */ + public static SSLContext create(boolean trustAll, KeyStore keyStore, char[] keyPassword) { + try { + TrustManager[] trustManagers = trustAll + ? new TrustManager[]{new TrustAllX509()} + : null; + + KeyManager[] keyManagers = null; + if (keyStore != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, keyPassword); + keyManagers = kmf.getKeyManagers(); + } + + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(keyManagers, trustManagers, new SecureRandom()); + return ctx; + + } catch (Exception e) { + throw new IllegalStateException("Failed to create SSLContext", e); + } + } + + /** + * Creates an SSLContext using explicit key and trust stores. + * + * @param keyStore Optional keystore for client/server authentication + * @param keyPassword Keystore password + * @param trustStore Optional truststore for certificate validation (root CAs) + */ + public static SSLContext create(KeyStore keyStore, char[] keyPassword, KeyStore trustStore) { + try { + KeyManager[] keyManagers = null; + if (keyStore != null) { + KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, keyPassword); + keyManagers = kmf.getKeyManagers(); + } + + TrustManager[] trustManagers = null; + if (trustStore != null) { + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(trustStore); + trustManagers = tmf.getTrustManagers(); + } + + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(keyManagers, trustManagers, new SecureRandom()); + return ctx; + } catch (Exception e) { + throw new IllegalStateException("Failed to create SSLContext", e); + } + } + + /** + * TrustManager that accepts all certificates. + */ + private static final class TrustAllX509 implements X509TrustManager { + @Override public void checkClientTrusted(X509Certificate[] c, String a) {} + @Override public void checkServerTrusted(X509Certificate[] c, String a) {} + @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java new file mode 100644 index 0000000..f6c17b1 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java @@ -0,0 +1,80 @@ +package dev.unlegitdqrk.unlegitlibrary.network.system.utils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.ByteBuffer; +import java.security.SecureRandom; + +/** + * Helper class for encrypting/decrypting UDP packets. + */ +public final class UdpCrypto { + + private static final SecureRandom RNG = new SecureRandom(); + private static final int GCM_TAG_BITS = 128; + + private UdpCrypto() {} + + /** + * Generates a random AES-256 key for UDP. + */ + public static SecretKey generateKey() { + byte[] key = new byte[32]; + RNG.nextBytes(key); + return new SecretKeySpec(key, "AES"); + } + + /** + * Creates a key from raw bytes. + */ + public static SecretKey fromBytes(byte[] keyBytes) { + return new SecretKeySpec(keyBytes, "AES"); + } + + /** + * Encrypts a UDP payload with AES-GCM. + * + * @param key AES key + * @param plaintext payload + * @param aad additional authenticated data (e.g., UUID + packetId) + * @return ByteBuffer ready to send + */ + public static ByteBuffer encrypt(SecretKey key, byte[] plaintext, byte[] aad) throws Exception { + byte[] iv = new byte[12]; + RNG.nextBytes(iv); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv)); + if (aad != null) cipher.updateAAD(aad); + + byte[] ciphertext = cipher.doFinal(plaintext); + + ByteBuffer buffer = ByteBuffer.allocate(12 + ciphertext.length); + buffer.put(iv).put(ciphertext); + buffer.flip(); + return buffer; + } + + /** + * Decrypts a UDP payload with AES-GCM. + * + * @param key AES key + * @param in ByteBuffer received + * @param aad additional authenticated data (must match encryption) + * @return plaintext + */ + public static byte[] decrypt(SecretKey key, ByteBuffer in, byte[] aad) throws Exception { + byte[] iv = new byte[12]; + in.get(iv); + byte[] ct = new byte[in.remaining()]; + in.get(ct); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv)); + if (aad != null) cipher.updateAAD(aad); + + return cipher.doFinal(ct); + } +} diff --git a/src/test/java/Client.java b/src/test/java/Client.java deleted file mode 100644 index 4c16d0d..0000000 --- a/src/test/java/Client.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2026 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 - */ - -import dev.unlegitdqrk.unlegitlibrary.event.EventListener; -import dev.unlegitdqrk.unlegitlibrary.event.EventManager; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*; -import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; - -public class Client extends EventListener { - - static EventManager eventManager = new EventManager(); - static NetworkClient client; - - public static void main(String[] args) throws Exception { - PacketHandler packetHandler = new PacketHandler(); - packetHandler.registerPacket(() -> new TestTextPacket("")); - client = new NetworkClient(packetHandler); - client.connect("127.0.0.1", 25565); - client.sendPacket(new TestTextPacket("client: tcp"), TransportProtocol.TCP); - client.sendPacket(new TestTextPacket("client: udp"), TransportProtocol.UDP); - } -} diff --git a/src/test/java/TestServerMain.java b/src/test/java/TestServerMain.java deleted file mode 100644 index db27afa..0000000 --- a/src/test/java/TestServerMain.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2026 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 - */ - -import dev.unlegitdqrk.unlegitlibrary.event.EventListener; -import dev.unlegitdqrk.unlegitlibrary.event.EventManager; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*; -import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; - -import java.io.IOException; - -public class TestServerMain extends EventListener { - - static NetworkServer server; - static EventManager eventManager = new EventManager(); - public static void main(String[] args) throws Exception { - PacketHandler packetHandler = new PacketHandler(); - packetHandler.registerPacket(() -> new TestTextPacket("")); - - eventManager.registerListener(new TestServerMain()); - - server = new NetworkServer(packetHandler); - server.start(25565, 25566); - - server.events = new NetworkServer.EventsServer() { - @Override - public void onConnect(ConnectedClient client) { - try { - client.sendPacket(new TestTextPacket("server: tcp"), TransportProtocol.TCP); - client.sendPacket(new TestTextPacket("server: udp"), TransportProtocol.UDP); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }; - } -} diff --git a/src/test/java/TestTextPacket.java b/src/test/java/TestTextPacket.java deleted file mode 100644 index 5615a96..0000000 --- a/src/test/java/TestTextPacket.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2026 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 - */ - - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; - -import java.io.*; -import java.util.UUID; - -/** - * Simple text packet used for round-trip transport tests over TCP and UDP. - */ -public final class TestTextPacket extends Packet { - - private volatile String message; - - @Override - public int getPacketID() { - return 100; - } - - /** - * Creates a packet with a message. - * - * @param message message - */ - public TestTextPacket(String message) { - this.message = message; - } - - public TestTextPacket() { - } - - /** - * @return message - */ - public String getMessage() { - return message; - } - - @Override - public void write(DataOutputStream stream) { - try { - stream.writeUTF(message == null ? "" : message); - System.out.println("[SEND] " + message); - } catch (IOException e) { - throw new IllegalStateException("Failed to write TestTextPacket: " + e.getMessage(), e); - } - } - - @Override - public void read(DataInputStream stream, UUID clientID) { - try { - this.message = stream.readUTF(); - System.out.println("[RECEIVE] " + this.message); - } catch (IOException e) { - throw new IllegalStateException("Failed to read TestTextPacket: " + e.getMessage(), e); - } - } -}