diff --git a/LaunchServer/runtime/plugin.js b/LaunchServer/runtime/plugin.js index 870d4d9..63b952d 100644 --- a/LaunchServer/runtime/plugin.js +++ b/LaunchServer/runtime/plugin.js @@ -7,7 +7,7 @@ server.commandHandler.registerCommand("test", new (Java.extend(Command, { getArgsDescription: function() { return "[anything]"; }, getUsageDescription: function() { return "plugin.js test command"; }, - + invoke: function(args) { LogHelper.info("[plugin.js] Command invoked! Args: " + java.util.Arrays.toString(args)); @@ -25,7 +25,7 @@ /* You can test custom request like this: var TestCustomRequest = Java.extend(CustomRequest, { getName: function() { return "test"; }, - + requestDoCustom: function(input, output) { return input.readInt(); } diff --git a/LaunchServer/source/LaunchServer.java b/LaunchServer/source/LaunchServer.java index 63de297..8ee9f90 100644 --- a/LaunchServer/source/LaunchServer.java +++ b/LaunchServer/source/LaunchServer.java @@ -31,7 +31,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.zip.CRC32; import launcher.Launcher; @@ -78,9 +77,14 @@ @LauncherAPI public static final Path UPDATES_DIR = IOHelper.WORKING_DIR.resolve("updates"); @LauncherAPI public static final Path PROFILES_DIR = IOHelper.WORKING_DIR.resolve("profiles"); + // Server config + @LauncherAPI public final Config config; + @LauncherAPI public final RSAPublicKey publicKey; + @LauncherAPI public final RSAPrivateKey privateKey; + // Launcher binary @LauncherAPI public final LauncherBinary launcherBinary = new JARLauncherBinary(this); - private volatile LauncherBinary launcherEXEBinary; + @LauncherAPI public final LauncherBinary launcherEXEBinary; // Server @LauncherAPI public final CommandHandler commandHandler; @@ -88,12 +92,6 @@ private final AtomicBoolean started = new AtomicBoolean(false); private final ScriptEngine engine = CommonHelper.newScriptEngine(); - // Launcher config - private Config config; - private final ReentrantReadWriteLock configLock = new ReentrantReadWriteLock(); - private volatile RSAPublicKey publicKey; - private volatile RSAPrivateKey privateKey; - // Updates and profiles private volatile List> profilesList; private volatile Map> updatesDirMap; @@ -115,9 +113,42 @@ } commandHandler = localCommandHandler; - // Setup - reloadKeyPair(); - reloadConfig(); + // Set key pair + if (IOHelper.isFile(PUBLIC_KEY_FILE) && IOHelper.isFile(PRIVATE_KEY_FILE)) { + LogHelper.info("Reading RSA keypair"); + publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(PUBLIC_KEY_FILE)); + privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(PRIVATE_KEY_FILE)); + if (!publicKey.getModulus().equals(privateKey.getModulus())) { + throw new IOException("Private and public key modulus mismatch"); + } + } else { + LogHelper.info("Generating RSA keypair"); + KeyPair pair = SecurityHelper.genRSAKeyPair(); + publicKey = (RSAPublicKey) pair.getPublic(); + privateKey = (RSAPrivateKey) pair.getPrivate(); + + // Write key pair files + LogHelper.info("Writing RSA keypair files"); + IOHelper.write(PUBLIC_KEY_FILE, publicKey.getEncoded()); + IOHelper.write(PRIVATE_KEY_FILE, privateKey.getEncoded()); + } + + // Print keypair fingerprints + CRC32 crc = new CRC32(); + crc.update(publicKey.getModulus().toByteArray()); + LogHelper.subInfo("Modulus CRC32: 0x%08x", crc.getValue()); + + // Read LaunchServer config + generateConfigIfNotExists(); + LogHelper.info("Reading LaunchServer config file"); + try (BufferedReader reader = IOHelper.newReader(CONFIG_FILE)) { + config = new Config(TextConfigReader.read(reader, true)); + } + config.verify(); + + // Set launcher EXE binary + launcherEXEBinary = config.launch4J ? + new EXEL4JLauncherBinary(this) : new EXELauncherBinary(this); syncLauncherBinaries(); // Sync updates dir @@ -162,32 +193,7 @@ @LauncherAPI public void buildLauncherBinaries() throws IOException { launcherBinary.build(); - getEXEBinary().build(); - } - - @LauncherAPI - public Config getConfig() { - configLock.readLock().lock(); - try { - return config; - } finally { - configLock.readLock().unlock(); - } - } - - @LauncherAPI - public LauncherBinary getEXEBinary() { - configLock.readLock().lock(); - try { - return launcherEXEBinary; - } finally { - configLock.readLock().unlock(); - } - } - - @LauncherAPI - public RSAPrivateKey getPrivateKey() { - return privateKey; + launcherEXEBinary.build(); } @LauncherAPI @@ -197,11 +203,6 @@ } @LauncherAPI - public RSAPublicKey getPublicKey() { - return publicKey; - } - - @LauncherAPI public SignedObjectHolder getUpdateDir(String name) { return updatesDirMap.get(name); } @@ -226,104 +227,6 @@ } @LauncherAPI - public void reloadConfig() throws IOException { - configLock.writeLock().lock(); - try { - Config oldConfig = config; - - // Create LaunchServer config if not exist - Config newConfig; - if (!IOHelper.isFile(CONFIG_FILE)) { - LogHelper.info("Creating LaunchServer config"); - try (BufferedReader reader = IOHelper.newReader(IOHelper.getResourceURL("launchserver/defaults/config.cfg"))) { - newConfig = new Config(TextConfigReader.read(reader, false)); - } - - // Set server address - LogHelper.println("LaunchServer address: "); - newConfig.setAddress(commandHandler.readLine()); - - // Write LaunchServer config - LogHelper.info("Writing LaunchServer config file"); - try (BufferedWriter writer = IOHelper.newWriter(CONFIG_FILE)) { - TextConfigWriter.write(newConfig.block, writer, true); - } - } - - // Flush old auth handler and provider - if (oldConfig != null) { - // Flush auth handler - try { - config.authHandler.flush(); - } catch (IOException e) { - LogHelper.error(e); - } - - // Flush auth provider - try { - config.authProvider.flush(); - } catch (IOException e) { - LogHelper.error(e); - } - } - - // Read LaunchServer config (also re-read after setup for RO) - LogHelper.info("Reading LaunchServer config file"); - try (BufferedReader reader = IOHelper.newReader(CONFIG_FILE)) { - newConfig = new Config(TextConfigReader.read(reader, true)); - if (oldConfig != null && !oldConfig.getBindAddress().equals(newConfig.getBindAddress())) { - LogHelper.warning("To bind new address, use 'rebind' command"); - } - } - newConfig.verify(); - - // Create new launcher EXE binary - LauncherBinary newExeBinary = newConfig.launch4J ? - new EXEL4JLauncherBinary(this) : new EXELauncherBinary(this); - newExeBinary.sync(); - - // Apply changes - config = newConfig; - launcherEXEBinary = newExeBinary; - } finally { - configLock.writeLock().unlock(); - } - } - - @LauncherAPI - public void reloadKeyPair() throws IOException, InvalidKeySpecException { - RSAPublicKey newPublicKey; - RSAPrivateKey newPrivateKey; - if (IOHelper.isFile(PUBLIC_KEY_FILE) && IOHelper.isFile(PRIVATE_KEY_FILE)) { - LogHelper.info("Reading RSA keypair"); - newPublicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(PUBLIC_KEY_FILE)); - newPrivateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(PRIVATE_KEY_FILE)); - if (!newPublicKey.getModulus().equals(newPrivateKey.getModulus())) { - throw new IOException("Private and public key modulus mismatch"); - } - - // Print keypair fingerprints - CRC32 crc = new CRC32(); - crc.update(newPublicKey.getModulus().toByteArray()); - LogHelper.subInfo("Modulus CRC32: 0x%08x", crc.getValue()); - } else { - LogHelper.info("Generating RSA keypair"); - KeyPair pair = SecurityHelper.genRSAKeyPair(); - newPublicKey = (RSAPublicKey) pair.getPublic(); - newPrivateKey = (RSAPrivateKey) pair.getPrivate(); - - // Write key pair files - LogHelper.info("Writing RSA keypair files"); - IOHelper.write(PUBLIC_KEY_FILE, newPublicKey.getEncoded()); - IOHelper.write(PRIVATE_KEY_FILE, newPrivateKey.getEncoded()); - } - - // Apply changes - publicKey = newPublicKey; - privateKey = newPrivateKey; - } - - @LauncherAPI public void syncLauncherBinaries() throws IOException { LogHelper.info("Syncing launcher binaries"); @@ -335,7 +238,7 @@ // Syncing launcher EXE binary LogHelper.subInfo("Syncing launcher EXE binary file"); - if (!getEXEBinary().sync()) { + if (!launcherEXEBinary.sync()) { LogHelper.subWarning("Missing launcher EXE binary file"); } } @@ -386,6 +289,29 @@ updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap); } + private void generateConfigIfNotExists() throws IOException { + if (IOHelper.isFile(CONFIG_FILE)) { + return; + } + + // Create new config + Config newConfig; + LogHelper.info("Creating LaunchServer config"); + try (BufferedReader reader = IOHelper.newReader(IOHelper.getResourceURL("launchserver/defaults/config.cfg"))) { + newConfig = new Config(TextConfigReader.read(reader, false)); + } + + // Set server address + LogHelper.println("LaunchServer address: "); + newConfig.setAddress(commandHandler.readLine()); + + // Write LaunchServer config + LogHelper.info("Writing LaunchServer config file"); + try (BufferedWriter writer = IOHelper.newWriter(CONFIG_FILE)) { + TextConfigWriter.write(newConfig.block, writer, true); + } + } + private void setScriptBindings() { LogHelper.info("Setting up server script engine bindings"); Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); @@ -399,24 +325,16 @@ private void shutdownHook() { serverSocketHandler.close(); - // Flush auth handler - configLock.readLock().lock(); + // Flush auth handler and provider try { config.authHandler.flush(); } catch (IOException e) { LogHelper.error(e); - } finally { - configLock.readLock().unlock(); } - - // Flush auth provider - configLock.readLock().lock(); try { config.authProvider.flush(); } catch (IOException e) { LogHelper.error(e); - } finally { - configLock.readLock().unlock(); } // Print last message before death :( diff --git a/LaunchServer/source/binary/JARLauncherBinary.java b/LaunchServer/source/binary/JARLauncherBinary.java index a32d561..f10fd14 100644 --- a/LaunchServer/source/binary/JARLauncherBinary.java +++ b/LaunchServer/source/binary/JARLauncherBinary.java @@ -60,8 +60,8 @@ byte[] launcherConfigBytes; try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) { try (HOutput configOutput = new HOutput(configArray)) { - LaunchServer.Config config = server.getConfig(); - new Launcher.Config(config.getAddress(), config.port, server.getPublicKey(), runtime).write(configOutput); + new Launcher.Config(server.config.getAddress(), server.config.port, + server.publicKey, runtime).write(configOutput); } launcherConfigBytes = configArray.toByteArray(); } diff --git a/LaunchServer/source/binary/LauncherBinary.java b/LaunchServer/source/binary/LauncherBinary.java index 0fa4555..0db3b47 100644 --- a/LaunchServer/source/binary/LauncherBinary.java +++ b/LaunchServer/source/binary/LauncherBinary.java @@ -35,7 +35,7 @@ @LauncherAPI public final boolean sync() throws IOException { boolean exists = exists(); - binary = exists ? new SignedBytesHolder(IOHelper.read(binaryFile), server.getPrivateKey()) : null; + binary = exists ? new SignedBytesHolder(IOHelper.read(binaryFile), server.privateKey) : null; return exists; } } diff --git a/LaunchServer/source/command/auth/AuthCommand.java b/LaunchServer/source/command/auth/AuthCommand.java index 730615e..f4d3ef8 100644 --- a/LaunchServer/source/command/auth/AuthCommand.java +++ b/LaunchServer/source/command/auth/AuthCommand.java @@ -30,11 +30,11 @@ String password = args[1]; // Authenticate - String username = server.getConfig().authProvider.auth(login, password); + String username = server.config.authProvider.auth(login, password); // Authenticate on server (and get UUID) String accessToken = SecurityHelper.randomStringToken(); - UUID uuid = server.getConfig().authHandler.auth(username, accessToken); + UUID uuid = server.config.authHandler.auth(username, accessToken); if (uuid == null) { throw new CommandException("Can't assing UUID (Command)"); } diff --git a/LaunchServer/source/command/auth/UUIDToUsernameCommand.java b/LaunchServer/source/command/auth/UUIDToUsernameCommand.java index a87b2cd..c8d8482 100644 --- a/LaunchServer/source/command/auth/UUIDToUsernameCommand.java +++ b/LaunchServer/source/command/auth/UUIDToUsernameCommand.java @@ -29,7 +29,7 @@ UUID uuid = parseUUID(args[0]); // Get UUID by username - String username = server.getConfig().authHandler.uuidToUsername(uuid); + String username = server.config.authHandler.uuidToUsername(uuid); if (username == null) { throw new CommandException("Unknown UUID: " + uuid); } diff --git a/LaunchServer/source/command/auth/UsernameToUUIDCommand.java b/LaunchServer/source/command/auth/UsernameToUUIDCommand.java index 3c887a1..b441e33 100644 --- a/LaunchServer/source/command/auth/UsernameToUUIDCommand.java +++ b/LaunchServer/source/command/auth/UsernameToUUIDCommand.java @@ -29,7 +29,7 @@ String username = parseUsername(args[0]); // Get UUID by username - UUID uuid = server.getConfig().authHandler.usernameToUUID(username); + UUID uuid = server.config.authHandler.usernameToUUID(username); if (uuid == null) { throw new CommandException(String.format("Unknown username: '%s'", username)); } diff --git a/LaunchServer/source/command/basic/ReloadConfigCommand.java b/LaunchServer/source/command/basic/ReloadConfigCommand.java deleted file mode 100644 index db47ed1..0000000 --- a/LaunchServer/source/command/basic/ReloadConfigCommand.java +++ /dev/null @@ -1,25 +0,0 @@ -package launchserver.command.basic; - -import launchserver.LaunchServer; -import launchserver.command.Command; - -public final class ReloadConfigCommand extends Command { - public ReloadConfigCommand(LaunchServer server) { - super(server); - } - - @Override - public String getArgsDescription() { - return null; - } - - @Override - public String getUsageDescription() { - return "Reload LaunchServer.cfg file"; - } - - @Override - public void invoke(String... args) throws Exception { - server.reloadConfig(); - } -} \ No newline at end of file diff --git a/LaunchServer/source/command/basic/ReloadKeyPairCommand.java b/LaunchServer/source/command/basic/ReloadKeyPairCommand.java deleted file mode 100644 index 62c3b7b..0000000 --- a/LaunchServer/source/command/basic/ReloadKeyPairCommand.java +++ /dev/null @@ -1,27 +0,0 @@ -package launchserver.command.basic; - -import launcher.helper.LogHelper; -import launchserver.LaunchServer; -import launchserver.command.Command; - -public final class ReloadKeyPairCommand extends Command { - public ReloadKeyPairCommand(LaunchServer server) { - super(server); - } - - @Override - public String getArgsDescription() { - return null; - } - - @Override - public String getUsageDescription() { - return "Reload public.key and private.key files"; - } - - @Override - public void invoke(String... args) throws Exception { - server.reloadKeyPair(); - LogHelper.subInfo("Key pair reloaded"); - } -} \ No newline at end of file diff --git a/LaunchServer/source/command/handler/CommandHandler.java b/LaunchServer/source/command/handler/CommandHandler.java index 46dfc6a..105d856 100644 --- a/LaunchServer/source/command/handler/CommandHandler.java +++ b/LaunchServer/source/command/handler/CommandHandler.java @@ -28,8 +28,6 @@ import launchserver.command.basic.GCCommand; import launchserver.command.basic.HelpCommand; import launchserver.command.basic.RebindCommand; -import launchserver.command.basic.ReloadConfigCommand; -import launchserver.command.basic.ReloadKeyPairCommand; import launchserver.command.basic.StopCommand; import launchserver.command.basic.VersionCommand; import launchserver.command.hash.DownloadAssetCommand; @@ -50,8 +48,6 @@ registerCommand("build", new BuildCommand(server)); registerCommand("stop", new StopCommand(server)); registerCommand("rebind", new RebindCommand(server)); - registerCommand("reloadConfig", new ReloadConfigCommand(server)); - registerCommand("reloadKeyPair", new ReloadKeyPairCommand(server)); registerCommand("eval", new EvalCommand(server)); registerCommand("debug", new DebugCommand(server)); registerCommand("clear", new ClearCommand(server)); diff --git a/LaunchServer/source/response/ResponseThread.java b/LaunchServer/source/response/ResponseThread.java index e556b75..877271a 100644 --- a/LaunchServer/source/response/ResponseThread.java +++ b/LaunchServer/source/response/ResponseThread.java @@ -86,7 +86,7 @@ // Verify key modulus BigInteger keyModulus = input.readBigInteger(SecurityHelper.RSA_KEY_LENGTH + 1); - if (!keyModulus.equals(server.getPrivateKey().getModulus())) { + if (!keyModulus.equals(server.privateKey.getModulus())) { output.writeBoolean(false); throw new IOException(String.format("#%d Key modulus mismatch", id)); } diff --git a/LaunchServer/source/response/ServerSocketHandler.java b/LaunchServer/source/response/ServerSocketHandler.java index 64cf32c..505365a 100644 --- a/LaunchServer/source/response/ServerSocketHandler.java +++ b/LaunchServer/source/response/ServerSocketHandler.java @@ -65,7 +65,7 @@ serverSocket.setReuseAddress(true); serverSocket.setPerformancePreferences(2, 1, 0); serverSocket.setReceiveBufferSize(IOHelper.BUFFER_SIZE); - serverSocket.bind(server.getConfig().getSocketAddress()); + serverSocket.bind(server.config.getSocketAddress()); LogHelper.info("Server socket thread successfully started"); // Listen for incoming connections diff --git a/LaunchServer/source/response/auth/AuthResponse.java b/LaunchServer/source/response/auth/AuthResponse.java index 0ac2c02..9ff7309 100644 --- a/LaunchServer/source/response/auth/AuthResponse.java +++ b/LaunchServer/source/response/auth/AuthResponse.java @@ -30,17 +30,16 @@ // Decrypt password String password; try { - password = IOHelper.decode(SecurityHelper.newRSADecryptCipher(server.getPrivateKey()).doFinal(encryptedPassword)); + password = IOHelper.decode(SecurityHelper.newRSADecryptCipher(server.privateKey).doFinal(encryptedPassword)); } catch (IllegalBlockSizeException | BadPaddingException ignored) { throw new RequestException("Password decryption error"); } // Authenticate debug("Login: '%s', Password: '%s'", id, login, echo(password.length())); - LaunchServer.Config config = server.getConfig(); String username; try { - username = VerifyHelper.verifyUsername(config.authProvider.auth(login, password)); + username = VerifyHelper.verifyUsername(server.config.authProvider.auth(login, password)); } catch (AuthException e) { throw new RequestException(e); } catch (Exception e) { @@ -51,7 +50,7 @@ // Authenticate on server (and get UUID) String accessToken = SecurityHelper.randomStringToken(); - UUID uuid = config.authHandler.auth(username, accessToken); + UUID uuid = server.config.authHandler.auth(username, accessToken); if (uuid == null) { throw new RequestException("Can't assign UUID"); } diff --git a/LaunchServer/source/response/auth/CheckServerResponse.java b/LaunchServer/source/response/auth/CheckServerResponse.java index 7931d4f..0512bd8 100644 --- a/LaunchServer/source/response/auth/CheckServerResponse.java +++ b/LaunchServer/source/response/auth/CheckServerResponse.java @@ -23,7 +23,7 @@ debug("Username: %s, Server ID: %s", id, username, serverID); // Check server - UUID uuid = server.getConfig().authHandler.checkServer(username, serverID); + UUID uuid = server.config.authHandler.checkServer(username, serverID); if (uuid == null) { output.writeBoolean(false); return; diff --git a/LaunchServer/source/response/auth/JoinServerResponse.java b/LaunchServer/source/response/auth/JoinServerResponse.java index 2cd14c6..21f7523 100644 --- a/LaunchServer/source/response/auth/JoinServerResponse.java +++ b/LaunchServer/source/response/auth/JoinServerResponse.java @@ -23,6 +23,6 @@ // Try join server with auth manager debug("Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID); - output.writeBoolean(server.getConfig().authHandler.joinServer(username, accessToken, serverID)); + output.writeBoolean(server.config.authHandler.joinServer(username, accessToken, serverID)); } } diff --git a/LaunchServer/source/response/profile/ProfileByUUIDResponse.java b/LaunchServer/source/response/profile/ProfileByUUIDResponse.java index d90583f..8a58a98 100644 --- a/LaunchServer/source/response/profile/ProfileByUUIDResponse.java +++ b/LaunchServer/source/response/profile/ProfileByUUIDResponse.java @@ -20,7 +20,7 @@ debug("UUID: " + uuid); // Verify has such profile - String username = server.getConfig().authHandler.uuidToUsername(uuid); + String username = server.config.authHandler.uuidToUsername(uuid); if (username == null) { output.writeBoolean(false); return; @@ -32,9 +32,8 @@ } public static PlayerProfile getProfile(LaunchServer server, UUID uuid, String username) { - LaunchServer.Config config = server.getConfig(); - String skinURL = config.getSkinURL(username, uuid); - String cloakURL = config.getCloakURL(username, uuid); + String skinURL = server.config.getSkinURL(username, uuid); + String cloakURL = server.config.getCloakURL(username, uuid); return new PlayerProfile(uuid, username, skinURL, cloakURL); } } diff --git a/LaunchServer/source/response/profile/ProfileByUsernameResponse.java b/LaunchServer/source/response/profile/ProfileByUsernameResponse.java index 2b6cabd..8fef87b 100644 --- a/LaunchServer/source/response/profile/ProfileByUsernameResponse.java +++ b/LaunchServer/source/response/profile/ProfileByUsernameResponse.java @@ -24,7 +24,7 @@ } public static void writeProfile(LaunchServer server, HOutput output, String username) throws IOException { - UUID uuid = server.getConfig().authHandler.usernameToUUID(username); + UUID uuid = server.config.authHandler.usernameToUUID(username); if (uuid == null) { output.writeBoolean(false); return; diff --git a/LaunchServer/source/response/update/LauncherResponse.java b/LaunchServer/source/response/update/LauncherResponse.java index f1a8e4c..9ce3db8 100644 --- a/LaunchServer/source/response/update/LauncherResponse.java +++ b/LaunchServer/source/response/update/LauncherResponse.java @@ -21,7 +21,8 @@ @Override public void reply() throws IOException { // Resolve launcher binary - SignedBytesHolder bytes = (input.readBoolean() ? server.getEXEBinary() : server.launcherBinary).getBytes(); + SignedBytesHolder bytes = (input.readBoolean() ? + server.launcherEXEBinary : server.launcherBinary).getBytes(); if (bytes == null) { throw new RequestException("Missing launcher binary"); }