Skip to content

Commit

Permalink
Fix TCPShield compat by using raw address for sessions
Browse files Browse the repository at this point in the history
TCPShield overwrites the IP address during connection. ProtocolLib
doesn't notice this change, because it uses the end-to-end connection
which is the proxy IP. This causes getAddress calls during Spigot play
state and ProtocolLib auth state not match and then have conflicting
session ids.

A solution is also to hold onto the temporary player object. However
since we don't get a notification for a disconnect, holding it will
prevent to get GCed until the timeout occurs (1 minute).

Fixes #595
  • Loading branch information
games647 committed Aug 14, 2021
1 parent 213bacf commit 6fd1e5e
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 65 deletions.
2 changes: 1 addition & 1 deletion bukkit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
<dependency>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.15.2-R0.1-SNAPSHOT</version>
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package com.github.games647.fastlogin.bukkit;

import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
Expand All @@ -51,6 +52,8 @@
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.slf4j.Logger;
Expand All @@ -64,10 +67,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
private final Logger logger;

private final BukkitScheduler scheduler;
private boolean serverStarted;
private BungeeManager bungeeManager;
private final BukkitScheduler scheduler;
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;

private PremiumPlaceholder premiumPlaceholder;
Expand All @@ -88,17 +90,26 @@ public void onEnable() {
setEnabled(false);
return;
}
// Check Floodgate config values
if (!isValidFloodgateConfigString("autoLoginFloodgate")
|| !isValidFloodgateConfigString("allowFloodgateNameConflict")) {
setEnabled(false);
return;
}

// Check Floodgate config values
if (!isValidFloodgateConfigString("autoLoginFloodgate")
|| !isValidFloodgateConfigString("allowFloodgateNameConflict")) {
setEnabled(false);
return;
}

bungeeManager = new BungeeManager(this);
bungeeManager.initialize();


getServer().getPluginManager().registerEvents(new Listener() {

@EventHandler
void onHandshake(PlayerHandshakeEvent handshakeEvent) {
handshakeEvent.setCancelled(false);
handshakeEvent.setSocketAddressHostname("192.168.0.1");
}
}, this);

PluginManager pluginManager = getServer().getPluginManager();
if (bungeeManager.isEnabled()) {
markInitialized();
Expand Down Expand Up @@ -182,10 +193,6 @@ public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
public BukkitLoginSession getSession(InetSocketAddress addr) {
String id = getSessionId(addr);
BukkitLoginSession session = loginSession.get(id);
if (session == null) {
logger.info("No session found for id {}", id);
}

return session;
}

Expand All @@ -195,7 +202,6 @@ public String getSessionId(InetSocketAddress addr) {

public void putSession(InetSocketAddress addr, BukkitLoginSession session) {
String id = getSessionId(addr);
logger.info("Starting session {}", id);
loginSession.put(id, session);
}

Expand Down Expand Up @@ -256,44 +262,45 @@ public BukkitScheduler getScheduler() {
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(message);
}

/**
* Checks if a config entry (related to Floodgate) is valid. <br>
* Writes to Log if the value is invalid.
* <p>
* This should be used for:
* <ul>
* <li>allowFloodgateNameConflict
* <li>autoLoginFloodgate
* <li>autoRegisterFloodgate
* </ul>
* </p>
*
* @param key the key of the entry in config.yml
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
*/
private boolean isValidFloodgateConfigString(String key) {
String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
logger.error("Invalid value detected for {} in FastLogin/config.yml.", key);
return false;
}
return true;
}

/**
* Checks if a plugin is installed on the server
* @param name the name of the plugin
* @return true if the plugin is installed
*/
@Override
public boolean isPluginInstalled(String name) {
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}

/**
* Send warning messages to log if incompatible plugins are used
* Checks if a config entry (related to Floodgate) is valid. <br>
* Writes to Log if the value is invalid.
* <p>
* This should be used for:
* <ul>
* <li>allowFloodgateNameConflict
* <li>autoLoginFloodgate
* <li>autoRegisterFloodgate
* </ul>
* </p>
*
* @param key the key of the entry in config.yml
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
*/
private boolean isValidFloodgateConfigString(String key) {
String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
logger.error("Invalid value detected for {} in FastLogin/config.yml.", key);
return false;
}
return true;
}

