refactor: ProxyAgnosticMojangResolver

ProxyAgnosticMojangResolver serves as a cleaner implementation of the hasJoined static method from previous commit. All the reflection involved was removed due to no longer being necessary. This way, a lot cleaner code could be made. All the inner workings remained the same. For more information on how it works, check out ProxyAgnosticMojangResolver.java.
This commit is contained in:
Enginecrafter77
2022-06-09 19:04:49 +02:00
parent 61b86fba52
commit 5d4483224d
3 changed files with 68 additions and 82 deletions

View File

@ -141,10 +141,7 @@ public class VerifyResponseTask implements Runnable {
try {
MojangResolver resolver = plugin.getCore().getResolver();
InetAddress address = socketAddress.getAddress();
// TODO edit marker
Optional<Verification> response = VerifyResponseTask.hasJoined(resolver, requestedUsername, serverId, address);
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
if (response.isPresent()) {
Verification verification = response.get();
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
@ -176,83 +173,6 @@ public class VerifyResponseTask implements Runnable {
}
}
/**
* 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 {

View File

@ -0,0 +1,64 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.mojang;
import com.github.games647.craftapi.model.auth.Verification;
import com.github.games647.craftapi.resolver.MojangResolver;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.util.Optional;
/**
* An extension to {@link MojangResolver} which allows connection using transparent reverse proxies.
* The significant difference is that unlike MojangResolver from the CraftAPI implementation, which sends the "ip" parameter
* when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation leaves out the "ip" parameter
* also for IPv4, effectively enabling transparent proxies to work.
* @author games647, Enginecrafter77
*/
public class ProxyAgnosticMojangResolver extends MojangResolver {
@Override
public Optional<Verification> hasJoined(String username, String serverHash, InetAddress hostIp) throws IOException
{
String url = String.format("https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s", username, serverHash);
HttpURLConnection conn = this.getConnection(url);
int responseCode = conn.getResponseCode();
Verification verification = null;
if(responseCode != 204)
verification = this.parseRequest(conn, this::parseVerification);
return Optional.ofNullable(verification);
}
// Functional implementation of InputStreamAction
protected Verification parseVerification(InputStream input) throws IOException
{
return this.readJson(input, Verification.class);
}
}

View File

@ -33,6 +33,7 @@ import com.github.games647.fastlogin.core.TickingRateLimiter;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
import com.github.games647.fastlogin.core.mojang.ProxyAgnosticMojangResolver;
import com.github.games647.fastlogin.core.storage.MySQLStorage;
import com.github.games647.fastlogin.core.storage.SQLStorage;
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
@ -83,7 +84,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
private final Collection<UUID> pendingConfirms = new HashSet<>();
private final T plugin;
private final MojangResolver resolver = new MojangResolver();
//private final MojangResolver resolver = new MojangResolver();
private final MojangResolver resolver = new ProxyAgnosticMojangResolver();
private Configuration config;
private SQLStorage storage;