From 58017960c936e98947635073a03bf19553f4a20d Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Wed, 30 Jan 2013 01:45:00 +0100 Subject: [PATCH] Don't reference classes by name - it is not safe. --- .../com/comphenix/protocol/CommandPacket.java | 2 +- .../comphenix/protocol/ProtocolLibrary.java | 2 - .../protocol/injector/EntityUtilities.java | 2 +- .../protocol/injector/MinecraftRegistry.java | 2 +- .../protocol/injector/PacketInjector.java | 2 +- .../player/InjectedServerConnection.java | 3 +- .../injector/player/PlayerInjector.java | 4 +- .../protocol/utility/MinecraftReflection.java | 80 ++++++++++++++++++- .../protocol/wrappers/WrappedDataWatcher.java | 2 +- 9 files changed, 85 insertions(+), 14 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java index 4da7a3c4..6c687412 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java @@ -415,7 +415,7 @@ class CommandPacket extends CommandBase { Class clazz = packet.getClass(); // Get the first Minecraft super class - while ((!clazz.getName().startsWith("net.minecraft.server") || + while ((!MinecraftReflection.isMinecraftClass(clazz) || Factory.class.isAssignableFrom(clazz)) && clazz != Object.class) { clazz = clazz.getSuperclass(); } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java index 40b1645e..28c1ef9d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolLibrary.java @@ -19,7 +19,6 @@ package com.comphenix.protocol; import java.io.File; import java.io.IOException; -import java.util.List; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; @@ -42,7 +41,6 @@ import com.comphenix.protocol.injector.PacketFilterManager.PlayerInjectHooks; import com.comphenix.protocol.metrics.Statistics; import com.comphenix.protocol.metrics.Updater; import com.comphenix.protocol.reflect.compiler.BackgroundCompiler; -import com.google.common.collect.Lists; /** * The main entry point for ProtocolLib. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index 2362ed55..c773f4aa 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -191,7 +191,7 @@ class EntityUtilities { // The Minecraft field that's NOT filled in by the constructor trackedEntitiesField = FuzzyReflection.fromObject(tracker, true). - getFieldByType(MinecraftReflection.MINECRAFT_OBJECT, ignoredTypes); + getFieldByType(MinecraftReflection.getMinecraftObjectMatcher(), ignoredTypes); } // Read the entity hashmap diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java index 7ad014cb..15561e8f 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/MinecraftRegistry.java @@ -174,7 +174,7 @@ class MinecraftRegistry { for (Map.Entry entry : getPacketToID().entrySet()) { if (Objects.equal(entry.getValue(), packetID)) { // Attempt to get the vanilla class here too - if (!forceVanilla || entry.getKey().getName().startsWith("net.minecraft.server")) + if (!forceVanilla || MinecraftReflection.isMinecraftClass(entry.getKey())) return removeEnhancer(entry.getKey(), forceVanilla); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java index 0a83c8ec..2ac36e95 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketInjector.java @@ -93,7 +93,7 @@ class PacketInjector { if (intHashMap == null) { // We're looking for the first static field with a Minecraft-object. This should be a IntHashMap. Field intHashMapField = FuzzyReflection.fromClass(MinecraftReflection.getPacketClass(), true). - getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); + getFieldByType(MinecraftReflection.getMinecraftObjectMatcher()); try { intHashMap = FieldUtils.readField(intHashMapField, (Object) null, true); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java index 6123ec0f..b9560cdf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/InjectedServerConnection.java @@ -90,7 +90,8 @@ class InjectedServerConnection { try { if (serverConnectionMethod == null) serverConnectionMethod = FuzzyReflection.fromClass(minecraftServerField.getType()). - getMethodByParameters("getServerConnection", ".*ServerConnection", new String[] {}); + getMethodByParameters("getServerConnection", + MinecraftReflection.getServerConnectionClass(), new Class[] {}); // We're using Minecraft 1.3.1 injectServerConnection(); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java index 25d683c6..79f90c6a 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/player/PlayerInjector.java @@ -330,7 +330,7 @@ abstract class PlayerInjector { Object handler = FieldUtils.readField(serverHandlerField, notchEntity, true); // Is this a Minecraft hook? - if (handler != null && !handler.getClass().getName().startsWith("net.minecraft.server")) { + if (handler != null && !MinecraftReflection.isMinecraftObject(handler)) { // This is our proxy object if (handler instanceof Factory) @@ -380,7 +380,7 @@ abstract class PlayerInjector { try { // Well, that sucks. Try just Minecraft objects then. netHandlerField = FuzzyReflection.fromClass(networkManager.getClass(), true). - getFieldByType(MinecraftReflection.MINECRAFT_OBJECT); + getFieldByType(MinecraftReflection.getMinecraftObjectMatcher()); } catch (RuntimeException e2) { throw new IllegalAccessException("Cannot locate net handler. " + e2.getMessage()); diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index fd9d6496..89119886 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -20,6 +20,7 @@ package com.comphenix.protocol.utility; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.regex.Pattern; import javax.annotation.Nonnull; @@ -38,9 +39,17 @@ import com.google.common.base.Joiner; public class MinecraftReflection { /** * Regular expression that matches a Minecraft object. + *

