Skip to content

Commit

Permalink
Added proxy online mode and persistent storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Lenni0451 committed Jan 4, 2025
1 parent 303c5b4 commit 478a1b8
Show file tree
Hide file tree
Showing 17 changed files with 431 additions and 49 deletions.
3 changes: 2 additions & 1 deletion src/main/java/net/lenni0451/miniconnect/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public void onEnable() {
ViaProxy.EVENT_MANAGER.register(new PacketHandlerRegistry());
ViaProxy.EVENT_MANAGER.register(new ViaLoadHandler());
ViaProxy.EVENT_MANAGER.register(new HAProxyEnableHandler());
ViaProxy.EVENT_MANAGER.register(new OnlineModeHandler());
ViaProxy.EVENT_MANAGER.register(new TargetOnlineModeHandler());
ViaProxy.EVENT_MANAGER.register(new ProxyOnlineModeHandler());
}

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package net.lenni0451.miniconnect.haproxy;

import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AbstractChannel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.haproxy.HAProxyCommand;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.codec.haproxy.HAProxyTLV;
import net.lenni0451.miniconnect.model.AttributeKeys;
import net.lenni0451.miniconnect.model.HandshakeData;
import net.lenni0451.reflect.stream.RStream;
import net.raphimc.netminecraft.packet.PacketTypes;
import org.apache.commons.lang3.tuple.Triple;

import java.net.InetSocketAddress;