/**
* Checks if a plugin is installed on the server
*
* @param name the name of the plugin
* @return true if the plugin is installed
*/
@Override
public boolean isPluginInstalled(String name) {
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}

/**
* Send warning messages to log if incompatible plugins are used
*/
private void dependencyWarnings() {
if (isPluginInstalled("floodgate-bukkit")) {
Expand All @@ -303,13 +310,13 @@ private void dependencyWarnings() {
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
logger.warn("Don't forget to update Geyser to a supported version as well from "
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
} else if (isPluginInstalled("floodgate") && isPluginInstalled("ProtocolLib")) {
} else if (isPluginInstalled("floodgate") && isPluginInstalled("ProtocolLib")) {
logger.warn("We have detected that you are running FastLogin alongside Floodgate and ProtocolLib.");
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate's name prefixes from " +
"showing up when it is together used with ProtocolLib.");
logger.warn("If you would like to use Floodgate name prefixes, you can replace ProtocolLib with " +
"ProtocolSupport which does not have this issue.");
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,20 @@
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;

import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.events.RestoreSessionEvent;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationMethod;

import java.lang.reflect.Field;

import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;

import java.lang.reflect.Field;

/**
* GitHub: https://github.com/Xephi/AuthMeReloaded/
* <p>
Expand Down Expand Up @@ -75,7 +77,7 @@ public AuthMeHook(FastLoginBukkit plugin) {
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
Player player = restoreSessionEvent.getPlayer();

BukkitLoginSession session = plugin.getSession(player.getAddress());
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
if (session != null && session.isVerified()) {
restoreSessionEvent.setCancelled(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void onRegisterMessage(Player player, String playerName, InetSocketAddre

private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
session.setVerified(true);
plugin.putSession(player.getAddress(), session);
plugin.putSession(player.spigot().getRawAddress(), session);

// only start a new login task if the join event fired earlier. This event then didn
boolean result = plugin.getBungeeManager().didJoinEventFired(player);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void onPlayerJoin(PlayerJoinEvent joinEvent) {
// session exists so the player is ready for force login
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
// having the login session from the login process
BukkitLoginSession session = plugin.getSession(player.getAddress());
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());

boolean isFloodgateLogin = false;
if (Bukkit.getServer().getPluginManager().isPluginEnabled("floodgate")) {
Expand All @@ -86,7 +86,7 @@ public void onPlayerJoin(PlayerJoinEvent joinEvent) {

if (!isFloodgateLogin) {
if (session == null) {
String sessionId = plugin.getSessionId(player.getAddress());
String sessionId = plugin.getSessionId(player.spigot().getRawAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
} else {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecur

//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(serverKey.getPrivate(), responseVerify))) {
//check if the verify token are equal to the server sent one
//check if the verify-token are equal to the server sent one
disconnect("invalid-verify-token", true
, "GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}"
, session.getRequestUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void kick(String message) {

@Override
public InetSocketAddress getAddress() {
return loginStartEvent.getAddress();
return loginStartEvent.getConnection().getRawAddress();
}

public PlayerLoginStartEvent getLoginStartEvent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
}

String username = loginStartEvent.getConnection().getProfile().getName();
InetSocketAddress address = loginStartEvent.getAddress();
InetSocketAddress address = loginStartEvent.getConnection().getRawAddress();

//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(address);
Expand All @@ -82,13 +82,14 @@ public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {

@EventHandler
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
InetSocketAddress address = closeEvent.getConnection().getAddress();
InetSocketAddress address = closeEvent.getConnection().getRawAddress();
plugin.removeSession(address);
}

@EventHandler
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
InetSocketAddress address = profileCompleteEvent.getAddress();
InetSocketAddress address = profileCompleteEvent.getConnection().getRawAddress();

BukkitLoginSession session = plugin.getSession(address);

if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
Expand Down

0 comments on commit 6fd1e5e

Please sign in to comment.