Merge branch 'release/5.1.4'

This commit is contained in:
Gravit 2020-04-27 04:20:32 +07:00
commit aed80e995a
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
185 changed files with 4209 additions and 4301 deletions

View file

@ -9,74 +9,74 @@ jobs:
name: Launcher name: Launcher
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
- name: Cache Gradle - name: Cache Gradle
uses: actions/cache@v1 uses: actions/cache@v1
with: with:
path: ~/.gradle/caches path: ~/.gradle/caches
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
- name: Set up JDK 11 - name: Set up JDK 11
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 11
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build
- name: Create artifacts - name: Create artifacts
run: | run: |
mkdir -p artifacts/modules mkdir -p artifacts/modules
cd LaunchServer/build/libs/ cd LaunchServer/build/libs/
zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar" zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar"
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
cd ../../../ServerWrapper/build/libs cd ../../../ServerWrapper/build/libs
cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar cp ServerWrapper.jar ../../../artifacts/ServerWrapper.jar
cd ../../../LauncherAuthlib/build/libs cd ../../../LauncherAuthlib/build/libs
cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar cp LauncherAuthlib.jar ../../../artifacts/LauncherAuthlib.jar
cd ../../../ cd ../../../
cp modules/*_module/build/libs/*.jar artifacts/modules cp modules/*_module/build/libs/*.jar artifacts/modules
cp modules/*_swmodule/build/libs/*.jar artifacts/modules cp modules/*_swmodule/build/libs/*.jar artifacts/modules
cp modules/*_lmodule/build/libs/*.jar artifacts/modules cp modules/*_lmodule/build/libs/*.jar artifacts/modules
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: Launcher name: Launcher
path: artifacts path: artifacts
- name: Create release - name: Create release
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1
if: github.event_name == 'create' if: github.event_name == 'create'
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
release_name: GravitLauncher ${{ github.ref }} release_name: GravitLauncher ${{ github.ref }}
draft: false draft: false
prerelease: false prerelease: false
- name: Pack release - name: Pack release
if: github.event_name == 'create' if: github.event_name == 'create'
run: | run: |
cd artifacts/ cd artifacts/
zip -r -9 ../Release.zip * zip -r -9 ../Release.zip *
- name: Upload release - name: Upload release
if: github.event_name == 'create' if: github.event_name == 'create'
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./Release.zip asset_path: ./Release.zip
asset_name: Release.zip asset_name: Release.zip
asset_content_type: application/zip asset_content_type: application/zip

2
.gitignore vendored
View file

@ -107,6 +107,6 @@ buildnumber
*.directory *.directory
cmd.bat cmd.bat
cmd.sh cmd.sh
project/target
## PVS Studio ## PVS Studio
.PVS-Studio/ .PVS-Studio/
project/target

View file

@ -70,7 +70,7 @@ task cleanjar(type: Jar, dependsOn: jar) {
dependencies { dependencies {
pack project(':LauncherAPI') pack project(':LauncherAPI')
bundle group: 'org.fusesource.jansi', name:'jansi', version: rootProject['verJansi'] bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline'] bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline']
@ -117,16 +117,18 @@ task hikari(type: Copy) {
task launch4j(type: Copy) { task launch4j(type: Copy) {
duplicatesStrategy = 'EXCLUDE' duplicatesStrategy = 'EXCLUDE'
into "$buildDir/libs/libraries/launch4j" into "$buildDir/libs/libraries/launch4j"
from(configurations.launch4j.collect { it.isDirectory() ? it : ((it.getName().startsWith("launch4j") && it.getName().contains("workdir")) ? zipTree(it) : it) }) from(configurations.launch4j.collect {
it.isDirectory() ? it : ((it.getName().startsWith("launch4j") && it.getName().contains("workdir")) ? zipTree(it) : it)
})
includeEmptyDirs false includeEmptyDirs false
eachFile { FileCopyDetails fcp -> eachFile { FileCopyDetails fcp ->
if (fcp.relativePath.pathString.startsWith("launch4j-") && if (fcp.relativePath.pathString.startsWith("launch4j-") &&
fcp.relativePath.pathString.contains("workdir")) { fcp.relativePath.pathString.contains("workdir")) {
def segments = fcp.relativePath.segments def segments = fcp.relativePath.segments
def pathSegments = segments[1..-1] as String[] def pathSegments = segments[1..-1] as String[]
fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathSegments) fcp.relativePath = new RelativePath(!fcp.file.isDirectory(), pathSegments)
} else if (fcp.relativePath.pathString.contains("META-INF")) fcp.exclude() } else if (fcp.relativePath.pathString.contains("META-INF")) fcp.exclude()
fcp.mode = 0755 fcp.mode = 0755
} }
} }

View file

@ -13,6 +13,7 @@
import pro.gravit.launchserver.binary.*; import pro.gravit.launchserver.binary.*;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.CertificateManager;
import pro.gravit.launchserver.manangers.MirrorManager; import pro.gravit.launchserver.manangers.MirrorManager;
import pro.gravit.launchserver.manangers.ReconfigurableManager; import pro.gravit.launchserver.manangers.ReconfigurableManager;
@ -51,215 +52,49 @@
public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable { public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable {
public enum ReloadType { public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
NO_AUTH,
NO_COMPONENTS,
FULL
}
public enum LaunchServerEnv {
TEST,
DEV,
DEBUG,
PRODUCTION
}
public interface LaunchServerConfigManager {
LaunchServerConfig readConfig() throws IOException;
LaunchServerRuntimeConfig readRuntimeConfig() throws IOException;
void writeConfig(LaunchServerConfig config) throws IOException;
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
}
public void reload(ReloadType type) throws Exception {
config.close(type);
Map<String, AuthProviderPair> pairs = null;
if (type.equals(ReloadType.NO_AUTH)) {
pairs = config.auth;
}
LogHelper.info("Reading LaunchServer config file");
config = launchServerConfigManager.readConfig();
config.setLaunchServer(this);
if (type.equals(ReloadType.NO_AUTH)) {
config.auth = pairs;
}
config.verify();
config.init(type);
if (type.equals(ReloadType.FULL) && config.components != null) {
LogHelper.debug("PreInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PreInit component %s", k);
v.preInit(this);
});
LogHelper.debug("PreInit components successful");
LogHelper.debug("Init components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("Init component %s", k);
v.init(this);
});
LogHelper.debug("Init components successful");
LogHelper.debug("PostInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PostInit component %s", k);
v.postInit(this);
});
LogHelper.debug("PostInit components successful");
}
}
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
SubCommand reload = new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
if (args.length == 0) {
reload(ReloadType.FULL);
return;
}
switch (args[0]) {
case "full":
reload(ReloadType.FULL);
break;
case "no_auth":
reload(ReloadType.NO_AUTH);
break;
case "no_components":
reload(ReloadType.NO_COMPONENTS);
break;
default:
reload(ReloadType.FULL);
break;
}
}
};
commands.put("reload", reload);
return commands;
}
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<ClientProfile> result;
private ProfilesFileVisitor(Collection<ClientProfile> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
}
profile.verify();
// Add SIGNED profile to result list
result.add(profile);
return super.visitFile(file, attrs);
}
}
// Constant paths
public final Path dir; public final Path dir;
public final LaunchServerEnv env; public final LaunchServerEnv env;
public final Path launcherLibraries; public final Path launcherLibraries;
public final Path launcherLibrariesCompile; public final Path launcherLibrariesCompile;
public final Path caCertFile; public final Path caCertFile;
// Constant paths
public final Path caKeyFile; public final Path caKeyFile;
public final Path serverCertFile; public final Path serverCertFile;
public final Path serverKeyFile; public final Path serverKeyFile;
public final Path updatesDir; public final Path updatesDir;
public final LaunchServerConfigManager launchServerConfigManager; public final LaunchServerConfigManager launchServerConfigManager;
//public static LaunchServer server = null;
public final Path profilesDir; public final Path profilesDir;
// Server config
public LaunchServerConfig config;
public final LaunchServerRuntimeConfig runtime; public final LaunchServerRuntimeConfig runtime;
public final ECPublicKey publicKey; public final ECPublicKey publicKey;
public final ECPrivateKey privateKey; public final ECPrivateKey privateKey;
// Launcher binary
public final JARLauncherBinary launcherBinary; public final JARLauncherBinary launcherBinary;
//public static LaunchServer server = null;
public final Class<? extends LauncherBinary> launcherEXEBinaryClass; public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
// Server config
public final LauncherBinary launcherEXEBinary; public final LauncherBinary launcherEXEBinary;
// HWID ban + anti-brutforce
public final SessionManager sessionManager; public final SessionManager sessionManager;
public final AuthHookManager authHookManager; public final AuthHookManager authHookManager;
// Server
public final LaunchServerModulesManager modulesManager; public final LaunchServerModulesManager modulesManager;
// Launcher binary
public final MirrorManager mirrorManager; public final MirrorManager mirrorManager;
public final ReconfigurableManager reconfigurableManager; public final ReconfigurableManager reconfigurableManager;
public final ConfigManager configManager; public final ConfigManager configManager;
// HWID ban + anti-brutforce
public final CertificateManager certificateManager; public final CertificateManager certificateManager;
public final ProguardConf proguardConf; public final ProguardConf proguardConf;
// Server
public final CommandHandler commandHandler; public final CommandHandler commandHandler;
public final NettyServerSocketHandler nettyServerSocketHandler; public final NettyServerSocketHandler nettyServerSocketHandler;
public final Timer taskPool;
private final AtomicBoolean started = new AtomicBoolean(false); public final AtomicBoolean started = new AtomicBoolean(false);
public final LauncherModuleLoader launcherModuleLoader;
public LaunchServerConfig config;
public volatile Map<String, HashedDir> updatesDirMap;
// Updates and profiles // Updates and profiles
private volatile List<ClientProfile> profilesList; private volatile List<ClientProfile> profilesList;
public volatile Map<String, HashedDir> updatesDirMap;
public final Timer taskPool;
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
public static class LaunchServerDirectories {
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile";
public Path updatesDir;
public Path profilesDir;
public Path launcherLibrariesDir;
public Path launcherLibrariesCompileDir;
public Path dir;
public Path trustStore;
public void collect() {
if (updatesDir == null) updatesDir = dir.resolve(UPDATES_NAME);
if (profilesDir == null) profilesDir = dir.resolve(PROFILES_NAME);
if (trustStore == null) trustStore = dir.resolve(TRUSTSTORE_NAME);
if (launcherLibrariesDir == null) launcherLibrariesDir = dir.resolve(LAUNCHERLIBRARIES_NAME);
if (launcherLibrariesCompileDir == null) launcherLibrariesCompileDir = dir.resolve(LAUNCHERLIBRARIESCOMPILE_NAME);
}
}
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException { public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, ECPublicKey publicKey, ECPrivateKey privateKey, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException {
this.dir = directories.dir; this.dir = directories.dir;
@ -365,7 +200,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
launcherBinary.init(); launcherBinary.init();
launcherEXEBinary.init(); launcherEXEBinary.init();
syncLauncherBinaries(); syncLauncherBinaries();
launcherModuleLoader = new LauncherModuleLoader(this);
// Sync updates dir // Sync updates dir
if (!IOHelper.isDir(updatesDir)) if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir); Files.createDirectory(updatesDir);
@ -375,7 +210,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
if (!IOHelper.isDir(profilesDir)) if (!IOHelper.isDir(profilesDir))
Files.createDirectory(profilesDir); Files.createDirectory(profilesDir);
syncProfilesDir(); syncProfilesDir();
launcherModuleLoader.init();
nettyServerSocketHandler = new NettyServerSocketHandler(this); nettyServerSocketHandler = new NettyServerSocketHandler(this);
// post init modules // post init modules
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this)); modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
@ -389,6 +224,73 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
} }
} }
public void reload(ReloadType type) throws Exception {
config.close(type);
Map<String, AuthProviderPair> pairs = null;
if (type.equals(ReloadType.NO_AUTH)) {
pairs = config.auth;
}
LogHelper.info("Reading LaunchServer config file");
config = launchServerConfigManager.readConfig();
config.setLaunchServer(this);
if (type.equals(ReloadType.NO_AUTH)) {
config.auth = pairs;
}
config.verify();
config.init(type);
if (type.equals(ReloadType.FULL) && config.components != null) {
LogHelper.debug("PreInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PreInit component %s", k);
v.preInit(this);
});
LogHelper.debug("PreInit components successful");
LogHelper.debug("Init components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("Init component %s", k);
v.init(this);
});
LogHelper.debug("Init components successful");
LogHelper.debug("PostInit components");
config.components.forEach((k, v) -> {
LogHelper.subDebug("PostInit component %s", k);
v.postInit(this);
});
LogHelper.debug("PostInit components successful");
}
}
@Override
public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>();
SubCommand reload = new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
if (args.length == 0) {
reload(ReloadType.FULL);
return;
}
switch (args[0]) {
case "full":
reload(ReloadType.FULL);
break;
case "no_auth":
reload(ReloadType.NO_AUTH);
break;
case "no_components":
reload(ReloadType.NO_COMPONENTS);
break;
default:
reload(ReloadType.FULL);
break;
}
}
};
commands.put("reload", reload);
return commands;
}
private LauncherBinary binary() { private LauncherBinary binary() {
if (launcherEXEBinaryClass != null) { if (launcherEXEBinaryClass != null) {
try { try {
@ -406,7 +308,6 @@ private LauncherBinary binary() {
return new EXELauncherBinary(this); return new EXELauncherBinary(this);
} }
public void buildLauncherBinaries() throws IOException { public void buildLauncherBinaries() throws IOException {
launcherBinary.build(); launcherBinary.build();
launcherEXEBinary.build(); launcherEXEBinary.build();
@ -435,7 +336,6 @@ public HashedDir getUpdateDir(String name) {
return updatesDirMap.get(name); return updatesDirMap.get(name);
} }
public Set<Entry<String, HashedDir>> getUpdateDirs() { public Set<Entry<String, HashedDir>> getUpdateDirs() {
return updatesDirMap.entrySet(); return updatesDirMap.entrySet();
} }
@ -467,7 +367,6 @@ public void run() {
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this)); modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
} }
public void syncLauncherBinaries() throws IOException { public void syncLauncherBinaries() throws IOException {
LogHelper.info("Syncing launcher binaries"); LogHelper.info("Syncing launcher binaries");
@ -482,7 +381,6 @@ public void syncLauncherBinaries() throws IOException {
} }
public void syncProfilesDir() throws IOException { public void syncProfilesDir() throws IOException {
LogHelper.info("Syncing profiles dir"); LogHelper.info("Syncing profiles dir");
List<ClientProfile> newProfies = new LinkedList<>(); List<ClientProfile> newProfies = new LinkedList<>();
@ -493,7 +391,6 @@ public void syncProfilesDir() throws IOException {
profilesList = Collections.unmodifiableList(newProfies); profilesList = Collections.unmodifiableList(newProfies);
} }
public void syncUpdatesDir(Collection<String> dirs) throws IOException { public void syncUpdatesDir(Collection<String> dirs) throws IOException {
LogHelper.info("Syncing updates dir"); LogHelper.info("Syncing updates dir");
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16); Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
@ -565,4 +462,73 @@ public void fullyRestart() {
restart(); restart();
JVMHelper.RUNTIME.exit(0); JVMHelper.RUNTIME.exit(0);
} }
public enum ReloadType {
NO_AUTH,
NO_COMPONENTS,
FULL
}
public enum LaunchServerEnv {
TEST,
DEV,
DEBUG,
PRODUCTION
}
public interface LaunchServerConfigManager {
LaunchServerConfig readConfig() throws IOException;
LaunchServerRuntimeConfig readRuntimeConfig() throws IOException;
void writeConfig(LaunchServerConfig config) throws IOException;
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
}
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<ClientProfile> result;
private ProfilesFileVisitor(Collection<ClientProfile> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
}
profile.verify();
// Add SIGNED profile to result list
result.add(profile);
return super.visitFile(file, attrs);
}
}
public static class LaunchServerDirectories {
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile";
public Path updatesDir;
public Path profilesDir;
public Path launcherLibrariesDir;
public Path launcherLibrariesCompileDir;
public Path dir;
public Path trustStore;
public void collect() {
if (updatesDir == null) updatesDir = dir.resolve(UPDATES_NAME);
if (profilesDir == null) profilesDir = dir.resolve(PROFILES_NAME);
if (trustStore == null) trustStore = dir.resolve(TRUSTSTORE_NAME);
if (launcherLibrariesDir == null) launcherLibrariesDir = dir.resolve(LAUNCHERLIBRARIES_NAME);
if (launcherLibrariesCompileDir == null)
launcherLibrariesCompileDir = dir.resolve(LAUNCHERLIBRARIESCOMPILE_NAME);
}
}
} }

View file

@ -2,6 +2,7 @@
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launchserver.auth.handler.AuthHandler;
@ -23,7 +24,6 @@
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import pro.gravit.launcher.LauncherTrustManager;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
@ -177,9 +177,9 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOExcept
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories(); LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
directories.dir = dir; directories.dir = dir;
if (inDocker) { if (inDocker) {
Path parentLibraries = StarterAgent.libraries.toAbsolutePath().normalize().getParent(); Path parentLibraries = StarterAgent.libraries.toAbsolutePath().normalize().getParent();
directories.launcherLibrariesCompileDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIESCOMPILE_NAME); directories.launcherLibrariesCompileDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIESCOMPILE_NAME);
directories.launcherLibrariesDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIES_NAME); directories.launcherLibrariesDir = parentLibraries.resolve(LaunchServer.LaunchServerDirectories.LAUNCHERLIBRARIES_NAME);
} }
LaunchServer server = new LaunchServerBuilder() LaunchServer server = new LaunchServerBuilder()
.setDirectories(directories) .setDirectories(directories)

View file

@ -6,15 +6,30 @@
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermission;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.jar.JarFile; import java.util.jar.JarFile;
public final class StarterAgent { public final class StarterAgent {
public static Instrumentation inst = null;
public static Path libraries = null;
private static boolean isStarted = false;
public static boolean isAgentStarted() {
return isStarted;
}
public static void premain(String agentArgument, Instrumentation inst) {
StarterAgent.inst = inst;
libraries = Paths.get(Optional.ofNullable(agentArgument).map(e -> e.trim()).filter(e -> !e.isEmpty()).orElse("libraries"));
isStarted = true;
try {
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
private static final class StarterVisitor extends SimpleFileVisitor<Path> { private static final class StarterVisitor extends SimpleFileVisitor<Path> {
private static final Set<PosixFilePermission> DPERMS; private static final Set<PosixFilePermission> DPERMS;
@ -49,23 +64,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
return super.visitFile(file, attrs); return super.visitFile(file, attrs);
} }
} }
public static Instrumentation inst = null;
public static Path libraries = null;
private static boolean isStarted = false;
public static boolean isAgentStarted() {
return isStarted;
}
public static void premain(String agentArgument, Instrumentation inst) {
StarterAgent.inst = inst;
libraries = Paths.get(Optional.ofNullable(agentArgument).map(e -> e.trim()).filter(e -> !e.isEmpty()).orElse("libraries"));
isStarted = true;
try {
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
} }

View file

@ -18,35 +18,20 @@
* чего угодно. Работает через поиск class-файлов в classpath. * чего угодно. Работает через поиск class-файлов в classpath.
*/ */
public class ClassMetadataReader implements Closeable { public class ClassMetadataReader implements Closeable {
private static class CheckSuperClassVisitor extends ClassVisitor {
String superClassName;
public CheckSuperClassVisitor() {
super(Opcodes.ASM7);
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
superClassName = superName;
}
}
private final List<JarFile> cp; private final List<JarFile> cp;
public ClassMetadataReader(List<JarFile> cp) { public ClassMetadataReader(List<JarFile> cp) {
this.cp = cp; this.cp = cp;
} }
public List<JarFile> getCp() {
return cp;
}
public ClassMetadataReader() { public ClassMetadataReader() {
this.cp = new ArrayList<>(); this.cp = new ArrayList<>();
} }
public List<JarFile> getCp() {
return cp;
}
public void acceptVisitor(byte[] classData, ClassVisitor visitor) { public void acceptVisitor(byte[] classData, ClassVisitor visitor) {
new ClassReader(classData).accept(visitor, 0); new ClassReader(classData).accept(visitor, 0);
} }
@ -63,7 +48,6 @@ public void acceptVisitor(String className, ClassVisitor visitor, int flags) thr
acceptVisitor(getClassData(className), visitor, flags); acceptVisitor(getClassData(className), visitor, flags);
} }
public byte[] getClassData(String className) throws IOException { public byte[] getClassData(String className) throws IOException {
for (JarFile f : cp) { for (JarFile f : cp) {
if (f.getEntry(className + ".class") != null) { if (f.getEntry(className + ".class") != null) {
@ -111,4 +95,19 @@ public void close() {
cp.clear(); cp.clear();
} }
private static class CheckSuperClassVisitor extends ClassVisitor {
String superClassName;
public CheckSuperClassVisitor() {
super(Opcodes.ASM7);
}
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
superClassName = superName;
}
}
} }

View file

@ -1,245 +1,243 @@
package pro.gravit.launchserver.asm; package pro.gravit.launchserver.asm;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
import pro.gravit.launchserver.binary.BuildContext;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import pro.gravit.launcher.LauncherInject; import pro.gravit.launcher.LauncherInject;
import pro.gravit.launcher.LauncherInjectionConstructor; import pro.gravit.launcher.LauncherInjectionConstructor;
import pro.gravit.launchserver.binary.BuildContext;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer { public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
private final Map<String, Object> values; private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class,
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
private static final Map<Class<?>, Serializer<?>> serializers;
public InjectClassAcceptor(Map<String, Object> values) { static {
this.values = values; serializers = new HashMap<>();
} serializers.put(List.class, new ListSerializer());
serializers.put(Map.class, new MapSerializer());
serializers.put(byte[].class, new ByteArraySerializer());
serializers.put(Short.class, serializerClass(Opcodes.I2S));
serializers.put(Byte.class, serializerClass(Opcodes.I2B));
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC
InsnList ret = new InsnList();
ret.add(new LdcInsnNode(e));
return ret;
});
serializers.put(Boolean.class, (Serializer<Boolean>) e -> {
InsnList ret = new InsnList();
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
return ret;
});
serializers.put(Character.class, (Serializer<Character>) e -> {
InsnList ret = new InsnList();
ret.add(NodeUtils.push((int) e));
ret.add(new InsnNode(Opcodes.I2C));
return ret;
});
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter);
}
private static final List<Class<?>> primitiveLDCClasses = Arrays.asList(java.lang.Integer.class, java.lang.Long.class, private final Map<String, Object> values;
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
private static final List<String> primitiveLDCDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
private static void visit(ClassNode classNode, Map<String, Object> values) { public InjectClassAcceptor(Map<String, Object> values) {
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name)) this.values = values;
.findFirst().orElseGet(() -> { }
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
"<clinit>", "()V", null, null);
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
classNode.methods.add(newClinitMethod);
return newClinitMethod;
});
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
.collect(Collectors.toList());
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
classNode.fields.forEach(field -> {
// Notice that fields that will be used with this algo should not have default
// value by = ...;
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
.orElse(null) : null;
if (valueAnnotation == null) {
return;
}
field.invisibleAnnotations.remove(valueAnnotation);
AtomicReference<String> valueName = new AtomicReference<String>(null);
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
@Override
public void visit(final String name, final Object value) {
if ("value".equals(name)) {
if (value.getClass() != String.class)
throw new IllegalArgumentException(
String.format("Invalid annotation with value class %s", field.getClass().getName()));
valueName.set(value.toString());
}
}
});
if (valueName.get() == null) {
throw new IllegalArgumentException("Annotation should always contains 'value' key");
}
if (!values.containsKey(valueName.get())) {
return;
}
Object value = values.get(valueName.get());
if ((field.access & Opcodes.ACC_STATIC) != 0) {
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
field.value = value;
return;
}
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
InsnList setter = serializeValue(value);
if (putStaticNodes.isEmpty()) {
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
} else {
if (initMethod == null) {
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
}
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
InsnList setter = serializeValue(value);
if (putFieldNodes.isEmpty()) {
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
initMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
}
});
}
private static final Map<Class<?>, Serializer<?>> serializers; private static void visit(ClassNode classNode, Map<String, Object> values) {
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
.findFirst().orElseGet(() -> {
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
"<clinit>", "()V", null, null);
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
classNode.methods.add(newClinitMethod);
return newClinitMethod;
});
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
.collect(Collectors.toList());
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
classNode.fields.forEach(field -> {
// Notice that fields that will be used with this algo should not have default
// value by = ...;
AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream()
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
.orElse(null) : null;
if (valueAnnotation == null) {
return;
}
field.invisibleAnnotations.remove(valueAnnotation);
AtomicReference<String> valueName = new AtomicReference<String>(null);
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
@Override
public void visit(final String name, final Object value) {
if ("value".equals(name)) {
if (value.getClass() != String.class)
throw new IllegalArgumentException(
String.format("Invalid annotation with value class %s", field.getClass().getName()));
valueName.set(value.toString());
}
}
});
if (valueName.get() == null) {
throw new IllegalArgumentException("Annotation should always contains 'value' key");
}
if (!values.containsKey(valueName.get())) {
return;
}
Object value = values.get(valueName.get());
if ((field.access & Opcodes.ACC_STATIC) != 0) {
if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) {
field.value = value;
return;
}
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
InsnList setter = serializeValue(value);
if (putStaticNodes.isEmpty()) {
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
} else {
if (initMethod == null) {
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
}
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
InsnList setter = serializeValue(value);
if (putFieldNodes.isEmpty()) {
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
Arrays.stream(initMethod.instructions.toArray())
.filter(node -> node.getOpcode() == Opcodes.RETURN)
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
} else {
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
initMethod.instructions.insertBefore(fieldInsnNode, setter);
}
}
}
});
}
static { private static Serializer<?> serializerClass(int opcode) {
serializers = new HashMap<>(); return new Serializer<Number>() {
serializers.put(List.class, new ListSerializer()); @Override
serializers.put(Map.class, new MapSerializer()); public InsnList serialize(Number value) {
serializers.put(byte[].class, new ByteArraySerializer()); InsnList ret = new InsnList();
serializers.put(Short.class, serializerClass(Opcodes.I2S)); ret.add(NodeUtils.push(value.intValue()));
serializers.put(Byte.class, serializerClass(Opcodes.I2B)); ret.add(new InsnNode(opcode));
serializers.put(Type.class, (Serializer<Type>) e -> { // ow.Type == java.lang.Class in LDC return ret;
InsnList ret = new InsnList(); }
ret.add(new LdcInsnNode(e));
return ret;
});
serializers.put(Boolean.class, (Serializer<Boolean>) e -> {
InsnList ret = new InsnList();
ret.add(new InsnNode(e ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
return ret;
});
serializers.put(Character.class, (Serializer<Character>) e -> {
InsnList ret = new InsnList();
ret.add(NodeUtils.push((int) e));
ret.add(new InsnNode(Opcodes.I2C));
return ret;
});
serializers.put(Enum.class, (Serializer<Enum>) NodeUtils::makeValueEnumGetter);
}
private static Serializer<?> serializerClass(int opcode) { };
return new Serializer<Number>() { }
@Override
public InsnList serialize(Number value) {
InsnList ret = new InsnList();
ret.add(NodeUtils.push(((Number) value).intValue()));
ret.add(new InsnNode(opcode));
return ret;
}
}; @SuppressWarnings("unchecked")
} private static InsnList serializeValue(Object value) {
if (value == null) {
InsnList insnList = new InsnList();
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
return insnList;
}
if (primitiveLDCClasses.contains(value.getClass())) {
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode(value));
return insnList;
}
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) {
return ((Serializer) serializerEntry.getValue()).serialize(value);
}
}
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
value.getClass()));
}
@FunctionalInterface @Override
private interface Serializer<T> { public void transform(ClassNode classNode, String className, BuildContext context) {
InsnList serialize(T value); visit(classNode, values);
} }
@SuppressWarnings("unchecked") @FunctionalInterface
private static InsnList serializeValue(Object value) { private interface Serializer<T> {
if (value == null) { InsnList serialize(T value);
InsnList insnList = new InsnList(); }
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
return insnList;
}
if (primitiveLDCClasses.contains(value.getClass())) {
InsnList insnList = new InsnList();
insnList.add(new LdcInsnNode(value));
return insnList;
}
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) {
return ((Serializer) serializerEntry.getValue()).serialize(value);
}
}
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
value.getClass()));
}
private static class ListSerializer implements Serializer<List> { private static class ListSerializer implements Serializer<List> {
@Override @Override
public InsnList serialize(List value) { public InsnList serialize(List value) {
InsnList insnList = new InsnList(); InsnList insnList = new InsnList();
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class))); insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(NodeUtils.push(value.size())); insnList.add(NodeUtils.push(value.size()));
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>", insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false)); Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
for (Object object : value) { for (Object object : value) {
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(serializeValue(object)); insnList.add(serializeValue(object));
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add", insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true)); Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
insnList.add(new InsnNode(Opcodes.POP)); insnList.add(new InsnNode(Opcodes.POP));
} }
return insnList; return insnList;
} }
} }
private static class MapSerializer implements Serializer<Map> { private static class MapSerializer implements Serializer<Map> {
@Override @Override
public InsnList serialize(Map value) { public InsnList serialize(Map value) {
InsnList insnList = new InsnList(); InsnList insnList = new InsnList();
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass()))); insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass())));
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>", insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE), false)); Type.getMethodDescriptor(Type.VOID_TYPE), false));
for (Object entryObject : value.entrySet()) { for (Object entryObject : value.entrySet()) {
Map.Entry entry = (Map.Entry) entryObject; Map.Entry entry = (Map.Entry) entryObject;
insnList.add(new InsnNode(Opcodes.DUP)); insnList.add(new InsnNode(Opcodes.DUP));
insnList.add(serializeValue(entry.getKey())); insnList.add(serializeValue(entry.getKey()));
insnList.add(serializeValue(entry.getValue())); insnList.add(serializeValue(entry.getValue()));
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put", insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put",
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)), Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)),
true)); true));
insnList.add(new InsnNode(Opcodes.POP)); insnList.add(new InsnNode(Opcodes.POP));
} }
return insnList; return insnList;
} }
} }
private static class ByteArraySerializer implements Serializer<byte[]> { private static class ByteArraySerializer implements Serializer<byte[]> {
@Override @Override
public InsnList serialize(byte[] value) { public InsnList serialize(byte[] value) {
InsnList insnList = new InsnList(); InsnList insnList = new InsnList();
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class), insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class),
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false)); "getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false));
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value))); insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value)));
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class), insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class),
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)), "decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)),
false)); false));
return insnList; return insnList;
} }
} }
@Override
public void transform(ClassNode classNode, String className, BuildContext context) {
visit(classNode, values);
}
} }

