diff --git a/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java b/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java index 32596344..13d16d8a 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java @@ -169,7 +169,7 @@ public final class ReflectionUtils { * * @param clazz the class name to get the field from * @param fieldName the name of the field - * @param declared if the field is declared or public. + * @param declared if the field is declared. * @return the field if found, otherwise null */ @Nullable @@ -206,7 +206,7 @@ public final class ReflectionUtils { * * @param clazz the class to search the field from * @param fieldType the type of the field - * @param declared if the field is declared or public + * @param declared if the field is declared * @return the field if it has been found, otherwise null */ @Nullable @@ -342,7 +342,7 @@ public final class ReflectionUtils { * * @param clazz the class to get the method from * @param method the name of the method to find - * @param declared if the the method is declared or public + * @param declared if the the method is declared * @param arguments the classes of the method arguments * @return the requested method if it has been found, otherwise null */ @@ -427,7 +427,7 @@ public final class ReflectionUtils { * * @param clazz the class to search the method in * @param methodName the name of the method - * @param declared if the method is declared or public + * @param declared if the method is declared * @return the method if it has been found, otherwise null */ @Nullable @@ -446,7 +446,7 @@ public final class ReflectionUtils { * * @param clazz the class to search the method in * @param paramType the type of one of the method parameters - * @param declared if the method is declared or public + * @param declared if the method is declared * @return the method if it has been found, otherwise null */ @Nullable diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java index a19b71ed..a623fdaf 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java @@ -28,12 +28,15 @@ package org.geysermc.floodgate.addon.data; import static com.google.common.base.Preconditions.checkNotNull; import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue; import static org.geysermc.floodgate.util.ReflectionUtils.getField; +import static org.geysermc.floodgate.util.ReflectionUtils.getMethodByName; import static org.geysermc.floodgate.util.ReflectionUtils.getPrefixedClass; +import static org.geysermc.floodgate.util.ReflectionUtils.invoke; import static org.geysermc.floodgate.util.ReflectionUtils.setValue; import io.netty.channel.Channel; import io.netty.util.AttributeKey; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.net.InetSocketAddress; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; @@ -48,6 +51,11 @@ public final class VelocityProxyDataHandler extends CommonDataHandler { private static final Field HANDSHAKE_SERVER_ADDRESS; private static final Field REMOTE_ADDRESS; + private static final Class SERVER_LOGIN_PACKET; + private static final Method GET_SESSION_HANDLER; + private static final Class INITIAL_LOGIN_SESSION_HANDLER; + private static final Field FORCE_KEY_AUTHENTICATION; + static { Class iic = getPrefixedClass("connection.client.InitialInboundConnection"); checkNotNull(iic, "InitialInboundConnection class cannot be null"); @@ -63,6 +71,20 @@ public final class VelocityProxyDataHandler extends CommonDataHandler { Class minecraftConnection = getPrefixedClass("connection.MinecraftConnection"); REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress"); + checkNotNull(REMOTE_ADDRESS, "remoteAddress cannot be null"); + + SERVER_LOGIN_PACKET = getPrefixedClass("protocol.packet.ServerLogin"); + checkNotNull(SERVER_LOGIN_PACKET, "ServerLogin packet class cannot be null"); + + GET_SESSION_HANDLER = getMethodByName(minecraftConnection, "getSessionHandler", true); + checkNotNull(GET_SESSION_HANDLER, "getSessionHandler method cannot be null"); + + INITIAL_LOGIN_SESSION_HANDLER = + getPrefixedClass("connection.client.InitialLoginSessionHandler"); + checkNotNull(INITIAL_LOGIN_SESSION_HANDLER, "InitialLoginSessionHandler cannot be null"); + + // allowed to be null if it's an old Velocity version + FORCE_KEY_AUTHENTICATION = getField(INITIAL_LOGIN_SESSION_HANDLER, "forceKeyAuthentication"); } private final FloodgateLogger logger; @@ -94,19 +116,35 @@ public final class VelocityProxyDataHandler extends CommonDataHandler { FloodgatePlayer player = result.getFloodgatePlayer(); logger.info("Floodgate player who is logged in as {} {} joined", player.getCorrectUsername(), player.getCorrectUniqueId()); + + // the way Velocity stores whether to force key authentication + boolean forceKeyAuthentication = Boolean.getBoolean("auth.forceSecureProfiles"); + // we need the login packet to bypass the 'force key authentication' + return !forceKeyAuthentication; } return super.shouldRemoveHandler(result); } @Override public boolean channelRead(Object packet) { - // we're only interested in the Handshake packet. - // it should be the first packet but you never know if (HANDSHAKE_PACKET.isInstance(packet)) { handle(packet, getCastedValue(packet, HANDSHAKE_SERVER_ADDRESS)); // otherwise, it'll get read twice. once by the packet queue and once by this method return false; } + + // at this point we know that forceKeyAuthentication is enabled + if (SERVER_LOGIN_PACKET.isInstance(packet)) { + Object minecraftConnection = ctx.pipeline().get("handler"); + Object sessionHandler = invoke(minecraftConnection, GET_SESSION_HANDLER); + if (!INITIAL_LOGIN_SESSION_HANDLER.isInstance(sessionHandler)) { + logger.error("Expected player's session handler to be InitialLoginSessionHandler"); + return true; + } + if (FORCE_KEY_AUTHENTICATION != null) { + setValue(sessionHandler, FORCE_KEY_AUTHENTICATION, false); + } + } return true; } }