Fix NPE for skin apply in ProtocolLib mode (Related #182)

This commit is contained in:
games647
2017-10-15 17:57:24 +02:00
parent 4858049c2a
commit 57eff4b3ec
18 changed files with 48 additions and 39 deletions

View File

@ -62,6 +62,7 @@ public class BukkitLoginSession extends LoginSession {
return ArrayUtils.clone(verifyToken);
}
//todo: this should be optional for players without a skin at all
public synchronized SkinProperties getSkinProperty() {
return skinProperty;
}

View File

@ -4,7 +4,7 @@ import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
import com.github.games647.fastlogin.bukkit.listener.BungeeListener;
import com.github.games647.fastlogin.bukkit.listener.JoinListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.LoginSkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
@ -70,7 +70,6 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
//check for incoming messages from the bungeecord version of this plugin
getServer().getMessenger().registerIncomingPluginChannel(this, getName(), new BungeeListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, getName());
//register listeners on success
} else {
if (!core.setupDatabase()) {
setEnabled(false);
@ -81,11 +80,10 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
getServer().getPluginManager().registerEvents(new ProtocolSupportListener(this), this);
} else if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
//they will be created with a static builder, because otherwise it will throw a
//java.lang.NoClassDefFoundError: com/comphenix/protocol/events/PacketListener if ProtocolSupport was
//only found
//NoClassDefFoundError: com/comphenix/protocol/events/PacketListener if only ProtocolSupport was found
ProtocolLibListener.register(this);
getServer().getPluginManager().registerEvents(new LoginSkinApplyListener(this), this);
getServer().getPluginManager().registerEvents(new SkinApplyListener(this), this);
} else {
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use BungeeCord");
}

View File

@ -7,10 +7,12 @@ import com.github.games647.fastlogin.core.shared.LoginSession;
import com.google.common.net.HostAndPort;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import org.slf4j.Logger;
@ -36,7 +38,8 @@ public class MojangApiBukkit extends MojangApiConnector {
HttpURLConnection conn = getConnection(url);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
//validate parsing
//http://wiki.vg/Protocol_Encryption#Server
VerificationReply verification = gson.fromJson(reader, VerificationReply.class);
@ -50,7 +53,7 @@ public class MojangApiBukkit extends MojangApiConnector {
return true;
}
} catch (Exception ex) {
} catch (IOException ex) {
//catch not only io-exceptions also parse and NPE on unexpected json format
logger.warn("Failed to verify session", ex);
}

View File

@ -36,7 +36,7 @@ public class CrackedCommand implements CommandExecutor {
plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setPremium(false);
profile.setUUID(null);
profile.setUuid(null);
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
plugin.getCore().getStorage().save(profile);
});

View File

@ -14,6 +14,7 @@ import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.util.Random;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
@ -93,6 +94,6 @@ public class ProtocolLibLoginSource implements LoginSource {
}
public byte[] getVerifyToken() {
return verifyToken;
return ArrayUtils.clone(verifyToken);
}
}

View File

