adicionar suporte para criação de conexões privadas, com uma interface para plugins que queiram usa-los e uma fabrica que faz a validação das conexões
This commit is contained in:
parent
7d9ca4ca71
commit
bbb1ceea23
|
|
@ -0,0 +1,138 @@
|
||||||
|
package pitiupi.net.tcp;
|
||||||
|
|
||||||
|
import pitiupi.net.Message;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class ConnectionFactory {
|
||||||
|
private static final int HANDSHAKE_TIMEOUT = 5000;
|
||||||
|
private static final int STANDARD_TIMEOUT = 300_000;
|
||||||
|
|
||||||
|
public static TcpConnection createConnection(InetAddress address, int port, String sender, String receiver, boolean isPrivate) {
|
||||||
|
try {
|
||||||
|
TcpConnection connection;
|
||||||
|
|
||||||
|
Socket socket = new Socket(address, port);
|
||||||
|
connection = new TcpConnection(socket);
|
||||||
|
connection.setSender(sender);
|
||||||
|
connection.setReceiver(receiver);
|
||||||
|
connection.setPrivate(isPrivate);
|
||||||
|
|
||||||
|
HandshakeMessage handshake = new HandshakeMessage(isPrivate, sender, receiver);
|
||||||
|
|
||||||
|
connection.send(handshake.toByteArray());
|
||||||
|
HandshakeMessage handshakeResponse = validateConnection(connection);
|
||||||
|
|
||||||
|
if (handshakeResponse == null) return null;
|
||||||
|
|
||||||
|
connection.setTimeout(STANDARD_TIMEOUT);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TcpConnection receiveConnection(Socket socket) {
|
||||||
|
try {
|
||||||
|
TcpConnection connection;
|
||||||
|
|
||||||
|
connection = new TcpConnection(socket);
|
||||||
|
|
||||||
|
HandshakeMessage handshake = validateConnection(connection);
|
||||||
|
|
||||||
|
if (handshake == null) return null;
|
||||||
|
|
||||||
|
connection.setSender(handshake.getSender());
|
||||||
|
connection.setReceiver(handshake.getReceiver());
|
||||||
|
connection.setPrivate(handshake.isPrivate());
|
||||||
|
|
||||||
|
connection.setTimeout(STANDARD_TIMEOUT);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HandshakeMessage validateConnection(TcpConnection connection) {
|
||||||
|
try {
|
||||||
|
connection.setTimeout(HANDSHAKE_TIMEOUT);
|
||||||
|
byte[] bytes = connection.receive();
|
||||||
|
|
||||||
|
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
|
||||||
|
ObjectInputStream ois = new ObjectInputStream(bis);
|
||||||
|
|
||||||
|
Object object = ois.readObject();
|
||||||
|
|
||||||
|
if (object instanceof Message message) {
|
||||||
|
if (SysInfoMessage.parseSystemInfo(message) != null) {
|
||||||
|
//TODO: Tratar erros
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message instanceof HandshakeMessage handshake) {
|
||||||
|
return (validateHandshake(handshake, connection)) ? handshake : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SysInfoMessage.sendSystemInfo(
|
||||||
|
connection,
|
||||||
|
SysInfoType.CONNECTION_REJECTED,
|
||||||
|
"The message was not from an allowed type."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException ex) {
|
||||||
|
SysInfoMessage.sendSystemInfo(
|
||||||
|
connection,
|
||||||
|
SysInfoType.CONNECTION_REJECTED,
|
||||||
|
"No handshake was received within the allowed time."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException | IOException ex) {
|
||||||
|
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
|
||||||
|
SysInfoMessage.sendSystemInfo(
|
||||||
|
connection,
|
||||||
|
SysInfoType.INTERNAL_ERROR,
|
||||||
|
"The server could not process the handshake."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validateHandshake(HandshakeMessage handshake, TcpConnection connection) {
|
||||||
|
String sender = handshake.getSender();
|
||||||
|
String receiver = handshake.getReceiver();
|
||||||
|
|
||||||
|
if (sender == null || sender.isBlank()) {
|
||||||
|
SysInfoMessage.sendSystemInfo(
|
||||||
|
connection,
|
||||||
|
SysInfoType.CONNECTION_REJECTED,
|
||||||
|
"Invalid Sender: " + sender + "."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (receiver == null || receiver.isBlank()) {
|
||||||
|
SysInfoMessage.sendSystemInfo(
|
||||||
|
connection,
|
||||||
|
SysInfoType.CONNECTION_REJECTED,
|
||||||
|
"Invalid Receiver: " + receiver + "."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package pitiupi.net.tcp;
|
||||||
|
|
||||||
|
import pitiupi.net.Message;
|
||||||
|
|
||||||
|
class HandshakeMessage extends Message {
|
||||||
|
private final boolean isPrivate;
|
||||||
|
private final String sender;
|
||||||
|
private final String receiver;
|
||||||
|
|
||||||
|
|
||||||
|
public HandshakeMessage(boolean isPrivate, String sender, String receiver) {
|
||||||
|
this.isPrivate = isPrivate;
|
||||||
|
this.sender = sender;
|
||||||
|
this.receiver = receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrivate() {
|
||||||
|
return isPrivate;
|
||||||
|
}
|
||||||
|
public String getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
public String getReceiver() {
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandshakeMessage createResponse() {
|
||||||
|
return new HandshakeMessage(isPrivate, receiver, sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package pitiupi.net.tcp;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representa uma conexão TCP persistente com um peer.
|
||||||
|
*
|
||||||
|
* <p>Encapsula o socket e os streams utilizados para enviar e receber
|
||||||
|
* mensagens. Também é responsável pelo framing das mensagens, permitindo
|
||||||
|
* que várias mensagens sejam transmitidas pela mesma conexão.</p>
|
||||||
|
*
|
||||||
|
* @author Gustavo
|
||||||
|
*/
|
||||||
|
public class TcpConnection {
|
||||||
|
private static final long TIMEOUT = 300_000; // 5 minutos
|
||||||
|
|
||||||
|
private final Socket socket;
|
||||||
|
private final DataInputStream input;
|
||||||
|
private final DataOutputStream output;
|
||||||
|
private final InetAddress address;
|
||||||
|
private String sender;
|
||||||
|
private String receiver;
|
||||||
|
|
||||||
|
private boolean isPrivate;
|
||||||
|
|
||||||
|
private volatile long lastActivity;
|
||||||
|
|
||||||
|
TcpConnection(Socket socket) throws IOException {
|
||||||
|
this.socket = socket;
|
||||||
|
this.input = new DataInputStream(socket.getInputStream());
|
||||||
|
this.output = new DataOutputStream(socket.getOutputStream());
|
||||||
|
this.address = socket.getInetAddress();
|
||||||
|
this.lastActivity = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void send(byte[] message) throws IOException {
|
||||||
|
output.writeInt(message.length);
|
||||||
|
output.write(message);
|
||||||
|
output.flush();
|
||||||
|
updateLastActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] receive() throws IOException {
|
||||||
|
int length = input.readInt();
|
||||||
|
|
||||||
|
if (length < 0) throw new IOException("Invalid message length.");
|
||||||
|
|
||||||
|
byte[] message = input.readNBytes(length);
|
||||||
|
|
||||||
|
if (message.length != length) {
|
||||||
|
throw new IOException("Connection closed before receiving the complete message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLastActivity();
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return socket.isClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() throws IOException {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InetAddress getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLastActivity() {
|
||||||
|
lastActivity = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTimeout(int timeout) throws SocketException {
|
||||||
|
socket.setSoTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasTimedOut() {
|
||||||
|
return System.currentTimeMillis() - lastActivity >= TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReceiver() {
|
||||||
|
return receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrivate() {
|
||||||
|
return isPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSender(String sender) {
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReceiver(String receiver) {
|
||||||
|
this.receiver = receiver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPrivate(boolean aPrivate) {
|
||||||
|
isPrivate = aPrivate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package pitiupi.plugin;
|
||||||
|
|
||||||
|
import pitiupi.net.tcp.TcpConnection;
|
||||||
|
|
||||||
|
public interface PrivateConnectionPlugin extends Plugin {
|
||||||
|
public void receivePrivateConnection(TcpConnection connection);
|
||||||
|
public void receiveBytes(byte[] bytes);
|
||||||
|
public void onSocketClosed(TcpConnection connection);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue