Initial commit.
Note, In Progress: Adding all fragment data is verified and sent correctly.
This commit is contained in:
commit
96c9864092
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal 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
8
.idea/.gitignore
vendored
Normal 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
7
.idea/discord.xml
Normal 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
6
.idea/misc.xml
Normal 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
8
.idea/modules.xml
Normal 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
29
LICENSE
Normal 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
10
README.md
Normal 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
12
calmnetlib.iml
Normal 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>
|
266
src/com/captainalm/lib/calmnet/SSLUtilities.java
Normal file
266
src/com/captainalm/lib/calmnet/SSLUtilities.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
src/com/captainalm/lib/calmnet/SSLUtilityException.java
Normal file
60
src/com/captainalm/lib/calmnet/SSLUtilityException.java
Normal 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);
|
||||
}
|
||||
}
|
8
src/com/captainalm/lib/calmnet/package-info.java
Normal file
8
src/com/captainalm/lib/calmnet/package-info.java
Normal 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;
|
146
src/com/captainalm/lib/calmnet/packet/CALMNETPacketFactory.java
Normal file
146
src/com/captainalm/lib/calmnet/packet/CALMNETPacketFactory.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
387
src/com/captainalm/lib/calmnet/packet/FragmentReceiver.java
Normal file
387
src/com/captainalm/lib/calmnet/packet/FragmentReceiver.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
320
src/com/captainalm/lib/calmnet/packet/FragmentSender.java
Normal file
320
src/com/captainalm/lib/calmnet/packet/FragmentSender.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
15
src/com/captainalm/lib/calmnet/packet/IAcknowledgement.java
Normal file
15
src/com/captainalm/lib/calmnet/packet/IAcknowledgement.java
Normal 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();
|
||||
}
|
39
src/com/captainalm/lib/calmnet/packet/IPacket.java
Normal file
39
src/com/captainalm/lib/calmnet/packet/IPacket.java
Normal 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;
|
||||
}
|
31
src/com/captainalm/lib/calmnet/packet/IPacketFactory.java
Normal file
31
src/com/captainalm/lib/calmnet/packet/IPacketFactory.java
Normal 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);
|
||||
}
|
42
src/com/captainalm/lib/calmnet/packet/IStreamedPacket.java
Normal file
42
src/com/captainalm/lib/calmnet/packet/IStreamedPacket.java
Normal 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;
|
||||
}
|
60
src/com/captainalm/lib/calmnet/packet/PacketException.java
Normal file
60
src/com/captainalm/lib/calmnet/packet/PacketException.java
Normal 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);
|
||||
}
|
||||
}
|
422
src/com/captainalm/lib/calmnet/packet/PacketLoader.java
Normal file
422
src/com/captainalm/lib/calmnet/packet/PacketLoader.java
Normal 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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
243
src/com/captainalm/lib/calmnet/packet/core/Base64Packet.java
Normal file
243
src/com/captainalm/lib/calmnet/packet/core/Base64Packet.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
458
src/com/captainalm/lib/calmnet/packet/core/EncryptedPacket.java
Normal file
458
src/com/captainalm/lib/calmnet/packet/core/EncryptedPacket.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This package contains the core network packets.
|
||||
* Major ID: 255
|
||||
*
|
||||
* @author Captain ALM
|
||||
*/
|
||||
package com.captainalm.lib.calmnet.packet.core;
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* This package contains the fragment network packets.
|
||||
* Major ID: 254
|
||||
*
|
||||
* @author Captain ALM
|
||||
*/
|
||||
package com.captainalm.lib.calmnet.packet.fragment;
|
6
src/com/captainalm/lib/calmnet/packet/package-info.java
Normal file
6
src/com/captainalm/lib/calmnet/packet/package-info.java
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This package contains the network packets and handling code.
|
||||
*
|
||||
* @author Captain ALM
|
||||
*/
|
||||
package com.captainalm.lib.calmnet.packet;
|
@ -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();
|
||||
}
|
||||
}
|
239
src/com/captainalm/lib/calmnet/stream/NetworkInputStream.java
Normal file
239
src/com/captainalm/lib/calmnet/stream/NetworkInputStream.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
315
src/com/captainalm/lib/calmnet/stream/NetworkOutputStream.java
Normal file
315
src/com/captainalm/lib/calmnet/stream/NetworkOutputStream.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
src/com/captainalm/lib/calmnet/stream/package-info.java
Normal file
6
src/com/captainalm/lib/calmnet/stream/package-info.java
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* This package provides streams for network packet streaming.
|
||||
*
|
||||
* @author Captain ALM
|
||||
*/
|
||||
package com.captainalm.lib.calmnet.stream;
|
Loading…
Reference in New Issue
Block a user