Initial commit.

Note, In Progress:
Adding all fragment data is verified and sent correctly.
This commit is contained in:
Captain ALM 2022-06-14 04:11:10 +01:00
commit 96c9864092
Signed by: alfred
GPG Key ID: 4E4ADD02609997B1
45 changed files with 4718 additions and 0 deletions

30
.gitignore vendored Normal file
View File

@ -0,0 +1,30 @@
target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Built files
out/
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

7
.idea/discord.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

6
.idea/misc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/calmnetlib.iml" filepath="$PROJECT_DIR$/calmnetlib.iml" />
</modules>
</component>
</project>

29
LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2022, Captain ALM
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Captain ALM Network Library (Java)
This is the java network library with support for SSL, standard cipher encryption (.net Compatible!), fragmentation, packet object and network stream support.
This library contains streams that can wrap both Socket and DatagramSocket objects.
Sending large amounts over UDP is also do-able using the fragmentation part of the library.
This library targets Java 8.
(C) Captain ALM 2022 - Under the BSD 3-Clause License

12
calmnetlib.iml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="calmstdcrypt" level="application" />
</component>
</module>

View File

@ -0,0 +1,266 @@
package com.captainalm.lib.calmnet;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* This class provides SSL utilities to create {@link SSLContext} and {@link SSLSocket}
* objects using JKS files (Or other supported formats).
*
* @author Captain ALM
*/
public class SSLUtilities {
protected static final X509TrustManager jreTrustManager = getJreTrustManager();
protected static final String defaultSSLContextProtocol = "TLSv1";
/**
* Loads a Keystore of a certain type from a file given the password.
*
* @param type The type of keystore (pass null for the default type).
* @param file The file to load the keystore from.
* @param password The password of the keystore (Can be null).
* @return The keystore.
* @throws NullPointerException file is null.
* @throws SSLUtilityException An Exception has occurred.
*/
public static KeyStore loadKeyStore(String type, File file, String password) throws SSLUtilityException {
if (file == null) throw new NullPointerException("file is null");
if (password == null || password.equals("")) password = "changeit";
try (FileInputStream inputStream = new FileInputStream(file)) {
KeyStore toret = KeyStore.getInstance((type == null) ? KeyStore.getDefaultType() : type);
toret.load(inputStream, password.toCharArray());
return toret;
} catch (IOException | CertificateException | KeyStoreException | NoSuchAlgorithmException e) {
throw new SSLUtilityException(e);
}
}
/**
* Gets the SSL context without the JRE Trust Store using a unified {@link KeyStore}.
*
* @param algorithmName The name of the context protocol or null for the JRE Default (TLSv1).
* @param unifiedKeyStore The keystore for use with the private and trust stores.
* @param keyStorePassword The password of the key store or null (Use "changeit" for JKS keystore defaults).
* @return The SSLContext.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLContext getSSLContextNoJRETrust(String algorithmName, KeyStore unifiedKeyStore, char[] keyStorePassword) throws SSLUtilityException {
return getSSLContextNoJRETrust(algorithmName, unifiedKeyStore, keyStorePassword, unifiedKeyStore);
}
/**
* Gets the SSL context without the JRE Trust Store using separate private and trust {@link KeyStore}s.
* @param algorithmName The name of the context protocol or null for the JRE Default (TLSv1).
* @param privateKeyStore The keystore for use with the private store.
* @param privateKeyStorePassword The password of the private key store or null (Use "changeit" for JKS keystore defaults).
* @param trustKeyStore The keystore for use with the trust store.
* @return The SSLContext.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLContext getSSLContextNoJRETrust(String algorithmName, KeyStore privateKeyStore, char[] privateKeyStorePassword, KeyStore trustKeyStore) throws SSLUtilityException {
try {
SSLContext toret = SSLContext.getInstance((algorithmName == null) ? defaultSSLContextProtocol : algorithmName);
if (algorithmName == null || !algorithmName.equalsIgnoreCase("Default")) {
KeyManagerFactory kmanagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmanagerFactory.init(privateKeyStore, privateKeyStorePassword);
TrustManagerFactory tmanagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmanagerFactory.init(trustKeyStore);
toret.init(kmanagerFactory.getKeyManagers(), buildTrustManagerArray(getX509TrustManager(tmanagerFactory)), null);
}
return toret;
} catch (RuntimeException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | KeyManagementException e) {
throw new SSLUtilityException(e);
}
}
/**
* Gets the SSL context merged with the JRE Trust Store using a unified {@link KeyStore}.
*
* @param algorithmName The name of the context protocol or null for the JRE Default (TLSv1).
* @param unifiedKeyStore The keystore for use with the private and trust stores.
* @param keyStorePassword The password of the key store or null (Use "changeit" for JKS keystore defaults).
* @return The SSLContext.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLContext getSSLContext(String algorithmName, KeyStore unifiedKeyStore, char[] keyStorePassword) throws SSLUtilityException {
return getSSLContext(algorithmName, unifiedKeyStore, keyStorePassword, unifiedKeyStore);
}
/**
* Gets the SSL context merged with the JRE Trust Store using separate private and trust {@link KeyStore}s.
* @param algorithmName The name of the context protocol or null for the JRE Default (TLSv1).
* @param privateKeyStore The keystore for use with the private store.
* @param privateKeyStorePassword The password of the private key store or null (Use "changeit" for JKS keystore defaults).
* @param trustKeyStore The keystore for use with the trust store.
* @return The SSLContext.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLContext getSSLContext(String algorithmName, KeyStore privateKeyStore, char[] privateKeyStorePassword, KeyStore trustKeyStore) throws SSLUtilityException {
try {
SSLContext toret = SSLContext.getInstance((algorithmName == null) ? defaultSSLContextProtocol : algorithmName);
if (algorithmName == null || !algorithmName.equalsIgnoreCase("Default")) {
KeyManagerFactory kmanagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmanagerFactory.init(privateKeyStore, privateKeyStorePassword);
toret.init(kmanagerFactory.getKeyManagers(), buildTrustManagerArray(new MergedX509TrustManager(trustKeyStore)), null);
}
return toret;
} catch (RuntimeException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | KeyManagementException e) {
throw new SSLUtilityException(e);
}
}
/**
* Creates a new {@link SSLSocket} using the specified {@link SSLContext}, host and port.
*
* @param sslContext The SSL Context to create the socket from.
* @param host The host to connect to.
* @param port The port to connect to.
* @return The SSLSocket.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLSocket getSSLClientSocket(SSLContext sslContext, String host, int port) throws SSLUtilityException {
try {
return (SSLSocket) sslContext.getSocketFactory().createSocket(host, port);
} catch (RuntimeException | IOException e) {
throw new SSLUtilityException(e);
}
}
/**
* Upgrades an existing {@link Socket} to an {@link SSLSocket} using the specified {@link SSLContext},
* {@link Socket}, host, port and if the specified socket should be closed when the returned socket is closed.
* This socket is in client mode (Upgrade for client side).
*
* @param sslContext The SSL Context to create the socket from.
* @param socket The socket to wrap.
* @param host The host to "connect" to.
* @param port The port to "connect" to.
* @param autoClose If the underlying socket should be closed when the returned socket is closed.
* @return The SSLSocket.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLSocket upgradeClientSocketToSSL(SSLContext sslContext, Socket socket, String host, int port, boolean autoClose) throws SSLUtilityException {
return upgradeClientSocketToSSL(sslContext, socket, host, port, autoClose, true);
}
/**
* Upgrades an existing {@link Socket} to an {@link SSLSocket} using the specified {@link SSLContext},
* {@link Socket}, host, port and if the specified socket should be closed when the returned socket is closed.
*
* @param sslContext The SSL Context to create the socket from.
* @param socket The socket to wrap.
* @param host The host to "connect" to.
* @param port The port to "connect" to.
* @param autoClose If the underlying socket should be closed when the returned socket is closed.
* @param onClient Is this being called on the client side.
* @return The SSLSocket.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLSocket upgradeClientSocketToSSL(SSLContext sslContext, Socket socket, String host, int port, boolean autoClose, boolean onClient) throws SSLUtilityException {
try {
SSLSocket toret = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
toret.setUseClientMode(onClient);
return toret;
} catch (RuntimeException | IOException e) {
throw new SSLUtilityException(e);
}
}
/**
* Gets the SSL Server socket for the specified {@link SSLContext}, port, backlog and {@link InetAddress}.
*
* @param sslContext The SSL Context to create the socket from.
* @param port The port to listen on.
* @param backlog The number of connections that can be queued.
* @param ifAddress The network interface to listen on (null means listen on all network interfaces).
* @return The SSLServerSocket.
* @throws SSLUtilityException An Exception has occurred.
*/
public static SSLServerSocket getSSLServerSocket(SSLContext sslContext, int port, int backlog, InetAddress ifAddress) throws SSLUtilityException {
try {
return (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port, backlog, ifAddress);
} catch (RuntimeException | IOException e) {
throw new SSLUtilityException(e);
}
}
protected static X509TrustManager getJreTrustManager() {
try {
return getX509TrustManager((KeyStore) null);
} catch (RuntimeException | KeyStoreException | NoSuchAlgorithmException e) {
return null;
}
}
protected static X509TrustManager getX509TrustManager(KeyStore keyStore) throws KeyStoreException, NoSuchAlgorithmException {
TrustManagerFactory tmanagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmanagerFactory.init(keyStore);
return getX509TrustManager(tmanagerFactory);
}
protected static X509TrustManager getX509TrustManager(TrustManagerFactory tmanagerFactory) {
for (TrustManager c : tmanagerFactory.getTrustManagers()) if (c instanceof X509TrustManager) return (X509TrustManager) c;
return null;
}
protected static TrustManager[] buildTrustManagerArray(TrustManager trustManager) {
if (trustManager == null) return null; else return new TrustManager[] {trustManager};
}
/**
* This class provides a merging of a loaded trust manager and the JRE default trust manager.
* (Useful for clients)
*
* @author Captain ALM
*/
private static class MergedX509TrustManager implements X509TrustManager {
protected final X509TrustManager loadedTrustManager;
public MergedX509TrustManager(KeyStore keyStore) throws KeyStoreException, NoSuchAlgorithmException {
loadedTrustManager = getX509TrustManager(keyStore);
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
loadedTrustManager.checkClientTrusted(chain, authType);
} catch (CertificateException e) {
if (jreTrustManager == null) throw new CertificateException(new NullPointerException("jreTrustManager is null"));
jreTrustManager.checkClientTrusted(chain, authType);
}
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
loadedTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
if (jreTrustManager == null) throw new CertificateException(new NullPointerException("jreTrustManager is null"));
jreTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
if (jreTrustManager == null) {
if (loadedTrustManager == null) return new X509Certificate[0];
return loadedTrustManager.getAcceptedIssuers();
} else {
X509Certificate[] jre = jreTrustManager.getAcceptedIssuers();
X509Certificate[] lod = loadedTrustManager.getAcceptedIssuers();
X509Certificate[] toret = new X509Certificate[jre.length + lod.length];
int index = 0;
for (X509Certificate c : jre) toret[index++] = c;
for (X509Certificate c : lod) toret[index++] = c;
return toret;
}
}
}
}

View File

