diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index f9696218..c333eed7 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -141,10 +141,7 @@ public class VerifyResponseTask implements Runnable { try { MojangResolver resolver = plugin.getCore().getResolver(); InetAddress address = socketAddress.getAddress(); - - // TODO edit marker - Optional response = VerifyResponseTask.hasJoined(resolver, requestedUsername, serverId, address); - + Optional 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 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 action = new AbstractResolverAdapter.InputStreamActionAdapter() { - @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 extends AbstractResolver.InputStreamAction {} - } - private void setPremiumUUID(UUID premiumUUID) { if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) { try { diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java new file mode 100644 index 00000000..18c75b8c --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java @@ -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 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); + } +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java index c016087b..99fd5b99 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java @@ -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

> { private final Collection 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;