com
Some checks failed
/ deploy (push) Failing after 24s

This commit is contained in:
Minecon724 2024-12-26 15:13:31 +01:00
parent 7bd35aab87
commit 1b3da520e7
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
14 changed files with 176 additions and 47 deletions

View file

@ -2,4 +2,12 @@
1. Get the `-server.jar` or `-client.jar` and make a new directory for it 1. Get the `-server.jar` or `-client.jar` and make a new directory for it
2. Run the JAR, then stop it 2. Run the JAR, then stop it
3. Configure in `config` 3. Configure in `config`
4. Now run the JAR. Done! 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

View file

@ -20,11 +20,6 @@
<artifactId>Java-WebSocket</artifactId> <artifactId>Java-WebSocket</artifactId>
<version>1.5.7</version> <version>1.5.7</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.seancfoley</groupId>
<artifactId>ipaddress</artifactId>
<version>5.5.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>

View file

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

View file

@ -23,6 +23,8 @@ public class Main {
return; return;
} }
ClientPrefs.init(config);
URI serverUri = URI.create(config.getString("remote")); URI serverUri = URI.create(config.getString("remote"));
var wireGuardLive = new WireGuardLive(new File(config.getString("wireguard.directory"))); var wireGuardLive = new WireGuardLive(new File(config.getString("wireguard.directory")));

View file

@ -5,6 +5,7 @@ import eu.m724.autopeerer.client.bird.BirdSession;
import eu.m724.autopeerer.client.wireguard.WireGuardKeys; import eu.m724.autopeerer.client.wireguard.WireGuardKeys;
import eu.m724.autopeerer.client.wireguard.WireGuardLive; import eu.m724.autopeerer.client.wireguard.WireGuardLive;
import eu.m724.autopeerer.client.wireguard.WireGuardSession; 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.Packet;
import eu.m724.autopeerer.common.packet.Packets; import eu.m724.autopeerer.common.packet.Packets;
import eu.m724.autopeerer.common.packet.s2c.PingRequestPacket; 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.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException; import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
@ -96,6 +98,24 @@ public class PacketHandler {
} }
private void handleSessionRequest(SessionRequestPacket packet) { 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 privateKey = WireGuardKeys.generatePrivateKey();
var publicKey = WireGuardKeys.derivePublicKey(privateKey); var publicKey = WireGuardKeys.derivePublicKey(privateKey);
@ -104,8 +124,8 @@ public class PacketHandler {
port = (int) (packet.asn % 10000); port = (int) (packet.asn % 10000);
} }
var wireGuardSession = new WireGuardSession(port, privateKey, serverLinkLocal, packet.linkLocal.toCompressedString(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey); var wireGuardSession = new WireGuardSession(port, privateKey, serverLinkLocal, packet.linkLocal.getHostAddress(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey);
var birdSession = new BirdSession(packet.asn, packet.linkLocal.toCompressedString()); var birdSession = new BirdSession(packet.asn, packet.linkLocal.getHostAddress());
try { try {
wireGuardLive.saveSession(packet.asn, wireGuardSession); 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); Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.OK, wireGuardSession.listenPort(), publicKey), sender);
} catch (FileAlreadyExistsException e) { } catch (FileAlreadyExistsException e) {
System.err.println("Tried to create a session which already exists: AS" + packet.asn); 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) { } 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); throw new RuntimeException(e);
} }
} }

View file

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

View file

@ -23,7 +23,7 @@ public class SessionResponsePacket implements Packet<SessionResponsePacket> {
return 2; return 2;
} }
public static SessionResponsePacket deserialize(ByteBuffer buffer) throws Exception { public static SessionResponsePacket deserialize(ByteBuffer buffer) {
var asn = Integer.toUnsignedLong(buffer.getInt()); var asn = Integer.toUnsignedLong(buffer.getInt());
var result = SessionResult.values()[buffer.get()]; var result = SessionResult.values()[buffer.get()];
@ -57,6 +57,6 @@ public class SessionResponsePacket implements Packet<SessionResponsePacket> {
} }
public enum SessionResult { public enum SessionResult {
OK, ERROR, DUPLICATE OK, ERROR_DUPLICATE, ERROR_RESOLVE, ERROR_OTHER
} }
} }