@ -0,0 +1,60 @@
package com.captainalm.lib.calmnet;
import java.security.PrivilegedActionException;
/**
* This class provides the SSL Utility exception wrapper class.
* See {@link SSLUtilityException#getCause()} to find out the underlying exception.
*
* @author Captain ALM
*/
public class SSLUtilityException extends Exception {
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public SSLUtilityException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public SSLUtilityException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of <tt>(cause==null ? null : cause.toString())</tt> (which
* typically contains the class and detail message of <tt>cause</tt>).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public SSLUtilityException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,8 @@
/**
* This library contains the network socket system for calmclientandserver.
*
* @since 0.0
* @author Captain ALM
* @version 0.0
*/
package com.captainalm.lib.calmnet;

View File

@ -0,0 +1,146 @@
package com.captainalm.lib.calmnet.packet;
import com.captainalm.lib.calmnet.packet.core.*;
import com.captainalm.lib.calmnet.packet.fragment.*;
import com.captainalm.lib.stdcrypt.encryption.ICipherFactory;
/**
* This class provides a standard extensible {@link IPacketFactory} for calmnet packets.
*
* @author Captain ALM
*/
public class CALMNETPacketFactory implements IPacketFactory {
protected IPacketFactory factoryToUse;
protected PacketLoader loaderToUse;
protected ICipherFactory cipherToUse;
protected boolean streamPreferred;
/**
* Constructs a new Instance of CALMNETPacketFactory with if {@link IStreamedPacket}s are preferred and the specified {@link PacketLoader}.
*
* @param preferStreamPackets If streamed packets are preferred for construction.
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public CALMNETPacketFactory(boolean preferStreamPackets, PacketLoader loader) {
this(preferStreamPackets ,loader, null);
}
/**
* Constructs a new Instance of CALMNETPacketFactory with if {@link IStreamedPacket}s are preferred, the specified {@link PacketLoader} and the {@link IPacketFactory}.
*
* @param preferStreamPackets If streamed packets are preferred for construction.
* @param loader The packet loader to use.
* @param factory The packet factory to use or null (null signifies to use the same instance).
* @throws NullPointerException loader is null.
*/
public CALMNETPacketFactory(boolean preferStreamPackets, PacketLoader loader, IPacketFactory factory) {
streamPreferred = preferStreamPackets;
setPacketLoader(loader);
setPacketFactory(factory);
}
/**
* Constructs a {@link IPacket} of the protocol specified by the passed {@link PacketProtocolInformation} instance.
*
* @param information The protocol information to use.
* @return The constructed packet or null.
* @throws NullPointerException The information is null.
*/
@Override
public IPacket getPacket(PacketProtocolInformation information) {
if (information == null) throw new NullPointerException("information is null");
if (information.equals(Base64Packet.getTheProtocol())) return new Base64Packet(factoryToUse, loaderToUse);
if (information.equals(EncryptedPacket.getTheProtocol()) && cipherToUse != null) return new EncryptedPacket(factoryToUse, loaderToUse, cipherToUse);
if (information.equals(NetworkEncryptionUpgradePacket.getTheProtocol())) return new NetworkEncryptionUpgradePacket(null, false, false, cipherToUse);
if (information.equals(NetworkIdentifierPacket.getTheProtocol())) return new NetworkIdentifierPacket(null);
if (information.equals(NetworkSSLUpgradePacket.getTheProtocol())) return new NetworkSSLUpgradePacket(null);
if (information.equals(FragmentAllocatePacket.getTheProtocol())) return new FragmentAllocatePacket(null, null);
if (information.equals(FragmentAllocationPacket.getTheProtocol())) return new FragmentAllocationPacket(null, null, null);
if (information.equals(FragmentMessagePacket.getTheProtocol())) return new FragmentMessagePacket(null, null, null);
if (information.equals(FragmentMessageResponsePacket.getTheProtocol())) return new FragmentMessageResponsePacket(null, null, null);
if (information.equals(FragmentRetrySendPacket.getTheProtocol())) return new FragmentRetrySendPacket(null, null);
if (information.equals(FragmentSendCompletePacket.getTheProtocol())) return new FragmentSendCompletePacket(null, null);
if (information.equals(FragmentSendStopPacket.getTheProtocol())) return new FragmentSendStopPacket(null);
return null;
}
/**
* Gets if {@link #getPacket(PacketProtocolInformation)} prefers returning {@link IStreamedPacket}s if possible.
*
* @return If streamed packets are preferred for construction.
*/
@Override
public boolean areStreamPacketsPreferred() {
return streamPreferred;
}
/**
* Sets if {@link #getPacket(PacketProtocolInformation)} prefers returning {@link IStreamedPacket}s if possible.
*
* @param preferred If streamed packets are preferred for construction.
*/
@Override
public void setStreamPacketsPreferred(boolean preferred) {
streamPreferred = preferred;
}
/**
* Gets the {@link IPacketFactory} in use (Could be the same instance).
*
* @return The packet factory in use.
*/
public IPacketFactory getPacketFactory() {
return factoryToUse;
}
/**
* Sets the {@link IPacketFactory} in use (null signifies to use the same instance).
*
* @param factory The packet factory to use or null.
*/
public void setPacketFactory(IPacketFactory factory) {
if (factory == null) factoryToUse = this; else factoryToUse = factory;
}
/**
* Gets the {@link PacketLoader} in use.
*
* @return The packet loader in use.
*/
public PacketLoader getPacketLoader() {
return loaderToUse;
}
/**
* Sets the {@link PacketLoader} to use.
*
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public void setPacketLoader(PacketLoader loader) {
if (loader == null) throw new NullPointerException("loader is null");
loaderToUse = loader;
}
/**
* Gets the {@link ICipherFactory} in use (Could be the same instance).
*
* @return The cipher factory in use.
*/
public ICipherFactory getCipherFactory() {
return cipherToUse;
}
/**
* Sets the {@link ICipherFactory} in use.
*
* @param factory The cipher factory to use or null.
*/
public void setCipherFactory(ICipherFactory factory) {
cipherToUse = factory;
}
}

View File

@ -0,0 +1,71 @@
package com.captainalm.lib.calmnet.packet;
import com.captainalm.lib.calmnet.packet.core.*;
/**
* This class provides a standard extensible {@link IPacketFactory} for calmnet packets
* with the ability to set the {@link IPacket} of supporting packets.
*
* @author Captain ALM
*/
public class CALMNETPacketFactoryWithPacket extends CALMNETPacketFactory {
protected IPacket packetToUse;
/**
* Constructs a new Instance of CALMNETPacketFactoryWithPacket with if {@link IStreamedPacket}s are preferred and the specified {@link PacketLoader}.
*
* @param preferStreamPackets If streamed packets are preferred for construction.
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public CALMNETPacketFactoryWithPacket(boolean preferStreamPackets, PacketLoader loader) {
this(preferStreamPackets, loader, null);
}
/**
* Constructs a new Instance of CALMNETPacketFactoryWithPacket with if {@link IStreamedPacket}s are preferred, the specified {@link PacketLoader} and the {@link IPacketFactory}.
*
* @param preferStreamPackets If streamed packets are preferred for construction.
* @param loader The packet loader to use.
* @param factory The packet factory to use or null (null signifies to use the same instance).
* @throws NullPointerException loader is null.
*/
public CALMNETPacketFactoryWithPacket(boolean preferStreamPackets, PacketLoader loader, IPacketFactory factory) {
super(preferStreamPackets, loader, factory);
}
/**
* Constructs a {@link IPacket} of the protocol specified by the passed {@link PacketProtocolInformation} instance.
*
* @param information The protocol information to use.
* @return The constructed packet or null.
* @throws NullPointerException The information is null.
*/
@Override
public IPacket getPacket(PacketProtocolInformation information) {
if (information == null) throw new NullPointerException("information is null");
if (information.equals(Base64Packet.getTheProtocol())) return new Base64Packet(factoryToUse, loaderToUse, packetToUse);
if (information.equals(EncryptedPacket.getTheProtocol()) && cipherToUse != null) return new EncryptedPacket(factoryToUse, loaderToUse, cipherToUse, packetToUse);
return super.getPacket(information);
}
/**
* Gets the {@link IPacket} in use (Could be the same instance).
*
* @return The packet in use.
*/
public IPacket getPacket() {
return packetToUse;
}
/**
* Sets the {@link IPacket} in use.
*
* @param packet The packet to use or null.
*/
public void setPacket(IPacket packet) {
packetToUse = packet;
}
}

View File

@ -0,0 +1,387 @@
package com.captainalm.lib.calmnet.packet;
import com.captainalm.lib.calmnet.packet.fragment.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
/**
* This class provides the ability to re-construct packets from {@link com.captainalm.lib.calmnet.packet.fragment}.
*
* @author Captain ALM
*/
public final class FragmentReceiver {
private final Queue<IPacket> outputQueue = new LinkedList<>();
private final Queue<Integer> finishedIDs = new LinkedList<>();
private final Queue<AllocationOutput> allocated = new LinkedList<>();
private final Queue<Integer> forceStopIDs = new LinkedList<>();
private final HashMap<Integer, FragmentInput> registry = new HashMap<>();
private final Object slock = new Object();
private final Object slockqueue = new Object();
private final Object slockfinish = new Object();
private int numberOfEmptySendsTillForced = 2;
private int IID = 0;
private PacketLoader packetLoader;
private IPacketFactory packetFactory;
private boolean verifyResponses = false;
/**
* Constructs a new FragmentReceiver with the specified {@link PacketLoader} and {@link IPacketFactory}.
*
* @param loader The packet loader to use.
* @param factory The packet factory to use.
* @throws NullPointerException loader or factory is null.
*/
public FragmentReceiver(PacketLoader loader, IPacketFactory factory) {
setPacketLoader(loader);
setPacketFactory(factory);
}
/**
* Receives a {@link IPacket} from the FragmentReceiver.
* This method blocks until a packet can be received.
*
* @return The received packet.
* @throws InterruptedException The Thread was Interrupted.
*/
public IPacket receivePacket() throws InterruptedException {
synchronized (slockqueue) {
while (outputQueue.size() < 1) slockqueue.wait();
return outputQueue.poll();
}
}
/**
* Sends the current {@link IPacket}s from the FragmentReceiver.
*
* @return The packets to send.
* @throws PacketException A Packet Exception has occurred.
*/
public IPacket[] sendPacket() throws PacketException {
synchronized (slock) {
ArrayList<IPacket> toret = new ArrayList<>();
while (allocated.size() > 0) toret.add(allocated.poll().getAllocation());
ArrayList<Integer> toRemove = new ArrayList<>();
for (int key : registry.keySet()) {
IPacket packet = registry.get(key).getSendPacket();
if (packet != null) {
toret.add(packet);
if (packet instanceof FragmentSendCompletePacket) {
toRemove.add(key);
synchronized (slockfinish) {
finishedIDs.add(key);
slockfinish.notify();
}
}
}
IPacket wasConsumed = registry.get(key).consume();
if (wasConsumed != null) {
synchronized (slockqueue) {
outputQueue.add(wasConsumed);
slockqueue.notify();
}
}
}
for (int i : toRemove) registry.remove(i);
while (forceStopIDs.size() > 0) toret.add(new FragmentSendStopPacket(forceStopIDs.poll()));
return toret.toArray(new IPacket[0]);
}
}
/**
* Receives a {@link IPacket} into the FragmentReceiver.
*
* @param packetIn The packet to receive.
* @throws PacketException A Packet Exception has occurred.
*/
public void receivePacket(IPacket packetIn) throws PacketException {
if (packetIn == null || !packetIn.isValid()) return;
if (packetIn instanceof FragmentPIDPacket) {
synchronized (slock) {
FragmentInput fragmentInput = registry.get(((FragmentPIDPacket) packetIn).getPacketID());
if (fragmentInput != null) fragmentInput.receivePacket(packetIn);
}
} else if (packetIn instanceof FragmentAllocatePacket && ((FragmentAllocatePacket)packetIn).getFragmentCount() > 0) {
synchronized (slock) {
if (!containsAllocationID(((FragmentAllocatePacket) packetIn).getAllocationID())) {
int currentID = getCurrentID();
UUID aid = ((FragmentAllocatePacket) packetIn).getAllocationID();
if (currentID >= 0) {
registry.put(currentID, new FragmentInput(currentID, ((FragmentAllocatePacket) packetIn).getFragmentCount(), aid));
allocated.add(new AllocationOutput(currentID, aid, true));
} else {
allocated.add(new AllocationOutput(0, aid, false));
}
}
}
}
}
private int getCurrentID() {
if (registry.containsKey(IID)) while (IID >= 0 && registry.containsKey(IID)) IID++;
if (IID < 0) IID = -1;
return IID;
}
private boolean containsAllocationID(UUID aid) {
for (int c : registry.keySet()) if (registry.get(c).allocationID.equals(aid)) return true;
return false;
}
/**
* Gets whether packets are waiting to be received.
*
* @return If packets are waiting to be received.
*/
public boolean arePacketsWaiting() {
synchronized (slockqueue) {
return outputQueue.size() > 0;
}
}
/**
* Clears the currently waiting packets.
*/
public void clearWaitingPackets() {
synchronized (slockqueue) {
outputQueue.clear();
}
}
/**
* Deletes a packet from the registry and requests the sender to stop sending.
*
* @param id The ID of the packet to remove.
*/
public void deletePacketFromRegistry(int id) {
synchronized (slock) {
forceStopIDs.add(id);
registry.remove(id);
}
}
/**
* Clears the registry (And requests the sender to stop sending).
* NOTE: Do NOT do this unless you are finished with the FragmentReceiver.
*/
public void clearRegistry() {
synchronized (slock) {
forceStopIDs.addAll(registry.keySet());
registry.clear();
}
}
/**
* Gets whether finished IDs are waiting for obtaining.
*
* @return If finished IDs are waiting for obtaining.
*/
public boolean areFinishedIDsWaiting() {
synchronized (slockfinish) {
return finishedIDs.size() > 0;
}
}
/**
* Gets the last finished packet ID.
* This method blocks until a packet finishes processing.
*
* @return The last finished packet ID.
* @throws InterruptedException The Thread was Interrupted.
*/
public int getLastIDFinished() throws InterruptedException {
synchronized (slockfinish) {
while (finishedIDs.size() < 1) slockfinish.wait();
Integer polled = finishedIDs.poll();
return (polled == null) ? -1 : polled;
}
}
/**
* Clears all the last finished packet IDs.
*/
public void clearLastIDFinished() {
synchronized (slockfinish) {
finishedIDs.clear();
}
}
/**
* Gets the {@link IPacketFactory} in use.
*
* @return The PacketFactory in use.
*/
public IPacketFactory getPacketFactory() {
return packetFactory;
}
/**
* Sets the {@link IPacketFactory} to use.
*
* @param factory The packet factory to use.
* @throws NullPointerException factory is null.
*/
public void setPacketFactory(IPacketFactory factory) {
if (factory == null) throw new NullPointerException("factory is null");
synchronized (slock) {
packetFactory = factory;
}
}
/**
* Gets the {@link PacketLoader} in use.
*
* @return The PacketLoader in use.
*/
public PacketLoader getPacketLoader() {
return packetLoader;
}
/**
* Sets the {@link PacketLoader} to use.
*
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public void setPacketLoader(PacketLoader loader) {
if (loader == null) throw new NullPointerException("loader is null");
synchronized (slock) {
packetLoader = loader;
}
}
/**
* Gets whether responses should be verified.
*
* @return Should responses be verified.
*/
public boolean shouldVerifyResponses() {
return verifyResponses;
}
/**
* Sets whether responses should be verified.
*
* @param state If responses should be verified.
*/
public void setResponseVerification(boolean state) {
synchronized (slock) {
verifyResponses = state;
}
}
/**
* Gets the number of {@link #sendPacket()} calls, that return null, to a registry entry are made before
* the {@link FragmentSendCompletePacket} or {@link FragmentRetrySendPacket} packets are sent.
* A {@link FragmentSendCompletePacket} is sent if completely received and a
* {@link FragmentRetrySendPacket} is sent if not completely received.
*
* @return The number of send packet calls before a completion or restart is forced.
*/
public int getNumberOfEmptySendsTillForcedCompleteOrResend() {
return numberOfEmptySendsTillForced;
}
/**
* Sets the number of {@link #sendPacket()} calls, that return null, to a registry entry are made before
* the {@link FragmentSendCompletePacket} or {@link FragmentRetrySendPacket} packets are sent.
* A {@link FragmentSendCompletePacket} is sent if completely received and a
* {@link FragmentRetrySendPacket} is sent if not completely received.
*
* @param numberOfEmptySends The number of empty sends to allow.
* @throws IllegalArgumentException numberOfEmptySends is less than 1.
*/
public void setNumberOfEmptySendsTillForcedCompleteOrResend(int numberOfEmptySends) {
if (numberOfEmptySends < 1) throw new IllegalArgumentException("numberOfEmptySends is less than 1");
numberOfEmptySendsTillForced = numberOfEmptySends;
}
/**
* This class provides the ability to store allocated responses to be sent back.
*
* @author Captain ALM
*/
private static final class AllocationOutput {
public final int packetID;
public final UUID allocationID;
public final boolean success;
public AllocationOutput(int packetID, UUID allocationID, boolean success) {
this.packetID = packetID;
this.allocationID = allocationID;
this.success = success;
}
public FragmentAllocationPacket getAllocation() {
return new FragmentAllocationPacket(packetID, allocationID, success);
}
}
/**
* This class provides the ability to store the received fragments and get the next packet to send on the protocol.
* The next packet for sending will change as signalling packets come through.
*
* @author Captain ALM
*/
private final class FragmentInput {
public final int packetID;
public final UUID allocationID;
private final ArrayList<Integer> idsToReceive = new ArrayList<>();
private final ArrayList<Integer> idsToAKN = new ArrayList<>();
private int msgPacketIndex = 0;
private boolean consumeDone = false;
private int sendsTillCompleteForced;
private boolean fsendActive = false;
private final FragmentMessagePacket[] messagePackets;
public FragmentInput(int id, int count, UUID aid) {
packetID = id;
messagePackets = new FragmentMessagePacket[count];
allocationID = aid;
sendsTillCompleteForced = numberOfEmptySendsTillForced + 1;
for (int i = 0; i < count; i++) idsToReceive.add(i);
}
public IPacket getSendPacket() {
if (msgPacketIndex < idsToAKN.size() && msgPacketIndex >= 0) {
int pindex = idsToAKN.get(msgPacketIndex++);
if (msgPacketIndex >= idsToAKN.size()) {
idsToAKN.clear();
msgPacketIndex = 0;
}
return new FragmentMessageResponsePacket(packetID, pindex, (verifyResponses) ? messagePackets[pindex].getFragmentMessage() : null);
}
if (fsendActive) {
if (sendsTillCompleteForced > 0) sendsTillCompleteForced--;
} else fsendActive = true;
if (sendsTillCompleteForced == 0) return (idsToReceive.size() < 1) ? new FragmentSendCompletePacket(packetID, true) : new FragmentRetrySendPacket(packetID, false);
return null;
}
public void receivePacket(IPacket packetIn) {
if ((packetIn instanceof FragmentSendCompletePacket && !((FragmentSendCompletePacket) packetIn).isAcknowledgement())) sendsTillCompleteForced = 0;
if ((packetIn instanceof FragmentRetrySendPacket && ((FragmentRetrySendPacket) packetIn).isAcknowledgement())) sendsTillCompleteForced = numberOfEmptySendsTillForced + 1;
if (packetIn instanceof FragmentMessagePacket) {
FragmentMessagePacket messagePacket = (FragmentMessagePacket) packetIn;
messagePackets[messagePacket.getFragmentID()] = messagePacket;
idsToReceive.remove(messagePacket.getFragmentID());
idsToAKN.add(messagePacket.getFragmentID());
}
}
public IPacket consume() throws PacketException {
if (consumeDone || idsToReceive.size() > 0 || messagePackets.length < 1) return null;
ByteArrayOutputStream packetStream = new ByteArrayOutputStream(messagePackets[0].getFragmentMessage().length);
for (FragmentMessagePacket messagePacket : messagePackets) {
try {
packetStream.write(messagePacket.getFragmentMessage());
} catch (IOException e) {
throw new PacketException(e);
}
}
consumeDone = true;
return packetLoader.readPacketNoDigest(packetStream.toByteArray(), packetFactory, null);
}
}
}

View File

@ -0,0 +1,320 @@
package com.captainalm.lib.calmnet.packet;
import com.captainalm.lib.calmnet.packet.fragment.*;
import java.util.*;
/**
* This class provides the ability to create packets for {@link com.captainalm.lib.calmnet.packet.fragment}.
*
* @author Captain ALM
*/
public final class FragmentSender {
private final Queue<IPacket> inputQueue = new LinkedList<>();
private final Queue<Integer> finishedIDs = new LinkedList<>();
private final HashMap<UUID, byte[]> allocationInputs = new HashMap<>();
private final HashMap<Integer, FragmentOutput> registry = new HashMap<>();
private final Object slock = new Object();
private final Object slockfinish = new Object();
private int splitSize = 496;
private PacketLoader packetLoader;
private boolean verifyResponses = false;
/**
* Constructs a new FragmentSender with the specified {@link PacketLoader}.
*
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public FragmentSender(PacketLoader loader) {
setPacketLoader(loader);
}
/**
* Constructs a new FragmentSender with the specified {@link PacketLoader}
* and packet split size in bytes.
*
* @param loader The packet loader to use.
* @param newSize The new split size.
* @throws NullPointerException loader is null.
* @throws IllegalArgumentException newSize is less than 1.
*/
public FragmentSender(PacketLoader loader, int newSize) {
this(loader);
setSplitSize(newSize);
}
/**
* Sends a {@link IPacket} using this FragmentSender.
*
* @param packetIn The packet to fragment and send.
*/
public void sendPacket(IPacket packetIn) {
if (packetIn == null) throw new NullPointerException("packetIn is null");
synchronized (slock) {
inputQueue.add(packetIn);
}
}
/**
* Sends the current {@link IPacket}s from the FragmentSender.
*
* @return The packets to send.
* @throws PacketException A Packet Exception has occurred.
*/
public IPacket[] sendPacket() throws PacketException {
synchronized (slock) {
ArrayList<IPacket> toret = new ArrayList<>();
while (inputQueue.size() > 0) allocationInputs.put(UUID.randomUUID(), packetLoader.writePacketNoDigest(inputQueue.poll(), true));
for (UUID c : allocationInputs.keySet()) {
toret.add(new FragmentAllocatePacket(getNumberOfFragments(allocationInputs.get(c)), c));
}
for (int key : registry.keySet()) {
IPacket packet = registry.get(key).getSendPacket();
if (packet != null) toret.add(packet);
}
return toret.toArray(new IPacket[0]);
}
}
/**
* Receives a {@link IPacket} into the FragmentSender.
*
* @param packetIn The packet to receive.
* @throws PacketException A Packet Exception has occurred.
*/
public void receivePacket(IPacket packetIn) throws PacketException {
if (packetIn == null || !packetIn.isValid()) return;
if (packetIn instanceof FragmentPIDPacket) {
int currentID = ((FragmentPIDPacket) packetIn).getPacketID();
synchronized (slock) {
if (packetIn instanceof FragmentAllocationPacket && allocationInputs.containsKey(((FragmentAllocationPacket) packetIn).getAllocationID()) && ((FragmentAllocationPacket) packetIn).allocationSuccessful()) {
registry.put(currentID, new FragmentOutput(currentID, allocationInputs.get(((FragmentAllocationPacket) packetIn).getAllocationID())));
allocationInputs.remove(((FragmentAllocationPacket) packetIn).getAllocationID());
} else {
FragmentOutput fragmentOutput = registry.get(currentID);
if (fragmentOutput != null && fragmentOutput.shouldBeRemovedReceivePacket(packetIn)) {
registry.remove(currentID);
synchronized (slockfinish) {
finishedIDs.add(currentID);
slockfinish.notify();
}
}
}
}
}
}
/**
* Gets whether packets are waiting for allocation.
*
* @return If packets are waiting for allocation.
*/
public boolean arePacketsWaiting() {
synchronized (slock) {
return inputQueue.size() > 0;
}
}
/**
* Clears the currently waiting packets.
*/
public void clearWaitingPackets() {
synchronized (slock) {
inputQueue.clear();
allocationInputs.clear();
}
}
/**
* Deletes a packet from the registry.
*
* @param id The ID of the packet to remove.
*/
public void deletePacketFromRegistry(int id) {
synchronized (slock) {
registry.remove(id);
}
}
/**
* Clears the registry.
* NOTE: Do NOT do this unless you are finished with the FragmentSender.
*/
public void clearRegistry() {
synchronized (slock) {
registry.clear();
}
}
/**
* Gets whether finished IDs are waiting for obtaining.
*
* @return If finished IDs are waiting for obtaining.
*/
public boolean areFinishedIDsWaiting() {
synchronized (slockfinish) {
return finishedIDs.size() > 0;
}
}
/**
* Gets the last finished packet ID.
* This method blocks until a packet finishes processing.
*
* @return The last finished packet ID.
* @throws InterruptedException The Thread was Interrupted.
*/
public int getLastIDFinished() throws InterruptedException {
synchronized (slockfinish) {
while (finishedIDs.size() < 1) slockfinish.wait();
Integer polled = finishedIDs.poll();
return (polled == null) ? -1 : polled;
}
}
/**
* Clears all the last finished packet IDs.
*/
public void clearLastIDFinished() {
synchronized (slockfinish) {
finishedIDs.clear();
}
}
private int getNumberOfFragments(byte[] toSplit) {
return (int) Math.ceil((double) toSplit.length / (double) splitSize);
}
/**
* Gets the current packet split size in bytes.
*
* @return The current packet split size.
*/
public int getSplitSize() {
return splitSize;
}
/**
* Sets the packet split size in bytes.
*
* @param newSize The new packet split size.
* @throws IllegalArgumentException newSize is less than 1.
*/
public void setSplitSize(int newSize) {
if (newSize < 1) throw new IllegalArgumentException("newSize is less than 1");
synchronized (slock) {
splitSize = newSize;
}
}
/**
* Gets the {@link PacketLoader} in use.
*
* @return The PacketLoader in use.
*/
public PacketLoader getPacketLoader() {
return packetLoader;
}
/**
* Sets the {@link PacketLoader} to use.
*
* @param loader The packet loader to use.
* @throws NullPointerException loader is null.
*/
public void setPacketLoader(PacketLoader loader) {
if (loader == null) throw new NullPointerException("loader is null");
synchronized (slock) {
packetLoader = loader;
}
}
/**
* Gets whether responses should be verified.
*
* @return Should responses be verified.
*/
public boolean shouldVerifyResponses() {
return verifyResponses;
}
/**
* Sets whether responses should be verified.
*
* @param state If responses should be verified.
*/
public void setResponseVerification(boolean state) {
synchronized (slock) {
verifyResponses = state;
}
}
/**
* This class provides the ability to get the next fragment packet information for sending.
* The next packet for sending will change as signalling packets come through.
*
* @author Captain ALM
*/
private final class FragmentOutput {
public final int packetID;
private final FragmentMessagePacket[] messagePackets;
private final ArrayList<Integer> msgToResend = new ArrayList<>();
private final ArrayList<Integer> msgToResendCurrent = new ArrayList<>();
private int msgPacketIndex = 0;
public boolean isResending = false;
public FragmentOutput(int id, byte[] toSplit) {
packetID = id;
messagePackets = new FragmentMessagePacket[getNumberOfFragments(toSplit)];
int index = 0;
for (int i = 0; i < messagePackets.length; i++) {
byte[] toInput = new byte[(index + splitSize > toSplit.length) ? toSplit.length - index : splitSize];
System.arraycopy(toSplit, index, toInput, 0, toInput.length);
messagePackets[i] = new FragmentMessagePacket(packetID, i, toInput);
msgToResend.add(i);
index += toInput.length;
}
}
public IPacket getSendPacket() {
if (msgPacketIndex < 0) {
msgPacketIndex = 0;
return new FragmentRetrySendPacket(packetID, true);
}
if (isResending) {
if (msgPacketIndex < msgToResendCurrent.size()) return messagePackets[msgToResendCurrent.get(msgPacketIndex++)];
} else {
if (msgPacketIndex < messagePackets.length) return messagePackets[msgPacketIndex++];
}
return new FragmentSendCompletePacket(packetID, false);
}
public boolean shouldBeRemovedReceivePacket(IPacket packetIn) {
if (packetIn instanceof FragmentSendStopPacket || (packetIn instanceof FragmentSendCompletePacket && ((FragmentSendCompletePacket) packetIn).isAcknowledgement())) {
msgPacketIndex = messagePackets.length;
return true;
}
if (packetIn instanceof FragmentMessageResponsePacket) {
FragmentMessageResponsePacket responsePacket = (FragmentMessageResponsePacket)packetIn;
if (!verifyResponses || compareData(responsePacket.getFragmentMessage(), messagePackets[responsePacket.getFragmentID()].getFragmentMessage()))
msgToResend.remove(responsePacket.getFragmentID());
}
if (packetIn instanceof FragmentRetrySendPacket && !((FragmentRetrySendPacket) packetIn).isAcknowledgement()) {
msgPacketIndex = -1;
isResending = true;
msgToResendCurrent.clear();
msgToResendCurrent.addAll(msgToResend);
}
return false;
}
private boolean compareData(byte[] data1, byte[] data2) {
if ((data1 == null && data2 != null) || (data1 != null && data2 == null)) return false;
if (data1 == data2) return true;
if (data1.length != data2.length) return false;
for (int i = 0; i < data1.length; i++) if (data1[i] != data2[i]) return false;
return true;
}
}
}

View File

@ -0,0 +1,15 @@
package com.captainalm.lib.calmnet.packet;
/**
* This interface allows obtaining if the class instance is an Acknowledgement.
*
* @author Captain ALM
*/
public interface IAcknowledgement {
/**
* Gets if the class instance is an Acknowledgement.
*
* @return If the class instance is an Acknowledgement.
*/
boolean isAcknowledgement();
}

View File

@ -0,0 +1,39 @@
package com.captainalm.lib.calmnet.packet;
/**
* This interface provides the packet methods.
*
* @author Captain ALM
*/
public interface IPacket {
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
boolean isValid();
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
PacketProtocolInformation getProtocol();
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
byte[] savePayload() throws PacketException;
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
void loadPayload(byte[] packetData) throws PacketException;
}

View File

@ -0,0 +1,31 @@
package com.captainalm.lib.calmnet.packet;
/**
* This interface provides the ability to construct {@link IPacket}s given their {@link PacketProtocolInformation}.
*
* @author Captain ALM
*/
public interface IPacketFactory {
/**
* Constructs a {@link IPacket} of the protocol specified by the passed {@link PacketProtocolInformation} instance.
*
* @param information The protocol information to use.
* @throws NullPointerException The information is null.
* @return The constructed packet or null.
*/
IPacket getPacket(PacketProtocolInformation information);
/**
* Gets if {@link #getPacket(PacketProtocolInformation)} prefers returning {@link IStreamedPacket}s if possible.
*
* @return If streamed packets are preferred for construction.
*/
boolean areStreamPacketsPreferred();
/**
* Sets if {@link #getPacket(PacketProtocolInformation)} prefers returning {@link IStreamedPacket}s if possible.
*
* @param preferred If streamed packets are preferred for construction.
*/
void setStreamPacketsPreferred(boolean preferred);
}

View File

@ -0,0 +1,42 @@
package com.captainalm.lib.calmnet.packet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* This interface provides the streaming packet methods.
*
* @author Captain ALM
*/
public interface IStreamedPacket extends IPacket {
/**
* Reads payload data to an {@link OutputStream}.
*
* @param outputStream The output stream to read data to.
* @throws NullPointerException outputStream is null.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
void readData(OutputStream outputStream) throws IOException, PacketException;
/**
* Writes payload data from an {@link InputStream}.
*
* @param inputStream The input stream to write data from.
* @param size The size of the input payload in bytes.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException size is less than 0.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
void writeData(InputStream inputStream, int size) throws IOException, PacketException;
/**
* Gets the size of the output data.
*
* @throws PacketException An Exception has occurred.
* @return The size of the output data in bytes.
*/
int getSize() throws PacketException;
}

View File

@ -0,0 +1,60 @@
package com.captainalm.lib.calmnet.packet;
import java.security.PrivilegedActionException;
/**
* This class provides the packet exception wrapper class.
* See {@link PacketException#getCause()} to find out the underlying exception.
*
* @author Captain ALM
*/
public class PacketException extends Exception {
/**
* Constructs a new exception with the specified detail message. The
* cause is not initialized, and may subsequently be initialized by
* a call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public PacketException(String message) {
super(message);
}
/**
* Constructs a new exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public PacketException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of <tt>(cause==null ? null : cause.toString())</tt> (which
* typically contains the class and detail message of <tt>cause</tt>).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public PacketException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,422 @@
package com.captainalm.lib.calmnet.packet;
import com.captainalm.lib.stdcrypt.digest.DigestComparer;
import com.captainalm.lib.stdcrypt.digest.DigestProvider;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import static com.captainalm.lib.calmnet.packet.PacketProtocolInformation.getProtocolInformation;
import static com.captainalm.lib.calmnet.packet.PacketProtocolInformation.savePacketProtocolInformation;
/**
* This class provides the ability to load and save {@link IPacket}
* to {@link java.io.InputStream} and {@link java.io.OutputStream}.
* Packets can have contents checking support using {@link DigestProvider}.
*
* @author Captain ALM
*/
public class PacketLoader {
protected boolean allowInvalidPackets;
/**
* Constructs a new Packet loader instance.
* If using a digest provider, use {@link #PacketLoader(DigestProvider)}
*/
public PacketLoader() {
this(null);
}
/**
* Constructs a new Packet loader instance with the specified {@link DigestProvider}.
* If using a digest provider, make sure all endpoints use the same algorithm;
* if null, no trailer is created or expected;
* this is ignored if saving / loading packets from byte arrays.
*
* @param provider The digest provider or null.
*/
public PacketLoader(DigestProvider provider) {
hashProvider = provider;
}
protected DigestProvider hashProvider;
/**
* This field provides the {@link DigestProvider} to use for the payload of the packets on the trailer.
*
* @return The digest provider in use or null.
*/
public DigestProvider getHashProvider() {
return hashProvider;
}
/**
* Gets whether invalid packets are allowed to be read and written.
*
* @return If invalid packets can be processed.
*/
public boolean areInvalidPacketsAllowed() {
return allowInvalidPackets;
}
/**
* This sets whether invalid packets are allowed to be read and written.
*
* @param allowInvalidPackets If invalid packets can be processed.
*/
public void setAllowInvalidPackets(boolean allowInvalidPackets) {
this.allowInvalidPackets = allowInvalidPackets;
}
protected boolean isPacketInvalid(IPacket packetIn) {
return (packetIn == null || !packetIn.isValid()) && !allowInvalidPackets;
}
/**
* Reads a {@link IPacket} from a byte array (No digest support).
* If the information parameter is null, this is obtained as part of the reading.
* NOTE: The {@link #getHashProvider()} for digests is NOT supported and no digest is expected for these packets.
*
* @param arrayIn The byte array for reading.
* @param factory The {@link IPacketFactory} to use to generate packets.
* @param information The protocol information or null.
* @return The loaded packet or null.
* @throws NullPointerException The arrayIn or the factory is null.
* @throws PacketException An Exception has occurred.
*/
public IPacket readPacketNoDigest(byte[] arrayIn, IPacketFactory factory, PacketProtocolInformation information) throws PacketException {
if (arrayIn == null) throw new NullPointerException("arrayIn is null");
if (factory == null) throw new NullPointerException("factory is null");
if (information == null) {
if (arrayIn.length < 2) throw new PacketException("arrayIn does not have an information header.");
information = new PacketProtocolInformation(arrayIn[0], arrayIn[1]);
}
IPacket toret = factory.getPacket(information);
if (toret != null) {
if (arrayIn.length < 6) throw new PacketException("arrayIn does not have a length header.");
int length = (arrayIn[2] & 0xff) * 16777216 + (arrayIn[3] & 0xff) * 65536 + (arrayIn[4] & 0xff) * 256 + (arrayIn[5] & 0xff);
byte[] loadArray = new byte[length];
System.arraycopy(arrayIn, 6, loadArray, 0, arrayIn.length - 6);
toret.loadPayload(loadArray);
if (isPacketInvalid(toret)) toret = null;
}
return toret;
}
/**
* Reads a {@link IPacket} from an input stream.
* If the information parameter is null, this is obtained as part of the reading.
*
* @param inputStream The input stream for reading.
* @param factory The {@link IPacketFactory} to use to generate packets.
* @param information The protocol information or null.
* @return The loaded packet or null.
* @throws NullPointerException The inputStream or the factory is null.
* @throws IOException A stream exception occurs.
* @throws PacketException An Exception has occurred.
*/
public IPacket readPacket(InputStream inputStream, IPacketFactory factory, PacketProtocolInformation information) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (factory == null) throw new NullPointerException("factory is null");
if (information == null) information = getProtocolInformation(inputStream);
IPacket toret = factory.getPacket(information);
if (toret != null) {
InputStream lIS = (hashProvider == null) ? inputStream : hashProvider.getDigestInputStream(inputStream);
byte[] loadArray = readArrayFromInputStream(lIS, readInteger(inputStream));
if (hashProvider == null || DigestComparer.compareDigests(inputStream, ((DigestInputStream) lIS).getMessageDigest().digest())) toret.loadPayload(loadArray);
if (isPacketInvalid(toret)) toret = null;
}
return toret;
}
/**
* Reads a {@link IPacket} from an input stream (No digest support).
* If the information parameter is null, this is obtained as part of the reading.
* NOTE: The {@link #getHashProvider()} for digests is NOT supported and no digest is expected for these packets.
*
* @param inputStream The input stream for reading.
* @param factory The {@link IPacketFactory} to use to generate packets.
* @param information The protocol information or null.
* @return The loaded packet or null.
* @throws NullPointerException The inputStream or the factory is null.
* @throws IOException A stream exception occurs.
* @throws PacketException An Exception has occurred.
*/
public IPacket readPacketNoDigest(InputStream inputStream, IPacketFactory factory, PacketProtocolInformation information) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (factory == null) throw new NullPointerException("factory is null");
if (information == null) information = getProtocolInformation(inputStream);
IPacket toret = factory.getPacket(information);
if (toret != null) {
byte[] loadArray = readArrayFromInputStream(inputStream, readInteger(inputStream));
toret.loadPayload(loadArray);
if (isPacketInvalid(toret)) toret = null;
}
return toret;
}
/**
* Reads a {@link IStreamedPacket} from an input stream.
* If the information parameter is null, this is obtained as part of the reading.
* NOTE: The packet may be an {@link IPacket} if no stream packet is available for that protocol.
*
* @param inputStream The input stream for reading.
* @param factory The {@link IPacketFactory} to use to generate packets.
* @param information The protocol information or null.
* @return The loaded packet or null.
* @throws NullPointerException The inputStream or the factory is null.
* @throws IOException A stream exception occurs.
* @throws PacketException An Exception has occurred.
*/
public IPacket readStreamedPacket(InputStream inputStream, IPacketFactory factory, PacketProtocolInformation information) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (factory == null) throw new NullPointerException("factory is null");
if (information == null) information = getProtocolInformation(inputStream);
IPacket toret = factory.getPacket(information);
if (toret instanceof IStreamedPacket) {
int length = readInteger(inputStream);
InputStream lIS = (hashProvider == null) ? inputStream : hashProvider.getDigestInputStream(inputStream);
((IStreamedPacket) toret).writeData(lIS, length);
if (hashProvider != null && !DigestComparer.compareDigests(inputStream, ((DigestInputStream) lIS).getMessageDigest().digest())) toret = null;
if (isPacketInvalid(toret)) toret = null;
} else if (toret != null) {
return readPacket(inputStream, factory, information);
}
return toret;
}
/**
* Reads a {@link IStreamedPacket} from an input stream (No digest support).
* If the information parameter is null, this is obtained as part of the reading.
* NOTE: The packet may be an {@link IPacket} if no stream packet is available for that protocol.
* NOTE: The {@link #getHashProvider()} for digests is NOT supported and no digest is expected for these packets.
*
* @param inputStream The input stream for reading.
* @param factory The {@link IPacketFactory} to use to generate packets.
* @param information The protocol information or null.
* @return The loaded packet or null.
* @throws NullPointerException The inputStream or the factory is null.
* @throws IOException A stream exception occurs.
* @throws PacketException An Exception has occurred.
*/
public IPacket readStreamedPacketNoDigest(InputStream inputStream, IPacketFactory factory, PacketProtocolInformation information) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (factory == null) throw new NullPointerException("factory is null");
if (information == null) information = getProtocolInformation(inputStream);
IPacket toret = factory.getPacket(information);
if (toret instanceof IStreamedPacket) {
int length = readInteger(inputStream);
((IStreamedPacket) toret).writeData(inputStream, length);
if (isPacketInvalid(toret)) toret = null;
} else if (toret != null) {
return readPacketNoDigest(inputStream, factory, information);
}
return toret;
}
/**
* Returns a {@link IPacket} to a byte array (No digest support).
* NOTE: The {@link #getHashProvider()} for digests is NOT supported and no digest is expected for these packets.
*
* @param packet The packet to save.
* @param writeInformation Write the {@link PacketProtocolInformation} to the beginning of the array.
* @throws NullPointerException A parameter is null.
* @throws PacketException An Exception has occurred.
*/
public byte[] writePacketNoDigest(IPacket packet, boolean writeInformation) throws PacketException {
if (packet == null) throw new NullPointerException("packet is null");
if (isPacketInvalid(packet)) throw new PacketException("packet is invalid");
byte[] header = (writeInformation) ? new byte[6] : new byte[4];
if (writeInformation) {
header[0] = (byte) packet.getProtocol().getMajor();
header[1] = (byte) packet.getProtocol().getMinor();
}
byte[] saveArray = packet.savePayload();
int length = saveArray.length;
header[((writeInformation) ? 2 : 0)] = (byte) (length / 16777216);
length %= 16777216;
header[((writeInformation) ? 3 : 1)] = (byte) (length / 65536);
length %= 65536;
header[((writeInformation) ? 4 : 2)] = (byte) (length / 256);
length %= 256;
header[((writeInformation) ? 5 : 3)] = (byte) (length);
byte[] toret = new byte[header.length + saveArray.length];
System.arraycopy(header, 0, toret, 0, header.length);
System.arraycopy(saveArray, 0, toret, header.length, saveArray.length);
return toret;
}
/**
* Writes a {@link IPacket} to an output stream.
*
* @param outputStream The output stream for writing.
* @param packet The packet to save.
* @param writeInformation Write the {@link PacketProtocolInformation} to the stream.
* @throws NullPointerException A parameter is null.
* @throws IOException A stream exception occurs.
* @throws PacketException An Exception has occurred.
*/
public void writePacket(OutputStream outputStream, IPacket packet, boolean writeInformation) throws IOException, PacketException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
if (packet == null) throw new NullPointerException("packet is null");
if (isPacketInvalid(packet)) throw new PacketException("packet is invalid");
if (writeInformation) savePacketProtocolInformation(outputStream, packet.getProtocol());
if (packet instanceof IStreamedPacket) {
writeInteger(outputStream, ((IStreamedPacket) packet).getSize());
OutputStream lOS = (hashProvider == null) ? outputStream : hashProvider.getDigestOutputStream(outputStream);
((IStreamedPacket) packet).readData(lOS);
if (hashProvider != null) outputStream.write(((DigestOutputStream) lOS).getMessageDigest().digest());
} else {
byte[] saveArray = packet.savePayload();
writeInteger(outputStream, saveArray.length);
outputStream.write(saveArray);
if (hashProvider != null) outputStream.write(hashProvider.getDigestOf(saveArray));
}
outputStream.flush();
}
/**
* Reads an Integer from an {@link InputStream}.
*
* @param inputStream The input stream to use.
* @return The integer that was stored.
* @throws NullPointerException inputStream is null.
* @throws IOException An I/O error has occurred.
*/
public static int readInteger(InputStream inputStream) throws IOException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
int length = (readByteFromInputStream(inputStream) & 0xff) * 16777216;
length += (readByteFromInputStream(inputStream) & 0xff) * 65536;
length += (readByteFromInputStream(inputStream) & 0xff) * 256;
length += (readByteFromInputStream(inputStream) & 0xff);
return length;
}
/**
* Writes an Integer to the {@link OutputStream} using 4 bytes.
*
* @param outputStream The output stream to use.
* @param i The integer to store.
* @throws NullPointerException outputStream is null.
* @throws IllegalArgumentException i is less than 0.
* @throws IOException An I/O error has occurred.
*/
public static void writeInteger(OutputStream outputStream, int i) throws IOException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
if (i < 0) throw new IllegalArgumentException("i is less than 0");
outputStream.write(i / 16777216);
i %= 16777216;
outputStream.write(i / 65536);
i %= 65536;
outputStream.write(i / 256);
i %= 256;
outputStream.write(i);
}
/**
* Reads a byte from an {@link InputStream}.
*
* @param inputStream The input stream to read from.
* @return The byte read.
* @throws NullPointerException inputStream is null.
* @throws IOException An I/O error has occurred or end of stream has been reached.
*/
public static byte readByteFromInputStream(InputStream inputStream) throws IOException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
int toret;
if ((toret = inputStream.read()) == -1) throw new IOException("inputStream end of stream");
return (byte) toret;
}
/**
* Reads in a byte array of a specified length from an {@link InputStream}.
*
* @param inputStream The input stream to read from.
* @param length The length of the stream.
* @return The array of read bytes.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException length is less than 0.
* @throws IOException An I/O error occurs or end of stream has been reached.
*/
public static byte[] readArrayFromInputStream(InputStream inputStream, int length) throws IOException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (length < 0) throw new IllegalArgumentException("length is less than 0");
byte[] toret = new byte[length];
int offset = 0;
int slen;
while (offset < length) if ((slen = inputStream.read(toret, offset, length - offset)) != -1) offset += slen; else throw new IOException("inputStream end of stream");
return toret;
}
/**
* Saves an Integer into a byte array.
*
* @param i The integer to save.
* @return The byte array.
* @throws IllegalArgumentException i is less than 0.
*/
public static byte[] getByteArrayFromInteger(int i) {
if (i < 0) throw new IllegalArgumentException("i is less than 0");
byte[] toret = new byte[4];
toret[0] = (byte) (i / 16777216);
i %= 16777216;
toret[1] = (byte) (i / 65536);
i %= 65536;
toret[2] = (byte) (i / 256);
i %= 256;
toret[3] = (byte) (i);
return toret;
}
/**
* Loads an Integer from a byte array.
*
* @param bytes The byte array.
* @return The integer.
* @throws NullPointerException bytes is null.
* @throws IllegalArgumentException bytes length is not 4.
*/
public static int getIntegerFromByteArray(byte[] bytes) {
if (bytes == null) throw new NullPointerException("bytes is null");
if (bytes.length != 4) throw new IllegalArgumentException("bytes length is not 4");
return (bytes[0] & 0xff) * 16777216 + (bytes[1] & 0xff) * 65536 + (bytes[2] & 0xff) * 256 + (bytes[3] & 0xff);
}
/**
* Gets the total size of a written packet in bytes.
*
* @param packet The packet to check.
* @param includeInformation If the 2 byte information header is included.
* @return The size of the packet in bytes.
* @throws NullPointerException packet is null.
* @throws PacketException A Packet Exception has occurred.
*/
public int getPacketSize(IPacket packet, boolean includeInformation) throws PacketException {
if (packet == null) throw new NullPointerException("packet is null");
return ((includeInformation) ? 2 : 0) + ((packet instanceof IStreamedPacket) ? ((IStreamedPacket) packet).getSize() : packet.savePayload().length)
+ ((hashProvider == null) ? 0 : hashProvider.getLength());
}
}

