mirror of
https://github.com/GeyserMC/Floodgate.git
synced 2025-12-19 14:59:20 +00:00
Paper added an option to disable username validation for a connection
Co-authored-by: Camotoy <20743703+camotoy@users.noreply.github.com>
This commit is contained in:
@@ -28,6 +28,7 @@ package org.geysermc.floodgate;
|
|||||||
import com.google.inject.Guice;
|
import com.google.inject.Guice;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeHandlers;
|
||||||
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
import org.geysermc.floodgate.module.PluginMessageModule;
|
import org.geysermc.floodgate.module.PluginMessageModule;
|
||||||
import org.geysermc.floodgate.module.ServerCommonModule;
|
import org.geysermc.floodgate.module.ServerCommonModule;
|
||||||
@@ -35,6 +36,7 @@ import org.geysermc.floodgate.module.SpigotAddonModule;
|
|||||||
import org.geysermc.floodgate.module.SpigotCommandModule;
|
import org.geysermc.floodgate.module.SpigotCommandModule;
|
||||||
import org.geysermc.floodgate.module.SpigotListenerModule;
|
import org.geysermc.floodgate.module.SpigotListenerModule;
|
||||||
import org.geysermc.floodgate.module.SpigotPlatformModule;
|
import org.geysermc.floodgate.module.SpigotPlatformModule;
|
||||||
|
import org.geysermc.floodgate.util.SpigotHandshakeHandler;
|
||||||
import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
|
import org.geysermc.floodgate.util.SpigotProtocolSupportHandler;
|
||||||
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
|
import org.geysermc.floodgate.util.SpigotProtocolSupportListener;
|
||||||
|
|
||||||
@@ -66,6 +68,10 @@ public final class SpigotPlugin extends JavaPlugin {
|
|||||||
new PluginMessageModule()
|
new PluginMessageModule()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//todo add proper support for disabling things on shutdown and enabling this on enable
|
||||||
|
injector.getInstance(HandshakeHandlers.class)
|
||||||
|
.addHandshakeHandler(injector.getInstance(SpigotHandshakeHandler.class));
|
||||||
|
|
||||||
// add ProtocolSupport support (hack)
|
// add ProtocolSupport support (hack)
|
||||||
if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) {
|
if (getServer().getPluginManager().getPlugin("ProtocolSupport") != null) {
|
||||||
injector.getInstance(SpigotProtocolSupportHandler.class);
|
injector.getInstance(SpigotProtocolSupportHandler.class);
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ import org.geysermc.floodgate.config.FloodgateConfig;
|
|||||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
import org.geysermc.floodgate.player.FloodgateHandshakeHandler;
|
||||||
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
|
import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult;
|
||||||
import org.geysermc.floodgate.util.ClassNames;
|
import org.geysermc.floodgate.util.ClassNames;
|
||||||
|
import org.geysermc.floodgate.util.ProxyUtils;
|
||||||
|
|
||||||
public final class SpigotDataHandler extends CommonDataHandler {
|
public final class SpigotDataHandler extends CommonDataHandler {
|
||||||
private Object networkManager;
|
private Object networkManager;
|
||||||
private FloodgatePlayer player;
|
private FloodgatePlayer player;
|
||||||
|
private boolean proxyData;
|
||||||
|
|
||||||
public SpigotDataHandler(
|
public SpigotDataHandler(
|
||||||
FloodgateHandshakeHandler handshakeHandler,
|
FloodgateHandshakeHandler handshakeHandler,
|
||||||
@@ -72,13 +74,18 @@ public final class SpigotDataHandler extends CommonDataHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paper now has username validation, so we have to help Paper to bypass the check.
|
// the server will do all the work if BungeeCord mode is enabled,
|
||||||
// The username is only validated in the login start packet, and that packet doesn't reach
|
// otherwise we have to help the server.
|
||||||
// the server handler when we call our own login cycle.
|
proxyData = ProxyUtils.isProxyData();
|
||||||
|
|
||||||
// After internal discussion, we've decided to require assistance for all Spigot platforms,
|
if (!proxyData) {
|
||||||
// in case Spigot decides to add username validation as well.
|
// Use a spoofedUUID for initUUID (just like Bungeecord)
|
||||||
return false;
|
setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can only remove the handler if the data is proxy data and username validation doesn't
|
||||||
|
// exist. Otherwise, we need to wait and disable it.
|
||||||
|
return proxyData && ClassNames.PAPER_DISABLE_USERNAME_VALIDATION == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -131,16 +138,22 @@ public final class SpigotDataHandler extends CommonDataHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ClassNames.PAPER_DISABLE_USERNAME_VALIDATION != null) {
|
||||||
|
// ensure that Paper will not be checking
|
||||||
|
setValue(packetListener, ClassNames.PAPER_DISABLE_USERNAME_VALIDATION, true);
|
||||||
|
if (proxyData) {
|
||||||
|
// the server will handle the rest if we have proxy data
|
||||||
|
ctx.pipeline().remove(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// set the player his GameProfile, we can't change the username without this
|
// set the player his GameProfile, we can't change the username without this
|
||||||
GameProfile gameProfile = new GameProfile(
|
GameProfile gameProfile = new GameProfile(
|
||||||
player.getCorrectUniqueId(), player.getCorrectUsername()
|
player.getCorrectUniqueId(), player.getCorrectUsername()
|
||||||
);
|
);
|
||||||
setValue(packetListener, ClassNames.LOGIN_PROFILE, gameProfile);
|
setValue(packetListener, ClassNames.LOGIN_PROFILE, gameProfile);
|
||||||
|
|
||||||
//todo we can remove this line + the initUUID line, but then if a skin has been sent
|
|
||||||
// from a proxy using bungee data, we won't be able to show it
|
|
||||||
setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId());
|
|
||||||
|
|
||||||
// we have to fake the offline player (login) cycle
|
// we have to fake the offline player (login) cycle
|
||||||
// just like on Spigot:
|
// just like on Spigot:
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.floodgate.util;
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
|
||||||
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
import static org.geysermc.floodgate.util.ReflectionUtils.getFieldOfType;
|
||||||
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
|
import static org.geysermc.floodgate.util.ReflectionUtils.getMethod;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
@SuppressWarnings("PMD.SystemPrintln")
|
@SuppressWarnings("PMD.SystemPrintln")
|
||||||
public class ClassNames {
|
public class ClassNames {
|
||||||
@@ -56,6 +58,8 @@ public class ClassNames {
|
|||||||
public static final Field HANDSHAKE_HOST;
|
public static final Field HANDSHAKE_HOST;
|
||||||
public static final Field LOGIN_PROFILE;
|
public static final Field LOGIN_PROFILE;
|
||||||
public static final Field PACKET_LISTENER;
|
public static final Field PACKET_LISTENER;
|
||||||
|
@Nullable
|
||||||
|
public static final Field PAPER_DISABLE_USERNAME_VALIDATION;
|
||||||
|
|
||||||
public static final Method GET_PROFILE_METHOD;
|
public static final Method GET_PROFILE_METHOD;
|
||||||
public static final Method LOGIN_DISCONNECT;
|
public static final Method LOGIN_DISCONNECT;
|
||||||
@@ -157,6 +161,13 @@ public class ClassNames {
|
|||||||
|
|
||||||
FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents");
|
FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents");
|
||||||
checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
checkNotNull(FIRE_LOGIN_EVENTS, "fireEvents from LoginHandler");
|
||||||
|
|
||||||
|
PAPER_DISABLE_USERNAME_VALIDATION = getField(LOGIN_LISTENER,
|
||||||
|
"iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation");
|
||||||
|
if (Constants.DEBUG_MODE) {
|
||||||
|
System.out.println("Paper disable username validation field exists? " +
|
||||||
|
(PAPER_DISABLE_USERNAME_VALIDATION != null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> getClassOrFallBack(String className, String fallbackName) {
|
private static Class<?> getClassOrFallBack(String className, String fallbackName) {
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static org.geysermc.floodgate.util.ReflectionUtils.getField;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
public final class ProxyUtils {
|
||||||
|
private static final Field IS_BUNGEE_DATA;
|
||||||
|
private static final Field IS_MODERN_FORWARDING;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Class<?> spigotConfig = ReflectionUtils.getClass("org.spigotmc.SpigotConfig");
|
||||||
|
IS_BUNGEE_DATA = getField(spigotConfig, "bungee");
|
||||||
|
checkNotNull(IS_BUNGEE_DATA, "bungee field cannot be null. Are you using CraftBukkit?");
|
||||||
|
|
||||||
|
Field velocitySupport;
|
||||||
|
try {
|
||||||
|
Class<?> paperConfig = Class.forName("com.destroystokyo.paper.PaperConfig");
|
||||||
|
velocitySupport = getField(paperConfig, "velocitySupport");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// We're not on a platform that has modern forwarding
|
||||||
|
velocitySupport = null; // NOPMD - there's really not a better way around this unless you want to use an optional
|
||||||
|
}
|
||||||
|
IS_MODERN_FORWARDING = velocitySupport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isProxyData() {
|
||||||
|
return isBungeeData() || isVelocitySupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBungeeData() {
|
||||||
|
return ReflectionUtils.getCastedValue(null, IS_BUNGEE_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isVelocitySupport() {
|
||||||
|
if (IS_MODERN_FORWARDING == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectionUtils.getCastedValue(null, IS_MODERN_FORWARDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Floodgate
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.floodgate.util;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeData;
|
||||||
|
import org.geysermc.floodgate.api.handshake.HandshakeHandler;
|
||||||
|
import org.geysermc.floodgate.api.logger.FloodgateLogger;
|
||||||
|
|
||||||
|
public class SpigotHandshakeHandler implements HandshakeHandler {
|
||||||
|
@Inject
|
||||||
|
private FloodgateLogger logger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HandshakeData data) {
|
||||||
|
BedrockData bedrockData = data.getBedrockData();
|
||||||
|
UUID correctUuid = data.getCorrectUniqueId();
|
||||||
|
|
||||||
|
// replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid
|
||||||
|
String[] split = data.getHostname().split("\0");
|
||||||
|
if (split.length >= 3) {
|
||||||
|
if (logger.isDebug()) {
|
||||||
|
logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'",
|
||||||
|
split[1], bedrockData.getIp(), split[2], correctUuid.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
split[1] = bedrockData.getIp();
|
||||||
|
split[2] = correctUuid.toString();
|
||||||
|
}
|
||||||
|
data.setHostname(String.join("\0", split));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user