View file

@ -1,39 +1,33 @@
package eu.m724.autopeerer.common.packet.s2c; package eu.m724.autopeerer.common.packet.s2c;
import eu.m724.autopeerer.common.AddressTools;
import eu.m724.autopeerer.common.packet.Packet; 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.IDN;
import java.net.InetAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
public class SessionRequestPacket implements Packet<SessionRequestPacket> { public class SessionRequestPacket implements Packet<SessionRequestPacket> {
public final long asn; public final long asn;
public final IPv6Address linkLocal; public final InetAddress linkLocal;
public final String publicKey; public final String publicKey;
public final String endpointHost; public final String endpointHost;
public final int endpointPort; 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; this.asn = asn;
assert asn < 0xFFFFFFFFL; assert asn < 0xFFFFFFFFL;
this.linkLocal = linkLocal; this.linkLocal = linkLocal;
assert new IPAddressString("fe80::/10").getAddress().contains(linkLocal); assert AddressTools.isLinkLocal(linkLocal);
this.publicKey = publicKey; this.publicKey = publicKey;
assert Base64.getDecoder().decode(publicKey).length == 32; assert Base64.getDecoder().decode(publicKey).length == 32;
// testing is done at another point
this.endpointHost = IDN.toASCII(endpointHost, IDN.ALLOW_UNASSIGNED); this.endpointHost = IDN.toASCII(endpointHost, IDN.ALLOW_UNASSIGNED);
try {
new HostName(endpointHost).validate();
} catch (HostNameException e) {
throw new RuntimeException(e);
}
this.endpointPort = endpointPort; this.endpointPort = endpointPort;
assert endpointPort > 0 && endpointPort < 65536; assert endpointPort > 0 && endpointPort < 65536;
@ -49,7 +43,7 @@ public class SessionRequestPacket implements Packet<SessionRequestPacket> {
var ll = new byte[16]; var ll = new byte[16];
buffer.get(ll); buffer.get(ll);
var linkLocal = new IPv6Address(ll); var linkLocal = InetAddress.getByAddress(ll);
var pk = new byte[32]; var pk = new byte[32];
buffer.get(pk); buffer.get(pk);
@ -70,7 +64,7 @@ public class SessionRequestPacket implements Packet<SessionRequestPacket> {
var buffer = ByteBuffer.allocate(55 + endpointHost.length()); var buffer = ByteBuffer.allocate(55 + endpointHost.length());
buffer.putInt((int) asn); buffer.putInt((int) asn);
buffer.put(linkLocal.getBytes()); // 16b buffer.put(linkLocal.getAddress()); // 16b
buffer.put(Base64.getDecoder().decode(publicKey)); // 32b buffer.put(Base64.getDecoder().decode(publicKey)); // 32b
buffer.putShort((short) endpointPort); // 2b buffer.putShort((short) endpointPort); // 2b
buffer.put((byte) endpointHost.length()); // 1b buffer.put((byte) endpointHost.length()); // 1b

View file

@ -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.c2s.SessionResponsePacket;
import eu.m724.autopeerer.common.packet.s2c.PingRequestPacket; import eu.m724.autopeerer.common.packet.s2c.PingRequestPacket;
import eu.m724.autopeerer.common.packet.s2c.SessionRequestPacket; import eu.m724.autopeerer.common.packet.s2c.SessionRequestPacket;
import inet.ipaddr.ipv6.IPv6Address;
import org.java_websocket.WebSocket; import org.java_websocket.WebSocket;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -52,7 +49,7 @@ public class ClientState {
return future; return future;
} }
CompletableFuture<SessionResponsePacket> session(long asn, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort) { CompletableFuture<SessionResponsePacket> session(long asn, InetAddress linkLocal, String publicKey, String endpointHost, int endpointPort) {
var future = new CompletableFuture<SessionResponsePacket>(); var future = new CompletableFuture<SessionResponsePacket>();
sessionConsumers.put(asn, future::complete); sessionConsumers.put(asn, future::complete);
@ -66,9 +63,8 @@ public class ClientState {
/* Packet functions */ /* Packet functions */
private Map<Short, Consumer<PingResponsePacket>> pingConsumers = new HashMap<>(); private final Map<Short, Consumer<PingResponsePacket>> pingConsumers = new HashMap<>();
private Map<Long, Consumer<SessionResponsePacket>> sessionConsumers = new HashMap<>(); private final Map<Long, Consumer<SessionResponsePacket>> sessionConsumers = new HashMap<>();
void onPacketReceived(Packet<?> p) { void onPacketReceived(Packet<?> p) {
if (p instanceof PingResponsePacket packet) { if (p instanceof PingResponsePacket packet) {

View file

@ -8,17 +8,11 @@ import java.util.Set;
public class Main { public class Main {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
System.out.println("Hello world!");
var config = new ServerConfiguration(); var config = new ServerConfiguration();
Set<Node> nodes; Set<Node> nodes;
try { try {
config.load(); config.load();
nodes = config.loadNodes(); 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) { } catch (IOException e) {
throw new RuntimeException("Failed to load configuration", e); throw new RuntimeException("Failed to load configuration", e);
} }
@ -28,7 +22,15 @@ public class Main {
return; 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 packetHandler = new PacketHandler(nodes);
var server = new MyWebsocketServer( var server = new MyWebsocketServer(
new InetSocketAddress(config.getString("socket.address"), config.getInt("socket.port")), new InetSocketAddress(config.getString("socket.address"), config.getInt("socket.port")),
packetHandler packetHandler

View file

@ -4,9 +4,6 @@ import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpHandler;
import eu.m724.autopeerer.common.packet.c2s.PingResponsePacket; import eu.m724.autopeerer.common.packet.c2s.PingResponsePacket;
import eu.m724.autopeerer.common.packet.c2s.SessionResponsePacket; 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.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -43,7 +40,7 @@ public class MyHttpHandler implements HttpHandler {
inner(exchange); inner(exchange);
} catch (IOException e) { } catch (IOException e) {
exchange.sendResponseHeaders(500, -1); exchange.sendResponseHeaders(500, -1);
} catch (EndException e) { } } catch (EndException ignored) { }
} }
private void inner(HttpExchange exchange) throws IOException, EndException { private void inner(HttpExchange exchange) throws IOException, EndException {
@ -116,7 +113,7 @@ public class MyHttpHandler implements HttpHandler {
sseWrite(exchange, response.toString()); sseWrite(exchange, response.toString());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} catch (EndException e) { } } catch (EndException ignored) { }
return (Void) null; return (Void) null;
}); });
@ -165,7 +162,7 @@ public class MyHttpHandler implements HttpHandler {
try { try {
future = node.getClient().session( future = node.getClient().session(
json.getLong("asn"), json.getLong("asn"),
new IPAddressString(json.getString("linkLocal")).getAddress().toIPv6(), InetAddress.getByName(json.getString("linkLocal")),
json.getString("publicKey"), json.getString("publicKey"),
json.getString("endpointHost"), json.getString("endpointHost"),
json.getInt("endpointPort") json.getInt("endpointPort")

View file

@ -6,6 +6,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@ -17,7 +18,14 @@ public class ServerConfiguration extends Configuration {
public Set<Node> loadNodes() throws IOException { public Set<Node> loadNodes() throws IOException {
var nd = new File(directory, "nodes"); 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<Node>(); var nodes = new HashSet<Node>();

View file

@ -1,4 +1,12 @@
# The server websocket
remote=ws://127.0.0.1:8002 remote=ws://127.0.0.1:8002
# Where are WG and BIRD configs located
wireguard.directory=config/wg wireguard.directory=config/wg
bird.directory=config/bird bird.directory=config/bird
link-local=fe80::129:0
# 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

View file

@ -0,0 +1,3 @@
key=AAAAAAAAAAA=
name="Node Number One"
host=example.com