More shared project code for less errors and less duplication

This commit is contained in:
games647
2016-09-11 18:59:42 +02:00
parent 8e5da01be0
commit 3e9c8e3a7e
16 changed files with 402 additions and 280 deletions
@@ -39,7 +39,7 @@ public class BukkitCore extends FastLoginCore {
private final FastLoginBukkit plugin;
public BukkitCore(FastLoginBukkit plugin) {
super(BukkitCore.<String, Object>buildCache(5, 0));
super(BukkitCore.<String, Object>buildCache(5, 0), plugin.getConfig().getValues(false));
this.plugin = plugin;
}
@@ -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<Player> {
/**
* Login the premium (paid account) player after
@@ -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<Player, ProtocolLibLoginSource> 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);
}
}
@@ -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;
}
}
@@ -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);
@@ -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;
}
}
@@ -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<Player, ProtocolLoginSource> 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);
}
}