From 7d9ffa407a8ca60de8ab7de218906ff52b722c2c Mon Sep 17 00:00:00 2001 From: games647 Date: Sun, 6 Sep 2015 20:31:44 +0200 Subject: [PATCH] Added /premium command --- README.md | 2 +- .../github/games647/fastlogin/Encryption.java | 26 ++++------- .../github/games647/fastlogin/FastLogin.java | 26 +++++++++-- .../games647/fastlogin/PremiumCommand.java | 45 +++++++++++++++++++ .../listener/EncryptionPacketListener.java | 24 +++++----- .../fastlogin/listener/PlayerListener.java | 3 +- .../listener/StartPacketListener.java | 5 ++- src/main/resources/plugin.yml | 13 ++++++ 8 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/github/games647/fastlogin/PremiumCommand.java diff --git a/README.md b/README.md index e92630d5..370ba59e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # FastLogin -Checks if a minecraft player has a valid paid account. If so, they can skip offline authentification. +Checks if a minecraft player has a valid premium (paid account). If so, they can skip offline authentification. Requirements: * [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/) diff --git a/src/main/java/com/github/games647/fastlogin/Encryption.java b/src/main/java/com/github/games647/fastlogin/Encryption.java index 2555d8e4..28fcb5ea 100644 --- a/src/main/java/com/github/games647/fastlogin/Encryption.java +++ b/src/main/java/com/github/games647/fastlogin/Encryption.java @@ -1,6 +1,7 @@ package com.github.games647.fastlogin; -import java.io.UnsupportedEncodingException; +import com.google.common.base.Charsets; + import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.Key; @@ -23,11 +24,9 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; /** - * Source: - * https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/MinecraftEncryption.java + * Source: https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/MinecraftEncryption.java * - * Remapped by: - * https://github.com/Techcable/MinecraftMappings/tree/master/1.8 + * Remapped by: https://github.com/Techcable/MinecraftMappings/tree/master/1.8 */ public class Encryption { @@ -44,24 +43,15 @@ public class Encryption { } public static byte[] getServerIdHash(String serverId, PublicKey publickey, SecretKey secretkey) { - try { - return digestOperation("SHA-1" - , new byte[][]{serverId.getBytes("ISO_8859_1"), secretkey.getEncoded(), publickey.getEncoded()}); - } catch (UnsupportedEncodingException unsupportedencodingexception) { - unsupportedencodingexception.printStackTrace(); - return null; - } + return digestOperation("SHA-1" + , new byte[][]{serverId.getBytes(Charsets.ISO_8859_1), secretkey.getEncoded(), publickey.getEncoded()}); } private static byte[] digestOperation(String algo, byte[]... content) { try { MessageDigest messagedigest = MessageDigest.getInstance(algo); - int dataLength = content.length; - - for (int i = 0; i < dataLength; ++i) { - byte[] abyte1 = content[i]; - - messagedigest.update(abyte1); + for (byte[] data : content) { + messagedigest.update(data); } return messagedigest.digest(); diff --git a/src/main/java/com/github/games647/fastlogin/FastLogin.java b/src/main/java/com/github/games647/fastlogin/FastLogin.java index c2903e4c..273cf880 100644 --- a/src/main/java/com/github/games647/fastlogin/FastLogin.java +++ b/src/main/java/com/github/games647/fastlogin/FastLogin.java @@ -8,12 +8,14 @@ import com.github.games647.fastlogin.hooks.AuthPlugin; import com.github.games647.fastlogin.listener.EncryptionPacketListener; import com.github.games647.fastlogin.listener.StartPacketListener; import com.google.common.cache.CacheLoader; +import com.google.common.collect.Sets; import com.google.common.reflect.ClassPath; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.security.KeyPair; +import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -27,6 +29,10 @@ public class FastLogin extends JavaPlugin { //provide a immutable key pair to be thread safe private final KeyPair keyPair = Encryption.generateKeyPair(); + + //we need a thread-safe set because we access it async in the packet listener + private final Set enabledPremium = Sets.newConcurrentHashSet(); + //this map is thread-safe for async access (Packet Listener) //SafeCacheBuilder is used in order to be version independent private final ConcurrentMap session = SafeCacheBuilder.newBuilder() @@ -62,17 +68,21 @@ public class FastLogin extends JavaPlugin { ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); protocolManager.addPacketListener(new EncryptionPacketListener(this, protocolManager)); protocolManager.addPacketListener(new StartPacketListener(this, protocolManager)); + + //register commands + getCommand("premium").setExecutor(new PremiumCommand(this)); } @Override public void onDisable() { //clean up session.clear(); + enabledPremium.clear(); } /** - * Gets a thread-safe map about players which are connecting - * to the server are being checked to be premium (paid account) + * Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid + * account) * * @return a thread-safe session map */ @@ -90,8 +100,16 @@ public class FastLogin extends JavaPlugin { } /** - * Prepares a Mojang API connection. The connection is not - * started in this method + * Gets a set of user who activated premium logins + * + * @return user who activated premium logins + */ + public Set getEnabledPremium() { + return enabledPremium; + } + + /** + * Prepares a Mojang API connection. The connection is not started in this method * * @param url the url connecting to * @return the prepared connection diff --git a/src/main/java/com/github/games647/fastlogin/PremiumCommand.java b/src/main/java/com/github/games647/fastlogin/PremiumCommand.java new file mode 100644 index 00000000..d4807780 --- /dev/null +++ b/src/main/java/com/github/games647/fastlogin/PremiumCommand.java @@ -0,0 +1,45 @@ +package com.github.games647.fastlogin; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class PremiumCommand implements CommandExecutor { + + private final FastLogin plugin; + + public PremiumCommand(FastLogin plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + if (!(sender instanceof Player)) { + //console or command block + sender.sendMessage(ChatColor.DARK_RED + "Only players can add themselves as premium"); + return true; + } + + String playerName = sender.getName(); + plugin.getEnabledPremium().add(playerName); + sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players"); + return true; + } + + if (sender.hasPermission(plugin.getName() + ".command." + command.getName() + ".others")) { + String playerName = args[0]; + //todo check if valid username + plugin.getEnabledPremium().add(playerName); + sender.sendMessage(ChatColor.DARK_GREEN + "Added " + + ChatColor.DARK_BLUE + ChatColor.BOLD + playerName + + ChatColor.RESET + ChatColor.DARK_GREEN + " to the list of premium players"); + } else { + sender.sendMessage(ChatColor.DARK_RED + "Not enough permissions"); + } + + return true; + } +} diff --git a/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java index 8eaf70a8..f5b9a45d 100644 --- a/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java +++ b/src/main/java/com/github/games647/fastlogin/listener/EncryptionPacketListener.java @@ -5,7 +5,6 @@ import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.injector.server.SocketInjector; import com.comphenix.protocol.injector.server.TemporaryPlayerFactory; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.wrappers.WrappedGameProfile; @@ -125,6 +124,8 @@ public class EncryptionPacketListener extends PacketAdapter { if (hasJoinedServer(username, serverId)) { session.setVerified(true); + plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username); + receiveFakeStartPacket(username, player); } else { //user tried to fake a authentification @@ -146,15 +147,15 @@ public class EncryptionPacketListener extends PacketAdapter { private Object getNetworkManager(Player player) throws SecurityException, IllegalAccessException, NoSuchFieldException { - SocketInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player); - Field declaredField = injector.getClass().getDeclaredField("injector"); - declaredField.setAccessible(true); + Object injector = TemporaryPlayerFactory.getInjectorFromPlayer(player); + Field injectorField = injector.getClass().getDeclaredField("injector"); + injectorField.setAccessible(true); - Object rawInjector = declaredField.get(injector); + Object rawInjector = injectorField.get(injector); - declaredField = rawInjector.getClass().getDeclaredField("networkManager"); - declaredField.setAccessible(true); - return declaredField.get(rawInjector); + injectorField = rawInjector.getClass().getDeclaredField("networkManager"); + injectorField.setAccessible(true); + return injectorField.get(rawInjector); } private boolean hasJoinedServer(String username, String serverId) { @@ -177,11 +178,12 @@ public class EncryptionPacketListener extends PacketAdapter { //catch not only ioexceptions also parse and NPE on unexpected json format plugin.getLogger().log(Level.WARNING, "Failed to verify if session is valid", ex); } + //this connection doesn't need to be closed. So can make use of keep alive in java return false; } - private void receiveFakeStartPacket(String username, Player player) { + private void receiveFakeStartPacket(String username, Player from) { //fake a new login packet //see StartPacketListener for packet information PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START, true); @@ -189,11 +191,11 @@ public class EncryptionPacketListener extends PacketAdapter { WrappedGameProfile fakeProfile = WrappedGameProfile.fromOfflinePlayer(Bukkit.getOfflinePlayer(username)); startPacket.getGameProfiles().write(0, fakeProfile); try { - protocolManager.recieveClientPacket(player, startPacket, false); + protocolManager.recieveClientPacket(from, startPacket, false); } catch (InvocationTargetException | IllegalAccessException ex) { plugin.getLogger().log(Level.WARNING, "Failed to fake a new start packet", ex); //cancel the event in order to prevent the server receiving an invalid packet - player.kickPlayer("Error occurred"); + from.kickPlayer("Error occurred"); } } } diff --git a/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java b/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java index 9d886846..c84db062 100644 --- a/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java +++ b/src/main/java/com/github/games647/fastlogin/listener/PlayerListener.java @@ -27,7 +27,8 @@ public class PlayerListener implements Listener { final Player player = joinEvent.getPlayer(); String address = player.getAddress().toString(); - PlayerSession session = plugin.getSessions().get(address); + //removing the session because we now use it + PlayerSession session = plugin.getSessions().remove(address); //check if it's the same player as we checked before if (session != null && session.getUsername().equals(player.getName()) && session.isVerified()) { diff --git a/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java index 680f920b..5c5a03b9 100644 --- a/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java +++ b/src/main/java/com/github/games647/fastlogin/listener/StartPacketListener.java @@ -82,7 +82,8 @@ public class StartPacketListener extends PacketAdapter { } private boolean isPremium(String playerName) { - if (playernameMatcher.matcher(playerName).matches()) { + //check if it's a valid playername and the user activated fast logins + if (playernameMatcher.matcher(playerName).matches() && plugin.getEnabledPremium().contains(playerName)) { //only make a API call if the name is valid existing mojang account try { HttpURLConnection connection = plugin.getConnection(UUID_LINK + playerName); @@ -93,12 +94,14 @@ public class StartPacketListener extends PacketAdapter { } catch (IOException ex) { plugin.getLogger().log(Level.SEVERE, "Failed to check if player has a paid account", ex); } + //this connection doesn't need to be closed. So can make use of keep alive in java } return false; } private void sentEncryptionRequest(String sessionKey, String username, Player player, PacketEvent packetEvent) { + plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); try { /** * Packet Information: http://wiki.vg/Protocol#Encryption_Request diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 332912ed..7649c61a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -17,3 +17,16 @@ softdepend: - AuthMe - CrazyLogin - LoginSecurity + +commands: + premium: + description: 'Marks the invoker or the player specified as premium' + aliases: [prem, fastlogin, loginfast] + usage: / [player] + +permissions: + ${project.artifactId}.command.premium: + description: 'Mark themselves as premium using a command' + default: true + ${project.artifactId}.command.premium.others: + description: 'Mark other people as premium' \ No newline at end of file