From f53032e9ea905d7f5fce420f832e1776eb99b610 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 27 Feb 2021 12:01:46 +0100 Subject: [PATCH] Added whitelist command, allow handler to set IP of non-Java players --- .../api/handshake/HandshakeData.java | 14 ++--- .../addon/data/BungeeProxyDataHandler.java | 14 ++--- .../addon/data/HandshakeDataImpl.java | 4 +- .../floodgate/command/LinkAccountCommand.java | 4 +- .../command/UnlinkAccountCommand.java | 4 +- .../floodgate/command/WhitelistCommand.java | 57 +++++++++++++------ .../player/FloodgateHandshakeHandler.java | 37 +++++++----- .../addon/data/SpigotDataHandler.java | 14 ++--- .../addon/data/VelocityProxyDataHandler.java | 11 ++-- 9 files changed, 95 insertions(+), 64 deletions(-) diff --git a/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java b/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java index 247e721a..b6bd1492 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java +++ b/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java @@ -90,20 +90,20 @@ public interface HandshakeData { void setHostname(String hostname); /** - * Returns the IP address of the Bedrock client. The initial value is {@link - * BedrockData#getIp()} (or null if BedrockData is null) but will return the changed IP if it - * has been changed using {@link #setBedrockIp(String)} + * Returns the IP address of the client. The initial value is {@link BedrockData#getIp()} when + * BedrockData isn't null, or null if BedrockData is null. This method will return the changed + * IP if it has been changed using {@link #setIp(String)} */ - String getBedrockIp(); + String getIp(); /** - * Set the IP address of the Bedrock client. Floodgate doesn't perform any checks if the + * Set the IP address of the connected client. Floodgate doesn't perform any checks if the * provided data is valid (hence one of the reasons why this class has been made for advanced * users), thank you for not abusing Floodgate's trust in you :) * - * @param address the IP address of the Bedrock client + * @param address the IP address of the client */ - void setBedrockIp(String address); + void setIp(String address); /** * Returns the reason to disconnect the current player. diff --git a/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java index 88bd9e28..f2080296 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java +++ b/bungee/src/main/java/org/geysermc/floodgate/addon/data/BungeeProxyDataHandler.java @@ -41,7 +41,6 @@ import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.packet.Handshake; import org.geysermc.floodgate.api.handshake.HandshakeData; -import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; @@ -93,10 +92,10 @@ public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter { HandshakeResult result = handler.handle(ctx.channel(), data); HandshakeData handshakeData = result.getHandshakeData(); - // we'll change the IP address from the proxy to the IP of the Bedrock client very early on - // so that almost every plugin will use the IP of the Bedrock client - if (result.getFloodgatePlayer() != null) { - + // we'll change the IP address from the proxy to the real IP of the client very early on + // so that almost every plugin will use the real IP of the client + InetSocketAddress newIp = result.getNewIp(ctx.channel()); + if (newIp != null) { HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class); // InitialHandler extends PacketHandler and implements PendingConnection InitialHandler connection = ReflectionUtils.getCastedValue(handlerBoss, HANDLER); @@ -104,10 +103,7 @@ public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter { ChannelWrapper channelWrapper = ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER); - InetSocketAddress address = - result.getFloodgatePlayer().getProperty(PropertyKey.SOCKET_ADDRESS); - - channelWrapper.setRemoteAddress(address); + channelWrapper.setRemoteAddress(newIp); } if (handshakeData.getDisconnectReason() != null) { diff --git a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java index 605827b3..9cf9a7d9 100644 --- a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java @@ -45,7 +45,7 @@ public class HandshakeDataImpl implements HandshakeData { @Setter private LinkedPlayer linkedPlayer; @Setter private String hostname; - @Setter private String bedrockIp; + @Setter private String ip; @Setter private String disconnectReason; public HandshakeDataImpl( @@ -74,7 +74,7 @@ public class HandshakeDataImpl implements HandshakeData { } javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid()); - this.bedrockIp = bedrockData.getIp(); + this.ip = bedrockData.getIp(); } this.javaUsername = javaUsername; diff --git a/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java b/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java index 49b1b64f..6a1bf3fb 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java +++ b/common/src/main/java/org/geysermc/floodgate/command/LinkAccountCommand.java @@ -27,9 +27,9 @@ package org.geysermc.floodgate.command; import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE; +import cloud.commandframework.ArgumentDescription; import cloud.commandframework.Command; import cloud.commandframework.CommandManager; -import cloud.commandframework.Description; import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.context.CommandContext; import com.google.inject.Inject; @@ -55,7 +55,7 @@ public final class LinkAccountCommand implements FloodgateCommand { @Override public Command buildCommand(CommandManager commandManager) { return commandManager.commandBuilder("linkaccount", - Description.of("Link your Java account with your Bedrock account")) + ArgumentDescription.of("Link your Java account with your Bedrock account")) .senderType(PlayerAudience.class) .permission("floodgate.command.linkaccount") .argument(UserAudienceArgument.of("player", true)) diff --git a/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java b/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java index 389da997..d69ec02a 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java +++ b/common/src/main/java/org/geysermc/floodgate/command/UnlinkAccountCommand.java @@ -27,9 +27,9 @@ package org.geysermc.floodgate.command; import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE; +import cloud.commandframework.ArgumentDescription; import cloud.commandframework.Command; import cloud.commandframework.CommandManager; -import cloud.commandframework.Description; import cloud.commandframework.context.CommandContext; import com.google.inject.Inject; import lombok.Getter; @@ -49,7 +49,7 @@ public final class UnlinkAccountCommand implements FloodgateCommand { @Override public Command buildCommand(CommandManager commandManager) { return commandManager.commandBuilder("unlinkaccount", - Description.of("Unlink your Java account from your Bedrock account")) + ArgumentDescription.of("Unlink your Java account from your Bedrock account")) .senderType(PlayerAudience.class) .permission("floodgate.command.unlinkaccount") .handler(this::execute) diff --git a/common/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java b/common/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java index 9b2aa64d..606a25dd 100644 --- a/common/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java +++ b/common/src/main/java/org/geysermc/floodgate/command/WhitelistCommand.java @@ -25,15 +25,18 @@ package org.geysermc.floodgate.command; +import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE; + +import cloud.commandframework.ArgumentDescription; import cloud.commandframework.Command; import cloud.commandframework.CommandManager; -import cloud.commandframework.Description; import cloud.commandframework.context.CommandContext; import com.google.gson.JsonObject; import com.google.inject.Inject; -import net.kyori.adventure.text.Component; +import lombok.Getter; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.platform.command.CommandMessage; import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.command.FloodgateCommand; import org.geysermc.floodgate.player.UserAudience; @@ -48,7 +51,7 @@ public class WhitelistCommand implements FloodgateCommand { @Override public Command buildCommand(CommandManager commandManager) { Command.Builder builder = commandManager.commandBuilder("fwhitelist", - Description.of("Easy way to whitelist Bedrock players")) + ArgumentDescription.of("Easy way to whitelist Bedrock players")) .permission("floodgate.command.fwhitelist"); commandManager.command(builder @@ -73,8 +76,7 @@ public class WhitelistCommand implements FloodgateCommand { } if (name.length() < 1 || name.length() > 16) { - sender.sendMessage(Component.text( - "The given username '" + name + "' is not a valid username.")); + sender.sendMessage(Message.INVALID_USERNAME); return; } @@ -85,8 +87,7 @@ public class WhitelistCommand implements FloodgateCommand { HttpUtils.asyncGet(Constants.GET_XUID_URL + name) .whenComplete((result, error) -> { if (error != null) { - sender.sendMessage(Component.text( - "An error occurred. See the console for more info")); + sender.sendMessage(Message.API_UNAVAILABLE); error.printStackTrace(); return; } @@ -95,8 +96,7 @@ public class WhitelistCommand implements FloodgateCommand { boolean success = response.get("success").getAsBoolean(); if (!success) { - sender.sendMessage(Component.text( - "An error occurred. See the console for more info")); + sender.sendMessage(Message.UNEXPECTED_ERROR); logger.error( "Got an error from requesting the xuid of a Bedrock player: {}", response.get("message").getAsString()); @@ -105,8 +105,7 @@ public class WhitelistCommand implements FloodgateCommand { JsonObject data = response.getAsJsonObject("data"); if (data.size() == 0) { - sender.sendMessage(Component.text( - "Couldn't find the user '" + tempName + "'")); + sender.sendMessage(Message.USER_NOT_FOUND); return; } @@ -114,12 +113,18 @@ public class WhitelistCommand implements FloodgateCommand { CommandUtil commandUtil = context.get("CommandUtil"); try { - if (add && commandUtil.whitelistPlayer(xuid, tempName)) { - sender.sendMessage(Component.text("Player has been whitelisted :)")); - } else if (!add && commandUtil.removePlayerFromWhitelist(xuid, tempName)) { - sender.sendMessage(Component.text("Player has been removed :o")); + if (add) { + if (commandUtil.whitelistPlayer(xuid, tempName)) { + sender.sendMessage(Message.PLAYER_ADDED); + } else { + sender.sendMessage(Message.PLAYER_ALREADY_WHITELISTED); + } } else { - sender.sendMessage(Component.text("Player was already whitelisted :o")); + if (commandUtil.removePlayerFromWhitelist(xuid, tempName)) { + sender.sendMessage(Message.PLAYER_REMOVED); + } else { + sender.sendMessage(Message.PLAYER_NOT_WHITELISTED); + } } } catch (Exception exception) { logger.error( @@ -133,4 +138,24 @@ public class WhitelistCommand implements FloodgateCommand { public void execute(CommandContext context) { // ignored, all the logic is in the other method } + + @Getter + public enum Message implements CommandMessage { + INVALID_USERNAME("floodgate.command.fwhitelist.invalid_username"), + API_UNAVAILABLE("floodgate.command.fwhitelist.api_unavailable " + CHECK_CONSOLE), + USER_NOT_FOUND("floodgate.command.fwhitelist.user_not_found"), + PLAYER_ADDED("floodgate.command.fwhitelist.player_added"), + PLAYER_REMOVED("floodgate.command.fwhitelist.player_removed"), + PLAYER_ALREADY_WHITELISTED("floodgate.command.fwhitelist.player_already_whitelisted"), + PLAYER_NOT_WHITELISTED("floodgate.command.fwhitelist.player_not_whitelisted"), + UNEXPECTED_ERROR("floodgate.command.fwhitelist.unexpected_error " + CHECK_CONSOLE); + + private final String rawMessage; + private final String[] translateParts; + + Message(String rawMessage) { + this.rawMessage = rawMessage; + this.translateParts = rawMessage.split(" "); + } + } } diff --git a/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java b/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java index 852d98a9..36e7e9b3 100644 --- a/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java @@ -150,10 +150,7 @@ public final class FloodgateHandshakeHandler { bedrockData.getVerifyCode()); } - UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid()); - handshakeData.setHostname(correctHostname( - handshakeData.getHostname(), bedrockData, javaUuid - )); + correctHostname(handshakeData); FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, handshakeData); @@ -163,8 +160,7 @@ public final class FloodgateHandshakeHandler { channel.attr(playerAttribute).set(player); int port = ((InetSocketAddress) channel.remoteAddress()).getPort(); - InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getBedrockIp(), - port); + InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getIp(), port); player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress); return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player); @@ -204,27 +200,27 @@ public final class FloodgateHandshakeHandler { handshakeHandlers.callHandshakeHandlers(handshakeData); if (bedrockData != null) { - UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid()); - handshakeData.setHostname(correctHostname( - handshakeData.getHostname(), bedrockData, javaUuid - )); + correctHostname(handshakeData); } return new HandshakeResult(resultType, handshakeData, bedrockData, null); } - private String correctHostname(String hostname, BedrockData data, UUID correctUuid) { + private void correctHostname(HandshakeData handshakeData) { + BedrockData bedrockData = handshakeData.getBedrockData(); + UUID correctUuid = Utils.getJavaUuid(bedrockData.getXuid()); + // replace the ip and uuid with the Bedrock client IP and an uuid based of the xuid - String[] split = hostname.split("\0"); + String[] split = handshakeData.getHostname().split("\0"); if (split.length >= 3) { if (logger.isDebug()) { logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'", - split[1], data.getIp(), split[2], correctUuid.toString()); + split[1], bedrockData.getIp(), split[2], correctUuid.toString()); } - split[1] = data.getIp(); + split[1] = bedrockData.getIp(); split[2] = correctUuid.toString(); } - return String.join("\0", split); + handshakeData.setHostname(String.join("\0", split)); } private LinkedPlayer fetchLinkedPlayer(UUID javaUniqueId) { @@ -255,5 +251,16 @@ public final class FloodgateHandshakeHandler { private final HandshakeData handshakeData; private final BedrockData bedrockData; private final FloodgatePlayer floodgatePlayer; + + public InetSocketAddress getNewIp(Channel channel) { + if (floodgatePlayer != null) { + return floodgatePlayer.getProperty(PropertyKey.SOCKET_ADDRESS); + } + if (handshakeData.getIp() != null) { + int port = ((InetSocketAddress) channel.remoteAddress()).getPort(); + return new InetSocketAddress(handshakeData.getIp(), port); + } + return null; + } } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index ad666fcb..0646369d 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -46,7 +46,6 @@ import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; @@ -182,10 +181,15 @@ public final class SpigotDataHandler extends ChannelInboundHandlerAdapter { HandshakeData handshakeData = result.getHandshakeData(); setValue(packet, HANDSHAKE_HOST, handshakeData.getHostname()); - logger.info(handshakeData.getHostname()); + + InetSocketAddress newIp = result.getNewIp(ctx.channel()); + if (newIp != null) { + setValue(networkManager, SOCKET_ADDRESS, newIp); + //todo the socket address will be overridden when bungeeData is true + } if (handshakeData.getDisconnectReason() != null) { - ctx.close(); // todo disconnect with message + ctx.close(); //todo disconnect with message return; } @@ -220,10 +224,6 @@ public final class SpigotDataHandler extends ChannelInboundHandlerAdapter { if (!bungeeData) { // Use a spoofedUUID for initUUID (just like Bungeecord) setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId()); - - // Use the player his IP for stuff instead of Geyser his IP - InetSocketAddress address = player.getProperty(PropertyKey.SOCKET_ADDRESS); - setValue(networkManager, SOCKET_ADDRESS, address); } } else if (isLogin) { if (!bungeeData) { 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 9d9842cf..9ef79915 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 @@ -36,11 +36,11 @@ import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.AttributeKey; import io.netty.util.ReferenceCountUtil; import java.lang.reflect.Field; +import java.net.InetSocketAddress; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.player.FloodgateHandshakeHandler.HandshakeResult; @@ -97,6 +97,12 @@ public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter HandshakeResult result = handshakeHandler.handle(ctx.channel(), address); HandshakeData handshakeData = result.getHandshakeData(); + InetSocketAddress newIp = result.getNewIp(ctx.channel()); + if (newIp != null) { + Object connection = ctx.pipeline().get("handler"); + setValue(connection, REMOTE_ADDRESS, newIp); + } + if (handshakeData.getDisconnectReason() != null) { ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason()); return; @@ -124,9 +130,6 @@ public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter setValue(packet, HANDSHAKE_SERVER_ADDRESS, handshakeData.getHostname()); - Object connection = ctx.pipeline().get("handler"); - setValue(connection, REMOTE_ADDRESS, player.getProperty(PropertyKey.SOCKET_ADDRESS)); - logger.info("Floodgate player who is logged in as {} {} joined", player.getCorrectUsername(), player.getCorrectUniqueId()); }