View File

@ -0,0 +1,116 @@
package com.captainalm.lib.calmnet.packet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
/**
* This class provides the packet protocol information for {@link IPacket}.
*
* @author Captain ALM
*/
public class PacketProtocolInformation {
/**
* Constructs a new instance of PacketProtocolInformation.
* This is set to 0 Major, 0 Minor.
*/
public PacketProtocolInformation(){
major = 0;
minor = 0;
}
/**
* Constructs a new instance of PacketProtocolInformation.
*
* @param major The major version.
* @param minor The minor version.
*/
public PacketProtocolInformation(byte major, byte minor) {
this.major = major;
this.minor = minor;
}
/**
* The major protocol of the packet.
*/
protected final byte major;
/**
* The minor protocol of the packet.
*/
protected final byte minor;
/**
* Gets the major protocol for the packet.
*
* @return The major protocol.
*/
public int getMajor() {
return major & 0xFF;
}
/**
* Gets the minor protocol for the packet.
*
* @return The minor protocol.
*/
public int getMinor() {
return minor & 0xFF;
}
/**
* Gets the {@link PacketProtocolInformation} of the packet.
*
* @param inputStream The input stream for reading.
* @return The protocol information.
* @throws NullPointerException The inputStream is null.
* @throws IOException A stream exception occurs.
*/
public static PacketProtocolInformation getProtocolInformation(InputStream inputStream) throws IOException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
byte major = PacketLoader.readByteFromInputStream(inputStream);
byte minor = PacketLoader.readByteFromInputStream(inputStream);
return new PacketProtocolInformation(major, minor);
}
/**
* Saves the {@link PacketProtocolInformation} of the packet.
*
* @param outputStream The output stream for writing.
* @param information The protocol information.
* @throws NullPointerException A parameter is null.
* @throws IOException A stream exception occurs.
*/
public static void savePacketProtocolInformation(OutputStream outputStream, PacketProtocolInformation information) throws IOException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
if (information == null) throw new NullPointerException("information is null");
outputStream.write(information.getMajor());
outputStream.write(information.getMinor());
}
/**
* Gets whether this object equals the passed object.
*
* @param o The object to check.
* @return If the objects are equivalent.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PacketProtocolInformation)) return false;
PacketProtocolInformation that = (PacketProtocolInformation) o;
return major == that.major && minor == that.minor;
}
/**
* Gets the hash code of the object.
*
* @return The hash code of the object.
*/
@Override
public int hashCode() {
return Objects.hash(major, minor);
}
}