Expand All @@ -35,11 +32,7 @@ protected void channelRead0(ChannelHandlerContext ctx, HAProxyMessage message) t
boolean hasHandshakeData = false;
for (HAProxyTLV tlv : message.tlvs()) {
if (tlv.typeByteValue() == (byte) 0xE0) {
ByteBuf content = tlv.content();
String host = PacketTypes.readString(content, Short.MAX_VALUE);
int port = content.readUnsignedShort();
ProtocolVersion clientVersion = ProtocolVersion.getProtocol(content.readInt());
ctx.channel().attr(AttributeKeys.HANDSHAKE_DATA).set(Triple.of(host, port, clientVersion));
ctx.channel().attr(AttributeKeys.HANDSHAKE_DATA).set(HandshakeData.read(tlv.content()));
hasHandshakeData = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import io.netty.channel.Channel;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.handler.codec.haproxy.*;
import net.raphimc.netminecraft.packet.PacketTypes;
import net.lenni0451.miniconnect.model.HandshakeData;

import java.net.*;
import java.util.ArrayList;
Expand All @@ -18,9 +18,7 @@ public class HAProxyUtil {
public static HAProxyMessage createMessage(final Channel sourceChannel, final Channel targetChannel, final HostAndPort handshakeAddress, final ProtocolVersion clientVersion) {
List<HAProxyTLV> tlvs = new ArrayList<>();
ByteBuf handshakeBuf = Unpooled.buffer();
PacketTypes.writeString(handshakeBuf, handshakeAddress.getHost());
handshakeBuf.writeShort(handshakeAddress.getPort());
handshakeBuf.writeInt(clientVersion.getOriginalVersion());
new HandshakeData(handshakeAddress.getHost(), handshakeAddress.getPort(), clientVersion).write(handshakeBuf);
tlvs.add(new HAProxyTLV((byte) 0xE0, handshakeBuf));

if (sourceChannel.remoteAddress() instanceof InetSocketAddress sourceAddress && targetChannel.remoteAddress() instanceof InetSocketAddress targetAddress) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package net.lenni0451.miniconnect.model;

import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.util.AttributeKey;
import org.apache.commons.lang3.tuple.Triple;

public class AttributeKeys {

public static final AttributeKey<ConnectionInfo> CONNECTION_INFO = AttributeKey.newInstance("MiniConnect_ConnectionInfo");
public static final AttributeKey<Boolean> ENABLE_HAPROXY = AttributeKey.newInstance("MiniConnect_EnableHAProxy");
public static final AttributeKey<Triple<String, Integer, ProtocolVersion>> HANDSHAKE_DATA = AttributeKey.newInstance("MiniConnect_HandshakeData");
public static final AttributeKey<HandshakeData> HANDSHAKE_DATA = AttributeKey.newInstance("MiniConnect_HandshakeData");

}
22 changes: 22 additions & 0 deletions src/main/java/net/lenni0451/miniconnect/model/HandshakeData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.lenni0451.miniconnect.model;

import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.buffer.ByteBuf;
import net.raphimc.netminecraft.packet.PacketTypes;

public record HandshakeData(String host, int port, ProtocolVersion clientVersion) {

public static HandshakeData read(final ByteBuf buf) {
String host = PacketTypes.readString(buf, Short.MAX_VALUE);
int port = buf.readUnsignedShort();
ProtocolVersion clientVersion = ProtocolVersion.getProtocol(buf.readInt());
return new HandshakeData(host, port, clientVersion);
}

public void write(final ByteBuf buf) {
PacketTypes.writeString(buf, this.host);
buf.writeShort(this.port);
buf.writeInt(this.clientVersion.getOriginalVersion());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
import net.lenni0451.miniconnect.model.ConnectionInfo;

import java.net.InetAddress;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class StateRegistry {

private final Map<InetAddress, ConnectionInfo> connectionTargets = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).<InetAddress, ConnectionInfo>build().asMap();
private final Map<InetAddress, ConnectionInfo> reconnectTargets = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).<InetAddress, ConnectionInfo>build().asMap();
private final Map<InetAddress, ConnectionInfo> lobbyTargets = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).<InetAddress, ConnectionInfo>build().asMap();
private final Set<UUID> verificationQueue = Collections.newSetFromMap(CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).<UUID, Boolean>build().asMap());

public Map<InetAddress, ConnectionInfo> getConnectionTargets() {
return this.connectionTargets;
Expand All @@ -25,4 +29,8 @@ public Map<InetAddress, ConnectionInfo> getLobbyTargets() {
return this.lobbyTargets;
}

public Set<UUID> getVerificationQueue() {
return this.verificationQueue;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
public class HAProxyEnableHandler {

@EventHandler
public void onViaProxyLoaded(final ViaProxyLoadedEvent events) {
public void onViaProxyLoaded(final ViaProxyLoadedEvent event) {
ViaProxy.getConfig().setBackendHaProxy(false);
ViaProxy.getConfig().setAllowLegacyClientPassthrough(false);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.lenni0451.miniconnect.proxy.event;

import net.lenni0451.lambdaevents.EventHandler;
import net.lenni0451.miniconnect.Main;
import net.lenni0451.miniconnect.utils.UUIDUtils;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
import net.raphimc.viaproxy.plugins.events.ViaProxyLoadedEvent;

import java.io.File;
import java.util.UUID;

public class ProxyOnlineModeHandler {

@EventHandler
public void onViaProxyLoaded(final ViaProxyLoadedEvent event) {
ViaProxy.getConfig().setProxyOnlineMode(true);
}

@EventHandler
public void onShouldVerifyOnlineMode(final ShouldVerifyOnlineModeEvent event) {
UUID uuid = event.getProxyConnection().getGameProfile().getId();
if (Main.getInstance().getStateRegistry().getVerificationQueue().contains(uuid)) {
event.setCancelled(false); //Enforce online mode verification
} else {
String hashedUUID = UUIDUtils.hash(uuid);
File settingsFile = new File(Main.getInstance().getDataFolder(), hashedUUID + ".dat");
if (!settingsFile.exists()) event.setCancelled(true);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class RedirectionHandler {
private static final DomainSocketAddress DUMMY_SOCKET_ADDRESS = new DomainSocketAddress("/miniconnect/lobby");

@EventHandler
public void onViaProxyLoaded(final ViaProxyLoadedEvent events) {
public void onViaProxyLoaded(final ViaProxyLoadedEvent event) {
ViaProxy.getConfig().setTargetAddress(DUMMY_SOCKET_ADDRESS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import net.lenni0451.lambdaevents.EventHandler;
import net.raphimc.viaproxy.plugins.events.JoinServerRequestEvent;

public class OnlineModeHandler {
public class TargetOnlineModeHandler {

@EventHandler(priority = Integer.MIN_VALUE)
public void onJoinServerRequest(final JoinServerRequestEvent event) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package net.lenni0451.miniconnect.server;

import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.ScheduledFuture;
import net.lenni0451.miniconnect.Main;
import net.lenni0451.miniconnect.model.AttributeKeys;
import net.lenni0451.miniconnect.model.ConnectionInfo;
import net.lenni0451.miniconnect.model.HandshakeData;
import net.lenni0451.miniconnect.server.model.PlayerConfig;
import net.lenni0451.miniconnect.server.states.*;
import net.lenni0451.miniconnect.utils.ChannelUtils;
import net.raphimc.netminecraft.constants.ConnectionState;
import net.raphimc.netminecraft.packet.Packet;
import org.apache.commons.lang3.tuple.Triple;
import net.raphimc.viaproxy.util.logging.Logger;

import java.nio.channels.ClosedChannelException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class LobbyServerHandler extends SimpleChannelInboundHandler<Packet> {
Expand All @@ -24,6 +25,27 @@ public class LobbyServerHandler extends SimpleChannelInboundHandler<Packet> {
private StateHandler handler;
private ScheduledFuture<?> tickTask;

public void loadPlayerConfig(final Channel channel, final UUID uuid) {
ConnectionInfo previousConnectionInfo = Main.getInstance().getStateRegistry().getLobbyTargets().remove(ChannelUtils.getChannelAddress(channel));
HandshakeData handshakeData = channel.attr(AttributeKeys.HANDSHAKE_DATA).get();
this.playerConfig = new PlayerConfig(uuid);
try {
if (Main.getInstance().getStateRegistry().getVerificationQueue().remove(uuid)) {
this.playerConfig.save();
} else {
this.playerConfig.load();
}
} catch (Throwable t) {
Logger.LOGGER.error("Failed to load settings file for player {}", uuid, t);
}
if (previousConnectionInfo != null) {
this.playerConfig.applyConnectionInfo(previousConnectionInfo);
}
this.playerConfig.handshakeAddress = handshakeData.host();
this.playerConfig.handshakePort = handshakeData.port();
this.playerConfig.clientVersion = handshakeData.clientVersion();
}

public PlayerConfig getPlayerConfig() {
return this.playerConfig;
}
Expand Down Expand Up @@ -57,18 +79,6 @@ protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
// int packetId = registry.getPacketId(packet);
// MCPackets packetType = MCPackets.getPacket(registry.getConnectionState(), PacketDirection.SERVERBOUND, ProtocolConstants.PROTOCOL_VERSION.getVersion(), packetId);
// System.out.println(packetType);
if (this.playerConfig == null) {
ConnectionInfo previousConnectionInfo = Main.getInstance().getStateRegistry().getLobbyTargets().remove(ChannelUtils.getChannelAddress(ctx.channel()));
Triple<String, Integer, ProtocolVersion> handshakeData = ctx.channel().attr(AttributeKeys.HANDSHAKE_DATA).get();
if (previousConnectionInfo == null) {
this.playerConfig = new PlayerConfig();
} else {
this.playerConfig = PlayerConfig.fromConnectionInfo(previousConnectionInfo);
}
this.playerConfig.handshakeAddress = handshakeData.getLeft();
this.playerConfig.handshakePort = handshakeData.getMiddle();
this.playerConfig.clientVersion = handshakeData.getRight();
}
this.handler.handle(packet);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
package net.lenni0451.miniconnect.server.model;

import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import net.lenni0451.commons.gson.GsonParser;
import net.lenni0451.commons.gson.elements.GsonObject;
import net.lenni0451.commons.gson.elements.GsonPrimitive;
import net.lenni0451.miniconnect.Main;
import net.lenni0451.miniconnect.model.ConnectionInfo;
import net.lenni0451.miniconnect.utils.AESEncryption;
import net.lenni0451.miniconnect.utils.UUIDUtils;
import net.raphimc.viaproxy.saves.impl.accounts.Account;
import net.raphimc.viaproxy.saves.impl.accounts.MicrosoftAccount;
import net.raphimc.viaproxy.util.AddressUtil;

import javax.annotation.Nullable;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.UUID;
import java.util.function.Function;

public class PlayerConfig {

public static PlayerConfig fromConnectionInfo(final ConnectionInfo connectionInfo) {
PlayerConfig playerConfig = new PlayerConfig();
playerConfig.serverAddress = connectionInfo.host();
playerConfig.serverPort = connectionInfo.port();
playerConfig.targetVersion = connectionInfo.protocolVersion();
playerConfig.account = connectionInfo.account();
return playerConfig;
}

private static final AESEncryption ENCRYPTION = new AESEncryption();

public final UUID uuid;
@Nullable
public String serverAddress;
@Nullable
Expand All @@ -33,10 +37,63 @@ public static PlayerConfig fromConnectionInfo(final ConnectionInfo connectionInf
public transient int handshakePort;
public transient ProtocolVersion clientVersion;
public transient Function<String, Boolean> chatListener;
public transient boolean isSaved;

public PlayerConfig(final UUID uuid) {
this.uuid = uuid;
}

public void applyConnectionInfo(final ConnectionInfo connectionInfo) {
this.serverAddress = connectionInfo.host();
this.serverPort = connectionInfo.port();
this.targetVersion = connectionInfo.protocolVersion();
this.account = connectionInfo.account();
}

public ConnectionInfo toConnectionInfo() {
int serverPort = this.serverPort == null || this.serverPort == -1 ? AddressUtil.getDefaultPort(this.targetVersion) : this.serverPort;
return new ConnectionInfo(this.handshakeAddress, this.handshakePort, this.serverAddress, serverPort, this.targetVersion, this.account);
}

public void load() throws Exception {
String hashedUUID = UUIDUtils.hash(this.uuid);
File settingsFile = new File(Main.getInstance().getDataFolder(), hashedUUID + ".dat");
if (!settingsFile.exists()) return;
this.isSaved = true;

byte[] key = UUIDUtils.toBytes(this.uuid);
byte[] data = Files.readAllBytes(settingsFile.toPath());
byte[] decryptedData = ENCRYPTION.decrypt(key, data);
String json = new String(decryptedData, StandardCharsets.UTF_8);
GsonObject object = GsonParser.parse(json).asObject();
this.serverAddress = object.getString("serverAddress", null);
this.serverPort = object.optPrimitive("serverPort").map(GsonPrimitive::asInt).orElse(null);
this.targetVersion = object.optPrimitive("targetVersion").map(p -> ProtocolVersion.getProtocol(p.asInt())).orElse(null);
this.account = object.optObject("account").map(GsonObject::getJsonObject).map(MicrosoftAccount::new).orElse(null);
}

public void save() throws Exception {
String hashedUUID = UUIDUtils.hash(this.uuid);
File settingsFile = new File(Main.getInstance().getDataFolder(), hashedUUID + ".dat");
settingsFile.getParentFile().mkdirs();
this.isSaved = true;

GsonObject object = new GsonObject();
if (this.serverAddress != null) object.add("serverAddress", this.serverAddress);
if (this.serverPort != null) object.add("serverPort", this.serverPort);
if (this.targetVersion != null) object.add("targetVersion", this.targetVersion.getOriginalVersion());
if (this.account != null) object.add("account", this.account.toJson());
byte[] key = UUIDUtils.toBytes(this.uuid);
byte[] data = object.toString().getBytes(StandardCharsets.UTF_8);
byte[] encryptedData = ENCRYPTION.encrypt(key, data);
Files.write(settingsFile.toPath(), encryptedData);
}

public void delete() {
String hashedUUID = UUIDUtils.hash(this.uuid);
File settingsFile = new File(Main.getInstance().getDataFolder(), hashedUUID + ".dat");
if (settingsFile.exists()) settingsFile.delete();
this.isSaved = false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public LoginStateHandler(final LobbyServerHandler handler, final Channel channel

@EventHandler
public void handle(final C2SLoginHelloPacket packet) {
this.handler.loadPlayerConfig(this.channel, packet.uuid);
this.send(new S2CLoginGameProfilePacket(packet.uuid, packet.name, new ArrayList<>()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

public class Items {

public static final String AIR = "air";
public static final String OAK_DOOR = "oak_door";
public static final String BARRIER = "barrier";
public static final String ANVIL = "anvil";
public static final String BOOK = "book";
public static final String WRITTEN_BOOK = "written_book";
public static final String NAMETAG = "name_tag";
public static final String ARROW = "arrow";
Expand All @@ -15,7 +15,7 @@ public class Items {
public static final String DIRT = "dirt";
public static final String GRAY_STAINED_GLASS_PANE = "gray_stained_glass_pane";
public static final String TRIAL_KEY = "trial_key";
public static final String LEVER = "lever";
public static final String ENDER_PEARL = "ender_pearl";
public static final String ENDER_CHEST = "ender_chest";

}
Loading

0 comments on commit 478a1b8

Please sign in to comment.