| /* Copyright 2015-2022 Rivoreo |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE |
| FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| package rivoreo.minecraft.tsauth; |
| |
| import org.bukkit.Server; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.InetAddress; |
| import java.util.logging.Logger; |
| import java.util.UUID; |
| |
| public class MojangAuthenticatorWrapper { |
| public MojangAuthenticatorWrapper(Logger log, Server craftbukkit_server) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { |
| this.log = log; |
| try { |
| Class<?> protocolsupport_minecraft_session_service_class = |
| Class.forName("protocolsupport.protocol.utils.authlib.MinecraftSessionService"); |
| mojang_minecraft_session_service_has_joined_server_method = |
| protocolsupport_minecraft_session_service_class.getDeclaredMethod("hasJoinedServer", String.class, String.class); |
| } catch(Exception e) { |
| Class<?> craftbukkit_server_class = craftbukkit_server.getClass(); |
| if(!craftbukkit_server_class.getSimpleName().equals("CraftServer")) { |
| throw new IllegalArgumentException("A CraftBukkit server class is required"); |
| } |
| Method craftbukkit_server_get_server_method = craftbukkit_server_class.getDeclaredMethod("getServer"); |
| Class<?> minecraft_server_class = craftbukkit_server_get_server_method.getReturnType(); |
| Object minecraft_server = craftbukkit_server_get_server_method.invoke(craftbukkit_server); |
| for(Method m : minecraft_server_class.getDeclaredMethods()) { |
| Class<?> c = m.getReturnType(); |
| String class_name = c.getName(); |
| if(class_name.equals("com.mojang.authlib.minecraft.MinecraftSessionService") || class_name.equals("net.minecraft.util.com.mojang.authlib.minecraft.MinecraftSessionService")) { |
| mojang_minecraft_session_service = m.invoke(minecraft_server); |
| for(Method mm : c.getDeclaredMethods()) { |
| if(mm.getName().equals("hasJoinedServer")) { |
| Class<?>[] arg_classes = mm.getParameterTypes(); |
| if((arg_classes.length == 2 || arg_classes.length == 3) && arg_classes[0].equals(mm.getReturnType()) && arg_classes[1].equals(String.class)) { |
| mojang_minecraft_session_service_has_joined_server_method = mm; |
| mojang_minecraft_session_service_has_joined_server_method_has_ip_address_argument = arg_classes.length > 2; |
| mojang_gameprofile_class = arg_classes[0]; |
| mojang_gameprofile_class_constructor = mojang_gameprofile_class.getDeclaredConstructor(UUID.class, String.class); |
| AUTH_UNAVAIL_MAX_TRIES = 6; |
| return; |
| } |
| } |
| } |
| throw new NoSuchMethodException("Cannot find hasJoinedServer method from MinecraftSessionService"); |
| } |
| } |
| throw new NoSuchMethodException("Cannot find method for MinecraftSessionService from MinecraftServer"); |
| } |
| AUTH_UNAVAIL_MAX_TRIES = 4; |
| } |
| |
| private final int AUTH_UNAVAIL_MAX_TRIES; |
| |
| private Logger log; |
| private Method mojang_minecraft_session_service_has_joined_server_method; |
| private boolean mojang_minecraft_session_service_has_joined_server_method_has_ip_address_argument; |
| private Object mojang_minecraft_session_service; |
| private Class<?> mojang_gameprofile_class; |
| private Constructor<?> mojang_gameprofile_class_constructor; |
| private Field minecraft_server_login_handler_gameprofile_field; |
| private Object minecraft_server_login_handler; |
| |
| public void set_minecraft_server_login_handler_from_network_manager(Object nm) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { |
| Class<?> nm_class = nm.getClass(); |
| Method get_handler_method; |
| try { |
| get_handler_method = nm_class.getDeclaredMethod("func_150729_e"); |
| } catch(NoSuchMethodException e) { |
| try { |
| get_handler_method = nm_class.getDeclaredMethod("getPacketListener"); |
| } catch(NoSuchMethodException ee) { |
| get_handler_method = null; |
| for(Method m : nm_class.getDeclaredMethods()) { |
| if(m.getReturnType().getSimpleName().equals("PacketListener")) { |
| get_handler_method = m; |
| break; |
| } |
| } |
| if(get_handler_method == null) throw ee; |
| } |
| } |
| Object handler = get_handler_method.invoke(nm); |
| Class<?> handler_class = handler.getClass(); |
| if(handler_class.getName().equals("protocolsupport.zplatform.impl.spigot.network.handler.SpigotLoginListenerPlay")) { |
| Class<?> abstract_login_listener = handler_class.getSuperclass(); |
| if(!abstract_login_listener.getName().equals("protocolsupport.protocol.packet.handler.AbstractLoginListenerPlay")) { |
| throw new IllegalStateException("protocolsupport.protocol.packet.handler.AbstractLoginListenerPlay isn't the superclass of protocolsupport.zplatform.impl.spigot.network.handler.SpigotLoginListenerPlay, but instead " + abstract_login_listener.getName()); |
| } |
| minecraft_server_login_handler = handler; |
| minecraft_server_login_handler_gameprofile_field = abstract_login_listener.getDeclaredField("profile"); |
| minecraft_server_login_handler_gameprofile_field.setAccessible(true); |
| return; |
| } |
| String handler_class_simple_name = handler_class.getSimpleName(); |
| if(!handler_class_simple_name.equals("NetHandlerLoginServer") && !handler_class_simple_name.equals("LoginListener")) { |
| throw new IllegalStateException("Minecraft network manager doesn't have login handler installed"); |
| } |
| minecraft_server_login_handler = handler; |
| for(Field f : handler_class.getDeclaredFields()) { |
| if(f.getType().equals(mojang_gameprofile_class)) { |
| f.setAccessible(true); |
| minecraft_server_login_handler_gameprofile_field = f; |
| return; |
| } |
| } |
| throw new NoSuchFieldException("Cannot find field with type GameProfile from " + handler_class_simple_name); |
| } |
| |
| public boolean authenticate(String user_name, String server_session_hash, InetAddress remote_address) throws IllegalAccessException, InstantiationException, InvocationTargetException { |
| int tries = 0; |
| Object game_profile; |
| Object[] args; |
| if(mojang_gameprofile_class == null) { |
| // ProtocolSupport implementation |
| args = new Object[] { user_name, server_session_hash }; |
| } else { |
| game_profile = mojang_gameprofile_class_constructor.newInstance(null, user_name); |
| args = mojang_minecraft_session_service_has_joined_server_method_has_ip_address_argument ? |
| new Object[] { game_profile, server_session_hash, remote_address } : |
| new Object[] { game_profile, server_session_hash }; |
| } |
| while(true) try { |
| game_profile = mojang_minecraft_session_service_has_joined_server_method.invoke(mojang_minecraft_session_service, args); |
| break; |
| } catch(InvocationTargetException e) { |
| Throwable cause = e.getCause(); |
| if(cause == null) throw e; |
| if(!cause.getClass().getSimpleName().equals("AuthenticationUnavailableException")) throw e; |
| if(++tries > AUTH_UNAVAIL_MAX_TRIES) throw e; |
| Throwable deeper_cause = cause.getCause(); |
| log.warning(String.format("Mojang authentication service temporarily unavailable: %s: %s; retrying (%d)", |
| cause.getMessage(), deeper_cause == null ? "Unknown error" : deeper_cause.getMessage(), tries)); |
| } |
| if(game_profile != null && minecraft_server_login_handler_gameprofile_field != null) { |
| minecraft_server_login_handler_gameprofile_field.set(minecraft_server_login_handler, game_profile); |
| } |
| return game_profile != null; |
| } |
| } |