View File

@ -0,0 +1,243 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.*;
import com.captainalm.lib.calmnet.stream.LengthClampedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;
/**
* This class provides a base64 encrypted packet that can hold an {@link IPacket}.
* <p>
* Major ID: 255
* Minor ID: 251
* </p>
*
* @author Captain ALM
*/
public class Base64Packet implements IStreamedPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 251);
protected final Object slock = new Object();
protected PacketLoader loader;
protected IPacketFactory factory;
protected IPacket held;
protected byte[] encryptedCache;
/**
* Constructs a new Base64Packet with the specified {@link IPacketFactory} and {@link PacketLoader}.
*
* @param factory The packet factory to use.
* @param loader The Packet Loader to use.
* @throws NullPointerException factory or loader is null.
*/
public Base64Packet(IPacketFactory factory, PacketLoader loader) {
this(factory, loader, null);
}
/**
* Constructs a new Base64Packet with the specified {@link IPacketFactory}, {@link PacketLoader} and {@link IPacket}.
*
* @param factory The packet factory to use.
* @param loader The Packet Loader to use.
* @param packet The packet to store or null.
* @throws NullPointerException factory or loader is null.
*/
public Base64Packet(IPacketFactory factory, PacketLoader loader, IPacket packet) {
if (factory == null) throw new NullPointerException("factory is null");
if (loader == null) throw new NullPointerException("loader is null");
this.factory = factory;
held = packet;
this.loader = loader;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (held != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
protected void processEncryptedCache() throws PacketException {
if (encryptedCache == null) {
if (held == null) throw new PacketException("no data");
encryptedCache = Base64.getEncoder().encode(loader.writePacketNoDigest(held, true));
}
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
synchronized (slock) {
processEncryptedCache();
return encryptedCache;
}
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
synchronized (slock) {
encryptedCache = packetData;
try {
byte[] payload = Base64.getDecoder().decode(encryptedCache);
held = loader.readPacketNoDigest(payload, factory, null);
} catch (IllegalArgumentException e) {
encryptedCache = null;
throw new PacketException(e);
}
}
}
/**
* Reads payload data to an {@link OutputStream}.
*
* @param outputStream The output stream to read data to.
* @throws NullPointerException outputStream is null.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void readData(OutputStream outputStream) throws IOException, PacketException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
synchronized (slock) {
processEncryptedCache();
outputStream.write(encryptedCache);
}
}
/**
* Writes payload data from an {@link InputStream}.
*
* @param inputStream The input stream to write data from.
* @param size The size of the input payload in bytes.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException size is less than 0.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void writeData(InputStream inputStream, int size) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (size < 0) throw new IllegalArgumentException("size is less than 0");
synchronized (slock) {
encryptedCache = null;
held = loader.readStreamedPacketNoDigest(Base64.getDecoder().wrap(new LengthClampedInputStream(inputStream, size)), factory, null);
}
}
/**
* Gets the size of the output data.
*
* @return The size of the output data in bytes.
* @throws PacketException An Exception has occurred.
*/
@Override
public int getSize() throws PacketException {
synchronized (slock) {
processEncryptedCache();
return encryptedCache.length;
}
}
/**
* Gets the {@link PacketLoader} in use.
*
* @return The Packet Loader in use.
*/
public PacketLoader getPacketLoader() {
return loader;
}
/**
* Sets the {@link PacketLoader} to use.
*
* @param loader The Packet Loader to use.
* @throws NullPointerException loader is null.
*/
public void setPacketLoader(PacketLoader loader) {
if (loader == null) throw new NullPointerException("loader is null");
synchronized (slock) {
this.loader = loader;
}
}
/**
* Gets the {@link IPacketFactory} in use.
*
* @return The Packet Factory in use.
*/
public IPacketFactory getFactory() {
return factory;
}
/**
* Sets the {@link IPacketFactory} to use.
*
* @param factory The Packet Factory to use.
* @throws NullPointerException factory is null.
*/
public void setFactory(IPacketFactory factory) {
if (factory == null) throw new NullPointerException("factory is null");
synchronized (slock) {
this.factory = factory;
}
}
/**
* Gets the held packet or null.
*
* @return The packet or null.
*/
public IPacket getHeldPacket() {
return held;
}
/**
* Sets the held packet.
*
* @param packet The new packet or null.
*/
public void setHeldPacket(IPacket packet) {
synchronized (slock) {
encryptedCache = null;
held = packet;
}
}
}

View File

@ -0,0 +1,458 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.*;
import com.captainalm.lib.calmnet.stream.LengthClampedInputStream;
import com.captainalm.lib.stdcrypt.encryption.CipherException;
import com.captainalm.lib.stdcrypt.encryption.ICipherFactory;
import javax.crypto.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import static com.captainalm.lib.calmnet.packet.PacketLoader.readByteFromInputStream;
/**
* This class provides an encrypted packet that can hold an {@link IPacket}.
* <p>
* Major ID: 255
* Minor ID: 252
* </p>
*
* @author Captain ALM
*/
public class EncryptedPacket implements IStreamedPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 252);
protected final Object slock = new Object();
protected PacketLoader loader;
protected IPacketFactory factory;
protected IPacket held;
protected byte[] encryptedCache;
protected int trailingArrayLengthCache;
protected Cipher cipher;
protected ICipherFactory cipherFactory;
protected String trailingPassword;
/**
* Constructs a new EncryptedPacket with the specified {@link IPacketFactory}, {@link PacketLoader} and {@link ICipherFactory}.
*
* @param factory The packet factory to use.
* @param loader The Packet Loader to use.
* @param cipherFactory The cipher factory to use.
* @throws NullPointerException factory, loader or cipherFactory is null.
*/
public EncryptedPacket(IPacketFactory factory, PacketLoader loader, ICipherFactory cipherFactory) {
this(factory, loader, cipherFactory, null);
}
/**
* Constructs a new EncryptedPacket with the specified {@link IPacketFactory}, {@link PacketLoader}, {@link ICipherFactory} and {@link IPacket}.
*
* @param factory The packet factory to use.
* @param loader The Packet Loader to use.
* @param cipherFactory The cipher factory to use.
* @throws NullPointerException factory, loader or cipherFactory is null.
*/
public EncryptedPacket(IPacketFactory factory, PacketLoader loader, ICipherFactory cipherFactory, IPacket packet) {
if (factory == null) throw new NullPointerException("factory is null");
if (loader == null) throw new NullPointerException("loader is null");
if (cipherFactory == null) throw new NullPointerException("cipherFactory is null");
this.factory = factory;
this.loader = loader;
this.cipher = null;
held = packet;
}
protected void generateCipher(int opmode) throws PacketException {
if (cipher != null) {
try {
cipher.doFinal();
} catch (BadPaddingException | IllegalBlockSizeException e) {
}
cipher = null;
}
try {
cipher = cipherFactory.getCipher(opmode);
} catch (CipherException e) {
throw new PacketException(e);
}
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (held != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
protected void processEncryptedCache() throws PacketException {
if (encryptedCache == null || cipherFactory.cipherAttributesModified()) {
if (held == null) throw new PacketException("no data");
generateCipher(Cipher.ENCRYPT_MODE);
byte[] savedArray = loader.writePacketNoDigest(held, true);
byte[] trailingArray = (trailingPassword == null || trailingPassword.length() < 1) ? new byte[0] : trailingPassword.getBytes(StandardCharsets.UTF_8);
trailingArrayLengthCache = trailingArray.length;
byte[] toEncrypt = new byte[savedArray.length + trailingArray.length];
System.arraycopy(savedArray, 0, toEncrypt, 0, savedArray.length);
if (trailingArray.length > 0) System.arraycopy(trailingArray, 0, toEncrypt, savedArray.length, trailingArray.length);
try {
encryptedCache = cipher.doFinal(toEncrypt);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new PacketException(e);
}
}
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
synchronized (slock) {
processEncryptedCache();
int cipherLen = cipherFactory.getSettingsNoSecretsLength();
byte[] toret = new byte[5 + cipherLen + ((trailingArrayLengthCache == 0) ? 0 : 4) + encryptedCache.length];
int index = 0;
toret[index++] = (byte) ((trailingPassword != null && trailingPassword.length() > 0) ? 1 : 0);
int length = cipherLen;
toret[index++] = (byte) (length / 16777216);
length %= 16777216;
toret[index++] = (byte) (length / 65536);
length %= 65536;
toret[index++] = (byte) (length / 256);
length %= 256;
toret[index++] = (byte) (length);
System.arraycopy(cipherFactory.getSettingsNoSecrets(), 0, toret, index, cipherLen); index += cipherLen;
if (trailingArrayLengthCache > 0) {
length = trailingArrayLengthCache;
toret[index++] = (byte) (length / 16777216);
length %= 16777216;
toret[index++] = (byte) (length / 65536);
length %= 65536;
toret[index++] = (byte) (length / 256);
length %= 256;
toret[index++] = (byte) (length);
}
System.arraycopy(encryptedCache, 0, toret, index, encryptedCache.length);
return toret;
}
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
synchronized (slock) {
int index = 1;
int cipherLenCache = (packetData[index++] & 0xff) * 16777216;
cipherLenCache += (packetData[index++] & 0xff) * 65536;
cipherLenCache += (packetData[index++] & 0xff) * 256;
cipherLenCache += (packetData[index++] & 0xff);
if (cipherLenCache < 1) throw new PacketException("cipher length less than 1");
byte[] cipherSettingsCache = new byte[cipherLenCache];
System.arraycopy(packetData, index, cipherSettingsCache, 0, cipherLenCache); index += cipherLenCache;
try {
cipherFactory.setSettings(cipherSettingsCache);
} catch (CipherException e) {
throw new PacketException(e);
}
generateCipher(Cipher.DECRYPT_MODE);
trailingArrayLengthCache = 0;
if ((packetData[0] & 1) == 1) {
trailingArrayLengthCache = (packetData[index++] & 0xff) * 16777216;
trailingArrayLengthCache += (packetData[index++] & 0xff) * 65536;
trailingArrayLengthCache += (packetData[index++] & 0xff) * 256;
trailingArrayLengthCache += (packetData[index++] & 0xff);
if (trailingArrayLengthCache < 1) throw new PacketException("trailer length less than 1");
}
encryptedCache = new byte[packetData.length - index];
System.arraycopy(packetData, index, encryptedCache, 0, encryptedCache.length);
try {
byte[] decrypted = cipher.doFinal(encryptedCache);
byte[] thePacket = new byte[decrypted.length - trailingArrayLengthCache];
System.arraycopy(decrypted, 0, thePacket, 0, thePacket.length);
if (trailingArrayLengthCache > 0) {
byte[] theTrailer = new byte[trailingArrayLengthCache];
System.arraycopy(decrypted, thePacket.length, theTrailer, 0, trailingArrayLengthCache);
trailingPassword = new String(theTrailer, StandardCharsets.UTF_8);
}
held = loader.readPacketNoDigest(thePacket, factory, null);
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new PacketException(e);
}
}
}
/**
* Reads payload data to an {@link OutputStream}.
*
* @param outputStream The output stream to read data to.
* @throws NullPointerException outputStream is null.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void readData(OutputStream outputStream) throws IOException, PacketException {
if (outputStream == null) throw new NullPointerException("outputStream is null");
synchronized (slock) {
processEncryptedCache();
outputStream.write((trailingPassword != null && trailingPassword.length() > 0) ? 1 : 0);
int length = cipherFactory.getSettingsNoSecretsLength();
PacketLoader.writeInteger(outputStream, length);
outputStream.write(cipherFactory.getSettingsNoSecrets());
if (trailingArrayLengthCache > 0) PacketLoader.writeInteger(outputStream, trailingArrayLengthCache);
outputStream.write(encryptedCache);
}
}
/**
* Writes payload data from an {@link InputStream}.
*
* @param inputStream The input stream to write data from.
* @param size The size of the input payload in bytes.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException size is less than 0.
* @throws IOException An IO Exception has occurred.
* @throws PacketException An Exception has occurred.
*/
@Override
public void writeData(InputStream inputStream, int size) throws IOException, PacketException {
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (size < 0) throw new IllegalArgumentException("size is less than 0");
synchronized (slock) {
if (size < 1) throw new IOException("inputStream end of stream");
int flag = readByteFromInputStream(inputStream) & 0xff;
if (size < 5) throw new IOException("inputStream end of stream");
int cipherLenCache = (readByteFromInputStream(inputStream) & 0xff) * 16777216;
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff) * 65536;
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff) * 256;
cipherLenCache += (readByteFromInputStream(inputStream) & 0xff);
if (cipherLenCache < 1) throw new PacketException("cipher length less than 1");
if (size < 5 + cipherLenCache) throw new IOException("inputStream end of stream");
byte[] cipherSettingsCache = new byte[cipherLenCache];
int offset = 0;
int slen;
while (offset < cipherLenCache) if ((slen = inputStream.read(cipherSettingsCache, offset, size - cipherLenCache)) != -1) offset += slen; else throw new IOException("inputStream end of stream");
try {
cipherFactory.setSettings(cipherSettingsCache);
} catch (CipherException e) {
throw new PacketException(e);
}
generateCipher(Cipher.DECRYPT_MODE);
trailingArrayLengthCache = 0;
if ((flag & 1) == 1) {
if (size < 9 + cipherLenCache) throw new IOException("inputStream end of stream");
trailingArrayLengthCache = (readByteFromInputStream(inputStream) & 0xff) * 16777216;
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff) * 65536;
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff) * 256;
trailingArrayLengthCache += (readByteFromInputStream(inputStream) & 0xff);
if (trailingArrayLengthCache < 1) throw new PacketException("trailer length less than 1");
}
encryptedCache = null;
CipherInputStream cipherInputStream = new CipherInputStream(new LengthClampedInputStream(inputStream, size - 5 - cipherLenCache - (((flag & 1) == 1) ? 4 + trailingArrayLengthCache : 0)), cipher);
held = loader.readStreamedPacketNoDigest(cipherInputStream, factory, null);
if (trailingArrayLengthCache > 0) {
byte[] theTrailer = PacketLoader.readArrayFromInputStream(cipherInputStream, trailingArrayLengthCache);
trailingPassword = new String(theTrailer, StandardCharsets.UTF_8);
}
try {
cipher.doFinal();
}
catch (BadPaddingException | IllegalBlockSizeException e) {
}
}
}
/**
* Gets the size of the output data.
*
* @return The size of the output data in bytes.
* @throws PacketException An Exception has occurred.
*/
@Override
public int getSize() throws PacketException {
synchronized (slock) {
processEncryptedCache();
return encryptedCache.length + 5 + cipherFactory.getSettingsNoSecretsLength() + ((trailingArrayLengthCache == 0) ? 0 : 4);
}
}
/**
* Gets the {@link PacketLoader} in use.
*
* @return The Packet Loader in use.
*/
public PacketLoader getPacketLoader() {
return loader;
}
/**
* Sets the {@link PacketLoader} to use.
*
* @param loader The Packet Loader to use.
* @throws NullPointerException loader is null.
*/
public void setPacketLoader(PacketLoader loader) {
if (loader == null) throw new NullPointerException("loader is null");
synchronized (slock) {
this.loader = loader;
}
}
/**
* Gets the {@link IPacketFactory} in use.
*
* @return The Packet Factory in use.
*/
public IPacketFactory getFactory() {
return factory;
}
/**
* Sets the {@link IPacketFactory} to use.
*
* @param factory The Packet Factory to use.
* @throws NullPointerException factory is null.
*/
public void setFactory(IPacketFactory factory) {
if (factory == null) throw new NullPointerException("factory is null");
synchronized (slock) {
this.factory = factory;
}
}
/**
* Gets the {@link ICipherFactory} being used.
*
* @return The Cipher Factory.
*/
public ICipherFactory getCipherFactory() {
return cipherFactory;
}
/**
* Sets the {@link ICipherFactory} being used.
*
* @param cipherFactory The Cipher Factory.
* @throws NullPointerException cipherFactory is null.
*/
public void setCipherFactory(ICipherFactory cipherFactory) {
if (cipherFactory == null) throw new NullPointerException("cipherFactory is null");
synchronized (slock) {
this.cipherFactory = cipherFactory;
}
}
/**
* Gets the trailing password (Or null if no trailing password).
*
* @return The trailing password or null.
*/
public String getTrailingPassword() {
return trailingPassword;
}
/**
* Sets the trailing password (Use null for no trailing password).
*
* @param trailingPassword The new trailing password or null.
*/
public void setTrailingPassword(String trailingPassword) {
synchronized (slock) {
encryptedCache = null;
this.trailingPassword = trailingPassword;
}
}
/**
* Gets the held packet or null.
*
* @return The packet or null.
*/
public IPacket getHeldPacket() {
return held;
}
/**
* Sets the held packet.
*
* @param packet The new packet or null.
*/
public void setHeldPacket(IPacket packet) {
synchronized (slock) {
encryptedCache = null;
held = packet;
}
}
}

