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:
GustavoHMDS 2026-09-08 01:24:35 -03:00
parent 7d9ca4ca71
commit bbb1ceea23
4 changed files with 288 additions and 0 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}