mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-07-29 18:27:36 +02:00
fix: Initial addressing of #810
This commit contains a crude but functional fix for problem described by issue #810. It works by reimplementing the MojangResolver#hasJoined method using reflection to access the inaccessible fields. The significant difference is that unlike in the CraftAPI implementation, which sends the "ip" parameter when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation ommits the "ip" parameter also for IPv4, effectively enabling transparent proxies to work. WARNING: This commit contains only a proof-of-concept code with crude hacks to make it work with the least amount of code edits. Improvements upon the code will be included in following commits. The code is at the moment really ineffective, and therefore SHOULD NOT BE USED IN PRODUCTION! You have been warned.
This commit is contained in:
@ -36,15 +36,18 @@ import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.github.games647.craftapi.model.auth.Verification;
|
||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
import com.github.games647.craftapi.resolver.AbstractResolver;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
@ -138,7 +141,10 @@ public class VerifyResponseTask implements Runnable {
|
||||
try {
|
||||
MojangResolver resolver = plugin.getCore().getResolver();
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
|
||||
|
||||
// TODO edit marker
|
||||
Optional<Verification> response = VerifyResponseTask.hasJoined(resolver, requestedUsername, serverId, address);
|
||||
|
||||
if (response.isPresent()) {
|
||||
Verification verification = response.get();
|
||||
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
|
||||
@ -162,14 +168,91 @@ public class VerifyResponseTask implements Runnable {
|
||||
} else {
|
||||
//user tried to fake an authentication
|
||||
disconnect("invalid-session",
|
||||
"GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}",
|
||||
session.getRequestUsername(), socketAddress, serverId);
|
||||
String.format("GameProfile %s (%s) tried to log in with an invalid session. ServerId: %s",
|
||||
session.getRequestUsername(), socketAddress, serverId));
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
disconnect("error-kick", "Failed to connect to session server", ioEx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reimplementation of {@link MojangResolver#hasJoined(String, String, InetAddress)} using various crude reflection-based hacks
|
||||
* to access the protected code. The significant difference is that unlike in the CraftAPI implementation, which sends the "ip" parameter
|
||||
* when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation ommits the "ip" parameter also for IPv4, effectively
|
||||
* enabling transparent proxies to work.
|
||||
*
|
||||
* TODO Reimplement as MojangResolver subclass overriding hasJoined method -> "ProxyAgnosticMojangResolver" perhaps?
|
||||
*
|
||||
* @param resolver The mojang resolver object the method will operate on
|
||||
* @param username The literal username of the player
|
||||
* @param serverHash The computed server hash sent to mojang session servers
|
||||
* @param hostIp The host IP address - not used, kept to maintain similar signature
|
||||
* @return An optional object containing the verification information, if any. If there is no verification information, the session servers consider the join invalid.
|
||||
* @throws IOException When an error occurs during the HTTP communication
|
||||
* @author games647, Enginecrafter77
|
||||
*/
|
||||
public static Optional<Verification> hasJoined(MojangResolver resolver, String username, String serverHash, InetAddress hostIp) throws IOException {
|
||||
/*String url;
|
||||
if (hostIp instanceof Inet6Address) {
|
||||
url = String.format("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s", username, serverHash);
|
||||
} else {
|
||||
String encodedIP = URLEncoder.encode(hostIp.getHostAddress(), StandardCharsets.UTF_8.name());
|
||||
url = String.format("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s", username, serverHash, encodedIP);
|
||||
}*/
|
||||
String url = String.format("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s", username, serverHash);
|
||||
|
||||
try
|
||||
{
|
||||
Class<?> inputStreamActionClass = Class.forName("com.github.games647.craftapi.resolver.AbstractResolver$InputStreamAction");
|
||||
Method getConnectionMethod = AbstractResolver.class.getDeclaredMethod("getConnection", String.class);
|
||||
Method parseRequestMethod = AbstractResolver.class.getDeclaredMethod("parseRequest", HttpURLConnection.class, inputStreamActionClass);
|
||||
Method readJsonMethod = AbstractResolver.class.getDeclaredMethod("readJson", InputStream.class, Class.class);
|
||||
|
||||
getConnectionMethod.setAccessible(true);
|
||||
parseRequestMethod.setAccessible(true);
|
||||
readJsonMethod.setAccessible(true);
|
||||
|
||||
HttpURLConnection conn = (HttpURLConnection)getConnectionMethod.invoke(resolver, url);
|
||||
int responseCode = conn.getResponseCode();
|
||||
|
||||
Verification result = null;
|
||||
if(responseCode != 204)
|
||||
{
|
||||
AbstractResolverAdapter.InputStreamActionAdapter<Verification> action = new AbstractResolverAdapter.InputStreamActionAdapter<Verification>() {
|
||||
@Override
|
||||
public Verification useStream(InputStream inp) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
return (Verification)readJsonMethod.invoke(resolver, new Object[] {inp, Verification.class});
|
||||
}
|
||||
catch(ReflectiveOperationException exc)
|
||||
{
|
||||
throw new IOException("Reflective method access failed", exc);
|
||||
}
|
||||
}
|
||||
};
|
||||
result = (Verification)parseRequestMethod.invoke(resolver, new Object[] {conn, action});
|
||||
}
|
||||
return Optional.ofNullable(result);
|
||||
}
|
||||
catch(ReflectiveOperationException exc)
|
||||
{
|
||||
throw new RuntimeException("Error occured in reflective hacks to MojangResolver", exc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An adapter class used to make the InputStreamAction interface accessible to us.
|
||||
* I know, it's a crude and disgusting solution, but bear with me, it's just temporary.
|
||||
*/
|
||||
public static class AbstractResolverAdapter extends AbstractResolver
|
||||
{
|
||||
@FunctionalInterface
|
||||
protected static interface InputStreamActionAdapter<R> extends AbstractResolver.InputStreamAction<R> {}
|
||||
}
|
||||
|
||||
private void setPremiumUUID(UUID premiumUUID) {
|
||||
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
|
||||
try {
|
||||
|
Reference in New Issue
Block a user