@ -19,15 +19,14 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
public class LoginSkinApplyListener implements Listener {
public class SkinApplyListener implements Listener {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
private final FastLoginBukkit plugin;
public LoginSkinApplyListener(FastLoginBukkit plugin) {
public SkinApplyListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@ -46,7 +45,10 @@ public class LoginSkinApplyListener implements Listener {
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
if (session.getUsername().equals(player.getName())) {
SkinProperties skinProperty = session.getSkinProperty();
applySkin(player, skinProperty.getValue(), skinProperty.getSignature());
if (skinProperty != null) {
applySkin(player, skinProperty.getValue(), skinProperty.getSignature());
}
break;
}
}
@ -56,13 +58,13 @@ public class LoginSkinApplyListener implements Listener {
private void applySkin(Player player, String skinData, String signature) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
if (skinData != null && signature != null) {
WrappedSignedProperty skin = WrappedSignedProperty.fromValues("textures", skinData, signature);
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(SkinProperties.TEXTURE_KEY, skinData, signature);
try {
gameProfile.getProperties().put("textures", skin);
gameProfile.getProperties().put(SkinProperties.TEXTURE_KEY, skin);
} catch (ClassCastException castException) {
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
try {
MethodUtils.invokeMethod(map, "put", new Object[]{"textures", skin.getHandle()});
MethodUtils.invokeMethod(map, "put", new Object[]{SkinProperties.TEXTURE_KEY, skin.getHandle()});
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
plugin.getLog().error("Error setting premium skin", ex);
}

View File

@ -41,7 +41,7 @@ public class VerifyResponseTask implements Runnable {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.sharedSecret = sharedSecret;
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
}
@Override

View File

@ -3,6 +3,7 @@ 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.core.PlayerProfile;
import com.github.games647.fastlogin.core.mojang.SkinProperties;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import java.net.InetSocketAddress;
@ -46,7 +47,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
BukkitLoginSession session = plugin.getLoginSessions().get(address.toString());
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures") && session != null) {
if (propertiesResolveEvent.hasProperty(SkinProperties.TEXTURE_KEY) && session != null) {
session.setVerified(true);
}
}

View File

@ -18,15 +18,17 @@ import org.bukkit.metadata.FixedMetadataValue;
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player) {
super(core, player);
super(core, player, getSession(core.getPlugin(), player));
}
private static BukkitLoginSession getSession(FastLoginBukkit plugin, Player player) {
//remove the bungeecord identifier if there is ones
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
return plugin.getLoginSessions().remove(id);
}
@Override
public void run() {
//remove the bungeecord identifier if there is ones
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
session = core.getPlugin().getLoginSessions().remove(id);
//blacklist this target player for BungeeCord Id brute force attacks
FastLoginBukkit plugin = core.getPlugin();
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));

View File

@ -69,7 +69,7 @@ public class ConnectListener implements Listener {
session.setUuid(connection.getUniqueId());
PlayerProfile playerProfile = session.getProfile();
playerProfile.setUUID(connection.getUniqueId());
playerProfile.setUuid(connection.getUniqueId());
//bungeecord will do this automatically so override it on disabled option
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {

View File

@ -44,7 +44,7 @@ public class AsyncToggleMessage implements Runnable {
}
playerProfile.setPremium(false);
playerProfile.setUUID(null);
playerProfile.setUuid(null);
core.getStorage().save(playerProfile);
sendMessage("remove-premium");
}

View File

@ -11,7 +11,6 @@ import com.google.common.io.ByteStreams;
import java.util.UUID;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
@ -22,15 +21,13 @@ public class ForceLoginTask
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
ProxiedPlayer player, Server server) {
super(core, player);
super(core, player, core.getPlugin().getSession().get(player.getPendingConnection()));
this.server = server;
}
@Override
public void run() {
PendingConnection pendingConnection = player.getPendingConnection();
session = core.getPlugin().getSession().get(pendingConnection);
if (session == null) {
return;
}

View File

@ -138,7 +138,7 @@ public class AuthStorage {
public void save(PlayerProfile playerProfile) {
try (Connection con = dataSource.getConnection()) {
UUID uuid = playerProfile.getUUID();
UUID uuid = playerProfile.getUuid();
if (playerProfile.getUserId() == -1) {
try (PreparedStatement saveStmt = con.prepareStatement(INSERT_PROFILE, RETURN_GENERATED_KEYS)) {

View File

@ -44,11 +44,11 @@ public class PlayerProfile {
}
//todo: this should be optional
public synchronized UUID getUUID() {
public synchronized UUID getUuid() {
return uuid;
}
public synchronized void setUUID(UUID uuid) {
public synchronized void setUuid(UUID uuid) {
this.uuid = uuid;
}

View File

@ -51,8 +51,9 @@ public class MojangApiConnector {
private Instant lastRateLimit = Instant.now().minus(10, ChronoUnit.MINUTES);
protected final Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
protected final Logger logger;
protected final Gson gson = new GsonBuilder()
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
public MojangApiConnector(Logger logger, Collection<String> localAddresses, int rateLimit
, Iterable<HostAndPort> proxies) {

View File

@ -2,6 +2,8 @@ package com.github.games647.fastlogin.core.mojang;
public class SkinProperties {
public static final String TEXTURE_KEY = "textures";
private final String name = "textures";
private String value;

View File

@ -1,5 +1,6 @@
package com.github.games647.fastlogin.core.mojang;
import java.util.Arrays;
import java.util.UUID;
public class VerificationReply {
@ -17,6 +18,6 @@ public class VerificationReply {
}
public SkinProperties[] getProperties() {
return properties;
return Arrays.copyOf(properties, properties.length);
}
}

View File

@ -9,12 +9,12 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
protected final FastLoginCore<P, C, T> core;
protected final P player;
protected final L session;
protected L session;
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player) {
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player, L session) {
this.core = core;
this.player = player;
this.session = session;
}
@Override
@ -49,7 +49,7 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setUUID(session.getUuid());
playerProfile.setUuid(session.getUuid());
playerProfile.setPremium(true);
storage.save(playerProfile);
}
@ -59,7 +59,7 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
}
} else if (playerProfile != null) {
//cracked player
playerProfile.setUUID(null);
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}