+ * Replaced by the method {@link #getMinecraftObjectMatcher()}. */ + @Deprecated public static final String MINECRAFT_OBJECT = "net\\.minecraft(\\.\\w+)+"; + /** + * Regular expression computed dynamically. + */ + private static String DYNAMIC_PACKAGE_MATCHER = null; + /** * The package name of all the classes that belongs to the native code in Minecraft. */ @@ -64,6 +73,20 @@ public class MinecraftReflection { // net.minecraft.server private static Class itemStackArrayClass; + private MinecraftReflection() { + // No need to make this constructable. + } + + /** + * Retrieve a regular expression that can match Minecraft package objects. + * @return Minecraft package matcher. + */ + public static String getMinecraftObjectMatcher() { + if (DYNAMIC_PACKAGE_MATCHER == null) + getMinecraftPackage(); + return DYNAMIC_PACKAGE_MATCHER; + } + /** * Retrieve the name of the Minecraft server package. * @return Full canonical name of the Minecraft server package. @@ -87,6 +110,21 @@ public class MinecraftReflection { // The return type will tell us the full package, regardless of formating CRAFTBUKKIT_PACKAGE = getPackage(craftClass.getCanonicalName()); MINECRAFT_FULL_PACKAGE = getPackage(returnName); + + // Pretty important invariant + if (!MINECRAFT_FULL_PACKAGE.startsWith(MINECRAFT_PREFIX_PACKAGE)) { + // Assume they're the same instead + MINECRAFT_PREFIX_PACKAGE = MINECRAFT_FULL_PACKAGE; + + // The package is usualy flat, so go with that assumtion + DYNAMIC_PACKAGE_MATCHER = + (MINECRAFT_PREFIX_PACKAGE.length() > 0 ? + Pattern.quote(MINECRAFT_PREFIX_PACKAGE + ".") : "") + "\\w+"; + } else { + // Use the standard matcher + DYNAMIC_PACKAGE_MATCHER = MINECRAFT_OBJECT; + } + return MINECRAFT_FULL_PACKAGE; } catch (SecurityException e) { @@ -126,7 +164,12 @@ public class MinecraftReflection { * @return The package name. */ private static String getPackage(String fullName) { - return fullName.substring(0, fullName.lastIndexOf(".")); + int index = fullName.lastIndexOf("."); + + if (index > 0) + return fullName.substring(0, index); + else + return ""; // Default package } /** @@ -160,6 +203,19 @@ public class MinecraftReflection { return obj.getClass().getName().startsWith(MINECRAFT_PREFIX_PACKAGE); } + /** + * Determine if the given class is found within the package net.minecraft.server, or any equivalent package. + * @param clazz - the class to test. + * @return TRUE if it can, FALSE otherwise. + */ + public static boolean isMinecraftClass(@Nonnull Class clazz) { + if (clazz == null) + throw new IllegalArgumentException("Class cannot be NULL."); + + // Doesn't matter if we don't check for the version here + return clazz.getName().startsWith(MINECRAFT_PREFIX_PACKAGE); + } + /** * Determine if a given object is found in net.minecraft.server, and has the given name. * @param obj - the object to test. @@ -289,7 +345,15 @@ public class MinecraftReflection { } /** - * Retrieve the NetLoginHandler class. + * Retrieve the player list class (or ServerConfigurationManager), + * @return The player list class. + */ + public static Class getPlayerListClass() { + return getMinecraftClass("ServerConfigurationManager", "PlayerList"); + } + + /** + * Retrieve the NetLoginHandler class (or PendingConnection) * @return The NetLoginHandler class. */ public static Class getNetLoginHandlerClass() { @@ -297,7 +361,7 @@ public class MinecraftReflection { } /** - * Retrieve the NetServerHandler class. + * Retrieve the NetServerHandler class (or PlayerConnection) * @return The NetServerHandler class. */ public static Class getNetServerHandlerClass() { @@ -313,7 +377,7 @@ public class MinecraftReflection { } /** - * Retrieve the NetHandler class. + * Retrieve the NetHandler class (or Connection) * @return The NetHandler class. */ public static Class getNetHandlerClass() { @@ -376,6 +440,14 @@ public class MinecraftReflection { return getMinecraftClass("WatchableObject"); } + /** + * Retrieve the ServerConnection abstract class. + * @return The ServerConnection class. + */ + public static Class getServerConnectionClass() { + return getMinecraftClass("ServerConnection"); + } + /** * Retrieve the NBT base class. * @return The NBT base class. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java index 012d9458..09ec2b10 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -574,7 +574,7 @@ public class WrappedDataWatcher implements Iterable { // Load the get-method try { getKeyValueMethod = fuzzy.getMethodByParameters( - "getWatchableObject", ".*WatchableObject", new String[] { int.class.getName() }); + "getWatchableObject", MinecraftReflection.getWatchableObjectClass(), new Class[] { int.class }); getKeyValueMethod.setAccessible(true); } catch (IllegalArgumentException e) { // Use fallback method