Added boundary for size of packets read by extension server

- Also added documentation and refactored the code
This commit is contained in:
Dorving 2022-04-11 03:16:18 +02:00
parent 8cd429cc23
commit 0951dba4c4

View File

@ -2,85 +2,125 @@ package gearth.services.extension_handler.extensions.implementations.network;
import gearth.protocol.HPacket; import gearth.protocol.HPacket;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducer; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducer;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerObserver; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerObserver;
import gearth.services.extension_handler.extensions.implementations.network.authentication.Authenticator; import gearth.services.extension_handler.extensions.implementations.network.authentication.Authenticator;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
/** /**
* Represents an {@link ExtensionProducer} that implements a server to which
* remotely-ran extensions can connect.
*
* @see ExtensionProducerFactory#getAll() for instance creation.
*
* Created by Jonas on 21/06/18. * Created by Jonas on 21/06/18.
*/ */
public class NetworkExtensionsProducer implements ExtensionProducer { public final class NetworkExtensionsProducer implements ExtensionProducer {
/**
* Initial port server tries to listen at, if {@link ServerSocket} creation fails,
* it tries next port.
*/
private static final int PORT_ONSET = 9092;
/**
* Represents the number of bytes per boolean encoded in an incoming packet.
*/
private static final int BOOLEAN_SIZE = 1;
/**
* Represents the maximum number of bytes per string encoded in an incoming packet.
*/
private static final int MAX_STRING_SIZE = Character.BYTES * 100;
/**
* Represents the maximum number of bytes of an incoming packet.
* <p>
* Used as a form of validation for packets, prevents other Apps that connect
* with the server from sending unexpected data and inexplicably causing huge byte array allocations.
* <p>
* Since the server only accepts {@link NetworkExtensionInfo.INCOMING_MESSAGES_IDS#EXTENSIONINFO} packets,
* this value is calculated based on that packet.
*/
private static final int MAX_PACKET_SIZE = (MAX_STRING_SIZE * 6) + (BOOLEAN_SIZE * 4);
/**
* The port at which the {@link #serverSocket} is listening for incoming connections.
*/
public static int extensionPort = -1; public static int extensionPort = -1;
private ServerSocket serverSocket; private ServerSocket serverSocket;
@Override @Override
public void startProducing(ExtensionProducerObserver observer) { public void startProducing(ExtensionProducerObserver observer) {
// serverSocket = new ServerSocket(0);
int port = 9092;
boolean serverSetup = false;
while (!serverSetup) {
serverSetup = createServer(port);
port++;
}
/*
Initialise the serverSocket at the argued port.
*/
int port = PORT_ONSET;
while (!createServer(port))
++port;
/*
Start connection listener thread.
*/
new Thread(() -> { new Thread(() -> {
try { try {
while (!serverSocket.isClosed()) { while (!serverSocket.isClosed()) {
Socket extensionSocket = serverSocket.accept();
// accept a new connection
final Socket extensionSocket = serverSocket.accept();
extensionSocket.setTcpNoDelay(true); extensionSocket.setTcpNoDelay(true);
/*
Start client session handler thread.
*/
new Thread(() -> { new Thread(() -> {
try { try {
// write INFOREQUEST packet to client
synchronized (extensionSocket) { synchronized (extensionSocket) {
extensionSocket.getOutputStream().write((new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes()); extensionSocket.getOutputStream().write((new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes());
} }
InputStream inputStream = extensionSocket.getInputStream(); final DataInputStream dIn = new DataInputStream(extensionSocket.getInputStream());
DataInputStream dIn = new DataInputStream(inputStream);
// listen to incoming data from client
while (!extensionSocket.isClosed()) { while (!extensionSocket.isClosed()) {
int length = dIn.readInt(); final int length = dIn.readInt();
byte[] headerandbody = new byte[length + 4];
int amountRead = 0; if (length > MAX_PACKET_SIZE)
while (amountRead < length) { break;
amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead));
}
HPacket packet = new HPacket(headerandbody); final HPacket packet = readPacket(dIn, length);
packet.fixLength();
if (packet.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONINFO) { if (packet.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONINFO) {
NetworkExtension gEarthExtension = new NetworkExtension(
packet,
extensionSocket
);
if (Authenticator.evaluate(gEarthExtension)) { final NetworkExtension gEarthExtension = new NetworkExtension(packet, extensionSocket);
if (Authenticator.evaluate(gEarthExtension))
observer.onExtensionProduced(gEarthExtension); observer.onExtensionProduced(gEarthExtension);
} else
else {
gEarthExtension.close(); gEarthExtension.close();
}
break; break;
} }
} }
} catch (IOException ignored) {
} catch (IOException ignored) {} }
}).start(); }).start();
} }
} catch (IOException e) {e.printStackTrace();} } catch (IOException e) {
e.printStackTrace();
}
}).start(); }).start();
} }
@ -94,7 +134,24 @@ public class NetworkExtensionsProducer implements ExtensionProducer {
} }
} }
private HPacket readPacket(DataInputStream dIn, int length) throws IOException {
final byte[] headerAndBody = new byte[length + 4];
int amountRead = 0;
while (amountRead < length)
amountRead += dIn.read(headerAndBody, 4 + amountRead, Math.min(dIn.available(), length - amountRead));
final HPacket packet = new HPacket(headerAndBody);
packet.fixLength();
return packet;
}
/**
* Retrieves the {@link ServerSocket#getLocalPort()} of {@link #serverSocket}.
*
* @return the port number to which {@link #serverSocket} is listening or -1 if the socket is not bound yet.
*/
public int getPort() { public int getPort() {
return serverSocket.getLocalPort(); return serverSocket.getLocalPort();
} }
} }