From 0f92f5bcbe69ef747810a5b48b342ae34e426162 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Tue, 10 Dec 2024 16:07:45 +0100 Subject: [PATCH] wip --- pom.xml | 36 ++++++++ .../autopeerer/client/MyWebsocketClient.java | 12 ++- .../m724/autopeerer/client/PacketHandler.java | 90 +++++++++++-------- .../client/wireguard/WireGuardKeys.java | 34 +++++++ .../client/wireguard/WireGuardSession.java | 25 ++++++ .../eu/m724/autopeerer/packet/Packets.java | 11 ++- .../packet/client/BgpRequestPacket.java | 43 +++++++++ .../{ => client}/PingRequestPacket.java | 4 +- .../packet/client/VpnRequestPacket.java | 80 +++++++++++++++++ .../packet/server/BgpResponsePacket.java | 37 ++++++++ .../{ => server}/PingResponsePacket.java | 4 +- .../packet/server/VpnResponsePacket.java | 48 ++++++++++ 12 files changed, 378 insertions(+), 46 deletions(-) create mode 100644 src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardKeys.java create mode 100644 src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardSession.java create mode 100644 src/main/java/eu/m724/autopeerer/packet/client/BgpRequestPacket.java rename src/main/java/eu/m724/autopeerer/packet/{ => client}/PingRequestPacket.java (93%) create mode 100644 src/main/java/eu/m724/autopeerer/packet/client/VpnRequestPacket.java create mode 100644 src/main/java/eu/m724/autopeerer/packet/server/BgpResponsePacket.java rename src/main/java/eu/m724/autopeerer/packet/{ => server}/PingResponsePacket.java (95%) create mode 100644 src/main/java/eu/m724/autopeerer/packet/server/VpnResponsePacket.java diff --git a/pom.xml b/pom.xml index b19f677..ed4b8d4 100644 --- a/pom.xml +++ b/pom.xml @@ -20,5 +20,41 @@ Java-WebSocket 1.5.7 + + com.github.seancfoley + ipaddress + 5.5.1 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + false + true + + + ** + + module-info.class + META-INF/ + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/eu/m724/autopeerer/client/MyWebsocketClient.java b/src/main/java/eu/m724/autopeerer/client/MyWebsocketClient.java index 93e69f5..bc94612 100644 --- a/src/main/java/eu/m724/autopeerer/client/MyWebsocketClient.java +++ b/src/main/java/eu/m724/autopeerer/client/MyWebsocketClient.java @@ -1,7 +1,10 @@ package eu.m724.autopeerer.client; import eu.m724.autopeerer.packet.Packets; -import eu.m724.autopeerer.packet.PingRequestPacket; +import eu.m724.autopeerer.packet.client.PingRequestPacket; +import eu.m724.autopeerer.packet.client.VpnRequestPacket; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.ipv6.IPv6Address; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; @@ -32,9 +35,10 @@ public class MyWebsocketClient extends WebSocketClient { System.out.printf("Connected in %.3f ms\n", connectTime); try { - for (int i=0; i<10; i++) { - send(Packets.compose(new PingRequestPacket((short)i, InetAddress.getByName("1.1.1." + i)))); - } + send(Packets.compose(new PingRequestPacket((short)1, InetAddress.getByName("1.1.1.3")))); + send(Packets.compose(new PingRequestPacket((short)2, InetAddress.getByName("1.1.1.1")))); + + send(Packets.compose(new VpnRequestPacket((short)1, (IPv6Address) new IPAddressString("fefe::fefe").getAddress(), "sAt8JSXW4leihcAAdsghsfgFWkO5stBZJm87PGLZFXY=", "example.com", 6823))); } catch (UnknownHostException e) { throw new RuntimeException(e); } diff --git a/src/main/java/eu/m724/autopeerer/client/PacketHandler.java b/src/main/java/eu/m724/autopeerer/client/PacketHandler.java index 96addd1..6ed08cd 100644 --- a/src/main/java/eu/m724/autopeerer/client/PacketHandler.java +++ b/src/main/java/eu/m724/autopeerer/client/PacketHandler.java @@ -1,9 +1,10 @@ package eu.m724.autopeerer.client; -import eu.m724.autopeerer.packet.Packet; -import eu.m724.autopeerer.packet.Packets; -import eu.m724.autopeerer.packet.PingRequestPacket; -import eu.m724.autopeerer.packet.PingResponsePacket; +import eu.m724.autopeerer.client.wireguard.WireGuardSession; +import eu.m724.autopeerer.packet.*; +import eu.m724.autopeerer.packet.client.PingRequestPacket; +import eu.m724.autopeerer.packet.client.VpnRequestPacket; +import eu.m724.autopeerer.packet.server.PingResponsePacket; import java.io.BufferedReader; import java.io.IOException; @@ -34,41 +35,52 @@ public class PacketHandler { throw new RuntimeException(e); } - if (p instanceof 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", packet.target.getHostAddress() }); - - try (BufferedReader reader = process.inputReader()) { - for (String l : reader.lines().toList()) { - boolean error = l.startsWith("From"); // indicates an error - boolean end = l.startsWith("rtt"); - - if (error) { - status = PingResponsePacket.PingResponseStatus.UNREACHABLE; - break; - } - - 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); - sender.accept(new PingResponsePacket(packet.requestId, status, average, meanDeviation).serialize()); - }); + if (p instanceof PingRequestPacket packet) { + handlePingRequest(packet); + } else if (p instanceof VpnRequestPacket packet) { + handleVpnRequest(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", packet.target.getHostAddress() }); + + try (BufferedReader reader = process.inputReader()) { + for (String l : reader.lines().toList()) { + boolean error = l.startsWith("From"); // indicates an error + boolean end = l.startsWith("rtt"); + + if (error) { + status = PingResponsePacket.PingResponseStatus.UNREACHABLE; + break; + } + + 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); + sender.accept(new PingResponsePacket(packet.requestId, status, average, meanDeviation).serialize()); + }); + } + + private void handleVpnRequest(VpnRequestPacket packet) { + var session = new WireGuardSession(12345, "serverpoecjteta", "fefe:fefe::fefe", packet.linkLocal.toCompressedString(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey); + System.err.println(session.config()); + } } diff --git a/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardKeys.java b/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardKeys.java new file mode 100644 index 0000000..01ef72a --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardKeys.java @@ -0,0 +1,34 @@ +package eu.m724.autopeerer.client.wireguard; + +import java.io.IOException; + +public class WireGuardKeys { + public static String generatePrivateKey() { + try { + Process process = Runtime.getRuntime().exec("wg genkey"); + process.waitFor(); + return process.inputReader().readLine(); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * Derive a WireGuard public key from a private key + * @param privateKey base64 encoded private key + * @return base64 encoded public key or null if invalid private key + */ + public static String derivePublicKey(String privateKey) { + try { + Process process = Runtime.getRuntime().exec("wg genkey"); + process.outputWriter().write(privateKey); + + int code = process.waitFor(); + if (code != 0) return null; + + return process.inputReader().readLine(); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardSession.java b/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardSession.java new file mode 100644 index 0000000..fb3d179 --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/client/wireguard/WireGuardSession.java @@ -0,0 +1,25 @@ +package eu.m724.autopeerer.client.wireguard; + +public record WireGuardSession( + int listenPort, + String serverPrivateKey, + String localLinkLocal, + String clientLinkLocal, + String endpoint, + String clientPublicKey +) { + public String config() { + return """ + [Interface] + ListenPort = %d + PrivateKey = %s + PostUp = /sbin/ip addr add dev %%i %s peer %s + Table = off + + [Peer] + Endpoint = %s + PublicKey = %s + AllowedIPs = ::/0""" + .formatted(listenPort, serverPrivateKey, localLinkLocal, clientLinkLocal, endpoint, clientPublicKey); + } +} diff --git a/src/main/java/eu/m724/autopeerer/packet/Packets.java b/src/main/java/eu/m724/autopeerer/packet/Packets.java index 31b9eaa..3fddb55 100644 --- a/src/main/java/eu/m724/autopeerer/packet/Packets.java +++ b/src/main/java/eu/m724/autopeerer/packet/Packets.java @@ -1,5 +1,10 @@ package eu.m724.autopeerer.packet; +import eu.m724.autopeerer.packet.client.PingRequestPacket; +import eu.m724.autopeerer.packet.client.VpnRequestPacket; +import eu.m724.autopeerer.packet.server.PingResponsePacket; +import eu.m724.autopeerer.packet.server.VpnResponsePacket; + import java.nio.ByteBuffer; public class Packets { @@ -10,6 +15,8 @@ public class Packets { if (id == 1) { packet = PingRequestPacket.deserialize(buffer); + } else if (id == 2) { + packet = VpnRequestPacket.deserialize(buffer); } return packet; @@ -21,7 +28,9 @@ public class Packets { Packet packet = null; if (id == 1) { - PingResponsePacket.deserialize(buffer); + packet = PingResponsePacket.deserialize(buffer); + } else if (id == 2) { + packet = VpnResponsePacket.deserialize(buffer); } return packet; diff --git a/src/main/java/eu/m724/autopeerer/packet/client/BgpRequestPacket.java b/src/main/java/eu/m724/autopeerer/packet/client/BgpRequestPacket.java new file mode 100644 index 0000000..9481e09 --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/packet/client/BgpRequestPacket.java @@ -0,0 +1,43 @@ +package eu.m724.autopeerer.packet.client; + +import eu.m724.autopeerer.packet.Packet; +import inet.ipaddr.ipv6.IPv6Address; + +import java.nio.ByteBuffer; + +public class BgpRequestPacket implements Packet { + public final short id; + public final long asn; + public final IPv6Address linkLocal; + + public BgpRequestPacket(short id, long asn, IPv6Address linkLocal) { + this.id = id; + this.asn = asn; + this.linkLocal = linkLocal; + } + + @Override + public byte getId() { + return 3; + } + + @Override + public ByteBuffer serialize() { + ByteBuffer buffer = ByteBuffer.allocate(22); + buffer.putShort(id); + buffer.putInt((int) (asn & 0xFFFFFFFFL)); + buffer.put(linkLocal.getBytes()); + return buffer; + } + + public static BgpRequestPacket deserialize(ByteBuffer buffer) { + var id = buffer.getShort(); + var asn = Integer.toUnsignedLong(buffer.getInt()); + + var ip = new byte[16]; + buffer.get(ip); + var linkLocal = new IPv6Address(ip); + + return new BgpRequestPacket(id, asn, linkLocal); + } +} diff --git a/src/main/java/eu/m724/autopeerer/packet/PingRequestPacket.java b/src/main/java/eu/m724/autopeerer/packet/client/PingRequestPacket.java similarity index 93% rename from src/main/java/eu/m724/autopeerer/packet/PingRequestPacket.java rename to src/main/java/eu/m724/autopeerer/packet/client/PingRequestPacket.java index 5c29024..c4c337f 100644 --- a/src/main/java/eu/m724/autopeerer/packet/PingRequestPacket.java +++ b/src/main/java/eu/m724/autopeerer/packet/client/PingRequestPacket.java @@ -1,4 +1,6 @@ -package eu.m724.autopeerer.packet; +package eu.m724.autopeerer.packet.client; + +import eu.m724.autopeerer.packet.Packet; import java.net.Inet6Address; import java.net.InetAddress; diff --git a/src/main/java/eu/m724/autopeerer/packet/client/VpnRequestPacket.java b/src/main/java/eu/m724/autopeerer/packet/client/VpnRequestPacket.java new file mode 100644 index 0000000..4023668 --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/packet/client/VpnRequestPacket.java @@ -0,0 +1,80 @@ +package eu.m724.autopeerer.packet.client; + +import eu.m724.autopeerer.packet.Packet; +import inet.ipaddr.HostName; +import inet.ipaddr.HostNameException; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.ipv6.IPv6Address; + +import java.net.IDN; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class VpnRequestPacket implements Packet { + public final short connectionId; + public final IPv6Address linkLocal; + public final String publicKey; + public final String endpointHost; + public final int endpointPort; + + public VpnRequestPacket(short connectionId, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort) { + this.connectionId = connectionId; + + this.linkLocal = linkLocal; + assert new IPAddressString("fe80::/10").getAddress().contains(linkLocal); + + this.publicKey = publicKey; + assert Base64.getDecoder().decode(publicKey).length == 32; + + this.endpointHost = IDN.toASCII(endpointHost, IDN.ALLOW_UNASSIGNED); + try { + new HostName(endpointHost).validate(); + } catch (HostNameException e) { + throw new RuntimeException(e); + } + + this.endpointPort = endpointPort; + assert endpointPort > 0 && endpointPort < 65536; + } + + @Override + public byte getId() { + return 2; + } + + public static VpnRequestPacket deserialize(ByteBuffer buffer) throws Exception { + var id = buffer.getShort(); + + var ll = new byte[16]; + buffer.get(ll); + var linkLocal = new IPv6Address(ll); + + var pk = new byte[32]; + buffer.get(pk); + var publicKey = Base64.getEncoder().encodeToString(pk); + + var endpointPort = buffer.getShort(); + + var epl = buffer.get() & 0xFF; + var ep = new byte[epl]; + buffer.get(ep); + var endpointHost = new String(ep, StandardCharsets.US_ASCII); + + return new VpnRequestPacket(id, linkLocal, publicKey, endpointHost, endpointPort); + } + + @Override + public ByteBuffer serialize() { + var buffer = ByteBuffer.allocate(53 + endpointHost.length()); + + buffer.putShort(connectionId); // 2b + buffer.put(linkLocal.getBytes()); // 16b + buffer.put(Base64.getDecoder().decode(publicKey)); // 32b + buffer.putShort((short)endpointPort); // 2b + buffer.put((byte) endpointHost.length()); // 1b + buffer.put(endpointHost.getBytes(StandardCharsets.US_ASCII)); + + return buffer; + } +} diff --git a/src/main/java/eu/m724/autopeerer/packet/server/BgpResponsePacket.java b/src/main/java/eu/m724/autopeerer/packet/server/BgpResponsePacket.java new file mode 100644 index 0000000..5975ab2 --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/packet/server/BgpResponsePacket.java @@ -0,0 +1,37 @@ +package eu.m724.autopeerer.packet.server; + +import eu.m724.autopeerer.packet.Packet; + +import java.nio.ByteBuffer; + +public class BgpResponsePacket implements Packet { + public final short id; + public final boolean success; + + public BgpResponsePacket(short id, boolean success) { + this.id = id; + this.success = success; + } + + @Override + public byte getId() { + return 3; + } + + @Override + public ByteBuffer serialize() { + ByteBuffer buffer = ByteBuffer.allocate(3); + + buffer.putShort(id); + buffer.put((byte) (success ? 1 : 0)); + + return buffer; + } + + public static BgpResponsePacket deserialize(ByteBuffer buffer) { + var id = buffer.getShort(); + var success = buffer.get() == 1; + + return new BgpResponsePacket(id, success); + } +} diff --git a/src/main/java/eu/m724/autopeerer/packet/PingResponsePacket.java b/src/main/java/eu/m724/autopeerer/packet/server/PingResponsePacket.java similarity index 95% rename from src/main/java/eu/m724/autopeerer/packet/PingResponsePacket.java rename to src/main/java/eu/m724/autopeerer/packet/server/PingResponsePacket.java index a27c000..478f311 100644 --- a/src/main/java/eu/m724/autopeerer/packet/PingResponsePacket.java +++ b/src/main/java/eu/m724/autopeerer/packet/server/PingResponsePacket.java @@ -1,4 +1,6 @@ -package eu.m724.autopeerer.packet; +package eu.m724.autopeerer.packet.server; + +import eu.m724.autopeerer.packet.Packet; import java.nio.ByteBuffer; diff --git a/src/main/java/eu/m724/autopeerer/packet/server/VpnResponsePacket.java b/src/main/java/eu/m724/autopeerer/packet/server/VpnResponsePacket.java new file mode 100644 index 0000000..2901db9 --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/packet/server/VpnResponsePacket.java @@ -0,0 +1,48 @@ +package eu.m724.autopeerer.packet.server; + +import eu.m724.autopeerer.packet.Packet; + +import java.nio.ByteBuffer; +import java.util.Base64; + +public class VpnResponsePacket implements Packet { + public final short connectionId; + public final boolean success; + public final int port; + public final String publicKey; + + public VpnResponsePacket(short connectionId, boolean success, int port, String publicKey) { + this.connectionId = connectionId; + this.success = success; + this.port = port; + this.publicKey = publicKey; + } + + @Override + public byte getId() { + return 2; + } + + public static VpnResponsePacket deserialize(ByteBuffer buffer) throws Exception { + var id = buffer.getShort(); + var port = buffer.getShort() & 0xFFFF; + var success = port != 0; + + var pkb = new byte[32]; + buffer.get(pkb); + var publicKey = Base64.getEncoder().encodeToString(pkb); + + return new VpnResponsePacket(id, success, port, publicKey); + } + + @Override + public ByteBuffer serialize() { + var buffer = ByteBuffer.allocate(35); + + buffer.putShort(connectionId); // 2b + buffer.put((byte) (success ? 1 : 0)); + buffer.put(Base64.getDecoder().decode(publicKey)); + + return buffer; + } +}