View file

@ -17,6 +17,8 @@
public final class NodeUtils { public final class NodeUtils {
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
private NodeUtils() { private NodeUtils() {
} }
@ -186,8 +188,6 @@ public static InsnList getSafeStringInsnList(String string) {
return insnList; return insnList;
} }
public static final int MAX_SAFE_BYTE_COUNT = 65535 - Byte.MAX_VALUE;
public static String[] splitUtf8ToChunks(String text, int maxBytes) { public static String[] splitUtf8ToChunks(String text, int maxBytes) {
List<String> parts = new ArrayList<>(); List<String> parts = new ArrayList<>();
@ -238,9 +238,9 @@ else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
} }
public static InsnList makeValueEnumGetter(@SuppressWarnings("rawtypes") Enum u) { public static InsnList makeValueEnumGetter(@SuppressWarnings("rawtypes") Enum u) {
InsnList ret = new InsnList(); InsnList ret = new InsnList();
Type e = Type.getType(u.getClass()); Type e = Type.getType(u.getClass());
ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor())); ret.add(new FieldInsnNode(Opcodes.GETSTATIC, e.getInternalName(), u.name(), e.getDescriptor()));
return ret; return ret;
} }
} }

View file

@ -9,13 +9,13 @@
import java.util.Map; import java.util.Map;
public class AuthProviderPair { public class AuthProviderPair {
public final boolean isDefault = true;
public AuthProvider provider; public AuthProvider provider;
public AuthHandler handler; public AuthHandler handler;
public TextureProvider textureProvider; public TextureProvider textureProvider;
public Map<String, String> links; public Map<String, String> links;
public transient String name; public transient String name;
public String displayName; public String displayName;
public final boolean isDefault = true;
public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvider textureProvider) { public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvider textureProvider) {
this.provider = provider; this.provider = provider;
@ -24,35 +24,33 @@ public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvi
} }
public void init(LaunchServer srv, String name) { public void init(LaunchServer srv, String name) {
this.name = name; this.name = name;
if(links != null) link(srv); if (links != null) link(srv);
if(provider == null) throw new NullPointerException(String.format("Auth %s provider null", name)); if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
if(handler == null) throw new NullPointerException(String.format("Auth %s handler null", name)); if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
if(textureProvider == null) throw new NullPointerException(String.format("Auth %s textureProvider null", name)); if (textureProvider == null)
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
provider.init(srv); provider.init(srv);
handler.init(srv); handler.init(srv);
} }
public void link(LaunchServer srv)
{ public void link(LaunchServer srv) {
links.forEach((k,v) -> { links.forEach((k, v) -> {
AuthProviderPair pair = srv.config.getAuthProviderPair(v); AuthProviderPair pair = srv.config.getAuthProviderPair(v);
if(pair == null) if (pair == null) {
{
throw new NullPointerException(String.format("Auth %s link failed. Pair %s not found", name, v)); throw new NullPointerException(String.format("Auth %s link failed. Pair %s not found", name, v));
} }
if("provider".equals(k)) if ("provider".equals(k)) {
{ if (pair.provider == null)
if(pair.provider == null) throw new NullPointerException(String.format("Auth %s link failed. %s.provider is null", name, v)); throw new NullPointerException(String.format("Auth %s link failed. %s.provider is null", name, v));
provider = pair.provider; provider = pair.provider;
} } else if ("handler".equals(k)) {
else if("handler".equals(k)) if (pair.handler == null)
{ throw new NullPointerException(String.format("Auth %s link failed. %s.handler is null", name, v));
if(pair.handler == null) throw new NullPointerException(String.format("Auth %s link failed. %s.handler is null", name, v));
handler = pair.handler; handler = pair.handler;
} } else if ("textureProvider".equals(k)) {
else if("textureProvider".equals(k)) if (pair.textureProvider == null)
{ throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
if(pair.textureProvider == null) throw new NullPointerException(String.format("Auth %s link failed. %s.textureProvider is null", name, v));
textureProvider = pair.textureProvider; textureProvider = pair.textureProvider;
} }
}); });

View file

@ -21,8 +21,8 @@ public final class PostgreSQLSourceConfig implements AutoCloseable {
private String poolName; private String poolName;
// Config // Config
private String address; private String[] addresses;
private int port; private int[] ports;
private String username; private String username;
private String password; private String password;
private String database; private String database;
@ -43,8 +43,8 @@ public synchronized Connection getConnection() throws SQLException {
PGSimpleDataSource postgresqlSource = new PGSimpleDataSource(); PGSimpleDataSource postgresqlSource = new PGSimpleDataSource();
// Set credentials // Set credentials
postgresqlSource.setServerNames(new String[] {address}); //TODO support multinode PostgreSQL DB postgresqlSource.setServerNames(addresses);
postgresqlSource.setPortNumbers(new int[] {port}); postgresqlSource.setPortNumbers(ports);
postgresqlSource.setUser(username); postgresqlSource.setUser(username);
postgresqlSource.setPassword(password); postgresqlSource.setPassword(password);
postgresqlSource.setDatabaseName(database); postgresqlSource.setDatabaseName(database);

View file

@ -12,7 +12,7 @@
public abstract class AuthHandler implements AutoCloseable { public abstract class AuthHandler implements AutoCloseable {
public static final ProviderMap<AuthHandler> providers = new ProviderMap<>("AuthHandler"); public static final ProviderMap<AuthHandler> providers = new ProviderMap<>("AuthHandler");
private static boolean registredHandl = false; private static boolean registredHandl = false;
protected transient LaunchServer srv;
public static UUID authError(String message) throws AuthException { public static UUID authError(String message) throws AuthException {
throw new AuthException(message); throw new AuthException(message);
@ -31,8 +31,6 @@ public static void registerHandlers() {
} }
} }
protected transient LaunchServer srv;
/** /**
* Returns the UUID associated with the account * Returns the UUID associated with the account
* *

View file

@ -18,26 +18,8 @@
import java.util.UUID; import java.util.UUID;
public abstract class CachedAuthHandler extends AuthHandler implements NeedGarbageCollection, Reconfigurable { public abstract class CachedAuthHandler extends AuthHandler implements NeedGarbageCollection, Reconfigurable {
public static final class Entry { private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
public final UUID uuid;
private String username;
private String accessToken;
private String serverID;
public Entry(UUID uuid, String username, String accessToken, String serverID) {
this.uuid = Objects.requireNonNull(uuid, "uuid");
this.username = Objects.requireNonNull(username, "username");
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
}
}
protected static class EntryAndUsername {
public Map<UUID, CachedAuthHandler.Entry> entryCache;
public Map<String, UUID> usernameCache;
}
@Override @Override
public Map<String, Command> getCommands() { public Map<String, Command> getCommands() {
@ -91,10 +73,6 @@ public void invoke(String... args) throws Exception {
return commands; return commands;
} }
private transient final Map<UUID, Entry> entryCache = new HashMap<>(1024);
private transient final Map<String, UUID> usernamesCache = new HashMap<>(1024);
protected void addEntry(Entry entry) { protected void addEntry(Entry entry) {
Entry previous = entryCache.put(entry.uuid, entry); Entry previous = entryCache.put(entry.uuid, entry);
if (previous != null) if (previous != null)
@ -122,10 +100,8 @@ public synchronized UUID checkServer(String username, String serverID) throws IO
serverID.equals(entry.serverID) ? entry.uuid : null; serverID.equals(entry.serverID) ? entry.uuid : null;
} }
protected abstract Entry fetchEntry(String username) throws IOException; protected abstract Entry fetchEntry(String username) throws IOException;
protected abstract Entry fetchEntry(UUID uuid) throws IOException; protected abstract Entry fetchEntry(UUID uuid) throws IOException;
private Entry getEntry(String username) throws IOException { private Entry getEntry(String username) throws IOException {
@ -187,7 +163,6 @@ public void loadUsernameCache(Map<String, UUID> map) {
protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException; protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException;
protected abstract boolean updateServerID(UUID uuid, String serverID) throws IOException; protected abstract boolean updateServerID(UUID uuid, String serverID) throws IOException;
@Override @Override
@ -201,4 +176,25 @@ public final synchronized String uuidToUsername(UUID uuid) throws IOException {
Entry entry = getEntry(uuid); Entry entry = getEntry(uuid);
return entry == null ? null : entry.username; return entry == null ? null : entry.username;
} }
public static final class Entry {
public final UUID uuid;
private String username;
private String accessToken;
private String serverID;
public Entry(UUID uuid, String username, String accessToken, String serverID) {
this.uuid = Objects.requireNonNull(uuid, "uuid");
this.username = Objects.requireNonNull(username, "username");
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
}
}
protected static class EntryAndUsername {
public Map<UUID, CachedAuthHandler.Entry> entryCache;
public Map<String, UUID> usernameCache;
}
} }

View file

@ -1,7 +1,7 @@
package pro.gravit.launchserver.auth.handler; package pro.gravit.launchserver.auth.handler;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.HTTPRequest; import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
@ -12,6 +12,31 @@ public class JsonAuthHandler extends CachedAuthHandler {
public URL updateAuthUrl; public URL updateAuthUrl;
public URL updateServerIdUrl; public URL updateServerIdUrl;
@Override
protected Entry fetchEntry(String username) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken)), updateAuthUrl), SuccessResponse.class).success;
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateServerIDRequest(uuid, serverID)), updateServerIdUrl), SuccessResponse.class).success;
}
@Override
public void close() {
}
public static class EntryRequestByUsername { public static class EntryRequestByUsername {
public final String username; public final String username;
@ -53,29 +78,4 @@ public UpdateServerIDRequest(UUID uuid, String serverID) {
public static class SuccessResponse { public static class SuccessResponse {
public boolean success; public boolean success;
} }
@Override
protected Entry fetchEntry(String username) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken)), updateAuthUrl), SuccessResponse.class).success;
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new UpdateServerIDRequest(uuid, serverID)), updateServerIdUrl), SuccessResponse.class).success;
}
@Override
public void close() {
}
} }

View file

@ -10,14 +10,12 @@
import java.util.UUID; import java.util.UUID;
public final class RequestAuthHandler extends CachedAuthHandler { public final class RequestAuthHandler extends CachedAuthHandler {
private String usernameFetch;
private String uuidFetch;
private String updateAuth;
private String updateServerID;
private final String splitSymbol = ":"; private final String splitSymbol = ":";
private final String goodResponse = "OK"; private final String goodResponse = "OK";
private String usernameFetch;
private String uuidFetch;
private String updateAuth;
private String updateServerID;
@Override @Override
public void init(LaunchServer srv) { public void init(LaunchServer srv) {

View file

@ -4,7 +4,6 @@
import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.SecureProtectHandler;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
public class AdvancedProtectHandler extends ProtectHandler implements SecureProtectHandler { public class AdvancedProtectHandler extends ProtectHandler implements SecureProtectHandler {

View file

@ -1,7 +1,6 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
public class NoProtectHandler extends ProtectHandler { public class NoProtectHandler extends ProtectHandler {

View file

@ -4,7 +4,6 @@
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -14,6 +13,7 @@
public class StdProtectHandler extends ProtectHandler implements ProfilesProtectHandler { public class StdProtectHandler extends ProtectHandler implements ProfilesProtectHandler {
public Map<String, List<String>> profileWhitelist = new HashMap<>(); public Map<String, List<String>> profileWhitelist = new HashMap<>();
public List<String> allowUpdates = new ArrayList<>(); public List<String> allowUpdates = new ArrayList<>();
@Override @Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) { public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign; return (context.authType == AuthResponse.ConnectTypes.CLIENT) && context.client.checkSign;
@ -36,13 +36,12 @@ public boolean canChangeProfile(ClientProfile profile, Client client) {
@Override @Override
public boolean canGetUpdates(String updatesDirName, Client client) { public boolean canGetUpdates(String updatesDirName, Client client) {
return client.profile != null && ( client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName)); return client.profile != null && (client.profile.getDir().equals(updatesDirName) || client.profile.getAssetDir().equals(updatesDirName) || allowUpdates.contains(updatesDirName));
} }
public boolean isWhitelisted(String profileTitle, String username) public boolean isWhitelisted(String profileTitle, String username) {
{
List<String> allowedUsername = profileWhitelist.get(profileTitle); List<String> allowedUsername = profileWhitelist.get(profileTitle);
if(allowedUsername == null) return true; if (allowedUsername == null) return true;
return allowedUsername.contains(username); return allowedUsername.contains(username);
} }
} }

View file

@ -4,20 +4,19 @@
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
public interface ProfilesProtectHandler { public interface ProfilesProtectHandler {
default boolean canGetProfiles(Client client) default boolean canGetProfiles(Client client) {
{
return true; return true;
} }
default boolean canGetProfile(ClientProfile profile, Client client)
{ default boolean canGetProfile(ClientProfile profile, Client client) {
return true; return true;
} }
default boolean canChangeProfile(ClientProfile profile, Client client)
{ default boolean canChangeProfile(ClientProfile profile, Client client) {
return client.isAuth; return client.isAuth;
} }
default boolean canGetUpdates(String updatesDirName, Client client)
{ default boolean canGetUpdates(String updatesDirName, Client client) {
return true; return true;
} }
} }

View file

@ -12,21 +12,23 @@
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
public interface SecureProtectHandler { public interface SecureProtectHandler {
default byte[] generateSecureLevelKey() default byte[] generateSecureLevelKey() {
{
return SecurityHelper.randomBytes(128); return SecurityHelper.randomBytes(128);
} }
default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signature) throws InvalidKeySpecException, SignatureException { default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signature) throws InvalidKeySpecException, SignatureException {
if(publicKey == null || signature == null) throw new InvalidKeySpecException(); if (publicKey == null || signature == null) throw new InvalidKeySpecException();
ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey); ECPublicKey pubKey = SecurityHelper.toPublicECKey(publicKey);
Signature sign = SecurityHelper.newECVerifySignature(pubKey); Signature sign = SecurityHelper.newECVerifySignature(pubKey);
sign.update(data); sign.update(data);
sign.verify(signature); sign.verify(signature);
} }
GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event); GetSecureLevelInfoRequestEvent onGetSecureLevelInfo(GetSecureLevelInfoRequestEvent event);
boolean allowGetSecureLevelInfo(Client client); boolean allowGetSecureLevelInfo(Client client);
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client)
{ default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) {
return new SecurityReportRequestEvent(); return new SecurityReportRequestEvent();
} }
} }

View file

@ -13,13 +13,6 @@ public abstract class AuthProvider implements AutoCloseable {
private static boolean registredProv = false; private static boolean registredProv = false;
protected transient LaunchServer srv = null; protected transient LaunchServer srv = null;
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
}
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
}
public static AuthProviderResult authError(String message) throws AuthException { public static AuthProviderResult authError(String message) throws AuthException {
throw new AuthException(message); throw new AuthException(message);
} }
@ -38,6 +31,13 @@ public static void registerProviders() {
} }
} }
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getFirstAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.PASSWORD;
}
public GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType getSecondAuthType() {
return GetAvailabilityAuthRequestEvent.AuthAvailability.AuthType.NONE;
}
/** /**
* Verifies the username and password * Verifies the username and password

View file

@ -3,10 +3,10 @@
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
@ -17,33 +17,6 @@ public final class JsonAuthProvider extends AuthProvider {
private URL url; private URL url;
private String apiKey; private String apiKey;
public static class authResult {
String username;
String error;
long permissions;
long flags;
}
public static class authRequest {
public authRequest(String username, String password, String ip) {
this.username = username;
this.password = password;
this.ip = ip;
}
public authRequest(String username, String password, String ip, String apiKey) {
this.username = username;
this.password = password;
this.ip = ip;
this.apiKey = apiKey;
}
final String username;
final String password;
final String ip;
String apiKey;
}
@Override @Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws IOException { public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws IOException {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported"); if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
@ -66,4 +39,31 @@ else if (result.error != null)
public void close() { public void close() {
// pass // pass
} }
public static class authResult {
String username;
String error;
long permissions;
long flags;
}
public static class authRequest {
final String username;
final String password;
final String ip;
String apiKey;
public authRequest(String username, String password, String ip) {
this.username = username;
this.password = password;
this.ip = ip;
}
public authRequest(String username, String password, String ip, String apiKey) {
this.username = username;
this.password = password;
this.ip = ip;
this.apiKey = apiKey;
}
}
} }

View file

@ -14,6 +14,9 @@
import java.util.Map; import java.util.Map;
public final class RejectAuthProvider extends AuthProvider implements Reconfigurable { public final class RejectAuthProvider extends AuthProvider implements Reconfigurable {
private String message;
private ArrayList<String> whitelist = new ArrayList<>();
public RejectAuthProvider() { public RejectAuthProvider() {
} }
@ -21,9 +24,6 @@ public RejectAuthProvider(String message) {
this.message = message; this.message = message;
} }
private String message;
private ArrayList<String> whitelist = new ArrayList<>();
@Override @Override
public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws AuthException { public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws AuthException {
if (whitelist != null) { if (whitelist != null) {

View file

@ -11,6 +11,10 @@
import java.util.UUID; import java.util.UUID;
public final class RequestTextureProvider extends TextureProvider { public final class RequestTextureProvider extends TextureProvider {
// Instance
private String skinURL;
private String cloakURL;
public RequestTextureProvider() { public RequestTextureProvider() {
} }
@ -37,11 +41,6 @@ private static String getTextureURL(String url, UUID uuid, String username, Stri
"client", IOHelper.urlEncode(client == null ? "unknown" : client)); "client", IOHelper.urlEncode(client == null ? "unknown" : client));
} }
// Instance
private String skinURL;
private String cloakURL;
@Override @Override
public void close() { public void close() {
// Do nothing // Do nothing

View file

@ -55,7 +55,7 @@ public void replacePre(Predicate<LauncherBuildTask> pred, LauncherBuildTask task
} }
public void replace(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) { public void replace(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) {
replaceCounted( 0, pred, taskRep); replaceCounted(0, pred, taskRep);
} }
public void replaceAfter(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) { public void replaceAfter(Predicate<LauncherBuildTask> pred, LauncherBuildTask taskRep) {

View file

@ -37,6 +37,115 @@ public class BuildContext {
public final HashSet<String> fileList; public final HashSet<String> fileList;
public final HashSet<String> clientModules; public final HashSet<String> clientModules;
public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainBuildTask task) {
this.output = output;
this.readerClassPath = readerClassPath;
this.task = task;
fileList = new HashSet<>(1024);
clientModules = new HashSet<>();
}
public void pushFile(String filename, InputStream inputStream) throws IOException {
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
IOHelper.transfer(inputStream, output);
output.closeEntry();
fileList.add(filename);
}
public void pushFile(String filename, StreamObject object) throws IOException {
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
object.write(new HOutput(output));
output.closeEntry();
fileList.add(filename);
}
public void pushFile(String filename, Object object, Type type) throws IOException {
String bytes = Launcher.gsonManager.gson.toJson(object, type);
pushBytes(filename, bytes.getBytes(UNICODE_CHARSET));
}
public void pushDir(Path dir, String targetDir, Map<String, byte[]> hashMap, boolean hidden) throws IOException {
IOHelper.walk(dir, new RuntimeDirVisitor(output, hashMap, dir, targetDir), hidden);
}
public void pushBytes(String filename, byte[] bytes) throws IOException {
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
output.write(bytes);
output.closeEntry();
fileList.add(filename);
}
@Deprecated
public void pushJarFile(ZipInputStream input) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
if (fileList.contains(e.getName())) {
e = input.getNextEntry();
continue;
}
output.putNextEntry(IOHelper.newZipEntry(e));
IOHelper.transfer(input, output);
fileList.add(e.getName());
e = input.getNextEntry();
}
}
@Deprecated
public void pushJarFile(ZipInputStream input, Set<String> blacklist) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
if (fileList.contains(e.getName()) || blacklist.contains(e.getName())) {
e = input.getNextEntry();
continue;
}
output.putNextEntry(IOHelper.newZipEntry(e));
IOHelper.transfer(input, output);
fileList.add(e.getName());
e = input.getNextEntry();
}
}
public void pushJarFile(Path jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException {
pushJarFile(jarfile.toUri().toURL(), filter, needTransform);
}
public void pushJarFile(URL jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException {
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(jarfile))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
String filename = e.getName();
if (e.isDirectory() || fileList.contains(filename) || filter.test(e)) {
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(IOHelper.newZipEntry(e));
} catch (ZipException ex) {
LogHelper.warning("Write %s failed: %s", filename, ex.getMessage() == null ? "null" : ex.getMessage());
e = input.getNextEntry();
continue;
}
if (filename.endsWith(".class")) {
String classname = filename.replace('/', '.').substring(0,
filename.length() - ".class".length());
if (!needTransform.test(classname)) {
IOHelper.transfer(input, output);
} else {
byte[] bytes = IOHelper.read(input);
bytes = task.transformClass(bytes, classname, this);
output.write(bytes);
}
} else
IOHelper.transfer(input, output);
fileList.add(filename);
e = input.getNextEntry();
}
}
}
private final static class RuntimeDirVisitor extends SimpleFileVisitor<Path> { private final static class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output; private final ZipOutputStream output;
private final Map<String, byte[]> hashs; private final Map<String, byte[]> hashs;
@ -60,7 +169,7 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(sourceDir.relativize(file)); String fileName = IOHelper.toString(sourceDir.relativize(file));
if(hashs != null) if (hashs != null)
hashs.put(fileName, SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, file)); hashs.put(fileName, SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents // Create zip entry and transfer contents
@ -72,122 +181,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
} }
private ZipEntry newEntry(String fileName) { private ZipEntry newEntry(String fileName) {
return newZipEntry( targetDir + IOHelper.CROSS_SEPARATOR + fileName); return newZipEntry(targetDir + IOHelper.CROSS_SEPARATOR + fileName);
}
}
public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainBuildTask task) {
this.output = output;
this.readerClassPath = readerClassPath;
this.task = task;
fileList = new HashSet<>(1024);
clientModules = new HashSet<>();
}
public void pushFile(String filename, InputStream inputStream) throws IOException {
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
IOHelper.transfer(inputStream, output);
output.closeEntry();
fileList.add(filename);
}
public void pushFile(String filename, StreamObject object) throws IOException
{
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
object.write(new HOutput(output));
output.closeEntry();
fileList.add(filename);
}
public void pushFile(String filename, Object object, Type type) throws IOException
{
String bytes = Launcher.gsonManager.gson.toJson(object, type);
pushBytes(filename, bytes.getBytes(UNICODE_CHARSET));
}
public void pushDir(Path dir, String targetDir, Map<String, byte[]> hashMap, boolean hidden) throws IOException
{
IOHelper.walk(dir, new RuntimeDirVisitor(output, hashMap, dir, targetDir), hidden);
}
public void pushBytes(String filename, byte[] bytes) throws IOException {
ZipEntry zip = IOHelper.newZipEntry(filename);
output.putNextEntry(zip);
output.write(bytes);
output.closeEntry();
fileList.add(filename);
}
@Deprecated
public void pushJarFile(ZipInputStream input) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
if (fileList.contains(e.getName())) {
e = input.getNextEntry();
continue;
}
output.putNextEntry(IOHelper.newZipEntry(e));
IOHelper.transfer(input, output);
fileList.add(e.getName());
e = input.getNextEntry();
}
}
@Deprecated
public void pushJarFile(ZipInputStream input, Set<String> blacklist) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
if (fileList.contains(e.getName()) || blacklist.contains(e.getName())) {
e = input.getNextEntry();
continue;
}
output.putNextEntry(IOHelper.newZipEntry(e));
IOHelper.transfer(input, output);
fileList.add(e.getName());
e = input.getNextEntry();
}
}
public void pushJarFile(Path jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException
{
pushJarFile(jarfile.toUri().toURL(), filter, needTransform);
}
public void pushJarFile(URL jarfile, Predicate<ZipEntry> filter, Predicate<String> needTransform) throws IOException
{
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(jarfile))) {
ZipEntry e = input.getNextEntry();
while(e != null)
{
String filename = e.getName();
if(e.isDirectory() || fileList.contains(filename) || filter.test(e))
{
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(IOHelper.newZipEntry(e));
} catch (ZipException ex) {
LogHelper.warning("Write %s failed: %s", filename, ex.getMessage() == null ? "null" : ex.getMessage());
e = input.getNextEntry();
continue;
}
if (filename.endsWith(".class")) {
String classname = filename.replace('/', '.').substring(0,
filename.length() - ".class".length());
if(!needTransform.test(classname))
{
IOHelper.transfer(input, output);
}
else
{
byte[] bytes = IOHelper.read(input);
bytes = task.transformClass(bytes, classname, this);
output.write(bytes);
}
} else
IOHelper.transfer(input, output);
fileList.add(filename);
e = input.getNextEntry();
}
} }
} }
} }

View file

@ -36,7 +36,7 @@ public JARLauncherBinary(LaunchServer server) throws IOException {
@Override @Override
public void init() { public void init() {
tasks.add(new PrepareBuildTask(server)); tasks.add(new PrepareBuildTask(server));
if(!server.config.sign.enabled) tasks.add(new CertificateAutogenTask(server)); if (!server.config.sign.enabled) tasks.add(new CertificateAutogenTask(server));
tasks.add(new MainBuildTask(server)); tasks.add(new MainBuildTask(server));
if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server)); if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
tasks.add(new ProGuardBuildTask(server)); tasks.add(new ProGuardBuildTask(server));

View file

@ -19,17 +19,18 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat
syncBinaryFile = binaryFile; syncBinaryFile = binaryFile;
} }
public void build() throws IOException public static Path resolve(LaunchServer server, String ext) {
{ return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
} }
public void build() throws IOException {
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
}
public final boolean exists() { public final boolean exists() {
return syncBinaryFile != null && IOHelper.isFile(syncBinaryFile); return syncBinaryFile != null && IOHelper.isFile(syncBinaryFile);
} }
public final byte[] getDigest() { public final byte[] getDigest() {
return digest; return digest;
} }
@ -48,8 +49,4 @@ public final boolean sync() throws IOException {
return exists; return exists;
} }
public static Path resolve(LaunchServer server, String ext) {
return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
}
} }

View file

@ -14,27 +14,16 @@
import java.util.List; import java.util.List;
public class ProguardConf { public class ProguardConf {
public static final String[] JAVA9_OPTS = new String[]{
"-libraryjars '<java.home>/jmods/'"
};
public static final String[] JAVA8_OPTS = new String[]{
"-libraryjars '<java.home>/lib/rt.jar'",
"-libraryjars '<java.home>/lib/jce.jar'",
"-libraryjars '<java.home>/lib/ext/nashorn.jar'",
"-libraryjars '<java.home>/lib/ext/jfxrt.jar'"
};
private static final char[] chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ".toCharArray(); private static final char[] chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ".toCharArray();
public static final String[] JAVA9_OPTS = new String[] {
"-libraryjars '<java.home>/jmods/'"
};
public static final String[] JAVA8_OPTS = new String[] {
"-libraryjars '<java.home>/lib/rt.jar'",
"-libraryjars '<java.home>/lib/jce.jar'",
"-libraryjars '<java.home>/lib/ext/nashorn.jar'",
"-libraryjars '<java.home>/lib/ext/jfxrt.jar'"
};
private static String generateString(SecureRandom rand, String lowString, String upString, int il) {
StringBuilder sb = new StringBuilder(Math.max(il, lowString.length()));
for (int i = 0; i < lowString.length(); ++i) {
sb.append(rand.nextBoolean() ? lowString.charAt(i) : upString.charAt(i));
}
int toI = il - lowString.length();
for (int i = 0; i < toI; i++) sb.append(chars[rand.nextInt(chars.length)]);
return sb.toString();
}
public final Path proguard; public final Path proguard;
public final Path config; public final Path config;
public final Path mappings; public final Path mappings;
@ -49,6 +38,16 @@ public ProguardConf(LaunchServer srv) {
this.srv = srv; this.srv = srv;
} }
private static String generateString(SecureRandom rand, String lowString, String upString, int il) {
StringBuilder sb = new StringBuilder(Math.max(il, lowString.length()));
for (int i = 0; i < lowString.length(); ++i) {
sb.append(rand.nextBoolean() ? lowString.charAt(i) : upString.charAt(i));
}
int toI = il - lowString.length();
for (int i = 0; i < toI; i++) sb.append(chars[rand.nextInt(chars.length)]);
return sb.toString();
}
public String[] buildConfig(Path inputJar, Path outputJar) { public String[] buildConfig(Path inputJar, Path outputJar) {
List<String> confStrs = new ArrayList<>(); List<String> confStrs = new ArrayList<>();
prepare(false); prepare(false);
@ -57,7 +56,7 @@ public String[] buildConfig(Path inputJar, Path outputJar) {
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'"); confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'"); confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'");
confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'"); confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");
Collections.addAll(confStrs, JVMHelper.JVM_VERSION >= 9 ? JAVA9_OPTS : JAVA8_OPTS); Collections.addAll(confStrs, JVMHelper.JVM_VERSION >= 9 ? JAVA9_OPTS : JAVA8_OPTS);
srv.launcherBinary.coreLibs.stream() srv.launcherBinary.coreLibs.stream()
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'") .map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'")
.forEach(confStrs::add); .forEach(confStrs::add);

View file

@ -40,22 +40,19 @@
*/ */
public class SignerJar implements AutoCloseable { public class SignerJar implements AutoCloseable {
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF"; private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
private static final String DIGEST_HASH = SignHelper.hashFunctionName + "-Digest";
private final String SIG_FN; private final String SIG_FN;
private final String SIG_KEY_FN; private final String SIG_KEY_FN;
private static final String DIGEST_HASH = SignHelper.hashFunctionName + "-Digest";
private final ZipOutputStream zos; private final ZipOutputStream zos;
private final Map<String, String> manifestAttributes; private final Map<String, String> manifestAttributes;
private final Map<String, String> fileDigests;
private final Map<String, String> sectionDigests;
private final Supplier<CMSSignedDataGenerator> gen;
private String manifestHash; private String manifestHash;
private String manifestMainHash; private String manifestMainHash;
private final Map<String, String> fileDigests;
private final Map<String, String> sectionDigests;
private final Supplier<CMSSignedDataGenerator> gen;
public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, String sig_fn, String sig_key_fn) { public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, String sig_fn, String sig_key_fn) {
zos = out; zos = out;
this.gen = gen; this.gen = gen;
@ -76,7 +73,7 @@ public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, Stri
* @throws NullPointerException if any of the arguments is {@code null} * @throws NullPointerException if any of the arguments is {@code null}
*/ */
public void addFileContents(String filename, byte[] contents) throws IOException { public void addFileContents(String filename, byte[] contents) throws IOException {
addFileContents(filename, new ByteArrayInputStream(contents)); addFileContents(filename, new ByteArrayInputStream(contents));
} }
/** /**
@ -89,7 +86,7 @@ public void addFileContents(String filename, byte[] contents) throws IOException
* @throws NullPointerException if any of the arguments is {@code null} * @throws NullPointerException if any of the arguments is {@code null}
*/ */
public void addFileContents(String filename, InputStream contents) throws IOException { public void addFileContents(String filename, InputStream contents) throws IOException {
addFileContents(IOHelper.newZipEntry(filename), contents); addFileContents(IOHelper.newZipEntry(filename), contents);
} }
/** /**
@ -102,7 +99,7 @@ public void addFileContents(String filename, InputStream contents) throws IOExce
* @throws NullPointerException if any of the arguments is {@code null} * @throws NullPointerException if any of the arguments is {@code null}
*/ */
public void addFileContents(ZipEntry entry, byte[] contents) throws IOException { public void addFileContents(ZipEntry entry, byte[] contents) throws IOException {
addFileContents(entry, new ByteArrayInputStream(contents)); addFileContents(entry, new ByteArrayInputStream(contents));
} }
/** /**
@ -138,7 +135,7 @@ public void addManifestAttribute(String name, String value) {
* underlying stream. * underlying stream.
* *
* @throws IOException * @throws IOException
* @throws RuntimeException if the signing goes wrong * @throws RuntimeException if the signing goes wrong
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
@ -152,7 +149,7 @@ public void close() throws IOException {
* underlying stream open. * underlying stream open.
* *
* @throws IOException * @throws IOException
* @throws RuntimeException if the signing goes wrong * @throws RuntimeException if the signing goes wrong
*/ */
public void finish() throws IOException { public void finish() throws IOException {
writeManifest(); writeManifest();
@ -272,7 +269,7 @@ private byte[] writeSigFile() throws IOException {
* Signs the .SIG file and writes the signature (.RSA file) to the JAR. * Signs the .SIG file and writes the signature (.RSA file) to the JAR.
* *
* @throws IOException * @throws IOException
* @throws RuntimeException if the signing failed * @throws RuntimeException if the signing failed
*/ */
private void writeSignature(byte[] sigFile) throws IOException { private void writeSignature(byte[] sigFile) throws IOException {
zos.putNextEntry(IOHelper.newZipEntry(SIG_KEY_FN)); zos.putNextEntry(IOHelper.newZipEntry(SIG_KEY_FN));

View file

@ -24,20 +24,6 @@ public AdditionalFixesApplyTask(LaunchServer server) {
this.server = server; this.server = server;
} }
@Override
public String getName() {
return "AdditionalFixesApply";
}
@Override
public Path process(Path inputFile) throws IOException {
Path out = server.launcherBinary.nextPath("post-fixed");
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(out))) {
apply(inputFile, inputFile, output, server, (e) -> false, true);
}
return out;
}
public static void apply(Path inputFile, Path addFile, ZipOutputStream output, LaunchServer srv, Predicate<ZipEntry> excluder, boolean needFixes) throws IOException { public static void apply(Path inputFile, Path addFile, ZipOutputStream output, LaunchServer srv, Predicate<ZipEntry> excluder, boolean needFixes) throws IOException {
try (ClassMetadataReader reader = new ClassMetadataReader()) { try (ClassMetadataReader reader = new ClassMetadataReader()) {
reader.getCp().add(new JarFile(inputFile.toFile())); reader.getCp().add(new JarFile(inputFile.toFile()));
@ -52,11 +38,10 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L
output.putNextEntry(IOHelper.newZipEntry(e)); output.putNextEntry(IOHelper.newZipEntry(e));
if (filename.endsWith(".class")) { if (filename.endsWith(".class")) {
byte[] bytes; byte[] bytes;
if(needFixes) { if (needFixes) {
bytes = classFix(input, reader, srv.config.launcher.stripLineNumbers); bytes = classFix(input, reader, srv.config.launcher.stripLineNumbers);
output.write(bytes); output.write(bytes);
} } else
else
IOHelper.transfer(input, output); IOHelper.transfer(input, output);
} else } else
IOHelper.transfer(input, output); IOHelper.transfer(input, output);
@ -75,6 +60,20 @@ private static byte[] classFix(InputStream input, ClassMetadataReader reader, bo
return cw.toByteArray(); return cw.toByteArray();
} }
@Override
public String getName() {
return "AdditionalFixesApply";
}
@Override
public Path process(Path inputFile) throws IOException {
Path out = server.launcherBinary.nextPath("post-fixed");
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(out))) {
apply(inputFile, inputFile, output, server, (e) -> false, true);
}
return out;
}
@Override @Override
public boolean allowDelete() { public boolean allowDelete() {
return true; return true;

View file

@ -3,7 +3,10 @@
import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*; import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
@ -29,6 +32,9 @@
import java.util.Date; import java.util.Date;
public class CertificateAutogenTask implements LauncherBuildTask { public class CertificateAutogenTask implements LauncherBuildTask {
public X509Certificate certificate;
public X509CertificateHolder bcCertificate;
public CMSSignedDataGenerator signedDataGenerator;
private LaunchServer server; private LaunchServer server;
public CertificateAutogenTask(LaunchServer server) { public CertificateAutogenTask(LaunchServer server) {
@ -39,13 +45,10 @@ public CertificateAutogenTask(LaunchServer server) {
public String getName() { public String getName() {
return "CertificateAutogen"; return "CertificateAutogen";
} }
public X509Certificate certificate;
public X509CertificateHolder bcCertificate;
public CMSSignedDataGenerator signedDataGenerator;
@Override @Override
public Path process(Path inputFile) throws IOException { public Path process(Path inputFile) throws IOException {
if(signedDataGenerator != null) return inputFile; if (signedDataGenerator != null) return inputFile;
try { try {
X500NameBuilder subject = new X500NameBuilder(); X500NameBuilder subject = new X500NameBuilder();
subject.addRDN(BCStyle.CN, server.config.projectName.concat(" Autogenerated")); subject.addRDN(BCStyle.CN, server.config.projectName.concat(" Autogenerated"));
@ -63,8 +66,8 @@ public Path process(Path inputFile) throws IOException {
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256WITHECDSA"); JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256WITHECDSA");
ContentSigner signer = csBuilder.build(server.privateKey); ContentSigner signer = csBuilder.build(server.privateKey);
bcCertificate = builder.build(signer); bcCertificate = builder.build(signer);
certificate = new JcaX509CertificateConverter().setProvider( "BC" ) certificate = new JcaX509CertificateConverter().setProvider("BC")
.getCertificate( bcCertificate ); .getCertificate(bcCertificate);
ArrayList<Certificate> chain = new ArrayList<>(); ArrayList<Certificate> chain = new ArrayList<>();
chain.add(certificate); chain.add(certificate);
signedDataGenerator = SignHelper.createSignedDataGenerator(server.privateKey, certificate, chain, "SHA256WITHECDSA"); signedDataGenerator = SignHelper.createSignedDataGenerator(server.privateKey, certificate, chain, "SHA256WITHECDSA");

View file

@ -27,94 +27,12 @@
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
public class MainBuildTask implements LauncherBuildTask { public class MainBuildTask implements LauncherBuildTask {
private final LaunchServer server;
public final ClassMetadataReader reader; public final ClassMetadataReader reader;
@FunctionalInterface private final LaunchServer server;
public interface Transformer {
byte[] transform(byte[] input, String classname, BuildContext context);
}
public static class IOHookSet<R> {
public final Set<IOHook<R>> list = new HashSet<>();
@FunctionalInterface
public interface IOHook<R> {
/**
* @param context custom param
* False to continue processing hook
* @throws HookException The hook may return the error text throwing this exception
*/
void hook(R context) throws HookException, IOException;
}
public void registerHook(IOHook<R> hook) {
list.add(hook);
}
public boolean unregisterHook(IOHook<R> hook) {
return list.remove(hook);
}
/**
* @param context custom param
* False to continue
* @throws HookException The hook may return the error text throwing this exception
*/
public void hook(R context) throws HookException, IOException {
for (IOHook<R> hook : list) {
hook.hook(context);
}
}
}
public interface ASMTransformer extends Transformer {
default byte[] transform(byte[] input, String classname, BuildContext context)
{
ClassReader reader = new ClassReader(input);
ClassNode cn = new ClassNode();
reader.accept(cn, 0);
transform(cn, classname, context);
SafeClassWriter writer = new SafeClassWriter(context.task.reader,ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(writer);
return writer.toByteArray();
}
void transform(ClassNode cn, String classname, BuildContext context);
}
public abstract static class ASMAnnotationFieldProcessor implements ASMTransformer
{
private final String desc;
protected ASMAnnotationFieldProcessor(String desc) {
this.desc = desc;
}
@Override
public void transform(ClassNode cn, String classname, BuildContext context) {
for(FieldNode fn : cn.fields)
{
if(fn.invisibleAnnotations == null || fn.invisibleAnnotations.isEmpty()) continue;
AnnotationNode found = null;
for(AnnotationNode an : fn.invisibleAnnotations)
{
if(an == null) continue;
if(desc.equals(an.desc))
{
found = an;
break;
}
}
if(found != null)
{
transformField(found, fn, cn, classname, context);
}
}
}
abstract public void transformField(AnnotationNode an, FieldNode fn, ClassNode cn, String classname, BuildContext context);
}
public Set<String> blacklist = new HashSet<>(); public Set<String> blacklist = new HashSet<>();
public List<Transformer> transformers = new ArrayList<>(); public List<Transformer> transformers = new ArrayList<>();
public IOHookSet<BuildContext> preBuildHook = new IOHookSet<>(); public IOHookSet<BuildContext> preBuildHook = new IOHookSet<>();
public IOHookSet<BuildContext> postBuildHook = new IOHookSet<>(); public IOHookSet<BuildContext> postBuildHook = new IOHookSet<>();
public Map<String, Object> properties = new HashMap<>(); public Map<String, Object> properties = new HashMap<>();
public MainBuildTask(LaunchServer srv) { public MainBuildTask(LaunchServer srv) {
@ -163,7 +81,7 @@ public Path process(Path inputJar) throws IOException {
} }
protected void postInitProps() { protected void postInitProps() {
List<byte[]> certificates = Arrays.stream(server.certificateManager.trustManager.getTrusted()).map(e -> { List<byte[]> certificates = Arrays.stream(server.certificateManager.trustManager.getTrusted()).map(e -> {
try { try {
return e.getEncoded(); return e.getEncoded();
} catch (CertificateEncodingException e2) { } catch (CertificateEncodingException e2) {
@ -171,8 +89,7 @@ protected void postInitProps() {
return new byte[0]; return new byte[0];
} }
}).collect(Collectors.toList()); }).collect(Collectors.toList());
if(!server.config.sign.enabled) if (!server.config.sign.enabled) {
{
CertificateAutogenTask task = server.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get(); CertificateAutogenTask task = server.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get();
try { try {
certificates.add(task.certificate.getEncoded()); certificates.add(task.certificate.getEncoded());
@ -181,10 +98,10 @@ protected void postInitProps() {
} }
} }
properties.put("launchercore.certificates", certificates); properties.put("launchercore.certificates", certificates);
} }
protected void initProps() { protected void initProps() {
properties.clear(); properties.clear();
properties.put("launcher.address", server.config.netty.address); properties.put("launcher.address", server.config.netty.address);
properties.put("launcher.projectName", server.config.projectName); properties.put("launcher.projectName", server.config.projectName);
properties.put("runtimeconfig.secretKeyClient", SecurityHelper.randomStringAESKey()); properties.put("runtimeconfig.secretKeyClient", SecurityHelper.randomStringAESKey());
@ -202,45 +119,37 @@ protected void initProps() {
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken(); if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
properties.put("runtimeconfig.oemUnlockKey", server.runtime.oemUnlockKey); properties.put("runtimeconfig.oemUnlockKey", server.runtime.oemUnlockKey);
} }
public byte[] transformClass(byte[] bytes, String classname, BuildContext context) public byte[] transformClass(byte[] bytes, String classname, BuildContext context) {
{
byte[] result = bytes; byte[] result = bytes;
ClassReader cr = null; ClassReader cr = null;
ClassWriter writer = null; ClassWriter writer = null;
ClassNode cn = null; ClassNode cn = null;
for(Transformer t : transformers) for (Transformer t : transformers) {
{ if (t instanceof ASMTransformer) {
if(t instanceof ASMTransformer)
{
ASMTransformer asmTransformer = (ASMTransformer) t; ASMTransformer asmTransformer = (ASMTransformer) t;
if(cn == null) if (cn == null) {
{
cr = new ClassReader(result); cr = new ClassReader(result);
cn = new ClassNode(); cn = new ClassNode();
cr.accept(cn, 0); cr.accept(cn, 0);
} }
asmTransformer.transform(cn, classname, context); asmTransformer.transform(cn, classname, context);
continue; continue;
} } else if (cn != null) {
else if(cn != null)
{
writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(writer); cn.accept(writer);
result = writer.toByteArray(); result = writer.toByteArray();
} }
byte[] old_result = result; byte[] old_result = result;
result = t.transform(result, classname, context); result = t.transform(result, classname, context);
if(old_result != result) if (old_result != result) {
{
cr = null; cr = null;
cn = null; cn = null;
} }
} }
if(cn != null) if (cn != null) {
{ writer = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
writer = new SafeClassWriter(reader,ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(writer); cn.accept(writer);
result = writer.toByteArray(); result = writer.toByteArray();
} }
@ -251,4 +160,84 @@ else if(cn != null)
public boolean allowDelete() { public boolean allowDelete() {
return true; return true;
} }
@FunctionalInterface
public interface Transformer {
byte[] transform(byte[] input, String classname, BuildContext context);
}
public interface ASMTransformer extends Transformer {
default byte[] transform(byte[] input, String classname, BuildContext context) {
ClassReader reader = new ClassReader(input);
ClassNode cn = new ClassNode();
reader.accept(cn, 0);
transform(cn, classname, context);
SafeClassWriter writer = new SafeClassWriter(context.task.reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(writer);
return writer.toByteArray();
}
void transform(ClassNode cn, String classname, BuildContext context);
}
public static class IOHookSet<R> {
public final Set<IOHook<R>> list = new HashSet<>();
public void registerHook(IOHook<R> hook) {
list.add(hook);
}
public boolean unregisterHook(IOHook<R> hook) {
return list.remove(hook);
}
/**
* @param context custom param
* False to continue
* @throws HookException The hook may return the error text throwing this exception
*/
public void hook(R context) throws HookException, IOException {
for (IOHook<R> hook : list) {
hook.hook(context);
}
}
@FunctionalInterface
public interface IOHook<R> {
/**
* @param context custom param
* False to continue processing hook
* @throws HookException The hook may return the error text throwing this exception
*/
void hook(R context) throws HookException, IOException;
}
}
public abstract static class ASMAnnotationFieldProcessor implements ASMTransformer {
private final String desc;
protected ASMAnnotationFieldProcessor(String desc) {
this.desc = desc;
}
@Override
public void transform(ClassNode cn, String classname, BuildContext context) {
for (FieldNode fn : cn.fields) {
if (fn.invisibleAnnotations == null || fn.invisibleAnnotations.isEmpty()) continue;
AnnotationNode found = null;
for (AnnotationNode an : fn.invisibleAnnotations) {
if (an == null) continue;
if (desc.equals(an.desc)) {
found = an;
break;
}
}
if (found != null) {
transformField(found, fn, cn, classname, context);
}
}
}
abstract public void transformField(AnnotationNode an, FieldNode fn, ClassNode cn, String classname, BuildContext context);
}
} }

View file

@ -33,6 +33,17 @@ public SignJarTask(LaunchServerConfig.JarSignerConf config, LaunchServer srv) {
this.srv = srv; this.srv = srv;
} }
public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config, KeyStore c) {
try {
return SignHelper.createSignedDataGenerator(c,
config.keyAlias, config.signAlgo, config.keyPass);
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
LogHelper.error(e);
return null;
}
}
@Override @Override
public String getName() { public String getName() {
return "SignJar"; return "SignJar";
@ -46,7 +57,7 @@ public Path process(Path inputFile) throws IOException {
} }
public void sign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException { public void sign(LaunchServerConfig.JarSignerConf config, Path inputFile, Path signedFile) throws IOException {
if(config.enabled) stdSign(config, inputFile, signedFile); if (config.enabled) stdSign(config, inputFile, signedFile);
else autoSign(inputFile, signedFile); else autoSign(inputFile, signedFile);
} }
@ -69,6 +80,7 @@ private void stdSign(LaunchServerConfig.JarSignerConf config, Path inputFile, Pa
} }
} }
} }
private void autoSign(Path inputFile, Path signedFile) throws IOException { private void autoSign(Path inputFile, Path signedFile) throws IOException {
try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> { try (SignerJar output = new SignerJar(new ZipOutputStream(IOHelper.newOutput(signedFile)), () -> {
CertificateAutogenTask task = srv.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get(); CertificateAutogenTask task = srv.launcherBinary.getTaskByClass(CertificateAutogenTask.class).get();
@ -95,15 +107,4 @@ private void autoSign(Path inputFile, Path signedFile) throws IOException {
public boolean allowDelete() { public boolean allowDelete() {
return true; return true;
} }
public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config, KeyStore c) {
try {
return SignHelper.createSignedDataGenerator(c,
config.keyAlias, config.signAlgo, config.keyPass);
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
LogHelper.error(e);
return null;
}
}
} }

View file

@ -13,23 +13,10 @@
import java.nio.file.Path; import java.nio.file.Path;
public class Launch4JTask implements LauncherBuildTask { public class Launch4JTask implements LauncherBuildTask {
private final static class Launch4JLog extends Log {
private static final Launch4JLog INSTANCE = new Launch4JLog();
@Override
public void append(String s) {
LogHelper.subInfo(s);
}
@Override
public void clear() {
// Do nothing
}
}
public static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle public static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
private static final String VERSION = Version.getVersion().getVersionString();
private static final int BUILD = Version.getVersion().build;
private final Path faviconFile; private final Path faviconFile;
private final LaunchServer server; private final LaunchServer server;
public Launch4JTask(LaunchServer launchServer) { public Launch4JTask(LaunchServer launchServer) {
@ -37,6 +24,10 @@ public Launch4JTask(LaunchServer launchServer) {
faviconFile = launchServer.dir.resolve("favicon.ico"); faviconFile = launchServer.dir.resolve("favicon.ico");
} }
public static String formatVars(String mask) {
return String.format(mask, VERSION, BUILD);
}
@Override @Override
public String getName() { public String getName() {
return "launch4j"; return "launch4j";
@ -70,6 +61,7 @@ public Path process(Path inputFile) throws IOException {
public boolean allowDelete() { public boolean allowDelete() {
return true; return true;
} }
private Path setConfig() { private Path setConfig() {
Path path = server.launcherEXEBinary.nextPath(getName()); Path path = server.launcherEXEBinary.nextPath(getName());
Config config = new Config(); Config config = new Config();
@ -120,10 +112,17 @@ private Path setConfig() {
return path; return path;
} }
private static final String VERSION = Version.getVersion().getVersionString(); private final static class Launch4JLog extends Log {
private static final int BUILD = Version.getVersion().build; private static final Launch4JLog INSTANCE = new Launch4JLog();
public static String formatVars(String mask) { @Override
return String.format(mask, VERSION, BUILD); public void append(String s) {
LogHelper.subInfo(s);
}
@Override
public void clear() {
// Do nothing
}
} }
} }

View file

@ -10,12 +10,12 @@
import java.security.KeyPair; import java.security.KeyPair;
public class TestCommand extends Command { public class TestCommand extends Command {
private NettyServerSocketHandler handler = null;
public TestCommand(LaunchServer server) { public TestCommand(LaunchServer server) {
super(server); super(server);
} }
private NettyServerSocketHandler handler = null;
@Override @Override
public String getArgsDescription() { public String getArgsDescription() {
return null; return null;

View file

@ -20,65 +20,23 @@
import java.util.Collections; import java.util.Collections;
public final class IndexAssetCommand extends Command { public final class IndexAssetCommand extends Command {
private static final Gson gson = new Gson();
public static class IndexObject {
final long size;
public IndexObject(long size, String hash) {
this.size = size;
this.hash = hash;
}
final String hash;
}
private static final class IndexAssetVisitor extends SimpleFileVisitor<Path> {
private final JsonObject objects;
private final Path inputAssetDir;
private final Path outputAssetDir;
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) {
this.objects = objects;
this.inputAssetDir = inputAssetDir;
this.outputAssetDir = outputAssetDir;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String name = IOHelper.toString(inputAssetDir.relativize(file));
LogHelper.subInfo("Indexing: '%s'", name);
// Add to index and copy file
String digest = SecurityHelper.toHex(SecurityHelper.digest(DigestAlgorithm.SHA1, file));
IndexObject obj = new IndexObject(attrs.size(), digest);
objects.add(name, gson.toJsonTree(obj));
IOHelper.copy(file, resolveObjectFile(outputAssetDir, digest));
// Continue visiting
return super.visitFile(file, attrs);
}
}
public static final String INDEXES_DIR = "indexes"; public static final String INDEXES_DIR = "indexes";
public static final String OBJECTS_DIR = "objects"; public static final String OBJECTS_DIR = "objects";
private static final Gson gson = new Gson();
private static final String JSON_EXTENSION = ".json"; private static final String JSON_EXTENSION = ".json";
public IndexAssetCommand(LaunchServer server) {
super(server);
}
public static Path resolveIndexFile(Path assetDir, String name) { public static Path resolveIndexFile(Path assetDir, String name) {
return assetDir.resolve(INDEXES_DIR).resolve(name + JSON_EXTENSION); return assetDir.resolve(INDEXES_DIR).resolve(name + JSON_EXTENSION);
} }
public static Path resolveObjectFile(Path assetDir, String hash) { public static Path resolveObjectFile(Path assetDir, String hash) {
return assetDir.resolve(OBJECTS_DIR).resolve(hash.substring(0, 2)).resolve(hash); return assetDir.resolve(OBJECTS_DIR).resolve(hash.substring(0, 2)).resolve(hash);
} }
public IndexAssetCommand(LaunchServer server) {
super(server);
}
@Override @Override
public String getArgsDescription() { public String getArgsDescription() {
return "[dir] [index] [output-dir]"; return "[dir] [index] [output-dir]";
@ -122,4 +80,41 @@ public void invoke(String... args) throws Exception {
server.syncUpdatesDir(Collections.singleton(outputAssetDirName)); server.syncUpdatesDir(Collections.singleton(outputAssetDirName));
LogHelper.subInfo("Asset successfully indexed: '%s'", inputAssetDirName); LogHelper.subInfo("Asset successfully indexed: '%s'", inputAssetDirName);
} }
public static class IndexObject {
final long size;
final String hash;
public IndexObject(long size, String hash) {
this.size = size;
this.hash = hash;
}
}
private static final class IndexAssetVisitor extends SimpleFileVisitor<Path> {
private final JsonObject objects;
private final Path inputAssetDir;
private final Path outputAssetDir;
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) {
this.objects = objects;
this.inputAssetDir = inputAssetDir;
this.outputAssetDir = outputAssetDir;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String name = IOHelper.toString(inputAssetDir.relativize(file));
LogHelper.subInfo("Indexing: '%s'", name);
// Add to index and copy file
String digest = SecurityHelper.toHex(SecurityHelper.digest(DigestAlgorithm.SHA1, file));
IndexObject obj = new IndexObject(attrs.size(), digest);
objects.add(name, gson.toJsonTree(obj));
IOHelper.copy(file, resolveObjectFile(outputAssetDir, digest));
// Continue visiting
return super.visitFile(file, attrs);
}
}
} }

View file

@ -19,6 +19,13 @@ public SaveProfilesCommand(LaunchServer server) {
super(server); super(server);
} }
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
try (Writer w = IOHelper.newWriter(path)) {
Launcher.gsonManager.configGson.toJson(profile, w);
}
}
@Override @Override
public String getArgsDescription() { public String getArgsDescription() {
return "[profile names...]"; return "[profile names...]";
@ -32,19 +39,15 @@ public String getUsageDescription() {
@Override @Override
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
if(args.length > 0) if (args.length > 0) {
{ for (String profileName : args) {
for(String profileName : args)
{
Path profilePath = server.profilesDir.resolve(profileName.concat(".json")); Path profilePath = server.profilesDir.resolve(profileName.concat(".json"));
if(!Files.exists(profilePath)) if (!Files.exists(profilePath)) {
{
LogHelper.error("Profile %s not found", profilePath.toString()); LogHelper.error("Profile %s not found", profilePath.toString());
return; return;
} }
ClientProfile profile; ClientProfile profile;
try(Reader reader = IOHelper.newReader(profilePath)) try (Reader reader = IOHelper.newReader(profilePath)) {
{
profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class); profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class);
} }
saveProfile(profile, profilePath); saveProfile(profile, profilePath);
@ -53,12 +56,4 @@ public void invoke(String... args) throws Exception {
server.syncProfilesDir(); server.syncProfilesDir();
} }
} }
public static void saveProfile(ClientProfile profile, Path path) throws IOException
{
if(profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
try(Writer w = IOHelper.newWriter(path))
{
Launcher.gsonManager.configGson.toJson(profile, w);
}
}
} }

View file

@ -12,26 +12,6 @@
import java.util.Optional; import java.util.Optional;
public class SignDirCommand extends Command { public class SignDirCommand extends Command {
private class SignJarVisitor extends SimpleFileVisitor<Path>
{
private SignJarTask task;
public SignJarVisitor(SignJarTask task) {
this.task = task;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar"))
{
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
LogHelper.info("Signing jar %s", file.toString());
task.sign(server.config.sign, file, tmpSign);
Files.deleteIfExists(file);
Files.move(tmpSign, file);
}
return super.visitFile(file, attrs);
}
}
public SignDirCommand(LaunchServer server) { public SignDirCommand(LaunchServer server) {
super(server); super(server);
} }
@ -50,11 +30,31 @@ public String getUsageDescription() {
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
Path targetDir = Paths.get(args[0]); Path targetDir = Paths.get(args[0]);
if(!IOHelper.isDir(targetDir)) if (!IOHelper.isDir(targetDir))
throw new IllegalArgumentException(String.format("%s not directory", targetDir.toString())); throw new IllegalArgumentException(String.format("%s not directory", targetDir.toString()));
Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class); Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class);
if(!task.isPresent()) throw new IllegalStateException("SignJarTask not found"); if (!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
IOHelper.walk(targetDir, new SignJarVisitor(task.get()), true); IOHelper.walk(targetDir, new SignJarVisitor(task.get()), true);
LogHelper.info("Success signed"); LogHelper.info("Success signed");
} }
private class SignJarVisitor extends SimpleFileVisitor<Path> {
private SignJarTask task;
public SignJarVisitor(SignJarTask task) {
this.task = task;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar")) {
Path tmpSign = server.dir.resolve("build").resolve(file.toFile().getName());
LogHelper.info("Signing jar %s", file.toString());
task.sign(server.config.sign, file, tmpSign);
Files.deleteIfExists(file);
Files.move(tmpSign, file);
}
return super.visitFile(file, attrs);
}
}
} }

View file

@ -30,16 +30,15 @@ public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
Path target = Paths.get(args[0]); Path target = Paths.get(args[0]);
Path tmpSign; Path tmpSign;
if(args.length > 1) if (args.length > 1)
tmpSign = Paths.get(args[1]); tmpSign = Paths.get(args[1]);
else else
tmpSign = server.dir.resolve("build").resolve(target.toFile().getName()); tmpSign = server.dir.resolve("build").resolve(target.toFile().getName());
LogHelper.info("Signing jar %s to %s", target.toString(), tmpSign.toString()); LogHelper.info("Signing jar %s to %s", target.toString(), tmpSign.toString());
Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class); Optional<SignJarTask> task = server.launcherBinary.getTaskByClass(SignJarTask.class);
if(!task.isPresent()) throw new IllegalStateException("SignJarTask not found"); if (!task.isPresent()) throw new IllegalStateException("SignJarTask not found");
task.get().sign(server.config.sign, target, tmpSign); task.get().sign(server.config.sign, target, tmpSign);
if(args.length <= 1) if (args.length <= 1) {
{
LogHelper.info("Move temp jar %s to %s", tmpSign.toString(), target.toString()); LogHelper.info("Move temp jar %s to %s", tmpSign.toString(), target.toString());
Files.deleteIfExists(target); Files.deleteIfExists(target);
Files.move(tmpSign, target); Files.move(tmpSign, target);

View file

@ -12,9 +12,10 @@
import java.util.Map; import java.util.Map;
public abstract class AbstractLimiter<T> extends Component implements NeedGarbageCollection, Reconfigurable { public abstract class AbstractLimiter<T> extends Component implements NeedGarbageCollection, Reconfigurable {
public final List<T> exclude = new ArrayList<>();
protected final transient Map<T, LimitEntry> map = new HashMap<>();
public int rateLimit; public int rateLimit;
public int rateLimitMillis; public int rateLimitMillis;
public final List<T> exclude = new ArrayList<>();
@Override @Override
public Map<String, Command> getCommands() { public Map<String, Command> getCommands() {
@ -67,23 +68,6 @@ public void garbageCollection() {
map.entrySet().removeIf((e) -> e.getValue().time + rateLimitMillis < time); map.entrySet().removeIf((e) -> e.getValue().time + rateLimitMillis < time);
} }
static class LimitEntry {
long time;
int trys;
public LimitEntry(long time, int trys) {
this.time = time;
this.trys = trys;
}
public LimitEntry() {
time = System.currentTimeMillis();
trys = 0;
}
}
protected final transient Map<T, LimitEntry> map = new HashMap<>();
public boolean check(T address) { public boolean check(T address) {
if (exclude.contains(address)) return true; if (exclude.contains(address)) return true;
LimitEntry entry = map.get(address); LimitEntry entry = map.get(address);
@ -105,4 +89,19 @@ public boolean check(T address) {
return false; return false;
} }
} }
static class LimitEntry {
long time;
int trys;
public LimitEntry(long time, int trys) {
this.time = time;
this.trys = trys;
}
public LimitEntry() {
time = System.currentTimeMillis();
trys = 0;
}
}
} }

View file

@ -7,6 +7,7 @@
import pro.gravit.utils.HookException; import pro.gravit.utils.HookException;
public class AuthLimiterComponent extends IPLimiter implements NeedGarbageCollection, AutoCloseable { public class AuthLimiterComponent extends IPLimiter implements NeedGarbageCollection, AutoCloseable {
public String message;
private transient LaunchServer srv; private transient LaunchServer srv;
@Override @Override
@ -31,8 +32,6 @@ public boolean preAuthHook(AuthResponse.AuthContext context, Client client) {
return false; return false;
} }
public String message;
@Override @Override
public void close() { public void close() {
srv.authHookManager.preHook.unregisterHook(this::preAuthHook); srv.authHookManager.preHook.unregisterHook(this::preAuthHook);

View file

@ -5,7 +5,6 @@
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig; import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.handler.MemoryAuthHandler; import pro.gravit.launchserver.auth.handler.MemoryAuthHandler;
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
@ -22,43 +21,109 @@
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public final class LaunchServerConfig { public final class LaunchServerConfig {
public String projectName;
public String[] mirrors;
public String binaryName;
public boolean copyBinaries = true;
public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth;
public DaoProvider dao;
// Handlers & Providers
public ProtectHandler protectHandler;
public Map<String, Component> components;
public ExeConf launch4j;
public NettyConfig netty;
public LauncherConf launcher;
public CertificateConf certificate;
public JarSignerConf sign;
public String startScript;
private transient LaunchServer server = null; private transient LaunchServer server = null;
private transient AuthProviderPair authDefault;
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
LaunchServerConfig newConfig = new LaunchServerConfig();
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/"};
newConfig.launch4j = new LaunchServerConfig.ExeConf();
newConfig.launch4j.enabled = true;
newConfig.launch4j.copyright = "© GravitLauncher Team";
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
newConfig.launch4j.internalName = "Launcher";
newConfig.launch4j.trademarks = "This product is licensed under GPLv3";
newConfig.launch4j.txtFileVersion = "%s, build %d";
newConfig.launch4j.txtProductVersion = "%s, build %d";
newConfig.launch4j.productName = "GravitLauncher";
newConfig.launch4j.productVer = newConfig.launch4j.fileVer;
newConfig.launch4j.maxVersion = "1.8.999";
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
newConfig.startScript = JVMHelper.OS_TYPE.equals(JVMHelper.OS.MUSTDIE) ? "." + File.separator + "start.bat" : "." + File.separator + "start.sh";
newConfig.auth = new HashMap<>();
AuthProviderPair a = new AuthProviderPair(new RejectAuthProvider("Настройте authProvider"),
new MemoryAuthHandler(),
new RequestTextureProvider("http://example.com/skins/%username%.png", "http://example.com/cloaks/%username%.png")
);
a.displayName = "Default";
newConfig.auth.put("std", a);
newConfig.protectHandler = new StdProtectHandler();
newConfig.binaryName = "Launcher";
newConfig.netty = new NettyConfig();
newConfig.netty.fileServerEnabled = true;
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
newConfig.netty.performance = new NettyPerformanceConfig();
try {
newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
} catch (Throwable e) {
// Epoll class line 51+ catch (Exception) but Error will be thrown by System.load
newConfig.netty.performance.usingEpoll = false;
} // such as on ARM
newConfig.netty.performance.bossThread = 2;
newConfig.netty.performance.workerThread = 8;
newConfig.launcher = new LauncherConf();
newConfig.launcher.guardType = "no";
newConfig.launcher.compress = true;
newConfig.launcher.warningMissArchJava = true;
newConfig.launcher.attachLibraryBeforeProGuard = false;
newConfig.launcher.deleteTempFiles = true;
newConfig.launcher.enabledProGuard = true;
newConfig.launcher.stripLineNumbers = true;
newConfig.launcher.proguardGenMappings = true;
newConfig.certificate = new LaunchServerConfig.CertificateConf();
newConfig.certificate.enabled = false;
newConfig.sign = new JarSignerConf();
newConfig.components = new HashMap<>();
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
authLimiterComponent.rateLimit = 3;
authLimiterComponent.rateLimitMillis = 8000;
authLimiterComponent.message = "Превышен лимит авторизаций";
newConfig.components.put("authLimiter", authLimiterComponent);
RegLimiterComponent regLimiterComponent = new RegLimiterComponent();
regLimiterComponent.rateLimit = 3;
regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов
regLimiterComponent.message = "Превышен лимит регистраций";
newConfig.components.put("regLimiter", regLimiterComponent);
newConfig.netty.sendExceptionEnabled = true;
return newConfig;
}
public LaunchServerConfig setLaunchServer(LaunchServer server) { public LaunchServerConfig setLaunchServer(LaunchServer server) {
this.server = server; this.server = server;
return this; return this;
} }
public String projectName;
public String[] mirrors;
public String binaryName;
public boolean copyBinaries = true;
public LauncherConfig.LauncherEnvironment env;
// Handlers & Providers
public Map<String, AuthProviderPair> auth;
public DaoProvider dao;
private transient AuthProviderPair authDefault;
public AuthProviderPair getAuthProviderPair(String name) { public AuthProviderPair getAuthProviderPair(String name) {
return auth.get(name); return auth.get(name);
} }
public ProtectHandler protectHandler;
public AuthProviderPair getAuthProviderPair() { public AuthProviderPair getAuthProviderPair() {
if (authDefault != null) return authDefault; if (authDefault != null) return authDefault;
for (AuthProviderPair pair : auth.values()) { for (AuthProviderPair pair : auth.values()) {
@ -70,17 +135,6 @@ public AuthProviderPair getAuthProviderPair() {
throw new IllegalStateException("Default AuthProviderPair not found"); throw new IllegalStateException("Default AuthProviderPair not found");
} }
public Map<String, Component> components;
public ExeConf launch4j;
public NettyConfig netty;
public LauncherConf launcher;
public CertificateConf certificate;
public JarSignerConf sign;
public String startScript;
public void setProjectName(String projectName) { public void setProjectName(String projectName) {
this.projectName = projectName; this.projectName = projectName;
} }
@ -93,7 +147,6 @@ public void setEnv(LauncherConfig.LauncherEnvironment env) {
this.env = env; this.env = env;
} }
public void verify() { public void verify() {
if (auth == null || auth.size() < 1) { if (auth == null || auth.size() < 1) {
throw new NullPointerException("AuthProviderPair`s count should be at least one"); throw new NullPointerException("AuthProviderPair`s count should be at least one");
@ -122,7 +175,7 @@ public void verify() {
public void init(LaunchServer.ReloadType type) { public void init(LaunchServer.ReloadType type) {
Launcher.applyLauncherEnv(env); Launcher.applyLauncherEnv(env);
for (Map.Entry<String,AuthProviderPair> provider : auth.entrySet()) { for (Map.Entry<String, AuthProviderPair> provider : auth.entrySet()) {
provider.getValue().init(server, provider.getKey()); provider.getValue().init(server, provider.getKey());
} }
if (dao != null) { if (dao != null) {
@ -170,10 +223,9 @@ public void close(LaunchServer.ReloadType type) {
} catch (Exception e) { } catch (Exception e) {
LogHelper.error(e); LogHelper.error(e);
} }
if(dao != null) { if (dao != null) {
server.unregisterObject("dao", dao); server.unregisterObject("dao", dao);
if(dao instanceof AutoCloseable) if (dao instanceof AutoCloseable) {
{
try { try {
((AutoCloseable) dao).close(); ((AutoCloseable) dao).close();
} catch (Exception e) { } catch (Exception e) {
@ -264,73 +316,4 @@ public NettyBindAddress(String address, int port) {
this.port = port; this.port = port;
} }
} }
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
LaunchServerConfig newConfig = new LaunchServerConfig();
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/"};
newConfig.launch4j = new LaunchServerConfig.ExeConf();
newConfig.launch4j.enabled = true;
newConfig.launch4j.copyright = "© GravitLauncher Team";
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
newConfig.launch4j.internalName = "Launcher";
newConfig.launch4j.trademarks = "This product is licensed under GPLv3";
newConfig.launch4j.txtFileVersion = "%s, build %d";
newConfig.launch4j.txtProductVersion = "%s, build %d";
newConfig.launch4j.productName = "GravitLauncher";
newConfig.launch4j.productVer = newConfig.launch4j.fileVer;
newConfig.launch4j.maxVersion = "1.8.999";
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
newConfig.startScript = JVMHelper.OS_TYPE.equals(JVMHelper.OS.MUSTDIE) ? "." + File.separator + "start.bat" : "." + File.separator + "start.sh";
newConfig.auth = new HashMap<>();
AuthProviderPair a = new AuthProviderPair(new RejectAuthProvider("Настройте authProvider"),
new MemoryAuthHandler(),
new RequestTextureProvider("http://example.com/skins/%username%.png", "http://example.com/cloaks/%username%.png")
);
a.displayName = "Default";
newConfig.auth.put("std", a);
newConfig.protectHandler = new StdProtectHandler();
newConfig.binaryName = "Launcher";
newConfig.netty = new NettyConfig();
newConfig.netty.fileServerEnabled = true;
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
newConfig.netty.performance = new NettyPerformanceConfig();
try {
newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
} catch (Throwable e) {
// Epoll class line 51+ catch (Exception) but Error will be thrown by System.load
newConfig.netty.performance.usingEpoll = false;
} // such as on ARM
newConfig.netty.performance.bossThread = 2;
newConfig.netty.performance.workerThread = 8;
newConfig.launcher = new LauncherConf();
newConfig.launcher.guardType = "no";
newConfig.launcher.compress = true;
newConfig.launcher.warningMissArchJava = true;
newConfig.launcher.attachLibraryBeforeProGuard = false;
newConfig.launcher.deleteTempFiles = true;
newConfig.launcher.enabledProGuard = true;
newConfig.launcher.stripLineNumbers = true;
newConfig.launcher.proguardGenMappings = true;
newConfig.certificate = new LaunchServerConfig.CertificateConf();
newConfig.certificate.enabled = false;
newConfig.sign = new JarSignerConf();
newConfig.components = new HashMap<>();
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
authLimiterComponent.rateLimit = 3;
authLimiterComponent.rateLimitMillis = 8000;
authLimiterComponent.message = "Превышен лимит авторизаций";
newConfig.components.put("authLimiter", authLimiterComponent);
RegLimiterComponent regLimiterComponent = new RegLimiterComponent();
regLimiterComponent.rateLimit = 3;
regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов
regLimiterComponent.message = "Превышен лимит регистраций";
newConfig.components.put("regLimiter", regLimiterComponent);
newConfig.netty.sendExceptionEnabled = true;
return newConfig;
}
} }

View file

@ -6,14 +6,24 @@
public interface User { public interface User {
String getUsername(); String getUsername();
ClientPermissions getPermissions(); ClientPermissions getPermissions();
void setPermissions(ClientPermissions permissions); void setPermissions(ClientPermissions permissions);
boolean verifyPassword(String password); boolean verifyPassword(String password);
void setPassword(String password); void setPassword(String password);
String getAccessToken(); String getAccessToken();
void setAccessToken(String accessToken); void setAccessToken(String accessToken);
String getServerID(); String getServerID();
void setServerID(String serverID); void setServerID(String serverID);
UUID getUuid(); UUID getUuid();
void setUuid(UUID uuid); void setUuid(UUID uuid);
} }

View file

@ -15,21 +15,21 @@
@Entity(name = "User") @Entity(name = "User")
@Table(name = "users") @Table(name = "users")
public class UserHibernateImpl implements User { public class UserHibernateImpl implements User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(unique = true) @Column(unique = true)
public String username; public String username;
public String email; public String email;
@Column(unique = true) @Column(unique = true)
public UUID uuid; public UUID uuid;
public String serverID;
public long permissions;
public long flags;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "password") @Column(name = "password")
private byte[] password; private byte[] password;
private String accessToken; private String accessToken;
public String serverID;
private String password_salt; private String password_salt;
public long permissions;
public long flags;
public void setPassword(String password) { public void setPassword(String password) {
password_salt = SecurityHelper.randomStringAESKey(); password_salt = SecurityHelper.randomStringAESKey();

View file

@ -6,8 +6,8 @@
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.Reconfigurable; import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.dao.User; import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.dao.impl.UserHibernateImpl;
import pro.gravit.launchserver.dao.impl.HibernateUserDAOImpl; import pro.gravit.launchserver.dao.impl.HibernateUserDAOImpl;
import pro.gravit.launchserver.dao.impl.UserHibernateImpl;
import pro.gravit.utils.command.Command; import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.SubCommand; import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.CommonHelper;

View file

@ -27,12 +27,94 @@
public class SignHelper { public class SignHelper {
public static final OutputStream NULL = new OutputStream() {
@Override
public String toString() {
return "NullOutputStream";
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b) {
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b, int off, int len) {
}
/** Discards the specified byte. */
@Override
public void write(int b) {
}
/** Never closes */
@Override
public void close() {
}
};
public static final String hashFunctionName = "SHA-256";
private SignHelper() {
}
/**
* Creates the KeyStore with given algo.
*/
public static KeyStore getStore(Path file, String storepass, String algo) throws IOException {
try {
KeyStore st = KeyStore.getInstance(algo);
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
return st;
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
throw new IOException(e);
}
}
/**
* Creates the beast that can actually sign the data (for JKS, for other make it).
*/
public static CMSSignedDataGenerator createSignedDataGenerator(KeyStore keyStore, String keyAlias, String signAlgo, String keyPassword) throws KeyStoreException, OperatorCreationException, CertificateEncodingException, UnrecoverableKeyException, NoSuchAlgorithmException, CMSException {
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
@SuppressWarnings("rawtypes")
Store certStore = new JcaCertStore(certChain);
Certificate cert = keyStore.getCertificate(keyAlias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword != null ? keyPassword.toCharArray() : null);
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
public static CMSSignedDataGenerator createSignedDataGenerator(PrivateKey privateKey, Certificate cert, List<Certificate> certChain, String signAlgo) throws OperatorCreationException, CertificateEncodingException, CMSException {
@SuppressWarnings("rawtypes")
Store certStore = new JcaCertStore(certChain);
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
public static MessageDigest hasher() {
try {
return MessageDigest.getInstance(hashFunctionName);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
/** /**
* Helper output stream that also sends the data to the given. * Helper output stream that also sends the data to the given.
*/ */
public static class HashingOutputStream extends OutputStream { public static class HashingOutputStream extends OutputStream {
public final OutputStream out; public final OutputStream out;
public final MessageDigest hasher; public final MessageDigest hasher;
public HashingOutputStream(OutputStream out, MessageDigest hasher) { public HashingOutputStream(OutputStream out, MessageDigest hasher) {
this.out = out; this.out = out;
@ -68,9 +150,10 @@ public void write(int b) throws IOException {
} }
public byte[] digest() { public byte[] digest() {
return hasher.digest(); return hasher.digest();
} }
} }
/** /**
* Helper output stream that also sends the data to the given. * Helper output stream that also sends the data to the given.
*/ */
@ -81,88 +164,7 @@ public HashingNonClosingOutputStream(OutputStream out, MessageDigest hasher) {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
// Do nothing // Do nothing
}
}
public static final OutputStream NULL = new OutputStream() {
@Override
public String toString() {
return "NullOutputStream";
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b) {
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b, int off, int len) {
}
/** Discards the specified byte. */
@Override
public void write(int b) {
}
/** Never closes */
@Override
public void close() {
}
};
private SignHelper() {
}
/**
* Creates the KeyStore with given algo.
*/
public static KeyStore getStore(Path file, String storepass, String algo) throws IOException {
try {
KeyStore st = KeyStore.getInstance(algo);
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
return st;
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
throw new IOException(e);
}
}
/**
* Creates the beast that can actually sign the data (for JKS, for other make it).
*/
public static CMSSignedDataGenerator createSignedDataGenerator(KeyStore keyStore, String keyAlias, String signAlgo, String keyPassword) throws KeyStoreException, OperatorCreationException, CertificateEncodingException, UnrecoverableKeyException, NoSuchAlgorithmException, CMSException {
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
@SuppressWarnings("rawtypes")
Store certStore = new JcaCertStore(certChain);
Certificate cert = keyStore.getCertificate(keyAlias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyPassword != null ? keyPassword.toCharArray() : null);
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
public static CMSSignedDataGenerator createSignedDataGenerator(PrivateKey privateKey, Certificate cert, List<Certificate> certChain, String signAlgo) throws OperatorCreationException, CertificateEncodingException, CMSException {
@SuppressWarnings("rawtypes")
Store certStore = new JcaCertStore(certChain);
ContentSigner signer = new JcaContentSignerBuilder(signAlgo).setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
public static final String hashFunctionName = "SHA-256";
public static MessageDigest hasher() {
try {
return MessageDigest.getInstance(hashFunctionName);
} catch (NoSuchAlgorithmException e) {
return null;
} }
} }
} }

View file

@ -0,0 +1,18 @@
package pro.gravit.launchserver.launchermodules;
import java.net.URL;
import java.net.URLClassLoader;
public class LauncherModuleClassLoader extends URLClassLoader {
public LauncherModuleClassLoader(ClassLoader parent) {
super(new URL[0], parent);
}
public void addURL(URL u) {
super.addURL(u);
}
public Class<?> rawDefineClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}

View file

@ -0,0 +1,143 @@
package pro.gravit.launchserver.launchermodules;
import pro.gravit.launcher.Launcher;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
public class LauncherModuleLoader {
public final List<ModuleEntity> launcherModules = new ArrayList<>();
public final Path modulesDir;
private final LaunchServer server;
public LauncherModuleLoader(LaunchServer server) {
this.server = server; modulesDir = server.dir.resolve("launcher-modules");
}
public void init() {
if (!IOHelper.isDir(modulesDir)) {
try {
Files.createDirectories(modulesDir);
} catch (IOException e) {
LogHelper.error(e);
}
}
server.commandHandler.registerCommand("syncLauncherModules", new SyncLauncherModulesCommand(this));
MainBuildTask mainTask = server.launcherBinary.getTaskByClass(MainBuildTask.class).get();
mainTask.preBuildHook.registerHook((buildContext) -> {
for (ModuleEntity e : launcherModules) {
if (e.propertyMap != null) buildContext.task.properties.putAll(e.propertyMap);
buildContext.clientModules.add(e.moduleMainClass);
buildContext.readerClassPath.add(new JarFile(e.path.toFile()));
}
});
mainTask.postBuildHook.registerHook((buildContext) -> {
for (ModuleEntity e : launcherModules) {
LogHelper.debug("Put %s launcher module", e.path.toString());
buildContext.pushJarFile(e.path, (en) -> false, (en) -> true);
}
});
try {
syncModules();
} catch (IOException e) {
LogHelper.error(e);
}
}
public void syncModules() throws IOException {
launcherModules.clear();
IOHelper.walk(modulesDir, new ModulesVisitor(), false);
}
static class ModuleEntity {
public Path path;
public String moduleMainClass;
public String moduleConfigClass;
public String moduleConfigName;
public Map<String, Object> propertyMap;
}
protected final class ModulesVisitor extends SimpleFileVisitor<Path> {
private LauncherModuleClassLoader classLoader;
private ModulesVisitor() {
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar"))
try (JarFile f = new JarFile(file.toFile())) {
Attributes attributes = f.getManifest().getMainAttributes();
String mainClass = attributes.getValue("Module-Main-Class");
if (mainClass == null) {
LogHelper.error("In module %s MainClass not found", file.toString());
} else {
ModuleEntity entity = new ModuleEntity();
entity.path = file;
entity.moduleMainClass = mainClass;
entity.moduleConfigClass = attributes.getValue("Module-Config-Class");
if (entity.moduleConfigClass != null) {
entity.moduleConfigName = attributes.getValue("Module-Config-Name");
if (entity.moduleConfigName == null) {
LogHelper.warning("Module-Config-Name in module %s null. Module not configured", file.toString());
} else {
try {
if (classLoader == null)
classLoader = new LauncherModuleClassLoader(server.modulesManager.getModuleClassLoader());
classLoader.addURL(file.toUri().toURL());
Class<?> clazz = classLoader.loadClass(entity.moduleConfigClass);
Path configPath = server.modulesManager.getConfigManager().getModuleConfig(entity.moduleConfigName);
Object defaultConfig = MethodHandles.publicLookup().findStatic(clazz, "getDefault", MethodType.methodType(Object.class)).invoke();
Object targetConfig;
if (!Files.exists(configPath)) {
LogHelper.debug("Write default config for module %s to %s", file.toString(), configPath.toString());
try (Writer writer = IOHelper.newWriter(configPath)) {
Launcher.gsonManager.configGson.toJson(defaultConfig, writer);
}
targetConfig = defaultConfig;
} else {
try (Reader reader = IOHelper.newReader(configPath)) {
targetConfig = Launcher.gsonManager.configGson.fromJson(reader, clazz);
}
}
Field[] fields = clazz.getFields();
for (Field field : fields) {
if ((field.getModifiers() & Modifier.STATIC) != 0) continue;
Object obj = field.get(targetConfig);
String configPropertyName = "modules.".concat(entity.moduleConfigName.toLowerCase()).concat(".").concat(field.getName().toLowerCase());
if (entity.propertyMap == null) entity.propertyMap = new HashMap<>();
LogHelper.dev("Property name %s", configPropertyName);
entity.propertyMap.put(configPropertyName, obj);
}
} catch (Throwable e) {
LogHelper.error(e);
}
}
}
launcherModules.add(entity);
}
}
return super.visitFile(file, attrs);
}
}
}

View file

@ -0,0 +1,29 @@
package pro.gravit.launchserver.launchermodules;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
public class SyncLauncherModulesCommand extends Command {
private final LauncherModuleLoader mod;
public SyncLauncherModulesCommand(LauncherModuleLoader mod) {
this.mod = mod;
}
@Override
public String getArgsDescription() {
return "Resync launcher modules";
}
@Override
public String getUsageDescription() {
return "[]";
}
@Override
public void invoke(String... args) throws Exception {
mod.syncModules();
LogHelper.info("Launcher Modules synced");
}
}

View file

@ -20,11 +20,11 @@
import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader; import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter; import org.bouncycastle.util.io.pem.PemWriter;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import pro.gravit.launcher.LauncherTrustManager;
import java.io.*; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
@ -48,17 +48,13 @@
import java.util.List; import java.util.List;
public class CertificateManager { public class CertificateManager {
public X509CertificateHolder ca;
public AsymmetricKeyParameter caKey;
public X509CertificateHolder server;
public AsymmetricKeyParameter serverKey;
public LauncherTrustManager trustManager;
public final int validDays = 60; public final int validDays = 60;
public final int minusHours = 6; public final int minusHours = 6;
public X509CertificateHolder ca;
public AsymmetricKeyParameter caKey;
public X509CertificateHolder server;
public AsymmetricKeyParameter serverKey;
public LauncherTrustManager trustManager;
public String orgName; public String orgName;
public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException { public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException {

View file

@ -14,26 +14,6 @@
import java.util.Arrays; import java.util.Arrays;
public class MirrorManager { public class MirrorManager {
public static class Mirror {
final String baseUrl;
boolean enabled;
Mirror(String url) {
//assetsURLMask = url.concat("assets/%s.zip");
//clientsURLMask = url.concat("clients/%s.zip");
baseUrl = url;
}
private URL formatArgs(String mask, Object... args) throws MalformedURLException {
Object[] data = Arrays.stream(args).map(e -> IOHelper.urlEncode(e.toString())).toArray();
return new URL(baseUrl.concat(String.format(mask, data)));
}
public URL getURL(String mask, Object... args) throws MalformedURLException {
return formatArgs(mask, args);
}
}
protected final ArrayList<Mirror> list = new ArrayList<>(); protected final ArrayList<Mirror> list = new ArrayList<>();
private Mirror defaultMirror; private Mirror defaultMirror;
@ -118,4 +98,24 @@ public JsonElement jsonRequest(JsonElement request, String method, String mask,
} }
throw new IOException("Error jsonRequest. All mirrors return error"); throw new IOException("Error jsonRequest. All mirrors return error");
} }
public static class Mirror {
final String baseUrl;
boolean enabled;
Mirror(String url) {
//assetsURLMask = url.concat("assets/%s.zip");
//clientsURLMask = url.concat("clients/%s.zip");
baseUrl = url;
}
private URL formatArgs(String mask, Object... args) throws MalformedURLException {
Object[] data = Arrays.stream(args).map(e -> IOHelper.urlEncode(e.toString())).toArray();
return new URL(baseUrl.concat(String.format(mask, data)));
}
public URL getURL(String mask, Object... args) throws MalformedURLException {
return formatArgs(mask, args);
}
}
} }

View file

@ -10,27 +10,6 @@
import java.util.Map; import java.util.Map;
public class ReconfigurableManager { public class ReconfigurableManager {
private static class ReconfigurableVirtualCommand extends Command {
public ReconfigurableVirtualCommand(Map<String, Command> childs) {
super(childs);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return null;
}
@Override
public void invoke(String... args) throws Exception {
invokeSubcommands(args);
}
}
private final HashMap<String, Command> RECONFIGURABLE = new HashMap<>(); private final HashMap<String, Command> RECONFIGURABLE = new HashMap<>();
public void registerReconfigurable(String name, Reconfigurable reconfigurable) { public void registerReconfigurable(String name, Reconfigurable reconfigurable) {
@ -59,4 +38,25 @@ public void printHelp(String name) throws CommandException {
public Map<String, Command> getCommands() { public Map<String, Command> getCommands() {
return RECONFIGURABLE; return RECONFIGURABLE;
} }
private static class ReconfigurableVirtualCommand extends Command {
public ReconfigurableVirtualCommand(Map<String, Command> childs) {
super(childs);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return null;
}
@Override
public void invoke(String... args) throws Exception {
invokeSubcommands(args);
}
}
} }

View file

@ -40,8 +40,7 @@ public Client getOrNewClient(long session) {
return clientSet.computeIfAbsent(session, Client::new); return clientSet.computeIfAbsent(session, Client::new);
} }
public Client removeClient(long session) public Client removeClient(long session) {
{
return clientSet.remove(session); return clientSet.remove(session);
} }

View file

@ -14,6 +14,7 @@ public class AuthHookManager {
public final BiHookSet<CheckServerResponse, Client> checkServerHook = new BiHookSet<>(); public final BiHookSet<CheckServerResponse, Client> checkServerHook = new BiHookSet<>();
public final BiHookSet<JoinServerResponse, Client> joinServerHook = new BiHookSet<>(); public final BiHookSet<JoinServerResponse, Client> joinServerHook = new BiHookSet<>();
public final BiHookSet<SetProfileResponse, Client> setProfileHook = new BiHookSet<>(); public final BiHookSet<SetProfileResponse, Client> setProfileHook = new BiHookSet<>();
public final HookSet<RegContext> registraion = new HookSet<>();
public static class RegContext { public static class RegContext {
public final String login; public final String login;
@ -28,6 +29,4 @@ public RegContext(String login, String password, String ip, boolean trustContext
this.trustContext = trustContext; this.trustContext = trustContext;
} }
} }
public final HookSet<RegContext> registraion = new HookSet<>();
} }

View file

@ -1,11 +1,11 @@
package pro.gravit.launchserver.modules.impl; package pro.gravit.launchserver.modules.impl;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launcher.modules.LauncherModuleInfo; import pro.gravit.launcher.modules.LauncherModuleInfo;
import pro.gravit.launcher.modules.impl.SimpleModuleManager; import pro.gravit.launcher.modules.impl.SimpleModuleManager;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.launcher.LauncherTrustManager;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;

View file

@ -47,8 +47,8 @@ public enum Type {
SERVER, SERVER,
USER USER
} }
public static class TrustLevel
{ public static class TrustLevel {
public byte[] verifySecureKey; public byte[] verifySecureKey;
public boolean keyChecked; public boolean keyChecked;
public byte[] publicKey; public byte[] publicKey;

View file

@ -25,12 +25,12 @@
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
public class LauncherNettyServer implements AutoCloseable { public class LauncherNettyServer implements AutoCloseable {
private static final String WEBSOCKET_PATH = "/api";
public final ServerBootstrap serverBootstrap; public final ServerBootstrap serverBootstrap;
public final EventLoopGroup bossGroup; public final EventLoopGroup bossGroup;
public final EventLoopGroup workerGroup; public final EventLoopGroup workerGroup;
public final WebSocketService service; public final WebSocketService service;
public final BiHookSet<NettyConnectContext, SocketChannel> pipelineHook = new BiHookSet<>(); public final BiHookSet<NettyConnectContext, SocketChannel> pipelineHook = new BiHookSet<>();
private static final String WEBSOCKET_PATH = "/api";
public LauncherNettyServer(LaunchServer server) { public LauncherNettyServer(LaunchServer server) {
LaunchServerConfig.NettyConfig config = server.config.netty; LaunchServerConfig.NettyConfig config = server.config.netty;

View file

@ -22,6 +22,7 @@
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse; import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUsername; import pro.gravit.launchserver.socket.response.profile.ProfileByUsername;
import pro.gravit.launchserver.socket.response.secure.GetSecureLevelInfoResponse; import pro.gravit.launchserver.socket.response.secure.GetSecureLevelInfoResponse;
import pro.gravit.launchserver.socket.response.secure.HardwareReportResponse;
import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse; import pro.gravit.launchserver.socket.response.secure.SecurityReportResponse;
import pro.gravit.launchserver.socket.response.secure.VerifySecureLevelKeyResponse; import pro.gravit.launchserver.socket.response.secure.VerifySecureLevelKeyResponse;
import pro.gravit.launchserver.socket.response.update.LauncherResponse; import pro.gravit.launchserver.socket.response.update.LauncherResponse;
@ -35,22 +36,11 @@
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class WebSocketService { public class WebSocketService {
public final ChannelGroup channels;
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>(); public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
public final ChannelGroup channels;
public static class WebSocketRequestContext {
public final WebSocketServerResponse response;
public final Client client;
public final String ip;
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
this.response = response;
this.client = client;
this.ip = ip;
}
}
public final BiHookSet<WebSocketRequestContext, ChannelHandlerContext> hook = new BiHookSet<>(); public final BiHookSet<WebSocketRequestContext, ChannelHandlerContext> hook = new BiHookSet<>();
private final LaunchServer server;
private final Gson gson;
public WebSocketService(ChannelGroup channels, LaunchServer server) { public WebSocketService(ChannelGroup channels, LaunchServer server) {
this.channels = channels; this.channels = channels;
@ -61,15 +51,36 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
this.gson = Launcher.gsonManager.gson; this.gson = Launcher.gsonManager.gson;
} }
private final LaunchServer server; public static void registerResponses() {
private final Gson gson; providers.register("auth", AuthResponse.class);
providers.register("checkServer", CheckServerResponse.class);
providers.register("joinServer", JoinServerResponse.class);
providers.register("profiles", ProfilesResponse.class);
providers.register("launcher", LauncherResponse.class);
providers.register("updateList", UpdateListResponse.class);
providers.register("cmdExec", ExecCommandResponse.class);
providers.register("setProfile", SetProfileResponse.class);
providers.register("addLogListener", AddLogListenerResponse.class);
providers.register("update", UpdateResponse.class);
providers.register("restoreSession", RestoreSessionResponse.class);
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("register", RegisterResponse.class);
providers.register("setPassword", SetPasswordResponse.class);
providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
providers.register("securityReport", SecurityReportResponse.class);
providers.register("hardwareReport", HardwareReportResponse.class);
}
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) { public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
String request = frame.text(); String request = frame.text();
WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class); WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class);
if(response == null) if (response == null) {
{ RequestEvent event = new ErrorRequestEvent("This type of request is not supported");
RequestEvent event= new ErrorRequestEvent("This type of request is not supported");
sendObject(ctx, event); sendObject(ctx, event);
} }
process(ctx, response, client, ip); process(ctx, response, client, ip);
@ -109,30 +120,6 @@ public void registerClient(Channel channel) {
channels.add(channel); channels.add(channel);
} }
public static void registerResponses() {
providers.register("auth", AuthResponse.class);
providers.register("checkServer", CheckServerResponse.class);
providers.register("joinServer", JoinServerResponse.class);
providers.register("profiles", ProfilesResponse.class);
providers.register("launcher", LauncherResponse.class);
providers.register("updateList", UpdateListResponse.class);
providers.register("cmdExec", ExecCommandResponse.class);
providers.register("setProfile", SetProfileResponse.class);
providers.register("addLogListener", AddLogListenerResponse.class);
providers.register("update", UpdateResponse.class);
providers.register("restoreSession", RestoreSessionResponse.class);
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("register", RegisterResponse.class);
providers.register("setPassword", SetPasswordResponse.class);
providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
providers.register("securityReport", SecurityReportResponse.class);
}
public void sendObject(ChannelHandlerContext ctx, Object obj) { public void sendObject(ChannelHandlerContext ctx, Object obj) {
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ctx.voidPromise()); ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ctx.voidPromise());
} }
@ -173,6 +160,18 @@ public void sendEvent(EventResult obj) {
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true); channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true);
} }
public static class WebSocketRequestContext {
public final WebSocketServerResponse response;
public final Client client;
public final String ip;
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
this.response = response;
this.client = client;
this.ip = ip;
}
}
public static class EventResult implements WebSocketEvent { public static class EventResult implements WebSocketEvent {
public EventResult() { public EventResult() {

View file

@ -4,29 +4,30 @@
import java.nio.file.Files; import java.nio.file.Files;
public enum ContentType { public enum ContentType {
NONE { NONE {
@Override @Override
public String forPath(File p) { public String forPath(File p) {
return null; return null;
} }
}, },
NIO { NIO {
@Override @Override
public String forPath(File p) { public String forPath(File p) {
try { try {
return Files.probeContentType(p.toPath()); return Files.probeContentType(p.toPath());
} catch (Throwable e) { } catch (Throwable e) {
return UNIVERSAL.forPath(p); return UNIVERSAL.forPath(p);
} }
} }
}, },
UNIVERSAL { UNIVERSAL {
@Override @Override
public String forPath(File p) { public String forPath(File p) {
return "application/octet-stream"; return "application/octet-stream";
} }
}; };
public abstract String forPath(File p);
public abstract String forPath(File p);
} }

View file

@ -7,21 +7,15 @@
import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLServerSocketFactory;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Set;
// TODO refactor
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class NettyServerSocketHandler implements Runnable, AutoCloseable { public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
private SSLServerSocketFactory ssf; private transient final LaunchServer server;
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections"); public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
public LauncherNettyServer nettyServer; public LauncherNettyServer nettyServer;
private SSLServerSocketFactory ssf;
// API
private Set<Socket> sockets;
private transient final LaunchServer server;
public NettyServerSocketHandler(LaunchServer server) { public NettyServerSocketHandler(LaunchServer server) {
this.server = server; this.server = server;
@ -29,7 +23,9 @@ public NettyServerSocketHandler(LaunchServer server) {
@Override @Override
public void close() { public void close() {
//TODO: Close Impl if (nettyServer == null) return;
nettyServer.close();
nettyServer.service.channels.close();
} }
@Override @Override

View file

@ -15,9 +15,14 @@
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> { public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
static {
}
public final LaunchServer srv; public final LaunchServer srv;
public final WebSocketService service; public final WebSocketService service;
private final UUID connectUUID = UUID.randomUUID();
public NettyConnectContext context; public NettyConnectContext context;
private Client client;
public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) { public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) {
this.context = context; this.context = context;
@ -25,20 +30,14 @@ public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebS
this.service = service; this.service = service;
} }
private Client client; public Client getClient() {
private final UUID connectUUID = UUID.randomUUID(); return client;
static {
} }
public void setClient(Client client) { public void setClient(Client client) {
this.client = client; this.client = client;
} }
public Client getClient() {
return client;
}
public final UUID getConnectUUID() { public final UUID getConnectUUID() {
return connectUUID; return connectUUID;
} }

View file

@ -28,13 +28,16 @@ public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpReque
public static final SimpleDateFormat dateFormatter; public static final SimpleDateFormat dateFormatter;
public static final String READ = "r"; public static final String READ = "r";
public static final int HTTP_CACHE_SECONDS = VerifyHelper.verifyInt(Integer.parseInt(System.getProperty("launcher.fileserver.cachesec", "60")), VerifyHelper.NOT_NEGATIVE, "HttpCache seconds should be positive");
private static final boolean OLD_ALGO = Boolean.parseBoolean(System.getProperty("launcher.fileserver.oldalgo", "true"));
private static final ContentType TYPE_PROBE = Arrays.stream(ContentType.values()).filter(e -> e.name().toLowerCase(Locale.US).equals(System.getProperty("launcher.fileserver.typeprobe", "nio"))).findFirst().orElse(ContentType.UNIVERSAL);
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
static { static {
dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT")); dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
} }
public static final int HTTP_CACHE_SECONDS = VerifyHelper.verifyInt(Integer.parseInt(System.getProperty("launcher.fileserver.cachesec", "60")), VerifyHelper.NOT_NEGATIVE, "HttpCache seconds should be positive");
private static final boolean OLD_ALGO = Boolean.parseBoolean(System.getProperty("launcher.fileserver.oldalgo", "true"));
private static final ContentType TYPE_PROBE = Arrays.stream(ContentType.values()).filter(e -> e.name().toLowerCase(Locale.US).equals(System.getProperty("launcher.fileserver.typeprobe", "nio"))).findFirst().orElse(ContentType.UNIVERSAL);
private final Path base; private final Path base;
private final boolean fullOut; private final boolean fullOut;
private final boolean showHiddenFiles; private final boolean showHiddenFiles;
@ -45,6 +48,120 @@ public FileServerHandler(Path base, boolean fullOut, boolean showHiddenFiles) {
this.showHiddenFiles = showHiddenFiles; this.showHiddenFiles = showHiddenFiles;
} }
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean showHidden) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
StringBuilder buf = new StringBuilder()
.append("<!DOCTYPE html>\r\n")
.append("<html><head><meta charset='utf-8' /><title>")
.append("Listing of: ")
.append(dirPath)
.append("</title></head><body>\r\n")
.append("<h3>Listing of: ")
.append(dirPath)
.append("</h3>\r\n")
.append("<ul>")
.append("<li><a href=\"../\">..</a></li>\r\n");
for (File f : dir.listFiles()) {
if ((f.isHidden() && !showHidden) || !f.canRead()) {
continue;
}
String name = f.getName();
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
continue;
}
buf.append("<li><a href=\"")
.append(name)
.append("\">")
.append(name)
.append("</a></li>\r\n");
}
buf.append("</ul></body></html>\r\n");
ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
response.content().writeBytes(buffer);
buffer.release();
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
response.headers().set(HttpHeaderNames.LOCATION, newUri);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
*
* @param ctx Context
*/
private static void sendNotModified(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
setDateHeader(response);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* Sets the Date header for the HTTP response
*
* @param response HTTP response
*/
private static void setDateHeader(FullHttpResponse response) {
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(new Date(System.currentTimeMillis())));
}
/**
* Sets the Date and Cache headers for the HTTP Response
*
* @param response HTTP response
* @param fileToCache file to extract content type
*/
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
// Date header
Calendar time = new GregorianCalendar();
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
// Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.headers().set(
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
}
/**
* Sets the content type header for the HTTP Response
*
* @param response HTTP response
* @param file file to extract content type
*/
private static void setContentTypeHeader(HttpResponse response, File file) {
String contentType = TYPE_PROBE.forPath(file);
if (contentType != null)
response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
}
@Override @Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (!request.decoderResult().isSuccess()) { if (!request.decoderResult().isSuccess()) {
@ -154,120 +271,4 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
sendError(ctx, INTERNAL_SERVER_ERROR); sendError(ctx, INTERNAL_SERVER_ERROR);
} }
} }
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean showHidden) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
StringBuilder buf = new StringBuilder()
.append("<!DOCTYPE html>\r\n")
.append("<html><head><meta charset='utf-8' /><title>")
.append("Listing of: ")
.append(dirPath)
.append("</title></head><body>\r\n")
.append("<h3>Listing of: ")
.append(dirPath)
.append("</h3>\r\n")
.append("<ul>")
.append("<li><a href=\"../\">..</a></li>\r\n");
for (File f : dir.listFiles()) {
if ((f.isHidden() && !showHidden) || !f.canRead()) {
continue;
}
String name = f.getName();
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
continue;
}
buf.append("<li><a href=\"")
.append(name)
.append("\">")
.append(name)
.append("</a></li>\r\n");
}
buf.append("</ul></body></html>\r\n");
ByteBuf buffer = Unpooled.copiedBuffer(buf, CharsetUtil.UTF_8);
response.content().writeBytes(buffer);
buffer.release();
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
response.headers().set(HttpHeaderNames.LOCATION, newUri);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
*
* @param ctx Context
*/
private static void sendNotModified(ChannelHandlerContext ctx) {
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
setDateHeader(response);
// Close the connection as soon as the error message is sent.
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
/**
* Sets the Date header for the HTTP response
*
* @param response HTTP response
*/
private static void setDateHeader(FullHttpResponse response) {
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(new Date(System.currentTimeMillis())));
}
/**
* Sets the Date and Cache headers for the HTTP Response
*
* @param response HTTP response
* @param fileToCache file to extract content type
*/
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
// Date header
Calendar time = new GregorianCalendar();
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
// Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.headers().set(
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
}
/**
* Sets the content type header for the HTTP Response
*
* @param response HTTP response
* @param file file to extract content type
*/
private static void setContentTypeHeader(HttpResponse response, File file) {
String contentType = TYPE_PROBE.forPath(file);
if (contentType != null)
response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
}
} }

View file

@ -2,7 +2,6 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthECPassword; import pro.gravit.launcher.request.auth.password.AuthECPassword;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword; import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
@ -22,7 +21,6 @@
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
@ -37,13 +35,6 @@ public class AuthResponse extends SimpleResponse {
public String auth_id; public String auth_id;
public ConnectTypes authType; public ConnectTypes authType;
public enum ConnectTypes {
@Deprecated
SERVER,
CLIENT,
API
}
@Override @Override
public String getType() { public String getType() {
return "auth"; return "auth";
@ -68,8 +59,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
AuthProviderPair pair; AuthProviderPair pair;
if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair(); if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
else pair = server.config.getAuthProviderPair(auth_id); else pair = server.config.getAuthProviderPair(auth_id);
if(pair == null) if (pair == null) {
{
sendError("auth_id incorrect"); sendError("auth_id incorrect");
return; return;
} }
@ -117,7 +107,22 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
} }
} }
public enum ConnectTypes {
@Deprecated
SERVER,
CLIENT,
API
}
public static class AuthContext { public static class AuthContext {
public final String login;
public final String profileName;
public final String ip;
public final ConnectTypes authType;
public final Client client;
@Deprecated
public int password_length; //Use AuthProvider for get password
public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType) { public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType) {
this.client = client; this.client = client;
this.login = login; this.login = login;
@ -125,13 +130,5 @@ public AuthContext(Client client, String login, String profileName, String ip, C
this.ip = ip; this.ip = ip;
this.authType = authType; this.authType = authType;
} }
public final String login;
@Deprecated
public int password_length; //Use AuthProvider for get password
public final String profileName;
public final String ip;
public final ConnectTypes authType;
public final Client client;
} }
} }

View file

@ -21,8 +21,7 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client pClient) { public void execute(ChannelHandlerContext ctx, Client pClient) {
if(!pClient.isAuth || pClient.type == AuthResponse.ConnectTypes.CLIENT) if (!pClient.isAuth || pClient.type == AuthResponse.ConnectTypes.CLIENT) {
{
sendError("Permissions denied"); sendError("Permissions denied");
return; return;
} }

View file

@ -11,6 +11,7 @@
public class ExitResponse extends SimpleResponse { public class ExitResponse extends SimpleResponse {
public boolean exitAll; public boolean exitAll;
public String username; public String username;
@Override @Override
public String getType() { public String getType() {
return "exit"; return "exit";
@ -18,66 +19,56 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(username != null && ( !client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN) )) if (username != null && (!client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN))) {
{
sendError("Permissions denied"); sendError("Permissions denied");
return; return;
} }
if(username == null) if (username == null) {
{ if (client.session == 0 && exitAll) {
if(client.session == 0 && exitAll)
{
sendError("Session invalid"); sendError("Session invalid");
return; return;
} }
WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler handler = ctx.pipeline().get(WebSocketFrameHandler.class);
if(handler == null) if (handler == null) {
{
sendError("Exit internal error"); sendError("Exit internal error");
return; return;
} }
Client newClient = new Client(0); Client newClient = new Client(0);
newClient.checkSign = client.checkSign; newClient.checkSign = client.checkSign;
handler.setClient(newClient); handler.setClient(newClient);
if(client.session != 0) server.sessionManager.removeClient(client.session); if (client.session != 0) server.sessionManager.removeClient(client.session);
if(exitAll) if (exitAll) {
{
service.channels.forEach((channel) -> { service.channels.forEach((channel) -> {
if(channel == null || channel.pipeline() == null) return; if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if(wsHandler == null || wsHandler == handler) return; if (wsHandler == null || wsHandler == handler) return;
Client chClient = wsHandler.getClient(); Client chClient = wsHandler.getClient();
if(client.isAuth && client.username != null) if (client.isAuth && client.username != null) {
{ if (!chClient.isAuth || !client.username.equals(chClient.username)) return;
if(!chClient.isAuth || !client.username.equals(chClient.username)) return; } else {
} if (chClient.session != client.session) return;
else
{
if(chClient.session != client.session) return;
} }
Client newCusClient = new Client(0); Client newCusClient = new Client(0);
newCusClient.checkSign = chClient.checkSign; newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient); wsHandler.setClient(newCusClient);
if(chClient.session != 0) server.sessionManager.removeClient(chClient.session); if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER); ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
event.requestUUID = RequestEvent.eventUUID; event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event); wsHandler.service.sendObject(channel, event);
}); });
} }
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
} } else {
else
{
service.channels.forEach((channel -> { service.channels.forEach((channel -> {
if(channel == null || channel.pipeline() == null) return; if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if(wsHandler == null) return; if (wsHandler == null) return;
Client chClient = wsHandler.getClient(); Client chClient = wsHandler.getClient();
if(!chClient.isAuth || !username.equals(chClient.username)) return; if (!chClient.isAuth || !username.equals(chClient.username)) return;
Client newCusClient = new Client(0); Client newCusClient = new Client(0);
newCusClient.checkSign = chClient.checkSign; newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient); wsHandler.setClient(newCusClient);
if(chClient.session != 0) server.sessionManager.removeClient(chClient.session); if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER); ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
event.requestUUID = RequestEvent.eventUUID; event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event); wsHandler.service.sendObject(channel, event);

View file

@ -20,8 +20,7 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) { public void execute(ChannelHandlerContext ctx, Client client) {
if(!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) if (!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) {
{
sendError("Permissions denied"); sendError("Permissions denied");
return; return;
} }

View file

@ -1,7 +1,6 @@
package pro.gravit.launchserver.socket.response.auth; package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.ErrorRequestEvent;
import pro.gravit.launcher.events.request.ProfilesRequestEvent; import pro.gravit.launcher.events.request.ProfilesRequestEvent;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
@ -26,20 +25,15 @@ public void execute(ChannelHandlerContext ctx, Client client) {
List<ClientProfile> profileList; List<ClientProfile> profileList;
List<ClientProfile> serverProfiles = server.getProfiles(); List<ClientProfile> serverProfiles = server.getProfiles();
if (server.config.protectHandler instanceof ProfilesProtectHandler) if (server.config.protectHandler instanceof ProfilesProtectHandler) {
{
ProfilesProtectHandler protectHandler = (ProfilesProtectHandler) server.config.protectHandler; ProfilesProtectHandler protectHandler = (ProfilesProtectHandler) server.config.protectHandler;
profileList = new ArrayList<>(4); profileList = new ArrayList<>(4);
for(ClientProfile profile : serverProfiles) for (ClientProfile profile : serverProfiles) {
{ if (protectHandler.canGetProfile(profile, client)) {
if(protectHandler.canGetProfile(profile, client))
{
profileList.add(profile); profileList.add(profile);
} }
} }
} } else {
else
{
profileList = serverProfiles; profileList = serverProfiles;
} }
sendResult(new ProfilesRequestEvent(profileList)); sendResult(new ProfilesRequestEvent(profileList));

View file

@ -19,6 +19,12 @@ public class RegisterResponse extends SimpleResponse {
public String email; public String email;
public byte[] verifyHash; public byte[] verifyHash;
public static byte[] registerHash(String login, String secret) throws NoSuchAlgorithmException {
String text = login.concat("+").concat(secret);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(text.getBytes(StandardCharsets.UTF_8));
}
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
byte[] normalHash = registerHash(login, server.runtime.registerApiKey); byte[] normalHash = registerHash(login, server.runtime.registerApiKey);
@ -43,10 +49,4 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
public String getType() { public String getType() {
return "register"; return "register";
} }
public static byte[] registerHash(String login, String secret) throws NoSuchAlgorithmException {
String text = login.concat("+").concat(secret);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(text.getBytes(StandardCharsets.UTF_8));
}
} }

View file

@ -10,11 +10,6 @@
import java.util.UUID; import java.util.UUID;
public class BatchProfileByUsername extends SimpleResponse { public class BatchProfileByUsername extends SimpleResponse {
static class Entry {
String username;
String client;
}
Entry[] list; Entry[] list;
@Override @Override
@ -36,4 +31,9 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
} }
sendResult(result); sendResult(result);
} }
static class Entry {
String username;
String client;
}
} }

View file

@ -14,21 +14,20 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!(server.config.protectHandler instanceof SecureProtectHandler)) if (!(server.config.protectHandler instanceof SecureProtectHandler)) {
{
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(null); GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(null);
response.enabled = false; response.enabled = false;
sendResult(response); sendResult(response);
return; return;
} }
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler; SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
if(!secureProtectHandler.allowGetSecureLevelInfo(client)) if (!secureProtectHandler.allowGetSecureLevelInfo(client)) {
{
sendError("Access denied"); sendError("Access denied");
return; return;
} }
if(client.trustLevel == null) client.trustLevel = new Client.TrustLevel(); if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
if(client.trustLevel.verifySecureKey == null) client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey(); if (client.trustLevel.verifySecureKey == null)
client.trustLevel.verifySecureKey = secureProtectHandler.generateSecureLevelKey();
GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(client.trustLevel.verifySecureKey); GetSecureLevelInfoRequestEvent response = new GetSecureLevelInfoRequestEvent(client.trustLevel.verifySecureKey);
response.enabled = true; response.enabled = true;
sendResult(secureProtectHandler.onGetSecureLevelInfo(response)); sendResult(secureProtectHandler.onGetSecureLevelInfo(response));

View file

@ -0,0 +1,20 @@
package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class HardwareReportResponse extends SimpleResponse {
public HardwareReportRequest.HardwareInfo hardware;
@Override
public String getType() {
return "hardwareReport";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
}
}

View file

@ -13,6 +13,7 @@ public class SecurityReportResponse extends SimpleResponse {
public String largeData; public String largeData;
public byte[] smallBytes; public byte[] smallBytes;
public byte[] largeBytes; public byte[] largeBytes;
@Override @Override
public String getType() { public String getType() {
return "securityReport"; return "securityReport";
@ -20,8 +21,7 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!(server.config.protectHandler instanceof SecureProtectHandler)) if (!(server.config.protectHandler instanceof SecureProtectHandler)) {
{
sendError("Method not allowed"); sendError("Method not allowed");
} }
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler; SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;

View file

@ -12,6 +12,7 @@
public class VerifySecureLevelKeyResponse extends SimpleResponse { public class VerifySecureLevelKeyResponse extends SimpleResponse {
public byte[] publicKey; public byte[] publicKey;
public byte[] signature; public byte[] signature;
@Override @Override
public String getType() { public String getType() {
return "verifySecureLevelKey"; return "verifySecureLevelKey";
@ -19,24 +20,20 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!(server.config.protectHandler instanceof SecureProtectHandler) || client.trustLevel == null || client.trustLevel.verifySecureKey == null) if (!(server.config.protectHandler instanceof SecureProtectHandler) || client.trustLevel == null || client.trustLevel.verifySecureKey == null) {
{
sendError("This method not allowed"); sendError("This method not allowed");
return; return;
} }
SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler; SecureProtectHandler secureProtectHandler = (SecureProtectHandler) server.config.protectHandler;
try { try {
secureProtectHandler.verifySecureLevelKey(publicKey, client.trustLevel.verifySecureKey, signature); secureProtectHandler.verifySecureLevelKey(publicKey, client.trustLevel.verifySecureKey, signature);
} catch (InvalidKeySpecException e) } catch (InvalidKeySpecException e) {
{
sendError("Invalid public key"); sendError("Invalid public key");
return; return;
} catch (SignatureException e) } catch (SignatureException e) {
{
sendError("Invalid signature"); sendError("Invalid signature");
return; return;
} catch (SecurityException e) } catch (SecurityException e) {
{
sendError(e.getMessage()); sendError(e.getMessage());
return; return;
} }

View file

@ -60,7 +60,6 @@ private boolean checkSecure(String hash, String salt) {
byte[] normal_hash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256, byte[] normal_hash = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
server.runtime.clientCheckSecret.concat(".").concat(salt)); server.runtime.clientCheckSecret.concat(".").concat(salt));
byte[] launcher_hash = Base64.getDecoder().decode(hash); byte[] launcher_hash = Base64.getDecoder().decode(hash);
//LogHelper.debug("[checkSecure] %s vs %s", Arrays.toString(normal_hash), Arrays.toString(launcher_hash));
return Arrays.equals(normal_hash, launcher_hash); return Arrays.equals(normal_hash, launcher_hash);
} }

View file

@ -3,12 +3,10 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.UpdateRequestEvent; import pro.gravit.launcher.events.request.UpdateRequestEvent;
import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler; import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
public class UpdateResponse extends SimpleResponse { public class UpdateResponse extends SimpleResponse {

View file

@ -12,40 +12,21 @@
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class ASMTransformersTest { public class ASMTransformersTest {
public static class ASMClassLoader extends ClassLoader
{
public ASMClassLoader(ClassLoader parent) {
super(parent);
}
public void rawDefineClass(String name, byte[] bytes, int offset, int length)
{
defineClass(name, bytes, offset, length);
}
}
public static ASMClassLoader classLoader; public static ASMClassLoader classLoader;
public static class TestClass
{
@LauncherInject(value = "testprop")
public int test;
@LauncherInject(value = "testprop2")
public List<String> s;
@LauncherInject(value = "testprop3")
public Map<String, String> map;
}
@BeforeAll @BeforeAll
public static void prepare() throws Throwable { public static void prepare() throws Throwable {
classLoader = new ASMClassLoader(ASMTransformersTest.class.getClassLoader()); classLoader = new ASMClassLoader(ASMTransformersTest.class.getClassLoader());
} }
@Test @Test
void testASM() throws Throwable void testASM() throws Throwable {
{
ClassReader reader = new ClassReader(JarHelper.getClassBytes(TestClass.class)); ClassReader reader = new ClassReader(JarHelper.getClassBytes(TestClass.class));
ClassNode node = new ClassNode(); ClassNode node = new ClassNode();
reader.accept(node, ClassReader.SKIP_DEBUG); reader.accept(node, ClassReader.SKIP_DEBUG);
@ -69,11 +50,30 @@ void testASM() throws Throwable
Class<?> clazz = classLoader.loadClass("ASMTestClass"); Class<?> clazz = classLoader.loadClass("ASMTestClass");
Object instance = MethodHandles.publicLookup().findConstructor(clazz, MethodType.methodType(void.class)).invoke(); Object instance = MethodHandles.publicLookup().findConstructor(clazz, MethodType.methodType(void.class)).invoke();
Assertions.assertEquals(1234, (int) Assertions.assertEquals(1234, (int)
MethodHandles.publicLookup().findGetter(clazz, "test", int.class).invoke(instance)); MethodHandles.publicLookup().findGetter(clazz, "test", int.class).invoke(instance));
Assertions.assertEquals(strings, (List<String>) Assertions.assertEquals(strings, (List<String>)
MethodHandles.publicLookup().findGetter(clazz, "s", List.class).invoke(instance)); MethodHandles.publicLookup().findGetter(clazz, "s", List.class).invoke(instance));
Assertions.assertEquals(byteMap, (Map<String, Object>) Assertions.assertEquals(byteMap, (Map<String, Object>)
MethodHandles.publicLookup().findGetter(clazz, "map", Map.class).invoke(instance)); MethodHandles.publicLookup().findGetter(clazz, "map", Map.class).invoke(instance));
} }
public static class ASMClassLoader extends ClassLoader {
public ASMClassLoader(ClassLoader parent) {
super(parent);
}
public void rawDefineClass(String name, byte[] bytes, int offset, int length) {
defineClass(name, bytes, offset, length);
}
}
public static class TestClass {
@LauncherInject(value = "testprop")
public int test;
@LauncherInject(value = "testprop2")
public List<String> s;
@LauncherInject(value = "testprop3")
public Map<String, String> map;
}
} }

View file

@ -1,5 +1,6 @@
package pro.gravit.launchserver; package pro.gravit.launchserver;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.io.TempDir;
@ -28,7 +29,7 @@ public class StartLaunchServerTest {
public static LaunchServer launchServer; public static LaunchServer launchServer;
@BeforeAll @BeforeAll
public static void prepare() throws Exception { public static void prepare() throws Throwable {
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir, null); LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir, null);
LaunchServerConfig config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST); LaunchServerConfig config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST);
Launcher.gsonManager = new LaunchServerGsonManager(modulesManager); Launcher.gsonManager = new LaunchServerGsonManager(modulesManager);
@ -48,12 +49,14 @@ public static void prepare() throws Exception {
.setLaunchServerConfigManager(new LaunchServer.LaunchServerConfigManager() { .setLaunchServerConfigManager(new LaunchServer.LaunchServerConfigManager() {
@Override @Override
public LaunchServerConfig readConfig() { public LaunchServerConfig readConfig() {
return null; return LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST);
} }
@Override @Override
public LaunchServerRuntimeConfig readRuntimeConfig() { public LaunchServerRuntimeConfig readRuntimeConfig() {
return null; LaunchServerRuntimeConfig r = new LaunchServerRuntimeConfig();
r.reset();
return r;
} }
@Override @Override
@ -71,6 +74,11 @@ public void writeRuntimeConfig(LaunchServerRuntimeConfig config) {
launchServer = builder.build(); launchServer = builder.build();
} }
@AfterAll
public static void complete() throws Throwable {
launchServer.close();
}
@Test @Test
public void start() { public void start() {
launchServer.run(); launchServer.run();

View file

@ -11,7 +11,7 @@
} }
javafx { javafx {
version = "12" version = "12"
modules = [ 'javafx.controls', 'javafx.fxml' ] modules = ['javafx.controls', 'javafx.fxml']
} }
sourceCompatibility = '1.8' sourceCompatibility = '1.8'
targetCompatibility = '1.8' targetCompatibility = '1.8'

View file

@ -19,8 +19,8 @@ public class ClientLauncherWrapper {
public static final String MAGIC_ARG = "-Djdk.attach.allowAttachSelf"; public static final String MAGIC_ARG = "-Djdk.attach.allowAttachSelf";
public static final String WAIT_PROCESS_PROPERTY = "launcher.waitProcess"; public static final String WAIT_PROCESS_PROPERTY = "launcher.waitProcess";
public static final String NO_JAVA9_CHECK_PROPERTY = "launcher.noJava9Check"; public static final String NO_JAVA9_CHECK_PROPERTY = "launcher.noJava9Check";
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
public static final boolean noJava9check = Boolean.getBoolean(NO_JAVA9_CHECK_PROPERTY); public static final boolean noJava9check = Boolean.getBoolean(NO_JAVA9_CHECK_PROPERTY);
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
public static void main(String[] arguments) throws IOException, InterruptedException { public static void main(String[] arguments) throws IOException, InterruptedException {
LogHelper.printVersion("Launcher"); LogHelper.printVersion("Launcher");
@ -76,7 +76,7 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
if (!modulePath.isEmpty()) { if (!modulePath.isEmpty()) {
args.add("--add-modules"); args.add("--add-modules");
String javaModules = "javafx.base,javafx.fxml,javafx.controls,jdk.unsupported"; String javaModules = "javafx.base,javafx.fxml,javafx.controls,jdk.unsupported";
if(useSwing) javaModules = javaModules.concat(",javafx.swing"); if (useSwing) javaModules = javaModules.concat(",javafx.swing");
args.add(javaModules); args.add(javaModules);
args.add("--module-path"); args.add("--module-path");
args.add(modulePath); args.add(modulePath);

View file

@ -8,11 +8,11 @@ public abstract class JSApplication extends Application {
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>(); private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
public static JSApplication getInstance() {
return INSTANCE.get();
}
public JSApplication() { public JSApplication() {
INSTANCE.set(this); INSTANCE.set(this);
} }
public static JSApplication getInstance() {
return INSTANCE.get();
}
} }

View file

@ -12,8 +12,8 @@
public final class LauncherAgent { public final class LauncherAgent {
private static boolean isAgentStarted = false;
public static Instrumentation inst; public static Instrumentation inst;
private static boolean isAgentStarted = false;
public static void addJVMClassPath(String path) throws IOException { public static void addJVMClassPath(String path) throws IOException {
LogHelper.debug("Launcher Agent addJVMClassPath"); LogHelper.debug("Launcher Agent addJVMClassPath");
@ -25,10 +25,6 @@ public static void addJVMClassPath(Path path) throws IOException {
inst.appendToSystemClassLoaderSearch(new JarFile(path.toFile())); inst.appendToSystemClassLoaderSearch(new JarFile(path.toFile()));
} }
public boolean isAgentStarted() {
return isAgentStarted;
}
public static void premain(String agentArgument, Instrumentation instrumentation) { public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("Launcher Agent"); System.out.println("Launcher Agent");
checkAgentStacktrace(); checkAgentStacktrace();
@ -58,4 +54,8 @@ public static void checkAgentStacktrace() {
public static boolean isStarted() { public static boolean isStarted() {
return isAgentStarted; return isAgentStarted;
} }
public boolean isAgentStarted() {
return isAgentStarted;
}
} }

View file

@ -1,6 +1,9 @@
package pro.gravit.launcher; package pro.gravit.launcher;
import pro.gravit.launcher.client.*; import pro.gravit.launcher.client.ClientLauncherEntryPoint;
import pro.gravit.launcher.client.ClientLauncherProcess;
import pro.gravit.launcher.client.ClientModuleManager;
import pro.gravit.launcher.client.DirBridge;
import pro.gravit.launcher.client.events.ClientEngineInitPhase; import pro.gravit.launcher.client.events.ClientEngineInitPhase;
import pro.gravit.launcher.client.events.ClientExitPhase; import pro.gravit.launcher.client.events.ClientExitPhase;
import pro.gravit.launcher.client.events.ClientPreGuiPhase; import pro.gravit.launcher.client.events.ClientPreGuiPhase;
@ -33,6 +36,20 @@
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public class LauncherEngine { public class LauncherEngine {
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
public static ClientLauncherProcess.ClientParams clientParams;
public static LauncherGuardInterface guard;
public static ClientModuleManager modulesManager;
// Instance
private final AtomicBoolean started = new AtomicBoolean(false);
public RuntimeProvider runtimeProvider;
public ECPublicKey publicKey;
public ECPrivateKey privateKey;
private LauncherEngine() {
}
//JVMHelper.getCertificates //JVMHelper.getCertificates
public static X509Certificate[] getCertificates(Class<?> clazz) { public static X509Certificate[] getCertificates(Class<?> clazz) {
Object[] signers = clazz.getSigners(); Object[] signers = clazz.getSigners();
@ -40,10 +57,6 @@ public static X509Certificate[] getCertificates(Class<?> clazz) {
return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new); return Arrays.stream(signers).filter((c) -> c instanceof X509Certificate).map((c) -> (X509Certificate) c).toArray(X509Certificate[]::new);
} }
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
public static ClientLauncherProcess.ClientParams clientParams;
public static LauncherGuardInterface guard;
public static void checkClass(Class<?> clazz) throws SecurityException { public static void checkClass(Class<?> clazz) throws SecurityException {
LauncherTrustManager trustManager = Launcher.getConfig().trustManager; LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
if (trustManager == null) return; if (trustManager == null) return;
@ -58,8 +71,7 @@ public static void checkClass(Class<?> clazz) throws SecurityException {
} }
} }
public static void exitLauncher(int code) public static void exitLauncher(int code) {
{
modulesManager.invokeEvent(new ClientExitPhase(code)); modulesManager.invokeEvent(new ClientExitPhase(code));
try { try {
System.exit(code); System.exit(code);
@ -108,7 +120,22 @@ public static void initGson(ClientModuleManager modulesManager) {
} }
public static void verifyNoAgent() { public static void verifyNoAgent() {
if (JVMHelper.RUNTIME_MXBEAN.getInputArguments().stream().filter(e -> e != null && !e.isEmpty()).anyMatch(e -> e.contains("javaagent"))) throw new SecurityException("JavaAgent found"); if (JVMHelper.RUNTIME_MXBEAN.getInputArguments().stream().filter(e -> e != null && !e.isEmpty()).anyMatch(e -> e.contains("javaagent")))
throw new SecurityException("JavaAgent found");
}
public static LauncherGuardInterface tryGetStdGuard() {
switch (Launcher.getConfig().guardType) {
case "no":
return new LauncherNoGuard();
case "wrapper":
return new LauncherWrapperGuard();
}
return null;
}
public static LauncherEngine clientInstance() {
return new LauncherEngine();
} }
public void readKeys() throws IOException, InvalidKeySpecException { public void readKeys() throws IOException, InvalidKeySpecException {
@ -133,20 +160,6 @@ public void readKeys() throws IOException, InvalidKeySpecException {
} }
} }
// Instance
private final AtomicBoolean started = new AtomicBoolean(false);
public RuntimeProvider runtimeProvider;
public ECPublicKey publicKey;
public ECPrivateKey privateKey;
public static ClientModuleManager modulesManager;
private LauncherEngine() {
}
public void start(String... args) throws Throwable { public void start(String... args) throws Throwable {
//Launcher.modulesManager = new ClientModuleManager(this); //Launcher.modulesManager = new ClientModuleManager(this);
LauncherEngine.guard = tryGetStdGuard(); LauncherEngine.guard = tryGetStdGuard();
@ -188,20 +201,4 @@ public void start(String... args) throws Throwable {
LogHelper.debug("Dir: %s", DirBridge.dir); LogHelper.debug("Dir: %s", DirBridge.dir);
runtimeProvider.run(args); runtimeProvider.run(args);
} }
public static LauncherGuardInterface tryGetStdGuard()
{
switch (Launcher.getConfig().guardType)
{
case "no":
return new LauncherNoGuard();
case "wrapper":
return new LauncherWrapperGuard();
}
return null;
}
public static LauncherEngine clientInstance() {
return new LauncherEngine();
}
} }

View file

@ -4,9 +4,14 @@
import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.hasher.HashedDir;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NewLauncherSettings { public class NewLauncherSettings {
@LauncherNetworkAPI
public final transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
@LauncherNetworkAPI @LauncherNetworkAPI
public Map<String, UserSettings> userSettings = new HashMap<>(); public Map<String, UserSettings> userSettings = new HashMap<>();
@LauncherNetworkAPI @LauncherNetworkAPI
@ -14,6 +19,14 @@ public class NewLauncherSettings {
@LauncherNetworkAPI @LauncherNetworkAPI
public String consoleUnlockKey; public String consoleUnlockKey;
public void putHDir(String name, Path path, HashedDir dir) {
String fullPath = path.toAbsolutePath().toString();
lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
e.needSave = true;
lastHDirs.add(e);
}
public static class HashedStoreEntry { public static class HashedStoreEntry {
public final HashedDir hdir; public final HashedDir hdir;
public final String name; public final String name;
@ -26,15 +39,4 @@ public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
this.fullPath = fullPath; this.fullPath = fullPath;
} }
} }
@LauncherNetworkAPI
public final transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
public void putHDir(String name, Path path, HashedDir dir) {
String fullPath = path.toAbsolutePath().toString();
lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
e.needSave = true;
lastHDirs.add(e);
}
} }

View file

@ -8,16 +8,16 @@ public class SystemService {
private SystemService() { private SystemService() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public static void exit(int code)
{ public static void exit(int code) {
LauncherEngine.exitLauncher(code); LauncherEngine.exitLauncher(code);
} }
public static void setSecurityManager(SecurityManager s)
{ public static void setSecurityManager(SecurityManager s) {
LogHelper.debug("Try set security manager %s", s == null ? "null" : s.getClass().getName()); LogHelper.debug("Try set security manager %s", s == null ? "null" : s.getClass().getName());
if(AuthService.profile == null || AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.NONE) return; if (AuthService.profile == null || AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.NONE)
if(AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.CLIENT) return;
{ if (AuthService.profile.securityManagerConfig == ClientProfile.SecurityManagerConfig.CLIENT) {
System.setSecurityManager(s); System.setSecurityManager(s);
} }
//TODO NEXT //TODO NEXT

View file

@ -62,21 +62,21 @@ public ClientClassLoader(URL[] urls, ClassLoader parent) {
public String findLibrary(String name) { public String findLibrary(String name) {
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(getNativePrefix()).concat(name).concat(getNativeEx()); return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(getNativePrefix()).concat(name).concat(getNativeEx());
} }
public String getNativeEx()
{ public String getNativeEx() {
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
return ".dll"; return ".dll";
else if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) else if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
return ".so"; return ".so";
else if(JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX) else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
return ".dylib"; return ".dylib";
return ""; return "";
} }
public String getNativePrefix()
{ public String getNativePrefix() {
if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
return "lib"; return "lib";
else if(JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX) else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX)
return "lib"; return "lib";
return ""; return "";
} }

View file

@ -26,7 +26,10 @@
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.net.*; import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -37,12 +40,12 @@
import java.util.stream.Stream; import java.util.stream.Stream;
public class ClientLauncherEntryPoint { public class ClientLauncherEntryPoint {
private static ClientClassLoader classLoader;
private static ClientLauncherProcess.ClientParams readParams(SocketAddress address) throws IOException { private static ClientLauncherProcess.ClientParams readParams(SocketAddress address) throws IOException {
try (Socket socket = IOHelper.newSocket()) try (Socket socket = IOHelper.newSocket()) {
{
socket.connect(address); socket.connect(address);
try(HInput input = new HInput(socket.getInputStream())) try (HInput input = new HInput(socket.getInputStream())) {
{
byte[] serialized = input.readByteArray(0); byte[] serialized = input.readByteArray(0);
ClientLauncherProcess.ClientParams params = Launcher.gsonManager.gson.fromJson(new String(serialized, IOHelper.UNICODE_CHARSET), ClientLauncherProcess.ClientParams.class); ClientLauncherProcess.ClientParams params = Launcher.gsonManager.gson.fromJson(new String(serialized, IOHelper.UNICODE_CHARSET), ClientLauncherProcess.ClientParams.class);
params.clientHDir = new HashedDir(input); params.clientHDir = new HashedDir(input);
@ -52,7 +55,7 @@ private static ClientLauncherProcess.ClientParams readParams(SocketAddress addre
} }
} }
} }
private static ClientClassLoader classLoader;
public static void main(String[] args) throws Throwable { public static void main(String[] args) throws Throwable {
LauncherEngine.IS_CLIENT.set(true); LauncherEngine.IS_CLIENT.set(true);
LauncherEngine engine = LauncherEngine.clientInstance(); LauncherEngine engine = LauncherEngine.clientInstance();
@ -147,10 +150,12 @@ public static void main(String[] args) throws Throwable {
launch(profile, params); launch(profile, params);
} }
} }
private static void initGson(ClientModuleManager moduleManager) { private static void initGson(ClientModuleManager moduleManager) {
Launcher.gsonManager = new ClientGsonManager(moduleManager); Launcher.gsonManager = new ClientGsonManager(moduleManager);
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
} }
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException { public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
//if (matcher != null) //if (matcher != null)
// matcher = matcher.verifyOnly(); // matcher = matcher.verifyOnly();
@ -172,6 +177,7 @@ public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher,
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir))); throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
} }
} }
public static void checkJVMBitsAndVersion() { public static void checkJVMBitsAndVersion() {
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) { if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS); String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
@ -188,6 +194,7 @@ public static void checkJVMBitsAndVersion() {
JOptionPane.showMessageDialog(null, error); JOptionPane.showMessageDialog(null, error);
} }
} }
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException { private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new)); return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
} }
@ -204,20 +211,7 @@ private static Stream<Path> resolveClassPathStream(Path clientDir, String... cla
} }
return builder.build(); return builder.build();
} }
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
private final Stream.Builder<Path> result;
private ClassPathFileVisitor(Stream.Builder<Path> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
result.accept(file);
return super.visitFile(file, attrs);
}
}
private static void launch(ClientProfile profile, ClientLauncherProcess.ClientParams params) throws Throwable { private static void launch(ClientProfile profile, ClientLauncherProcess.ClientParams params) throws Throwable {
// Add client args // Add client args
Collection<String> args = new LinkedList<>(); Collection<String> args = new LinkedList<>();
@ -239,8 +233,7 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
LogHelper.debug("Args: " + copy); LogHelper.debug("Args: " + copy);
// Resolve main class and method // Resolve main class and method
Class<?> mainClass = classLoader.loadClass(profile.getMainClass()); Class<?> mainClass = classLoader.loadClass(profile.getMainClass());
for(URL u : classLoader.getURLs()) for (URL u : classLoader.getURLs()) {
{
LogHelper.info("ClassLoader URL: %s", u.toString()); LogHelper.info("ClassLoader URL: %s", u.toString());
} }
FMLPatcher.apply(); FMLPatcher.apply();
@ -260,4 +253,19 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
} }
} }
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
private final Stream.Builder<Path> result;
private ClassPathFileVisitor(Stream.Builder<Path> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
result.accept(file);
return super.visitFile(file, attrs);
}
}
} }

View file

@ -24,18 +24,19 @@
import java.util.*; import java.util.*;
public class ClientLauncherProcess { public class ClientLauncherProcess {
private transient Process process;
private final transient Boolean[] waitWriteParams = new Boolean[] {false};
public Path executeFile;
public Path workDir;
public Path javaDir;
public final ClientParams params = new ClientParams(); public final ClientParams params = new ClientParams();
public final List<String> jvmArgs = new LinkedList<>(); public final List<String> jvmArgs = new LinkedList<>();
public final List<String> systemClientArgs = new LinkedList<>(); public final List<String> systemClientArgs = new LinkedList<>();
public final List<String> systemClassPath = new LinkedList<>(); public final List<String> systemClassPath = new LinkedList<>();
public final Map<String, String> systemEnv = new HashMap<>(); public final Map<String, String> systemEnv = new HashMap<>();
public final String mainClass; public final String mainClass;
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
public Path executeFile;
public Path workDir;
public Path javaDir;
public boolean useLegacyJavaClassPathProperty;
public boolean isStarted; public boolean isStarted;
private transient Process process;
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) { public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
this.executeFile = executeFile; this.executeFile = executeFile;
@ -74,8 +75,15 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
this.params.javaHDir = jvmHDir; this.params.javaHDir = jvmHDir;
applyClientProfile(); applyClientProfile();
} }
private void applyClientProfile()
{ public static String getPathSeparator() {
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
return ";";
else
return ":";
}
private void applyClientProfile() {
this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()); this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString());
Collections.addAll(this.jvmArgs, this.params.profile.getJvmArgs()); Collections.addAll(this.jvmArgs, this.params.profile.getJvmArgs());
this.params.profile.pushOptionalJvmArgs(this.jvmArgs); this.params.profile.pushOptionalJvmArgs(this.jvmArgs);
@ -89,9 +97,68 @@ private void applyClientProfile()
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderCreateEvent(this)); LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderCreateEvent(this));
} }
public void start(boolean pipeOutput) throws IOException, InterruptedException {
if (isStarted) throw new IllegalStateException("Process already started");
if (LauncherEngine.guard != null) LauncherEngine.guard.applyGuardParams(this);
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderPreLaunchEvent(this));
List<String> processArgs = new LinkedList<>();
processArgs.add(executeFile.toString());
processArgs.addAll(jvmArgs);
//ADD CLASSPATH
if (useLegacyJavaClassPathProperty) {
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
} else {
processArgs.add("-cp");
processArgs.add(String.join(getPathSeparator(), systemClassPath));
}
processArgs.add(mainClass);
processArgs.addAll(systemClientArgs);
synchronized (waitWriteParams) {
if (!waitWriteParams[0]) {
waitWriteParams.wait(1000);
}
}
if (LogHelper.isDebugEnabled())
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
EnvHelper.addEnv(processBuilder);
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
processBuilder.environment().putAll(systemEnv);
processBuilder.directory(workDir.toFile());
processBuilder.inheritIO();
if (pipeOutput) {
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
}
process = processBuilder.start();
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderLaunchedEvent(this));
isStarted = true;
}
public static class ClientParams public void runWriteParams(SocketAddress address) throws IOException {
{ try (ServerSocket serverSocket = new ServerSocket()) {
serverSocket.bind(address);
synchronized (waitWriteParams) {
waitWriteParams[0] = true;
waitWriteParams.notifyAll();
}
Socket socket = serverSocket.accept();
try (HOutput output = new HOutput(socket.getOutputStream())) {
byte[] serializedMainParams = Launcher.gsonManager.gson.toJson(params).getBytes(IOHelper.UNICODE_CHARSET);
output.writeByteArray(serializedMainParams, 0);
params.clientHDir.write(output);
params.assetHDir.write(output);
params.javaHDir.write(output);
}
}
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
}
public Process getProcess() {
return process;
}
public static class ClientParams {
public String assetDir; public String assetDir;
public String clientDir; public String clientDir;
@ -128,27 +195,13 @@ public static class ClientParams
public transient HashedDir javaHDir; public transient HashedDir javaHDir;
public void addClientArgs(Collection<String> args) public void addClientArgs(Collection<String> args) {
{
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0) if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
addModernClientArgs(args); addModernClientArgs(args);
else else
addClientLegacyArgs(args); addClientLegacyArgs(args);
} }
public static class ClientUserProperties {
@LauncherNetworkAPI
public String[] skinURL;
@LauncherNetworkAPI
public String[] skinDigest;
@LauncherNetworkAPI
public String[] cloakURL;
@LauncherNetworkAPI
public String[] cloakDigest;
}
public void addClientLegacyArgs(Collection<String> args) { public void addClientLegacyArgs(Collection<String> args) {
args.add(playerProfile.username); args.add(playerProfile.username);
args.add(accessToken); args.add(accessToken);
@ -211,74 +264,16 @@ private void addModernClientArgs(Collection<String> args) {
Collections.addAll(args, "--height", Integer.toString(height)); Collections.addAll(args, "--height", Integer.toString(height));
} }
} }
}
public void start(boolean pipeOutput) throws IOException, InterruptedException {
if(isStarted) throw new IllegalStateException("Process already started");
if(LauncherEngine.guard != null) LauncherEngine.guard.applyGuardParams(this);
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderPreLaunchEvent(this));
List<String> processArgs = new LinkedList<>();
processArgs.add(executeFile.toString());
processArgs.addAll(jvmArgs);
processArgs.add("-cp");
//ADD CLASSPATH
processArgs.add(String.join(getPathSeparator(), systemClassPath));
processArgs.add(mainClass);
processArgs.addAll(systemClientArgs);
synchronized (waitWriteParams)
{
if(!waitWriteParams[0])
{
waitWriteParams.wait(1000);
}
}
if(LogHelper.isDebugEnabled())
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
EnvHelper.addEnv(processBuilder);
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
processBuilder.environment().putAll(systemEnv);
processBuilder.directory(workDir.toFile());
processBuilder.inheritIO();
if (pipeOutput) {
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
}
process = processBuilder.start();
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderLaunchedEvent(this));
isStarted = true;
}
public void runWriteParams(SocketAddress address) throws IOException
{
try(ServerSocket serverSocket = new ServerSocket())
{
serverSocket.bind(address);
synchronized (waitWriteParams)
{
waitWriteParams[0] = true;
waitWriteParams.notifyAll();
}
Socket socket = serverSocket.accept();
try(HOutput output = new HOutput(socket.getOutputStream()))
{
byte[] serializedMainParams = Launcher.gsonManager.gson.toJson(params).getBytes(IOHelper.UNICODE_CHARSET);
output.writeByteArray(serializedMainParams, 0);
params.clientHDir.write(output);
params.assetHDir.write(output);
params.javaHDir.write(output);
}
}
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
}
public Process getProcess() { public static class ClientUserProperties {
return process; @LauncherNetworkAPI
} public String[] skinURL;
@LauncherNetworkAPI
public static String getPathSeparator() public String[] skinDigest;
{ @LauncherNetworkAPI
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) public String[] cloakURL;
return ";"; @LauncherNetworkAPI
else public String[] cloakDigest;
return ":"; }
} }
} }

View file

@ -1,9 +1,9 @@
package pro.gravit.launcher.client; package pro.gravit.launcher.client;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launcher.modules.impl.SimpleModuleManager; import pro.gravit.launcher.modules.impl.SimpleModuleManager;
import pro.gravit.launcher.LauncherTrustManager;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collection; import java.util.Collection;

View file

@ -29,12 +29,34 @@ public class DirBridge {
public static boolean useLegacyDir; public static boolean useLegacyDir;
static {
public static void move(Path newDir) throws IOException { String projectName = Launcher.getConfig().projectName;
IOHelper.move(dirUpdates, newDir); try {
dirUpdates = newDir; DirBridge.dir = getLauncherDir(projectName);
if (!IOHelper.exists(DirBridge.dir)) Files.createDirectories(DirBridge.dir);
DirBridge.defaultUpdatesDir = DirBridge.dir.resolve("updates");
if (!IOHelper.exists(DirBridge.defaultUpdatesDir)) Files.createDirectories(DirBridge.defaultUpdatesDir);
DirBridge.dirStore = getStoreDir(projectName);
if (!IOHelper.exists(DirBridge.dirStore)) Files.createDirectories(DirBridge.dirStore);
DirBridge.dirProjectStore = getProjectStoreDir(projectName);
if (!IOHelper.exists(DirBridge.dirProjectStore)) Files.createDirectories(DirBridge.dirProjectStore);
} catch (IOException e) {
LogHelper.error(e);
}
} }
public static void move(Path newDir) throws IOException {
if (newDir == null) {
LogHelper.debug("Invalid dir (null)");
if (LogHelper.isDevEnabled())
LogHelper.dev(LogHelper.toString(new Throwable("Check stack of call DirBridge with null path...")));
return;
}
Path oldUpdates = dirUpdates;
dirUpdates = newDir;
LogHelper.dev(newDir.toString());
IOHelper.move(oldUpdates, dirUpdates);
}
public static Path getAppDataDir() throws IOException { public static Path getAppDataDir() throws IOException {
boolean isCustomDir = Boolean.getBoolean(System.getProperty(USE_CUSTOMDIR_PROPERTY, "false")); boolean isCustomDir = Boolean.getBoolean(System.getProperty(USE_CUSTOMDIR_PROPERTY, "false"));
@ -53,6 +75,10 @@ public static Path getAppDataDir() throws IOException {
return local; return local;
} }
} else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) { } else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
if (System.getenv().containsKey("appdata"))
return Paths.get(System.getenv().get("appdata")).toAbsolutePath();
if (System.getenv().containsKey("APPDATA")) // Because it is windows
return Paths.get(System.getenv().get("APPDATA")).toAbsolutePath();
Path appdata = IOHelper.HOME_DIR.resolve("AppData").resolve("Roaming"); Path appdata = IOHelper.HOME_DIR.resolve("AppData").resolve("Roaming");
if (!IOHelper.isDir(appdata)) Files.createDirectories(appdata); if (!IOHelper.isDir(appdata)) Files.createDirectories(appdata);
return appdata; return appdata;
@ -65,12 +91,10 @@ public static Path getAppDataDir() throws IOException {
} }
} }
public static Path getLauncherDir(String projectname) throws IOException { public static Path getLauncherDir(String projectname) throws IOException {
return getAppDataDir().resolve(projectname); return getAppDataDir().resolve(projectname);
} }
public static Path getStoreDir(String projectname) throws IOException { public static Path getStoreDir(String projectname) throws IOException {
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
return getAppDataDir().resolve("store"); return getAppDataDir().resolve("store");
@ -80,39 +104,19 @@ else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
return getAppDataDir().resolve("minecraftStore"); return getAppDataDir().resolve("minecraftStore");
} }
public static Path getProjectStoreDir(String projectname) throws IOException { public static Path getProjectStoreDir(String projectname) throws IOException {
return getStoreDir(projectname).resolve(projectname); return getStoreDir(projectname).resolve(projectname);
} }
public static Path getGuardDir() { public static Path getGuardDir() {
return dir.resolve("guard"); return dir.resolve("guard");
} }
public static Path getLegacyLauncherDir(String projectname) { public static Path getLegacyLauncherDir(String projectname) {
return IOHelper.HOME_DIR.resolve(projectname); return IOHelper.HOME_DIR.resolve(projectname);
} }
public static void setUseLegacyDir(boolean b) { public static void setUseLegacyDir(boolean b) {
useLegacyDir = b; useLegacyDir = b;
} }
static {
String projectName = Launcher.getConfig().projectName;
try {
DirBridge.dir = getLauncherDir(projectName);
if (!IOHelper.exists(DirBridge.dir)) Files.createDirectories(DirBridge.dir);
DirBridge.defaultUpdatesDir = DirBridge.dir.resolve("updates");
if (!IOHelper.exists(DirBridge.defaultUpdatesDir)) Files.createDirectories(DirBridge.defaultUpdatesDir);
DirBridge.dirStore = getStoreDir(projectName);
if (!IOHelper.exists(DirBridge.dirStore)) Files.createDirectories(DirBridge.dirStore);
DirBridge.dirProjectStore = getProjectStoreDir(projectName);
if (!IOHelper.exists(DirBridge.dirProjectStore)) Files.createDirectories(DirBridge.dirProjectStore);
} catch (IOException e) {
LogHelper.error(e);
}
}
} }

View file

@ -21,34 +21,24 @@
public final class ServerPinger { public final class ServerPinger {
public static final class Result {
public final int onlinePlayers;
public final int maxPlayers;
public final String raw;
public Result(int onlinePlayers, int maxPlayers, String raw) {
this.onlinePlayers = VerifyHelper.verifyInt(onlinePlayers,
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
this.maxPlayers = VerifyHelper.verifyInt(maxPlayers,
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
this.raw = raw;
}
public boolean isOverfilled() {
return onlinePlayers >= maxPlayers;
}
}
// Constants // Constants
private static final String LEGACY_PING_HOST_MAGIC = "§1"; private static final String LEGACY_PING_HOST_MAGIC = "§1";
private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost"; private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost";
private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL); private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL);
private static final int PACKET_LENGTH = 65535; private static final int PACKET_LENGTH = 65535;
// Instance
private final InetSocketAddress address;
private final ClientProfile.Version version;
// Cache
private final Object cacheLock = new Object();
private Result cache = null;
private Exception cacheException = null;
private Instant cacheTime = null;
public ServerPinger(ClientProfile profile) {
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
this.version = Objects.requireNonNull(profile.getVersion(), "version");
}
private static String readUTF16String(HInput input) throws IOException { private static String readUTF16String(HInput input) throws IOException {
int length = input.readUnsignedShort() << 1; int length = input.readUnsignedShort() << 1;
@ -61,24 +51,6 @@ private static void writeUTF16String(HOutput output, String s) throws IOExceptio
output.stream.write(s.getBytes(StandardCharsets.UTF_16BE)); output.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
} }
// Instance
private final InetSocketAddress address;
private final ClientProfile.Version version;
// Cache
private final Object cacheLock = new Object();
private Result cache = null;
private Exception cacheException = null;
private Instant cacheTime = null;
public ServerPinger(ClientProfile profile) {
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
this.version = Objects.requireNonNull(profile.getVersion(), "version");
}
private Result doPing() throws IOException { private Result doPing() throws IOException {
try (Socket socket = IOHelper.newSocket()) { try (Socket socket = IOHelper.newSocket()) {
socket.connect(IOHelper.resolve(address), IOHelper.SOCKET_TIMEOUT); socket.connect(IOHelper.resolve(address), IOHelper.SOCKET_TIMEOUT);
@ -192,7 +164,6 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
return new Result(online, max, response); return new Result(online, max, response);
} }
public Result ping() throws IOException { public Result ping() throws IOException {
Instant now = Instant.now(); Instant now = Instant.now();
synchronized (cacheLock) { synchronized (cacheLock) {
@ -222,4 +193,26 @@ public Result ping() throws IOException {
return cache; return cache;
} }
} }
public static final class Result {
public final int onlinePlayers;
public final int maxPlayers;
public final String raw;
public Result(int onlinePlayers, int maxPlayers, String raw) {
this.onlinePlayers = VerifyHelper.verifyInt(onlinePlayers,
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
this.maxPlayers = VerifyHelper.verifyInt(maxPlayers,
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
this.raw = raw;
}
public boolean isOverfilled() {
return onlinePlayers >= maxPlayers;
}
}
} }

View file

@ -3,7 +3,6 @@
import pro.gravit.launcher.LauncherEngine; import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.ClientLauncherProcess; import pro.gravit.launcher.client.ClientLauncherProcess;
import pro.gravit.launcher.modules.events.PostInitPhase; import pro.gravit.launcher.modules.events.PostInitPhase;
import pro.gravit.launcher.profiles.ClientProfile;
public class ClientProcessReadyEvent extends PostInitPhase { public class ClientProcessReadyEvent extends PostInitPhase {
public final LauncherEngine clientInstance; public final LauncherEngine clientInstance;

View file

@ -21,8 +21,7 @@ public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
if (ConsoleManager.checkUnlockKey(args[0])) { if (ConsoleManager.checkUnlockKey(args[0])) {
LogHelper.info("Unlock successful"); LogHelper.info("Unlock successful");
if(!ConsoleManager.unlock()) if (!ConsoleManager.unlock()) {
{
LogHelper.error("Console unlock canceled"); LogHelper.error("Console unlock canceled");
return; return;
} }

View file

@ -0,0 +1,48 @@
package pro.gravit.launcher.console.test;
import pro.gravit.launcher.utils.HWIDProvider;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
public class PrintHardwareInfoCommand extends Command {
@Override
public String getArgsDescription() {
return "[]";
}
@Override
public String getUsageDescription() {
return "print your hardware info and timings";
}
@Override
public void invoke(String... args) throws Exception {
LogHelper.info("Your Hardware ID:");
long startTime = System.currentTimeMillis();
long currentTime;
HWIDProvider provider = new HWIDProvider();
currentTime = System.currentTimeMillis();
LogHelper.info("Create HWIDProvider instance: %d ms", currentTime - startTime);
startTime = System.currentTimeMillis();
int bitness = provider.getBitness();
long totalMemory = provider.getTotalMemory();
boolean isBattery = provider.isBattery();
currentTime = System.currentTimeMillis();
LogHelper.info("Bitness: %d, totalMemory: %d(%.3f GB), battery %s, TIME: %d ms", bitness, totalMemory, (double) totalMemory / (1024.0 * 1024.0 * 1024.0), Boolean.toString(isBattery), currentTime - startTime);
startTime = System.currentTimeMillis();
int logicalProcessors = provider.getProcessorLogicalCount();
int physicalProcessors = provider.getProcessorPhysicalCount();
long processorMaxFreq = provider.getProcessorMaxFreq();
currentTime = System.currentTimeMillis();
LogHelper.info("Processors || logical: %d physical %d freq %d, TIME: %d ms", logicalProcessors, physicalProcessors, processorMaxFreq, currentTime - startTime);
startTime = System.currentTimeMillis();
String hwDiskID = provider.getHWDiskID();
currentTime = System.currentTimeMillis();
LogHelper.info("HWDiskID %s, TIME: %d ms", hwDiskID, currentTime - startTime);
startTime = System.currentTimeMillis();
String baseboardSerial = provider.getBaseboardSerialNumber();
currentTime = System.currentTimeMillis();
LogHelper.info("BaseboardSerial %s, TIME: %d ms", baseboardSerial, currentTime - startTime);
LogHelper.info("Hardware ID end");
}
}

View file

@ -1,10 +1,5 @@
package pro.gravit.launcher.guard; package pro.gravit.launcher.guard;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig;
import java.nio.file.Path;
public class LauncherGuardManager { public class LauncherGuardManager {
public static LauncherGuardInterface guard; public static LauncherGuardInterface guard;

View file

@ -10,20 +10,6 @@
public class LauncherWrapperGuard implements LauncherGuardInterface { public class LauncherWrapperGuard implements LauncherGuardInterface {
@Override
public String getName() {
return "wrapper";
}
@Override
public void applyGuardParams(ClientLauncherProcess process) {
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
String projectName = Launcher.getConfig().projectName;
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
process.executeFile = DirBridge.getGuardDir().resolve(wrapperUnpackName);
}
}
public LauncherWrapperGuard() { public LauncherWrapperGuard() {
try { try {
String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe"; String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe";
@ -36,4 +22,19 @@ public LauncherWrapperGuard() {
throw new SecurityException(e); throw new SecurityException(e);
} }
} }
@Override
public String getName() {
return "wrapper";
}
@Override
public void applyGuardParams(ClientLauncherProcess process) {
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
String projectName = Launcher.getConfig().projectName;
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
process.executeFile = DirBridge.getGuardDir().resolve(wrapperUnpackName);
process.useLegacyJavaClassPathProperty = true;
}
}
} }

View file

@ -4,6 +4,7 @@
import pro.gravit.launcher.LauncherEngine; import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.events.ClientUnlockConsoleEvent; import pro.gravit.launcher.client.events.ClientUnlockConsoleEvent;
import pro.gravit.launcher.console.UnlockCommand; import pro.gravit.launcher.console.UnlockCommand;
import pro.gravit.launcher.console.test.PrintHardwareInfoCommand;
import pro.gravit.utils.command.CommandHandler; import pro.gravit.utils.command.CommandHandler;
import pro.gravit.utils.command.JLineCommandHandler; import pro.gravit.utils.command.JLineCommandHandler;
import pro.gravit.utils.command.StdCommandHandler; import pro.gravit.utils.command.StdCommandHandler;
@ -44,6 +45,7 @@ public static void registerCommands() {
handler.registerCommand("gc", new GCCommand()); handler.registerCommand("gc", new GCCommand());
handler.registerCommand("clear", new ClearCommand(handler)); handler.registerCommand("clear", new ClearCommand(handler));
handler.registerCommand("unlock", new UnlockCommand()); handler.registerCommand("unlock", new UnlockCommand());
handler.registerCommand("printhardware", new PrintHardwareInfoCommand());
} }
public static boolean checkUnlockKey(String key) { public static boolean checkUnlockKey(String key) {
@ -51,10 +53,10 @@ public static boolean checkUnlockKey(String key) {
} }
public static boolean unlock() { public static boolean unlock() {
if(isConsoleUnlock) return true; if (isConsoleUnlock) return true;
ClientUnlockConsoleEvent event = new ClientUnlockConsoleEvent(handler); ClientUnlockConsoleEvent event = new ClientUnlockConsoleEvent(handler);
LauncherEngine.modulesManager.invokeEvent(event); LauncherEngine.modulesManager.invokeEvent(event);
if(event.isCancel()) return false; if (event.isCancel()) return false;
handler.registerCommand("debug", new DebugCommand()); handler.registerCommand("debug", new DebugCommand());
handler.unregisterCommand("unlock"); handler.unregisterCommand("unlock");
isConsoleUnlock = true; isConsoleUnlock = true;

View file

@ -10,10 +10,68 @@
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import java.io.IOException; import java.io.IOException;
import java.nio.file.*; import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
public class SettingsManager extends JsonConfigurable<NewLauncherSettings> { public class SettingsManager extends JsonConfigurable<NewLauncherSettings> {
public static NewLauncherSettings settings;
public SettingsManager() {
super(NewLauncherSettings.class, DirBridge.dir.resolve("settings.json"));
}
@Override
public NewLauncherSettings getConfig() {
return settings;
}
@Override
public void setConfig(NewLauncherSettings config) {
settings = config;
if (settings.consoleUnlockKey != null && !ConsoleManager.isConsoleUnlock) {
if (ConsoleManager.checkUnlockKey(settings.consoleUnlockKey)) {
ConsoleManager.unlock();
LogHelper.info("Console auto unlocked");
}
}
}
@Override
public NewLauncherSettings getDefaultConfig() {
return new NewLauncherSettings();
}
public void loadHDirStore(Path storePath) throws IOException {
Files.createDirectories(storePath);
IOHelper.walk(storePath, new StoreFileVisitor(), false);
}
public void saveHDirStore(Path storeProjectPath) throws IOException {
Files.createDirectories(storeProjectPath);
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
if (!e.needSave) continue;
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
if (!Files.exists(file)) Files.createFile(file);
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
output.writeString(e.name, 128);
output.writeString(e.fullPath, 1024);
e.hdir.write(output);
}
}
}
public void loadHDirStore() throws IOException {
loadHDirStore(DirBridge.dirStore);
}
public void saveHDirStore() throws IOException {
saveHDirStore(DirBridge.dirProjectStore);
}
public static class StoreFileVisitor extends SimpleFileVisitor<Path> { public static class StoreFileVisitor extends SimpleFileVisitor<Path> {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
@ -30,66 +88,4 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
} }
} }
public static NewLauncherSettings settings;
public SettingsManager() {
super(NewLauncherSettings.class, DirBridge.dir.resolve("settings.json"));
}
@Override
public NewLauncherSettings getConfig() {
return settings;
}
@Override
public NewLauncherSettings getDefaultConfig() {
return new NewLauncherSettings();
}
@Override
public void setConfig(NewLauncherSettings config) {
settings = config;
if (settings.consoleUnlockKey != null && !ConsoleManager.isConsoleUnlock) {
if (ConsoleManager.checkUnlockKey(settings.consoleUnlockKey)) {
ConsoleManager.unlock();
LogHelper.info("Console auto unlocked");
}
}
}
public void loadHDirStore(Path storePath) throws IOException {
Files.createDirectories(storePath);
IOHelper.walk(storePath, new StoreFileVisitor(), false);
}
public void saveHDirStore(Path storeProjectPath) throws IOException {
Files.createDirectories(storeProjectPath);
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
if (!e.needSave) continue;
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
if (!Files.exists(file)) Files.createFile(file);
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
output.writeString(e.name, 128);
output.writeString(e.fullPath, 1024);
e.hdir.write(output);
}
}
}
public void loadHDirStore() throws IOException {
loadHDirStore(DirBridge.dirStore);
}
public void saveHDirStore() throws IOException {
saveHDirStore(DirBridge.dirProjectStore);
}
} }

View file

@ -16,9 +16,13 @@
public class FMLPatcher extends ClassLoader implements Opcodes { public class FMLPatcher extends ClassLoader implements Opcodes {
public static final MethodType EXITMH = MethodType.methodType(void.class, int.class); public static final MethodType EXITMH = MethodType.methodType(void.class, int.class);
public static volatile FMLPatcher INSTANCE = null;
public static final String[] PACKAGES = new String[]{"cpw.mods.fml.", "net.minecraftforge.fml.", "cpw.mods."}; public static final String[] PACKAGES = new String[]{"cpw.mods.fml.", "net.minecraftforge.fml.", "cpw.mods."};
public static final Vector<MethodHandle> MHS = new Vector<>(); public static final Vector<MethodHandle> MHS = new Vector<>();
public static volatile FMLPatcher INSTANCE = null;
public FMLPatcher(final ClassLoader cl) {
super(cl);
}
public static void apply() { public static void apply() {
INSTANCE = new FMLPatcher(null); // Never cause ClassFormatError (fuck forge 1.14!!!) INSTANCE = new FMLPatcher(null); // Never cause ClassFormatError (fuck forge 1.14!!!)
@ -97,10 +101,6 @@ public static String randomStr(final int lenght) {
return sb.toString(); return sb.toString();
} }
public FMLPatcher(final ClassLoader cl) {
super(cl);
}
public Class<?> def(final String name, final String exName) { public Class<?> def(final String name, final String exName) {
return super.defineClass(name, ByteBuffer.wrap(gen(name.replace('.', '/'), exName)), null); return super.defineClass(name, ByteBuffer.wrap(gen(name.replace('.', '/'), exName)), null);
} }

View file

@ -18,60 +18,20 @@
import java.util.Objects; import java.util.Objects;
public final class DirWatcher implements Runnable, AutoCloseable { public final class DirWatcher implements Runnable, AutoCloseable {
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
FileVisitResult result = super.preVisitDirectory(dir, attrs);
if (DirWatcher.this.dir.equals(dir)) {
dir.register(service, KINDS);
return result;
}
// Maybe it's unnecessary to go deeper
//if (matcher != null && !matcher.shouldVerify(path)) {
// return FileVisitResult.SKIP_SUBTREE;
//}
// Register
dir.register(service, KINDS);
return result;
}
}
public static final boolean FILE_TREE_SUPPORTED = JVMHelper.OS_TYPE == OS.MUSTDIE; public static final boolean FILE_TREE_SUPPORTED = JVMHelper.OS_TYPE == OS.MUSTDIE;
public static final String IGN_OVERFLOW = "launcher.dirwatcher.ignoreOverflows";
// Constants // Constants
private static final Kind<?>[] KINDS = { private static final Kind<?>[] KINDS = {
StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE
}; };
private static final boolean PROP_IGN_OVERFLOW = Boolean.parseBoolean(System.getProperty(IGN_OVERFLOW, "true"));
public static final String IGN_OVERFLOW = "launcher.dirwatcher.ignoreOverflows";
private static final boolean PROP_IGN_OVERFLOW = Boolean.parseBoolean(System.getProperty(IGN_OVERFLOW, "true"));
private static void handleError(Throwable e) {
LogHelper.error(e);
NativeJVMHalt.haltA(-123);
}
private static Deque<String> toPath(Iterable<Path> path) {
Deque<String> result = new LinkedList<>();
for (Path pe : path)
result.add(pe.toString());
return result;
}
// Instance // Instance
private final Path dir; private final Path dir;
private final HashedDir hdir; private final HashedDir hdir;
private final FileNameMatcher matcher; private final FileNameMatcher matcher;
private final WatchService service; private final WatchService service;
private final boolean digest; private final boolean digest;
public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException { public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
this.dir = Objects.requireNonNull(dir, "dir"); this.dir = Objects.requireNonNull(dir, "dir");
this.hdir = Objects.requireNonNull(hdir, "hdir"); this.hdir = Objects.requireNonNull(hdir, "hdir");
@ -84,6 +44,18 @@ public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean dig
LogHelper.subInfo("DirWatcher %s", dir.toString()); LogHelper.subInfo("DirWatcher %s", dir.toString());
} }
private static void handleError(Throwable e) {
LogHelper.error(e);
NativeJVMHalt.haltA(-123);
}
private static Deque<String> toPath(Iterable<Path> path) {
Deque<String> result = new LinkedList<>();
for (Path pe : path)
result.add(pe.toString());
return result;
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
service.close(); service.close();
@ -136,4 +108,25 @@ public void run() {
handleError(exc); handleError(exc);
} }
} }
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
FileVisitResult result = super.preVisitDirectory(dir, attrs);
if (DirWatcher.this.dir.equals(dir)) {
dir.register(service, KINDS);
return result;
}
// Maybe it's unnecessary to go deeper
//if (matcher != null && !matcher.shouldVerify(path)) {
// return FileVisitResult.SKIP_SUBTREE;
//}
// Register
dir.register(service, KINDS);
return result;
}
}
} }

Some files were not shown because too many files have changed in this diff Show more