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);
/**
* 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.

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.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) {

View File

@@ -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;

View File

@@ -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<UserAudience> buildCommand(CommandManager<UserAudience> 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))

View File

@@ -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<UserAudience> buildCommand(CommandManager<UserAudience> 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)

View File

@@ -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<UserAudience> buildCommand(CommandManager<UserAudience> commandManager) {
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");
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(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) {
logger.error(
@@ -133,4 +138,24 @@ public class WhitelistCommand implements FloodgateCommand {
public void execute(CommandContext<UserAudience> 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(" ");
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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());
}