1
0
mirror of https://github.com/GeyserMC/Floodgate.git synced 2025-12-19 14:59:20 +00:00

Added whitelist command, allow handler to set IP of non-Java players

This commit is contained in:
Tim203
2021-02-27 12:01:46 +01:00
parent 3a66d524a1
commit f53032e9ea
9 changed files with 95 additions and 64 deletions

View File

@@ -90,20 +90,20 @@ public interface HandshakeData {
void setHostname(String hostname); void setHostname(String hostname);
/** /**
* Returns the IP address of the Bedrock client. The initial value is {@link * Returns the IP address of the client. The initial value is {@link BedrockData#getIp()} when
* BedrockData#getIp()} (or null if BedrockData is null) but will return the changed IP if it * BedrockData isn't null, or null if BedrockData is null. This method will return the changed
* has been changed using {@link #setBedrockIp(String)} * 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 * 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 :) * 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. * Returns the reason to disconnect the current player.

View File

@@ -41,7 +41,6 @@ import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.packet.Handshake; import net.md_5.bungee.protocol.packet.Handshake;
import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.config.ProxyFloodgateConfig;
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;
@@ -93,10 +92,10 @@ public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter {
HandshakeResult result = handler.handle(ctx.channel(), data); HandshakeResult result = handler.handle(ctx.channel(), data);
HandshakeData handshakeData = result.getHandshakeData(); HandshakeData handshakeData = result.getHandshakeData();
// we'll change the IP address from the proxy to the IP of the Bedrock client very early on // 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 IP of the Bedrock client // so that almost every plugin will use the real IP of the client
if (result.getFloodgatePlayer() != null) { InetSocketAddress newIp = result.getNewIp(ctx.channel());
if (newIp != null) {
HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class); HandlerBoss handlerBoss = ctx.pipeline().get(HandlerBoss.class);
// InitialHandler extends PacketHandler and implements PendingConnection // InitialHandler extends PacketHandler and implements PendingConnection
InitialHandler connection = ReflectionUtils.getCastedValue(handlerBoss, HANDLER); InitialHandler connection = ReflectionUtils.getCastedValue(handlerBoss, HANDLER);
@@ -104,10 +103,7 @@ public class BungeeProxyDataHandler extends ChannelInboundHandlerAdapter {
ChannelWrapper channelWrapper = ChannelWrapper channelWrapper =
ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER); ReflectionUtils.getCastedValue(connection, CHANNEL_WRAPPER);
InetSocketAddress address = channelWrapper.setRemoteAddress(newIp);
result.getFloodgatePlayer().getProperty(PropertyKey.SOCKET_ADDRESS);
channelWrapper.setRemoteAddress(address);
} }
if (handshakeData.getDisconnectReason() != null) { if (handshakeData.getDisconnectReason() != null) {

View File

@@ -45,7 +45,7 @@ public class HandshakeDataImpl implements HandshakeData {
@Setter private LinkedPlayer linkedPlayer; @Setter private LinkedPlayer linkedPlayer;
@Setter private String hostname; @Setter private String hostname;
@Setter private String bedrockIp; @Setter private String ip;
@Setter private String disconnectReason; @Setter private String disconnectReason;
public HandshakeDataImpl( public HandshakeDataImpl(
@@ -74,7 +74,7 @@ public class HandshakeDataImpl implements HandshakeData {
} }
javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid()); javaUniqueId = Utils.getJavaUuid(bedrockData.getXuid());
this.bedrockIp = bedrockData.getIp(); this.ip = bedrockData.getIp();
} }
this.javaUsername = javaUsername; this.javaUsername = javaUsername;

View File

@@ -27,9 +27,9 @@ package org.geysermc.floodgate.command;
import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE; import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.CommandManager; import cloud.commandframework.CommandManager;
import cloud.commandframework.Description;
import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -55,7 +55,7 @@ public final class LinkAccountCommand implements FloodgateCommand {
@Override @Override
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) { public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
return commandManager.commandBuilder("linkaccount", 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) .senderType(PlayerAudience.class)
.permission("floodgate.command.linkaccount") .permission("floodgate.command.linkaccount")
.argument(UserAudienceArgument.of("player", true)) .argument(UserAudienceArgument.of("player", true))

View File

@@ -27,9 +27,9 @@ package org.geysermc.floodgate.command;
import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE; import static org.geysermc.floodgate.command.CommonCommandMessage.CHECK_CONSOLE;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.CommandManager; import cloud.commandframework.CommandManager;
import cloud.commandframework.Description;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.google.inject.Inject; import com.google.inject.Inject;
import lombok.Getter; import lombok.Getter;
@@ -49,7 +49,7 @@ public final class UnlinkAccountCommand implements FloodgateCommand {
@Override @Override
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) { public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
return commandManager.commandBuilder("unlinkaccount", 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) .senderType(PlayerAudience.class)
.permission("floodgate.command.unlinkaccount") .permission("floodgate.command.unlinkaccount")
.handler(this::execute) .handler(this::execute)

View File

@@ -25,15 +25,18 @@
package org.geysermc.floodgate.command; 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.Command;
import cloud.commandframework.CommandManager; import cloud.commandframework.CommandManager;
import cloud.commandframework.Description;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.inject.Inject; 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.api.logger.FloodgateLogger;
import org.geysermc.floodgate.config.FloodgateConfig; 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.CommandUtil;
import org.geysermc.floodgate.platform.command.FloodgateCommand; import org.geysermc.floodgate.platform.command.FloodgateCommand;
import org.geysermc.floodgate.player.UserAudience; import org.geysermc.floodgate.player.UserAudience;
@@ -48,7 +51,7 @@ public class WhitelistCommand implements FloodgateCommand {
@Override @Override
public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) { public Command<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
Command.Builder<UserAudience> builder = commandManager.commandBuilder("fwhitelist", Command.Builder<UserAudience> builder = commandManager.commandBuilder("fwhitelist",
Description.of("Easy way to whitelist Bedrock players")) ArgumentDescription.of("Easy way to whitelist Bedrock players"))
.permission("floodgate.command.fwhitelist"); .permission("floodgate.command.fwhitelist");
commandManager.command(builder commandManager.command(builder
@@ -73,8 +76,7 @@ public class WhitelistCommand implements FloodgateCommand {
} }
if (name.length() < 1 || name.length() > 16) { if (name.length() < 1 || name.length() > 16) {
sender.sendMessage(Component.text( sender.sendMessage(Message.INVALID_USERNAME);
"The given username '" + name + "' is not a valid username."));
return; return;
} }
@@ -85,8 +87,7 @@ public class WhitelistCommand implements FloodgateCommand {
HttpUtils.asyncGet(Constants.GET_XUID_URL + name) HttpUtils.asyncGet(Constants.GET_XUID_URL + name)
.whenComplete((result, error) -> { .whenComplete((result, error) -> {
if (error != null) { if (error != null) {
sender.sendMessage(Component.text( sender.sendMessage(Message.API_UNAVAILABLE);
"An error occurred. See the console for more info"));
error.printStackTrace(); error.printStackTrace();
return; return;
} }
@@ -95,8 +96,7 @@ public class WhitelistCommand implements FloodgateCommand {
boolean success = response.get("success").getAsBoolean(); boolean success = response.get("success").getAsBoolean();
if (!success) { if (!success) {
sender.sendMessage(Component.text( sender.sendMessage(Message.UNEXPECTED_ERROR);
"An error occurred. See the console for more info"));
logger.error( logger.error(
"Got an error from requesting the xuid of a Bedrock player: {}", "Got an error from requesting the xuid of a Bedrock player: {}",
response.get("message").getAsString()); response.get("message").getAsString());
@@ -105,8 +105,7 @@ public class WhitelistCommand implements FloodgateCommand {
JsonObject data = response.getAsJsonObject("data"); JsonObject data = response.getAsJsonObject("data");
if (data.size() == 0) { if (data.size() == 0) {
sender.sendMessage(Component.text( sender.sendMessage(Message.USER_NOT_FOUND);
"Couldn't find the user '" + tempName + "'"));
return; return;
} }
@@ -114,12 +113,18 @@ public class WhitelistCommand implements FloodgateCommand {
CommandUtil commandUtil = context.get("CommandUtil"); CommandUtil commandUtil = context.get("CommandUtil");
try { try {
if (add && commandUtil.whitelistPlayer(xuid, tempName)) { if (add) {
sender.sendMessage(Component.text("Player has been whitelisted :)")); if (commandUtil.whitelistPlayer(xuid, tempName)) {
} else if (!add && commandUtil.removePlayerFromWhitelist(xuid, tempName)) { sender.sendMessage(Message.PLAYER_ADDED);
sender.sendMessage(Component.text("Player has been removed :o"));
} else { } else {
sender.sendMessage(Component.text("Player was already whitelisted :o")); sender.sendMessage(Message.PLAYER_ALREADY_WHITELISTED);
}
} else {
if (commandUtil.removePlayerFromWhitelist(xuid, tempName)) {
sender.sendMessage(Message.PLAYER_REMOVED);
} else {
sender.sendMessage(Message.PLAYER_NOT_WHITELISTED);
}
} }
} catch (Exception exception) { } catch (Exception exception) {
logger.error( logger.error(
@@ -133,4 +138,24 @@ public class WhitelistCommand implements FloodgateCommand {
public void execute(CommandContext<UserAudience> context) { public void execute(CommandContext<UserAudience> context) {
// ignored, all the logic is in the other method // 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(" ");
}
}
} }

View File

@@ -150,10 +150,7 @@ public final class FloodgateHandshakeHandler {
bedrockData.getVerifyCode()); bedrockData.getVerifyCode());
} }
UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid()); correctHostname(handshakeData);
handshakeData.setHostname(correctHostname(
handshakeData.getHostname(), bedrockData, javaUuid
));
FloodgatePlayer player = FloodgatePlayer player =
FloodgatePlayerImpl.from(bedrockData, handshakeData); FloodgatePlayerImpl.from(bedrockData, handshakeData);
@@ -163,8 +160,7 @@ public final class FloodgateHandshakeHandler {
channel.attr(playerAttribute).set(player); channel.attr(playerAttribute).set(player);
int port = ((InetSocketAddress) channel.remoteAddress()).getPort(); int port = ((InetSocketAddress) channel.remoteAddress()).getPort();
InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getBedrockIp(), InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getIp(), port);
port);
player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress); player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress);
return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player); return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player);
@@ -204,27 +200,27 @@ public final class FloodgateHandshakeHandler {
handshakeHandlers.callHandshakeHandlers(handshakeData); handshakeHandlers.callHandshakeHandlers(handshakeData);
if (bedrockData != null) { if (bedrockData != null) {
UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid()); correctHostname(handshakeData);
handshakeData.setHostname(correctHostname(
handshakeData.getHostname(), bedrockData, javaUuid
));
} }
return new HandshakeResult(resultType, handshakeData, bedrockData, null); 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 // 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 (split.length >= 3) {
if (logger.isDebug()) { if (logger.isDebug()) {
logger.info("Replacing hostname arg1 '{}' with '{}' and arg2 '{}' with '{}'", 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(); split[2] = correctUuid.toString();
} }
return String.join("\0", split); handshakeData.setHostname(String.join("\0", split));
} }
private LinkedPlayer fetchLinkedPlayer(UUID javaUniqueId) { private LinkedPlayer fetchLinkedPlayer(UUID javaUniqueId) {
@@ -255,5 +251,16 @@ public final class FloodgateHandshakeHandler {
private final HandshakeData handshakeData; private final HandshakeData handshakeData;
private final BedrockData bedrockData; private final BedrockData bedrockData;
private final FloodgatePlayer floodgatePlayer; 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;
}
} }
} }

View File

@@ -46,7 +46,6 @@ import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.config.FloodgateConfig; 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;
@@ -182,7 +181,12 @@ public final class SpigotDataHandler extends ChannelInboundHandlerAdapter {
HandshakeData handshakeData = result.getHandshakeData(); HandshakeData handshakeData = result.getHandshakeData();
setValue(packet, HANDSHAKE_HOST, handshakeData.getHostname()); 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) { if (handshakeData.getDisconnectReason() != null) {
ctx.close(); //todo disconnect with message ctx.close(); //todo disconnect with message
@@ -220,10 +224,6 @@ public final class SpigotDataHandler extends ChannelInboundHandlerAdapter {
if (!bungeeData) { if (!bungeeData) {
// Use a spoofedUUID for initUUID (just like Bungeecord) // Use a spoofedUUID for initUUID (just like Bungeecord)
setValue(networkManager, "spoofedUUID", player.getCorrectUniqueId()); 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) { } else if (isLogin) {
if (!bungeeData) { if (!bungeeData) {

View File

@@ -36,11 +36,11 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.api.handshake.HandshakeData;
import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.logger.FloodgateLogger;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.geysermc.floodgate.api.player.PropertyKey;
import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.config.ProxyFloodgateConfig;
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;
@@ -97,6 +97,12 @@ public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter
HandshakeResult result = handshakeHandler.handle(ctx.channel(), address); HandshakeResult result = handshakeHandler.handle(ctx.channel(), address);
HandshakeData handshakeData = result.getHandshakeData(); 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) { if (handshakeData.getDisconnectReason() != null) {
ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason()); ctx.channel().attr(kickMessageAttribute).set(handshakeData.getDisconnectReason());
return; return;
@@ -124,9 +130,6 @@ public final class VelocityProxyDataHandler extends ChannelInboundHandlerAdapter
setValue(packet, HANDSHAKE_SERVER_ADDRESS, handshakeData.getHostname()); 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", logger.info("Floodgate player who is logged in as {} {} joined",
player.getCorrectUsername(), player.getCorrectUniqueId()); player.getCorrectUsername(), player.getCorrectUniqueId());
} }