diff --git a/CHANGELOG.md b/CHANGELOG.md index 385eac0d..cecd2d1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 1.10 +* Add support for HTTP proxies * Set the fake offline UUID on lowest priority (-> as soon as possible) * Remove bungee chatcolor for Bukkit to support KCauldron * Minor cleanup using inspections + Https diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java index 252daef3..33e1d2fb 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/EncryptionUtil.java @@ -41,7 +41,7 @@ public class EncryptionUtil { public static byte[] getServerIdHash(String serverId, Key publicKey, Key secretKey) { return digestOperation("SHA-1" - , new byte[][]{serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded()}); + , serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded()); } private static byte[] digestOperation(String algo, byte[]... content) { diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java index 0ddca41d..cfe51ce7 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java @@ -195,7 +195,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin loadYamlFile(Reader reader) { YamlConfiguration config = YamlConfiguration.loadConfiguration(reader); - return config.getValues(false); + return config.getValues(true); } @Override @@ -215,7 +215,8 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin addresses, int requests) { - return new MojangApiBukkit(logger, addresses, requests); + public MojangApiConnector makeApiConnector(Logger logger, List addresses, int requests + , Map proxies) { + return new MojangApiBukkit(logger, addresses, requests, proxies); } } diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java index e424743b..a585b811 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/MojangApiBukkit.java @@ -8,6 +8,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -20,8 +21,8 @@ public class MojangApiBukkit extends MojangApiConnector { //mojang api check to prove a player is logged in minecraft and made a join server request private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?"; - public MojangApiBukkit(Logger logger, List localAddresses, int rateLimit) { - super(logger, localAddresses, rateLimit); + public MojangApiBukkit(Logger logger, List localAddresses, int rateLimit, Map proxies) { + super(logger, localAddresses, rateLimit, proxies); } @Override diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java index 417c961c..271a0d73 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/FastLoginBungee.java @@ -8,17 +8,12 @@ import com.github.games647.fastlogin.core.shared.MojangApiConnector; import com.github.games647.fastlogin.core.shared.PlatformPlugin; import com.google.common.collect.Maps; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.io.Reader; -import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ThreadFactory; import java.util.function.Function; -import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -70,28 +65,6 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin getCore() { return core; } @@ -139,7 +112,8 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin addresses, int requests) { - return new MojangApiBungee(logger, addresses, requests); + public MojangApiConnector makeApiConnector(Logger logger, List addresses, int requests + , Map proxies) { + return new MojangApiBungee(logger, addresses, requests, proxies); } } diff --git a/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiBungee.java b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiBungee.java index 3f413499..3f058f03 100644 --- a/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiBungee.java +++ b/bungee/src/main/java/com/github/games647/fastlogin/bungee/MojangApiBungee.java @@ -4,14 +4,15 @@ import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.MojangApiConnector; import java.util.List; +import java.util.Map; import java.util.logging.Logger; import net.md_5.bungee.BungeeCord; public class MojangApiBungee extends MojangApiConnector { - public MojangApiBungee(Logger logger, List localAddresses, int rateLimit) { - super(logger, localAddresses, rateLimit); + public MojangApiBungee(Logger logger, List localAddresses, int rateLimit, Map proxies) { + super(logger, localAddresses, rateLimit, proxies); } @Override diff --git a/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java index 3ac4666e..db95fbc7 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/AuthStorage.java @@ -28,41 +28,39 @@ public class AuthStorage { , String user, String pass, boolean useSSL) { this.core = core; - HikariConfig databaseConfig = new HikariConfig(); - databaseConfig.setUsername(user); - databaseConfig.setPassword(pass); - databaseConfig.setDriverClassName(driver); + HikariConfig config = new HikariConfig(); + config.setUsername(user); + config.setPassword(pass); + config.setDriverClassName(driver); //a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647 Properties properties = new Properties(); properties.setProperty("date_string_format", "yyyy-MM-dd HH:mm:ss"); properties.setProperty("useSSL", String.valueOf(useSSL)); - databaseConfig.setDataSourceProperties(properties); - - ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder() - .setNameFormat(core.getPlugin().getName() + " Database Pool Thread #%1$d") - //Hikari create daemons by default - .setDaemon(true); + config.setDataSourceProperties(properties); ThreadFactory platformThreadFactory = core.getPlugin().getThreadFactory(); if (platformThreadFactory != null) { - threadFactoryBuilder.setThreadFactory(platformThreadFactory); + config.setThreadFactory(new ThreadFactoryBuilder() + .setNameFormat(core.getPlugin().getName() + " Database Pool Thread #%1$d") + //Hikari create daemons by default + .setDaemon(true) + .setThreadFactory(platformThreadFactory) + .build()); } - databaseConfig.setThreadFactory(threadFactoryBuilder.build()); - databasePath = databasePath.replace("{pluginDir}", core.getPlugin().getDataFolder().getAbsolutePath()); String jdbcUrl = "jdbc:"; if (driver.contains("sqlite")) { jdbcUrl += "sqlite" + "://" + databasePath; - databaseConfig.setConnectionTestQuery("SELECT 1"); + config.setConnectionTestQuery("SELECT 1"); } else { jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath; } - databaseConfig.setJdbcUrl(jdbcUrl); - this.dataSource = new HikariDataSource(databaseConfig); + config.setJdbcUrl(jdbcUrl); + this.dataSource = new HikariDataSource(config); } public DataSource getDataSource() { diff --git a/core/src/main/java/com/github/games647/fastlogin/core/BalancedSSLFactory.java b/core/src/main/java/com/github/games647/fastlogin/core/BalancedSSLFactory.java index b0908ade..1735cc1c 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/BalancedSSLFactory.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/BalancedSSLFactory.java @@ -41,13 +41,13 @@ public class BalancedSSLFactory extends SSLSocketFactory { } @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + public Socket createSocket(String host, int port) throws IOException { return oldFactory.createSocket(host, port, getNextLocalAddress(), 0); } @Override public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) - throws IOException, UnknownHostException { + throws IOException { //default return oldFactory.createSocket(host, port, localAddress, localPort); } 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 9ebd5ecb..b6247d2c 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 @@ -31,6 +31,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.logging.Level; +import java.util.stream.Collectors; /** * @param

Player class @@ -126,7 +127,12 @@ public class FastLoginCore

> { List ipAddresses = sharedConfig.get("ip-addresses"); int requestLimit = sharedConfig.get("mojang-request-limit"); - this.apiConnector = plugin.makeApiConnector(plugin.getLogger(), ipAddresses, requestLimit); + List proxyList = sharedConfig.get("proxies"); + Map proxies = proxyList.stream() + .collect(Collectors + .toMap(line -> line.split(":")[0], line -> Integer.parseInt(line.split(":")[1]))); + + this.apiConnector = plugin.makeApiConnector(plugin.getLogger(), ipAddresses, requestLimit, proxies); } public MojangApiConnector getApiConnector() { @@ -189,7 +195,7 @@ public class FastLoginCore

> { try { if (sqlite && importPlugin == ImportPlugin.AUTO_IN) { - //load sqlite driver + //load SQLite driver Class.forName("org.sqlite.JDBC"); String jdbcUrl = "jdbc:sqlite:" + AutoInImporter.getSQLitePath(); diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/MojangApiConnector.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/MojangApiConnector.java index 6aa36035..6ce15eb1 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/MojangApiConnector.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/MojangApiConnector.java @@ -1,6 +1,8 @@ package com.github.games647.fastlogin.core.shared; import com.github.games647.fastlogin.core.BalancedSSLFactory; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.BufferedReader; @@ -8,9 +10,16 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Proxy.Type; import java.net.URL; import java.net.UnknownHostException; import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentMap; @@ -33,9 +42,10 @@ public abstract class MojangApiConnector { private static final int RATE_LIMIT_CODE = 429; - //compile the pattern only on plugin enable -> and this have to be threadsafe - private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME); + //compile the pattern only on plugin enable -> and this have to be thread-safe + private final Pattern nameMatcher = Pattern.compile(VALID_PLAYERNAME); + private final Iterator proxies; private final ConcurrentMap requests = FastLoginCore.buildCache(10, -1); private final BalancedSSLFactory sslFactory; private final int rateLimit; @@ -43,7 +53,8 @@ public abstract class MojangApiConnector { protected final Logger logger; - public MojangApiConnector(Logger logger, Collection localAddresses, int rateLimit) { + public MojangApiConnector(Logger logger, Collection localAddresses, int rateLimit + , Map proxies) { this.logger = logger; if (rateLimit > 600) { @@ -72,26 +83,37 @@ public abstract class MojangApiConnector { this.sslFactory = new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses); } + + List proxyBuilder = Lists.newArrayList(); + for (Entry proxy : proxies.entrySet()) { + proxyBuilder.add(new Proxy(Type.HTTP, new InetSocketAddress(proxy.getKey(), proxy.getValue()))); + } + + this.proxies = Iterables.cycle(proxyBuilder).iterator(); } + /** - * - * @param playerName * @return null on non-premium */ public UUID getPremiumUUID(String playerName) { - //check if it's a valid playername - if (playernameMatcher.matcher(playerName).matches()) { -// only make a API call if the name is valid existing mojang account - - if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) { - return null; - } - - requests.put(new Object(), new Object()); - + //check if it's a valid player name + if (nameMatcher.matcher(playerName).matches()) { try { - HttpsURLConnection connection = getConnection(UUID_LINK + playerName); + HttpsURLConnection connection; + if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) { + synchronized (proxies) { + if (proxies.hasNext()) { + connection = getConnection(UUID_LINK + playerName, proxies.next()); + } else { + return null; + } + } + } else { + requests.put(new Object(), new Object()); + connection = getConnection(UUID_LINK + playerName); + } + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line = reader.readLine(); @@ -101,13 +123,14 @@ public abstract class MojangApiConnector { } else if (connection.getResponseCode() == RATE_LIMIT_CODE) { logger.info("RATE_LIMIT REACHED"); lastRateLimit = System.currentTimeMillis(); - return null; + if (!connection.usingProxy()) { + return getPremiumUUID(playerName); + } } //204 - no content for not found } catch (Exception ex) { logger.log(Level.SEVERE, "Failed to check if player has a paid account", ex); } - //this connection doesn't need to be closed. So can make use of keep alive in java } return null; @@ -117,18 +140,23 @@ public abstract class MojangApiConnector { protected abstract String getUUIDFromJson(String json); - protected HttpsURLConnection getConnection(String url) throws IOException { - HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + protected HttpsURLConnection getConnection(String url, Proxy proxy) throws IOException { + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(proxy); connection.setConnectTimeout(TIMEOUT); connection.setReadTimeout(2 * TIMEOUT); //the new Mojang API just uses json as response connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("User-Agent", USER_AGENT); + //this connection doesn't need to be closed. So can make use of keep alive in java if (sslFactory != null) { connection.setSSLSocketFactory(sslFactory); } return connection; } + + protected HttpsURLConnection getConnection(String url) throws IOException { + return getConnection(url, Proxy.NO_PROXY); + } } diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java index 2d21bb6f..45c84a5c 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/PlatformPlugin.java @@ -23,5 +23,6 @@ public interface PlatformPlugin { String translateColorCodes(char colorChar, String rawMessage); - MojangApiConnector makeApiConnector(Logger logger, List addresses, int requests); + MojangApiConnector makeApiConnector(Logger logger, List addresses, int requests + , Map proxies); } diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 121185f0..5133dc7a 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -176,3 +176,12 @@ database: '{pluginDir}/FastLogin.db' # It's strongly recommended to enable SSL and setup a SSL certificate if the MySQL server isn't running on the same # machine #useSSL: false + +# HTTP proxies for connecting to the Mojang servers in order to check if the username of a player is premium. +# This is a workaround to prevent rate-limiting by Mojang. These proxies will only be used once your server hit +# the rate-limit or the custom value above. +# Please make sure you use reliable proxies. +proxies: + # 'IP:Port' or 'Domain:Port' + # - 'xyz.com:1337' + # - 'test.com:5131'