From 1436069cb56ec4090b2c247aff8384ec5eae6763 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 2 Mar 2024 00:43:42 +0100 Subject: [PATCH 1/5] Mod detection refactoring Adding Lunar support Fixing LabyMod support Restricting ReplayMod Restricting WorldDownloader Enabling Fabric without FabricModSender Adding logging for unknown channels Untested. Signed-off-by: Lixfel --- build.gradle | 12 ++ src/de/steamwar/bungeecore/BungeeCore.java | 38 +--- .../bungeecore/commands/TypeMappers.java | 5 - .../bungeecore/commands/WhoisCommand.java | 4 +- .../bungeecore/listeners/BanListener.java | 6 +- .../bungeecore/listeners/BrandListener.java | 71 ------- .../listeners/ConnectionListener.java | 4 +- .../listeners/NonFabricFabricCheck.java | 97 --------- .../bungeecore/listeners/PluginMessage.java | 177 ++++++++++++++++ .../bungeecore/listeners/mods/Badlion.java | 31 --- .../bungeecore/listeners/mods/Fabric.java | 193 ------------------ .../bungeecore/listeners/mods/Forge.java | 147 ------------- .../bungeecore/listeners/mods/Forge12.java | 113 ---------- .../bungeecore/listeners/mods/LabyMod.java | 184 ----------------- .../listeners/mods/ModLoaderBlocker.java | 87 -------- .../listeners/mods/WorldDownloader.java | 33 --- .../listeners/ping/PingListener.java | 32 --- .../listeners/ping/SteamWarServerPing.java | 90 -------- src/de/steamwar/bungeecore/mods/Badlion.java | 57 ++++++ src/de/steamwar/bungeecore/mods/FML.java | 94 +++++++++ src/de/steamwar/bungeecore/mods/FML2.java | 151 ++++++++++++++ .../bungeecore/mods/FabricModSender.java | 141 +++++++++++++ src/de/steamwar/bungeecore/mods/LabyMod.java | 83 ++++++++ src/de/steamwar/bungeecore/mods/Lunar.java | 132 ++++++++++++ .../mods/Utils.java => mods/ModUtils.java} | 82 +++----- .../steamwar/bungeecore/mods/ReplayMod.java | 71 +++++++ .../steamwar/bungeecore/mods/Schematica.java | 40 ++++ .../bungeecore/mods/ServerListPing.java | 119 +++++++++++ .../bungeecore/mods/WorldDownloader.java | 56 +++++ .../bungeecore/network/NetworkReceiver.java | 58 ------ .../network/SWScriptSyntaxForwarder.java | 48 ----- .../steamwar/messages/BungeeCore.properties | 7 - .../messages/BungeeCore_de.properties | 7 - 33 files changed, 1178 insertions(+), 1292 deletions(-) delete mode 100644 src/de/steamwar/bungeecore/listeners/BrandListener.java delete mode 100644 src/de/steamwar/bungeecore/listeners/NonFabricFabricCheck.java create mode 100644 src/de/steamwar/bungeecore/listeners/PluginMessage.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/Badlion.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/Fabric.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/Forge.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/Forge12.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/LabyMod.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java delete mode 100644 src/de/steamwar/bungeecore/listeners/mods/WorldDownloader.java delete mode 100644 src/de/steamwar/bungeecore/listeners/ping/PingListener.java delete mode 100644 src/de/steamwar/bungeecore/listeners/ping/SteamWarServerPing.java create mode 100644 src/de/steamwar/bungeecore/mods/Badlion.java create mode 100644 src/de/steamwar/bungeecore/mods/FML.java create mode 100644 src/de/steamwar/bungeecore/mods/FML2.java create mode 100644 src/de/steamwar/bungeecore/mods/FabricModSender.java create mode 100644 src/de/steamwar/bungeecore/mods/LabyMod.java create mode 100644 src/de/steamwar/bungeecore/mods/Lunar.java rename src/de/steamwar/bungeecore/{listeners/mods/Utils.java => mods/ModUtils.java} (55%) create mode 100644 src/de/steamwar/bungeecore/mods/ReplayMod.java create mode 100644 src/de/steamwar/bungeecore/mods/Schematica.java create mode 100644 src/de/steamwar/bungeecore/mods/ServerListPing.java create mode 100644 src/de/steamwar/bungeecore/mods/WorldDownloader.java delete mode 100644 src/de/steamwar/bungeecore/network/NetworkReceiver.java delete mode 100644 src/de/steamwar/bungeecore/network/SWScriptSyntaxForwarder.java diff --git a/build.gradle b/build.gradle index 44980581..95a7911f 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,15 @@ repositories { mavenCentral() maven { url 'https://m2.dv8tion.net/releases' + content { + includeGroup 'net.dv8tion' + } + } + maven { + url 'https://repo.lunarclient.dev' + content { + includeGroup 'com.lunarclient' + } } } @@ -75,4 +84,7 @@ dependencies { } implementation project(":CommonCore") + + implementation 'com.lunarclient:apollo-api:1.1.0' + implementation 'com.lunarclient:apollo-common:1.1.0' } \ No newline at end of file diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 643921c9..c043d27c 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -23,11 +23,9 @@ import de.steamwar.bungeecore.bot.SteamwarDiscordBot; import de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig; import de.steamwar.bungeecore.commands.*; import de.steamwar.bungeecore.listeners.*; -import de.steamwar.bungeecore.listeners.mods.*; -import de.steamwar.bungeecore.listeners.ping.PingListener; +import de.steamwar.bungeecore.mods.ServerListPing; +import de.steamwar.bungeecore.mods.*; import de.steamwar.bungeecore.network.BungeeNetworkHandler; -import de.steamwar.bungeecore.network.NetworkReceiver; -import de.steamwar.bungeecore.network.SWScriptSyntaxForwarder; import de.steamwar.bungeecore.tablist.TablistManager; import de.steamwar.command.SWCommandUtils; import de.steamwar.command.SWTypeMapperCreator; @@ -38,7 +36,6 @@ import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.Team; import de.steamwar.sql.UserElo; import de.steamwar.sql.internal.Statement; -import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; @@ -76,11 +73,6 @@ public class BungeeCore extends Plugin { @Override public void onEnable(){ - getProxy().registerChannel("sw:bridge"); - getProxy().registerChannel("fabricmodsender:mods"); - getProxy().registerChannel("nochatreports:sync"); - getProxy().registerChannel("sw:script_syntax"); - setInstance(this); MAIN_SERVER = ProxyServer.getInstance().getConfig().getListeners().stream().anyMatch(info -> ((InetSocketAddress) info.getSocketAddress()).getPort() == 25565); loadConfig(); @@ -98,25 +90,21 @@ public class BungeeCore extends Plugin { return tabCompleter.apply(sender, s); } }); - BungeeCord.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS); + ProxyServer.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS); - new NonFabricFabricCheck(); - - new SWScriptSyntaxForwarder(); - new ConnectionListener(); - new Forge(); - new Forge12(); - new LabyMod(); + new ServerListPing(); + new PluginMessage(); + new Schematica(); new Badlion(); + new FabricModSender(); + new ReplayMod(); + new FML2(); + + new ConnectionListener(); new ChatListener(); new BanListener(); new CheckListener(); - new ModLoaderBlocker(); - new WorldDownloader(); - new BrandListener(); - new Fabric(); new SubserverProtocolFixer(); - new PingListener(); local = new Node.LocalNode(); if(MAIN_SERVER) { @@ -186,7 +174,6 @@ public class BungeeCore extends Plugin { new EventStarter(); new SessionManager(); - new NetworkReceiver(); BungeeNetworkHandler.register(); tablistManager = new TablistManager(); new SettingsChangedListener(); @@ -317,9 +304,6 @@ public class BungeeCore extends Plugin { serverName, cmds.toArray(new String[0]) ); - if(server.getBoolean("modchecked", false)) { - ModLoaderBlocker.addServer(serverName); - } } File discordFile = new File(System.getProperty("user.home"), "discord.yml"); diff --git a/src/de/steamwar/bungeecore/commands/TypeMappers.java b/src/de/steamwar/bungeecore/commands/TypeMappers.java index 7ae85ab7..1af07b5f 100644 --- a/src/de/steamwar/bungeecore/commands/TypeMappers.java +++ b/src/de/steamwar/bungeecore/commands/TypeMappers.java @@ -20,7 +20,6 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.ArenaMode; -import de.steamwar.bungeecore.listeners.mods.ModLoaderBlocker; import de.steamwar.command.SWCommandUtils; import de.steamwar.command.TypeMapper; import de.steamwar.command.TypeValidator; @@ -49,10 +48,6 @@ public class TypeMappers { return false; } - if (ModLoaderBlocker.isFabric(value)) { - messageSender.send("MODLOADER_DENIED"); - return false; - } return true; }; } diff --git a/src/de/steamwar/bungeecore/commands/WhoisCommand.java b/src/de/steamwar/bungeecore/commands/WhoisCommand.java index 0890c054..023d2110 100644 --- a/src/de/steamwar/bungeecore/commands/WhoisCommand.java +++ b/src/de/steamwar/bungeecore/commands/WhoisCommand.java @@ -21,7 +21,7 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Storage; -import de.steamwar.bungeecore.listeners.mods.Utils; +import de.steamwar.bungeecore.mods.ModUtils; import de.steamwar.command.SWCommand; import de.steamwar.command.SWCommandUtils; import de.steamwar.command.TypeMapper; @@ -118,7 +118,7 @@ public class WhoisCommand extends SWCommand { sender.system("WHOIS_CURRENT_SERVER", target.getServer().getInfo().getName()); sender.system("WHOIS_CURRENT_PROTOCOL", target.getPendingConnection().getVersion()); - List mods = Utils.playerModMap.get(user.getUUID()); + List mods = ModUtils.getPlayerModMap().get(user.getUUID()); if(mods == null) mods = Collections.emptyList(); diff --git a/src/de/steamwar/bungeecore/listeners/BanListener.java b/src/de/steamwar/bungeecore/listeners/BanListener.java index df8fa0ea..1e48bbd5 100644 --- a/src/de/steamwar/bungeecore/listeners/BanListener.java +++ b/src/de/steamwar/bungeecore/listeners/BanListener.java @@ -23,7 +23,6 @@ import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.commands.PunishmentCommand; import de.steamwar.bungeecore.commands.WebpasswordCommand; -import de.steamwar.bungeecore.listeners.mods.Forge; import de.steamwar.messages.ChatSender; import de.steamwar.sql.BannedUserIPs; import de.steamwar.sql.Punishment; @@ -57,7 +56,6 @@ public class BanListener extends BasicListener { List ips = BannedUserIPs.get(connection.getAddress().getAddress().getHostAddress()); if(!ips.isEmpty()){ - Timestamp highestBan = ips.get(0).getTimestamp(); boolean perma = false; for(BannedUserIPs banned : ips) { @@ -74,7 +72,7 @@ public class BanListener extends BasicListener { } ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/ban " + user.getUserName() + " " + (perma?"perma":highestBan.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy_HH:mm"))) - + " Bannumgehung"); + + " Ban Evasion - Bannumgehung"); ChatSender.serverteamReceivers().forEach(sender -> sender.system( "BAN_AVOIDING_ALERT", @@ -89,7 +87,7 @@ public class BanListener extends BasicListener { )); } - Forge.onServerConnected(event); + event.completeIntent(BungeeCore.get()); }); } } diff --git a/src/de/steamwar/bungeecore/listeners/BrandListener.java b/src/de/steamwar/bungeecore/listeners/BrandListener.java deleted file mode 100644 index 0877d1a7..00000000 --- a/src/de/steamwar/bungeecore/listeners/BrandListener.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - */ - -package de.steamwar.bungeecore.listeners; - -import de.steamwar.messages.ChatSender; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.protocol.DefinedPacket; - -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; - -public class BrandListener extends BasicListener { - - private static boolean isLocalHost(InetAddress addr) { - if (addr.isAnyLocalAddress() || addr.isLoopbackAddress()) { - return true; - } - try { - return NetworkInterface.getByInetAddress(addr) != null; - } catch (SocketException e) { - return false; - } - } - - @EventHandler - public void onServerSwitch(PluginMessageEvent event) { - if(!event.getTag().equals("minecraft:brand") && !event.getTag().equals("MC|Brand")) { - return; - } - - if (event.getReceiver() == null || isLocalHost(event.getReceiver().getAddress().getAddress())) { - return; - } - - if (!(event.getReceiver() instanceof ProxiedPlayer)) { - return; - } - event.setCancelled(true); - - ProxiedPlayer player = (ProxiedPlayer) event.getReceiver(); - String brandString = ChatSender.of(player).parseToLegacy("STEAMWAR_BRAND", ProxyServer.getInstance().getName(), player.getServer().getInfo().getName(), new String(event.getData(), 1, event.getData().length - 1)); - - ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer(); - DefinedPacket.writeString(brandString, brand); - player.sendData(event.getTag(), DefinedPacket.toArray(brand)); - brand.release(); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java index 2e8a8792..6cafdb01 100644 --- a/src/de/steamwar/bungeecore/listeners/ConnectionListener.java +++ b/src/de/steamwar/bungeecore/listeners/ConnectionListener.java @@ -29,7 +29,7 @@ import de.steamwar.bungeecore.commands.ChallengeCommand; import de.steamwar.bungeecore.commands.CheckCommand; import de.steamwar.bungeecore.commands.ModCommand; import de.steamwar.bungeecore.commands.MsgCommand; -import de.steamwar.bungeecore.listeners.mods.Utils; +import de.steamwar.bungeecore.mods.ModUtils; import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.UserPerm; import net.md_5.bungee.api.AbstractReconnectHandler; @@ -156,7 +156,7 @@ public class ConnectionListener extends BasicListener { public void onDisconnect(PlayerDisconnectEvent e){ ChallengeCommand.remove(e.getPlayer()); MsgCommand.remove(e.getPlayer()); - Utils.playerModMap.remove(e.getPlayer().getUniqueId()); + ModUtils.getPlayerModMap().remove(e.getPlayer().getUniqueId()); ModCommand.playerFilterType.remove(e.getPlayer()); } } diff --git a/src/de/steamwar/bungeecore/listeners/NonFabricFabricCheck.java b/src/de/steamwar/bungeecore/listeners/NonFabricFabricCheck.java deleted file mode 100644 index 0f825ab4..00000000 --- a/src/de/steamwar/bungeecore/listeners/NonFabricFabricCheck.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2022 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.bungeecore.listeners; - -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.Message; -import de.steamwar.bungeecore.Storage; -import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.LoginEvent; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; - -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -public class NonFabricFabricCheck extends BasicListener { - - private final Set usingFabric = new HashSet<>(); - - private final Set checking = new HashSet<>(); - private final Set vanilla = new HashSet<>(); - - { - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), usingFabric::clear, 0, 15, TimeUnit.MINUTES); - } - - @EventHandler - public void pluginMessageEvent(PluginMessageEvent e) { - Connection sender = e.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - - ProxiedPlayer p = (ProxiedPlayer) sender; - if (e.getTag().equals("minecraft:register") && new String(e.getData()).contains("fabric-screen-handler-api-v1:open_screen")) { - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - if (!sender.isConnected()) return; - if (!vanilla.remove(p)) return; - if (Storage.fabricCheckedPlayers.containsKey(p)) return; - p.disconnect(Message.parse("MOD_USE_MODSENDER", p)); - }, 25, TimeUnit.SECONDS); - return; - } - - if(!e.getTag().equals("minecraft:brand")) - return; - - if(!new String(e.getData()).equals("vanilla")) - return; - vanilla.add(p); - - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - if (!p.isConnected()) return; - if (Storage.fabricCheckedPlayers.containsKey(p)) return; - checking.add(p); - p.sendData("fabric-screen-handler-api-v1:open_screen", new byte[] {0}); - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> checking.remove(p), 1, TimeUnit.SECONDS); - }, 30, TimeUnit.SECONDS); - } - - @EventHandler - public void onPlayerPreLogin(LoginEvent e) { - if (usingFabric.remove(e.getConnection().getUniqueId())) { - e.getConnection().disconnect(Message.parse("MOD_USE_MODSENDER", Locale.getDefault())); - } - } - - @EventHandler - public void onPlayerDisconnect(PlayerDisconnectEvent e) { - if (checking.remove(e.getPlayer())) { - usingFabric.add(e.getPlayer().getUniqueId()); - } - vanilla.remove(e.getPlayer()); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/PluginMessage.java b/src/de/steamwar/bungeecore/listeners/PluginMessage.java new file mode 100644 index 00000000..d032be72 --- /dev/null +++ b/src/de/steamwar/bungeecore/listeners/PluginMessage.java @@ -0,0 +1,177 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.listeners; + +import com.lunarclient.apollo.ApolloManager; +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.commands.TeamCommand; +import de.steamwar.bungeecore.mods.*; +import de.steamwar.bungeecore.network.ServerMetaInfo; +import de.steamwar.messages.ChatSender; +import de.steamwar.network.packets.NetworkPacket; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +import java.net.InetSocketAddress; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +public class PluginMessage extends BasicListener { + + public static void send(ProxiedPlayer player, String legacyChannel, String channel, byte[] data) { + // 1.12 format change + player.sendData(player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data); + } + + //TODO change to warning level + private static final Parser UNKNOWN = event -> BungeeCore.get().getLogger().log(Level.INFO, () -> "Undefined PluginMessage on channel " + event.getTag() + " from " + event.getSender() + " received.\n" + Arrays.toString(event.getData())); + private static final Parser PASS_THROUGH = event -> event.setCancelled(false); + private static final Parser DROP = event -> {}; + + private final Lunar lunar = new Lunar(); + + private final Map handlers = new HashMap<>(); + + public PluginMessage() { + LabyMod labyMod = new LabyMod(); + FML fml = new FML(); + FabricModSender fabricModSender = new FabricModSender(); + WorldDownloader wdl = new WorldDownloader(); + + register("REGISTER", false, directional(PASS_THROUGH, this::clientRegistersChannel)); + register("minecraft:register", false, directional(PASS_THROUGH, this::clientRegistersChannel)); + + register("BungeeCord", false, onlySWSource(PASS_THROUGH)); + register("bungeecord:main", false, onlySWSource(PASS_THROUGH)); + register("MC|Brand", true, directional(this::steamWarBrand, this::userBrand)); + register("minecraft:brand", true, directional(this::steamWarBrand, this::userBrand)); + + register("sw:script_syntax", false, directional(onlySWSource(PASS_THROUGH), UNKNOWN)); + register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN)); + register("fabricmodsender:mods", true, directional(UNKNOWN, fabricModSender::handlePluginMessage)); + + register("WDL|REQUEST", false, DROP); + register("wdl:request", false, DROP); + register("WDL|INIT", true, directional(UNKNOWN, wdl::handlePluginMessage)); + register("wdl:init", true, directional(UNKNOWN, wdl::handlePluginMessage)); + + register(ApolloManager.PLUGIN_MESSAGE_CHANNEL, true, async(lunar::handlePluginMessage)); + register("LMC", true, directional(UNKNOWN, async(labyMod::handlePluginMessage))); + register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage))); + register(FML.CHANNEL, true, directional(UNKNOWN, fml::handlePluginMessage)); + + //litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65 + } + + @EventHandler + public void onPluginMessage(PluginMessageEvent event) { + event.setCancelled(true); + + BungeeCore.get().getLogger().log(Level.INFO, event + "\n" + new String(event.getData()) + "\n" + Arrays.toString(event.getData())); //TODO remove logging + + try { + handlers.getOrDefault(event.getTag(), UNKNOWN).handle(event); + } catch (Exception e) { + throw new SecurityException("PluginMessage handling exception: " + event + "\n" + Arrays.toString(event.getData()), e); + } + } + + private void register(String channel, boolean clientSideRegister, Parser handler) { + handlers.put(channel, handler); + if(clientSideRegister) + ProxyServer.getInstance().registerChannel(channel); + } + + private void clientRegistersChannel(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + + for(String channel : new String(event.getData()).split("\0")) { + if(channel.equals(ApolloManager.PLUGIN_MESSAGE_CHANNEL)) + lunar.sendRestrictions(player); + + if(!handlers.containsKey(channel)) + //TODO change to warning level + BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " registered unknown channel " + channel); + } + + PASS_THROUGH.handle(event); + } + + private void userBrand(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + String brand = new String(event.getData()); + + if(brand.equals("vanilla")) { + // nothing to do + } else if(brand.startsWith("lunarclient:")) { + lunar.sendRestrictions(player); + } else { + //TODO change to warning level + BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " joined with unknown brand " + brand); + } + + PASS_THROUGH.handle(event); + } + + private void steamWarBrand(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getReceiver(); + String brandString = ChatSender.of(player).parseToLegacy("STEAMWAR_BRAND", ProxyServer.getInstance().getName(), player.getServer().getInfo().getName(), new String(event.getData(), 1, event.getData().length - 1)); + + ByteBuf brand = ByteBufAllocator.DEFAULT.heapBuffer(); + DefinedPacket.writeString(brandString, brand); + player.sendData(event.getTag(), DefinedPacket.toArray(brand)); + brand.release(); + } + + + private Parser directional(Parser fromServer, Parser fromPlayer) { + return event -> { + if(event.getSender() instanceof ProxiedPlayer) + fromPlayer.handle(event); + else + fromServer.handle(event); + }; + } + + private Parser onlySWSource(Parser parser) { + return event -> { + if(TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) + parser.handle(event); + else + UNKNOWN.handle(event); + }; + } + + private Parser async(Parser parser) { + return event -> ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> parser.handle(event)); + } + + private interface Parser { + void handle(PluginMessageEvent event); + } +} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Badlion.java b/src/de/steamwar/bungeecore/listeners/mods/Badlion.java deleted file mode 100644 index a194f02d..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/Badlion.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.steamwar.bungeecore.listeners.mods; - -import de.steamwar.bungeecore.listeners.BasicListener; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.event.EventHandler; - -public class Badlion extends BasicListener { - - @EventHandler - public void onPostLogin(PostLoginEvent event) { - /* - { - "modsDisallowed": { - "Clear Glass":{"disabled":true}, - "ClearWater":{"disabled":true}, - "FOV Changer":{"disabled":true}, - "Hitboxes":{"disabled":true}, - "MiniMap":{"disabled":true}, - "MLG Cobweb":{"disabled":true}, - "Replay":{"disabled":true}, - "Schematica":{"disabled":true}, - "ToggleSneak":{"disabled":true}, - "ToggleSprint":{"disabled":true}, - "TNT Time":{"disabled":true} - } - } - */ - - event.getPlayer().sendData("badlion:mods", ("{\"Clear Glass\":{\"disabled\":true},\"ClearWater\":{\"disabled\":true},\"FOV Changer\":{\"disabled\":true},\"Hitboxes\":{\"disabled\":true},\"LevelHead\":{\"disabled\":true},\"MiniMap\":{\"disabled\":true},\"MLG Cobweb\":{\"disabled\":true},\"Replay\":{\"disabled\":true},\"Schematica\":{\"disabled\":true},\"ToggleSneak\":{\"disabled\":true},\"ToggleSprint\":{\"disabled\":true},\"TNT Time\":{\"disabled\":true}}").getBytes()); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Fabric.java b/src/de/steamwar/bungeecore/listeners/mods/Fabric.java deleted file mode 100644 index 7f5e90bc..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/Fabric.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.mods; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.Storage; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.Mod; -import de.steamwar.sql.SWException; -import de.steamwar.sql.SteamwarUser; -import net.md_5.bungee.BungeeCord; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.event.EventHandler; - -import java.nio.charset.StandardCharsets; -import java.nio.charset.UnsupportedCharsetException; -import java.util.*; -import java.util.concurrent.TimeUnit; - -public class Fabric extends BasicListener { - - public static void remove(ProxiedPlayer player) { - Storage.fabricCheckedPlayers.remove(player); - synchronized (Storage.fabricExpectPluginMessage) { - Storage.fabricExpectPluginMessage.remove(player); - } - } - - private static final HashSet ppCircumventerList = new HashSet<>(); - static { - ppCircumventerList.add("java"); - ppCircumventerList.add("minecraft"); - ppCircumventerList.add("org_joml_joml"); - ppCircumventerList.add("steamwarmodsender"); - } - - private static final Set neededMods = new HashSet<>(); - static { - neededMods.add("java"); - neededMods.add("minecraft"); - neededMods.add("fabricloader"); - neededMods.add("steamwarmodsender"); - } - - { - BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - synchronized (Storage.fabricExpectPluginMessage) { - for (Map.Entry entry : Storage.fabricExpectPluginMessage.entrySet()) { - if (!Storage.fabricCheckedPlayers.containsKey(entry.getKey())) { - continue; - } - if (System.currentTimeMillis() - entry.getValue() > TimeUnit.SECONDS.toMillis(20)) { - logMessage(SteamwarUser.get(entry.getKey().getUniqueId()), "Expected message not received", String.valueOf(entry.getValue())); - Storage.fabricExpectPluginMessage.remove(entry.getKey()); - return; - } - } - } - }, 0, 1, TimeUnit.SECONDS); - } - - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent e){ - if(!e.getTag().equals("fabricmodsender:mods")) - return; - - if (!(e.getSender() instanceof ProxiedPlayer)) { - return; - } - - ProxiedPlayer player = (ProxiedPlayer) e.getSender(); - SteamwarUser user = SteamwarUser.get(player.getUniqueId()); - byte[] data = e.getData(); - - if (!Storage.fabricCheckedPlayers.containsKey(player)) { - synchronized (Storage.fabricExpectPluginMessage) { - if (Storage.fabricExpectPluginMessage.containsKey(player)) { - logMessage(user, "Was not fabric checked but send message nonetheless", Arrays.toString(data)); - return; - } - } - } - Storage.fabricExpectPluginMessage.remove(player); - - List mods = new ArrayList<>(); - - Utils.VarInt varInt = Utils.readVarInt(data,0); - - if(data.length != varInt.length + varInt.value) { - logMessage(user, "Invalid message length", Arrays.toString(data)); - return; - } - - data = Arrays.copyOfRange(data,varInt.length, data.length); - - String dataString; - - try{ - dataString = new String(data, StandardCharsets.UTF_8); - }catch (UnsupportedCharsetException exception) { - logMessage(user, "Unsupported charset", Arrays.toString(data)); - return; - } - - JsonArray array; - - try { - array = JsonParser.parseString(dataString).getAsJsonArray(); - }catch (JsonSyntaxException exception) { - logMessage(user, "Invalid json", dataString); - return; - } - - for(JsonElement mod : array) { - mods.add(Mod.getOrCreate(mod.getAsString(), Mod.Platform.FABRIC)); - } - - boolean neededMods = neededModsContained(mods); - if(!neededMods) { - logMessage(user, "Needed mods are not contained", dataString); - return; - } - - if(ppCircumventerCheck(mods)) - logMessage(user, "PP circumventer suspicion", dataString); - - if(!Utils.handleMods(player,mods)) - return; - - if (Storage.fabricCheckedPlayers.containsKey(player)) { - long current = Storage.fabricCheckedPlayers.get(player); - if (current != dataString.hashCode()) { - logMessage(user, "Mods changed during runtime", dataString); - return; - } - } else { - Storage.fabricCheckedPlayers.put(player, dataString.hashCode()); - } - Storage.fabricPlayers.remove(player); - } - - @EventHandler - public void onServerSwitchEvent(ServerSwitchEvent e) { - if (e.getFrom() == null) return; - synchronized (Storage.fabricExpectPluginMessage) { - Storage.fabricExpectPluginMessage.put(e.getPlayer(), System.currentTimeMillis()); - } - } - - private boolean neededModsContained(List mods) { - return mods.stream() - .map(Mod::getModName) - .filter(neededMods::contains) - .count() == neededMods.size(); - } - - private void logMessage(SteamwarUser user, String reason, String data) { - SWException.log("FabricModSender " + user.getUserName() + ": " + reason, data); - } - - private boolean ppCircumventerCheck(List mods) { - for(Mod mod : mods) { - String name = mod.getModName(); - if(!name.startsWith("fabric") && !ppCircumventerList.contains(name)) - return false; - } - - return true; - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Forge.java b/src/de/steamwar/bungeecore/listeners/mods/Forge.java deleted file mode 100644 index 3749a37f..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/Forge.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.mods; - -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.Mod; -import io.netty.channel.ChannelPipeline; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.api.event.LoginEvent; -import net.md_5.bungee.connection.InitialHandler; -import net.md_5.bungee.netty.ChannelWrapper; -import net.md_5.bungee.netty.HandlerBoss; -import net.md_5.bungee.netty.PacketHandler; -import net.md_5.bungee.protocol.packet.LoginPayloadRequest; -import net.md_5.bungee.protocol.packet.LoginPayloadResponse; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.logging.Level; - -public class Forge extends BasicListener { - - private static final String WRAPPER = "fml:loginwrapper"; - - private static final Field initialHandlerCh; - static{ - try { - initialHandlerCh = InitialHandler.class.getDeclaredField("ch"); - } catch (NoSuchFieldException e) { - throw new SecurityException("Could not initialize Reflection", e); - } - initialHandlerCh.setAccessible(true); - } - - public static void onServerConnected(LoginEvent event) { - if(event.getConnection().getVersion() < 341 || event.getConnection().getVersion() > 763) { //1.13 - 1.20.1 - event.completeIntent(BungeeCore.get()); - return; - } - - //fml:handshake without mods, channels and registries - //for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 - event.getConnection().unsafe().sendPacket(new LoginPayloadRequest(1, WRAPPER, new byte[]{13,102,109,108,58,104,97,110,100,115,104,97,107,101,4,1,0,0,0})); - - InitialHandler handler = (InitialHandler) event.getConnection(); - - ChannelWrapper wrapper; - try{ - wrapper = (ChannelWrapper) initialHandlerCh.get(handler); - } catch (IllegalAccessException e) { - BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e); - event.completeIntent(BungeeCore.get()); - return; - } - - ChannelPipeline pipeline = wrapper.getHandle().pipeline(); - if(pipeline != null) { - HandlerBoss handlerBoss = pipeline.get(HandlerBoss.class); - if(handlerBoss != null) - handlerBoss.setHandler(new CustomPacketHandler(event)); - } - } - - private static class CustomPacketHandler extends PacketHandler { - private final LoginEvent event; - - public CustomPacketHandler(LoginEvent event) { - this.event = event; - } - - @Override - public String toString() { - return "SteamWar Forge Handler"; - } - - @Override - public void handle(LoginPayloadResponse response){ - byte[] data = response.getData(); - if(data == null) { - event.completeIntent(BungeeCore.get()); - return; - } - - //for more information see https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 - Utils.VarInt channelLength = Utils.readVarInt(data, 0); - int pos = channelLength.length; - if(!new String(data, pos, channelLength.value).equals("fml:handshake")) { - event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x00)")); - return; - } - pos += channelLength.value; - - Utils.VarInt length = Utils.readVarInt(data, pos); - pos += length.length; - if(channelLength.length + channelLength.value + length.length + length.value != data.length) { - event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x01)")); - return; - } - - Utils.VarInt packetId = Utils.readVarInt(data, pos); - pos += packetId.length; - if(packetId.value != 2) { - event.getConnection().disconnect(TextComponent.fromLegacyText("Invalid forge registry response (0x02)")); - return; - } - - Utils.VarInt modCount = Utils.readVarInt(data, pos); - pos += modCount.length; - - List mods = new ArrayList<>(); - for(int i = 0; i < modCount.value; i++) { - Utils.VarInt nameLength = Utils.readVarInt(data, pos); - pos += nameLength.length; - - mods.add(Mod.getOrCreate(new String(data, pos, nameLength.value), Mod.Platform.FORGE)); - pos += nameLength.value; - } - - PendingConnection connection = event.getConnection(); - if(!Utils.handleMods(connection.getUniqueId(), Locale.getDefault(), event::setCancelReason, mods)) { - event.setCancelled(true); - } - event.completeIntent(BungeeCore.get()); - } - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Forge12.java b/src/de/steamwar/bungeecore/listeners/mods/Forge12.java deleted file mode 100644 index 02a90822..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/Forge12.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2022 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.mods; - -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.Mod; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.UnpooledByteBufAllocator; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.event.EventHandler; - -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.TimeUnit; - -public class Forge12 extends BasicListener { - private static final String FMLHS = "FML|HS"; - private static final byte[] REGISTER; - private static final byte[] HELLO = new byte[]{0, 2, 0, 0, 0, 0}; - - private static final Set unlocked = new HashSet<>(); - - static { - ByteBuf buf = UnpooledByteBufAllocator.DEFAULT.directBuffer(7); - buf.writeByte(6); - buf.writeCharSequence(FMLHS, StandardCharsets.UTF_8); - REGISTER = new byte[buf.readableBytes()]; - buf.readBytes(REGISTER); - } - - - @EventHandler - public void onPostLogin(PostLoginEvent event) { - ProxiedPlayer player = event.getPlayer(); - - synchronized (unlocked) { - if(unlocked.contains(player.getUniqueId())){ - unlocked.remove(player.getUniqueId()); - return; - } - } - - if(player.getPendingConnection().getVersion() <= 340) { - player.sendData("REGISTER", REGISTER); //1.12- - player.sendData(FMLHS, HELLO); - } - } - - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent e){ - if(!e.getTag().equals(FMLHS)) - return; - - e.setCancelled(true); - byte[] data = e.getData(); - - Connection sender = e.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - ProxiedPlayer p = (ProxiedPlayer) sender; - - if (data[0] == 2) { - Utils.VarInt numMods = Utils.readVarInt(data, 1); - List mods = new LinkedList<>(); - - int bytePos = 1 + numMods.length; - for (int i = 0; i < numMods.value; i++) { - byte[] name = Arrays.copyOfRange(data, bytePos + 1, bytePos + data[bytePos] + 1); - bytePos += 1 + data[bytePos]; - //Version information is unused - bytePos += 1 + data[bytePos]; - - mods.add(Mod.getOrCreate(new String(name), Mod.Platform.FORGE)); - } - - if (Utils.handleMods(p, mods)) { - synchronized (unlocked) { - unlocked.add(p.getUniqueId()); - } - ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), - () -> p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten")), - 2, TimeUnit.SECONDS); - ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { - synchronized (unlocked) { - unlocked.remove(p.getUniqueId()); - } - }, 30, TimeUnit.SECONDS); - } - } - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/LabyMod.java b/src/de/steamwar/bungeecore/listeners/mods/LabyMod.java deleted file mode 100644 index 20fcaca8..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/LabyMod.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.mods; - -import com.google.gson.JsonObject; -import com.google.gson.stream.JsonReader; -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.Mod; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; - -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; - -public class LabyMod extends BasicListener { - - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent event){ - if(!event.getTag().equals("LMC")) - return; - - Connection sender = event.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - - event.setCancelled(true); - updateGameInfo((ProxiedPlayer) sender); - byte[] data = event.getData(); - - ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> runAsync(data, (ProxiedPlayer) sender)); - } - - private void runAsync(byte[] data, ProxiedPlayer player) { - VarString purpose = readString(data, 0); - if(!"INFO".equals(purpose.value)) - return; - - VarString value = readString(data, purpose.length); - List mods = new LinkedList<>(); - - try{ - InfoPacket info = new InfoPacket(value.value); - for(InfoPacket.Addon addon : info.addons) { - mods.add(Mod.getOrCreate(addon.name, Mod.Platform.LABYMOD)); - } - }catch(IOException e){ - BungeeCore.log("Could not read JSON", e); - } - - Utils.handleMods(player, mods); - } - - private VarString readString(byte[] array, int startPos){ - Utils.VarInt varInt = Utils.readVarInt(array, startPos); - startPos += varInt.length; - return new VarString(varInt.value+varInt.length, new String(Arrays.copyOfRange(array, startPos, startPos + varInt.value), StandardCharsets.UTF_8)); - } - - private void updateGameInfo(ProxiedPlayer proxiedPlayer) { - JsonObject obj = new JsonObject(); - obj.addProperty("hasGame", true); - obj.addProperty("game_mode", "steamwar.de"); - obj.addProperty("game_startTime", 0); - obj.addProperty("game_endTime", 0); - String output = "{ \"hasGame\" : \"true\", \"game_mode\" : \"steamwar.de\", \"game_startTime\" : \"0\", \"game_endTime\" : \"0\" }"; - - proxiedPlayer.sendData("LMC", output.getBytes()); - } - - private static class VarString{ - private final int length; - private final String value; - - private VarString(int length, String value) { - this.length = length; - this.value = value; - } - } - - private static class InfoPacket{ - String version = null; - Feature ccp = null; - Feature shadow = null; - List addons = new LinkedList<>(); - - InfoPacket(String input) throws IOException { - JsonReader reader = new JsonReader(new StringReader(input)); - reader.beginObject(); - while(reader.hasNext()){ - String name = reader.nextName(); - switch(name){ - case "version": - version = reader.nextString(); - break; - case "ccp": - ccp = new Feature(reader); - break; - case "shadow": - shadow = new Feature(reader); - break; - case "addons": - reader.beginArray(); - while(reader.hasNext()){ - addons.add(new Addon(reader)); - } - reader.endArray(); - break; - default: - reader.skipValue(); - } - } - reader.endObject(); - reader.close(); - } - - static class Addon{ - UUID uuid = null; - String name = null; - - Addon(JsonReader reader) throws IOException { - reader.beginObject(); - while(reader.hasNext()){ - String n = reader.nextName(); - if(n.equals("uuid")) - try{ - uuid = UUID.fromString(reader.nextString()); - }catch(IllegalArgumentException ignored){ - //ignored - } - else if(n.equals("name")) - name = reader.nextString(); - else - reader.skipValue(); - } - reader.endObject(); - } - } - - static class Feature{ - boolean enabled; - int version; - - Feature(JsonReader reader) throws IOException { - reader.beginObject(); - while(reader.hasNext()){ - String name = reader.nextName(); - if(name.equals("version")) - version = reader.nextInt(); - else if(name.equals("enabled")) - enabled = reader.nextBoolean(); - else - reader.skipValue(); - } - reader.endObject(); - } - } - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java b/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java deleted file mode 100644 index 4a17b00f..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/ModLoaderBlocker.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - */ - -package de.steamwar.bungeecore.listeners.mods; - -import de.steamwar.bungeecore.*; -import de.steamwar.bungeecore.listeners.BasicListener; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.event.EventHandler; - -import java.util.HashSet; -import java.util.Set; - -public class ModLoaderBlocker extends BasicListener { - - private static final Set BLOCKED_SERVER = new HashSet<>(); - - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent e){ - Connection sender = e.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - - ProxiedPlayer p = (ProxiedPlayer) sender; - if (e.getTag().equals("lunarclient:pm")) { - p.disconnect(Message.parseToComponent("MOD_LOADER_LUNAR_CLIENT", false, p)); - return; - } - - if(!e.getTag().equals("minecraft:brand")) - return; - - String brand = new String(e.getData()); - if(brand.contains("fabric") || brand.contains("quilt") || brand.contains("LiteLoader")){ - if (!Storage.fabricCheckedPlayers.containsKey(p)) { - Storage.fabricPlayers.add(p); - } - } else if (brand.contains("lunar")) { - p.disconnect(Message.parseToComponent("MOD_LOADER_LUNAR_CLIENT", false, p)); - } - } - - @EventHandler - public void onDisconnect(PlayerDisconnectEvent e){ - Fabric.remove(e.getPlayer()); - Storage.fabricPlayers.remove(e.getPlayer()); - } - - @EventHandler - public void onServerSwitch(ServerSwitchEvent event) { - if(((Subserver.getSubserver(event.getPlayer()) != null - && Subserver.getSubserver(event.getPlayer()).getType() == Servertype.ARENA) - || BLOCKED_SERVER.contains(event.getPlayer().getServer().getInfo().getName())) - && isFabric(event.getPlayer())) { - event.getPlayer().connect(BungeeCore.get().getProxy().getServerInfo(BungeeCore.LOBBY_SERVER)); - Message.send("MODLOADER_DENIED", event.getPlayer()); - } - } - - public static boolean isFabric(ProxiedPlayer player) { - return Storage.fabricPlayers.contains(player); - } - - public static void addServer(String server) { - BLOCKED_SERVER.add(server); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/mods/WorldDownloader.java b/src/de/steamwar/bungeecore/listeners/mods/WorldDownloader.java deleted file mode 100644 index f0030984..00000000 --- a/src/de/steamwar/bungeecore/listeners/mods/WorldDownloader.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.steamwar.bungeecore.listeners.mods; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.Mod; -import net.md_5.bungee.api.connection.Connection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; - -import java.util.Collections; -import java.util.Set; - -public class WorldDownloader extends BasicListener { - - private static final Set wdlTags = Collections.unmodifiableSet( - Sets.newHashSet("WDL|INIT", "wdl:init", "WDL|REQUEST", "wdl:request") - ); - - @EventHandler - public void onPluginMessageEvent(PluginMessageEvent event){ - if(!wdlTags.contains(event.getTag())) - return; - - Connection sender = event.getSender(); - if(!(sender instanceof ProxiedPlayer)) - return; - - event.setCancelled(true); - Utils.handleMods((ProxiedPlayer) sender, Lists.newArrayList(Mod.getOrCreate("wdl", Mod.Platform.FORGE))); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/ping/PingListener.java b/src/de/steamwar/bungeecore/listeners/ping/PingListener.java deleted file mode 100644 index a7bbf6e0..00000000 --- a/src/de/steamwar/bungeecore/listeners/ping/PingListener.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2022 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.ping; - -import de.steamwar.bungeecore.listeners.BasicListener; -import net.md_5.bungee.api.event.ProxyPingEvent; -import net.md_5.bungee.event.EventHandler; - -public class PingListener extends BasicListener { - - @EventHandler - public void onPing(ProxyPingEvent event) { - event.setResponse(new SteamWarServerPing(event.getResponse(), event.getConnection().getVersion())); - } -} diff --git a/src/de/steamwar/bungeecore/listeners/ping/SteamWarServerPing.java b/src/de/steamwar/bungeecore/listeners/ping/SteamWarServerPing.java deleted file mode 100644 index 76240ac1..00000000 --- a/src/de/steamwar/bungeecore/listeners/ping/SteamWarServerPing.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2022 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.ping; - -import net.md_5.bungee.api.ServerPing; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class SteamWarServerPing extends ServerPing { - - private final boolean preventsChatReports = true; - private final ForgeData forgeData; - - public SteamWarServerPing(ServerPing existing, int version) { - super(existing.getVersion(), existing.getPlayers(), existing.getDescriptionComponent(), existing.getFaviconObject()); - forgeData = new ForgeData(version); - } - - private static class ForgeData { - private final List channels = new ArrayList<>(); - private final List mods = new ArrayList<>(); - private final int fmlNetworkVersion = 2; - - public ForgeData(int versionNumber) { - channels.add(new ForgeChannel("minecraft:unregister")); - channels.add(new ForgeChannel("minecraft:register")); - channels.add(new ForgeChannel("fml:handshake")); - mods.add(new ForgeMod("minecraft", ProtocolVersion.getVersion(versionNumber))); - mods.add(new ForgeMod("forge", "ANY")); - } - - public final static class ProtocolVersion { - - private static final HashMap versions; - - static { - versions = new HashMap(); - versions.put(757, "1.18"); - versions.put(756, "1.17.1"); - versions.put(754, "1.16.5"); - versions.put(578, "1.15.2"); - versions.put(498, "1.14.1"); - versions.put(393, "1.13"); - } - - public static String getVersion(int version) { - return versions.get(version); - } - } - - private static class ForgeChannel { - private final String res; - private final String version = "FML2"; - private final boolean required = true; - - private ForgeChannel(String res) { - this.res = res; - } - } - - private static class ForgeMod { - private final String modId; - private final String modmarker; - - private ForgeMod(String modId, String modmarker) { - this.modId = modId; - this.modmarker = modmarker; - } - } - } -} diff --git a/src/de/steamwar/bungeecore/mods/Badlion.java b/src/de/steamwar/bungeecore/mods/Badlion.java new file mode 100644 index 00000000..ef2d90a5 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/Badlion.java @@ -0,0 +1,57 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import com.google.gson.JsonObject; +import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.event.EventHandler; + +public class Badlion extends BasicListener { + // https://github.com/BadlionClient/BadlionClientModAPI + + private final byte[] packet; + + public Badlion() { //TODO check if working or (json) modsDisallowed wrapper necessary + JsonObject disabled = new JsonObject(); + disabled.addProperty("disabled", true); + + JsonObject json = new JsonObject(); + json.add("Clear Glass", disabled); + json.add("ClearWater", disabled); + json.add("FOV Changer", disabled); + json.add("Hitboxes", disabled); + json.add("LevelHead", disabled); + json.add("MiniMap", disabled); + json.add("MLG Cobweb", disabled); + //json.add("Replay", disabled); //TODO check if ReplayMod restrictions work + json.add("Schematica", disabled); + json.add("ToggleSneak", disabled); + json.add("ToggleSprint", disabled); + json.add("TNT Time", disabled); + + packet = json.toString().getBytes(); + } + + @EventHandler + public void onPostLogin(PostLoginEvent event) { + event.getPlayer().sendData("badlion:mods", packet); + } +} diff --git a/src/de/steamwar/bungeecore/mods/FML.java b/src/de/steamwar/bungeecore/mods/FML.java new file mode 100644 index 00000000..9a5c8fd6 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/FML.java @@ -0,0 +1,94 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.sql.Mod; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class FML extends BasicListener { + // https://wiki.vg/Minecraft_Forge_Handshake#FML_protocol_.281.7_-_1.12.29 + + public static final String CHANNEL = "FML|HS"; + private final byte[] helloPacket = new byte[]{ + /* Packet type: ServerHello */ 0, + /* FML protocol version */ 2, + /* Override dimension (int) */ 0, 0, 0, 0 + }; + + private static final Set unlocked = new HashSet<>(); + + @EventHandler + public void onPostLogin(PostLoginEvent event) { + ProxiedPlayer player = event.getPlayer(); + + synchronized (unlocked) { + if(unlocked.contains(player.getUniqueId())){ + unlocked.remove(player.getUniqueId()); + return; + } + } + + if(((InitialHandler)player.getPendingConnection()).getHandshake().getHost().endsWith("\0FML\0")) { + player.sendData(CHANNEL, helloPacket); + } + } + + public void handlePluginMessage(PluginMessageEvent event) { + ProxiedPlayer p = (ProxiedPlayer) event.getSender(); + ByteBuf buf = Unpooled.wrappedBuffer(event.getData()); + + if (buf.readByte() == /* ModList */ 2) { + int numMods = DefinedPacket.readVarInt(buf); + + List mods = new ArrayList<>(); + for(int i = 0; i < numMods; i++) { + String name = DefinedPacket.readString(buf); + DefinedPacket.readString(buf); // version + + mods.add(Mod.getOrCreate(name, Mod.Platform.FORGE)); + } + + if (ModUtils.handleMods(p, mods)) { + synchronized (unlocked) { + unlocked.add(p.getUniqueId()); + } + p.disconnect(BungeeCore.stringToText("§7Deine installierten Mods wurden überprüft\n§aDu kannst nun §eSteam§8War §abetreten")); + ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { + synchronized (unlocked) { + unlocked.remove(p.getUniqueId()); + } + }, 30, TimeUnit.SECONDS); + } + } + } +} diff --git a/src/de/steamwar/bungeecore/mods/FML2.java b/src/de/steamwar/bungeecore/mods/FML2.java new file mode 100644 index 00000000..b6be9841 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/FML2.java @@ -0,0 +1,151 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.sql.Mod; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.netty.ChannelWrapper; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.packet.LoginPayloadRequest; +import net.md_5.bungee.protocol.packet.LoginPayloadResponse; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Level; + +public class FML2 extends BasicListener { + // https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 + + public static boolean isFML2(PendingConnection connection) { + return ((InitialHandler)connection).getHandshake().getHost().endsWith("\0FML2\0"); + } + + private final Field initialHandlerCh; + + public FML2() { + try { + initialHandlerCh = InitialHandler.class.getDeclaredField("ch"); + } catch (NoSuchFieldException e) { + throw new SecurityException("Could not initialize Reflection", e); + } + initialHandlerCh.setAccessible(true); + } + + //TODO current identifier is \0FORGE and fmlNetworkVersion 0 ? + //TODO links for current implementation (if current checker not working) + //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ServerStatusPing.java + //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkContext.java#L26 + //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/tasks/ForgeNetworkConfigurationHandler.java#L20 + //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkRegistry.java#L177 + + @EventHandler + public void onLogin(LoginEvent event) { + PendingConnection connection = event.getConnection(); + + if(!isFML2(connection)) + return; + + try { + ((ChannelWrapper) initialHandlerCh.get(connection)).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event)); + } catch (IllegalAccessException e) { + BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e); + return; + } + + event.registerIntent(BungeeCore.get()); + connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", new byte[] { + /* fml:handshake */ 13,102,109,108,58,104,97,110,100,115,104,97,107,101, + /* Packet length */ 4, + /* Type mod list */ 1, + /* Mod count */ 0, + /* Channel count */ 0, + /* Registry count */ 0 + })); + } + + private static class FML2LoginHandler extends PacketHandler { + private final LoginEvent event; + + public FML2LoginHandler(LoginEvent event) { + this.event = event; + } + + @Override + public String toString() { + return "SteamWar FML2 Handler"; + } + + @Override + public void handle(LoginPayloadResponse response) { + byte[] data = response.getData(); + if(data == null) { + abort(response, "Not FML2 client"); + return; + } + + ByteBuf buf = Unpooled.wrappedBuffer(data); + if(!DefinedPacket.readString(buf).equals("fml:handshake")) { + abort(response, "Not FML2 handshake response"); + return; + } + + int packetLength = DefinedPacket.readVarInt(buf); + if(packetLength != buf.readableBytes()) { + abort(response, "FML2 packet size mismatch"); + return; + } + + if(DefinedPacket.readVarInt(buf) != /* Mod List Reply */ 2) { + abort(response, "FML2 no mod list reply"); + return; + } + + List mods = new ArrayList<>(); + + int modCount = DefinedPacket.readVarInt(buf); + for(int i = 0; i < modCount; i++) + mods.add(Mod.getOrCreate(DefinedPacket.readString(buf), Mod.Platform.FORGE)); + + if(!ModUtils.handleMods(event.getConnection().getUniqueId(), Locale.getDefault(), event::setReason, mods)) + event.setCancelled(true); + + event.completeIntent(BungeeCore.get()); + } + + private void abort(LoginPayloadResponse response, String error) { + event.setReason(TextComponent.fromLegacy(error)); + event.setCancelled(true); + event.completeIntent(BungeeCore.get()); + BungeeCore.get().getLogger().log(Level.SEVERE, () -> error + "\n" + response); + } + } +} diff --git a/src/de/steamwar/bungeecore/mods/FabricModSender.java b/src/de/steamwar/bungeecore/mods/FabricModSender.java new file mode 100644 index 00000000..e75e4702 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/FabricModSender.java @@ -0,0 +1,141 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.Storage; +import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.sql.Mod; +import de.steamwar.sql.SWException; +import de.steamwar.sql.SteamwarUser; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class FabricModSender extends BasicListener { + + private final Set neededMods = new HashSet<>(); + + public FabricModSender() { + neededMods.add("java"); + neededMods.add("minecraft"); + neededMods.add("fabricloader"); + neededMods.add("steamwarmodsender"); + + BungeeCord.getInstance().getScheduler().schedule(BungeeCore.get(), () -> { + synchronized (Storage.fabricExpectPluginMessage) { + for (Map.Entry entry : Storage.fabricExpectPluginMessage.entrySet()) { + if (!Storage.fabricCheckedPlayers.containsKey(entry.getKey())) { + continue; + } + if (System.currentTimeMillis() - entry.getValue() > TimeUnit.SECONDS.toMillis(20)) { + Storage.fabricExpectPluginMessage.remove(entry.getKey()); + return; + } + } + } + }, 0, 1, TimeUnit.SECONDS); + } + + public void handlePluginMessage(PluginMessageEvent e){ + ProxiedPlayer player = (ProxiedPlayer) e.getSender(); + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + + if (!Storage.fabricCheckedPlayers.containsKey(player)) { + synchronized (Storage.fabricExpectPluginMessage) { + if (Storage.fabricExpectPluginMessage.containsKey(player)) { + logMessage(user, "Was not fabric checked but send message nonetheless", Arrays.toString(e.getData())); + return; + } + } + } + Storage.fabricExpectPluginMessage.remove(player); + + List mods = new ArrayList<>(); + + ByteBuf buf = Unpooled.wrappedBuffer(e.getData()); + String data = DefinedPacket.readString(buf, 1024*1024); + if(buf.readableBytes() > 0) { + logMessage(user, "Invalid message length", Arrays.toString(e.getData())); + return; + } + + JsonArray array = JsonParser.parseString(data).getAsJsonArray(); + + for(JsonElement mod : array) { + mods.add(Mod.getOrCreate(mod.getAsString(), Mod.Platform.FABRIC)); + } + + if(!neededModsContained(mods)) { + logMessage(user, "Needed mods are not contained", data); + return; + } + + if(!ModUtils.handleMods(player,mods)) + return; + + if (!Storage.fabricCheckedPlayers.containsKey(player)) { + Storage.fabricCheckedPlayers.put(player, data.hashCode()); + } else if (Storage.fabricCheckedPlayers.get(player) != data.hashCode()) { + logMessage(user, "Mods changed during runtime", data); + } + } + + @EventHandler + public void onServerSwitchEvent(ServerSwitchEvent e) { + if (e.getFrom() == null) return; + synchronized (Storage.fabricExpectPluginMessage) { + Storage.fabricExpectPluginMessage.put(e.getPlayer(), System.currentTimeMillis()); + } + } + + @EventHandler + public void onDisconnect(PlayerDisconnectEvent e) { + ProxiedPlayer player = e.getPlayer(); + + Storage.fabricCheckedPlayers.remove(player); + synchronized (Storage.fabricExpectPluginMessage) { + Storage.fabricExpectPluginMessage.remove(player); + } + } + + private boolean neededModsContained(List mods) { + return mods.stream() + .map(Mod::getModName) + .filter(neededMods::contains) + .count() == neededMods.size(); + } + + private void logMessage(SteamwarUser user, String reason, String data) { + SWException.log("FabricModSender " + user.getUserName() + ": " + reason, data); + } +} diff --git a/src/de/steamwar/bungeecore/mods/LabyMod.java b/src/de/steamwar/bungeecore/mods/LabyMod.java new file mode 100644 index 00000000..ed2ae223 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/LabyMod.java @@ -0,0 +1,83 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.steamwar.sql.Mod; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.protocol.DefinedPacket; + +import java.util.LinkedList; +import java.util.List; + +public class LabyMod { + // https://docs.labymod.net/pages/server/introduction/ + // https://github.com/LabyMod/labymod-server-api + // https://dl.labymod.net/addons.json + + private final byte[] gameInfoPacket; + public LabyMod() { + ByteBuf buf = Unpooled.buffer(); + DefinedPacket.writeString("discord_rpc", buf); + + JsonObject json = new JsonObject(); + json.addProperty("hasGame", true); + json.addProperty("game_mode", "steamwar.de"); + json.addProperty("game_startTime", 0); + json.addProperty("game_endTime", 0); + DefinedPacket.writeString(json.toString(), buf); + + gameInfoPacket = new byte[buf.readableBytes()]; + buf.readBytes(gameInfoPacket); + } + + public void handlePluginMessage(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + player.sendData(event.getTag(), gameInfoPacket); + + ByteBuf buf = Unpooled.wrappedBuffer(event.getData()); + String purpose = DefinedPacket.readString(buf); + if(!"INFO".equals(purpose)) + return; + + JsonObject message = JsonParser.parseString(DefinedPacket.readString(buf)).getAsJsonObject(); + List mods = new LinkedList<>(); + + for(JsonElement element : message.getAsJsonArray("addons")) { + JsonObject addon = element.getAsJsonObject(); + mods.add(Mod.getOrCreate(addon.get("name").getAsString(), Mod.Platform.LABYMOD)); + } + + if(message.has("mods")) { + for(JsonElement element : message.getAsJsonArray("mods")) { + JsonObject addon = element.getAsJsonObject(); + //TODO observer, FORGE and FABRIC mods available, do they always and with .jar? (would equal new mod platform) + //mods.add(Mod.getOrCreate(addon.get("name").getAsString().replace(".jar", ""), Mod.Platform.FORGE)); + } + } + + ModUtils.handleMods(player, mods); + } +} diff --git a/src/de/steamwar/bungeecore/mods/Lunar.java b/src/de/steamwar/bungeecore/mods/Lunar.java new file mode 100644 index 00000000..5c19d523 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/Lunar.java @@ -0,0 +1,132 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import com.lunarclient.apollo.ApolloManager; +import com.lunarclient.apollo.libs.protobuf.Any; +import com.lunarclient.apollo.libs.protobuf.InvalidProtocolBufferException; +import com.lunarclient.apollo.libs.protobuf.Message; +import com.lunarclient.apollo.mods.impl.*; +import com.lunarclient.apollo.module.ApolloModuleManager; +import com.lunarclient.apollo.module.ApolloModuleManagerImpl; +import com.lunarclient.apollo.module.modsetting.ModSettingModule; +import com.lunarclient.apollo.network.NetworkOptions; +import com.lunarclient.apollo.option.Options; +import com.lunarclient.apollo.player.AbstractApolloPlayer; +import com.lunarclient.apollo.player.v1.ModMessage; +import com.lunarclient.apollo.player.v1.PlayerHandshakeMessage; +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.sql.Mod; +import lombok.AllArgsConstructor; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.logging.Level; + +public class Lunar { + // https://lunarclient.dev/apollo/introduction + // https://github.com/LunarClient/Apollo + + private final ApolloModuleManager manager = new ApolloModuleManagerImpl().addModule(ModSettingModule.class); + + public Lunar() { + Options modSettings = manager.getModule(ModSettingModule.class).getOptions(); + //modSettings.set(ModReplaymod.ENABLED, false); // TODO check if restrictions working + modSettings.set(ModFreelook.ENABLED, false); + modSettings.set(ModHypixelMod.ENABLED, false); + modSettings.set(ModMinimap.ENABLED, false); + modSettings.set(ModNametag.ENABLED, false); + modSettings.set(ModTeamView.ENABLED, false); + modSettings.set(ModTntCountdown.ENABLED, false); + modSettings.set(ModToggleSneak.TOGGLE_SNEAK_CONTAINER, false); + } + + public void sendRestrictions(ProxiedPlayer player) { + NetworkOptions.sendOptions(manager.getModules(), true, new SWApolloPlayer(player)); + } + + public void handlePluginMessage(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer) event.getSender(); + Any packet; + + try { + packet = Any.parseFrom(event.getData()); + } catch (InvalidProtocolBufferException e) { + throw new SecurityException(e); + } + + handle(PlayerHandshakeMessage.class, packet, handshake -> { + List mods = new ArrayList<>(); + + for(ModMessage mod : handshake.getInstalledModsList()) { + switch(mod.getType()) { + case TYPE_FABRIC_INTERNAL: + case TYPE_FORGE_INTERNAL: + // Controlled with ModSettings + break; + case TYPE_FABRIC_EXTERNAL: + mods.add(Mod.getOrCreate(mod.getName(), Mod.Platform.FABRIC)); + break; + case TYPE_FORGE_EXTERNAL: + mods.add(Mod.getOrCreate(mod.getName(), Mod.Platform.FORGE)); + break; + case TYPE_UNSPECIFIED: + case UNRECOGNIZED: + default: + BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " uses Lunar mod with unknown type " + mod); + break; + } + } + + ModUtils.handleMods(player, mods); + }); + } + + private void handle(Class type, Any packet, Consumer handler) { + try { + handler.accept(packet.unpack(type)); + } catch (InvalidProtocolBufferException ignored) { /*ignored*/ } + } + + @AllArgsConstructor + private static class SWApolloPlayer extends AbstractApolloPlayer { + + private final ProxiedPlayer player; + + @Override + public void sendPacket(Message message) { + sendPacket(Any.pack(message).toByteArray()); + } + + @Override + public void sendPacket(byte[] bytes) { + player.sendData(ApolloManager.PLUGIN_MESSAGE_CHANNEL, bytes); + } + + @Override public UUID getUniqueId() { return player.getUniqueId(); } + @Override public String getName() { return player.getName(); } + @Override public boolean hasPermission(String s) { return player.hasPermission(s); } + @Override public Object getPlayer() { return player; } + } +} diff --git a/src/de/steamwar/bungeecore/listeners/mods/Utils.java b/src/de/steamwar/bungeecore/mods/ModUtils.java similarity index 55% rename from src/de/steamwar/bungeecore/listeners/mods/Utils.java rename to src/de/steamwar/bungeecore/mods/ModUtils.java index ae5d472d..a9e45a11 100644 --- a/src/de/steamwar/bungeecore/listeners/mods/Utils.java +++ b/src/de/steamwar/bungeecore/mods/ModUtils.java @@ -1,23 +1,23 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.bungeecore.listeners.mods; +package de.steamwar.bungeecore.mods; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Message; @@ -27,6 +27,8 @@ import de.steamwar.messages.ChatSender; import de.steamwar.sql.Mod; import de.steamwar.sql.Mod.ModType; import de.steamwar.sql.UserPerm; +import lombok.Getter; +import lombok.experimental.UtilityClass; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -39,35 +41,17 @@ import java.util.function.Consumer; import java.util.logging.Level; import java.util.stream.Collectors; -public class Utils { +@UtilityClass +public class ModUtils { - public static final Map> playerModMap = new HashMap<>(); + @Getter + private static final Map> playerModMap = new HashMap<>(); - private Utils(){} - - static VarInt readVarInt(byte[] array, int startPos) { - int numRead = 0; - int result = 0; - byte read; - do { - read = array[startPos + numRead]; - int value = (read & 0b01111111); - result |= (value << (7 * numRead)); - - numRead++; - if (numRead > 5) { - break; - } - } while ((read & 0b10000000) != 0); - - return new VarInt(numRead, result); - } - - static boolean handleMods(ProxiedPlayer player, List mods) { + public static boolean handleMods(ProxiedPlayer player, List mods) { return handleMods(player.getUniqueId(), ChatSender.of(player).getLocale(), player::disconnect, mods); } - static boolean handleMods(UUID uuid, Locale locale, Consumer disconnect, List mods){ + public static boolean handleMods(UUID uuid, Locale locale, Consumer disconnect, List mods){ SteamwarUser user = SteamwarUser.get(uuid); playerModMap.put(uuid,new ArrayList<>(mods)); @@ -101,17 +85,7 @@ public class Utils { BungeeCore.log(Level.SEVERE, user.getUserName() + " " + user.getId() + " wurde automatisch wegen der Mods " + modList + " gebannt."); } - disconnect.accept(TextComponent.fromLegacyText(message)); + disconnect.accept(TextComponent.fromLegacy(message)); return false; } - - static class VarInt{ - final int length; - final int value; - - VarInt(int length, int value){ - this.length = length; - this.value = value; - } - } } diff --git a/src/de/steamwar/bungeecore/mods/ReplayMod.java b/src/de/steamwar/bungeecore/mods/ReplayMod.java new file mode 100644 index 00000000..d3c813ab --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/ReplayMod.java @@ -0,0 +1,71 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.Bauserver; +import de.steamwar.bungeecore.Subserver; +import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.bungeecore.listeners.PluginMessage; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.event.EventHandler; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +public class ReplayMod extends BasicListener { + // https://gist.github.com/Johni0702/2547c463e51f65f312cb + // https://github.com/ReplayMod/replay-restrictions/blob/master/bungeecord/src/main/java/de/johni0702/replay/restrictions/BungeeCordPlugin.java + + private final byte[] restrict; + private final byte[] unlock; + + public ReplayMod() { + restrict = generatePacket(true); + unlock = generatePacket(false); + } + + @EventHandler + public void onPlayerJoin(ServerSwitchEvent event) { + ProxiedPlayer player = event.getPlayer(); + Subserver server = Subserver.getSubserver(player.getServer().getInfo()); + + PluginMessage.send( + player, "Replay|Restrict", "replaymod:restrict", + (server instanceof Bauserver && ((Bauserver) server).getOwner().equals(player.getUniqueId())) ? unlock : restrict + ); + } + + private byte[] generatePacket(boolean restrict) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + for(String restriction : Arrays.asList("no_xray", "no_noclip", "only_first_person", "only_recording_player")) { + byte[] bytes = restriction.getBytes(); + stream.write(bytes.length); + stream.write(bytes); + stream.write(restrict ? 1 : 0); + } + } catch (IOException e) { + throw new SecurityException(e); + } + return stream.toByteArray(); + } +} diff --git a/src/de/steamwar/bungeecore/mods/Schematica.java b/src/de/steamwar/bungeecore/mods/Schematica.java new file mode 100644 index 00000000..c39ad59a --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/Schematica.java @@ -0,0 +1,40 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.event.EventHandler; + +public class Schematica extends BasicListener { + // https://github.com/Lunatrius/SchematicaPlugin/blob/master/src/main/java/com/github/lunatrius/schematica/plugin/SchematicaPlugin.java + + private final byte[] packet = new byte[] { + /* ProtocolVersion? */ 0, + /* PERM_PRINTER */ 1, + /* PERM_SAVE */ 0, + /* PERM_LOAD */ 1 + }; + + @EventHandler + public void onPostLogin(PostLoginEvent event) { + event.getPlayer().sendData("schematica", packet); + } +} diff --git a/src/de/steamwar/bungeecore/mods/ServerListPing.java b/src/de/steamwar/bungeecore/mods/ServerListPing.java new file mode 100644 index 00000000..ae12cfff --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/ServerListPing.java @@ -0,0 +1,119 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import com.google.gson.*; +import de.steamwar.bungeecore.listeners.BasicListener; +import lombok.AllArgsConstructor; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.event.EventPriority; +import net.md_5.bungee.protocol.ProtocolConstants; + +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ServerListPing extends BasicListener implements JsonSerializer, JsonDeserializer { + // https://github.com/Aizistral-Studios/No-Chat-Reports/discussions/206 + // https://github.com/Aizistral-Studios/No-Chat-Reports/wiki/How-to-Get-Safe-Server-Status + // https://wiki.vg/Minecraft_Forge_Handshake#Changes_to_Server_List_Ping_2 + + private final Gson gson; + private final Map versionMap = new HashMap<>(); + private final FML2Channel[] fml2Channels = new FML2Channel[]{new FML2Channel("minecraft:register"), new FML2Channel("minecraft:unregister"), new FML2Channel("fml:handshake")}; + + private final Map connections = new ConcurrentHashMap<>(); + + public ServerListPing() { + BungeeCord bungeeCord = BungeeCord.getInstance(); + gson = bungeeCord.gson; + + try { + Field gsonField = BungeeCord.class.getDeclaredField("gson"); + gsonField.setAccessible(true); + gsonField.set(bungeeCord, gson.newBuilder().registerTypeAdapter(ServerPing.class, this).create()); + + for(Field version : ProtocolConstants.class.getFields()) { + String name = version.getName(); + if(version.getType() != int.class || !name.startsWith("MINECRAFT_")) + continue; + + versionMap.put(version.getInt(null), name.substring(10).replace('_', '.')); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + throw new SecurityException("Failed to inject ServerListPing", e); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPing(ProxyPingEvent event) { + connections.put(event.getResponse(), event.getConnection()); + connections.values().removeIf(connection -> !connection.isConnected()); // Prevent long term leaks + } + + @Override + public JsonElement serialize(ServerPing ping, Type type, JsonSerializationContext context) { + JsonElement element = gson.toJsonTree(ping, type); + + JsonObject object = element.getAsJsonObject(); + object.addProperty("preventsChatReports", true); + + PendingConnection connection = connections.remove(ping); + if(FML2.isFML2(connection)) { + object.add("forgeData", gson.toJsonTree(new FML2Data(fml2Channels, new FML2Mod[]{ + new FML2Mod("forge", "ANY"), + new FML2Mod("minecraft", versionMap.get(connection.getVersion())) + }), FML2Data.class)); + } + + return element; + } + + @Override + public ServerPing deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException { + return gson.fromJson(element, ServerPing.class); + } + + @AllArgsConstructor + private static class FML2Data { + private final int fmlNetworkVersion = 2; + private final FML2Channel[] channels; + private final FML2Mod[] mods; + } + + @AllArgsConstructor + private static class FML2Channel { + private final String res; + private final String version = "FML2"; + private final boolean required = true; + } + + @AllArgsConstructor + private static class FML2Mod { + private final String modId; + private final String modmarker; + } +} diff --git a/src/de/steamwar/bungeecore/mods/WorldDownloader.java b/src/de/steamwar/bungeecore/mods/WorldDownloader.java new file mode 100644 index 00000000..2087baac --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/WorldDownloader.java @@ -0,0 +1,56 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.listeners.PluginMessage; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PluginMessageEvent; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class WorldDownloader { + + private final byte[] controlPacket; + + public WorldDownloader() { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(stream); + + try { + out.writeInt(1); // basic data packet + out.writeBoolean(false); // General download enabled + out.writeInt(-1); // Save radius + out.writeBoolean(false); // Chunk caching enabled + out.writeBoolean(false); // Entity saving enabled + out.writeBoolean(false); // Tile entity saving disabled + out.writeBoolean(false); // Container saving disabled + } catch (IOException e) { + throw new SecurityException("Could not create AntiWDL packet", e); + } + + controlPacket = stream.toByteArray(); + } + + public void handlePluginMessage(PluginMessageEvent event) { + PluginMessage.send((ProxiedPlayer) event.getSender(), "WDL|CONTROL", "wdl:control", controlPacket); + } +} diff --git a/src/de/steamwar/bungeecore/network/NetworkReceiver.java b/src/de/steamwar/bungeecore/network/NetworkReceiver.java deleted file mode 100644 index f1fcd5a5..00000000 --- a/src/de/steamwar/bungeecore/network/NetworkReceiver.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2022 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.bungeecore.network; - -import de.steamwar.bungeecore.BungeeCore; -import de.steamwar.bungeecore.commands.TeamCommand; -import de.steamwar.bungeecore.listeners.BasicListener; -import de.steamwar.sql.SWException; -import de.steamwar.network.packets.NetworkPacket; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.event.EventPriority; - -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; - -public class NetworkReceiver extends BasicListener { - - private static final List blockedTags = Arrays.asList("bungeecord:main", "BungeeCord", "sw:bridge"); - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPluginMessage(PluginMessageEvent event) { - if (blockedTags.contains(event.getTag()) && !TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) { - SWException.log(((InetSocketAddress) event.getSender().getSocketAddress()).getHostString() + " tried to send a plugin message with tag " + event.getTag(), Base64.getEncoder().encodeToString(event.getData())); - event.setCancelled(true); - return; - } - if(!event.getTag().equals("sw:bridge")) - return; - - event.setCancelled(true); - if(!(event.getSender() instanceof Server)) - return; - - ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData())); - } -} diff --git a/src/de/steamwar/bungeecore/network/SWScriptSyntaxForwarder.java b/src/de/steamwar/bungeecore/network/SWScriptSyntaxForwarder.java deleted file mode 100644 index b604d549..00000000 --- a/src/de/steamwar/bungeecore/network/SWScriptSyntaxForwarder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2022 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.bungeecore.network; - -import de.steamwar.bungeecore.commands.TeamCommand; -import de.steamwar.bungeecore.listeners.BasicListener; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.api.event.PluginMessageEvent; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.event.EventPriority; - -import java.net.InetSocketAddress; - -public class SWScriptSyntaxForwarder extends BasicListener { - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPluginMessage(PluginMessageEvent event) { - if (!event.getTag().equals("sw:script_syntax")) { - return; - } - event.setCancelled(true); - if (!(event.getSender() instanceof Server)) { - return; - } - if (!TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) { - return; - } - ((ProxiedPlayer) event.getReceiver()).sendData("sw:script_syntax", event.getData()); - } -} diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index 5042013b..b8470988 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -20,9 +20,6 @@ DISABLED=§cCurrently disabled. SERVER_START_OVERLOAD=§cServer start cancelled due to overload. Please try again later. -#ModLoader blocker -MODLOADER_DENIED=§cYou cannot join arenas with LiteLoader or Fabric without the FabricModSender https://steamwar.de/downloads installed. - #Help command HELP_LOBBY=§7Return from anywhere to the lobby using §8/§el§7! HELP_LOBBY_HOVER=§eBack to the lobby @@ -118,10 +115,6 @@ MOD_RED_PLUR=Attempted use of mods:\n{0} MOD_YELLOW_SING=§7Deactivate the mod §e{0}§7 to continue playing on §eSteam§8War§7. MOD_YELLOW_PLUR=§7Deactivate the mods\n§e{0}\n§7to continue playing on §eSteam§8War§7. -MOD_LOADER_LUNAR_CLIENT=§eLunar Client§7 is not supported on §eSteam§8War§7. - -MOD_USE_MODSENDER=§cPlease use the §c§lFabricModSender§c (https://steamwar.de/downloads/) or remove the mod that is rebranding your installation. - #Various commands ALERT=§f{0} STAT_SERVER=§7Server §e{0}§8: §7Below limit §e{1} §7Server count §e{2} diff --git a/src/de/steamwar/messages/BungeeCore_de.properties b/src/de/steamwar/messages/BungeeCore_de.properties index 9490b31e..979a5a33 100644 --- a/src/de/steamwar/messages/BungeeCore_de.properties +++ b/src/de/steamwar/messages/BungeeCore_de.properties @@ -16,9 +16,6 @@ DISABLED=§cDerzeit deaktiviert. SERVER_START_OVERLOAD=§cDer Serverstart wurde aufgrund von Überlastung abgebrochen. Versuche es später erneut. -#ModLoader blocker -MODLOADER_DENIED=§cMit LiteLoader oder Fabric kannst du keinen Arenen ohne den FabricModSender https://steamwar.de/downloads beitreten. - #Help command HELP_LOBBY=§7Kehre von überall mit §8/§el §7zur Lobby zurück! HELP_LOBBY_HOVER=§eZurück zur Lobby @@ -103,10 +100,6 @@ MOD_RED_PLUR=Versuchte Benutzung der Mods:\n{0} MOD_YELLOW_SING=§7Deaktiviere den Mod §e{0}§7, um weiter auf §eSteam§8War §7spielen zu können. MOD_YELLOW_PLUR=§7Deaktiviere die Mods\n§e{0}\n§7um weiter auf §eSteam§8War §7spielen zu können. -MOD_LOADER_LUNAR_CLIENT=§eLunar Client§7 ist auf §eSteam§8War§7 nicht unterstützt. - -MOD_USE_MODSENDER=§cBitte nutze den §c§lFabricModSender§c (https://steamwar.de/downloads/) oder entferne den Mod, der deine Fabric-Installation tarnt. - #Various commands STAT_SERVER=§7Server §e{0}§8: §7Startfähig §e{1} §7Serveranzahl §e{2} From 437d1c055c61527d53f38179ba07fbed68c3e1a5 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sun, 3 Mar 2024 18:36:28 +0100 Subject: [PATCH 2/5] Fixed ReplayMod, FML2, FML3, FORGE, Hostname detection Signed-off-by: Lixfel --- build.gradle | 9 ++ src/de/steamwar/bungeecore/BungeeCore.java | 3 +- src/de/steamwar/bungeecore/Node.java | 8 +- ...verProtocolFixer.java => IPSanitizer.java} | 57 ++++---- .../bungeecore/listeners/PluginMessage.java | 64 +++++++-- src/de/steamwar/bungeecore/mods/FML.java | 8 +- src/de/steamwar/bungeecore/mods/FML2.java | 126 +++++++++++------- src/de/steamwar/bungeecore/mods/Hostname.java | 58 ++++++++ .../steamwar/bungeecore/mods/ReplayMod.java | 40 +++--- .../bungeecore/mods/ServerListPing.java | 56 -------- .../bungeecore/mods/WorldDownloader.java | 3 + 11 files changed, 261 insertions(+), 171 deletions(-) rename src/de/steamwar/bungeecore/listeners/{SubserverProtocolFixer.java => IPSanitizer.java} (52%) create mode 100644 src/de/steamwar/bungeecore/mods/Hostname.java diff --git a/build.gradle b/build.gradle index 95a7911f..c88301df 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,15 @@ repositories { } } +shadowJar { + exclude 'META-INF/*' + //https://imperceptiblethoughts.com/shadow/configuration/minimizing/ + minimize { + exclude project(':') + } + duplicatesStrategy DuplicatesStrategy.INCLUDE +} + dependencies { compileOnly 'org.projectlombok:lombok:1.18.22' testCompileOnly 'org.projectlombok:lombok:1.18.22' diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index c043d27c..5c34f26e 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -92,6 +92,7 @@ public class BungeeCore extends Plugin { }); ProxyServer.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS); + new Hostname(); new ServerListPing(); new PluginMessage(); new Schematica(); @@ -104,7 +105,7 @@ public class BungeeCore extends Plugin { new ChatListener(); new BanListener(); new CheckListener(); - new SubserverProtocolFixer(); + new IPSanitizer(); local = new Node.LocalNode(); if(MAIN_SERVER) { diff --git a/src/de/steamwar/bungeecore/Node.java b/src/de/steamwar/bungeecore/Node.java index 682e4d65..b7b244cd 100644 --- a/src/de/steamwar/bungeecore/Node.java +++ b/src/de/steamwar/bungeecore/Node.java @@ -22,7 +22,10 @@ package de.steamwar.bungeecore; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ProxyServer; -import java.io.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.nio.file.Files; import java.util.*; import java.util.concurrent.TimeUnit; @@ -31,6 +34,7 @@ import java.util.logging.Level; public abstract class Node { + //-Xquickstart Langzeitperformance testen! private static final List OPENJ9_ARGS = Arrays.asList( "-XX:+EnableCRIUSupport", "-XX:-CRIURestoreNonPortableMode", "-Xgc:excessiveGCratio=80", "-Xdisableexplicitgc", "-Xnoclassgc", "-Xmos128M", "-Xmns48M", "-XX:+ExitOnOutOfMemoryError", // initial heap half values of memory observed by 1.19 spectate server @@ -118,6 +122,8 @@ public abstract class Node { cmd.add(worldDir); cmd.add("--level-name"); cmd.add(levelName); + cmd.add("--plugins"); + cmd.add("/home/lixfel/bpl"); cmd.add("--port"); cmd.add(String.valueOf(port)); cmd.add("nogui"); diff --git a/src/de/steamwar/bungeecore/listeners/SubserverProtocolFixer.java b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java similarity index 52% rename from src/de/steamwar/bungeecore/listeners/SubserverProtocolFixer.java rename to src/de/steamwar/bungeecore/listeners/IPSanitizer.java index ff74ed7c..cc637ea1 100644 --- a/src/de/steamwar/bungeecore/listeners/SubserverProtocolFixer.java +++ b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java @@ -19,52 +19,41 @@ package de.steamwar.bungeecore.listeners; -import net.md_5.bungee.BungeeCord; +import de.steamwar.bungeecore.BungeeCore; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.connection.InitialHandler; -import net.md_5.bungee.connection.LoginResult; import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.protocol.Property; -import net.md_5.bungee.util.AddressUtil; +import net.md_5.bungee.netty.ChannelWrapper; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.util.logging.Level; -public class SubserverProtocolFixer extends BasicListener { +public class IPSanitizer extends BasicListener { + + private static final Field initialHandlerCh; + static { + try { + initialHandlerCh = InitialHandler.class.getDeclaredField("ch"); + } catch (NoSuchFieldException e) { + throw new SecurityException("Could not initialize Reflection", e); + } + initialHandlerCh.setAccessible(true); + } + + public static ChannelWrapper getChannelWrapper(InitialHandler handler) { + try { + return (ChannelWrapper) initialHandlerCh.get(handler); + } catch (IllegalAccessException e) { + throw new SecurityException("Could not get channel wrapper", e); + } + } private final InetSocketAddress inetSocketAddress = new InetSocketAddress("127.127.127.127", 25565); - private Field field; - - { - try { - field = InitialHandler.class.getDeclaredField("extraDataInHandshake"); - field.setAccessible(true); - } catch (NoSuchFieldException e) { - BungeeCord.getInstance().getLogger().log(Level.SEVERE, e.getMessage(), e); - } - } - @EventHandler public void loginEvent(LoginEvent e) { - InitialHandler initialHandler = ((InitialHandler) e.getConnection()); - - String undashedUUID = initialHandler.getUniqueId().toString().replace("-", ""); - String extraData = "\00" + AddressUtil.sanitizeAddress(inetSocketAddress) + "\00" + undashedUUID; - - LoginResult result = initialHandler.getLoginProfile(); - if (result != null) { - Property[] properties = result.getProperties(); - if (properties.length > 0) { - extraData += "\00" + BungeeCord.getInstance().gson.toJson(properties); - } - } - - try { - field.set(initialHandler, extraData); - } catch (IllegalAccessException ex) { - BungeeCord.getInstance().getLogger().log(Level.SEVERE, ex.getMessage(), ex); - } + BungeeCore.get().getLogger().log(Level.INFO, e.getConnection().getSocketAddress() + " has logged in with user name " + e.getConnection().getName()); + getChannelWrapper((InitialHandler) e.getConnection()).setRemoteAddress(inetSocketAddress); } } diff --git a/src/de/steamwar/bungeecore/listeners/PluginMessage.java b/src/de/steamwar/bungeecore/listeners/PluginMessage.java index d032be72..b57b2372 100644 --- a/src/de/steamwar/bungeecore/listeners/PluginMessage.java +++ b/src/de/steamwar/bungeecore/listeners/PluginMessage.java @@ -28,24 +28,29 @@ import de.steamwar.messages.ChatSender; import de.steamwar.network.packets.NetworkPacket; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.protocol.DefinedPacket; import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.logging.Level; public class PluginMessage extends BasicListener { public static void send(ProxiedPlayer player, String legacyChannel, String channel, byte[] data) { // 1.12 format change - player.sendData(player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data); + send(player, player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data); + } + + public static void send(ProxiedPlayer player, String channel, byte[] data) { + if(((InitialHandler)player.getPendingConnection()).getRegisteredChannels().contains(channel)) + player.sendData(channel, data); } //TODO change to warning level @@ -55,6 +60,8 @@ public class PluginMessage extends BasicListener { private final Lunar lunar = new Lunar(); + private final Set knownBrands = new HashSet<>(); + private final Set knownChannels = new HashSet<>(); private final Map handlers = new HashMap<>(); public PluginMessage() { @@ -63,8 +70,31 @@ public class PluginMessage extends BasicListener { FabricModSender fabricModSender = new FabricModSender(); WorldDownloader wdl = new WorldDownloader(); - register("REGISTER", false, directional(PASS_THROUGH, this::clientRegistersChannel)); - register("minecraft:register", false, directional(PASS_THROUGH, this::clientRegistersChannel)); + knownBrands.add("vanilla"); + knownBrands.add("fabric"); + knownBrands.add("forge"); //Forge registers all channels the server registers + + knownChannels.add("fabric:container/open"); + knownChannels.add("fabric:registry/sync/direct"); + knownChannels.add("fabric-screen-handler-api-v1:open_screen"); + + knownChannels.add(FML.CHANNEL); + knownChannels.add("fml:loginwrapper"); + knownChannels.add("fml:handshake"); + knownChannels.add("fml:play"); + knownChannels.add("forge:tier_sorting"); + knownChannels.add("forge:split"); + knownChannels.add("forge:login"); + knownChannels.add("forge:handshake"); + + knownChannels.add("Replay|Restrict"); + knownChannels.add("replaymod:restrict"); + knownChannels.add("WDL|CONTROL"); + knownChannels.add("wdl:control"); + knownChannels.add("worldedit:cui"); + + register("REGISTER", false, directional(this::serverRegistersChannel, this::clientRegistersChannel)); + register("minecraft:register", false, directional(this::serverRegistersChannel, this::clientRegistersChannel)); register("BungeeCord", false, onlySWSource(PASS_THROUGH)); register("bungeecord:main", false, onlySWSource(PASS_THROUGH)); @@ -73,6 +103,7 @@ public class PluginMessage extends BasicListener { register("sw:script_syntax", false, directional(onlySWSource(PASS_THROUGH), UNKNOWN)); register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN)); + register("worldedit:cui", false, PASS_THROUGH); register("fabricmodsender:mods", true, directional(UNKNOWN, fabricModSender::handlePluginMessage)); register("WDL|REQUEST", false, DROP); @@ -83,7 +114,7 @@ public class PluginMessage extends BasicListener { register(ApolloManager.PLUGIN_MESSAGE_CHANNEL, true, async(lunar::handlePluginMessage)); register("LMC", true, directional(UNKNOWN, async(labyMod::handlePluginMessage))); register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage))); - register(FML.CHANNEL, true, directional(UNKNOWN, fml::handlePluginMessage)); + register(FML.CHANNEL, true, directional(UNKNOWN, async(fml::handlePluginMessage))); //litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65 } @@ -114,7 +145,7 @@ public class PluginMessage extends BasicListener { if(channel.equals(ApolloManager.PLUGIN_MESSAGE_CHANNEL)) lunar.sendRestrictions(player); - if(!handlers.containsKey(channel)) + if(!knownChannels.contains(channel)) //TODO change to warning level BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " registered unknown channel " + channel); } @@ -122,15 +153,22 @@ public class PluginMessage extends BasicListener { PASS_THROUGH.handle(event); } + private void serverRegistersChannel(PluginMessageEvent event) { + ProxiedPlayer player = (ProxiedPlayer)event.getReceiver(); + + List channels = new ArrayList<>(Arrays.asList(new String(event.getData()).split("\0"))); + channels.removeIf(channel -> channel.equals("sw:bridge")); + player.sendData((player).getPendingConnection().getVersion() > 340 ? "minecraft:register" : "REGISTER", String.join("\0", channels).getBytes()); + } + private void userBrand(PluginMessageEvent event) { ProxiedPlayer player = (ProxiedPlayer) event.getSender(); - String brand = new String(event.getData()); + ByteBuf buf = Unpooled.wrappedBuffer(event.getData()); + String brand = DefinedPacket.readString(buf); - if(brand.equals("vanilla")) { - // nothing to do - } else if(brand.startsWith("lunarclient:")) { + if(brand.startsWith("lunarclient:")) { lunar.sendRestrictions(player); - } else { + } else if(!knownBrands.contains(brand)) { //TODO change to warning level BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " joined with unknown brand " + brand); } diff --git a/src/de/steamwar/bungeecore/mods/FML.java b/src/de/steamwar/bungeecore/mods/FML.java index 9a5c8fd6..1bd78053 100644 --- a/src/de/steamwar/bungeecore/mods/FML.java +++ b/src/de/steamwar/bungeecore/mods/FML.java @@ -25,6 +25,7 @@ import de.steamwar.sql.Mod; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.api.event.PostLoginEvent; @@ -38,6 +39,10 @@ import java.util.concurrent.TimeUnit; public class FML extends BasicListener { // https://wiki.vg/Minecraft_Forge_Handshake#FML_protocol_.281.7_-_1.12.29 + public static boolean isFML(PendingConnection connection, String type) { + return ((InitialHandler)connection).getExtraDataInHandshake().equals("\0" + type); + } + public static final String CHANNEL = "FML|HS"; private final byte[] helloPacket = new byte[]{ /* Packet type: ServerHello */ 0, @@ -58,9 +63,8 @@ public class FML extends BasicListener { } } - if(((InitialHandler)player.getPendingConnection()).getHandshake().getHost().endsWith("\0FML\0")) { + if(isFML(player.getPendingConnection(), "FML\0")) player.sendData(CHANNEL, helloPacket); - } } public void handlePluginMessage(PluginMessageEvent event) { diff --git a/src/de/steamwar/bungeecore/mods/FML2.java b/src/de/steamwar/bungeecore/mods/FML2.java index b6be9841..b52a7b3e 100644 --- a/src/de/steamwar/bungeecore/mods/FML2.java +++ b/src/de/steamwar/bungeecore/mods/FML2.java @@ -21,6 +21,7 @@ package de.steamwar.bungeecore.mods; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.listeners.BasicListener; +import de.steamwar.bungeecore.listeners.IPSanitizer; import de.steamwar.sql.Mod; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -29,67 +30,91 @@ import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.LoginPayloadRequest; import net.md_5.bungee.protocol.packet.LoginPayloadResponse; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.logging.Level; public class FML2 extends BasicListener { - // https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 + // FML2: https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29 + // FML3: https://github.com/adde0109/Ambassador/tree/non-api/src/main/java/org/adde0109/ambassador/forge - public static boolean isFML2(PendingConnection connection) { - return ((InitialHandler)connection).getHandshake().getHost().endsWith("\0FML2\0"); - } + // FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkInitialization.java + // FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ForgePacketHandler.java + // FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/packets/ModVersions.java - private final Field initialHandlerCh; + private final byte[] fml2ModListPacket; + private final byte[] fml3ModListPacket; + private final byte[] forgeModListPacket; public FML2() { - try { - initialHandlerCh = InitialHandler.class.getDeclaredField("ch"); - } catch (NoSuchFieldException e) { - throw new SecurityException("Could not initialize Reflection", e); - } - initialHandlerCh.setAccessible(true); - } + fml2ModListPacket = generateModListPacket(false); + fml3ModListPacket = generateModListPacket(true); - //TODO current identifier is \0FORGE and fmlNetworkVersion 0 ? - //TODO links for current implementation (if current checker not working) - //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ServerStatusPing.java - //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkContext.java#L26 - //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/tasks/ForgeNetworkConfigurationHandler.java#L20 - //https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkRegistry.java#L177 + ByteBuf packet = Unpooled.buffer(); + packet.writeByte(1); // Mod list packet + DefinedPacket.writeVarInt(0, packet); // Mod amount + + ByteBuf wrapper = Unpooled.buffer(); + wrapper.writeByte(0); // Login wrapper packet + DefinedPacket.writeString("forge:handshake", wrapper); + DefinedPacket.writeVarInt(packet.readableBytes(), wrapper); + wrapper.writeBytes(packet); + + forgeModListPacket = new byte[wrapper.readableBytes()]; + wrapper.readBytes(forgeModListPacket); + } @EventHandler public void onLogin(LoginEvent event) { PendingConnection connection = event.getConnection(); - if(!isFML2(connection)) + boolean fml2 = FML.isFML(connection, "FML2\0"); + boolean fml3 = FML.isFML(connection, "FML3\0"); + boolean forge = FML.isFML(connection, "FORGE"); + if(!fml2 && !fml3 && !forge) return; - try { - ((ChannelWrapper) initialHandlerCh.get(connection)).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event)); - } catch (IllegalAccessException e) { - BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e); - return; - } + IPSanitizer.getChannelWrapper((InitialHandler) connection).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event)); event.registerIntent(BungeeCore.get()); - connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", new byte[] { - /* fml:handshake */ 13,102,109,108,58,104,97,110,100,115,104,97,107,101, - /* Packet length */ 4, - /* Type mod list */ 1, - /* Mod count */ 0, - /* Channel count */ 0, - /* Registry count */ 0 - })); + if(forge) + connection.unsafe().sendPacket(new LoginPayloadRequest(1, "forge:login", forgeModListPacket)); + else + connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", fml3 ? fml3ModListPacket : fml2ModListPacket)); + } + + private byte[] generateModListPacket(boolean fml3) { + ByteBuf packet = Unpooled.buffer(); + packet.writeByte(1); // Mod list packet + DefinedPacket.writeVarInt(0, packet); // Mod amount + + if(fml3) { + DefinedPacket.writeVarInt(1, packet); // Channel amount + DefinedPacket.writeString("forge:tier_sorting", packet); + DefinedPacket.writeString("1.0", packet); + } else { + DefinedPacket.writeVarInt(0, packet); // Channel amount + } + + DefinedPacket.writeVarInt(0, packet); // Registries amount + if(fml3) + DefinedPacket.writeVarInt(0, packet); // DataPacks amount + + ByteBuf wrapper = Unpooled.buffer(); + DefinedPacket.writeString("fml:handshake", wrapper); + DefinedPacket.writeVarInt(packet.readableBytes(), wrapper); + wrapper.writeBytes(packet); + + byte[] data = new byte[wrapper.readableBytes()]; + wrapper.readBytes(data); + return data; } private static class FML2LoginHandler extends PacketHandler { @@ -101,40 +126,51 @@ public class FML2 extends BasicListener { @Override public String toString() { - return "SteamWar FML2 Handler"; + return "SteamWar Forge Handler"; } @Override public void handle(LoginPayloadResponse response) { + boolean forge = FML.isFML(event.getConnection(), "FORGE"); byte[] data = response.getData(); if(data == null) { - abort(response, "Not FML2 client"); + abort(response, "Not FML2/3 client"); return; } ByteBuf buf = Unpooled.wrappedBuffer(data); - if(!DefinedPacket.readString(buf).equals("fml:handshake")) { - abort(response, "Not FML2 handshake response"); + if(forge && buf.readByte() != 0) { + abort(response, "Not FORGE login wrapper"); return; } - int packetLength = DefinedPacket.readVarInt(buf); - if(packetLength != buf.readableBytes()) { - abort(response, "FML2 packet size mismatch"); + if(!DefinedPacket.readString(buf).equals(forge ? "forge:handshake" : "fml:handshake")) { + abort(response, "Not FML2/3/FORGE handshake response"); return; } - if(DefinedPacket.readVarInt(buf) != /* Mod List Reply */ 2) { - abort(response, "FML2 no mod list reply"); + if(DefinedPacket.readVarInt(buf) != buf.readableBytes()) { + abort(response, "FML2/3/FORGE packet size mismatch"); + return; + } + + if(DefinedPacket.readVarInt(buf) != (forge ? /* Mod Versions */ 1 : /* Mod List Reply */ 2)) { + abort(response, "Not FML2/3/FORGE mod list reply"); return; } List mods = new ArrayList<>(); int modCount = DefinedPacket.readVarInt(buf); - for(int i = 0; i < modCount; i++) + for(int i = 0; i < modCount; i++) { mods.add(Mod.getOrCreate(DefinedPacket.readString(buf), Mod.Platform.FORGE)); + if(forge) { + DefinedPacket.readString(buf); // Human readable name + DefinedPacket.readString(buf); // Version + } + } + if(!ModUtils.handleMods(event.getConnection().getUniqueId(), Locale.getDefault(), event::setReason, mods)) event.setCancelled(true); diff --git a/src/de/steamwar/bungeecore/mods/Hostname.java b/src/de/steamwar/bungeecore/mods/Hostname.java new file mode 100644 index 00000000..a9728dc9 --- /dev/null +++ b/src/de/steamwar/bungeecore/mods/Hostname.java @@ -0,0 +1,58 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2024 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bungeecore.mods; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.listeners.BasicListener; +import net.md_5.bungee.api.event.PlayerHandshakeEvent; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.event.EventHandler; + +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; + +public class Hostname extends BasicListener { + + private final Set knownHostnames = new HashSet<>(); + private final Set knownExtraData = new HashSet<>(); + + public Hostname() { + knownHostnames.add("steamwar.de"); + knownHostnames.add("78.31.71.136"); + + knownExtraData.add(""); + knownExtraData.add("\0FML\0"); + knownExtraData.add("\0FML2\0"); + knownExtraData.add("\0FML3\0"); + knownExtraData.add("\0FORGE"); + } + + @EventHandler + public void onHandshake(PlayerHandshakeEvent event) { + String extraDataInHandshake = ((InitialHandler) event.getConnection()).getExtraDataInHandshake(); + if (!knownHostnames.contains(event.getHandshake().getHost().toLowerCase())) { + BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown hostname " + event.getHandshake() + " " + extraDataInHandshake); + } else if (!knownExtraData.contains(extraDataInHandshake)) { + BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown extra data " + event.getHandshake() + " " + extraDataInHandshake); + + } + } +} diff --git a/src/de/steamwar/bungeecore/mods/ReplayMod.java b/src/de/steamwar/bungeecore/mods/ReplayMod.java index d3c813ab..2130ebc0 100644 --- a/src/de/steamwar/bungeecore/mods/ReplayMod.java +++ b/src/de/steamwar/bungeecore/mods/ReplayMod.java @@ -20,9 +20,13 @@ package de.steamwar.bungeecore.mods; import de.steamwar.bungeecore.Bauserver; +import de.steamwar.bungeecore.Builderserver; +import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Subserver; import de.steamwar.bungeecore.listeners.BasicListener; import de.steamwar.bungeecore.listeners.PluginMessage; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ServerSwitchEvent; import net.md_5.bungee.event.EventHandler; @@ -34,38 +38,36 @@ import java.util.Arrays; public class ReplayMod extends BasicListener { // https://gist.github.com/Johni0702/2547c463e51f65f312cb // https://github.com/ReplayMod/replay-restrictions/blob/master/bungeecord/src/main/java/de/johni0702/replay/restrictions/BungeeCordPlugin.java + // https://github.com/ReplayMod/ReplayMod/blob/stable/src/main/java/com/replaymod/core/utils/Restrictions.java private final byte[] restrict; - private final byte[] unlock; public ReplayMod() { - restrict = generatePacket(true); - unlock = generatePacket(false); - } - - @EventHandler - public void onPlayerJoin(ServerSwitchEvent event) { - ProxiedPlayer player = event.getPlayer(); - Subserver server = Subserver.getSubserver(player.getServer().getInfo()); - - PluginMessage.send( - player, "Replay|Restrict", "replaymod:restrict", - (server instanceof Bauserver && ((Bauserver) server).getOwner().equals(player.getUniqueId())) ? unlock : restrict - ); - } - - private byte[] generatePacket(boolean restrict) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { for(String restriction : Arrays.asList("no_xray", "no_noclip", "only_first_person", "only_recording_player")) { byte[] bytes = restriction.getBytes(); stream.write(bytes.length); stream.write(bytes); - stream.write(restrict ? 1 : 0); + stream.write(1); // restrict } } catch (IOException e) { throw new SecurityException(e); } - return stream.toByteArray(); + restrict = stream.toByteArray(); + } + + @EventHandler + public void onPlayerJoin(ServerSwitchEvent event) { + ProxiedPlayer player = event.getPlayer(); + ServerInfo server = player.getServer().getInfo(); + if(ProxyServer.getInstance().getServerInfo(BungeeCore.LOBBY_SERVER) == server) + return; + + Subserver subserver = Subserver.getSubserver(server); + if(subserver instanceof Builderserver || (subserver instanceof Bauserver && ((Bauserver) subserver).getOwner().equals(player.getUniqueId()))) + return; + + PluginMessage.send(player, "Replay|Restrict", "replaymod:restrict", restrict); } } diff --git a/src/de/steamwar/bungeecore/mods/ServerListPing.java b/src/de/steamwar/bungeecore/mods/ServerListPing.java index ae12cfff..e774794c 100644 --- a/src/de/steamwar/bungeecore/mods/ServerListPing.java +++ b/src/de/steamwar/bungeecore/mods/ServerListPing.java @@ -21,31 +21,17 @@ package de.steamwar.bungeecore.mods; import com.google.gson.*; import de.steamwar.bungeecore.listeners.BasicListener; -import lombok.AllArgsConstructor; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ServerPing; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.api.event.ProxyPingEvent; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.event.EventPriority; -import net.md_5.bungee.protocol.ProtocolConstants; import java.lang.reflect.Field; import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; public class ServerListPing extends BasicListener implements JsonSerializer, JsonDeserializer { // https://github.com/Aizistral-Studios/No-Chat-Reports/discussions/206 // https://github.com/Aizistral-Studios/No-Chat-Reports/wiki/How-to-Get-Safe-Server-Status - // https://wiki.vg/Minecraft_Forge_Handshake#Changes_to_Server_List_Ping_2 private final Gson gson; - private final Map versionMap = new HashMap<>(); - private final FML2Channel[] fml2Channels = new FML2Channel[]{new FML2Channel("minecraft:register"), new FML2Channel("minecraft:unregister"), new FML2Channel("fml:handshake")}; - - private final Map connections = new ConcurrentHashMap<>(); public ServerListPing() { BungeeCord bungeeCord = BungeeCord.getInstance(); @@ -55,25 +41,11 @@ public class ServerListPing extends BasicListener implements JsonSerializer !connection.isConnected()); // Prevent long term leaks - } - @Override public JsonElement serialize(ServerPing ping, Type type, JsonSerializationContext context) { JsonElement element = gson.toJsonTree(ping, type); @@ -81,14 +53,6 @@ public class ServerListPing extends BasicListener implements JsonSerializer Date: Mon, 4 Mar 2024 12:44:33 +0100 Subject: [PATCH 3/5] Fix IP sanitizing impacts Signed-off-by: Lixfel --- .../commands/PunishmentCommand.java | 6 ++++-- .../bungeecore/listeners/BanListener.java | 5 +++-- .../bungeecore/listeners/IPSanitizer.java | 13 ++++++++++--- .../bungeecore/listeners/PluginMessage.java | 19 +++++++++---------- src/de/steamwar/bungeecore/mods/FML2.java | 3 +-- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/de/steamwar/bungeecore/commands/PunishmentCommand.java b/src/de/steamwar/bungeecore/commands/PunishmentCommand.java index 47ed67af..03a36b37 100644 --- a/src/de/steamwar/bungeecore/commands/PunishmentCommand.java +++ b/src/de/steamwar/bungeecore/commands/PunishmentCommand.java @@ -23,6 +23,7 @@ import com.google.gson.JsonParser; import de.steamwar.bungeecore.BungeeCore; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.listeners.ConnectionListener; +import de.steamwar.bungeecore.listeners.IPSanitizer; import de.steamwar.command.PreviousArguments; import de.steamwar.command.SWCommand; import de.steamwar.command.TypeMapper; @@ -96,14 +97,15 @@ public class PunishmentCommand { ProxiedPlayer player = ProxyServer.getInstance().getPlayer(user.getUUID()); if (player != null) { + String ip = IPSanitizer.getTrueAddress(player.getPendingConnection()).getHostAddress(); ChatSender.disconnect(player).system(punishmentMessage(user, Punishment.PunishmentType.Ban)); - for (BannedUserIPs banned : BannedUserIPs.get(player.getAddress().getAddress().getHostAddress())) { + for (BannedUserIPs banned : BannedUserIPs.get(ip)) { SteamwarUser bannedUser = SteamwarUser.get(banned.getUserID()); if (bannedUser.isPunished(Punishment.PunishmentType.Ban) && bannedUser.getPunishment(Punishment.PunishmentType.Ban).getEndTime().before(time)) { bannedUser.punish(Punishment.PunishmentType.Ban, time, banReason, punisher.getId(), perma); } } - BannedUserIPs.banIP(user.getId(), player.getAddress().getAddress().getHostAddress()); + BannedUserIPs.banIP(user.getId(), ip); } } diff --git a/src/de/steamwar/bungeecore/listeners/BanListener.java b/src/de/steamwar/bungeecore/listeners/BanListener.java index 1e48bbd5..3cb9f0f9 100644 --- a/src/de/steamwar/bungeecore/listeners/BanListener.java +++ b/src/de/steamwar/bungeecore/listeners/BanListener.java @@ -46,15 +46,16 @@ public class BanListener extends BasicListener { ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> { PendingConnection connection = event.getConnection(); SteamwarUser user = SteamwarUser.getOrCreate(connection.getUniqueId(), connection.getName(), ConnectionListener::newPlayer, WebpasswordCommand::changeUsername); + String ip = IPSanitizer.getTrueAddress(connection).getHostAddress(); if (user.isPunished(Punishment.PunishmentType.Ban)) { event.setCancelled(true); - BannedUserIPs.banIP(user.getId(), connection.getAddress().getAddress().getHostAddress()); + BannedUserIPs.banIP(user.getId(), ip); ChatSender.of(event).system(PunishmentCommand.punishmentMessage(user, Punishment.PunishmentType.Ban)); event.completeIntent(BungeeCore.get()); return; } - List ips = BannedUserIPs.get(connection.getAddress().getAddress().getHostAddress()); + List ips = BannedUserIPs.get(ip); if(!ips.isEmpty()){ Timestamp highestBan = ips.get(0).getTimestamp(); boolean perma = false; diff --git a/src/de/steamwar/bungeecore/listeners/IPSanitizer.java b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java index cc637ea1..50c3ee2e 100644 --- a/src/de/steamwar/bungeecore/listeners/IPSanitizer.java +++ b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java @@ -20,12 +20,14 @@ package de.steamwar.bungeecore.listeners; import de.steamwar.bungeecore.BungeeCore; +import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.ChannelWrapper; import java.lang.reflect.Field; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.logging.Level; @@ -41,19 +43,24 @@ public class IPSanitizer extends BasicListener { initialHandlerCh.setAccessible(true); } - public static ChannelWrapper getChannelWrapper(InitialHandler handler) { + public static ChannelWrapper getChannelWrapper(PendingConnection connection) { try { - return (ChannelWrapper) initialHandlerCh.get(handler); + return (ChannelWrapper) initialHandlerCh.get(connection); } catch (IllegalAccessException e) { throw new SecurityException("Could not get channel wrapper", e); } } + public static InetAddress getTrueAddress(PendingConnection connection) { + return ((InetSocketAddress) getChannelWrapper(connection).getHandle().remoteAddress()).getAddress(); + } + + private final InetSocketAddress inetSocketAddress = new InetSocketAddress("127.127.127.127", 25565); @EventHandler public void loginEvent(LoginEvent e) { BungeeCore.get().getLogger().log(Level.INFO, e.getConnection().getSocketAddress() + " has logged in with user name " + e.getConnection().getName()); - getChannelWrapper((InitialHandler) e.getConnection()).setRemoteAddress(inetSocketAddress); + getChannelWrapper(e.getConnection()).setRemoteAddress(inetSocketAddress); } } diff --git a/src/de/steamwar/bungeecore/listeners/PluginMessage.java b/src/de/steamwar/bungeecore/listeners/PluginMessage.java index b57b2372..3246e055 100644 --- a/src/de/steamwar/bungeecore/listeners/PluginMessage.java +++ b/src/de/steamwar/bungeecore/listeners/PluginMessage.java @@ -30,6 +30,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.Connection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.event.PluginMessageEvent; @@ -37,7 +38,6 @@ import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.protocol.DefinedPacket; -import java.net.InetSocketAddress; import java.util.*; import java.util.logging.Level; @@ -53,8 +53,7 @@ public class PluginMessage extends BasicListener { player.sendData(channel, data); } - //TODO change to warning level - private static final Parser UNKNOWN = event -> BungeeCore.get().getLogger().log(Level.INFO, () -> "Undefined PluginMessage on channel " + event.getTag() + " from " + event.getSender() + " received.\n" + Arrays.toString(event.getData())); + private static final Parser UNKNOWN = event -> BungeeCore.get().getLogger().log(Level.WARNING, () -> "Undefined PluginMessage on channel " + event.getTag() + " from " + event.getSender() + " received.\n" + Arrays.toString(event.getData())); private static final Parser PASS_THROUGH = event -> event.setCancelled(false); private static final Parser DROP = event -> {}; @@ -98,8 +97,8 @@ public class PluginMessage extends BasicListener { register("BungeeCord", false, onlySWSource(PASS_THROUGH)); register("bungeecord:main", false, onlySWSource(PASS_THROUGH)); - register("MC|Brand", true, directional(this::steamWarBrand, this::userBrand)); - register("minecraft:brand", true, directional(this::steamWarBrand, this::userBrand)); + register("MC|Brand", false, directional(this::steamWarBrand, this::userBrand)); + register("minecraft:brand", false, directional(this::steamWarBrand, this::userBrand)); register("sw:script_syntax", false, directional(onlySWSource(PASS_THROUGH), UNKNOWN)); register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN)); @@ -146,8 +145,7 @@ public class PluginMessage extends BasicListener { lunar.sendRestrictions(player); if(!knownChannels.contains(channel)) - //TODO change to warning level - BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " registered unknown channel " + channel); + BungeeCore.get().getLogger().log(Level.WARNING, () -> player.getName() + " registered unknown channel " + channel); } PASS_THROUGH.handle(event); @@ -169,8 +167,7 @@ public class PluginMessage extends BasicListener { if(brand.startsWith("lunarclient:")) { lunar.sendRestrictions(player); } else if(!knownBrands.contains(brand)) { - //TODO change to warning level - BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " joined with unknown brand " + brand); + BungeeCore.get().getLogger().log(Level.WARNING, () -> player.getName() + " joined with unknown brand " + brand); } PASS_THROUGH.handle(event); @@ -196,9 +193,11 @@ public class PluginMessage extends BasicListener { }; } + @SuppressWarnings("deprecation") private Parser onlySWSource(Parser parser) { return event -> { - if(TeamCommand.isLocalhost(((InetSocketAddress) event.getSender().getSocketAddress()).getAddress())) + Connection sender = event.getSender(); + if(TeamCommand.isLocalhost(sender instanceof ProxiedPlayer ? IPSanitizer.getTrueAddress(((ProxiedPlayer) sender).getPendingConnection()) : sender.getAddress().getAddress())) parser.handle(event); else UNKNOWN.handle(event); diff --git a/src/de/steamwar/bungeecore/mods/FML2.java b/src/de/steamwar/bungeecore/mods/FML2.java index b52a7b3e..f815869d 100644 --- a/src/de/steamwar/bungeecore/mods/FML2.java +++ b/src/de/steamwar/bungeecore/mods/FML2.java @@ -28,7 +28,6 @@ import io.netty.buffer.Unpooled; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.event.LoginEvent; -import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.PacketHandler; @@ -81,7 +80,7 @@ public class FML2 extends BasicListener { if(!fml2 && !fml3 && !forge) return; - IPSanitizer.getChannelWrapper((InitialHandler) connection).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event)); + IPSanitizer.getChannelWrapper(connection).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event)); event.registerIntent(BungeeCore.get()); if(forge) From 8029dd09e99b2bc4d10eec7c54b84089e27f001a Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 11 Mar 2024 22:29:22 +0100 Subject: [PATCH 4/5] Fix Node debug code changes, Debug messages, script_syntax, Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/Node.java | 7 +------ src/de/steamwar/bungeecore/listeners/IPSanitizer.java | 4 ++-- src/de/steamwar/bungeecore/listeners/PluginMessage.java | 8 +++++--- src/de/steamwar/bungeecore/mods/LabyMod.java | 5 ++++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/de/steamwar/bungeecore/Node.java b/src/de/steamwar/bungeecore/Node.java index b7b244cd..eb132e20 100644 --- a/src/de/steamwar/bungeecore/Node.java +++ b/src/de/steamwar/bungeecore/Node.java @@ -22,10 +22,7 @@ package de.steamwar.bungeecore; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ProxyServer; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.nio.file.Files; import java.util.*; import java.util.concurrent.TimeUnit; @@ -122,8 +119,6 @@ public abstract class Node { cmd.add(worldDir); cmd.add("--level-name"); cmd.add(levelName); - cmd.add("--plugins"); - cmd.add("/home/lixfel/bpl"); cmd.add("--port"); cmd.add(String.valueOf(port)); cmd.add("nogui"); diff --git a/src/de/steamwar/bungeecore/listeners/IPSanitizer.java b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java index 50c3ee2e..9396a27d 100644 --- a/src/de/steamwar/bungeecore/listeners/IPSanitizer.java +++ b/src/de/steamwar/bungeecore/listeners/IPSanitizer.java @@ -56,11 +56,11 @@ public class IPSanitizer extends BasicListener { } - private final InetSocketAddress inetSocketAddress = new InetSocketAddress("127.127.127.127", 25565); + private final InetSocketAddress sanitized = new InetSocketAddress("127.127.127.127", 25565); @EventHandler public void loginEvent(LoginEvent e) { BungeeCore.get().getLogger().log(Level.INFO, e.getConnection().getSocketAddress() + " has logged in with user name " + e.getConnection().getName()); - getChannelWrapper(e.getConnection()).setRemoteAddress(inetSocketAddress); + getChannelWrapper(e.getConnection()).setRemoteAddress(sanitized); } } diff --git a/src/de/steamwar/bungeecore/listeners/PluginMessage.java b/src/de/steamwar/bungeecore/listeners/PluginMessage.java index 3246e055..64e70597 100644 --- a/src/de/steamwar/bungeecore/listeners/PluginMessage.java +++ b/src/de/steamwar/bungeecore/listeners/PluginMessage.java @@ -86,6 +86,8 @@ public class PluginMessage extends BasicListener { knownChannels.add("forge:login"); knownChannels.add("forge:handshake"); + knownChannels.add(ApolloManager.PLUGIN_MESSAGE_CHANNEL); + knownChannels.add("Replay|Restrict"); knownChannels.add("replaymod:restrict"); knownChannels.add("WDL|CONTROL"); @@ -100,7 +102,6 @@ public class PluginMessage extends BasicListener { register("MC|Brand", false, directional(this::steamWarBrand, this::userBrand)); register("minecraft:brand", false, directional(this::steamWarBrand, this::userBrand)); - register("sw:script_syntax", false, directional(onlySWSource(PASS_THROUGH), UNKNOWN)); register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN)); register("worldedit:cui", false, PASS_THROUGH); register("fabricmodsender:mods", true, directional(UNKNOWN, fabricModSender::handlePluginMessage)); @@ -115,6 +116,9 @@ public class PluginMessage extends BasicListener { register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage))); register(FML.CHANNEL, true, directional(UNKNOWN, async(fml::handlePluginMessage))); + //vanilla does not register any channels (sends only one minecraft:brand vanilla, nothing else (potential spoofed client detection)) + //meteor https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/ServerSpoof.java https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/DiscordPresence.java + //feather:client https://github.com/Koupah/Feather-Client-API/blob/main/src/club/koupah/feather/handler/FeatherHandler.java //litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65 } @@ -122,8 +126,6 @@ public class PluginMessage extends BasicListener { public void onPluginMessage(PluginMessageEvent event) { event.setCancelled(true); - BungeeCore.get().getLogger().log(Level.INFO, event + "\n" + new String(event.getData()) + "\n" + Arrays.toString(event.getData())); //TODO remove logging - try { handlers.getOrDefault(event.getTag(), UNKNOWN).handle(event); } catch (Exception e) { diff --git a/src/de/steamwar/bungeecore/mods/LabyMod.java b/src/de/steamwar/bungeecore/mods/LabyMod.java index ed2ae223..1114adc6 100644 --- a/src/de/steamwar/bungeecore/mods/LabyMod.java +++ b/src/de/steamwar/bungeecore/mods/LabyMod.java @@ -22,6 +22,7 @@ package de.steamwar.bungeecore.mods; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import de.steamwar.bungeecore.BungeeCore; import de.steamwar.sql.Mod; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -31,6 +32,7 @@ import net.md_5.bungee.protocol.DefinedPacket; import java.util.LinkedList; import java.util.List; +import java.util.logging.Level; public class LabyMod { // https://docs.labymod.net/pages/server/introduction/ @@ -71,9 +73,10 @@ public class LabyMod { } if(message.has("mods")) { + BungeeCore.get().getLogger().log(Level.WARNING, () -> "LabyMod External Mods for debugging: " + message.getAsJsonArray("mods")); for(JsonElement element : message.getAsJsonArray("mods")) { JsonObject addon = element.getAsJsonObject(); - //TODO observer, FORGE and FABRIC mods available, do they always and with .jar? (would equal new mod platform) + //TODO observe: FORGE and FABRIC mods available, do they always and with .jar? (would equal new mod platform) //mods.add(Mod.getOrCreate(addon.get("name").getAsString().replace(".jar", ""), Mod.Platform.FORGE)); } } From a3a65b0f5374a575804fbdbc26c5e5dd3c626b9f Mon Sep 17 00:00:00 2001 From: Lixfel Date: Thu, 14 Mar 2024 11:42:01 +0100 Subject: [PATCH 5/5] Disable Replay by default (for now, until tests have been done) Signed-off-by: Lixfel --- src/de/steamwar/bungeecore/mods/Badlion.java | 2 +- src/de/steamwar/bungeecore/mods/Lunar.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/bungeecore/mods/Badlion.java b/src/de/steamwar/bungeecore/mods/Badlion.java index ef2d90a5..4a58f0f0 100644 --- a/src/de/steamwar/bungeecore/mods/Badlion.java +++ b/src/de/steamwar/bungeecore/mods/Badlion.java @@ -41,7 +41,7 @@ public class Badlion extends BasicListener { json.add("LevelHead", disabled); json.add("MiniMap", disabled); json.add("MLG Cobweb", disabled); - //json.add("Replay", disabled); //TODO check if ReplayMod restrictions work + json.add("Replay", disabled); //TODO check if ReplayMod restrictions work json.add("Schematica", disabled); json.add("ToggleSneak", disabled); json.add("ToggleSprint", disabled); diff --git a/src/de/steamwar/bungeecore/mods/Lunar.java b/src/de/steamwar/bungeecore/mods/Lunar.java index 5c19d523..904d7bf9 100644 --- a/src/de/steamwar/bungeecore/mods/Lunar.java +++ b/src/de/steamwar/bungeecore/mods/Lunar.java @@ -52,7 +52,7 @@ public class Lunar { public Lunar() { Options modSettings = manager.getModule(ModSettingModule.class).getOptions(); - //modSettings.set(ModReplaymod.ENABLED, false); // TODO check if restrictions working + modSettings.set(ModReplaymod.ENABLED, false); // TODO check if restrictions working modSettings.set(ModFreelook.ENABLED, false); modSettings.set(ModHypixelMod.ENABLED, false); modSettings.set(ModMinimap.ENABLED, false);