View File

@ -0,0 +1,199 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.IAcknowledgement;
import com.captainalm.lib.stdcrypt.encryption.CipherException;
import com.captainalm.lib.stdcrypt.encryption.ICipherFactory;
import com.captainalm.lib.calmnet.packet.IPacket;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides the ability for supporting streams to upgrade to using password encrypted and / or base64 connections.
* This class can also signal the use changes for {@link Base64Packet}s or {@link EncryptedPacket}s.
* <p>
* Major ID: 255
* Minor ID: 253
* </p>
*
* @author Captain ALM
*/
public class NetworkEncryptionUpgradePacket implements IPacket, IAcknowledgement {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 253);
protected Boolean acknowledgement;
protected boolean upgrade;
protected boolean base64ed;
protected ICipherFactory cipherFactory;
protected final Object slock = new Object();
/**
* Constructs a new NetworkEncryptionUpgradePacket with the specified acknowledgement value, upgrade value, base 64 value and {@link ICipherFactory}.
*
* @param acknowledgement The acknowledgement value to use (Can be null).
* @param upgrade Is the packet treated as a stream upgrade, See: {@link #isUpgrade()}.
* @param base64ed Is the packet signalling base 64 to be used.
* @param cipherFactory The cipherFactory to signal for use.
*/
public NetworkEncryptionUpgradePacket(Boolean acknowledgement, boolean upgrade, boolean base64ed, ICipherFactory cipherFactory) {
this.acknowledgement = acknowledgement;
this.upgrade = upgrade;
this.base64ed = base64ed;
this.cipherFactory = cipherFactory;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (acknowledgement != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
synchronized (slock) {
if (acknowledgement == null) throw new PacketException("no data");
byte[] cipherBytes = (cipherFactory == null) ? null : cipherFactory.getSettings();
byte[] toret = new byte[2 + ((cipherBytes == null) ? 0 : cipherBytes.length)];
toret[0] = (acknowledgement) ? (byte) 1 : (byte) 0;
toret[1] = (byte) (((upgrade) ? 1 : 0) + ((base64ed) ? 2 : 0));
if (cipherBytes != null) System.arraycopy(cipherBytes, 0, toret, 2, cipherBytes.length);
return toret;
}
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length < 2) throw new PacketException("no data");
synchronized (slock) {
acknowledgement = (packetData[0] == 1);
if (!acknowledgement && packetData[0] != 0) acknowledgement = null;
upgrade = ((packetData[1] & 1) == 1);
base64ed = ((packetData[1] & 2) == 2);
if (cipherFactory != null && packetData.length > 2) {
byte[] cipherBytes = new byte[packetData.length - 2];
System.arraycopy(packetData, 2, cipherBytes, 0, cipherBytes.length);
try {
cipherFactory.setSettings(cipherBytes);
} catch (CipherException e) {
throw new PacketException(e);
}
}
}
}
/**
* Gets if the packet is treated as a stream upgrade or
* a change in packet use for {@link EncryptedPacket} and {@link Base64Packet}.
*
* @return If the packet is a stream upgrade.
*/
public boolean isUpgrade() {
return upgrade;
}
/**
* Sets if the packet is treated as a stream upgrade or
* a change in packet use for {@link EncryptedPacket} and {@link Base64Packet}.
*
* @param upgrade If the packet is a stream upgrade.
*/
public void setUpgrade(boolean upgrade) {
synchronized (slock) {
this.upgrade = upgrade;
}
}
/**
* Gets if base 64 is used.
* (This is not for this packet, it is part of the upgrade attributes.)
*
* @return If base 64 is used.
*/
public boolean isBase64ed() {
return base64ed;
}
/**
* Sets if base64 is used.
* (This is not for this packet, it is part of the upgrade attributes.)
*
* @param base64ed If base 64 is to be used.
*/
public void setBase64ed(boolean base64ed) {
synchronized (slock) {
this.base64ed = base64ed;
}
}
/**
* Gets the {@link ICipherFactory} being used or null.
*
* @return The Cipher Factory or null.
*/
public ICipherFactory getCipherFactory() {
return cipherFactory;
}
/**
* Sets the {@link ICipherFactory} being used.
*
* @param cipherFactory The Cipher Factory or null.
*/
public void setCipherFactory(ICipherFactory cipherFactory) {
synchronized (slock) {
this.cipherFactory = cipherFactory;
}
}
/**
* Gets if the class instance is an Acknowledgement.
*
* @return If the class instance is an Acknowledgement.
*/
@Override
public boolean isAcknowledgement() {
return (acknowledgement != null && acknowledgement);
}
}

