From 1b3da520e7d546677dbfcf96e47cbca037b6622a Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Thu, 26 Dec 2024 15:13:31 +0100 Subject: [PATCH] com --- README.md | 10 +++- pom.xml | 5 -- .../m724/autopeerer/client/ClientPrefs.java | 45 +++++++++++++++++ .../java/eu/m724/autopeerer/client/Main.java | 2 + .../m724/autopeerer/client/PacketHandler.java | 29 +++++++++-- .../m724/autopeerer/common/AddressTools.java | 50 +++++++++++++++++++ .../packet/c2s/SessionResponsePacket.java | 4 +- .../packet/s2c/SessionRequestPacket.java | 22 +++----- .../m724/autopeerer/server/ClientState.java | 10 ++-- .../java/eu/m724/autopeerer/server/Main.java | 14 +++--- .../m724/autopeerer/server/MyHttpHandler.java | 9 ++-- .../server/ServerConfiguration.java | 10 +++- src/main/resources/client.properties | 10 +++- src/main/resources/nodes/node1.properties | 3 ++ 14 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 src/main/java/eu/m724/autopeerer/client/ClientPrefs.java create mode 100644 src/main/java/eu/m724/autopeerer/common/AddressTools.java create mode 100644 src/main/resources/nodes/node1.properties diff --git a/README.md b/README.md index b5d1886..626a39d 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,12 @@ 1. Get the `-server.jar` or `-client.jar` and make a new directory for it 2. Run the JAR, then stop it 3. Configure in `config` -4. Now run the JAR. Done! \ No newline at end of file +4. Now run the JAR. Done! + +# Client config +- `remote` the autopeerer server websocket URL +- `wireguard.directory` directory for WireGuard config files +- `bird.directory` directory for BIRD config files +- `link-local` the link local for peering +- `stack.ipv4` IPv4 availability. Values: `no`, `yes`, `restricted` +- `stack.ipv6` same but for IPv6 \ No newline at end of file diff --git a/pom.xml b/pom.xml index db1d5c3..fef70cb 100644 --- a/pom.xml +++ b/pom.xml @@ -20,11 +20,6 @@ Java-WebSocket 1.5.7 - - com.github.seancfoley - ipaddress - 5.5.1 - org.json json diff --git a/src/main/java/eu/m724/autopeerer/client/ClientPrefs.java b/src/main/java/eu/m724/autopeerer/client/ClientPrefs.java new file mode 100644 index 0000000..3072aef --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/client/ClientPrefs.java @@ -0,0 +1,45 @@ +package eu.m724.autopeerer.client; + +public class ClientPrefs { + private static ClientPrefs INSTANCE; + + private final boolean ipv4, restricted4, ipv6, restricted6; + + private ClientPrefs( boolean ipv4, boolean restricted4, boolean ipv6, boolean restricted6) { + this.ipv4 = ipv4; + this.restricted4 = restricted4; + this.ipv6 = ipv6; + this.restricted6 = restricted6; + } + + public static void init(ClientConfiguration configuration) { + var ipv4 = configuration.getString("stack.ipv4"); + var ipv6 = configuration.getString("stack.ipv6"); + + INSTANCE = new ClientPrefs( + ipv4.equalsIgnoreCase("yes") || ipv4.equalsIgnoreCase("restricted"), + ipv4.equalsIgnoreCase("restricted"), + ipv6.equalsIgnoreCase("yes") || ipv6.equalsIgnoreCase("restricted"), + ipv6.equalsIgnoreCase("restricted") + ); + } + + /* */ + + public static boolean ipv4Supported() { + return INSTANCE.ipv4; + } + + public static boolean ipv6Supported() { + return INSTANCE.ipv6; + } + + public static boolean ipv4Restricted() { + return INSTANCE.restricted4; + } + + public static boolean ipv6Restricted() { + return INSTANCE.restricted6; + } + +} diff --git a/src/main/java/eu/m724/autopeerer/client/Main.java b/src/main/java/eu/m724/autopeerer/client/Main.java index 772f439..ab2cfa9 100644 --- a/src/main/java/eu/m724/autopeerer/client/Main.java +++ b/src/main/java/eu/m724/autopeerer/client/Main.java @@ -23,6 +23,8 @@ public class Main { return; } + ClientPrefs.init(config); + URI serverUri = URI.create(config.getString("remote")); var wireGuardLive = new WireGuardLive(new File(config.getString("wireguard.directory"))); diff --git a/src/main/java/eu/m724/autopeerer/client/PacketHandler.java b/src/main/java/eu/m724/autopeerer/client/PacketHandler.java index a5080ad..28632f9 100644 --- a/src/main/java/eu/m724/autopeerer/client/PacketHandler.java +++ b/src/main/java/eu/m724/autopeerer/client/PacketHandler.java @@ -5,6 +5,7 @@ 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; @@ -14,6 +15,7 @@ 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; @@ -96,6 +98,24 @@ public class PacketHandler { } 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); @@ -104,8 +124,8 @@ public class PacketHandler { port = (int) (packet.asn % 10000); } - var wireGuardSession = new WireGuardSession(port, privateKey, serverLinkLocal, packet.linkLocal.toCompressedString(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey); - var birdSession = new BirdSession(packet.asn, packet.linkLocal.toCompressedString()); + 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); @@ -115,9 +135,10 @@ public class PacketHandler { 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.DUPLICATE, -1, null), sender); + 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, -1, null), sender); + Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.ERROR_OTHER, -1, null), sender); + System.err.println("Failed to save session"); throw new RuntimeException(e); } } diff --git a/src/main/java/eu/m724/autopeerer/common/AddressTools.java b/src/main/java/eu/m724/autopeerer/common/AddressTools.java new file mode 100644 index 0000000..e86a89e --- /dev/null +++ b/src/main/java/eu/m724/autopeerer/common/AddressTools.java @@ -0,0 +1,50 @@ +package eu.m724.autopeerer.common; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; + +public class AddressTools { + private static boolean isIp(InetAddress address, boolean ipv6) { + return ipv6 ? address instanceof Inet6Address : address instanceof Inet4Address; + } + + /** + * Checks if address is link local + * @param address the address + * @return whether it's IPv6 and link local + */ + public static boolean isLinkLocal(InetAddress address) { + if (isIp(address, true)) return false; + var bytes = address.getAddress(); + + return bytes[0] == -2 && bytes[1] >= 0 && bytes[1] <= 63; + } + + /** + * Resolve a host to IPv4 or IPv6 + * + * @param host the host to resolve + * @param ipv6 whether to resolve IPv6 or not (IPv4) + * @return The resolved IP or null + * @throws UnknownHostException if host is invalid + * @throws MultipleRecordsException if host has multiple records + */ + public static InetAddress resolve(String host, boolean ipv6) throws UnknownHostException, MultipleRecordsException { + var addr = Arrays.stream(InetAddress.getAllByName(host)) + .filter(address -> isIp(address, ipv6)) + .toList(); + + if (addr.size() > 1) + throw new MultipleRecordsException(); + + return addr.get(0); + } + + /** + * If a host has multiple A or AAAA records, and it's impossible to pick one + */ + public static class MultipleRecordsException extends Exception {} +} diff --git a/src/main/java/eu/m724/autopeerer/common/packet/c2s/SessionResponsePacket.java b/src/main/java/eu/m724/autopeerer/common/packet/c2s/SessionResponsePacket.java index 22146e6..ec93359 100644 --- a/src/main/java/eu/m724/autopeerer/common/packet/c2s/SessionResponsePacket.java +++ b/src/main/java/eu/m724/autopeerer/common/packet/c2s/SessionResponsePacket.java @@ -23,7 +23,7 @@ public class SessionResponsePacket implements Packet { return 2; } - public static SessionResponsePacket deserialize(ByteBuffer buffer) throws Exception { + public static SessionResponsePacket deserialize(ByteBuffer buffer) { var asn = Integer.toUnsignedLong(buffer.getInt()); var result = SessionResult.values()[buffer.get()]; @@ -57,6 +57,6 @@ public class SessionResponsePacket implements Packet { } public enum SessionResult { - OK, ERROR, DUPLICATE + OK, ERROR_DUPLICATE, ERROR_RESOLVE, ERROR_OTHER } } diff --git a/src/main/java/eu/m724/autopeerer/common/packet/s2c/SessionRequestPacket.java b/src/main/java/eu/m724/autopeerer/common/packet/s2c/SessionRequestPacket.java index 361965f..90f6489 100644 --- a/src/main/java/eu/m724/autopeerer/common/packet/s2c/SessionRequestPacket.java +++ b/src/main/java/eu/m724/autopeerer/common/packet/s2c/SessionRequestPacket.java @@ -1,39 +1,33 @@ package eu.m724.autopeerer.common.packet.s2c; +import eu.m724.autopeerer.common.AddressTools; import eu.m724.autopeerer.common.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.net.InetAddress; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Base64; public class SessionRequestPacket implements Packet { public final long asn; - public final IPv6Address linkLocal; + public final InetAddress linkLocal; public final String publicKey; public final String endpointHost; public final int endpointPort; - public SessionRequestPacket(long asn, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort) { + public SessionRequestPacket(long asn, InetAddress linkLocal, String publicKey, String endpointHost, int endpointPort) { this.asn = asn; assert asn < 0xFFFFFFFFL; this.linkLocal = linkLocal; - assert new IPAddressString("fe80::/10").getAddress().contains(linkLocal); + assert AddressTools.isLinkLocal(linkLocal); this.publicKey = publicKey; assert Base64.getDecoder().decode(publicKey).length == 32; + // testing is done at another point 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; @@ -49,7 +43,7 @@ public class SessionRequestPacket implements Packet { var ll = new byte[16]; buffer.get(ll); - var linkLocal = new IPv6Address(ll); + var linkLocal = InetAddress.getByAddress(ll); var pk = new byte[32]; buffer.get(pk); @@ -70,7 +64,7 @@ public class SessionRequestPacket implements Packet { var buffer = ByteBuffer.allocate(55 + endpointHost.length()); buffer.putInt((int) asn); - buffer.put(linkLocal.getBytes()); // 16b + buffer.put(linkLocal.getAddress()); // 16b buffer.put(Base64.getDecoder().decode(publicKey)); // 32b buffer.putShort((short) endpointPort); // 2b buffer.put((byte) endpointHost.length()); // 1b diff --git a/src/main/java/eu/m724/autopeerer/server/ClientState.java b/src/main/java/eu/m724/autopeerer/server/ClientState.java index f04fcaf..3b0647e 100644 --- a/src/main/java/eu/m724/autopeerer/server/ClientState.java +++ b/src/main/java/eu/m724/autopeerer/server/ClientState.java @@ -6,15 +6,12 @@ import eu.m724.autopeerer.common.packet.c2s.PingResponsePacket; import eu.m724.autopeerer.common.packet.c2s.SessionResponsePacket; import eu.m724.autopeerer.common.packet.s2c.PingRequestPacket; import eu.m724.autopeerer.common.packet.s2c.SessionRequestPacket; -import inet.ipaddr.ipv6.IPv6Address; import org.java_websocket.WebSocket; import java.net.InetAddress; -import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Consumer; @@ -52,7 +49,7 @@ public class ClientState { return future; } - CompletableFuture session(long asn, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort) { + CompletableFuture session(long asn, InetAddress linkLocal, String publicKey, String endpointHost, int endpointPort) { var future = new CompletableFuture(); sessionConsumers.put(asn, future::complete); @@ -66,9 +63,8 @@ public class ClientState { /* Packet functions */ - private Map> pingConsumers = new HashMap<>(); - private Map> sessionConsumers = new HashMap<>(); - + private final Map> pingConsumers = new HashMap<>(); + private final Map> sessionConsumers = new HashMap<>(); void onPacketReceived(Packet p) { if (p instanceof PingResponsePacket packet) { diff --git a/src/main/java/eu/m724/autopeerer/server/Main.java b/src/main/java/eu/m724/autopeerer/server/Main.java index 9c28776..dae011b 100644 --- a/src/main/java/eu/m724/autopeerer/server/Main.java +++ b/src/main/java/eu/m724/autopeerer/server/Main.java @@ -8,17 +8,11 @@ import java.util.Set; public class Main { public static void main(String[] args) throws IOException { - System.out.println("Hello world!"); - var config = new ServerConfiguration(); Set nodes; try { config.load(); nodes = config.loadNodes(); - if (nodes.isEmpty()) - System.err.println("Loaded 0 nodes. No client will be able to connect."); - else - System.out.printf("Loaded %d nodes.\n", nodes.size()); } catch (IOException e) { throw new RuntimeException("Failed to load configuration", e); } @@ -28,7 +22,15 @@ public class Main { return; } + if (nodes.isEmpty()) + System.err.println("Loaded 0 nodes. No client will be able to connect."); + else + System.out.printf("Loaded %d nodes.\n", nodes.size()); + + + var packetHandler = new PacketHandler(nodes); + var server = new MyWebsocketServer( new InetSocketAddress(config.getString("socket.address"), config.getInt("socket.port")), packetHandler diff --git a/src/main/java/eu/m724/autopeerer/server/MyHttpHandler.java b/src/main/java/eu/m724/autopeerer/server/MyHttpHandler.java index 86b738d..bda1e91 100644 --- a/src/main/java/eu/m724/autopeerer/server/MyHttpHandler.java +++ b/src/main/java/eu/m724/autopeerer/server/MyHttpHandler.java @@ -4,9 +4,6 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import eu.m724.autopeerer.common.packet.c2s.PingResponsePacket; import eu.m724.autopeerer.common.packet.c2s.SessionResponsePacket; -import inet.ipaddr.IPAddress; -import inet.ipaddr.IPAddressString; -import inet.ipaddr.ipv6.IPv6Address; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -43,7 +40,7 @@ public class MyHttpHandler implements HttpHandler { inner(exchange); } catch (IOException e) { exchange.sendResponseHeaders(500, -1); - } catch (EndException e) { } + } catch (EndException ignored) { } } private void inner(HttpExchange exchange) throws IOException, EndException { @@ -116,7 +113,7 @@ public class MyHttpHandler implements HttpHandler { sseWrite(exchange, response.toString()); } catch (IOException e) { e.printStackTrace(); - } catch (EndException e) { } + } catch (EndException ignored) { } return (Void) null; }); @@ -165,7 +162,7 @@ public class MyHttpHandler implements HttpHandler { try { future = node.getClient().session( json.getLong("asn"), - new IPAddressString(json.getString("linkLocal")).getAddress().toIPv6(), + InetAddress.getByName(json.getString("linkLocal")), json.getString("publicKey"), json.getString("endpointHost"), json.getInt("endpointPort") diff --git a/src/main/java/eu/m724/autopeerer/server/ServerConfiguration.java b/src/main/java/eu/m724/autopeerer/server/ServerConfiguration.java index 144e47f..c05a563 100644 --- a/src/main/java/eu/m724/autopeerer/server/ServerConfiguration.java +++ b/src/main/java/eu/m724/autopeerer/server/ServerConfiguration.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.file.Files; import java.util.HashSet; import java.util.Properties; import java.util.Set; @@ -17,7 +18,14 @@ public class ServerConfiguration extends Configuration { public Set loadNodes() throws IOException { var nd = new File(directory, "nodes"); - nd.mkdir(); + + if (!nd.exists()) { + nd.mkdir(); + var f = new File(nd, "node1.properties"); + try (var is = getClass().getClassLoader().getResourceAsStream(f.getName())) { + Files.write(f.toPath(), is.readAllBytes()); + } + } var nodes = new HashSet(); diff --git a/src/main/resources/client.properties b/src/main/resources/client.properties index 45dd800..1a7d92b 100644 --- a/src/main/resources/client.properties +++ b/src/main/resources/client.properties @@ -1,4 +1,12 @@ +# The server websocket remote=ws://127.0.0.1:8002 +# Where are WG and BIRD configs located wireguard.directory=config/wg bird.directory=config/bird -link-local=fe80::129:0 \ No newline at end of file + +# Link local used for peering +link-local=fe80::129:0 +# Is IPv4 available? Yes, no, restricted +stack.ipv4=yes +# Is IPv6 available? Yes, no, restricted +stack.ipv6=yes \ No newline at end of file diff --git a/src/main/resources/nodes/node1.properties b/src/main/resources/nodes/node1.properties new file mode 100644 index 0000000..6ec89e4 --- /dev/null +++ b/src/main/resources/nodes/node1.properties @@ -0,0 +1,3 @@ +key=AAAAAAAAAAA= +name="Node Number One" +host=example.com \ No newline at end of file