diff --git a/BungeeCord-Patches/0067-Rework-information-forwarding.patch.patch b/BungeeCord-Patches/0067-Rework-information-forwarding.patch.patch new file mode 100644 index 000000000..be1149c59 --- /dev/null +++ b/BungeeCord-Patches/0067-Rework-information-forwarding.patch.patch @@ -0,0 +1,396 @@ +From 2b48760148e5ef1812f4e6b6fb5f9cdaef1bc560 Mon Sep 17 00:00:00 2001 +From: Ismael Hanbel +Date: Sat, 8 Jul 2023 20:36:35 +0200 +Subject: [PATCH] Rework-information-forwarding.patch + + +diff --git a/api/src/main/java/io/github/waterfallmc/waterfall/forwarding/ForwardingMode.java b/api/src/main/java/io/github/waterfallmc/waterfall/forwarding/ForwardingMode.java +new file mode 100644 +index 00000000..d6c17460 +--- /dev/null ++++ b/api/src/main/java/io/github/waterfallmc/waterfall/forwarding/ForwardingMode.java +@@ -0,0 +1,12 @@ ++package io.github.waterfallmc.waterfall.forwarding; ++ ++/** ++ * + * This enum represents the forwarding modes supported by Waterfall. ++ * + ++ */ ++ ++public enum ForwardingMode { ++ BUNGEECORD_LEGACY, ++ BUNGEEGUARD, ++ VELOCITY_MODERN ++} +\ No newline at end of file +diff --git a/api/src/main/java/net/md_5/bungee/Util.java b/api/src/main/java/net/md_5/bungee/Util.java +index 91efc0a6..8d83f41c 100644 +--- a/api/src/main/java/net/md_5/bungee/Util.java ++++ b/api/src/main/java/net/md_5/bungee/Util.java +@@ -8,7 +8,9 @@ import java.net.InetSocketAddress; + import java.net.SocketAddress; + import java.net.URI; + import java.net.URISyntaxException; ++import java.security.SecureRandom; + import java.util.Locale; ++import java.util.Random; + import java.util.UUID; + + import io.github.waterfallmc.waterfall.utils.Hex; +@@ -141,4 +143,22 @@ public class Util + { + return new UUID( UnsignedLongs.parseUnsignedLong( uuid.substring( 0, 16 ), 16 ), UnsignedLongs.parseUnsignedLong( uuid.substring( 16 ), 16 ) ); + } ++ ++ // Waterfall start: Forwarding rework ++ /** ++ * Generates an alphanumeric A-Z,a-z,0-9 byte-sequence. ++ * ++ * @param len the length of the sequence ++ * @return a UTF/ASCII compatible alphanumeric byte-sequence ++ */ ++ public static byte[] randomAlphanumericSequence(int len) { ++ Random random = new SecureRandom(); ++ byte[] ret = new byte[len]; ++ for (int i = 0; i < len; i++) { ++ int seq = random.nextInt(62); ++ ret[i] = (byte) (seq < 10 ? seq + 48 : seq < 36 ? seq + 55 : seq + 61); ++ } ++ return ret; ++ } ++ // Waterfall end: Forwarding rework + } +diff --git a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java +index 469fe0e1..b78c0d5f 100644 +--- a/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java ++++ b/api/src/main/java/net/md_5/bungee/api/ProxyConfig.java +@@ -2,6 +2,8 @@ package net.md_5.bungee.api; + + import java.util.Collection; + import java.util.Map; ++ ++import io.github.waterfallmc.waterfall.forwarding.ForwardingMode; + import net.md_5.bungee.api.config.ListenerInfo; + import net.md_5.bungee.api.config.ServerInfo; + +@@ -275,4 +277,11 @@ public interface ProxyConfig + * @return the configured limit + */ + int getPluginChannelNameLimit(); ++ ++ /** ++ * Represents the forwarding mode as configured. ++ * ++ * @return the mode set in the config ++ */ ++ ForwardingMode getForwardingMode(); + } +diff --git a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java +index 1ba5b249..c0797a4a 100644 +--- a/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java ++++ b/api/src/main/java/net/md_5/bungee/api/plugin/PluginManager.java +@@ -49,7 +49,7 @@ import org.yaml.snakeyaml.introspector.PropertyUtils; + * example event handling and plugin management. + */ + @RequiredArgsConstructor +-public final class PluginManager ++public final class PluginManager + { + + /*========================================================================*/ +@@ -440,6 +440,14 @@ public final class PluginManager + Preconditions.checkNotNull( desc.getName(), "Plugin from %s has no name", file ); + Preconditions.checkNotNull( desc.getMain(), "Plugin from %s has no main", file ); + ++ // Waterfall start: Forwarding rework ++ if (desc.getName().equals("BungeeGuard")) { ++ proxy.getLogger().warning("Detected the plugin BungeeGuard. " + ++ "Waterfall now supports the functionality this plugin provides natively. " + ++ "Please refer to the Waterfall documentation for more information."); ++ } ++ // Waterfall end: Forwarding rework ++ + desc.setFile( file ); + toLoad.put( desc.getName(), desc ); + } +diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +index da0efa36..03c628f4 100644 +--- a/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java ++++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/conf/WaterfallConfiguration.java +@@ -1,11 +1,16 @@ + package io.github.waterfallmc.waterfall.conf; + + import com.google.common.base.Joiner; ++import io.github.waterfallmc.waterfall.forwarding.ForwardingMode; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.Util; + import net.md_5.bungee.conf.Configuration; + import net.md_5.bungee.conf.YamlConfig; + import net.md_5.bungee.protocol.ProtocolConstants; + + import java.io.File; ++import java.nio.charset.StandardCharsets; ++import java.util.logging.Logger; + + public class WaterfallConfiguration extends Configuration { + +@@ -45,6 +50,9 @@ public class WaterfallConfiguration extends Configuration { + private boolean disableEntityMetadataRewrite = false; + private boolean disableTabListRewrite = true; + ++ private ForwardingMode forwardingMode = ForwardingMode.BUNGEECORD_LEGACY; ++ private byte[] forwardingSecret = Util.randomAlphanumericSequence(12); ++ + /* + * Plugin Message limiting options + * Allows for more control over server-client communication +@@ -77,6 +85,25 @@ public class WaterfallConfiguration extends Configuration { + disableTabListRewrite = config.getBoolean("disable_tab_list_rewrite", disableTabListRewrite); + pluginChannelLimit = config.getInt("registered_plugin_channels_limit", pluginChannelLimit); + pluginChannelNameLimit = config.getInt("plugin_channel_name_limit", pluginChannelNameLimit); ++ // Forwarding options ++ forwardingMode = ForwardingMode.valueOf(config.getString("forwarding_mode", ForwardingMode.BUNGEECORD_LEGACY.toString()).toUpperCase()); ++ Logger logger = BungeeCord.getInstance().getLogger(); ++ if (super.isIpForward()) { ++ if ((forwardingMode) == ForwardingMode.BUNGEECORD_LEGACY) { ++ logger.warning("The forwarding mode is set as BungeeCord legacy, it is recommended that you use another mode to avoid spoofing information attacks."); ++ } ++ ++ } else { ++ logger.warning("Information forwarding (ip-forwarding) is disabled. " + ++ "Player UUIDs may not be consistent across the servers. " + ++ "For the optimal experience please enable ip_forward in the config.yml and " + ++ "configure forwarding and on your servers."); ++ } ++ ++ if (config.getString("forwarding_secret", "").isEmpty()) { ++ config.regenerateForwardingSecret(); ++ logger.warning("A new forwarding secret has been generated. If this was the " + ++ "first start of the proxy please configure forwarding for your network."); ++ } ++ forwardingSecret = config.getString("forwarding_secret", "").getBytes(StandardCharsets.UTF_8); ++ } + + @Override +@@ -123,4 +150,13 @@ public class WaterfallConfiguration extends Configuration { + public int getPluginChannelNameLimit() { + return pluginChannelNameLimit; + } ++ ++ @Override ++ public ForwardingMode getForwardingMode() { ++ return forwardingMode; ++ } ++ ++ public byte[] getForwardingSecret() { ++ return forwardingSecret; ++ } + } +diff --git a/proxy/src/main/java/io/github/waterfallmc/waterfall/forwarding/VelocityForwardingUtil.java b/proxy/src/main/java/io/github/waterfallmc/waterfall/forwarding/VelocityForwardingUtil.java +new file mode 100644 +index 00000000..b9990f92 +--- /dev/null ++++ b/proxy/src/main/java/io/github/waterfallmc/waterfall/forwarding/VelocityForwardingUtil.java +@@ -0,0 +1,69 @@ ++package io.github.waterfallmc.waterfall.forwarding; ++ ++import io.github.waterfallmc.waterfall.conf.WaterfallConfiguration; ++import io.netty.buffer.ByteBuf; ++import io.netty.buffer.ByteBufUtil; ++import io.netty.buffer.Unpooled; ++import lombok.experimental.UtilityClass; ++import net.md_5.bungee.BungeeCord; ++import net.md_5.bungee.protocol.DefinedPacket; ++import net.md_5.bungee.protocol.Property; ++ ++import javax.crypto.Mac; ++import javax.crypto.SecretKey; ++import javax.crypto.spec.SecretKeySpec; ++import java.security.InvalidKeyException; ++import java.security.NoSuchAlgorithmException; ++import java.util.UUID; ++ ++@UtilityClass ++public class VelocityForwardingUtil { ++ ++ public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info"; ++ public static final int FORWARDING_VERSION = 1; ++ ++ public static final String MODERN_IP_FORWARDING_FAILURE = "Your server did not send a forwarding request to the proxy. Is it set up correctly?"; ++ ++ public static byte[] writeForwardingData(String address, String name, UUID playerUUID, Property[] properties) { ++ ++ ByteBuf buf = Unpooled.buffer(2048); ++ ++ try { ++ DefinedPacket.writeVarInt(FORWARDING_VERSION, buf); ++ DefinedPacket.writeString(address, buf); ++ DefinedPacket.writeUUID(playerUUID, buf); ++ DefinedPacket.writeString(name, buf); ++ DefinedPacket.writeVarInt(properties.length, buf); ++ for (Property property : properties) { ++ DefinedPacket.writeString(property.getName(), buf); ++ DefinedPacket.writeString(property.getValue(), buf); ++ String signature = property.getSignature(); ++ if (signature != null && !signature.isEmpty()) { ++ buf.writeBoolean(true); ++ DefinedPacket.writeString(signature, buf); ++ } else { ++ buf.writeBoolean(false); ++ } ++ } ++ ++ byte[] forwardingSecret = ((WaterfallConfiguration) BungeeCord.getInstance().config).getForwardingSecret(); ++ SecretKey key = new SecretKeySpec(forwardingSecret, "HmacSHA256"); ++ Mac mac = Mac.getInstance("HmacSHA256"); ++ mac.init(key); ++ mac.update(buf.array(), buf.arrayOffset(), buf.readableBytes()); ++ byte[] sig = mac.doFinal(); ++ ++ ByteBuf finished = Unpooled.wrappedBuffer(Unpooled.wrappedBuffer(sig), buf); ++ byte[] encoded = ByteBufUtil.getBytes(finished); ++ finished.release(); ++ return encoded; ++ } catch (InvalidKeyException e) { ++ buf.release(); ++ throw new RuntimeException("Unable to authenticate data", e); ++ } catch (NoSuchAlgorithmException e) { ++ // Should never happen ++ buf.release(); ++ throw new AssertionError(e); ++ } ++ } ++} +\ No newline at end of file +diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +index 60232e52..dfde3535 100644 +--- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java ++++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +@@ -2,15 +2,15 @@ package net.md_5.bungee; + + import com.google.common.base.Joiner; + import com.google.common.base.Preconditions; ++import io.github.waterfallmc.waterfall.conf.WaterfallConfiguration; ++import io.github.waterfallmc.waterfall.forwarding.ForwardingMode; ++import io.github.waterfallmc.waterfall.forwarding.VelocityForwardingUtil; + import io.netty.buffer.ByteBuf; + import io.netty.buffer.ByteBufAllocator; + import java.net.InetSocketAddress; + import java.nio.charset.StandardCharsets; +-import java.util.Locale; +-import java.util.Arrays; // Waterfall +-import java.util.Queue; +-import java.util.Set; +-import java.util.UUID; ++import java.util.*; ++ + import lombok.Getter; + import lombok.RequiredArgsConstructor; + import net.md_5.bungee.api.ChatColor; +@@ -35,11 +35,7 @@ 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.netty.PipelineUtils; +-import net.md_5.bungee.protocol.DefinedPacket; +-import net.md_5.bungee.protocol.MinecraftDecoder; +-import net.md_5.bungee.protocol.PacketWrapper; +-import net.md_5.bungee.protocol.Protocol; +-import net.md_5.bungee.protocol.ProtocolConstants; ++import net.md_5.bungee.protocol.*; + import net.md_5.bungee.protocol.packet.EncryptionRequest; + import net.md_5.bungee.protocol.packet.EntityStatus; + import net.md_5.bungee.protocol.packet.GameState; +@@ -73,6 +69,8 @@ public class ServerConnector extends PacketHandler + private ForgeServerHandler handshakeHandler; + private boolean obsolete; + ++ private boolean didForwardInformation = false; // Waterfall: Forwarding rework ++ + private enum State + { + +@@ -106,6 +104,8 @@ public class ServerConnector extends PacketHandler + Handshake originalHandshake = user.getPendingConnection().getHandshake(); + Handshake copiedHandshake = new Handshake( originalHandshake.getProtocolVersion(), originalHandshake.getHost(), originalHandshake.getPort(), 2 ); + ++ if(BungeeCord.getInstance().config.getForwardingMode() != ForwardingMode.VELOCITY_MODERN) // Waterfall: Forwarding rework ++ + if ( BungeeCord.getInstance().config.isIpForward() && user.getSocketAddress() instanceof InetSocketAddress ) + { + String newHost = copiedHandshake.getHost() + "\00" + AddressUtil.sanitizeAddress( user.getAddress() ) + "\00" + user.getUUID(); +@@ -120,6 +120,15 @@ public class ServerConnector extends PacketHandler + properties = profile.getProperties(); + } + ++ // Waterfall start: Forwarding rework ++ if (BungeeCord.getInstance().config.getForwardingMode() == ForwardingMode.BUNGEEGUARD) { ++ List temp = new ArrayList<>(Arrays.asList(properties)); ++ String token = new String(((WaterfallConfiguration) BungeeCord.getInstance().config).getForwardingSecret(), StandardCharsets.UTF_8); ++ temp.add(new Property("bungeeguard-token", token, null)); ++ properties = temp.toArray(new Property[0]); ++ } ++ // Waterfall end: Forwarding rework ++ + if ( user.getForgeClientHandler().isFmlTokenInHandshake() ) + { + // Get the current properties and copy them into a slightly bigger array. +@@ -171,6 +180,12 @@ public class ServerConnector extends PacketHandler + @Override + public void handle(LoginSuccess loginSuccess) throws Exception + { ++ // Waterfall start: Forwarding rework ++ if (!didForwardInformation && BungeeCord.getInstance().config.isIpForward() ++ && BungeeCord.getInstance().config.getForwardingMode() == ForwardingMode.VELOCITY_MODERN) { ++ throw new QuietException(VelocityForwardingUtil.MODERN_IP_FORWARDING_FAILURE); ++ } ++ // Waterfall end: Forwarding rework + Preconditions.checkState( thisState == State.LOGIN_SUCCESS, "Not expecting LOGIN_SUCCESS" ); + ch.setProtocol( Protocol.GAME ); + thisState = State.LOGIN; +@@ -480,6 +495,20 @@ public class ServerConnector extends PacketHandler + @Override + public void handle(LoginPayloadRequest loginPayloadRequest) + { ++ // Waterfall start: Forwarding rework ++ if (!didForwardInformation && BungeeCord.getInstance().config.isIpForward() ++ && BungeeCord.getInstance().config.getForwardingMode() == ForwardingMode.VELOCITY_MODERN ++ && loginPayloadRequest.getChannel().equals(VelocityForwardingUtil.VELOCITY_IP_FORWARDING_CHANNEL)) { ++ ++ byte[] forwardingData = VelocityForwardingUtil ++ .writeForwardingData(user.getAddress().getAddress().getHostAddress(), ++ user.getName(), user.getUniqueId(), ++ user.getPendingConnection().getLoginProfile().getProperties()); ++ ch.write(new LoginPayloadResponse(loginPayloadRequest.getId(), forwardingData)); ++ didForwardInformation = true; ++ return; ++ } ++ // Waterfall end: Forwarding rework + ch.write( new LoginPayloadResponse( loginPayloadRequest.getId(), null ) ); + } + +diff --git a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +index 0dd69778..e3220e08 100644 +--- a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java ++++ b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +@@ -10,6 +10,7 @@ import java.io.InputStream; + import java.io.OutputStreamWriter; + import java.io.Writer; + import java.net.SocketAddress; ++import java.nio.charset.StandardCharsets; + import java.util.ArrayList; + import java.util.Arrays; + import java.util.Collection; +@@ -332,4 +333,10 @@ public class YamlConfig implements ConfigurationAdapter + Collection permissions = get( "permissions." + group, null ); + return ( permissions == null ) ? Collections.EMPTY_SET : permissions; + } ++ ++ // Waterfall start: Forwarding rework ++ public void regenerateForwardingSecret() { ++ set("forwarding_secret", new String(Util.randomAlphanumericSequence(12), StandardCharsets.UTF_8)); ++ } ++ // Waterfall end: Forwarding rework + } +-- +2.41.0.windows.1 +