145 lines
6.2 KiB
Java
145 lines
6.2 KiB
Java
package eu.m724.autopeerer.client;
|
|
|
|
import eu.m724.autopeerer.client.bird.BirdLive;
|
|
import eu.m724.autopeerer.client.bird.BirdSession;
|
|
import eu.m724.autopeerer.client.wireguard.WireGuardKeys;
|
|
import eu.m724.autopeerer.client.wireguard.WireGuardLive;
|
|
import eu.m724.autopeerer.client.wireguard.WireGuardSession;
|
|
import eu.m724.autopeerer.common.AddressTools;
|
|
import eu.m724.autopeerer.common.packet.Packet;
|
|
import eu.m724.autopeerer.common.packet.Packets;
|
|
import eu.m724.autopeerer.common.packet.s2c.PingRequestPacket;
|
|
import eu.m724.autopeerer.common.packet.s2c.SessionRequestPacket;
|
|
import eu.m724.autopeerer.common.packet.c2s.PingResponsePacket;
|
|
import eu.m724.autopeerer.common.packet.c2s.SessionResponsePacket;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.net.UnknownHostException;
|
|
import java.nio.BufferUnderflowException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.file.FileAlreadyExistsException;
|
|
import java.util.Base64;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
|
import java.util.function.Consumer;
|
|
|
|
public class PacketHandler {
|
|
Consumer<ByteBuffer> sender;
|
|
|
|
private final WireGuardLive wireGuardLive;
|
|
private final BirdLive birdLive;
|
|
private final String serverLinkLocal;
|
|
|
|
public PacketHandler(WireGuardLive wireGuardLive, BirdLive birdLive, String serverLinkLocal) {
|
|
this.wireGuardLive = wireGuardLive;
|
|
this.birdLive = birdLive;
|
|
this.serverLinkLocal = serverLinkLocal;
|
|
}
|
|
|
|
void handle(ByteBuffer bytes) {
|
|
Packet<?> p;
|
|
try {
|
|
p = Packets.parseClient(bytes);
|
|
} catch (BufferUnderflowException e) {
|
|
bytes.rewind();
|
|
byte[] bytez = new byte[bytes.remaining()];
|
|
bytes.get(bytez);
|
|
|
|
System.err.println("Received too short packet");
|
|
System.err.println("> Length: " + bytez.length);
|
|
System.err.println("> Packet: " + Base64.getEncoder().encodeToString(bytez));
|
|
|
|
return;
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
|
|
if (p instanceof PingRequestPacket packet) {
|
|
handlePingRequest(packet);
|
|
} else if (p instanceof SessionRequestPacket packet) {
|
|
handleSessionRequest(packet);
|
|
}
|
|
}
|
|
|
|
private void handlePingRequest(PingRequestPacket packet) {
|
|
System.out.printf("Ping request #%d to %s\n", packet.requestId, packet.target.getHostAddress());
|
|
CompletableFuture.runAsync(() -> {
|
|
float average = -1, meanDeviation = -1;
|
|
var status = PingResponsePacket.PingResponseStatus.ERROR;
|
|
|
|
try {
|
|
Process process = Runtime.getRuntime().exec(new String[] { "ping", "-3Ac", "10", "-W", "1", packet.target.getHostAddress() });
|
|
|
|
try (BufferedReader reader = process.inputReader()) {
|
|
for (String l : reader.lines().toList()) {
|
|
boolean end = l.startsWith("rtt");
|
|
|
|
if (l.startsWith("PING")) {
|
|
status = PingResponsePacket.PingResponseStatus.UNREACHABLE;
|
|
}
|
|
|
|
if (end) {
|
|
String[] results = l.split(" ")[3].split("/");
|
|
average = Float.parseFloat(results[1]);
|
|
meanDeviation = Float.parseFloat(results[3]);
|
|
status = PingResponsePacket.PingResponseStatus.OK;
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
System.err.println("Error pinging");
|
|
e.printStackTrace();
|
|
}
|
|
|
|
System.out.printf("Ping request #%d to %s - %s avg %.3f / mdev %.3f ms\n", packet.requestId, packet.target.getHostAddress(), status, average, meanDeviation);
|
|
Packets.send(new PingResponsePacket(packet.requestId, status, average, meanDeviation), sender);
|
|
});
|
|
}
|
|
|
|
private void handleSessionRequest(SessionRequestPacket packet) {
|
|
// validate endpoint
|
|
var resolved = false;
|
|
try {
|
|
if (ClientPrefs.ipv6Supported()) {
|
|
var res = AddressTools.resolve(packet.endpointHost, true);
|
|
resolved = res != null;
|
|
}
|
|
if (!resolved && ClientPrefs.ipv4Supported()) {
|
|
var res = AddressTools.resolve(packet.endpointHost, true);
|
|
resolved = res != null;
|
|
}
|
|
} catch (UnknownHostException | AddressTools.MultipleRecordsException ignored) { }
|
|
|
|
if (!resolved) {
|
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.ERROR_RESOLVE, -1, null), sender);
|
|
return;
|
|
}
|
|
|
|
var privateKey = WireGuardKeys.generatePrivateKey();
|
|
var publicKey = WireGuardKeys.derivePublicKey(privateKey);
|
|
|
|
int port = ThreadLocalRandom.current().nextInt() & 0xFFFF;
|
|
if (packet.asn > 4242420000L) {
|
|
port = (int) (packet.asn % 10000);
|
|
}
|
|
|
|
var wireGuardSession = new WireGuardSession(port, privateKey, serverLinkLocal, packet.linkLocal.getHostAddress(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey);
|
|
var birdSession = new BirdSession(packet.asn, packet.linkLocal.getHostAddress());
|
|
|
|
try {
|
|
wireGuardLive.saveSession(packet.asn, wireGuardSession);
|
|
birdLive.saveSession(birdSession);
|
|
|
|
System.out.printf("Created session AS%d to %s\n", packet.asn, packet.endpointHost);
|
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.OK, wireGuardSession.listenPort(), publicKey), sender);
|
|
} catch (FileAlreadyExistsException e) {
|
|
System.err.println("Tried to create a session which already exists: AS" + packet.asn);
|
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.ERROR_DUPLICATE, -1, null), sender);
|
|
} catch (IOException e) {
|
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.ERROR_OTHER, -1, null), sender);
|
|
System.err.println("Failed to save session");
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
}
|