View File

@ -0,0 +1,84 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.IPacket;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
import java.nio.charset.StandardCharsets;
/**
* This class provides a packet that is used to identify the type of network client is using this library.
* <p>
* Major ID: 255
* Minor ID: 255
* </p>
*
* @author Captain ALM
*/
public class NetworkIdentifierPacket implements IPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 255);
protected String id;
/**
* Constructs a new instance of NetworkIdentifierPacket with the specified ID.
*
* @param id The network ID of the client.
*/
public NetworkIdentifierPacket(String id) {
this.id = id;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (id != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (id == null) throw new PacketException("no data");
return id.getBytes(StandardCharsets.UTF_8);
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
id = new String(packetData, StandardCharsets.UTF_8);
}
}

View File

@ -0,0 +1,96 @@
package com.captainalm.lib.calmnet.packet.core;
import com.captainalm.lib.calmnet.packet.IAcknowledgement;
import com.captainalm.lib.calmnet.packet.IPacket;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides the ability for supporting streams to upgrade to using SSL connections.
* <p>
* Major ID: 255
* Minor ID: 254
* </p>
*
* @author Captain ALM
*/
public class NetworkSSLUpgradePacket implements IPacket, IAcknowledgement {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 255, (byte) 254);
protected Boolean acknowledgement;
/**
* Constructs a new NetworkSSLUpgrade packet with the specified acknowledgement value.
*
* @param acknowledgement The acknowledgement value to use (Can be null).
*/
public NetworkSSLUpgradePacket(Boolean acknowledgement) {
this.acknowledgement = acknowledgement;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (acknowledgement != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (acknowledgement == null) throw new PacketException("no data");
return new byte[] {((acknowledgement) ? (byte) 1 : (byte) 0)};
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 1) throw new PacketException("no data");
acknowledgement = (packetData[0] == 1);
if (!acknowledgement && packetData[0] != 0) acknowledgement = null;
}
/**
* Gets if the class instance is an Acknowledgement.
*
* @return If the class instance is an Acknowledgement.
*/
@Override
public boolean isAcknowledgement() {
return (acknowledgement != null && acknowledgement);
}
}

View File

@ -0,0 +1,7 @@
/**
* This package contains the core network packets.
* Major ID: 255
*
* @author Captain ALM
*/
package com.captainalm.lib.calmnet.packet.core;

View File

@ -0,0 +1,123 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.IPacket;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
import java.nio.ByteBuffer;
import java.util.UUID;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This class provides a packet for fragment allocation requesting.
* The response packet is: {@link FragmentAllocationPacket}.
* <p>
* Major ID: 254
* Minor ID: 1
* </p>
*
* @author Captain ALM
*/
public class FragmentAllocatePacket implements IPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 1);
protected Integer fragmentCount;
protected UUID allocationID;
/**
* Constructs a new FragmentAllocatePacket given the fragment count and allocation UUID.
*
* @param fragmentCount The number of fragments to allocate.
* @param allocationID The allocation ID.
* @throws IllegalArgumentException fragmentCount is less than 1.
*/
public FragmentAllocatePacket(Integer fragmentCount, UUID allocationID) {
if (fragmentCount != null && fragmentCount < 1) throw new IllegalArgumentException("fragmentCount is less than 1");
this.fragmentCount = fragmentCount;
this.allocationID = (allocationID == null) ? UUID.randomUUID() : allocationID;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (fragmentCount != null) && (allocationID != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (fragmentCount == null || allocationID == null) throw new PacketException("no data");
ByteBuffer buffer = ByteBuffer.wrap(new byte[20]);
buffer.put(getByteArrayFromInteger(fragmentCount));
buffer.putLong(allocationID.getMostSignificantBits());
buffer.putLong(allocationID.getLeastSignificantBits());
return buffer.array();
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 20) throw new PacketException("packet length is not 20");
ByteBuffer buffer = ByteBuffer.wrap(packetData);
byte[] toProcess = new byte[4];
buffer.get(toProcess);
fragmentCount = getIntegerFromByteArray(toProcess);
long mostSig = buffer.getLong();
allocationID = new UUID(mostSig, buffer.getLong());
}
/**
* Gets the number of fragments or null.
*
* @return The number of fragments or null.
*/
public Integer getFragmentCount() {
return fragmentCount;
}
/**
* Gets the allocation ID or null.
*
* @return The allocation ID or null.
*/
public UUID getAllocationID() {
return allocationID;
}
}

