diff --git a/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java b/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java index f1ba3e17..bafde963 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java +++ b/api/src/main/java/org/geysermc/floodgate/api/FloodgateApi.java @@ -32,6 +32,7 @@ import org.geysermc.cumulus.Form; import org.geysermc.cumulus.util.FormBuilder; import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.unsafe.Unsafe; public interface FloodgateApi { /** @@ -139,4 +140,6 @@ public interface FloodgateApi { default PlayerLink getPlayerLink() { return InstanceHolder.getPlayerLink(); } + + Unsafe unsafe(); } diff --git a/api/src/main/java/org/geysermc/floodgate/api/unsafe/Unsafe.java b/api/src/main/java/org/geysermc/floodgate/api/unsafe/Unsafe.java new file mode 100644 index 00000000..519d2cde --- /dev/null +++ b/api/src/main/java/org/geysermc/floodgate/api/unsafe/Unsafe.java @@ -0,0 +1,43 @@ +/* + * 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.api.unsafe; + +import java.util.UUID; +import org.geysermc.floodgate.api.player.FloodgatePlayer; + +public interface Unsafe { + void sendPacket(UUID bedrockPlayer, byte[] packetData, boolean encrypt); + + default void sendPacket(UUID bedrockPlayer, byte[] packetData) { + sendPacket(bedrockPlayer, packetData, true); + } + + void sendPacket(FloodgatePlayer player, byte[] packetData, boolean encrypt); + + default void sendPacket(FloodgatePlayer player, byte[] packetData) { + sendPacket(player, packetData, true); + } +} diff --git a/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java b/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java index f06465a1..00f64cf0 100644 --- a/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java +++ b/core/src/main/java/org/geysermc/floodgate/api/ProxyFloodgateApi.java @@ -26,6 +26,7 @@ package org.geysermc.floodgate.api; import java.nio.charset.StandardCharsets; +import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; @@ -37,8 +38,9 @@ public final class ProxyFloodgateApi extends SimpleFloodgateApi { public ProxyFloodgateApi( PluginMessageManager pluginMessageManager, FloodgateConfigHolder configHolder, + FloodgateLogger logger, FloodgateCipher cipher) { - super(pluginMessageManager, configHolder); + super(pluginMessageManager, configHolder, logger); this.cipher = cipher; } diff --git a/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java b/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java index 359e75f2..ae9eab51 100644 --- a/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java +++ b/core/src/main/java/org/geysermc/floodgate/api/SimpleFloodgateApi.java @@ -37,7 +37,9 @@ import javax.annotation.Nullable; import lombok.RequiredArgsConstructor; import org.geysermc.cumulus.Form; import org.geysermc.cumulus.util.FormBuilder; +import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.unsafe.Unsafe; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.player.FloodgatePlayerImpl; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; @@ -52,6 +54,7 @@ public class SimpleFloodgateApi implements FloodgateApi { private final Map players = new HashMap<>(); private final PluginMessageManager pluginMessageManager; private final FloodgateConfigHolder configHolder; + private final FloodgateLogger logger; @Override public String getPlayerPrefix() { @@ -152,6 +155,15 @@ public class SimpleFloodgateApi implements FloodgateApi { }); } + @Override + public final Unsafe unsafe() { + String callerClass = Thread.currentThread().getStackTrace()[2].getClassName(); + logger.warn("A plugin is trying to access an unsafe part of the Floodgate api!" + + " The use of this api can result in client crashes if used incorrectly." + + " Caller: " + callerClass); + return new UnsafeFloodgateApi(pluginMessageManager); + } + public FloodgatePlayer addPlayer(UUID uuid, FloodgatePlayer player) { return players.put(uuid, player); } diff --git a/core/src/main/java/org/geysermc/floodgate/api/UnsafeFloodgateApi.java b/core/src/main/java/org/geysermc/floodgate/api/UnsafeFloodgateApi.java new file mode 100644 index 00000000..a54ebd97 --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/api/UnsafeFloodgateApi.java @@ -0,0 +1,55 @@ +/* + * 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.api; + +import java.util.UUID; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.unsafe.Unsafe; +import org.geysermc.floodgate.pluginmessage.PluginMessageManager; +import org.geysermc.floodgate.pluginmessage.channel.PacketChannel; + +public final class UnsafeFloodgateApi implements Unsafe { + private final PacketChannel packetChannel; + + UnsafeFloodgateApi(PluginMessageManager pluginMessageManager) { + StackTraceElement element = Thread.currentThread().getStackTrace()[2]; + if (!SimpleFloodgateApi.class.getName().equals(element.getClassName())) { + throw new IllegalStateException("Use the Floodgate api to get an instance"); + } + + packetChannel = pluginMessageManager.getChannel(PacketChannel.class); + } + + @Override + public void sendPacket(UUID bedrockPlayer, byte[] packetData, boolean encrypt) { + packetChannel.sendPacket(bedrockPlayer, packetData, encrypt, this); + } + + @Override + public void sendPacket(FloodgatePlayer player, byte[] packetData, boolean encrypt) { + sendPacket(player.getCorrectUniqueId(), packetData, encrypt); + } +} diff --git a/core/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java b/core/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java index 282667d8..f8218c73 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/PluginMessageModule.java @@ -30,6 +30,7 @@ import com.google.inject.Singleton; import com.google.inject.multibindings.ProvidesIntoSet; import org.geysermc.floodgate.pluginmessage.PluginMessageChannel; import org.geysermc.floodgate.pluginmessage.channel.FormChannel; +import org.geysermc.floodgate.pluginmessage.channel.PacketChannel; import org.geysermc.floodgate.pluginmessage.channel.SkinChannel; import org.geysermc.floodgate.pluginmessage.channel.TransferChannel; import org.geysermc.floodgate.register.PluginMessageRegister; @@ -57,4 +58,10 @@ public final class PluginMessageModule extends AbstractModule { public PluginMessageChannel transferChannel() { return new TransferChannel(); } + + @Singleton + @ProvidesIntoSet + public PluginMessageChannel packetChannel() { + return new PacketChannel(); + } } diff --git a/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java index 1133a73f..99afa520 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java @@ -31,6 +31,7 @@ import com.google.inject.name.Named; import java.nio.file.Path; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.config.ProxyFloodgateConfig; @@ -60,7 +61,8 @@ public final class ProxyCommonModule extends CommonModule { public ProxyFloodgateApi proxyFloodgateApi( PluginMessageManager pluginMessageManager, FloodgateConfigHolder configHolder, + FloodgateLogger logger, FloodgateCipher cipher) { - return new ProxyFloodgateApi(pluginMessageManager, configHolder, cipher); + return new ProxyFloodgateApi(pluginMessageManager, configHolder, logger, cipher); } } diff --git a/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java b/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java index 27f850d9..4b696fcb 100644 --- a/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java +++ b/core/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java @@ -30,6 +30,7 @@ import com.google.inject.Singleton; import com.google.inject.name.Named; import java.nio.file.Path; import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; @@ -50,7 +51,8 @@ public final class ServerCommonModule extends CommonModule { @Singleton public SimpleFloodgateApi floodgateApi( PluginMessageManager pluginMessageManager, - FloodgateConfigHolder configHolder) { - return new SimpleFloodgateApi(pluginMessageManager, configHolder); + FloodgateConfigHolder configHolder, + FloodgateLogger logger) { + return new SimpleFloodgateApi(pluginMessageManager, configHolder, logger); } } diff --git a/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/FormChannel.java b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/FormChannel.java index ec12f46b..5a436474 100644 --- a/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/FormChannel.java +++ b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/FormChannel.java @@ -93,7 +93,7 @@ public class FormChannel implements PluginMessageChannel { public boolean sendForm(UUID player, Form form) { byte[] formData = createFormData(form); - return pluginMessageUtils.sendMessage(player, false, getIdentifier(), formData); + return pluginMessageUtils.sendMessage(player, getIdentifier(), formData); } public byte[] createFormData(Form form) { diff --git a/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/PacketChannel.java b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/PacketChannel.java new file mode 100644 index 00000000..8b97cd46 --- /dev/null +++ b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/PacketChannel.java @@ -0,0 +1,74 @@ +/* + * 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.pluginmessage.channel; + +import com.google.inject.Inject; +import java.util.UUID; +import org.geysermc.floodgate.api.UnsafeFloodgateApi; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageUtils; +import org.geysermc.floodgate.pluginmessage.PluginMessageChannel; + +public final class PacketChannel implements PluginMessageChannel { + @Inject private PluginMessageUtils pluginMessageUtils; + + @Override + public String getIdentifier() { + return "floodgate:packet"; + } + + @Override + public Result handleProxyCall(byte[] data, UUID targetUuid, String targetUsername, + Identity targetIdentity, UUID sourceUuid, String sourceUsername, + Identity sourceIdentity) { + if (sourceIdentity == Identity.SERVER) { + // send it to the client + return Result.forward(); + } + + if (sourceIdentity == Identity.PLAYER) { + return handleServerCall(data, targetUuid, targetUsername); + } + + return Result.handled(); + } + + @Override + public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) { + return Result.kick("Cannot send packets from Geyser/Floodgate to Floodgate"); + } + + public boolean sendPacket(UUID player, byte[] packet, boolean encrypt, UnsafeFloodgateApi api) { + if (api == null) { + throw new IllegalArgumentException("Can only send a packet using the unsafe api"); + } + + byte[] finalData = new byte[packet.length + 1]; + finalData[0] = (byte) (encrypt ? 1 : 0); + System.arraycopy(packet, 0, finalData, 1, packet.length); + + return pluginMessageUtils.sendMessage(player, getIdentifier(), finalData); + } +} diff --git a/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java index 9051bcde..69b3e4a3 100644 --- a/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java +++ b/core/src/main/java/org/geysermc/floodgate/pluginmessage/channel/TransferChannel.java @@ -76,6 +76,6 @@ public class TransferChannel implements PluginMessageChannel { data[3] = (byte) (port); System.arraycopy(addressBytes, 0, data, 4, addressBytes.length); - return pluginMessageUtils.sendMessage(player, false, getIdentifier(), data); + return pluginMessageUtils.sendMessage(player, getIdentifier(), data); } }