diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitCore.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitCore.java index 3eb6f1d8..50fe68d1 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitCore.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/BukkitCore.java @@ -39,7 +39,7 @@ public class BukkitCore extends FastLoginCore { private final FastLoginBukkit plugin; public BukkitCore(FastLoginBukkit plugin) { - super(BukkitCore.buildCache(5, 0)); + super(BukkitCore.buildCache(5, 0), plugin.getConfig().getValues(false)); this.plugin = plugin; } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/BukkitAuthPlugin.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/BukkitAuthPlugin.java index 6fbf8438..6f22b11f 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/BukkitAuthPlugin.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/hooks/BukkitAuthPlugin.java @@ -1,11 +1,13 @@ package com.github.games647.fastlogin.bukkit.hooks; +import com.github.games647.fastlogin.core.AuthPlugin; + import org.bukkit.entity.Player; /** * Represents a supporting authentication plugin in Bukkit/Spigot/... servers */ -public interface BukkitAuthPlugin { +public interface BukkitAuthPlugin extends AuthPlugin { /** * Login the premium (paid account) player after diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java index 1fe86992..70046a90 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/NameCheckTask.java @@ -1,25 +1,18 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib; -import com.comphenix.protocol.PacketType; import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; +import com.github.games647.fastlogin.core.JoinManagement; import com.github.games647.fastlogin.core.PlayerProfile; -import java.lang.reflect.InvocationTargetException; -import java.security.PublicKey; import java.util.Random; -import java.util.UUID; import java.util.logging.Level; import org.bukkit.entity.Player; -public class NameCheckTask implements Runnable { - - private static final int VERIFY_TOKEN_LENGTH = 4; +public class NameCheckTask extends JoinManagement implements Runnable { private final FastLoginBukkit plugin; private final PacketEvent packetEvent; @@ -30,6 +23,8 @@ public class NameCheckTask implements Runnable { private final String username; public NameCheckTask(FastLoginBukkit plugin, PacketEvent packetEvent, Random random, Player player, String username) { + super(plugin.getCore(), plugin.getAuthPlugin()); + this.plugin = plugin; this.packetEvent = packetEvent; this.random = random; @@ -40,120 +35,40 @@ public class NameCheckTask implements Runnable { @Override public void run() { try { - nameCheck(); + super.onLogin(username, new ProtocolLibLoginSource(plugin, packetEvent, player, random)); } finally { ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent); } } - private void nameCheck() { - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username); - if (profile == null) { + //minecraft server implementation + //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161 + @Override + public void requestPremiumLogin(ProtocolLibLoginSource source, PlayerProfile profile, String username, boolean registered) { + try { + source.setOnlineMode(); + } catch (Exception ex) { + plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to cracked login", ex); return; } - if (profile.getUserId() == -1) { - UUID premiumUUID = null; + String ip = player.getAddress().getAddress().getHostAddress(); + core.getPendingLogins().put(ip + username, new Object()); - String ip = player.getAddress().getAddress().getHostAddress(); - if (plugin.getCore().getPendingLogins().containsKey(ip + username) - && plugin.getConfig().getBoolean("secondAttemptCracked")) { - plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username); + String serverId = source.getServerId(); + byte[] verify = source.getVerifyToken(); - //first login request failed so make a cracked session - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(player.getAddress().toString(), loginSession); - return; - } - - //user not exists in the db - try { - boolean isRegistered = plugin.getAuthPlugin().isRegistered(username); - if (plugin.getConfig().getBoolean("nameChangeCheck") - || (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) { - premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username); - } - - if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) { - PlayerProfile uuidProfile = plugin.getCore().getStorage().loadProfile(premiumUUID); - if (uuidProfile != null) { - plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID); - - //update the username to the new one in the database - uuidProfile.setPlayerName(username); - - enablePremiumLogin(uuidProfile, false); - return; - } - } - - if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) { - plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); - enablePremiumLogin(profile, false); - return; - } - - //no premium check passed so we save it as a cracked player - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(player.getAddress().toString(), loginSession); - } catch (Exception ex) { - plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex); - } - } else if (profile.isPremium()) { - enablePremiumLogin(profile, true); - } else { - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(player.getAddress().toString(), loginSession); + BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile); + plugin.getSessions().put(player.getAddress().toString(), playerSession); + //cancel only if the player has a paid account otherwise login as normal offline player + synchronized (packetEvent.getAsyncMarker().getProcessingLock()) { + packetEvent.setCancelled(true); } } - //minecraft server implementation - //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161 - private void enablePremiumLogin(PlayerProfile profile, boolean registered) { - //randomized server id to make sure the request is for our server - //this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/ - String serverId = Long.toString(random.nextLong(), 16); - - //generate a random token which should be the same when we receive it from the client - byte[] verify = new byte[VERIFY_TOKEN_LENGTH]; - random.nextBytes(verify); - - boolean success = sentEncryptionRequest(player, serverId, verify); - if (success) { - String ip = player.getAddress().getAddress().getHostAddress(); - plugin.getCore().getPendingLogins().put(ip + username, new Object()); - - BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile); - plugin.getSessions().put(player.getAddress().toString(), playerSession); - //cancel only if the player has a paid account otherwise login as normal offline player - synchronized (packetEvent.getAsyncMarker().getProcessingLock()) { - packetEvent.setCancelled(true); - } - } - } - - private boolean sentEncryptionRequest(Player player, String serverId, byte[] verifyToken) { - ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); - try { - /** - * Packet Information: http://wiki.vg/Protocol#Encryption_Request - * - * ServerID="" (String) key=public server key verifyToken=random 4 byte array - */ - PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN); - - newPacket.getStrings().write(0, serverId); - newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic()); - - newPacket.getByteArrays().write(0, verifyToken); - - //serverId is a empty string - protocolManager.sendServerPacket(player, newPacket); - return true; - } catch (InvocationTargetException ex) { - plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex); - } - - return false; + @Override + public void startCrackedSession(ProtocolLibLoginSource source, PlayerProfile profile, String username) { + BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); + plugin.getSessions().put(player.getAddress().toString(), loginSession); } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java new file mode 100644 index 00000000..30ee54b9 --- /dev/null +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibLoginSource.java @@ -0,0 +1,99 @@ +package com.github.games647.fastlogin.bukkit.listener.protocollib; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.WrappedChatComponent; +import com.github.games647.fastlogin.bukkit.FastLoginBukkit; +import com.github.games647.fastlogin.core.LoginSource; +import java.lang.reflect.InvocationTargetException; + +import java.net.InetSocketAddress; +import java.security.PublicKey; +import java.util.Random; + +import org.bukkit.entity.Player; + +public class ProtocolLibLoginSource implements LoginSource { + + private static final int VERIFY_TOKEN_LENGTH = 4; + + private final FastLoginBukkit plugin; + + private final PacketEvent packetEvent; + private final Player player; + + private final Random random; + + private String serverId; + private byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH]; + + public ProtocolLibLoginSource(FastLoginBukkit plugin, PacketEvent packetEvent, Player player, Random random) { + this.plugin = plugin; + this.packetEvent = packetEvent; + this.player = player; + this.random = random; + } + + @Override + public void setOnlineMode() throws Exception { + //randomized server id to make sure the request is for our server + //this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/ + serverId = Long.toString(random.nextLong(), 16); + + //generate a random token which should be the same when we receive it from the client + random.nextBytes(verifyToken); + + sentEncryptionRequest(); + } + + @Override + public void kick(String message) throws Exception { + ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + PacketContainer kickPacket = protocolManager.createPacket(PacketType.Login.Server.DISCONNECT); + kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(message)); + + try { + //send kick packet at login state + //the normal event.getPlayer.kickPlayer(String) method does only work at play state + protocolManager.sendServerPacket(player, kickPacket); + } finally { + //tell the server that we want to close the connection + player.kickPlayer("Disconnect"); + } + } + + @Override + public InetSocketAddress getAddress() { + return packetEvent.getPlayer().getAddress(); + } + + private void sentEncryptionRequest() throws InvocationTargetException { + ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + /** + * Packet Information: http://wiki.vg/Protocol#Encryption_Request + * + * ServerID="" (String) key=public server key verifyToken=random 4 byte array + */ + PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN); + + newPacket.getStrings().write(0, serverId); + newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic()); + + newPacket.getByteArrays().write(0, verifyToken); + + //serverId is a empty string + protocolManager.sendServerPacket(player, newPacket); + } + + public String getServerId() { + return serverId; + } + + public byte[] getVerifyToken() { + return verifyToken; + } +} diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/StartPacketListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/StartPacketListener.java index b6d6a520..a3c99581 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/StartPacketListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/StartPacketListener.java @@ -5,12 +5,11 @@ import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketEvent; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin; import java.util.Random; import java.util.logging.Level; -import org.bukkit.Bukkit; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; /** @@ -52,7 +51,7 @@ public class StartPacketListener extends PacketAdapter { */ @Override public void onPacketReceiving(PacketEvent packetEvent) { - if (packetEvent.isCancelled()) { + if (packetEvent.isCancelled() || plugin.getAuthPlugin() == null) { return; } @@ -72,11 +71,6 @@ public class StartPacketListener extends PacketAdapter { String username = packet.getGameProfiles().read(0).getName(); plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username}); - BukkitAuthPlugin authPlugin = plugin.getAuthPlugin(); - if (authPlugin == null) { - return; - } - packetEvent.getAsyncMarker().incrementProcessingDelay(); NameCheckTask nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username); Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask); diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolLoginSource.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolLoginSource.java new file mode 100644 index 00000000..f91f4c0a --- /dev/null +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolLoginSource.java @@ -0,0 +1,35 @@ +package com.github.games647.fastlogin.bukkit.listener.protocolsupport; + +import com.github.games647.fastlogin.core.LoginSource; + +import java.net.InetSocketAddress; + +import protocolsupport.api.events.PlayerLoginStartEvent; + +public class ProtocolLoginSource implements LoginSource { + + private final PlayerLoginStartEvent loginStartEvent; + + public ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) { + this.loginStartEvent = loginStartEvent; + } + + @Override + public void setOnlineMode() { + loginStartEvent.setOnlineMode(true); + } + + @Override + public void kick(String message) { + loginStartEvent.denyLogin(message); + } + + @Override + public InetSocketAddress getAddress() { + return loginStartEvent.getAddress(); + } + + public PlayerLoginStartEvent getLoginStartEvent() { + return loginStartEvent; + } +} diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java index bbbf6a56..a9becf1a 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocolsupport/ProtocolSupportListener.java @@ -2,31 +2,32 @@ package com.github.games647.fastlogin.bukkit.listener.protocolsupport; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; -import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin; +import com.github.games647.fastlogin.core.JoinManagement; import com.github.games647.fastlogin.core.PlayerProfile; import java.net.InetSocketAddress; -import java.util.UUID; -import java.util.logging.Level; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import protocolsupport.api.events.PlayerLoginStartEvent; import protocolsupport.api.events.PlayerPropertiesResolveEvent; -public class ProtocolSupportListener implements Listener { +public class ProtocolSupportListener extends JoinManagement implements Listener { protected final FastLoginBukkit plugin; public ProtocolSupportListener(FastLoginBukkit plugin) { + super(plugin.getCore(), plugin.getAuthPlugin()); + this.plugin = plugin; } @EventHandler(ignoreCancelled = true) public void onLoginStart(PlayerLoginStartEvent loginStartEvent) { plugin.setServerStarted(); - if (loginStartEvent.isLoginDenied()) { + if (loginStartEvent.isLoginDenied() || plugin.getAuthPlugin() == null) { return; } @@ -36,68 +37,7 @@ public class ProtocolSupportListener implements Listener { //remove old data every time on a new login in order to keep the session only for one person plugin.getSessions().remove(address.toString()); - BukkitAuthPlugin authPlugin = plugin.getAuthPlugin(); - if (authPlugin == null) { - return; - } - - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username); - if (profile != null) { - if (profile.getUserId() == -1) { - - String ip = address.getAddress().getHostAddress(); - if (plugin.getCore().getPendingLogins().containsKey(ip + username) - && plugin.getConfig().getBoolean("secondAttemptCracked")) { - plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username); - - //first login request failed so make a cracked session - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(address.toString(), loginSession); - return; - } - - UUID premiumUUID = null; - - //user not exists in the db - try { - boolean isRegistered = plugin.getAuthPlugin().isRegistered(username); - if (plugin.getConfig().getBoolean("nameChangeCheck") - || (plugin.getConfig().getBoolean("autoRegister") && !isRegistered)) { - premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username); - } - - if (premiumUUID != null && plugin.getConfig().getBoolean("nameChangeCheck")) { - PlayerProfile uuidProfile = plugin.getCore().getStorage().loadProfile(premiumUUID); - if (uuidProfile != null) { - plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID); - - //update the username to the new one in the database - uuidProfile.setPlayerName(username); - - startPremiumSession(username, loginStartEvent, false, uuidProfile); - return; - } - } - - if (premiumUUID != null && plugin.getConfig().getBoolean("autoRegister") && !isRegistered) { - plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); - startPremiumSession(username, loginStartEvent, false, profile); - return; - } - - //no premium check passed so we save it as a cracked player - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(address.toString(), loginSession); - } catch (Exception ex) { - plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex); - } - } else if (profile.isPremium()) { - startPremiumSession(username, loginStartEvent, true, profile); - } else { - BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); - plugin.getSessions().put(address.toString(), loginSession); - } - } + super.onLogin(username, new ProtocolLoginSource(loginStartEvent)); } @EventHandler(ignoreCancelled = true) @@ -115,18 +55,23 @@ public class ProtocolSupportListener implements Listener { } } - private void startPremiumSession(String username, PlayerLoginStartEvent loginStartEvent, boolean registered - , PlayerProfile playerProfile) { - loginStartEvent.setOnlineMode(true); - InetSocketAddress address = loginStartEvent.getAddress(); + @Override + public void requestPremiumLogin(ProtocolLoginSource source, PlayerProfile profile, String username, boolean registered) { + source.setOnlineMode(); - String ip = address.getAddress().getHostAddress(); + String ip = source.getAddress().getAddress().getHostAddress(); plugin.getCore().getPendingLogins().put(ip + username, new Object()); - BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, playerProfile); - plugin.getSessions().put(address.toString(), playerSession); + BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, profile); + plugin.getSessions().put(source.getAddress().toString(), playerSession); if (plugin.getConfig().getBoolean("premiumUuid")) { - loginStartEvent.setUseOnlineModeUUID(true); + source.getLoginStartEvent().setUseOnlineModeUUID(true); } } + + @Override + public void startCrackedSession(ProtocolLoginSource source, PlayerProfile profile, String username) { + BukkitLoginSession loginSession = new BukkitLoginSession(username, profile); + plugin.getSessions().put(source.getAddress().toString(), loginSession); + } } diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeCore.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeCore.java index c5982334..34c0f62e 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeCore.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeCore.java @@ -2,12 +2,15 @@ package com.github.games647.fastlogin.bungee; import com.github.games647.fastlogin.core.FastLoginCore; import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Maps; import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.util.Collection; +import java.util.Map; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -24,7 +27,8 @@ public class BungeeCore extends FastLoginCore { private final FastLoginBungee plugin; public BungeeCore(FastLoginBungee plugin) { - super(CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build().asMap()); + super(CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build().asMap() + , generateConfigMap(plugin.getConfig())); this.plugin = plugin; } @@ -90,4 +94,14 @@ public class BungeeCore extends FastLoginCore { } } } + + private static Map generateConfigMap(Configuration config) { + Map configMap = Maps.newHashMap(); + Collection keys = config.getKeys(); + for (String key : keys) { + configMap.put(key, config.get(key)); + } + + return configMap; + } } diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeLoginSource.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeLoginSource.java new file mode 100644 index 00000000..82853a67 --- /dev/null +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/BungeeLoginSource.java @@ -0,0 +1,35 @@ +package com.github.games647.fastlogin.bungee; + +import com.github.games647.fastlogin.core.LoginSource; + +import java.net.InetSocketAddress; + +import net.md_5.bungee.api.connection.PendingConnection; + +public class BungeeLoginSource implements LoginSource { + + private final PendingConnection connection; + + public BungeeLoginSource(PendingConnection connection) { + this.connection = connection; + } + + @Override + public void setOnlineMode() { + connection.setOnlineMode(true); + } + + @Override + public void kick(String message) { + connection.disconnect(message); + } + + @Override + public InetSocketAddress getAddress() { + return connection.getAddress(); + } + + public PendingConnection getConnection() { + return connection; + } +} diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthPlugin.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthPlugin.java index 39259cbb..daafa057 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthPlugin.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/hooks/BungeeAuthPlugin.java @@ -1,11 +1,12 @@ package com.github.games647.fastlogin.bungee.hooks; +import com.github.games647.fastlogin.core.AuthPlugin; import net.md_5.bungee.api.connection.ProxiedPlayer; /** * Represents a supporting authentication plugin in BungeeCord/Waterfall/... servers */ -public interface BungeeAuthPlugin { +public interface BungeeAuthPlugin extends AuthPlugin { /** * Login the premium (paid account) player after diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java index d31eca6e..c2be8c82 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/tasks/AsyncPremiumCheck.java @@ -1,22 +1,23 @@ package com.github.games647.fastlogin.bungee.tasks; import com.github.games647.fastlogin.bungee.BungeeLoginSession; +import com.github.games647.fastlogin.bungee.BungeeLoginSource; import com.github.games647.fastlogin.bungee.FastLoginBungee; -import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin; +import com.github.games647.fastlogin.core.JoinManagement; import com.github.games647.fastlogin.core.PlayerProfile; -import java.util.UUID; -import java.util.logging.Level; - import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.PreLoginEvent; -public class AsyncPremiumCheck implements Runnable { +public class AsyncPremiumCheck extends JoinManagement implements Runnable { private final FastLoginBungee plugin; private final PreLoginEvent preLoginEvent; public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent) { + super(plugin.getCore(), plugin.getBungeeAuthPlugin()); + this.plugin = plugin; this.preLoginEvent = preLoginEvent; } @@ -27,89 +28,24 @@ public class AsyncPremiumCheck implements Runnable { plugin.getSession().remove(connection); String username = connection.getName(); - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(username); - if (profile == null) { - return; - } - try { - if (profile.getUserId() == -1) { - String ip = connection.getAddress().getAddress().getHostAddress(); - if (plugin.getCore().getPendingLogins().containsKey(ip + username) - && plugin.getConfig().getBoolean("secondAttemptCracked")) { - plugin.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username); - - //first login request failed so make a cracked session - plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile)); - return; - } - - UUID premiumUUID = null; - if (plugin.getConfig().getBoolean("nameChangeCheck") || plugin.getConfig().getBoolean("autoRegister")) { - premiumUUID = plugin.getCore().getMojangApiConnector().getPremiumUUID(username); - } - - if (premiumUUID == null - || (!checkNameChange(premiumUUID, connection, username) - && !checkPremiumName(username, connection, profile))) { - //nothing detected the player as premium -> start a cracked session - plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile)); - } - } else if (profile.isPremium()) { - requestPremiumLogin(connection, profile, username, true); - } else { - if (plugin.getConfig().getBoolean("switchMode")) { - connection.disconnect(plugin.getCore().getMessage("switch-kick-message")); - return; - } - - //Cracked session - plugin.getSession().put(connection, new BungeeLoginSession(username, false, profile)); - } - } catch (Exception ex) { - plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex); + super.onLogin(username, new BungeeLoginSource(connection)); } finally { preLoginEvent.completeIntent(plugin); } } - private boolean checkPremiumName(String username, PendingConnection connection, PlayerProfile profile) - throws Exception { - BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin(); - if (plugin.getConfig().getBoolean("autoRegister") - && (authPlugin == null || !authPlugin.isRegistered(username))) { - plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); - requestPremiumLogin(connection, profile, username, false); - return true; - } + @Override + public void requestPremiumLogin(BungeeLoginSource source, PlayerProfile profile, String username, boolean registered) { + source.setOnlineMode(); + plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile)); - return false; - } - - private boolean checkNameChange(UUID premiumUUID, PendingConnection connection, String username) { - //user not exists in the db - if (plugin.getConfig().getBoolean("nameChangeCheck")) { - PlayerProfile profile = plugin.getCore().getStorage().loadProfile(premiumUUID); - if (profile != null) { - //uuid exists in the database - plugin.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID); - - //update the username to the new one in the database - profile.setPlayerName(username); - - requestPremiumLogin(connection, profile, username, false); - return true; - } - } - - return false; - } - - private void requestPremiumLogin(PendingConnection con, PlayerProfile profile, String username, boolean register) { - con.setOnlineMode(true); - plugin.getSession().put(con, new BungeeLoginSession(username, register, profile)); - - String ip = con.getAddress().getAddress().getHostAddress(); + String ip = source.getAddress().getAddress().getHostAddress(); plugin.getCore().getPendingLogins().put(ip + username, new Object()); } + + @Override + public void startCrackedSession(BungeeLoginSource source, PlayerProfile profile, String username) { + plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, false, profile)); + } } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/AuthPlugin.java b/core/src/main/java/com/github/games647/fastlogin/core/AuthPlugin.java new file mode 100644 index 00000000..2e09a607 --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/AuthPlugin.java @@ -0,0 +1,10 @@ +package com.github.games647.fastlogin.core; + +public interface AuthPlugin { + + boolean forceLogin(T player); + + boolean forceRegister(T player, String password); + + boolean isRegistered(String playerName) throws Exception; +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/FastLoginCore.java index 5175bb11..4966b53a 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/FastLoginCore.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/FastLoginCore.java @@ -33,12 +33,14 @@ public abstract class FastLoginCore { protected final Map localeMessages = new ConcurrentHashMap<>(); private final ConcurrentMap pendingLogins; + private final SharedConfig sharedConfig; private MojangApiConnector mojangApiConnector; private AuthStorage storage; - public FastLoginCore(ConcurrentMap pendingLogins) { + public FastLoginCore(ConcurrentMap pendingLogins, Map config) { this.pendingLogins = pendingLogins; + this.sharedConfig = new SharedConfig(config); } public void setMojangApiConnector(MojangApiConnector mojangApiConnector) { @@ -118,6 +120,10 @@ public abstract class FastLoginCore { return false; } + public SharedConfig getSharedConfig() { + return sharedConfig; + } + public ConcurrentMap getPendingLogins() { return pendingLogins; } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/JoinManagement.java b/core/src/main/java/com/github/games647/fastlogin/core/JoinManagement.java new file mode 100644 index 00000000..e925ef9d --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/JoinManagement.java @@ -0,0 +1,96 @@ +package com.github.games647.fastlogin.core; + +import java.util.UUID; +import java.util.logging.Level; + +public abstract class JoinManagement { + + protected final FastLoginCore core; + protected final AuthPlugin authHook; + + public JoinManagement(FastLoginCore core, AuthPlugin authHook) { + this.core = core; + this.authHook = authHook; + } + + public void onLogin(String username, S source) { + PlayerProfile profile = core.getStorage().loadProfile(username); + if (profile == null) { + return; + } + + SharedConfig sharedConfig = core.getSharedConfig(); + + String ip = source.getAddress().getAddress().getHostAddress(); + try { + if (profile.getUserId() == -1) { + if (core.getPendingLogins().containsKey(ip + username) + && sharedConfig.get("secondAttemptCracked", false)) { + core.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username); + + //first login request failed so make a cracked session + startCrackedSession(source, profile, username); + return; + } + + UUID premiumUUID = null; + if (sharedConfig.get("nameChangeCheck", false) || sharedConfig.get("autoRegister", false)) { + premiumUUID = core.getMojangApiConnector().getPremiumUUID(username); + } + + if (premiumUUID == null + || (!checkNameChange(premiumUUID, source, username) + && !checkPremiumName(username, source, profile))) { + //nothing detected the player as premium -> start a cracked session + startCrackedSession(source, profile, username); + } + } else if (profile.isPremium()) { + requestPremiumLogin(source, profile, username, true); + } else { + if (core.getSharedConfig().get("switchMode", false)) { + source.kick(core.getMessage("switch-kick-message")); + return; + } + + startCrackedSession(source, profile, username); + } + } catch (Exception ex) { + core.getLogger().log(Level.SEVERE, "Failed to check premium state", ex); + } + } + + private boolean checkPremiumName(String username, S source, PlayerProfile profile) + throws Exception { + if (core.getSharedConfig().get("autoRegister", false) + && (authHook == null || !authHook.isRegistered(username))) { + core.getLogger().log(Level.FINER, "Player {0} uses a premium username", username); + requestPremiumLogin(source, profile, username, false); + return true; + } + + return false; + } + + private boolean checkNameChange(UUID premiumUUID, S source, String username) { + //user not exists in the db + if (core.getSharedConfig().get("nameChangeCheck", false)) { + PlayerProfile profile = core.getStorage().loadProfile(premiumUUID); + if (profile != null) { + //uuid exists in the database + core.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID); + + //update the username to the new one in the database + profile.setPlayerName(username); + + requestPremiumLogin(source, profile, username, false); + return true; + } + } + + return false; + } + + public abstract void requestPremiumLogin(S source, PlayerProfile profile, String username, boolean registered); + + public abstract void startCrackedSession(S source, PlayerProfile profile, String username); +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/LoginSource.java b/core/src/main/java/com/github/games647/fastlogin/core/LoginSource.java new file mode 100644 index 00000000..0103b269 --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/LoginSource.java @@ -0,0 +1,12 @@ +package com.github.games647.fastlogin.core; + +import java.net.InetSocketAddress; + +public interface LoginSource { + + void setOnlineMode() throws Exception; + + void kick(String message) throws Exception; + + InetSocketAddress getAddress(); +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/SharedConfig.java b/core/src/main/java/com/github/games647/fastlogin/core/SharedConfig.java new file mode 100644 index 00000000..72cc2958 --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/SharedConfig.java @@ -0,0 +1,22 @@ +package com.github.games647.fastlogin.core; + +import java.util.Map; + +public class SharedConfig { + + private final Map configValues; + + public SharedConfig(Map configValues) { + this.configValues = configValues; + } + + @SuppressWarnings("unchecked") + public T get(String path, T def) { + Object val = configValues.get(path); + return ( val != null ) ? (T) val : def; + } + + public T get(String path) { + return get(path, null); + } +}