View File

@ -0,0 +1,126 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
import java.nio.ByteBuffer;
import java.util.UUID;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This class provides a packet for giving the allocated packetID
* as a response for {@link FragmentAllocatePacket}.
* <p>
* Major ID: 254
* Minor ID: 2
* </p>
*
* @author Captain ALM
*/
public class FragmentAllocationPacket extends FragmentPIDPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 2);
protected Boolean success;
protected UUID allocationID;
/**
* Constructs a new FragmentAllocationPacket given the packet ID, allocation ID and if it's successful.
*
* @param packetID The packet ID.
* @param allocationID The allocation ID.
* @param success The allocation was successful.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentAllocationPacket(Integer packetID, UUID allocationID, Boolean success) {
super(packetID);
this.success = success;
this.allocationID = allocationID;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return super.isValid() && (success != null) && (allocationID != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (packetID == null || success == null || allocationID == null) throw new PacketException("no data");
ByteBuffer buffer = ByteBuffer.wrap(new byte[21]);
buffer.put(getByteArrayFromInteger(packetID));
buffer.put((success) ? (byte) 1 : (byte) 0);
buffer.putLong(allocationID.getMostSignificantBits());
buffer.putLong(allocationID.getLeastSignificantBits());
return buffer.array();
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 21) throw new PacketException("packet length is not 21");
ByteBuffer buffer = ByteBuffer.wrap(packetData);
byte[] toProcess = new byte[4];
buffer.get(toProcess);
packetID = getIntegerFromByteArray(toProcess);
success = (buffer.get() == 1);
if (!success && packetData[4] != 0) success = null;
long mostSig = buffer.getLong();
allocationID = new UUID(mostSig, buffer.getLong());
}
/**
* Checks if the allocation is successful.
*
* @return If the allocation was successful.
*/
public boolean allocationSuccessful() {
return (success != null && success);
}
/**
* Gets the allocation ID or null.
*
* @return The allocation ID or null.
*/
public UUID getAllocationID() {
return allocationID;
}
}

View File

@ -0,0 +1,71 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides a packet for sending a payload with a packetID and fragmentID.
* The response packet is: {@link FragmentMessageResponsePacket}.
* <p>
* Major ID: 254
* Minor ID: 3
* </p>
*
* @author Captain ALM
*/
public class FragmentMessagePacket extends FragmentPIDMSGPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 3);
/**
* Constructs a new FragmentMessagePacket given the packet ID, fragment ID and payload.
*
* @param packetID The packet ID.
* @param fragmentID The fragment ID.
* @param payload The payload to store.
* @throws IllegalArgumentException packetID or fragmentID is less than 0.
*/
public FragmentMessagePacket(Integer packetID, Integer fragmentID, byte[] payload) {
super(packetID, fragmentID, payload);
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return super.isValid() && (payload != null);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (payload == null) throw new PacketException("no data");
return super.savePayload();
}
}

View File

@ -0,0 +1,48 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides a packet for sending an optional payload with a packetID and fragmentID
* as a response for {@link FragmentMessagePacket}.
* <p>
* Major ID: 254
* Minor ID: 4
* </p>
*
* @author Captain ALM
*/
public class FragmentMessageResponsePacket extends FragmentPIDMSGPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 4);
/**
* Constructs a new FragmentMessageResponsePacket given the packet ID, fragment ID and payload.
*
* @param packetID The packet ID.
* @param fragmentID The fragment ID.
* @param payload The payload to store.
* @throws IllegalArgumentException packetID or fragmentID is less than 0.
*/
public FragmentMessageResponsePacket(Integer packetID, Integer fragmentID, byte[] payload) {
super(packetID, fragmentID, payload);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
}

View File

@ -0,0 +1,81 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.IAcknowledgement;
import com.captainalm.lib.calmnet.packet.PacketException;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This abstract base class provides the ability for packets to contain an ID and if it is an Acknowledgement.
*
* @author Captain ALM
*/
public abstract class FragmentPIDAKNPacket extends FragmentPIDPacket implements IAcknowledgement {
protected Boolean acknowledgement;
/**
* Constructs a new FragmentPIDPacket given the packet ID and the acknowledgement value.
*
* @param packetID The packet ID.
* @param acknowledgement The acknowledgement value to use.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentPIDAKNPacket(Integer packetID, Boolean acknowledgement) {
super(packetID);
this.acknowledgement = acknowledgement;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return super.isValid() && (acknowledgement != null);
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (packetID == null || acknowledgement == null) throw new PacketException("no data");
byte[] toret = new byte[5];
System.arraycopy(getByteArrayFromInteger(packetID), 0, toret, 0, 4);
toret[4] = (acknowledgement) ? (byte) 1 : (byte) 0;
return toret;
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 5) throw new PacketException("packet length is not 5");
byte[] toProcess = new byte[4];
System.arraycopy(packetData, 0, toProcess, 0, 4);
packetID = getIntegerFromByteArray(toProcess);
acknowledgement = (packetData[4] == 1);
if (!acknowledgement && packetData[4] != 0) acknowledgement = null;
}
/**
* Gets if the class instance is an Acknowledgement.
*
* @return If the class instance is an Acknowledgement.
*/
@Override
public boolean isAcknowledgement() {
return (acknowledgement != null && acknowledgement);
}
}

View File

@ -0,0 +1,96 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketException;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This abstract base class provides the ability for packets to contain an ID, a Fragment ID and a payload.
*
* @author Captain ALM
*/
public abstract class FragmentPIDMSGPacket extends FragmentPIDPacket {
protected Integer fragmentID;
protected byte[] payload;
/**
* Constructs a new FragmentPIDMSGPacket given the packet ID, fragment ID and payload.
*
* @param packetID The packet ID.
* @param fragmentID The fragment ID.
* @param payload The payload to store.
* @throws IllegalArgumentException packetID or fragmentID is less than 0.
*/
public FragmentPIDMSGPacket(Integer packetID, Integer fragmentID, byte[] payload) {
super(packetID);
if (fragmentID != null && fragmentID < 0) throw new IllegalArgumentException("fragmentID is less than 0");
this.fragmentID = fragmentID;
this.payload = payload;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return super.isValid() && (fragmentID != null);
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (packetID == null || fragmentID == null) throw new PacketException("no data");
byte[] localPayload = (payload == null) ? new byte[0] : payload;
byte[] toret = new byte[8 + localPayload.length];
System.arraycopy(getByteArrayFromInteger(packetID), 0, toret, 0, 4);
System.arraycopy(getByteArrayFromInteger(fragmentID), 0, toret, 4, 4);
System.arraycopy(localPayload, 0, toret, 8, localPayload.length);
return toret;
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length < 8) throw new PacketException("packet length is less than 8");
byte[] toProcess = new byte[4];
System.arraycopy(packetData, 0, toProcess, 0, 4);
packetID = getIntegerFromByteArray(toProcess);
System.arraycopy(packetData, 4, toProcess, 0, 4);
fragmentID = getIntegerFromByteArray(toProcess);
payload = new byte[packetData.length - 8];
System.arraycopy(packetData, 8, payload, 0, payload.length);
}
/**
* Gets the fragment message byte array or null.
*
* @return The byte array or null.
*/
public byte[] getFragmentMessage() {
return payload;
}
/**
* Gets the fragment ID or null.
*
* @return The fragment ID or null.
*/
public Integer getFragmentID() {
return fragmentID;
}
}

View File

@ -0,0 +1,42 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.IPacket;
/**
* This abstract base class provides the ability for packets to return an ID.
*
* @author Captain ALM
*/
public abstract class FragmentPIDPacket implements IPacket {
protected Integer packetID;
/**
* Constructs a new FragmentPIDPacket given the packet ID.
*
* @param packetID The packet ID.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentPIDPacket(Integer packetID) {
if (packetID != null && packetID < 0) throw new IllegalArgumentException("packetID is less than 0");
this.packetID = packetID;
}
/**
* Gets if the packet is valid.
*
* @return Is the packet valid?
*/
@Override
public boolean isValid() {
return (packetID != null);
}
/**
* Gets the packet ID or null.
*
* @return The packet ID or null.
*/
public Integer getPacketID() {
return packetID;
}
}

View File

@ -0,0 +1,47 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides a packet for signalling that the sending end
* should start re-sending un acknowledged fragment packets.
* <p>
* Major ID: 254
* Minor ID: 6
* </p>
*
* @author Captain ALM
*/
public class FragmentRetrySendPacket extends FragmentPIDAKNPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 6);
/**
* Constructs a new FragmentRetrySendPacket given the packet ID and the acknowledgement value.
*
* @param packetID The packet ID.
* @param acknowledgement The acknowledgement value to use.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentRetrySendPacket(Integer packetID, Boolean acknowledgement) {
super(packetID, acknowledgement);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
}

View File

@ -0,0 +1,47 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
/**
* This class provides a packet for signalling that all fragments for a packet have been sent or that sending has been successfully cancelled.
* <p>
* Major ID: 254
* Minor ID: 5
* </p>
*
* @author Captain ALM
*/
public class FragmentSendCompletePacket extends FragmentPIDAKNPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 5);
/**
* Constructs a new FragmentSendCompletePacket given the packet ID and the acknowledgement value.
*
* @param packetID The packet ID.
* @param acknowledgement The acknowledgement value to use.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentSendCompletePacket(Integer packetID, Boolean acknowledgement) {
super(packetID, acknowledgement);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
}

View File

@ -0,0 +1,76 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.FragmentSender;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This class provides a packet for stopping the remote {@link FragmentSender}.
* <p>
* Major ID: 254
* Minor ID: 7
* </p>
*
* @author Captain ALM
*/
public class FragmentSendStopPacket extends FragmentPIDPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 7);
/**
* Constructs a new FragmentSendStopPacket given the packet ID.
*
* @param packetID The packet ID.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentSendStopPacket(Integer packetID) {
super(packetID);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (packetID == null) throw new PacketException("no data");
return getByteArrayFromInteger(packetID);
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 4) throw new PacketException("packet length is not 4");
packetID = getIntegerFromByteArray(packetData);
}
}

View File

@ -0,0 +1,75 @@
package com.captainalm.lib.calmnet.packet.fragment;
import com.captainalm.lib.calmnet.packet.PacketException;
import com.captainalm.lib.calmnet.packet.PacketProtocolInformation;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getByteArrayFromInteger;
import static com.captainalm.lib.calmnet.packet.PacketLoader.getIntegerFromByteArray;
/**
* This class provides a packet for stating that all packets have been successfully sent and verified.
* <p>
* Major ID: 254
* Minor ID: 8
* </p>
*
* @author Captain ALM
*/
public class FragmentSendVerifyCompletePacket extends FragmentPIDPacket {
private static final PacketProtocolInformation protocol = new PacketProtocolInformation((byte) 254, (byte) 8);
/**
* Constructs a new FragmentSendVerifyCompletePacket given the packet ID.
*
* @param packetID The packet ID.
* @throws IllegalArgumentException packetID is less than 0.
*/
public FragmentSendVerifyCompletePacket(Integer packetID) {
super(packetID);
}
/**
* Gets the protocol information.
*
* @return The protocol information.
*/
@Override
public PacketProtocolInformation getProtocol() {
return protocol;
}
/**
* Gets the protocol information statically.
*
* @return The protocol information.
*/
public static PacketProtocolInformation getTheProtocol() {
return protocol;
}
/**
* Saves the packet payload to a byte array.
*
* @return The packet payload data.
* @throws PacketException An Exception has occurred.
*/
@Override
public byte[] savePayload() throws PacketException {
if (packetID == null) throw new PacketException("no data");
return getByteArrayFromInteger(packetID);
}
/**
* Loads the packet payload from save data.
*
* @param packetData The packet payload data.
* @throws NullPointerException The new store data is null.
* @throws PacketException An Exception has occurred.
*/
@Override
public void loadPayload(byte[] packetData) throws PacketException {
if (packetData == null) throw new NullPointerException("packetData is null");
if (packetData.length != 4) throw new PacketException("packet length is not 4");
packetID = getIntegerFromByteArray(packetData);
}
}

View File

@ -0,0 +1,7 @@
/**
* This package contains the fragment network packets.
* Major ID: 254
*
* @author Captain ALM
*/
package com.captainalm.lib.calmnet.packet.fragment;

View File

@ -0,0 +1,6 @@
/**
* This package contains the network packets and handling code.
*
* @author Captain ALM
*/
package com.captainalm.lib.calmnet.packet;

View File

@ -0,0 +1,133 @@
package com.captainalm.lib.calmnet.stream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* This class provides the ability to limit the number of bytes read from the underlying stream.
* When the limit is reached, this class considers that state as end of stream.
*
* @author Captain ALM
*/
public class LengthClampedInputStream extends FilterInputStream {
protected boolean closed;
protected int clampedLength;
protected int markReadLimit;
protected int markResetLength;
/**
* Creates a LengthClampedInputStream with the specified {@link InputStream}
* and the maximum number of bytes that can be read from the stream.
*
* @param inputStream The input stream to clamp.
* @param length The maximum number of bytes that can be read before end of stream is reached.
* @throws NullPointerException inputStream is null.
* @throws IllegalArgumentException length is less than 0.
*/
public LengthClampedInputStream(InputStream inputStream, int length) {
super(inputStream);
if (inputStream == null) throw new NullPointerException("inputStream is null");
if (length < 0) throw new IllegalArgumentException("length is less than 0");
clampedLength = length;
}
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned. This method blocks until input data
* is available, the end of the stream is detected, or an exception
* is thrown.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
*/
@Override
public int read() throws IOException {
if (closed) throw new IOException("stream closed");
if (clampedLength > 0) clampedLength--; else return -1;
decrementMarkResetLength();
return super.read();
}
protected synchronized void decrementMarkResetLength() {
if (markResetLength >= 0) markResetLength--;
}
/**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* caller of a method for this input stream. The next caller might be
* the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking.
* @exception IOException if an I/O error occurs.
*/
@Override
public int available() throws IOException {
if (closed) throw new IOException("stream closed");
return Math.min(super.available(), clampedLength);
}
/**
* Marks the current position in this input stream. A subsequent
* call to the <code>reset</code> method repositions this stream at
* the last marked position so that subsequent reads re-read the same bytes.
* <p>
* The <code>readlimit</code> argument tells this input stream to
* allow that many bytes to be read before the mark position gets
* invalidated.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
*/
@Override
public synchronized void mark(int readlimit) {
if (super.markSupported()) {
super.mark(readlimit);
markReadLimit = readlimit;
markResetLength = readlimit;
}
}
/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
* <p>
* Stream marks are intended to be used in
* situations where you need to read ahead a little to see what's in
* the stream. Often this is most easily done by invoking some
* general parser. If the stream is of the type handled by the
* parse, it just chugs along happily. If the stream is not of
* that type, the parser should toss an exception when it fails.
* If this happens within readlimit bytes, it allows the outer
* code to reset the stream and try another parser.
*
* @exception IOException if the stream has not been marked, if the
* mark has been invalidated or marking is not supported.
*/
@Override
public synchronized void reset() throws IOException {
if (closed) return;
super.reset();
if (markResetLength >= 0) clampedLength += (markReadLimit - markResetLength);
markResetLength = -1;
}
/**
* Closes this input stream and releases any system resources
* associated with the stream.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
if (!closed) closed = true;
super.close();
}
}

View File

@ -0,0 +1,239 @@
package com.captainalm.lib.calmnet.stream;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
/**
* This class provides a Network Input stream for either {@link Socket}s or {@link DatagramSocket}s.
*
* @author Captain ALM
*/
public class NetworkInputStream extends InputStream {
protected boolean closed = false;
protected Socket socket;
protected InputStream socketStream;
protected DatagramSocket dsocket;
protected DatagramPacket dsocketPacket;
protected int dsocketPacketIndex = 0;
protected InetAddress dAddress;
protected int dPort = -1;
protected int dlen = 0;
/**
* Constructs a new NetworkInputStream with the specified {@link Socket}.
*
* @param socketIn The socket to use.
* @throws NullPointerException socketIn is null.
*/
public NetworkInputStream(Socket socketIn) {
if (socketIn == null) throw new NullPointerException("socketIn is null");
socket = socketIn;
}
/**
* Constructs a new NetworkInputStream with the specified {@link DatagramSocket}.
*
* @param socketIn The datagram socket to use.
* @throws NullPointerException socketIn is null.
*/
public NetworkInputStream(DatagramSocket socketIn) {
if (socketIn == null) throw new NullPointerException("socketIn is null");
dsocket = socketIn;
}
protected void assureDSocketPacket() throws IOException {
if (dsocketPacket == null) {
dsocketPacket = new DatagramPacket(new byte[65535], 65535);
dsocket.receive(dsocketPacket);
dAddress = dsocketPacket.getAddress();
dPort = dsocketPacket.getPort();
dsocketPacketIndex = 0;
dlen = dsocketPacket.getLength();
if (dlen < 1) dsocketPacket = null;
}
}
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @throws IOException if an I/O error occurs.
*/
@Override
public int read() throws IOException {
if (closed) throw new IOException("stream closed");
if (socket == null) {
int toret = -1;
assureDSocketPacket();
if (dsocketPacket != null) {
toret = dsocketPacket.getData()[dsocketPacketIndex++] & 0xff;
if (dsocketPacketIndex >= dlen) dsocketPacket = null;
}
return toret;
} else {
if (socketStream == null) socketStream = socket.getInputStream();
return socketStream.read();
}
}
/**
* Gets the current {@link InetAddress} of the stream.
* Can be null.
*
* @return The address.
*/
public InetAddress getAddress() {
if (dsocket == null) {
return socket.getInetAddress();
} else {
return (dAddress == null) ? dsocket.getInetAddress() : dAddress;
}
}
/**
* Gets the current port of the stream.
* Can be -1.
*
* @return The current port.
*/
public Integer getPort() {
if (dsocket == null) {
return socket.getPort();
} else {
return (dPort == -1) ? dsocket.getPort() : dPort;
}
}
/**
* Gets the local {@link InetAddress} of the stream.
* Can be null.
*
* @return The local address.
*/
public InetAddress getLocalAddress() {
if (dsocket == null) {
return socket.getLocalAddress();
} else {
return dsocket.getLocalAddress();
}
}
/**
* Gets the local port of the stream.
* Can be -1.
*
* @return The local port.
*/
public Integer getLocalPort() {
if (dsocket == null) {
return socket.getPort();
} else {
return dsocket.getPort();
}
}
/**
* Gets the socket in use or null.
*
* @return The socket in use or null.
*/
public Socket getSocket() {
return socket;
}
/**
* Sets the socket in use.
*
* @param socketIn The socket to now use.
* @throws NullPointerException socketIn is null.
* @throws IOException stream closed or not using a socket.
*/
public void setSocket(Socket socketIn) throws IOException {
if (closed) throw new IOException("stream closed");
if (socket == null) throw new IOException("not using a socket");
if (socketIn == null) throw new NullPointerException("socketIn is null");
socket = socketIn;
socketStream = null;
}
/**
* Gets the datagram socket in use or null.
*
* @return The datagram socket in use or null.
*/
public DatagramSocket getDatagramSocket() {
return dsocket;
}
/**
* Sets the datagram socket in use.
*
* @param socketIn The datagram socket to now use.
* @throws NullPointerException socketIn is null.
* @throws IOException stream closed or not using a datagram socket.
*/
public void setDatagramSocket(DatagramSocket socketIn) throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) throw new IOException("not using a datagram socket");
if (socketIn == null) throw new NullPointerException("socketIn is null");
dsocket = socketIn;
}
/**
* Returns an estimate of the number of bytes that can be read (or
* skipped over) from this input stream without blocking by the next
* invocation of a method for this input stream. The next invocation
* might be the same thread or another thread. A single read or skip of this
* many bytes will not block, but may read or skip fewer bytes.
*
* @return an estimate of the number of bytes that can be read (or skipped
* over) from this input stream without blocking or {@code 0} when
* it reaches the end of the input stream.
* @throws IOException if an I/O error occurs.
*/
@Override
public int available() throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) {
if (socketStream == null) socketStream = socket.getInputStream();
return socketStream.available();
} else {
assureDSocketPacket();
return (dsocketPacket == null) ? 0 : dlen - dsocketPacketIndex;
}
}
/**
* Closes this input stream and releases any system resources associated
* with the stream. The underlying socket is closed.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
if (socket == null) {
dsocketPacket = null;
dAddress = null;
dPort = -1;
dsocket.close();
dsocket = null;
} else {
socketStream = null;
socket.close();
socket = null;
}
}
}
}

View File

@ -0,0 +1,315 @@
package com.captainalm.lib.calmnet.stream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
/**
* This class provides a Network Output stream for either {@link Socket}s or {@link DatagramSocket}s.
*
* @author Captain ALM
*/
public class NetworkOutputStream extends OutputStream {
protected boolean closed = false;
protected Socket socket;
protected OutputStream socketStream;
protected DatagramSocket dsocket;
protected byte[] dsocketBuffer;
protected int dsocketBufferIndex = 0;
protected InetAddress dAddress;
protected int dPort = -1;
/**
* Constructs a new NetworkOutputStream with the specified {@link Socket}.
*
* @param socketIn The socket to use.
* @throws NullPointerException socketIn is null.
*/
public NetworkOutputStream(Socket socketIn) {
if (socketIn == null) throw new NullPointerException("socketIn is null");
socket = socketIn;
}
/**
* Constructs a new NetworkOutputStream with the specified {@link DatagramSocket}.
*
* @param socketIn The datagram socket to use.
* @throws NullPointerException socketIn is null.
*/
public NetworkOutputStream(DatagramSocket socketIn) {
if (socketIn == null) throw new NullPointerException("socketIn is null");
dsocket = socketIn;
}
/**
* Constructs a new NetworkOutputStream with the specified {@link DatagramSocket} and datagram buffer size.
*
* @param socketIn The datagram socket to use.
* @param size The size of the buffer.
* @throws NullPointerException socketIn is null.
* @throws IllegalArgumentException size is less than 1 or greater than 65535.
*/
public NetworkOutputStream(DatagramSocket socketIn, int size) {
this(socketIn);
try {
setDatagramBufferSize(size);
} catch (IOException e) {
}
}
/**
* Constructs a new NetworkOutputStream with the specified {@link DatagramSocket}, datagram buffer size, {@link InetAddress} target and port target.
*
* @param socketIn The datagram socket to use.
* @param size The size of the buffer.
* @param address The target address to set to.
* @param port The target port to set to.
* @throws NullPointerException socketIn or address is null.
* @throws IllegalArgumentException size is less than 1 or greater than 65535 or port is less than 0 or greater than 65535.
*/
public NetworkOutputStream(DatagramSocket socketIn, int size, InetAddress address, int port) {
this(socketIn, size);
try {
setDatagramTarget(address, port);
} catch (IOException e) {
}
}
/**
* Gets if the {@link #setDatagramBufferSize(int)} can be used.
*
* @return If the datagram buffer size can be set.
*/
public boolean canDatagramBufferBeSet() {
return dsocket != null && dsocketBufferIndex == 0;
}
/**
* Sets the buffer size for sending datagrams.
*
* @param size The size to set to.
* @throws IllegalArgumentException size is less than 1 or greater than 65535.
* @throws IOException a datagram socket is not in use, buffer index is not null or the stream is closed.
*/
public void setDatagramBufferSize(int size) throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) throw new IOException("not using a datagram socket");
if (size < 1) throw new IllegalArgumentException("size is less than 1");
if (size > 65535) throw new IllegalArgumentException("size is greater than 65535");
if (dsocketBufferIndex != 0) throw new IOException("buffer index is not 0");
dsocketBuffer = new byte[size];
}
/**
* Sets the datagram target {@link InetAddress} and port.
*
* @param address The address to set to.
* @param port The port to set to.
* @throws NullPointerException address is null.
* @throws IllegalArgumentException port is less than 0 or greater than 65535.
* @throws IOException a datagram socket is not in use or the stream is closed.
*/
public void setDatagramTarget(InetAddress address, int port) throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) throw new IOException("not using a datagram socket");
if (address == null) throw new NullPointerException("address is null");
if (port < 0) throw new IllegalArgumentException("port is less than 0");
if (port > 65535) throw new IllegalArgumentException("port is greater than 65535");
dAddress = address;
dPort = port;
}
/**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
* low-order bits of the argument <code>b</code>. The 24
* high-order bits of <code>b</code> are ignored.
*
* @param b the <code>byte</code>.
* @throws IOException if an I/O error occurs. In particular,
* an <code>IOException</code> will be thrown if the
* output stream has been closed.
*/
@Override
public void write(int b) throws IOException {
if (closed) throw new IOException("stream closed");
if (socket == null) {
if (dsocketBuffer != null) {
dsocketBuffer[dsocketBufferIndex++] = (byte) b;
if (dsocketBufferIndex >= dsocketBuffer.length) {
sendDatagramData(dsocketBuffer, dsocketBuffer.length);
dsocketBufferIndex = 0;
dsocketBuffer = new byte[dsocketBuffer.length];
}
} else {
throw new IOException("null datagram buffer");
}
} else {
if (socketStream == null) socketStream = socket.getOutputStream();
socketStream.write(b);
}
}
/**
* Gets the current {@link InetAddress} of the stream.
* Can be null.
*
* @return The address.
*/
public InetAddress getAddress() {
if (dsocket == null) {
return socket.getInetAddress();
} else {
return (dAddress == null) ? dsocket.getInetAddress() : dAddress;
}
}
/**
* Gets the current port of the stream.
* Can be -1.
*
* @return The current port.
*/
public Integer getPort() {
if (dsocket == null) {
return socket.getPort();
} else {
return (dPort == -1) ? dsocket.getPort() : dPort;
}
}
/**
* Gets the local {@link InetAddress} of the stream.
* Can be null.
*
* @return The local address.
*/
public InetAddress getLocalAddress() {
if (dsocket == null) {
return socket.getLocalAddress();
} else {
return dsocket.getLocalAddress();
}
}
/**
* Gets the local port of the stream.
* Can be -1.
*
* @return The local port.
*/
public Integer getLocalPort() {
if (dsocket == null) {
return socket.getPort();
} else {
return dsocket.getPort();
}
}
/**
* Gets the socket in use or null.
*
* @return The socket in use or null.
*/
public Socket getSocket() {
return socket;
}
/**
* Sets the socket in use.
*
* @param socketIn The socket to now use.
* @throws NullPointerException socketIn is null.
* @throws IOException stream closed or not using a socket.
*/
public void setSocket(Socket socketIn) throws IOException {
if (closed) throw new IOException("stream closed");
if (socket == null) throw new IOException("not using a socket");
if (socketIn == null) throw new NullPointerException("socketIn is null");
socket = socketIn;
socketStream = null;
}
/**
* Gets the datagram socket in use or null.
*
* @return The datagram socket in use or null.
*/
public DatagramSocket getDatagramSocket() {
return dsocket;
}
/**
* Sets the datagram socket in use.
*
* @param socketIn The datagram socket to now use.
* @throws NullPointerException socketIn is null.
* @throws IOException stream closed or not using a datagram socket.
*/
public void setDatagramSocket(DatagramSocket socketIn) throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) throw new IOException("not using a datagram socket");
if (socketIn == null) throw new NullPointerException("socketIn is null");
dsocket = socketIn;
}
/**
* Flushes this output stream and forces any buffered output bytes
* to be written out. The general contract of <code>flush</code> is
* that calling it is an indication that, if any bytes previously
* written have been buffered by the implementation of the output
* stream, such bytes should immediately be written to their
* intended destination.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void flush() throws IOException {
if (closed) throw new IOException("stream closed");
if (dsocket == null) {
if (socketStream == null) socketStream = socket.getOutputStream();
socketStream.flush();
} else {
sendDatagramData(dsocketBuffer, dsocketBufferIndex);
dsocketBufferIndex = 0;
dsocketBuffer = (dsocketBuffer == null) ? null : new byte[dsocketBuffer.length];
}
}
protected void sendDatagramData(byte[] data, int length) throws IOException {
if (data == null || length < 1) return;
if (dAddress == null || dPort < 0 || dPort > 65535) throw new IOException("no datagram target parameters set");
DatagramPacket packet = new DatagramPacket(data, length, dAddress, dPort);
dsocket.send(packet);
}
/**
* Closes this output stream and releases any system resources
* associated with this stream. The general contract of <code>close</code>
* is that it closes the output stream. A closed stream cannot perform
* output operations and cannot be reopened. The underlying socket is closed.
*
* @exception IOException if an I/O error occurs.
*/
@Override
public void close() throws IOException {
if (!closed) {
closed = true;
if (socket == null) {
dsocketBuffer = null;
dAddress = null;
dPort = -1;
dsocket.close();
dsocket = null;
} else {
socketStream = null;
socket.close();
socket = null;
}
}
}
}

View File

@ -0,0 +1,6 @@
/**
* This package provides streams for network packet streaming.
*
* @author Captain ALM
*/
package com.captainalm.lib.calmnet.stream;