Merge branch 'release/5.1.8' into master

This commit is contained in:
Gravit 2020-09-29 01:35:23 +07:00
commit 90f360c565
No known key found for this signature in database
GPG key ID: 98A079490768CCE5
125 changed files with 1796 additions and 680 deletions

View file

@ -12,8 +12,8 @@
} }
} }
sourceCompatibility = '1.8' sourceCompatibility = '11'
targetCompatibility = '1.8' targetCompatibility = '11'
configurations { configurations {
compileOnlyA compileOnlyA
@ -33,6 +33,7 @@
from(parent.childProjects.Launcher.tasks.genRuntimeJS) from(parent.childProjects.Launcher.tasks.genRuntimeJS)
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName, "Premain-Class": mainAgentName,
"Multi-Release": "true",
"Can-Redefine-Classes": "true", "Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "true", "Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true" "Can-Set-Native-Method-Prefix": "true"
@ -80,6 +81,7 @@ pack project(':LauncherAPI')
bundle group: 'org.slf4j', name: 'slf4j-simple', version: rootProject['verSlf4j'] bundle group: 'org.slf4j', name: 'slf4j-simple', version: rootProject['verSlf4j']
bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j'] bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j']
bundle group: 'org.hibernate', name: 'hibernate-core', version: rootProject['verHibernate'] bundle group: 'org.hibernate', name: 'hibernate-core', version: rootProject['verHibernate']
bundle group: 'org.hibernate', name: 'hibernate-hikaricp', version: rootProject['verHibernate']
bundle group: 'mysql', name: 'mysql-connector-java', version: rootProject['verMySQLConn'] bundle group: 'mysql', name: 'mysql-connector-java', version: rootProject['verMySQLConn']
bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn'] bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn']
bundle group: 'net.sf.proguard', name: 'proguard-base', version: rootProject['verProguard'] bundle group: 'net.sf.proguard', name: 'proguard-base', version: rootProject['verProguard']
@ -184,6 +186,18 @@ task dumpClientLibs(type: Copy) {
url = 'https://www.gnu.org/licenses/gpl-3.0.html' url = 'https://www.gnu.org/licenses/gpl-3.0.html'
} }
} }
developers {
developer {
id = 'gravita'
name = 'Gravita'
email = 'gravita@gravit.pro'
}
developer {
id = 'zaxar163'
name = 'Zaxar163'
email = 'zahar.vcherachny@yandex.ru'
}
}
scm { scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git' connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'

View file

@ -388,7 +388,7 @@ public void syncProfilesDir() throws IOException {
// Sort and set new profiles // Sort and set new profiles
newProfies.sort(Comparator.comparing(a -> a)); newProfies.sort(Comparator.comparing(a -> a));
profilesList = Collections.unmodifiableList(newProfies); profilesList = Collections.unmodifiableList(newProfies);
if(pingServerManager != null) if (pingServerManager != null)
pingServerManager.syncServers(); pingServerManager.syncServers();
} }

View file

@ -4,6 +4,7 @@
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager; import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
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;
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
@ -213,6 +214,7 @@ public static void registerAll() {
DaoProvider.registerProviders(); DaoProvider.registerProviders();
AuthRequest.registerProviders(); AuthRequest.registerProviders();
HWIDProvider.registerProviders(); HWIDProvider.registerProviders();
OptionalAction.registerProviders();
} }
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException { public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {

View file

@ -21,7 +21,7 @@ public static boolean isAgentStarted() {
public static void premain(String agentArgument, Instrumentation inst) { public static void premain(String agentArgument, Instrumentation inst) {
StarterAgent.inst = inst; StarterAgent.inst = inst;
libraries = Paths.get(Optional.ofNullable(agentArgument).map(e -> e.trim()).filter(e -> !e.isEmpty()).orElse("libraries")); libraries = Paths.get(Optional.ofNullable(agentArgument).map(String::trim).filter(e -> !e.isEmpty()).orElse("libraries"));
isStarted = true; isStarted = true;
try { try {
Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor()); Files.walkFileTree(libraries, Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor());
@ -40,11 +40,10 @@ private static final class StarterVisitor extends SimpleFileVisitor<Path> {
DPERMS = Collections.unmodifiableSet(perms); DPERMS = Collections.unmodifiableSet(perms);
} }
private final Path filef;
private final boolean fixLib; private final boolean fixLib;
private StarterVisitor() { private StarterVisitor() {
this.filef = StarterAgent.libraries.resolve(".libraries_chmoded"); Path filef = StarterAgent.libraries.resolve(".libraries_chmoded");
this.fixLib = !Files.exists(filef) && !Boolean.getBoolean("launcher.noLibrariesPosixPermsFix"); this.fixLib = !Files.exists(filef) && !Boolean.getBoolean("launcher.noLibrariesPosixPermsFix");
if (fixLib) { if (fixLib) {
try { try {

View file

@ -79,7 +79,7 @@ private static void visit(ClassNode classNode, Map<String, Object> values) {
return; return;
} }
field.invisibleAnnotations.remove(valueAnnotation); field.invisibleAnnotations.remove(valueAnnotation);
AtomicReference<String> valueName = new AtomicReference<String>(null); AtomicReference<String> valueName = new AtomicReference<>(null);
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) { valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
@Override @Override
public void visit(final String name, final Object value) { public void visit(final String name, final Object value) {
@ -142,15 +142,11 @@ public void visit(final String name, final Object value) {
} }
private static Serializer<?> serializerClass(int opcode) { private static Serializer<?> serializerClass(int opcode) {
return new Serializer<Number>() { return (Serializer<Number>) value -> {
@Override
public InsnList serialize(Number value) {
InsnList ret = new InsnList(); InsnList ret = new InsnList();
ret.add(NodeUtils.push(value.intValue())); ret.add(NodeUtils.push(value.intValue()));
ret.add(new InsnNode(opcode)); ret.add(new InsnNode(opcode));
return ret; return ret;
}
}; };
} }
@ -175,9 +171,8 @@ private static InsnList serializeValue(Object value) {
value.getClass())); value.getClass()));
} }
public static boolean isSerializableValue(Object value) public static boolean isSerializableValue(Object value) {
{ if (value == null) return true;
if(value == null) return true;
if (primitiveLDCClasses.contains(value.getClass())) return true; if (primitiveLDCClasses.contains(value.getClass())) return true;
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) { for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
if (serializerEntry.getKey().isInstance(value)) { if (serializerEntry.getKey().isInstance(value)) {

View file

@ -42,6 +42,21 @@ public MySQLSourceConfig(String poolName) {
this.poolName = poolName; this.poolName = poolName;
} }
public MySQLSourceConfig(String poolName, String address, int port, String username, String password, String database) {
this.poolName = poolName;
this.address = address;
this.port = port;
this.username = username;
this.password = password;
this.database = database;
}
public MySQLSourceConfig(String poolName, DataSource source, boolean hikari) {
this.poolName = poolName;
this.source = source;
this.hikari = hikari;
}
@Override @Override
public synchronized void close() { public synchronized void close() {
if (hikari) if (hikari)

View file

@ -9,7 +9,7 @@ public class HibernateAuthHandler extends CachedAuthHandler {
protected Entry fetchEntry(String username) { protected Entry fetchEntry(String username) {
User user = srv.config.dao.userDAO.findByUsername(username); User user = srv.config.dao.userDAO.findByUsername(username);
if (user == null) return null; if (user == null) return null;
return new Entry(user.getUuid(), username, user.getAccessToken(), user.getServerID()); return new Entry(user.getUuid(), user.getUsername(), user.getAccessToken(), user.getServerID());
} }
@Override @Override

View file

@ -11,25 +11,26 @@ public class JsonAuthHandler extends CachedAuthHandler {
public URL getUrl; public URL getUrl;
public URL updateAuthUrl; public URL updateAuthUrl;
public URL updateServerIdUrl; public URL updateServerIdUrl;
public String apiKey;
@Override @Override
protected Entry fetchEntry(String username) throws IOException { protected Entry fetchEntry(String username) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUsername(username)), getUrl), Entry.class); return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new EntryRequestByUsername(username, apiKey)), getUrl), Entry.class);
} }
@Override @Override
protected Entry fetchEntry(UUID uuid) throws IOException { protected Entry fetchEntry(UUID uuid) throws IOException {
return Launcher.gsonManager.configGson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.configGson.toJsonTree(new EntryRequestByUUID(uuid)), getUrl), Entry.class); return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new EntryRequestByUUID(uuid, apiKey)), getUrl), Entry.class);
} }
@Override @Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException { 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; return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new UpdateAuthRequest(uuid, username, accessToken, apiKey)), updateAuthUrl), SuccessResponse.class).success;
} }
@Override @Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException { 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; return Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new UpdateServerIDRequest(uuid, serverID, apiKey)), updateServerIdUrl), SuccessResponse.class).success;
} }
@Override @Override
@ -39,17 +40,21 @@ public void close() {
public static class EntryRequestByUsername { public static class EntryRequestByUsername {
public final String username; public final String username;
public final String apiKey;
public EntryRequestByUsername(String username) { public EntryRequestByUsername(String username, String apiKey) {
this.username = username; this.username = username;
this.apiKey = apiKey;
} }
} }
public static class EntryRequestByUUID { public static class EntryRequestByUUID {
public final UUID uuid; public final UUID uuid;
public final String apiKey;
public EntryRequestByUUID(UUID uuid) { public EntryRequestByUUID(UUID uuid, String apiKey) {
this.uuid = uuid; this.uuid = uuid;
this.apiKey = apiKey;
} }
} }
@ -57,21 +62,25 @@ public static class UpdateAuthRequest {
public final UUID uuid; public final UUID uuid;
public final String username; public final String username;
public final String accessToken; public final String accessToken;
public final String apiKey;
public UpdateAuthRequest(UUID uuid, String username, String accessToken) { public UpdateAuthRequest(UUID uuid, String username, String accessToken, String apiKey) {
this.uuid = uuid; this.uuid = uuid;
this.username = username; this.username = username;
this.accessToken = accessToken; this.accessToken = accessToken;
this.apiKey = apiKey;
} }
} }
public static class UpdateServerIDRequest { public static class UpdateServerIDRequest {
public final UUID uuid; public final UUID uuid;
public final String serverID; public final String serverID;
public final String apiKey;
public UpdateServerIDRequest(UUID uuid, String serverID) { public UpdateServerIDRequest(UUID uuid, String serverID, String apiKey) {
this.uuid = uuid; this.uuid = uuid;
this.serverID = serverID; this.serverID = serverID;
this.apiKey = apiKey;
} }
} }

View file

@ -3,6 +3,7 @@
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent; import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
import pro.gravit.launcher.events.request.HardwareReportRequestEvent; import pro.gravit.launcher.events.request.HardwareReportRequestEvent;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent; import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.Reconfigurable; import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.launchserver.auth.protect.hwid.HWIDException; import pro.gravit.launchserver.auth.protect.hwid.HWIDException;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
@ -21,6 +22,7 @@
public class AdvancedProtectHandler extends StdProtectHandler implements SecureProtectHandler, HardwareProtectHandler, JoinServerProtectHandler, Reconfigurable { public class AdvancedProtectHandler extends StdProtectHandler implements SecureProtectHandler, HardwareProtectHandler, JoinServerProtectHandler, Reconfigurable {
public boolean enableHardwareFeature; public boolean enableHardwareFeature;
public HWIDProvider provider; public HWIDProvider provider;
private transient LaunchServer server;
@Override @Override
public boolean allowGetAccessToken(AuthResponse.AuthContext context) { public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
@ -44,14 +46,12 @@ public boolean allowGetSecureLevelInfo(Client client) {
@Override @Override
public void onHardwareReport(HardwareReportResponse response, Client client) { public void onHardwareReport(HardwareReportResponse response, Client client) {
if(!enableHardwareFeature) if (!enableHardwareFeature) {
{
response.sendResult(new HardwareReportRequestEvent()); response.sendResult(new HardwareReportRequestEvent());
return; return;
} }
try { try {
if(!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) if (!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) {
{
response.sendError("Access denied"); response.sendError("Access denied");
return; return;
} }
@ -59,7 +59,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) {
LogHelper.debug("[HardwareInfo] HardwareInfo received"); LogHelper.debug("[HardwareInfo] HardwareInfo received");
boolean needCreate = !provider.addPublicKeyToHardwareInfo(response.hardware, client.trustLevel.publicKey, client); boolean needCreate = !provider.addPublicKeyToHardwareInfo(response.hardware, client.trustLevel.publicKey, client);
LogHelper.debug("[HardwareInfo] HardwareInfo needCreate: %s", needCreate ? "true" : "false"); LogHelper.debug("[HardwareInfo] HardwareInfo needCreate: %s", needCreate ? "true" : "false");
if(needCreate) if (needCreate)
provider.createHardwareInfo(response.hardware, client.trustLevel.publicKey, client); provider.createHardwareInfo(response.hardware, client.trustLevel.publicKey, client);
client.trustLevel.hardwareInfo = response.hardware; client.trustLevel.hardwareInfo = response.hardware;
} catch (HWIDException e) { } catch (HWIDException e) {
@ -70,17 +70,13 @@ public void onHardwareReport(HardwareReportResponse response, Client client) {
@Override @Override
public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
if(enableHardwareFeature) if (enableHardwareFeature) {
{ if (provider == null) {
if(provider == null)
{
LogHelper.warning("HWIDProvider null. HardwareInfo not checked!"); LogHelper.warning("HWIDProvider null. HardwareInfo not checked!");
} } else {
else
{
try { try {
client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client); client.trustLevel.hardwareInfo = provider.findHardwareInfoByPublicKey(client.trustLevel.publicKey, client);
if(client.trustLevel.hardwareInfo == null) //HWID not found? if (client.trustLevel.hardwareInfo == null) //HWID not found?
return new VerifySecureLevelKeyRequestEvent(true); return new VerifySecureLevelKeyRequestEvent(true);
} catch (HWIDException e) { } catch (HWIDException e) {
throw new SecurityException(e.getMessage()); //Show banned message throw new SecurityException(e.getMessage()); //Show banned message
@ -94,8 +90,7 @@ public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
@Override @Override
public Map<String, Command> getCommands() { public Map<String, Command> getCommands() {
Map<String, Command> commands = new HashMap<>(); Map<String, Command> commands = new HashMap<>();
if(provider instanceof Reconfigurable) if (provider instanceof Reconfigurable) {
{
commands.putAll(((Reconfigurable) provider).getCommands()); commands.putAll(((Reconfigurable) provider).getCommands());
} }
return commands; return commands;
@ -107,14 +102,14 @@ public boolean onJoinServer(String serverID, String username, Client client) {
} }
@Override @Override
public void init() { public void init(LaunchServer server) {
if(provider != null) if (provider != null)
provider.init(); provider.init(server);
} }
@Override @Override
public void close() { public void close() {
if(provider != null) if (provider != null)
provider.close(); provider.close();
} }
} }

View file

@ -1,5 +1,6 @@
package pro.gravit.launchserver.auth.protect; package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
@ -21,13 +22,11 @@ public static void registerHandlers() {
public abstract void checkLaunchServerLicense(); //Выдает SecurityException при ошибке проверки лицензии public abstract void checkLaunchServerLicense(); //Выдает SecurityException при ошибке проверки лицензии
public void init() public void init(LaunchServer server) {
{
} }
public void close() public void close() {
{
} }
//public abstract //public abstract

View file

@ -1,6 +1,7 @@
package pro.gravit.launchserver.auth.protect.hwid; package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.helper.DamerauHelper; import pro.gravit.launchserver.helper.DamerauHelper;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
@ -12,107 +13,102 @@
public abstract class HWIDProvider { public abstract class HWIDProvider {
public static final ProviderMap<HWIDProvider> providers = new ProviderMap<>("HWIDProvider"); public static final ProviderMap<HWIDProvider> providers = new ProviderMap<>("HWIDProvider");
private static boolean registredProv = false; private static boolean registredProv = false;
public static void registerProviders() { public static void registerProviders() {
if(!registredProv) if (!registredProv) {
{
providers.register("memory", MemoryHWIDProvider.class); providers.register("memory", MemoryHWIDProvider.class);
providers.register("mysql", MysqlHWIDProvider.class); providers.register("mysql", MysqlHWIDProvider.class);
providers.register("json", JsonHWIDProvider.class);
registredProv = true; registredProv = true;
} }
} }
public abstract HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException; public abstract HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException;
public abstract void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException; public abstract void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException;
public abstract boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException; public abstract boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException;
public void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) public void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) {
{ if (hardwareInfo.baseboardSerialNumber != null)
if(hardwareInfo.baseboardSerialNumber != null) hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim(); hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim();
if(hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim(); if (hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim();
} }
public static class HardwareInfoCompareResult
{ public static class HardwareInfoCompareResult {
public double firstSpoofingLevel = 0.0; public double firstSpoofingLevel = 0.0;
public double secondSpoofingLevel = 0.0; public double secondSpoofingLevel = 0.0;
public double compareLevel; public double compareLevel;
} }
//Required normalize HardwareInfo //Required normalize HardwareInfo
public HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) public HardwareInfoCompareResult compareHardwareInfo(HardwareReportRequest.HardwareInfo first, HardwareReportRequest.HardwareInfo second) {
{
HardwareInfoCompareResult result = new HardwareInfoCompareResult(); HardwareInfoCompareResult result = new HardwareInfoCompareResult();
if(first.hwDiskId == null || first.hwDiskId.isEmpty()) result.firstSpoofingLevel += 0.9; if (first.hwDiskId == null || first.hwDiskId.isEmpty()) result.firstSpoofingLevel += 0.9;
if(first.displayId == null || first.displayId.length < 4) result.firstSpoofingLevel += 0.3; if (first.displayId == null || first.displayId.length < 4) result.firstSpoofingLevel += 0.3;
if(first.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty()) result.firstSpoofingLevel += 0.2; if (first.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty())
if(second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9; result.firstSpoofingLevel += 0.2;
if(second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3; if (second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9;
if(second.baseboardSerialNumber == null || second.baseboardSerialNumber.trim().isEmpty()) result.secondSpoofingLevel += 0.2; if (second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3;
if(first.hwDiskId != null && second.hwDiskId != null) if (second.baseboardSerialNumber == null || second.baseboardSerialNumber.trim().isEmpty())
{ result.secondSpoofingLevel += 0.2;
if (first.hwDiskId != null && second.hwDiskId != null) {
int hwDIskIdRate = DamerauHelper.calculateDistance(first.hwDiskId.toLowerCase(), second.hwDiskId.toLowerCase()); int hwDIskIdRate = DamerauHelper.calculateDistance(first.hwDiskId.toLowerCase(), second.hwDiskId.toLowerCase());
if(hwDIskIdRate == 0) // 100% compare if (hwDIskIdRate == 0) // 100% compare
{ {
result.compareLevel += 0.99; result.compareLevel += 0.99;
} } else if (hwDIskIdRate < 3) //Very small change
else if(hwDIskIdRate < 3) //Very small change
{ {
result.compareLevel += 0.85; result.compareLevel += 0.85;
} } else if (hwDIskIdRate < (first.hwDiskId.length() + second.hwDiskId.length()) / 4) {
else if(hwDIskIdRate < (first.hwDiskId.length()+second.hwDiskId.length()) / 4) double addLevel = hwDIskIdRate / ((double) (first.hwDiskId.length() + second.hwDiskId.length()) / 2.0);
{ if (addLevel > 0.0 && addLevel < 0.85) result.compareLevel += addLevel;
double addLevel = hwDIskIdRate / ( (double)(first.hwDiskId.length()+second.hwDiskId.length()) / 2.0 );
if(addLevel > 0.0 && addLevel < 0.85) result.compareLevel += addLevel;
} }
} }
if(first.baseboardSerialNumber != null && second.baseboardSerialNumber != null) if (first.baseboardSerialNumber != null && second.baseboardSerialNumber != null) {
{
int baseboardSerialRate = DamerauHelper.calculateDistance(first.baseboardSerialNumber.toLowerCase(), second.baseboardSerialNumber.toLowerCase()); int baseboardSerialRate = DamerauHelper.calculateDistance(first.baseboardSerialNumber.toLowerCase(), second.baseboardSerialNumber.toLowerCase());
if(baseboardSerialRate == 0) // 100% compare if (baseboardSerialRate == 0) // 100% compare
{ {
result.compareLevel += 0.3; result.compareLevel += 0.3;
} } else if (baseboardSerialRate < 3) //Very small change
else if(baseboardSerialRate < 3) //Very small change
{ {
result.compareLevel += 0.15; result.compareLevel += 0.15;
} }
} }
if(first.displayId != null && second.displayId != null) if (first.displayId != null && second.displayId != null) {
{ if (Arrays.equals(first.displayId, second.displayId)) {
if(Arrays.equals(first.displayId, second.displayId))
{
result.compareLevel += 0.75; result.compareLevel += 0.75;
} }
} }
//Check statistic info //Check statistic info
if(first.logicalProcessors == 0 || first.physicalProcessors == 0 || first.logicalProcessors < first.physicalProcessors) //WTF if (first.logicalProcessors == 0 || first.physicalProcessors == 0 || first.logicalProcessors < first.physicalProcessors) //WTF
result.firstSpoofingLevel += 0.9; result.firstSpoofingLevel += 0.9;
if(second.logicalProcessors == 0 || second.physicalProcessors == 0 || second.logicalProcessors < second.physicalProcessors) //WTF if (second.logicalProcessors == 0 || second.physicalProcessors == 0 || second.logicalProcessors < second.physicalProcessors) //WTF
result.secondSpoofingLevel += 0.9; result.secondSpoofingLevel += 0.9;
if(first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors) if (first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors)
result.compareLevel += 0.05; result.compareLevel += 0.05;
if(first.battery != second.battery) if (first.battery != second.battery)
result.compareLevel -= 0.05; result.compareLevel -= 0.05;
if(first.processorMaxFreq == second.processorMaxFreq) if (first.processorMaxFreq == second.processorMaxFreq)
result.compareLevel += 0.1; result.compareLevel += 0.1;
if(first.totalMemory == second.totalMemory) if (first.totalMemory == second.totalMemory)
result.compareLevel += 0.1; result.compareLevel += 0.1;
if(Math.abs(first.totalMemory - second.totalMemory) < 32*1024) if (Math.abs(first.totalMemory - second.totalMemory) < 32 * 1024)
result.compareLevel += 0.05; result.compareLevel += 0.05;
return result; return result;
} }
protected void printHardwareInfo(LogHelper.Level logLevel, HardwareReportRequest.HardwareInfo info) protected void printHardwareInfo(LogHelper.Level logLevel, HardwareReportRequest.HardwareInfo info) {
{ LogHelper.log(logLevel, String.format("[HardwareInfo] Processor: logical %d | physical %d | freq %d | bitness %d", info.logicalProcessors, info.physicalProcessors, info.processorMaxFreq, info.bitness), false);
LogHelper.log(logLevel, String.format("[HardwareInfo] Processor: logical %d | physical %d | freq %d | bitness %d", info.logicalProcessors, info.physicalProcessors, info.processorMaxFreq, info.bitness) , false); LogHelper.log(logLevel, String.format("[HardwareInfo] Memory max: %d | battery %s", info.totalMemory, info.battery ? "true" : "false"), false);
LogHelper.log(logLevel, String.format("[HardwareInfo] Memory max: %d | battery %s", info.totalMemory, info.battery ? "true" : "false") , false); LogHelper.log(logLevel, String.format("[HardwareInfo] HWDiskID %s | baseboardSerialNumber %s | displayId hash: %s", info.hwDiskId, info.baseboardSerialNumber, SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, info.displayId))), false);
LogHelper.log(logLevel, String.format("[HardwareInfo] HWDiskID %s | baseboardSerialNumber %s | displayId hash: %s", info.hwDiskId, info.baseboardSerialNumber, SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, info.displayId))) , false);
} }
public void init() public void init(LaunchServer server) {
{
} }
public void close() public void close() {
{
} }
} }

View file

@ -0,0 +1,101 @@
package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.socket.Client;
import java.net.URL;
public class JsonHWIDProvider extends HWIDProvider {
public URL findHardwareInfoByPublicKeyRequest;
public URL createHardwareInfoRequest;
public URL addPublicKeyToHardwareInfoRequest;
public String apiKey;
public static class RequestFind {
public byte[] publicKey;
public Client client;
public String apiKey;
}
public static class ResultFind {
public String error;
public HardwareReportRequest.HardwareInfo info;
}
@Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
try {
RequestFind req = new RequestFind();
req.publicKey = publicKey;
req.client = client;
req.apiKey = apiKey;
ResultFind r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), findHardwareInfoByPublicKeyRequest), ResultFind.class);
if (r.error != null) throw new HWIDException(r.error);
return r.info;
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
public static class RequestCreate {
public byte[] publicKey;
public Client client;
public HardwareReportRequest.HardwareInfo hardwareInfo;
public String apiKey;
}
public static class ResultCreate {
public String error;
}
@Override
public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try {
RequestCreate req = new RequestCreate();
req.publicKey = publicKey;
req.client = client;
req.hardwareInfo = hardwareInfo;
req.apiKey = apiKey;
ResultCreate r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), createHardwareInfoRequest), ResultCreate.class);
if (r.error != null) throw new HWIDException(r.error);
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
public static class RequestAddKey {
public byte[] publicKey;
public Client client;
public HardwareReportRequest.HardwareInfo hardwareInfo;
public String apiKey;
}
public static class ResultAddKey {
public String error;
public boolean success;
}
@Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try {
RequestAddKey req = new RequestAddKey();
req.publicKey = publicKey;
req.client = client;
req.hardwareInfo = hardwareInfo;
req.apiKey = apiKey;
ResultAddKey r = Launcher.gsonManager.gson.fromJson(HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(req), addPublicKeyToHardwareInfoRequest), ResultAddKey.class);
if (r.error != null) throw new HWIDException(r.error);
return r.success;
} catch (HWIDException t) {
throw t;
} catch (Throwable t) {
throw new HWIDException(t);
}
}
}

View file

@ -24,8 +24,7 @@ public Map<String, Command> getCommands() {
commands.put("hardwarelist", new SubCommand() { commands.put("hardwarelist", new SubCommand() {
@Override @Override
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
for(MemoryHWIDEntity e : db) for (MemoryHWIDEntity e : db) {
{
printHardwareInfo(LogHelper.Level.INFO, e.hardware); printHardwareInfo(LogHelper.Level.INFO, e.hardware);
LogHelper.info("ID %d banned %s", e.id, e.banned ? "true" : "false"); LogHelper.info("ID %d banned %s", e.id, e.banned ? "true" : "false");
LogHelper.info("PublicKey Hash: %s", SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA1, e.publicKey))); LogHelper.info("PublicKey Hash: %s", SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA1, e.publicKey)));
@ -37,10 +36,8 @@ public void invoke(String... args) throws Exception {
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
long id = Long.parseLong(args[0]); long id = Long.parseLong(args[0]);
for(MemoryHWIDEntity e : db) for (MemoryHWIDEntity e : db) {
{ if (e.id == id) {
if(e.id == id)
{
e.banned = true; e.banned = true;
LogHelper.info("HardwareID %d banned", e.id); LogHelper.info("HardwareID %d banned", e.id);
} }
@ -50,8 +47,7 @@ public void invoke(String... args) throws Exception {
return commands; return commands;
} }
static class MemoryHWIDEntity static class MemoryHWIDEntity {
{
public HardwareReportRequest.HardwareInfo hardware; public HardwareReportRequest.HardwareInfo hardware;
public byte[] publicKey; public byte[] publicKey;
public boolean banned; public boolean banned;
@ -63,17 +59,17 @@ public MemoryHWIDEntity(HardwareReportRequest.HardwareInfo hardware, byte[] publ
this.id = SecurityHelper.newRandom().nextLong(); this.id = SecurityHelper.newRandom().nextLong();
} }
} }
public Set<MemoryHWIDEntity> db = ConcurrentHashMap.newKeySet(); public Set<MemoryHWIDEntity> db = ConcurrentHashMap.newKeySet();
@Override @Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException { public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
for(MemoryHWIDEntity e : db) { for (MemoryHWIDEntity e : db) {
if(Arrays.equals(e.publicKey, publicKey)) if (Arrays.equals(e.publicKey, publicKey)) {
{ if (e.banned) throw new HWIDException("You HWID banned");
if(e.banned) throw new HWIDException("You HWID banned");
return e.hardware; return e.hardware;
} }
}; }
return null; return null;
} }
@ -85,17 +81,15 @@ public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo,
@Override @Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException { public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
boolean isAlreadyWarning = false; boolean isAlreadyWarning = false;
for(MemoryHWIDEntity e : db) { for (MemoryHWIDEntity e : db) {
HardwareInfoCompareResult result = compareHardwareInfo(e.hardware, hardwareInfo); HardwareInfoCompareResult result = compareHardwareInfo(e.hardware, hardwareInfo);
if(warningSpoofingLevel > 0 && result.firstSpoofingLevel > warningSpoofingLevel && !isAlreadyWarning) if (warningSpoofingLevel > 0 && result.firstSpoofingLevel > warningSpoofingLevel && !isAlreadyWarning) {
{
LogHelper.warning("HardwareInfo spoofing level too high: %d", result.firstSpoofingLevel); LogHelper.warning("HardwareInfo spoofing level too high: %d", result.firstSpoofingLevel);
isAlreadyWarning = true; isAlreadyWarning = true;
} }
if(result.compareLevel > criticalCompareLevel) if (result.compareLevel > criticalCompareLevel) {
{
LogHelper.debug("HardwareInfo publicKey change: compareLevel %d", result.compareLevel); LogHelper.debug("HardwareInfo publicKey change: compareLevel %d", result.compareLevel);
if(e.banned) throw new HWIDException("You HWID banned"); if (e.banned) throw new HWIDException("You HWID banned");
e.publicKey = publicKey; e.publicKey = publicKey;
return true; return true;
} }

View file

@ -1,6 +1,7 @@
package pro.gravit.launchserver.auth.protect.hwid; package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.MySQLSourceConfig; import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
@ -29,37 +30,34 @@ public class MysqlHWIDProvider extends HWIDProvider {
private String sqlUpdateUsers; private String sqlUpdateUsers;
@Override @Override
public void init() { public void init(LaunchServer server) {
sqlFindByPublicKey = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s WHERE `publicKey` = ?", tableHWID); sqlFindByPublicKey = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s WHERE `publicKey` = ?", tableHWID);
sqlFindByHardware = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s", tableHWID); sqlFindByHardware = String.format("SELECT hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, banned FROM %s", tableHWID);
sqlCreateHardware = String.format("INSERT INTO `%s` (`publickey`, `hwDiskId`, `baseboardSerialNumber`, `displayId`, `bitness`, `totalMemory`, `logicalProcessors`, `physicalProcessors`, `processorMaxFreq`, `battery`, `banned`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')", tableHWID); sqlCreateHardware = String.format("INSERT INTO `%s` (`publickey`, `hwDiskId`, `baseboardSerialNumber`, `displayId`, `bitness`, `totalMemory`, `logicalProcessors`, `physicalProcessors`, `processorMaxFreq`, `battery`, `banned`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')", tableHWID);
sqlCreateHWIDLog = String.format("INSERT INTO %s (`hwidId`, `newPublicKey`) VALUES (?, ?)", tableHWIDLog); sqlCreateHWIDLog = String.format("INSERT INTO %s (`hwidId`, `newPublicKey`) VALUES (?, ?)", tableHWIDLog);
sqlUpdateHardware = String.format("UPDATE %s SET `publicKey` = ? WHERE `id` = ?", tableHWID); sqlUpdateHardware = String.format("UPDATE %s SET `publicKey` = ? WHERE `id` = ?", tableHWID);
if(tableUsers != null && usersHWIDColumn != null && usersNameColumn != null) if (tableUsers != null && usersHWIDColumn != null && usersNameColumn != null) {
{
sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", tableUsers, usersHWIDColumn, usersNameColumn); sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", tableUsers, usersHWIDColumn, usersNameColumn);
} else {
LogHelper.warning("[MysqlHWIDProvider] Link to users table not configured");
} }
} }
@Override @Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException { public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
try(Connection connection = mySQLHolder.getConnection()) try (Connection connection = mySQLHolder.getConnection()) {
{
PreparedStatement s = connection.prepareStatement(sqlFindByPublicKey); PreparedStatement s = connection.prepareStatement(sqlFindByPublicKey);
s.setBlob(1, new ByteArrayInputStream(publicKey)); s.setBlob(1, new ByteArrayInputStream(publicKey));
ResultSet set = s.executeQuery(); ResultSet set = s.executeQuery();
if(set.next()) if (set.next()) {
{ if (set.getBoolean(11)) //isBanned
if(set.getBoolean(11)) //isBanned
{ {
throw new SecurityException("You HWID banned"); throw new SecurityException("You HWID banned");
} }
long id = set.getLong(10); long id = set.getLong(10);
setUserHardwareId(connection, client.username, id); setUserHardwareId(connection, client.username, id);
return fetchHardwareInfo(set); return fetchHardwareInfo(set);
} } else {
else
{
return null; return null;
} }
} catch (SQLException | IOException throwables) { } catch (SQLException | IOException throwables) {
@ -85,8 +83,7 @@ private HardwareReportRequest.HardwareInfo fetchHardwareInfo(ResultSet set) thro
@Override @Override
public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException { public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try(Connection connection = mySQLHolder.getConnection()) try (Connection connection = mySQLHolder.getConnection()) {
{
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS); PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
s.setBlob(1, new ByteArrayInputStream(publicKey)); s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setString(2, hardwareInfo.hwDiskId); s.setString(2, hardwareInfo.hwDiskId);
@ -113,18 +110,15 @@ public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo,
@Override @Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException { public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
try(Connection connection = mySQLHolder.getConnection()) try (Connection connection = mySQLHolder.getConnection()) {
{
PreparedStatement s = connection.prepareStatement(sqlFindByHardware); PreparedStatement s = connection.prepareStatement(sqlFindByHardware);
ResultSet set = s.executeQuery(); ResultSet set = s.executeQuery();
while(set.next()) while (set.next()) {
{
HardwareReportRequest.HardwareInfo hw = fetchHardwareInfo(set); HardwareReportRequest.HardwareInfo hw = fetchHardwareInfo(set);
long id = set.getLong(10); long id = set.getLong(10);
HardwareInfoCompareResult result = compareHardwareInfo(hw, hardwareInfo); HardwareInfoCompareResult result = compareHardwareInfo(hw, hardwareInfo);
if(result.compareLevel > criticalCompareLevel) if (result.compareLevel > criticalCompareLevel) {
{ if (set.getBoolean(11)) //isBanned
if(set.getBoolean(11)) //isBanned
{ {
throw new SecurityException("You HWID banned"); throw new SecurityException("You HWID banned");
} }
@ -134,27 +128,29 @@ public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo har
return true; return true;
} }
} }
} catch (SQLException | IOException throwables) } catch (SQLException | IOException throwables) {
{
LogHelper.error(throwables); LogHelper.error(throwables);
throw new SecurityException("SQL error. Please try again later"); throw new SecurityException("SQL error. Please try again later");
} }
return false; return false;
} }
private void changePublicKey(Connection connection, long id, byte[] publicKey) throws SQLException { private void changePublicKey(Connection connection, long id, byte[] publicKey) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardware); PreparedStatement s = connection.prepareStatement(sqlUpdateHardware);
s.setBlob(1, new ByteArrayInputStream(publicKey)); s.setBlob(1, new ByteArrayInputStream(publicKey));
s.setLong(2, id); s.setLong(2, id);
s.executeUpdate(); s.executeUpdate();
} }
private void writeHwidLog(Connection connection, long hwidId, byte[] newPublicKey) throws SQLException { private void writeHwidLog(Connection connection, long hwidId, byte[] newPublicKey) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlCreateHWIDLog); PreparedStatement s = connection.prepareStatement(sqlCreateHWIDLog);
s.setLong(1, hwidId); s.setLong(1, hwidId);
s.setBlob(2, new ByteArrayInputStream(newPublicKey)); s.setBlob(2, new ByteArrayInputStream(newPublicKey));
s.executeUpdate(); s.executeUpdate();
} }
private void setUserHardwareId(Connection connection, String username, long hwidId) throws SQLException { private void setUserHardwareId(Connection connection, String username, long hwidId) throws SQLException {
if(sqlUpdateUsers == null || username == null) return; if (sqlUpdateUsers == null || username == null) return;
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers); PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
s.setLong(1, hwidId); s.setLong(1, hwidId);
s.setString(2, username); s.setString(2, username);

View file

@ -3,8 +3,7 @@
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
public interface JoinServerProtectHandler { public interface JoinServerProtectHandler {
default boolean onJoinServer(String serverID, String username, Client client) default boolean onJoinServer(String serverID, String username, Client client) {
{
return true; return true;
} }
} }

View file

@ -32,8 +32,8 @@ default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signatur
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) { default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) {
return new SecurityReportRequestEvent(); return new SecurityReportRequestEvent();
} }
default VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client)
{ default VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
return new VerifySecureLevelKeyRequestEvent(); return new VerifySecureLevelKeyRequestEvent();
} }
} }

View file

@ -26,7 +26,7 @@ public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface p
if (user == null) throw new AuthException("Username incorrect"); if (user == null) throw new AuthException("Username incorrect");
else throw new AuthException("Username or password incorrect"); else throw new AuthException("Username or password incorrect");
} }
return new AuthProviderDAOResult(login, SecurityHelper.randomStringToken(), user.getPermissions(), user); return new AuthProviderDAOResult(user.getUsername(), SecurityHelper.randomStringToken(), user.getPermissions(), user);
} }
@Override @Override

View file

@ -1,9 +1,9 @@
package pro.gravit.launchserver.auth.provider; package pro.gravit.launchserver.auth.provider;
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.HTTPRequest;
import pro.gravit.launcher.Launcher;
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;
@ -11,28 +11,22 @@
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Objects;
public final class JsonAuthProvider extends AuthProvider { public final class JsonAuthProvider extends AuthProvider {
private static final Gson gson = new Gson(); public URL url;
private URL url; public String apiKey;
private 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");
authRequest authRequest = new authRequest(login, ((AuthPlainPassword) password).password, ip, apiKey); JsonElement content = HTTPRequest.jsonRequest(Launcher.gsonManager.gson.toJsonTree(new authRequest(login, ((AuthPlainPassword) password).password, ip, apiKey)), url);
JsonElement request = gson.toJsonTree(authRequest);
JsonElement content = HTTPRequest.jsonRequest(request, url);
if (!content.isJsonObject()) if (!content.isJsonObject())
return authError("Authentication server response is malformed"); return authError("Authentication server response is malformed");
authResult result = Launcher.gsonManager.gson.fromJson(content, authResult.class);
authResult result = gson.fromJson(content, authResult.class);
if (result.username != null) if (result.username != null)
return new AuthProviderResult(result.username, SecurityHelper.randomStringToken(), new ClientPermissions(result.permissions, result.flags)); return new AuthProviderResult(result.username, SecurityHelper.randomStringToken(), new ClientPermissions(result.permissions, result.flags));
else if (result.error != null) else return authError(Objects.requireNonNullElse(result.error, "Authentication server response is malformed"));
return authError(result.error);
else
return authError("Authentication server response is malformed");
} }
@Override @Override

View file

@ -14,8 +14,8 @@
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; public String message;
private ArrayList<String> whitelist = new ArrayList<>(); public ArrayList<String> whitelist = new ArrayList<>();
public RejectAuthProvider() { public RejectAuthProvider() {
} }

View file

@ -11,15 +11,24 @@
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public final class RequestAuthProvider extends AuthProvider { public final class RequestAuthProvider extends AuthProvider {
private String url; public String url;
private transient Pattern pattern; public transient Pattern pattern;
private String response; public String response;
private boolean flagsEnabled; public boolean flagsEnabled;
public boolean usePermission = true;
public int timeout = 5000;
private final HttpClient client = HttpClient.newBuilder()
.build();
@Override @Override
public void init(LaunchServer srv) { public void init(LaunchServer srv) {
@ -30,15 +39,21 @@ public void init(LaunchServer srv) {
} }
@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, URISyntaxException, InterruptedException {
if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported"); if (!(password instanceof AuthPlainPassword)) throw new AuthException("This password type not supported");
String currentResponse = IOHelper.request(new URL(getFormattedURL(login, ((AuthPlainPassword) password).password, ip))); HttpResponse<String> response = client.send(HttpRequest.newBuilder()
.uri(new URI(getFormattedURL(login, ((AuthPlainPassword) password).password, ip)))
.header("User-Agent", IOHelper.USER_AGENT)
.timeout(Duration.ofMillis(timeout))
.GET()
.build(), HttpResponse.BodyHandlers.ofString());
// Match username // Match username
String currentResponse = response.body();
Matcher matcher = pattern.matcher(currentResponse); Matcher matcher = pattern.matcher(currentResponse);
return matcher.matches() && matcher.groupCount() >= 1 ? return matcher.matches() && matcher.groupCount() >= 1 ?
new AuthProviderResult(matcher.group("username"), SecurityHelper.randomStringToken(), new ClientPermissions( new AuthProviderResult(matcher.group("username"), SecurityHelper.randomStringToken(), new ClientPermissions(
Long.parseLong(matcher.group("permissions")), flagsEnabled ? Long.parseLong(matcher.group("flags")) : 0)) : usePermission ? Long.parseLong(matcher.group("permissions")) : 0, flagsEnabled ? Long.parseLong(matcher.group("flags")) : 0)) :
authError(currentResponse); authError(currentResponse);
} }

View file

@ -52,19 +52,19 @@ public String[] buildConfig(Path inputJar, Path outputJar) {
List<String> confStrs = new ArrayList<>(); List<String> confStrs = new ArrayList<>();
prepare(false); prepare(false);
if (srv.config.launcher.proguardGenMappings) if (srv.config.launcher.proguardGenMappings)
confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'"); confStrs.add("-printmapping '" + mappings.toFile().getName() + "'");
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);
srv.launcherBinary.addonLibs.stream() srv.launcherBinary.addonLibs.stream()
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'") .map(e -> "-libraryjars '" + e.toAbsolutePath().toString() + "'")
.forEach(confStrs::add); .forEach(confStrs::add);
confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'"); confStrs.add("-classobfuscationdictionary '" + words.toFile().getName() + "'");
confStrs.add("@".concat(config.toFile().getName())); confStrs.add("@".concat(config.toFile().getName()));
return confStrs.toArray(new String[0]); return confStrs.toArray(new String[0]);
} }

View file

@ -35,7 +35,7 @@ public class CertificateAutogenTask implements LauncherBuildTask {
public X509Certificate certificate; public X509Certificate certificate;
public X509CertificateHolder bcCertificate; public X509CertificateHolder bcCertificate;
public CMSSignedDataGenerator signedDataGenerator; public CMSSignedDataGenerator signedDataGenerator;
private LaunchServer server; private final LaunchServer server;
public CertificateAutogenTask(LaunchServer server) { public CertificateAutogenTask(LaunchServer server) {
this.server = server; this.server = server;

View file

@ -29,11 +29,11 @@
public class MainBuildTask implements LauncherBuildTask { public class MainBuildTask implements LauncherBuildTask {
public final ClassMetadataReader reader; public final ClassMetadataReader reader;
private final LaunchServer server; private final LaunchServer server;
public Set<String> blacklist = new HashSet<>(); public final Set<String> blacklist = new HashSet<>();
public List<Transformer> transformers = new ArrayList<>(); public final List<Transformer> transformers = new ArrayList<>();
public IOHookSet<BuildContext> preBuildHook = new IOHookSet<>(); public final IOHookSet<BuildContext> preBuildHook = new IOHookSet<>();
public IOHookSet<BuildContext> postBuildHook = new IOHookSet<>(); public final IOHookSet<BuildContext> postBuildHook = new IOHookSet<>();
public Map<String, Object> properties = new HashMap<>(); public final Map<String, Object> properties = new HashMap<>();
public MainBuildTask(LaunchServer srv) { public MainBuildTask(LaunchServer srv) {
server = srv; server = srv;
@ -123,14 +123,13 @@ protected void initProps() {
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; ClassWriter writer;
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); ClassReader cr = new ClassReader(result);
cn = new ClassNode(); cn = new ClassNode();
cr.accept(cn, 0); cr.accept(cn, 0);
} }
@ -144,7 +143,6 @@ public byte[] transformClass(byte[] bytes, String classname, BuildContext contex
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;
cn = null; cn = null;
} }
} }

View file

@ -32,19 +32,18 @@ public Path process(Path inputFile) throws IOException {
Configuration proguard_cfg = new Configuration(); Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar), ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar),
server.proguardConf.proguard.toFile(), System.getProperties()); server.proguardConf.proguard.toFile(), System.getProperties());
if (JVMHelper.JVM_VERSION >= 9) if (JVMHelper.JVM_VERSION >= 9) {
{
Path javaJModsPath = Paths.get(System.getProperty("java.home")).resolve("jmods"); Path javaJModsPath = Paths.get(System.getProperty("java.home")).resolve("jmods");
if(!IOHelper.exists(javaJModsPath)) if (!IOHelper.exists(javaJModsPath)) {
{
LogHelper.warning("Directory %s not found. It is not good", javaJModsPath); LogHelper.warning("Directory %s not found. It is not good", javaJModsPath);
} } else {
else
{
//Find javaFX libraries //Find javaFX libraries
if(!IOHelper.exists(javaJModsPath.resolve("javafx.base.jmod"))) LogHelper.warning("javafx.base.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?"); if (!IOHelper.exists(javaJModsPath.resolve("javafx.base.jmod")))
if(!IOHelper.exists(javaJModsPath.resolve("javafx.graphics.jmod"))) LogHelper.warning("javafx.graphics.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?"); LogHelper.error("javafx.base.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?");
if(!IOHelper.exists(javaJModsPath.resolve("javafx.controls.jmod"))) LogHelper.warning("javafx.controls.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?"); if (!IOHelper.exists(javaJModsPath.resolve("javafx.graphics.jmod")))
LogHelper.error("javafx.graphics.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?");
if (!IOHelper.exists(javaJModsPath.resolve("javafx.controls.jmod")))
LogHelper.error("javafx.controls.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?");
} }
} }
try { try {

View file

@ -0,0 +1,4 @@
package pro.gravit.launchserver.binary.tasks.exe;
public interface BuildExeMainTask {
}

View file

@ -12,7 +12,7 @@
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
public class Launch4JTask implements LauncherBuildTask { public class Launch4JTask implements LauncherBuildTask, BuildExeMainTask {
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 String VERSION = Version.getVersion().getVersionString();
private static final int BUILD = Version.getVersion().build; private static final int BUILD = Version.getVersion().build;

View file

@ -5,8 +5,6 @@
import java.util.Map; import java.util.Map;
public abstract class Command extends pro.gravit.utils.command.Command { public abstract class Command extends pro.gravit.utils.command.Command {
protected final LaunchServer server; protected final LaunchServer server;

View file

@ -1,10 +1,14 @@
package pro.gravit.launchserver.command.basic; package pro.gravit.launchserver.command.basic;
import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509CertificateHolder;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler; import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.LogHelper;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.KeyPair; import java.security.KeyPair;
@ -54,5 +58,28 @@ public void invoke(String... args) throws Exception {
server.certificateManager.writePrivateKey(Paths.get(name.concat(".key")), pair.getPrivate()); server.certificateManager.writePrivateKey(Paths.get(name.concat(".key")), pair.getPrivate());
server.certificateManager.writeCertificate(Paths.get(name.concat(".crt")), cert); server.certificateManager.writeCertificate(Paths.get(name.concat(".crt")), cert);
} }
if (args[0].equals("authstresser")) {
AuthProviderPair pair = server.config.getAuthProviderPair();
AuthPlainPassword plainPassword = new AuthPlainPassword("test");
Runnable runnable = () -> {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; ++i) {
try {
pair.provider.auth("Test", plainPassword, "127.0.0.1");
} catch (AuthException ignored) {
} catch (Exception e) {
throw new RuntimeException(e);
}
if ((i % 10000) == 0) {
LogHelper.info("Completed %d requests", i);
}
}
LogHelper.info("Completed all requests. Time %d ms", System.currentTimeMillis() - startTime);
};
for (int i = 0; i < 7; ++i) {
CommonHelper.newThread(String.format("Stresser #%d", i), true, runnable).start();
}
}
} }
} }

View file

@ -78,6 +78,7 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
service.registerCommand("clients", new ClientsCommand(server)); service.registerCommand("clients", new ClientsCommand(server));
service.registerCommand("signJar", new SignJarCommand(server)); service.registerCommand("signJar", new SignJarCommand(server));
service.registerCommand("signDir", new SignDirCommand(server)); service.registerCommand("signDir", new SignDirCommand(server));
service.registerCommand("securitycheck", new SecurityCheckCommand(server));
Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components"); Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components");
handler.registerCategory(serviceCategory); handler.registerCategory(serviceCategory);
} }

View file

@ -63,11 +63,9 @@ public void invoke(String... args) throws IOException, CommandException {
client.setTitle(dirName); client.setTitle(dirName);
client.setDir(dirName); client.setDir(dirName);
client.setUUID(UUID.randomUUID()); client.setUUID(UUID.randomUUID());
if(client.getServers() != null) if (client.getServers() != null) {
{
ClientProfile.ServerProfile serverProfile = client.getDefaultServerProfile(); ClientProfile.ServerProfile serverProfile = client.getDefaultServerProfile();
if(serverProfile != null) if (serverProfile != null) {
{
serverProfile.name = dirName; serverProfile.name = dirName;
} }
} }

View file

@ -2,6 +2,8 @@
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.optional.OptionalFile;
import pro.gravit.launcher.profiles.optional.actions.*;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.command.Command;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
@ -12,6 +14,9 @@
import java.io.Writer; import java.io.Writer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID; import java.util.UUID;
public class SaveProfilesCommand extends Command { public class SaveProfilesCommand extends Command {
@ -19,11 +24,10 @@ public SaveProfilesCommand(LaunchServer server) {
super(server); super(server);
} }
@SuppressWarnings("deprecated") @SuppressWarnings("deprecation")
public static void saveProfile(ClientProfile profile, Path path) throws IOException { public static void saveProfile(ClientProfile profile, Path path) throws IOException {
if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID()); if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
if(profile.getServers().size() == 0) if (profile.getServers().size() == 0) {
{
ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile(); ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile();
serverProfile.isDefault = true; serverProfile.isDefault = true;
serverProfile.name = profile.getTitle(); serverProfile.name = profile.getTitle();
@ -31,6 +35,34 @@ public static void saveProfile(ClientProfile profile, Path path) throws IOExcept
serverProfile.serverPort = profile.getServerPort(); serverProfile.serverPort = profile.getServerPort();
profile.getServers().add(serverProfile); profile.getServers().add(serverProfile);
} }
for (OptionalFile file : profile.getOptional()) {
if (file.list != null) {
String[] list = file.list;
file.list = null;
if (file.actions == null) file.actions = new ArrayList<>(2);
OptionalAction action;
switch (file.type) {
case FILE:
OptionalActionFile result = new OptionalActionFile(new HashMap<>());
for (String s : list) result.files.put(s, "");
action = result;
break;
case CLASSPATH:
action = new OptionalActionClassPath(list);
break;
case JVMARGS:
action = new OptionalActionJvmArgs(Arrays.asList(list));
break;
case CLIENTARGS:
action = new OptionalActionClientArgs(Arrays.asList(list));
break;
default:
LogHelper.warning("Not converted optional %s with type %s. Type unknown", file.name, file.type.toString());
continue;
}
file.actions.add(action);
}
}
try (Writer w = IOHelper.newWriter(path)) { try (Writer w = IOHelper.newWriter(path)) {
Launcher.gsonManager.configGson.toJson(profile, w); Launcher.gsonManager.configGson.toJson(profile, w);
} }

View file

@ -29,7 +29,7 @@ public void invoke(String... args) {
service.channels.forEach((channel -> { service.channels.forEach((channel -> {
WebSocketFrameHandler frameHandler = channel.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler frameHandler = channel.pipeline().get(WebSocketFrameHandler.class);
Client client = frameHandler.getClient(); Client client = frameHandler.getClient();
String ip = IOHelper.getIP(channel.remoteAddress()); String ip = frameHandler.context.ip != null ? frameHandler.context.ip : IOHelper.getIP(channel.remoteAddress());
if (!client.isAuth) if (!client.isAuth)
LogHelper.info("Channel %s | connectUUID %s | checkSign %s", ip, frameHandler.getConnectUUID(), client.checkSign ? "true" : "false"); LogHelper.info("Channel %s | connectUUID %s | checkSign %s", ip, frameHandler.getConnectUUID(), client.checkSign ? "true" : "false");
else { else {

View file

@ -0,0 +1,190 @@
package pro.gravit.launchserver.command.service;
import org.fusesource.jansi.Ansi;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.handler.MemoryAuthHandler;
import pro.gravit.launchserver.auth.protect.AdvancedProtectHandler;
import pro.gravit.launchserver.auth.protect.NoProtectHandler;
import pro.gravit.launchserver.auth.protect.StdProtectHandler;
import pro.gravit.launchserver.auth.provider.AcceptAuthProvider;
import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.utils.helper.FormatHelper;
import pro.gravit.utils.helper.LogHelper;
import java.util.StringTokenizer;
public class SecurityCheckCommand extends Command {
public SecurityCheckCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return "[]";
}
@Override
public String getUsageDescription() {
return "check configuration";
}
@Override
public void invoke(String... args) throws Exception {
LaunchServerConfig config = server.config;
config.auth.forEach((name, pair) -> {
if (pair.provider instanceof AcceptAuthProvider) {
printCheckResult(LogHelper.Level.INFO, String.format("auth.%s.provider", name), "Accept auth provider", false);
} else {
printCheckResult(LogHelper.Level.INFO, String.format("auth.%s.provider", name), "", true);
}
if (pair.handler instanceof MemoryAuthHandler) {
printCheckResult(LogHelper.Level.INFO, String.format("auth.%s.handler", name), "MemoryAuthHandler test-only", false);
} else {
printCheckResult(LogHelper.Level.INFO, String.format("auth.%s.handler", name), "", true);
}
});
if (config.protectHandler instanceof NoProtectHandler) {
printCheckResult(LogHelper.Level.INFO, "protectHandler", "protectHandler none", false);
} else if (config.protectHandler instanceof AdvancedProtectHandler) {
printCheckResult(LogHelper.Level.INFO, "protectHandler", "", true);
if (!((AdvancedProtectHandler) config.protectHandler).enableHardwareFeature) {
printCheckResult(LogHelper.Level.INFO, "protectHandler.hardwareId", "you can improve security by using hwid provider", null);
} else {
printCheckResult(LogHelper.Level.INFO, "protectHandler.hardwareId", "", true);
}
} else if (config.protectHandler instanceof StdProtectHandler) {
printCheckResult(LogHelper.Level.INFO, "protectHandler", "you can improve security by using advanced", null);
} else {
printCheckResult(LogHelper.Level.INFO, "protectHandler", "unknown protectHandler", null);
}
if (config.netty.address.startsWith("ws://")) {
if (config.netty.ipForwarding)
printCheckResult(LogHelper.Level.INFO, "netty.ipForwarding", "ipForwarding may be used to spoofing ip", null);
printCheckResult(LogHelper.Level.INFO, "netty.address", "websocket connection not secure", false);
} else if (config.netty.address.startsWith("wss://")) {
if (!config.netty.ipForwarding)
printCheckResult(LogHelper.Level.INFO, "netty.ipForwarding", "ipForwarding not enabled. authLimiter may be get incorrect ip", null);
printCheckResult(LogHelper.Level.INFO, "netty.address", "", true);
}
if (config.netty.sendExceptionEnabled) {
printCheckResult(LogHelper.Level.INFO, "netty.sendExceptionEnabled", "recommend \"false\" in production", false);
} else {
printCheckResult(LogHelper.Level.INFO, "netty.sendExceptionEnabled", "", true);
}
if (config.netty.launcherURL.startsWith("http://")) {
printCheckResult(LogHelper.Level.INFO, "netty.launcherUrl", "launcher jar download connection not secure", false);
} else if (config.netty.launcherURL.startsWith("https://")) {
printCheckResult(LogHelper.Level.INFO, "netty.launcherUrl", "", true);
}
if (config.netty.launcherEXEURL.startsWith("http://")) {
printCheckResult(LogHelper.Level.INFO, "netty.launcherExeUrl", "launcher exe download connection not secure", false);
} else if (config.netty.launcherEXEURL.startsWith("https://")) {
printCheckResult(LogHelper.Level.INFO, "netty.launcherExeUrl", "", true);
}
if (config.netty.downloadURL.startsWith("http://")) {
printCheckResult(LogHelper.Level.INFO, "netty.downloadUrl", "assets/clients download connection not secure", false);
} else if (config.netty.downloadURL.startsWith("https://")) {
printCheckResult(LogHelper.Level.INFO, "netty.downloadUrl", "", true);
}
if (!config.sign.enabled) {
printCheckResult(LogHelper.Level.INFO, "sign", "it is recommended to use a signature", null);
} else {
/*boolean bad = false;
KeyStore keyStore = SignHelper.getStore(new File(config.sign.keyStore).toPath(), config.sign.keyStorePass, config.sign.keyStoreType);
X509Certificate[] certChain = (X509Certificate[]) keyStore.getCertificateChain(config.sign.keyAlias);
X509Certificate cert = (X509Certificate) keyStore.getCertificate(config.sign.keyAlias);
cert.checkValidity();
if(certChain.length <= 1) {
printCheckResult(LogHelper.Level.INFO, "sign", "certificate chain contains <2 element(recommend 2 and more)", false);
bad = true;
}
if((cert.getBasicConstraints() & 1) != 0) {
printCheckResult(LogHelper.Level.INFO, "sign", "end certificate - CA", false);
bad = true;
}
for(X509Certificate certificate : certChain)
{
certificate.checkValidity();
}
if(!bad)*/
printCheckResult(LogHelper.Level.INFO, "sign", "", true);
}
if (!config.launcher.enabledProGuard) {
printCheckResult(LogHelper.Level.INFO, "launcher.enabledProGuard", "proguard not enabled", false);
} else {
printCheckResult(LogHelper.Level.INFO, "launcher.enabledProGuard", "", true);
}
if (!config.launcher.stripLineNumbers) {
printCheckResult(LogHelper.Level.INFO, "launcher.stripLineNumbers", "stripLineNumbers not enabled", false);
} else {
printCheckResult(LogHelper.Level.INFO, "launcher.stripLineNumbers", "", true);
}
switch (config.env) {
case DEV:
printCheckResult(LogHelper.Level.INFO, "env", "found env DEV", false);
break;
case DEBUG:
printCheckResult(LogHelper.Level.INFO, "env", "found env DEBUG", false);
break;
case STD:
printCheckResult(LogHelper.Level.INFO, "env", "you can improve security by using env PROD", null);
break;
case PROD:
printCheckResult(LogHelper.Level.INFO, "env", "", true);
break;
}
//Profiles
for (ClientProfile profile : server.getProfiles()) {
boolean bad = false;
String profileModuleName = String.format("profiles.%s", profile.getTitle());
for (String exc : profile.getUpdateExclusions()) {
StringTokenizer tokenizer = new StringTokenizer(exc, "/");
if (exc.endsWith(".jar")) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);
bad = true;
continue;
}
if (tokenizer.hasMoreTokens() && tokenizer.nextToken().equals("mods")) {
String nextToken = tokenizer.nextToken();
if (!tokenizer.hasMoreTokens()) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);
bad = true;
} else {
if (nextToken.equals("memory_repo") || nextToken.equals(profile.getVersion().name)) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);
bad = true;
}
}
}
}
if (!bad)
printCheckResult(LogHelper.Level.INFO, profileModuleName, "", true);
}
LogHelper.info("Check completed");
}
public static void printCheckResult(LogHelper.Level level, String module, String comment, Boolean status) {
LogHelper.rawLog(() -> FormatHelper.rawFormat(level, LogHelper.getDataTime(), false).concat(String.format("[%s] %s - %s", module, comment, status == null ? "WARN" : (status ? "OK" : "FAIL"))),
() -> FormatHelper.rawAnsiFormat(level, LogHelper.getDataTime(), false)
.fgBright(Ansi.Color.WHITE)
.a("[")
.fgBright(Ansi.Color.BLUE)
.a(module)
.fgBright(Ansi.Color.WHITE)
.a("] ".concat(comment).concat(" - "))
.fgBright(status == null ? Ansi.Color.YELLOW : (status ? Ansi.Color.GREEN : Ansi.Color.RED))
.a(status == null ? "WARN" : (status ? "OK" : "FAIL"))
.reset().toString());
}
}

View file

@ -39,7 +39,7 @@ public void invoke(String... args) throws Exception {
} }
private class SignJarVisitor extends SimpleFileVisitor<Path> { private class SignJarVisitor extends SimpleFileVisitor<Path> {
private SignJarTask task; private final SignJarTask task;
public SignJarVisitor(SignJarTask task) { public SignJarVisitor(SignJarTask task) {
this.task = task; this.task = task;

View file

@ -75,6 +75,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
newConfig.netty = new NettyConfig(); newConfig.netty = new NettyConfig();
newConfig.netty.fileServerEnabled = true; newConfig.netty.fileServerEnabled = true;
newConfig.netty.sendExceptionEnabled = false;
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)}; newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
newConfig.netty.performance = new NettyPerformanceConfig(); newConfig.netty.performance = new NettyPerformanceConfig();
try { try {
@ -111,7 +112,6 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов regLimiterComponent.rateLimitMillis = 1000 * 60 * 60 * 10; //Блок на 10 часов
regLimiterComponent.message = "Превышен лимит регистраций"; regLimiterComponent.message = "Превышен лимит регистраций";
newConfig.components.put("regLimiter", regLimiterComponent); newConfig.components.put("regLimiter", regLimiterComponent);
newConfig.netty.sendExceptionEnabled = true;
return newConfig; return newConfig;
} }
@ -184,7 +184,7 @@ public void init(LaunchServer.ReloadType type) {
} }
if (protectHandler != null) { if (protectHandler != null) {
server.registerObject("protectHandler", protectHandler); server.registerObject("protectHandler", protectHandler);
protectHandler.init(); protectHandler.init(server);
protectHandler.checkLaunchServerLicense(); protectHandler.checkLaunchServerLicense();
} }
if (components != null) { if (components != null) {
@ -296,6 +296,7 @@ public static class NettyConfig {
public boolean fileServerEnabled; public boolean fileServerEnabled;
public boolean sendExceptionEnabled; public boolean sendExceptionEnabled;
public boolean ipForwarding; public boolean ipForwarding;
public boolean disableWebApiInterface;
public boolean showHiddenFiles; public boolean showHiddenFiles;
public String launcherURL; public String launcherURL;
public String downloadURL; public String downloadURL;

View file

@ -6,7 +6,7 @@
public abstract class DaoProvider { public abstract class DaoProvider {
public static final ProviderMap<DaoProvider> providers = new ProviderMap<>("DaoProvider"); public static final ProviderMap<DaoProvider> providers = new ProviderMap<>("DaoProvider");
public UserDAO userDAO; public transient UserDAO userDAO;
public static void registerProviders() { public static void registerProviders() {
// None // None

View file

@ -1,7 +1,5 @@
package pro.gravit.launchserver.helper; package pro.gravit.launchserver.helper;
import java.util.Arrays;
public class DamerauHelper { public class DamerauHelper {
//Расстояние Дамерау Левенштейна. GitHub https://github.com/crwohlfeil/damerau-levenshtein //Расстояние Дамерау Левенштейна. GitHub https://github.com/crwohlfeil/damerau-levenshtein
public static int calculateDistance(CharSequence source, CharSequence target) { public static int calculateDistance(CharSequence source, CharSequence target) {

View file

@ -27,32 +27,7 @@
public class SignHelper { public class SignHelper {
public static final OutputStream NULL = new OutputStream() { public static final OutputStream NULL = OutputStream.nullOutputStream();
@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"; public static final String hashFunctionName = "SHA-256";
private SignHelper() { private SignHelper() {

View file

@ -29,7 +29,8 @@ public class LauncherModuleLoader {
private final LaunchServer server; private final LaunchServer server;
public LauncherModuleLoader(LaunchServer server) { public LauncherModuleLoader(LaunchServer server) {
this.server = server; modulesDir = server.dir.resolve("launcher-modules"); this.server = server;
modulesDir = server.dir.resolve("launcher-modules");
} }
public void init() { public void init() {
@ -140,19 +141,17 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
return super.visitFile(file, attrs); return super.visitFile(file, attrs);
} }
} }
public void addClassFieldsToProperties(Map<String, Object> propertyMap, String prefix, Object object, Class<?> classOfObject) throws IllegalAccessException { public void addClassFieldsToProperties(Map<String, Object> propertyMap, String prefix, Object object, Class<?> classOfObject) throws IllegalAccessException {
Field[] fields = classOfObject.getFields(); Field[] fields = classOfObject.getFields();
for (Field field : fields) { for (Field field : fields) {
if ((field.getModifiers() & Modifier.STATIC) != 0) continue; if ((field.getModifiers() & Modifier.STATIC) != 0) continue;
Object obj = field.get(object); Object obj = field.get(object);
String propertyName = prefix.concat(".").concat(field.getName().toLowerCase(Locale.US)); String propertyName = prefix.concat(".").concat(field.getName().toLowerCase(Locale.US));
if(InjectClassAcceptor.isSerializableValue(obj)) if (InjectClassAcceptor.isSerializableValue(obj)) {
{
LogHelper.dev("Property name %s", propertyName); LogHelper.dev("Property name %s", propertyName);
propertyMap.put(propertyName, obj); propertyMap.put(propertyName, obj);
} } else {
else
{
//Try recursive add fields //Try recursive add fields
addClassFieldsToProperties(propertyMap, propertyName, obj, obj.getClass()); addClassFieldsToProperties(propertyMap, propertyName, obj, obj.getClass());
} }

View file

@ -177,7 +177,7 @@ public void readTrustStore(Path dir) throws IOException, CertificateException {
} }
List<X509Certificate> certificates = new ArrayList<>(); List<X509Certificate> certificates = new ArrayList<>();
CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
IOHelper.walk(dir, new SimpleFileVisitor<Path>() { IOHelper.walk(dir, new SimpleFileVisitor<>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".crt")) { if (file.toFile().getName().endsWith(".crt")) {

View file

@ -3,6 +3,7 @@
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import pro.gravit.launcher.managers.GsonManager; import pro.gravit.launcher.managers.GsonManager;
import pro.gravit.launcher.modules.events.PreGsonPhase; import pro.gravit.launcher.modules.events.PreGsonPhase;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.request.JsonResultSerializeAdapter; import pro.gravit.launcher.request.JsonResultSerializeAdapter;
import pro.gravit.launcher.request.WebSocketEvent; import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
@ -39,6 +40,7 @@ public void registerAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(WebSocketEvent.class, new JsonResultSerializeAdapter()); builder.registerTypeAdapter(WebSocketEvent.class, new JsonResultSerializeAdapter());
builder.registerTypeAdapter(AuthRequest.AuthPasswordInterface.class, new UniversalJsonAdapter<>(AuthRequest.providers)); builder.registerTypeAdapter(AuthRequest.AuthPasswordInterface.class, new UniversalJsonAdapter<>(AuthRequest.providers));
builder.registerTypeAdapter(HWIDProvider.class, new UniversalJsonAdapter<>(HWIDProvider.providers)); builder.registerTypeAdapter(HWIDProvider.class, new UniversalJsonAdapter<>(HWIDProvider.providers));
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder)); modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder); //ClientWebSocketService.appendTypeAdapters(builder);
} }

View file

@ -8,10 +8,9 @@
import java.util.Map; import java.util.Map;
public class PingServerManager { public class PingServerManager {
public static long REPORT_EXPIRED_TIME = 20*1000; public static final long REPORT_EXPIRED_TIME = 20 * 1000;
public static class ServerInfoEntry public static class ServerInfoEntry {
{
public PingServerReportRequest.PingServerReport lastReport; public PingServerReportRequest.PingServerReport lastReport;
public long lastReportTime; public long lastReportTime;
public final ClientProfile profile; public final ClientProfile profile;
@ -26,37 +25,34 @@ public ServerInfoEntry(ClientProfile profile) {
this.profile = profile; this.profile = profile;
} }
public boolean isExpired() public boolean isExpired() {
{
return System.currentTimeMillis() - lastReportTime > REPORT_EXPIRED_TIME; return System.currentTimeMillis() - lastReportTime > REPORT_EXPIRED_TIME;
} }
} }
public final Map<String, ServerInfoEntry> map = new HashMap<>(); public final Map<String, ServerInfoEntry> map = new HashMap<>();
private final LaunchServer server; private final LaunchServer server;
public PingServerManager(LaunchServer server) { public PingServerManager(LaunchServer server) {
this.server = server; this.server = server;
} }
public void syncServers()
{ public void syncServers() {
server.getProfiles().forEach((p) -> { server.getProfiles().forEach((p) -> {
for(ClientProfile.ServerProfile sp : p.getServers()) for (ClientProfile.ServerProfile sp : p.getServers()) {
{
ServerInfoEntry entry = map.get(sp.name); ServerInfoEntry entry = map.get(sp.name);
if(entry == null) if (entry == null) {
{
map.put(sp.name, new ServerInfoEntry(p)); map.put(sp.name, new ServerInfoEntry(p));
} }
} }
}); });
} }
public boolean updateServer(String name, PingServerReportRequest.PingServerReport report)
{ public boolean updateServer(String name, PingServerReportRequest.PingServerReport report) {
ServerInfoEntry entry = map.get(name); ServerInfoEntry entry = map.get(name);
if(entry == null) if (entry == null)
return false; return false;
else else {
{
entry.lastReportTime = System.currentTimeMillis(); entry.lastReportTime = System.currentTimeMillis();
entry.lastReport = report; entry.lastReport = report;
return true; return true;

View file

@ -3,17 +3,14 @@
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import java.util.HashMap; import java.util.*;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class SessionManager implements NeedGarbageCollection { public class SessionManager implements NeedGarbageCollection {
public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа
private final Map<Long, Client> clientSet = new HashMap<>(128); private final Map<UUID, Client> clientSet = new HashMap<>(128);
public boolean addClient(Client client) { public boolean addClient(Client client) {
@ -31,21 +28,21 @@ public void garbageCollection() {
} }
public Client getClient(long session) { public Client getClient(UUID session) {
return clientSet.get(session); return clientSet.get(session);
} }
public Client getOrNewClient(long session) { public Client getOrNewClient(UUID session) {
return clientSet.computeIfAbsent(session, Client::new); return clientSet.computeIfAbsent(session, Client::new);
} }
public Client removeClient(long session) { public Client removeClient(UUID session) {
return clientSet.remove(session); return clientSet.remove(session);
} }
public void updateClient(long session) { public void updateClient(UUID session) {
Client c = clientSet.get(session); Client c = clientSet.get(session);
if (c != null) { if (c != null) {
c.up(); c.up();

View file

@ -7,10 +7,13 @@
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.dao.User; import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.LogHelper;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class Client { public class Client {
public long session; public UUID session;
public String auth_id; public String auth_id;
public long timestamp; public long timestamp;
public AuthResponse.ConnectTypes type; public AuthResponse.ConnectTypes type;
@ -20,13 +23,14 @@ public class Client {
public ClientPermissions permissions; public ClientPermissions permissions;
public String username; public String username;
public TrustLevel trustLevel; public TrustLevel trustLevel;
public transient LogHelper.OutputEnity logOutput;
public transient AuthProviderPair auth; public transient AuthProviderPair auth;
public transient User daoObject; public transient User daoObject;
public Client(long session) { public transient Map<String, Object> properties;
public Client(UUID session) {
this.session = session; this.session = session;
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
type = null; type = null;
@ -58,4 +62,15 @@ public static class TrustLevel {
public byte[] publicKey; public byte[] publicKey;
public HardwareReportRequest.HardwareInfo hardwareInfo; public HardwareReportRequest.HardwareInfo hardwareInfo;
} }
@SuppressWarnings("unchecked")
public <T> T getProperty(String name) {
if (properties == null) properties = new HashMap<>();
return (T) properties.get(name);
}
public <T> void setProperty(String name, T object) {
if (properties == null) properties = new HashMap<>();
properties.put(name, object);
}
} }

View file

@ -17,6 +17,7 @@
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.socket.handlers.NettyIpForwardHandler; import pro.gravit.launchserver.socket.handlers.NettyIpForwardHandler;
import pro.gravit.launchserver.socket.handlers.NettyWebAPIHandler;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.handlers.fileserver.FileServerHandler; import pro.gravit.launchserver.socket.handlers.fileserver.FileServerHandler;
import pro.gravit.utils.BiHookSet; import pro.gravit.utils.BiHookSet;
@ -41,8 +42,8 @@ public LauncherNettyServer(LaunchServer server) {
if (config.performance.usingEpoll && !Epoll.isAvailable()) { if (config.performance.usingEpoll && !Epoll.isAvailable()) {
LogHelper.error("Epoll is not available: (netty,perfomance.usingEpoll configured wrongly)", Epoll.unavailabilityCause()); LogHelper.error("Epoll is not available: (netty,perfomance.usingEpoll configured wrongly)", Epoll.unavailabilityCause());
} }
bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread); bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread, "LauncherNettyServer.bossGroup");
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread); workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread, "LauncherNettyServer.workerGroup");
serverBootstrap = new ServerBootstrap(); serverBootstrap = new ServerBootstrap();
service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server); service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server);
serverBootstrap.group(bossGroup, workerGroup) serverBootstrap.group(bossGroup, workerGroup)
@ -60,6 +61,8 @@ public void initChannel(SocketChannel ch) {
pipeline.addLast("forward-http", new NettyIpForwardHandler(context)); pipeline.addLast("forward-http", new NettyIpForwardHandler(context));
pipeline.addLast("websock-comp", new WebSocketServerCompressionHandler()); pipeline.addLast("websock-comp", new WebSocketServerCompressionHandler());
pipeline.addLast("websock-codec", new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true)); pipeline.addLast("websock-codec", new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
if (!server.config.netty.disableWebApiInterface)
pipeline.addLast("webapi", new NettyWebAPIHandler(context));
if (server.config.netty.fileServerEnabled) if (server.config.netty.fileServerEnabled)
pipeline.addLast("fileserver", new FileServerHandler(server.updatesDir, true, config.showHiddenFiles)); pipeline.addLast("fileserver", new FileServerHandler(server.updatesDir, true, config.showHiddenFiles));
pipeline.addLast("launchserver", new WebSocketFrameHandler(context, server, service)); pipeline.addLast("launchserver", new WebSocketFrameHandler(context, server, service));

View file

@ -15,7 +15,7 @@ public static void setUsingEpoll(boolean value) {
epoll = value; epoll = value;
} }
public static EventLoopGroup newEventLoopGroup(int threads) { public static EventLoopGroup newEventLoopGroup(int threads, String poolName) {
if (epoll) if (epoll)
return new EpollEventLoopGroup(threads); return new EpollEventLoopGroup(threads);
else else
@ -28,5 +28,4 @@ public static ChannelFactory<? extends ServerChannel> getServerSocketChannelFact
else else
return NioServerSocketChannel::new; return NioServerSocketChannel::new;
} }
} }

View file

@ -0,0 +1,20 @@
package pro.gravit.launchserver.socket;
import io.netty.util.concurrent.DefaultThreadFactory;
import pro.gravit.utils.helper.LogHelper;
public class NettyThreadFactory extends DefaultThreadFactory {
public NettyThreadFactory(String poolName) {
super(poolName);
}
@Override
protected Thread newThread(Runnable r, String name) {
Thread thread = super.newThread(r, name);
thread.setUncaughtExceptionHandler((th, e) -> {
if (LogHelper.isDebugEnabled())
LogHelper.error(e);
});
return thread;
}
}

View file

@ -13,6 +13,7 @@
import pro.gravit.launcher.events.request.ErrorRequestEvent; import pro.gravit.launcher.events.request.ErrorRequestEvent;
import pro.gravit.launcher.request.WebSocketEvent; import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse; import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*; import pro.gravit.launchserver.socket.response.auth.*;
@ -36,6 +37,7 @@
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
public class WebSocketService { public class WebSocketService {
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>(); public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
@ -45,16 +47,16 @@ public class WebSocketService {
private final Gson gson; private final Gson gson;
//Statistic data //Statistic data
public AtomicLong shortRequestLatency = new AtomicLong(); public final AtomicLong shortRequestLatency = new AtomicLong();
public AtomicLong shortRequestCounter = new AtomicLong(); public final AtomicLong shortRequestCounter = new AtomicLong();
public AtomicLong middleRequestLatency = new AtomicLong(); public final AtomicLong middleRequestLatency = new AtomicLong();
public AtomicLong middleRequestCounter = new AtomicLong(); public final AtomicLong middleRequestCounter = new AtomicLong();
public AtomicLong longRequestLatency = new AtomicLong(); public final AtomicLong longRequestLatency = new AtomicLong();
public AtomicLong longRequestCounter = new AtomicLong(); public final AtomicLong longRequestCounter = new AtomicLong();
public AtomicLong lastRequestTime = new AtomicLong(); public final AtomicLong lastRequestTime = new AtomicLong();
public WebSocketService(ChannelGroup channels, LaunchServer server) { public WebSocketService(ChannelGroup channels, LaunchServer server) {
this.channels = channels; this.channels = channels;
@ -65,6 +67,15 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
this.gson = Launcher.gsonManager.gson; this.gson = Launcher.gsonManager.gson;
} }
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
channels.forEach((channel) -> {
if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) return;
callback.accept(channel, wsHandler);
});
}
public static void registerResponses() { public static void registerResponses() {
providers.register("auth", AuthResponse.class); providers.register("auth", AuthResponse.class);
providers.register("checkServer", CheckServerResponse.class); providers.register("checkServer", CheckServerResponse.class);
@ -79,8 +90,6 @@ public static void registerResponses() {
providers.register("profileByUsername", ProfileByUsername.class); providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class); providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class); providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("register", RegisterResponse.class);
providers.register("setPassword", SetPasswordResponse.class);
providers.register("exit", ExitResponse.class); providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class); providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class); providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
@ -89,6 +98,7 @@ public static void registerResponses() {
providers.register("serverStatus", ServerStatusResponse.class); providers.register("serverStatus", ServerStatusResponse.class);
providers.register("pingServerReport", PingServerReportResponse.class); providers.register("pingServerReport", PingServerReportResponse.class);
providers.register("pingServer", PingServerResponse.class); providers.register("pingServer", PingServerResponse.class);
providers.register("currentUser", CurrentUserResponse.class);
} }
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) { public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
@ -102,32 +112,28 @@ public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client
} }
process(ctx, response, client, ip); process(ctx, response, client, ip);
long executeTime = System.nanoTime() - startTimeNanos; long executeTime = System.nanoTime() - startTimeNanos;
if(executeTime > 0) if (executeTime > 0) {
{
addRequestTimeToStats(executeTime); addRequestTimeToStats(executeTime);
} }
} }
public void addRequestTimeToStats(long nanos) public void addRequestTimeToStats(long nanos) {
{ if (nanos < 100_000_000L) // < 100 millis
if(nanos < 100_000_000L) // < 100 millis
{ {
shortRequestCounter.getAndIncrement(); shortRequestCounter.getAndIncrement();
shortRequestLatency.getAndAdd(nanos); shortRequestLatency.getAndAdd(nanos);
} } else if (nanos < 1_000_000_000L) // > 100 millis and < 1 second
else if(nanos < 1_000_000_000L) // > 100 millis and < 1 second
{ {
middleRequestCounter.getAndIncrement(); middleRequestCounter.getAndIncrement();
middleRequestLatency.getAndAdd(nanos); middleRequestLatency.getAndAdd(nanos);
} } else // > 1 second
else // > 1 second
{ {
longRequestCounter.getAndIncrement(); longRequestCounter.getAndIncrement();
longRequestLatency.getAndAdd(nanos); longRequestLatency.getAndAdd(nanos);
} }
long lastTime = lastRequestTime.get(); long lastTime = lastRequestTime.get();
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
if(currentTime - lastTime > 60*1000) //1 minute if (currentTime - lastTime > 60 * 1000) //1 minute
{ {
lastRequestTime.set(currentTime); lastRequestTime.set(currentTime);
shortRequestLatency.set(0); shortRequestLatency.set(0);

View file

@ -0,0 +1,67 @@
package pro.gravit.launchserver.socket.handlers;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import pro.gravit.launchserver.socket.NettyConnectContext;
import java.util.Comparator;
import java.util.TreeSet;
public class NettyWebAPIHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
private final NettyConnectContext context;
public NettyWebAPIHandler(NettyConnectContext context) {
super();
this.context = context;
}
@FunctionalInterface
public interface SimpleSeverletHandler {
void handle(ChannelHandlerContext ctx, FullHttpRequest msg, NettyConnectContext context) throws Exception;
}
public static class SeverletPathPair {
public final String key;
public final SimpleSeverletHandler callback;
public SeverletPathPair(String key, SimpleSeverletHandler callback) {
this.key = key;
this.callback = callback;
}
}
private static final TreeSet<SeverletPathPair> severletList = new TreeSet<>(Comparator.comparingInt((e) -> -e.key.length()));
public static SeverletPathPair addNewSeverlet(String path, SimpleSeverletHandler callback) {
SeverletPathPair pair = new SeverletPathPair("/webapi/".concat(path), callback);
severletList.add(pair);
return pair;
}
public static SeverletPathPair addUnsafeSeverlet(String path, SimpleSeverletHandler callback) {
SeverletPathPair pair = new SeverletPathPair(path, callback);
severletList.add(pair);
return pair;
}
public static void removeSeverlet(SeverletPathPair pair) {
severletList.remove(pair);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
boolean isNext = true;
for (SeverletPathPair pair : severletList) {
if (msg.uri().startsWith(pair.key)) {
pair.callback.handle(ctx, msg, context);
isNext = false;
break;
}
}
if (isNext) {
msg.retain();
ctx.fireChannelRead(msg);
}
}
}

View file

@ -4,12 +4,12 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*; import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.concurrent.ScheduledFuture;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.NettyConnectContext; import pro.gravit.launchserver.socket.NettyConnectContext;
import pro.gravit.launchserver.socket.WebSocketService; import pro.gravit.launchserver.socket.WebSocketService;
import pro.gravit.utils.BiHookSet; import pro.gravit.utils.BiHookSet;
import pro.gravit.utils.HookSet;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -17,15 +17,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(); private final UUID connectUUID = UUID.randomUUID();
public NettyConnectContext context; public NettyConnectContext context;
public BiHookSet<ChannelHandlerContext, WebSocketFrame> hooks = new BiHookSet<>(); public final BiHookSet<ChannelHandlerContext, WebSocketFrame> hooks = new BiHookSet<>();
private Client client; private Client client;
private ScheduledFuture<?> future;
public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) { public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) {
this.context = context; this.context = context;
@ -50,20 +49,35 @@ public void channelActive(ChannelHandlerContext ctx) {
if (LogHelper.isDevEnabled()) { if (LogHelper.isDevEnabled()) {
LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress())); LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
} }
client = new Client(0); client = new Client(null);
Channel ch = ctx.channel(); Channel ch = ctx.channel();
service.registerClient(ch); service.registerClient(ch);
ctx.executor().schedule(() -> { future = ctx.executor().scheduleAtFixedRate(() -> ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise()), 30L, 30L, TimeUnit.SECONDS);
ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise());
}, 30L, TimeUnit.SECONDS);
} }
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
// ping and pong frames already handled // ping and pong frames already handled
if(hooks.hook(ctx, frame)) return; try {
if (hooks.hook(ctx, frame)) return;
} catch (Throwable ex) {
LogHelper.error(ex);
}
if (frame instanceof TextWebSocketFrame) { if (frame instanceof TextWebSocketFrame) {
try {
service.process(ctx, (TextWebSocketFrame) frame, client, context.ip); service.process(ctx, (TextWebSocketFrame) frame, client, context.ip);
} catch (Throwable ex) {
if (LogHelper.isDebugEnabled()) {
LogHelper.warning("Client %s send invalid request. Connection force closed.", context.ip == null ? IOHelper.getIP(ctx.channel().remoteAddress()) : context.ip);
if (LogHelper.isDevEnabled()) {
LogHelper.dev("Client message: %s", ((TextWebSocketFrame) frame).text());
}
if (LogHelper.isStacktraceEnabled()) {
LogHelper.error(ex);
}
}
ctx.channel().close();
}
} else if ((frame instanceof PingWebSocketFrame)) { } else if ((frame instanceof PingWebSocketFrame)) {
frame.content().retain(); frame.content().retain();
ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content())); ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content()));
@ -77,4 +91,10 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
LogHelper.error(new UnsupportedOperationException(message)); // prevent strange crash here. LogHelper.error(new UnsupportedOperationException(message)); // prevent strange crash here.
} }
} }
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (future != null) future.cancel(true);
super.channelInactive(ctx);
}
} }

View file

@ -16,8 +16,17 @@
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.SimpleDateFormat; import java.time.Clock;
import java.util.*; import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static io.netty.handler.codec.http.HttpMethod.GET; import static io.netty.handler.codec.http.HttpMethod.GET;
@ -26,7 +35,7 @@
public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
public static final SimpleDateFormat dateFormatter; public static final DateTimeFormatter 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"); 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 boolean OLD_ALGO = Boolean.parseBoolean(System.getProperty("launcher.fileserver.oldalgo", "true"));
@ -34,8 +43,7 @@ public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpReque
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*"); 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 = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US).withZone(ZoneId.of("UTC"));
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
} }
private final Path base; private final Path base;
@ -66,7 +74,7 @@ private static void sendListing(ChannelHandlerContext ctx, File dir, String dirP
.append("<ul>") .append("<ul>")
.append("<li><a href=\"../\">..</a></li>\r\n"); .append("<li><a href=\"../\">..</a></li>\r\n");
for (File f : dir.listFiles()) { for (File f : Objects.requireNonNull(dir.listFiles())) {
if ((f.isHidden() && !showHidden) || !f.canRead()) { if ((f.isHidden() && !showHidden) || !f.canRead()) {
continue; continue;
} }
@ -128,7 +136,7 @@ private static void sendNotModified(ChannelHandlerContext ctx) {
* @param response HTTP response * @param response HTTP response
*/ */
private static void setDateHeader(FullHttpResponse response) { private static void setDateHeader(FullHttpResponse response) {
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(new Date(System.currentTimeMillis()))); response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(Instant.ofEpochMilli(System.currentTimeMillis())));
} }
/** /**
@ -139,15 +147,14 @@ private static void setDateHeader(FullHttpResponse response) {
*/ */
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) { private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
// Date header // Date header
Calendar time = new GregorianCalendar(); LocalDateTime time = LocalDateTime.now(Clock.systemUTC());
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime())); response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time));
// Add cache headers // Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS); response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.plus(HTTP_CACHE_SECONDS, ChronoUnit.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.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
response.headers().set( response.headers().set(
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified()))); HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(Instant.ofEpochMilli(fileToCache.lastModified())));
} }
/** /**
@ -209,11 +216,11 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr
// Cache Validation // Cache Validation
String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE); String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE);
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); TemporalAccessor ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
// Only compare up to the second because the datetime format we send to the client // Only compare up to the second because the datetime format we send to the client
// does not have milliseconds // does not have milliseconds
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; long ifModifiedSinceDateSeconds = ifModifiedSinceDate.get(ChronoField.INSTANT_SECONDS);
long fileLastModifiedSeconds = file.lastModified() / 1000; long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
sendNotModified(ctx); sendNotModified(ctx);

View file

@ -57,8 +57,15 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
throw new AuthException("Password decryption error"); throw new AuthException("Password decryption error");
} }
} }
if (clientData.isAuth) {
if (LogHelper.isDevEnabled()) {
LogHelper.warning("Client %s double auth", clientData.username == null ? ip : clientData.username);
}
sendError("You are already logged in");
return;
}
AuthProviderPair pair; AuthProviderPair pair;
if (auth_id.isEmpty()) pair = server.config.getAuthProviderPair(); if (auth_id == null || 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");
@ -81,19 +88,18 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
clientData.permissions = aresult.permissions; clientData.permissions = aresult.permissions;
clientData.auth_id = auth_id; clientData.auth_id = auth_id;
clientData.updateAuth(server); clientData.updateAuth(server);
if (result.playerProfile != null) if (aresult.username != null)
clientData.username = result.playerProfile.username; clientData.username = aresult.username;
else else
clientData.username = login; clientData.username = login;
if(aresult instanceof AuthProviderDAOResult) if (aresult instanceof AuthProviderDAOResult) {
{
clientData.daoObject = ((AuthProviderDAOResult) aresult).daoObject; clientData.daoObject = ((AuthProviderDAOResult) aresult).daoObject;
} }
result.accessToken = aresult.accessToken; result.accessToken = aresult.accessToken;
result.permissions = clientData.permissions; result.permissions = clientData.permissions;
if (getSession) { if (getSession) {
if (clientData.session == 0) { if (clientData.session == null) {
clientData.session = random.nextLong(); clientData.session = UUID.randomUUID();
server.sessionManager.addClient(clientData); server.sessionManager.addClient(clientData);
} }
result.session = clientData.session; result.session = clientData.session;
@ -104,9 +110,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
if (LogHelper.isDebugEnabled()) { if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString()); LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString());
} }
} } else {
else
{
uuid = pair.handler.usernameToUUID(aresult.username); uuid = pair.handler.usernameToUUID(aresult.username);
result.accessToken = null; result.accessToken = null;
} }

View file

@ -0,0 +1,34 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.CurrentUserRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import java.io.IOException;
import java.util.UUID;
public class CurrentUserResponse extends SimpleResponse {
@Override
public String getType() {
return "currentUser";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendResult(new CurrentUserRequestEvent(collectUserInfoFromClient(client)));
}
public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client client) throws IOException {
CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo();
if (client.auth != null && client.isAuth && client.username != null) {
UUID uuid = client.auth.handler.usernameToUUID(client.username);
if (uuid != null) {
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider);
}
}
result.permissions = client.permissions;
return result;
}
}

View file

@ -1,9 +1,11 @@
package pro.gravit.launchserver.socket.response.auth; package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.ExitRequestEvent; import pro.gravit.launcher.events.request.ExitRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
@ -24,7 +26,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
return; return;
} }
if (username == null) { if (username == null) {
if (client.session == 0 && exitAll) { if (client.session == null && exitAll) {
sendError("Session invalid"); sendError("Session invalid");
return; return;
} }
@ -33,47 +35,42 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendError("Exit internal error"); sendError("Exit internal error");
return; return;
} }
Client newClient = new Client(0); Client newClient = new Client(null);
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 != null) server.sessionManager.removeClient(client.session);
if (exitAll) { if (exitAll) {
service.channels.forEach((channel) -> { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
if (channel == null || channel.pipeline() == null) return; Client client1 = webSocketFrameHandler.getClient();
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null || wsHandler == handler) return;
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 (!client1.isAuth || !client.username.equals(client1.username)) return;
} else { } else {
if (chClient.session != client.session) return; if (client1.session != client.session) return;
} }
Client newCusClient = new Client(0); exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER);
newCusClient.checkSign = chClient.checkSign; }));
wsHandler.setClient(newCusClient);
if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event);
});
} }
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
} else { } else {
service.channels.forEach((channel -> { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
if (channel == null || channel.pipeline() == null) return; Client client1 = webSocketFrameHandler.getClient();
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class); if (client1 != null && client.isAuth && client.username != null && client1.username.equals(username)) {
if (wsHandler == null) return; exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER);
Client chClient = wsHandler.getClient(); }
if (!chClient.isAuth || !username.equals(chClient.username)) return;
Client newCusClient = new Client(0);
newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient);
if (chClient.session != 0) server.sessionManager.removeClient(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(ExitRequestEvent.ExitReason.SERVER);
event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event);
})); }));
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.NO_EXIT)); sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.NO_EXIT));
} }
} }
public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Channel channel, ExitRequestEvent.ExitReason reason) {
Client chClient = wsHandler.getClient();
Client newCusClient = new Client(null);
newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient);
if (chClient.session != null) server.sessionManager.removeClient(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(reason);
event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event);
}
} }

View file

@ -25,14 +25,16 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Permissions denied"); sendError("Permissions denied");
return; return;
} }
if (username == null || accessToken == null || serverID == null) {
sendError("Invalid request");
return;
}
boolean success; boolean success;
try { try {
server.authHookManager.joinServerHook.hook(this, client); server.authHookManager.joinServerHook.hook(this, client);
if(server.config.protectHandler instanceof JoinServerProtectHandler) if (server.config.protectHandler instanceof JoinServerProtectHandler) {
{
success = ((JoinServerProtectHandler) server.config.protectHandler).onJoinServer(serverID, username, client); success = ((JoinServerProtectHandler) server.config.protectHandler).onJoinServer(serverID, username, client);
if(!success) if (!success) {
{
sendResult(new JoinServerRequestEvent(false)); sendResult(new JoinServerRequestEvent(false));
return; return;
} }

View file

@ -1,44 +0,0 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class RegisterResponse extends SimpleResponse {
public String login;
public String password;
public String email;
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
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
byte[] normalHash = registerHash(login, server.runtime.registerApiKey);
if (!(client.isAuth && client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN)) && !Arrays.equals(normalHash, verifyHash)) {
sendError("Hash invalid");
return;
}
User checkUser = server.config.dao.userDAO.findByUsername(login);
if (checkUser != null) {
sendError("User already register");
return;
}
}
@Override
public String getType() {
return "register";
}
}

View file

@ -7,9 +7,12 @@
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.UUID;
public class RestoreSessionResponse extends SimpleResponse { public class RestoreSessionResponse extends SimpleResponse {
@LauncherNetworkAPI @LauncherNetworkAPI
public long session; public UUID session;
public boolean needUserInfo;
@Override @Override
public String getType() { public String getType() {
@ -17,13 +20,18 @@ public String getType() {
} }
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client rClient = server.sessionManager.getClient(session); Client rClient = server.sessionManager.getClient(session);
if (rClient == null) { if (rClient == null) {
sendError("Session invalid"); sendError("Session invalid");
return;
} }
WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class); WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class);
frameHandler.setClient(rClient); frameHandler.setClient(rClient);
if (needUserInfo) {
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient)));
} else {
sendResult(new RestoreSessionRequestEvent()); sendResult(new RestoreSessionRequestEvent());
} }
}
} }

View file

@ -1,48 +0,0 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.request.SetPasswordRequestEvent;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class SetPasswordResponse extends SimpleResponse {
public String oldPassword;
public String newPassword;
public String username;
@Override
public String getType() {
return "setPassword";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
if ((oldPassword == null && username == null) || newPassword == null) {
sendError("Request invalid");
return;
}
if (!client.isAuth) {
sendError("You not authorized");
return;
}
if (username != null && !client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN)) {
sendError("You not admin");
return;
}
if (username != null) {
User user = server.config.dao.userDAO.findByUsername(username);
user.setPassword(newPassword);
sendResult(new SetPasswordRequestEvent());
} else {
User user = server.config.dao.userDAO.findByUsername(client.username);
if (user.verifyPassword(oldPassword)) {
user.setPassword(newPassword);
sendResult(new SetPasswordRequestEvent());
} else {
sendError("Old password incorrect");
}
}
}
}

View file

@ -10,6 +10,7 @@
public class PingServerReportResponse extends SimpleResponse { public class PingServerReportResponse extends SimpleResponse {
public PingServerReportRequest.PingServerReport data; public PingServerReportRequest.PingServerReport data;
public String name; public String name;
@Override @Override
public String getType() { public String getType() {
return "pingServerReport"; return "pingServerReport";
@ -17,9 +18,9 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.MANAGEMENT)) if (!client.isAuth || client.permissions == null || !client.permissions.isPermission(ClientPermissions.PermissionConsts.MANAGEMENT)) {
{
sendError("Access denied"); sendError("Access denied");
return;
} }
server.pingServerManager.updateServer(name, data); server.pingServerManager.updateServer(name, data);
sendResult(new PingServerReportRequestEvent()); sendResult(new PingServerReportRequestEvent());

View file

@ -13,6 +13,7 @@
public class PingServerResponse extends SimpleResponse { public class PingServerResponse extends SimpleResponse {
public List<String> serverNames; //May be null public List<String> serverNames; //May be null
@Override @Override
public String getType() { public String getType() {
return "pingServer"; return "pingServer";
@ -21,24 +22,18 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Map<String, PingServerReportRequest.PingServerReport> map = new HashMap<>(); Map<String, PingServerReportRequest.PingServerReport> map = new HashMap<>();
if(serverNames == null) if (serverNames == null) {
{
server.pingServerManager.map.forEach((name, entity) -> { server.pingServerManager.map.forEach((name, entity) -> {
if(server.config.protectHandler instanceof ProfilesProtectHandler) if (server.config.protectHandler instanceof ProfilesProtectHandler) {
{ if (!((ProfilesProtectHandler) server.config.protectHandler).canGetProfile(entity.profile, client)) {
if(!((ProfilesProtectHandler) server.config.protectHandler).canGetProfile(entity.profile, client))
{
return; return;
} }
} }
if(!entity.isExpired()) if (!entity.isExpired()) {
{
map.put(name, entity.lastReport); map.put(name, entity.lastReport);
} }
}); });
} } else {
else
{
sendError("Not implemented"); sendError("Not implemented");
return; return;
} }

View file

@ -3,7 +3,6 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.ServerStatusRequestEvent; import pro.gravit.launcher.events.request.ServerStatusRequestEvent;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.WebSocketService;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.JVMHelper;
@ -18,11 +17,11 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
ServerStatusRequestEvent event = new ServerStatusRequestEvent(server.config.projectName); ServerStatusRequestEvent event = new ServerStatusRequestEvent(server.config.projectName);
event.totalJavaMemory = JVMHelper.RUNTIME.totalMemory(); event.totalJavaMemory = JVMHelper.RUNTIME.totalMemory();
event.freeJavaMemory = JVMHelper.RUNTIME.freeMemory(); event.freeJavaMemory = JVMHelper.RUNTIME.freeMemory();
event.shortLatency = ( service.shortRequestLatency.get() / service.shortRequestCounter.get() ) / 1_000_000; event.shortLatency = (service.shortRequestLatency.get() / service.shortRequestCounter.get()) / 1_000_000;
event.middleLatency = ( service.middleRequestLatency.get() / service.middleRequestCounter.get() ) / 1_000_000; event.middleLatency = (service.middleRequestLatency.get() / service.middleRequestCounter.get()) / 1_000_000;
event.longLatency = ( service.longRequestLatency.get() / service.longRequestCounter.get() ) / 1_000_000; event.longLatency = (service.longRequestLatency.get() / service.longRequestCounter.get()) / 1_000_000;
event.latency = ( ( service.shortRequestLatency.get() + service.middleRequestLatency.get() + service.longRequestLatency.get() ) / event.latency = ((service.shortRequestLatency.get() + service.middleRequestLatency.get() + service.longRequestLatency.get()) /
( service.shortRequestCounter.get() + service.middleRequestCounter.get() + service.longRequestCounter.get() ) ) / 1_000_000; (service.shortRequestCounter.get() + service.middleRequestCounter.get() + service.longRequestCounter.get())) / 1_000_000;
sendResult(event); sendResult(event);
} }
} }

View file

@ -3,9 +3,9 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.BatchProfileByUsernameRequestEvent; import pro.gravit.launcher.events.request.BatchProfileByUsernameRequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile; import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launchserver.auth.AuthProviderPair;
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.utils.helper.LogHelper;
import java.util.UUID; import java.util.UUID;
@ -20,14 +20,19 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
BatchProfileByUsernameRequestEvent result = new BatchProfileByUsernameRequestEvent(); BatchProfileByUsernameRequestEvent result = new BatchProfileByUsernameRequestEvent();
if (list == null) {
sendError("Invalid request");
return;
}
result.playerProfiles = new PlayerProfile[list.length]; result.playerProfiles = new PlayerProfile[list.length];
for (int i = 0; i < list.length; ++i) { for (int i = 0; i < list.length; ++i) {
UUID uuid; UUID uuid;
if (client.auth == null) { AuthProviderPair pair = client.auth;
LogHelper.warning("Client auth is null. Using default."); if (pair == null) {
uuid = server.config.getAuthProviderPair().handler.usernameToUUID(list[i].username); pair = server.config.getAuthProviderPair();
} else uuid = client.auth.handler.usernameToUUID(list[i].username); }
result.playerProfiles[i] = ProfileByUUIDResponse.getProfile(uuid, list[i].username, list[i].client, client.auth.textureProvider); uuid = pair.handler.usernameToUUID(list[i].username);
result.playerProfiles[i] = ProfileByUUIDResponse.getProfile(uuid, list[i].username, list[i].client, pair.textureProvider);
} }
sendResult(result); sendResult(result);
} }

View file

@ -50,7 +50,6 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
String username; String username;
AuthProviderPair pair; AuthProviderPair pair;
if (client.auth == null) { if (client.auth == null) {
LogHelper.warning("Client auth is null. Using default.");
pair = server.config.getAuthProviderPair(); pair = server.config.getAuthProviderPair();
} else { } else {
pair = client.auth; pair = client.auth;

View file

@ -2,9 +2,9 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent; import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
import pro.gravit.launchserver.auth.AuthProviderPair;
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.utils.helper.LogHelper;
import java.util.UUID; import java.util.UUID;
@ -20,10 +20,13 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
UUID uuid; UUID uuid;
if (client.auth == null) { AuthProviderPair pair = client.auth;
LogHelper.warning("Client auth is null. Using default."); if (pair == null) pair = server.config.getAuthProviderPair();
uuid = server.config.getAuthProviderPair().handler.usernameToUUID(username); uuid = pair.handler.usernameToUUID(username);
} else uuid = client.auth.handler.usernameToUUID(username); if (uuid == null) {
sendResult(new ProfileByUsernameRequestEvent(ProfileByUUIDResponse.getProfile(uuid, username, this.client, client.auth.textureProvider))); sendError("User not found");
return;
}
sendResult(new ProfileByUsernameRequestEvent(ProfileByUUIDResponse.getProfile(uuid, username, this.client, pair.textureProvider)));
} }
} }

View file

@ -17,17 +17,13 @@ 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 HardwareProtectHandler) if (server.config.protectHandler instanceof HardwareProtectHandler) {
{
try { try {
((HardwareProtectHandler) server.config.protectHandler).onHardwareReport(this, client); ((HardwareProtectHandler) server.config.protectHandler).onHardwareReport(this, client);
} catch (SecurityException e) } catch (SecurityException e) {
{
sendError(e.getMessage()); sendError(e.getMessage());
} }
} } else {
else
{
sendResult(new HardwareReportRequestEvent()); sendResult(new HardwareReportRequestEvent());
} }
} }

View file

@ -1,7 +1,6 @@
package pro.gravit.launchserver.socket.response.secure; package pro.gravit.launchserver.socket.response.secure;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
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.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
@ -41,8 +40,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
client.trustLevel.publicKey = publicKey; client.trustLevel.publicKey = publicKey;
try { try {
sendResult(secureProtectHandler.onSuccessVerify(client)); sendResult(secureProtectHandler.onSuccessVerify(client));
} catch (SecurityException e) } catch (SecurityException e) {
{
sendError(e.getMessage()); sendError(e.getMessage());
} }

View file

@ -23,6 +23,10 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Access denied"); sendError("Access denied");
return; return;
} }
if (dirName == null) {
sendError("Invalid request");
return;
}
HashedDir dir = server.updatesDirMap.get(dirName); HashedDir dir = server.updatesDirMap.get(dirName);
if (dir == null) { if (dir == null) {
sendError(String.format("Directory %s not found", dirName)); sendError(String.format("Directory %s not found", dirName));

View file

@ -25,6 +25,7 @@ public static void prepare() throws Throwable {
classLoader = new ASMClassLoader(ASMTransformersTest.class.getClassLoader()); classLoader = new ASMClassLoader(ASMTransformersTest.class.getClassLoader());
} }
@SuppressWarnings("unchecked")
@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));

View file

@ -27,6 +27,7 @@
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName, "Premain-Class": mainAgentName,
"Can-Redefine-Classes": "true", "Can-Redefine-Classes": "true",
"Multi-Release": "true",
"Can-Retransform-Classes": "true", "Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true", "Can-Set-Native-Method-Prefix": "true",
"Multi-Release-Jar": "true") "Multi-Release-Jar": "true")
@ -93,6 +94,18 @@ task dumpLibs(type: Copy) {
url = 'https://www.gnu.org/licenses/gpl-3.0.html' url = 'https://www.gnu.org/licenses/gpl-3.0.html'
} }
} }
developers {
developer {
id = 'gravita'
name = 'Gravita'
email = 'gravita@gravit.pro'
}
developer {
id = 'zaxar163'
name = 'Zaxar163'
email = 'zahar.vcherachny@yandex.ru'
}
}
scm { scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git' connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git' developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'

View file

@ -22,8 +22,8 @@ public class ClientLauncherWrapper {
public static final String NO_JAVA_CHECK_PROPERTY = "launcher.noJavaCheck"; public static final String NO_JAVA_CHECK_PROPERTY = "launcher.noJavaCheck";
public static boolean noJavaCheck = Boolean.getBoolean(NO_JAVA_CHECK_PROPERTY); public static boolean noJavaCheck = Boolean.getBoolean(NO_JAVA_CHECK_PROPERTY);
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY); public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
public static class JavaVersion
{ public static class JavaVersion {
public final Path jvmDir; public final Path jvmDir;
public final int version; public final int version;
public boolean enabledJavaFX; public boolean enabledJavaFX;
@ -33,31 +33,29 @@ public JavaVersion(Path jvmDir, int version) {
this.version = version; this.version = version;
this.enabledJavaFX = true; this.enabledJavaFX = true;
} }
public static JavaVersion getCurrentJavaVersion()
{ public static JavaVersion getCurrentJavaVersion() {
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion()); return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion());
} }
public static JavaVersion getByPath(Path jvmDir) throws IOException { public static JavaVersion getByPath(Path jvmDir) throws IOException {
Path releaseFile = jvmDir.resolve("release"); Path releaseFile = jvmDir.resolve("release");
if(!IOHelper.isFile(releaseFile)) return null; if (!IOHelper.isFile(releaseFile)) return null;
Properties properties = new Properties(); Properties properties = new Properties();
properties.load(IOHelper.newReader(releaseFile)); properties.load(IOHelper.newReader(releaseFile));
int javaVersion = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", "")); int javaVersion = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, javaVersion); JavaVersion resultJavaVersion = new JavaVersion(jvmDir, javaVersion);
if(javaVersion <= 8) if (javaVersion <= 8) {
{
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt"); resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
} } else {
else
{
resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir, "javafx.base") != null; resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir, "javafx.base") != null;
if(!resultJavaVersion.enabledJavaFX) if (!resultJavaVersion.enabledJavaFX)
resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir.resolve("jre"), "javafx.base") != null; resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir.resolve("jre"), "javafx.base") != null;
} }
return resultJavaVersion; return resultJavaVersion;
} }
public static boolean isExistExtJavaLibrary(Path jvmDir, String name)
{ public static boolean isExistExtJavaLibrary(Path jvmDir, String name) {
Path jrePath = jvmDir.resolve("lib").resolve("ext").resolve(name.concat(".jar")); Path jrePath = jvmDir.resolve("lib").resolve("ext").resolve(name.concat(".jar"));
Path jdkPath = jvmDir.resolve("jre").resolve("lib").resolve("ext").resolve(name.concat(".jar")); Path jdkPath = jvmDir.resolve("jre").resolve("lib").resolve("ext").resolve(name.concat(".jar"));
return IOHelper.isFile(jrePath) || IOHelper.isFile(jdkPath); return IOHelper.isFile(jrePath) || IOHelper.isFile(jdkPath);
@ -94,12 +92,11 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
JavaVersion javaVersion = null; JavaVersion javaVersion = null;
try { try {
if(!noJavaCheck) javaVersion = findJava(); if (!noJavaCheck) javaVersion = findJava();
} catch (Throwable e) { } catch (Throwable e) {
LogHelper.error(e); LogHelper.error(e);
} }
if (javaVersion == null) if (javaVersion == null) {
{
javaVersion = JavaVersion.getCurrentJavaVersion(); javaVersion = JavaVersion.getCurrentJavaVersion();
} }
@ -187,55 +184,46 @@ public static boolean tryAddModule(Path[] paths, String moduleName, StringBuilde
return false; return false;
} }
public static JavaVersion findJavaByProgramFiles(Path path) public static JavaVersion findJavaByProgramFiles(Path path) {
{
LogHelper.debug("Check Java in %s", path.toString()); LogHelper.debug("Check Java in %s", path.toString());
JavaVersion selectedJava = null; JavaVersion selectedJava = null;
File[] candidates = path.toFile().listFiles(File::isDirectory); File[] candidates = path.toFile().listFiles(File::isDirectory);
if(candidates == null) return null; if (candidates == null) return null;
for(File candidate : candidates) for (File candidate : candidates) {
{
Path javaPath = candidate.toPath(); Path javaPath = candidate.toPath();
try { try {
JavaVersion javaVersion = JavaVersion.getByPath(javaPath); JavaVersion javaVersion = JavaVersion.getByPath(javaPath);
if(javaVersion == null || javaVersion.version < 8) continue; if (javaVersion == null || javaVersion.version < 8) continue;
LogHelper.debug("Found Java %d in %s (javafx %s)", javaVersion.version, javaVersion.jvmDir.toString(), javaVersion.enabledJavaFX ? "true" : "false"); LogHelper.debug("Found Java %d in %s (javafx %s)", javaVersion.version, javaVersion.jvmDir.toString(), javaVersion.enabledJavaFX ? "true" : "false");
if(javaVersion.enabledJavaFX && (selectedJava == null || !selectedJava.enabledJavaFX)) if (javaVersion.enabledJavaFX && (selectedJava == null || !selectedJava.enabledJavaFX)) {
{
selectedJava = javaVersion; selectedJava = javaVersion;
continue; continue;
} }
if(selectedJava != null && javaVersion.enabledJavaFX && javaVersion.version < selectedJava.version) if (selectedJava != null && javaVersion.enabledJavaFX && javaVersion.version < selectedJava.version) {
{
selectedJava = javaVersion; selectedJava = javaVersion;
} }
} catch (IOException e) { } catch (IOException e) {
LogHelper.error(e); LogHelper.error(e);
} }
} }
if(selectedJava != null) if (selectedJava != null) {
{
LogHelper.debug("Selected Java %d in %s (javafx %s)", selectedJava.version, selectedJava.jvmDir.toString(), selectedJava.enabledJavaFX ? "true" : "false"); LogHelper.debug("Selected Java %d in %s (javafx %s)", selectedJava.version, selectedJava.jvmDir.toString(), selectedJava.enabledJavaFX ? "true" : "false");
} }
return selectedJava; return selectedJava;
} }
public static JavaVersion findJava() public static JavaVersion findJava() {
{ if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
{
JavaVersion result = null; JavaVersion result = null;
Path defaultJvmContainerDir = Paths.get(System.getProperty("java.home")).getParent(); Path defaultJvmContainerDir = Paths.get(System.getProperty("java.home")).getParent();
if(defaultJvmContainerDir.getParent().getFileName().toString().contains("x86")) //Program Files (x86) ? if (defaultJvmContainerDir.getParent().getFileName().toString().contains("x86")) //Program Files (x86) ?
{ {
Path programFiles64 = defaultJvmContainerDir.getParent().getParent().resolve("Program Files").resolve("Java"); Path programFiles64 = defaultJvmContainerDir.getParent().getParent().resolve("Program Files").resolve("Java");
if(IOHelper.isDir(programFiles64)) if (IOHelper.isDir(programFiles64)) {
{
result = findJavaByProgramFiles(programFiles64); result = findJavaByProgramFiles(programFiles64);
} }
} }
if(result == null) if (result == null) {
{
result = findJavaByProgramFiles(defaultJvmContainerDir); result = findJavaByProgramFiles(defaultJvmContainerDir);
} }
return result; return result;
@ -243,8 +231,7 @@ public static JavaVersion findJava()
return null; return null;
} }
public static int getJavaVersion(String version) public static int getJavaVersion(String version) {
{
if (version.startsWith("1.")) { if (version.startsWith("1.")) {
version = version.substring(2, 3); version = version.substring(2, 3);
} else { } else {

View file

@ -13,8 +13,10 @@
import pro.gravit.launcher.managers.ClientGsonManager; import pro.gravit.launcher.managers.ClientGsonManager;
import pro.gravit.launcher.managers.ConsoleManager; import pro.gravit.launcher.managers.ConsoleManager;
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.RequestException; import pro.gravit.launcher.request.RequestException;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.RestoreSessionRequest; import pro.gravit.launcher.request.auth.RestoreSessionRequest;
import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.utils.NativeJVMHalt; import pro.gravit.launcher.utils.NativeJVMHalt;
@ -113,6 +115,8 @@ public static void main(String... args) throws Throwable {
} }
public static void initGson(ClientModuleManager modulesManager) { public static void initGson(ClientModuleManager modulesManager) {
AuthRequest.registerProviders();
OptionalAction.registerProviders();
Launcher.gsonManager = new ClientGsonManager(modulesManager); Launcher.gsonManager = new ClientGsonManager(modulesManager);
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
} }

View file

@ -14,8 +14,12 @@
import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.patches.FMLPatcher; import pro.gravit.launcher.patches.FMLPatcher;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClientArgs;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.RequestException; import pro.gravit.launcher.request.RequestException;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.RestoreSessionRequest; import pro.gravit.launcher.request.auth.RestoreSessionRequest;
import pro.gravit.launcher.serialize.HInput; import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.utils.DirWatcher; import pro.gravit.launcher.utils.DirWatcher;
@ -50,6 +54,8 @@ private static ClientLauncherProcess.ClientParams readParams(SocketAddress addre
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);
params.assetHDir = new HashedDir(input); params.assetHDir = new HashedDir(input);
boolean isNeedReadJavaDir = input.readBoolean();
if (isNeedReadJavaDir)
params.javaHDir = new HashedDir(input); params.javaHDir = new HashedDir(input);
return params; return params;
} }
@ -93,9 +99,10 @@ public static void main(String[] args) throws Throwable {
List<URL> classpath = new LinkedList<>(); List<URL> classpath = new LinkedList<>();
resolveClassPathStream(clientDir, params.profile.getClassPath()).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath)); resolveClassPathStream(clientDir, params.profile.getClassPath()).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
params.profile.pushOptionalClassPath((opt) -> { for (OptionalAction a : params.actions) {
resolveClassPathStream(clientDir, opt).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath)); if (a instanceof OptionalActionClassPath)
}); resolveClassPathStream(clientDir, ((OptionalActionClassPath) a).args).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
}
classLoader = new ClientClassLoader(classpath.toArray(new URL[0]), ClassLoader.getSystemClassLoader()); classLoader = new ClientClassLoader(classpath.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(classLoader); Thread.currentThread().setContextClassLoader(classLoader);
classLoader.nativePath = clientDir.resolve("natives").toString(); classLoader.nativePath = clientDir.resolve("natives").toString();
@ -133,26 +140,34 @@ public static void main(String[] args) throws Throwable {
LogHelper.debug("Starting JVM and client WatchService"); LogHelper.debug("Starting JVM and client WatchService");
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher(); FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher(); FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
Path javaDir = Paths.get(System.getProperty("java.home"));
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, digest); try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, digest);
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, digest)) { DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, digest);
DirWatcher javaWatcher = params.javaHDir == null ? null : new DirWatcher(javaDir, params.javaHDir, null, digest)) {
// Verify current state of all dirs // Verify current state of all dirs
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest); //verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
//for (OptionalFile s : Launcher.profile.getOptional()) { //for (OptionalFile s : Launcher.profile.getOptional()) {
// if (params.updateOptional.contains(s)) s.mark = true; // if (params.updateOptional.contains(s)) s.mark = true;
// else hdir.removeR(s.file); // else hdir.removeR(s.file);
//} //}
Launcher.profile.pushOptionalFile(params.clientHDir, false);
// Start WatchService, and only then client // Start WatchService, and only then client
CommonHelper.newThread("Asset Directory Watcher", true, assetWatcher).start(); CommonHelper.newThread("Asset Directory Watcher", true, assetWatcher).start();
CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start(); CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start();
if (javaWatcher != null)
CommonHelper.newThread("Java Directory Watcher", true, clientWatcher).start();
verifyHDir(assetDir, params.assetHDir, assetMatcher, digest); verifyHDir(assetDir, params.assetHDir, assetMatcher, digest);
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest); verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
if (javaWatcher != null)
verifyHDir(javaDir, params.javaHDir, null, digest);
if (params.javaHDir != null)
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params)); LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
launch(profile, params); launch(profile, params);
} }
} }
private static void initGson(ClientModuleManager moduleManager) { private static void initGson(ClientModuleManager moduleManager) {
AuthRequest.registerProviders();
OptionalAction.registerProviders();
Launcher.gsonManager = new ClientGsonManager(moduleManager); Launcher.gsonManager = new ClientGsonManager(moduleManager);
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
} }
@ -223,7 +238,12 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir); System.setProperty("minecraft.applet.TargetDirectory", params.clientDir);
} }
Collections.addAll(args, profile.getClientArgs()); Collections.addAll(args, profile.getClientArgs());
profile.pushOptionalClientArgs(args); for(OptionalAction action : params.actions) {
if(action instanceof OptionalActionClientArgs)
{
args.addAll(((OptionalActionClientArgs) action).args);
}
}
List<String> copy = new ArrayList<>(args); List<String> copy = new ArrayList<>(args);
for (int i = 0, l = copy.size(); i < l; i++) { for (int i = 0, l = copy.size(); i < l; i++) {
String s = copy.get(i); String s = copy.get(i);

View file

@ -10,6 +10,10 @@
import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.PlayerProfile; import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launcher.profiles.optional.OptionalView;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClientArgs;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionJvmArgs;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.serialize.HOutput; import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.utils.Version; import pro.gravit.utils.Version;
@ -34,6 +38,7 @@ public class ClientLauncherProcess {
public Path executeFile; public Path executeFile;
public Path workDir; public Path workDir;
public Path javaDir; public Path javaDir;
public int bits;
public boolean useLegacyJavaClassPathProperty; public boolean useLegacyJavaClassPathProperty;
public boolean isStarted; public boolean isStarted;
private transient Process process; private transient Process process;
@ -48,17 +53,17 @@ public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, Strin
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir,
ClientProfile profile, PlayerProfile playerProfile, String accessToken, ClientProfile profile, PlayerProfile playerProfile, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) { HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this(clientDir, assetDir, javaDir, clientDir.resolve("resourcepacks"), profile, playerProfile, accessToken, clientHDir, assetHDir, jvmHDir); this(clientDir, assetDir, javaDir, clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
} }
public ClientLauncherProcess(Path clientDir, Path assetDir, public ClientLauncherProcess(Path clientDir, Path assetDir,
ClientProfile profile, PlayerProfile playerProfile, String accessToken, ClientProfile profile, PlayerProfile playerProfile, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) { HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this(clientDir, assetDir, Paths.get(System.getProperty("java.home")), clientDir.resolve("resourcepacks"), profile, playerProfile, accessToken, clientHDir, assetHDir, jvmHDir); this(clientDir, assetDir, Paths.get(System.getProperty("java.home")), clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
} }
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path resourcePackDir, public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path resourcePackDir,
ClientProfile profile, PlayerProfile playerProfile, String accessToken, ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) { HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this.workDir = clientDir.toAbsolutePath(); this.workDir = clientDir.toAbsolutePath();
this.javaDir = javaDir; this.javaDir = javaDir;
@ -73,6 +78,10 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
this.params.assetHDir = assetHDir; this.params.assetHDir = assetHDir;
this.params.clientHDir = clientHDir; this.params.clientHDir = clientHDir;
this.params.javaHDir = jvmHDir; this.params.javaHDir = jvmHDir;
if (view != null) {
this.params.actions = view.getEnabledActions();
}
this.bits = JVMHelper.JVM_BITS;
applyClientProfile(); applyClientProfile();
} }
@ -86,7 +95,11 @@ public static String getPathSeparator() {
private void applyClientProfile() { 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); for (OptionalAction a : this.params.actions) {
if (a instanceof OptionalActionJvmArgs) {
this.jvmArgs.addAll(((OptionalActionJvmArgs) a).args);
}
}
this.systemEnv.put("JAVA_HOME", javaDir.toString()); this.systemEnv.put("JAVA_HOME", javaDir.toString());
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath()); Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
if (params.ram > 0) { if (params.ram > 0) {
@ -148,9 +161,14 @@ public void runWriteParams(SocketAddress address) throws IOException {
output.writeByteArray(serializedMainParams, 0); output.writeByteArray(serializedMainParams, 0);
params.clientHDir.write(output); params.clientHDir.write(output);
params.assetHDir.write(output); params.assetHDir.write(output);
if (params.javaHDir == null || params.javaHDir == params.assetHDir) { //TODO: OLD RUNTIME USE params.assetHDir AS NULL IN java.javaHDir
output.writeBoolean(false);
} else {
output.writeBoolean(true);
params.javaHDir.write(output); params.javaHDir.write(output);
} }
} }
}
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this)); LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
} }
@ -185,9 +203,11 @@ public static class ClientParams {
public int height; public int height;
public Set<OptionalAction> actions = new HashSet<>();
//======== //========
public long session; public UUID session;
public transient HashedDir assetHDir; public transient HashedDir assetHDir;
@ -255,7 +275,11 @@ private void addModernClientArgs(Collection<String> args) {
Collections.addAll(args, "--server", profile.getServerAddress()); Collections.addAll(args, "--server", profile.getServerAddress());
Collections.addAll(args, "--port", Integer.toString(profile.getServerPort())); Collections.addAll(args, "--port", Integer.toString(profile.getServerPort()));
} }
profile.pushOptionalClientArgs(args); for (OptionalAction a : actions) {
if (a instanceof OptionalActionClientArgs) {
args.addAll(((OptionalActionClientArgs) a).args);
}
}
// Add window size args // Add window size args
if (fullScreen) if (fullScreen)
Collections.addAll(args, "--fullscreen", Boolean.toString(true)); Collections.addAll(args, "--fullscreen", Boolean.toString(true));

View file

@ -36,8 +36,15 @@ public final class ServerPinger {
private Instant cacheTime = null; private Instant cacheTime = null;
public ServerPinger(ClientProfile profile) { public ServerPinger(ClientProfile profile) {
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address"); this(profile.getDefaultServerProfile(), profile.getVersion());
this.version = Objects.requireNonNull(profile.getVersion(), "version"); }
public ServerPinger(ClientProfile.ServerProfile profile, ClientProfile.Version version) {
if (profile == null) {
throw new NullPointerException("ServerProfile null");
}
this.address = profile.toSocketAddress();
this.version = Objects.requireNonNull(version, "version");
} }
private static String readUTF16String(HInput input) throws IOException { private static String readUTF16String(HInput input) throws IOException {

View file

@ -9,9 +9,9 @@
import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launcher.request.secure.HardwareReportRequest;
public class HWIDProvider { public class HWIDProvider {
public SystemInfo systemInfo; public final SystemInfo systemInfo;
public OperatingSystem system; public final OperatingSystem system;
public HardwareAbstractionLayer hardware; public final HardwareAbstractionLayer hardware;
public HWIDProvider() { public HWIDProvider() {
systemInfo = new SystemInfo(); systemInfo = new SystemInfo();

View file

@ -14,10 +14,29 @@ api project(':LauncherCore')
} }
} }
sourceSets {
java11 {
java {
srcDirs = ['src/main/java11']
}
dependencies {
java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava }
}
}
}
jar { jar {
into('META-INF/versions/11') {
from sourceSets.java11.output
}
classifier = 'clean' classifier = 'clean'
} }
compileJava11Java {
sourceCompatibility = 11
targetCompatibility = 11
}
task sourcesJar(type: Jar) { task sourcesJar(type: Jar) {
from sourceSets.main.allJava from sourceSets.main.allJava
archiveClassifier = 'sources' archiveClassifier = 'sources'
@ -47,6 +66,18 @@ task javadocJar(type: Jar) {
url = 'https://www.gnu.org/licenses/gpl-3.0.html' url = 'https://www.gnu.org/licenses/gpl-3.0.html'
} }
} }
developers {
developer {
id = 'gravita'
name = 'Gravita'
email = 'gravita@gravit.pro'
}
developer {
id = 'zaxar163'
name = 'Zaxar163'
email = 'zahar.vcherachny@yandex.ru'
}
}
scm { scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git' connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git' developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'

View file

@ -31,6 +31,7 @@ public ClientPermissions(long permissions, long flags) {
public static ClientPermissions getSuperuserAccount() { public static ClientPermissions getSuperuserAccount() {
ClientPermissions perm = new ClientPermissions(); ClientPermissions perm = new ClientPermissions();
perm.setPermission(PermissionConsts.ADMIN, true);
return perm; return perm;
} }

View file

@ -5,8 +5,10 @@
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile; import pro.gravit.launcher.profiles.PlayerProfile;
public class AuthRequestEvent extends RequestEvent { import java.util.UUID;
public class AuthRequestEvent extends RequestEvent {
public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa";
@LauncherNetworkAPI @LauncherNetworkAPI
public ClientPermissions permissions; public ClientPermissions permissions;
@LauncherNetworkAPI @LauncherNetworkAPI
@ -16,7 +18,7 @@ public class AuthRequestEvent extends RequestEvent {
@LauncherNetworkAPI @LauncherNetworkAPI
public String protectToken; public String protectToken;
@LauncherNetworkAPI @LauncherNetworkAPI
public long session; public UUID session;
public AuthRequestEvent() { public AuthRequestEvent() {
} }
@ -34,7 +36,7 @@ public AuthRequestEvent(ClientPermissions permissions, PlayerProfile playerProfi
this.protectToken = protectToken; this.protectToken = protectToken;
} }
public AuthRequestEvent(ClientPermissions permissions, PlayerProfile playerProfile, String accessToken, String protectToken, long session) { public AuthRequestEvent(ClientPermissions permissions, PlayerProfile playerProfile, String accessToken, String protectToken, UUID session) {
this.permissions = permissions; this.permissions = permissions;
this.playerProfile = playerProfile; this.playerProfile = playerProfile;
this.accessToken = accessToken; this.accessToken = accessToken;

View file

@ -0,0 +1,24 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile;
public class CurrentUserRequestEvent extends RequestEvent {
public static class UserInfo {
public ClientPermissions permissions;
public String accessToken;
public PlayerProfile playerProfile;
}
public final UserInfo userInfo;
public CurrentUserRequestEvent(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public String getType() {
return "currentUser";
}
}

View file

@ -3,6 +3,15 @@
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
public class RestoreSessionRequestEvent extends RequestEvent { public class RestoreSessionRequestEvent extends RequestEvent {
public CurrentUserRequestEvent.UserInfo userInfo;
public RestoreSessionRequestEvent() {
}
public RestoreSessionRequestEvent(CurrentUserRequestEvent.UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override @Override
public String getType() { public String getType() {
return "restoreSession"; return "restoreSession";

View file

@ -1,7 +1,6 @@
package pro.gravit.launcher.events.request; package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.utils.helper.JVMHelper;
public class ServerStatusRequestEvent extends RequestEvent { public class ServerStatusRequestEvent extends RequestEvent {
public final String projectName; public final String projectName;

View file

@ -1,10 +0,0 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
public class SetPasswordRequestEvent extends RequestEvent {
@Override
public String getType() {
return "setPassword";
}
}

View file

@ -1,15 +0,0 @@
package pro.gravit.launcher.hwid;
@Deprecated
public interface HWID {
int getLevel(); //Уровень доверия, насколько уникальные значения
int getAntiLevel(); //Уровень лживости, насколько фальшивые значения
int compare(HWID hwid);
boolean isNull();
void normalize();
}

View file

@ -72,20 +72,23 @@ public final class ClientProfile implements Comparable<ClientProfile> {
@LauncherNetworkAPI @LauncherNetworkAPI
private String mainClass; private String mainClass;
public static class ServerProfile public static class ServerProfile {
{
public String name; public String name;
public String serverAddress; public String serverAddress;
public int serverPort; public int serverPort;
public boolean isDefault = true; public boolean isDefault = true;
public InetSocketAddress toSocketAddress() {
return InetSocketAddress.createUnresolved(serverAddress, serverPort);
} }
}
@LauncherNetworkAPI @LauncherNetworkAPI
private List<ServerProfile> servers = new ArrayList<>(1); private List<ServerProfile> servers = new ArrayList<>(1);
public ServerProfile getDefaultServerProfile()
{ public ServerProfile getDefaultServerProfile() {
for(ServerProfile profile : servers) for (ServerProfile profile : servers) {
{ if (profile.isDefault) return profile;
if(profile.isDefault) return profile;
} }
return null; return null;
} }
@ -127,6 +130,10 @@ public String getAssetDir() {
return assetDir; return assetDir;
} }
public List<String> getUpdateExclusions() {
return Collections.unmodifiableList(updateExclusions);
}
public FileNameMatcher getClientUpdateMatcher(/*boolean excludeOptional*/) { public FileNameMatcher getClientUpdateMatcher(/*boolean excludeOptional*/) {
String[] updateArray = update.toArray(new String[0]); String[] updateArray = update.toArray(new String[0]);
String[] verifyArray = updateVerify.toArray(new String[0]); String[] verifyArray = updateVerify.toArray(new String[0]);
@ -169,28 +176,36 @@ public void updateOptionalGraph() {
if (file.dependenciesFile != null) { if (file.dependenciesFile != null) {
file.dependencies = new OptionalFile[file.dependenciesFile.length]; file.dependencies = new OptionalFile[file.dependenciesFile.length];
for (int i = 0; i < file.dependenciesFile.length; ++i) { for (int i = 0; i < file.dependenciesFile.length; ++i) {
file.dependencies[i] = getOptionalFile(file.dependenciesFile[i].name, file.dependenciesFile[i].type); file.dependencies[i] = getOptionalFile(file.dependenciesFile[i].name);
} }
} }
if (file.conflictFile != null) { if (file.conflictFile != null) {
file.conflict = new OptionalFile[file.conflictFile.length]; file.conflict = new OptionalFile[file.conflictFile.length];
for (int i = 0; i < file.conflictFile.length; ++i) { for (int i = 0; i < file.conflictFile.length; ++i) {
file.conflict[i] = getOptionalFile(file.conflictFile[i].name, file.conflictFile[i].type); file.conflict[i] = getOptionalFile(file.conflictFile[i].name);
} }
} }
} }
} }
@Deprecated
public OptionalFile getOptionalFile(String file, OptionalType type) { public OptionalFile getOptionalFile(String file, OptionalType type) {
for (OptionalFile f : updateOptional) for (OptionalFile f : updateOptional)
if (f.type.equals(type) && f.name.equals(file)) return f; if (f.type.equals(type) && f.name.equals(file)) return f;
return null; return null;
} }
public OptionalFile getOptionalFile(String file) {
for (OptionalFile f : updateOptional)
if (f.name.equals(file)) return f;
return null;
}
public Collection<String> getShared() { public Collection<String> getShared() {
return updateShared; return updateShared;
} }
@Deprecated
public void markOptional(OptionalFile file) { public void markOptional(OptionalFile file) {
if (file.mark) return; if (file.mark) return;
@ -210,6 +225,7 @@ public void markOptional(OptionalFile file) {
} }
} }
@Deprecated
public void unmarkOptional(OptionalFile file) { public void unmarkOptional(OptionalFile file) {
if (!file.mark) return; if (!file.mark) return;
file.mark = false; file.mark = false;
@ -236,6 +252,7 @@ public void unmarkOptional(OptionalFile file) {
} }
} }
@Deprecated
public void pushOptionalFile(HashedDir dir, boolean digest) { public void pushOptionalFile(HashedDir dir, boolean digest) {
for (OptionalFile opt : updateOptional) { for (OptionalFile opt : updateOptional) {
if (opt.type.equals(OptionalType.FILE) && !opt.mark) { if (opt.type.equals(OptionalType.FILE) && !opt.mark) {
@ -245,6 +262,7 @@ public void pushOptionalFile(HashedDir dir, boolean digest) {
} }
} }
@Deprecated
public void pushOptionalJvmArgs(Collection<String> jvmArgs1) { public void pushOptionalJvmArgs(Collection<String> jvmArgs1) {
for (OptionalFile opt : updateOptional) { for (OptionalFile opt : updateOptional) {
if (opt.type.equals(OptionalType.JVMARGS) && opt.mark) { if (opt.type.equals(OptionalType.JVMARGS) && opt.mark) {
@ -253,6 +271,7 @@ public void pushOptionalJvmArgs(Collection<String> jvmArgs1) {
} }
} }
@Deprecated
public void pushOptionalClientArgs(Collection<String> clientArgs1) { public void pushOptionalClientArgs(Collection<String> clientArgs1) {
for (OptionalFile opt : updateOptional) { for (OptionalFile opt : updateOptional) {
if (opt.type.equals(OptionalType.CLIENTARGS) && opt.mark) { if (opt.type.equals(OptionalType.CLIENTARGS) && opt.mark) {
@ -261,6 +280,7 @@ public void pushOptionalClientArgs(Collection<String> clientArgs1) {
} }
} }
@Deprecated
public void pushOptionalClassPath(pushOptionalClassPathCallback callback) throws IOException { public void pushOptionalClassPath(pushOptionalClassPathCallback callback) throws IOException {
for (OptionalFile opt : updateOptional) { for (OptionalFile opt : updateOptional) {
if (opt.type.equals(OptionalType.CLASSPATH) && opt.mark) { if (opt.type.equals(OptionalType.CLASSPATH) && opt.mark) {
@ -273,6 +293,7 @@ public int getServerPort() {
ServerProfile profile = getDefaultServerProfile(); ServerProfile profile = getDefaultServerProfile();
return profile == null ? 25565 : profile.serverPort; return profile == null ? 25565 : profile.serverPort;
} }
@Deprecated @Deprecated
public InetSocketAddress getServerSocketAddress() { public InetSocketAddress getServerSocketAddress() {
return InetSocketAddress.createUnresolved(getServerAddress(), getServerPort()); return InetSocketAddress.createUnresolved(getServerAddress(), getServerPort());
@ -358,11 +379,6 @@ public void verify() {
for (OptionalFile f : updateOptional) { for (OptionalFile f : updateOptional) {
if (f == null) throw new IllegalArgumentException("Found null entry in updateOptional"); if (f == null) throw new IllegalArgumentException("Found null entry in updateOptional");
if (f.name == null) throw new IllegalArgumentException("Optional: name must not be null"); if (f.name == null) throw new IllegalArgumentException("Optional: name must not be null");
if (f.list == null) throw new IllegalArgumentException("Optional: list must not be null");
for (String s : f.list) {
if (s == null)
throw new IllegalArgumentException(String.format("Found null entry in updateOptional.%s.list", f.name));
}
if (f.conflictFile != null) for (OptionalDepend s : f.conflictFile) { if (f.conflictFile != null) for (OptionalDepend s : f.conflictFile) {
if (s == null) if (s == null)
throw new IllegalArgumentException(String.format("Found null entry in updateOptional.%s.conflictFile", f.name)); throw new IllegalArgumentException(String.format("Found null entry in updateOptional.%s.conflictFile", f.name));
@ -410,7 +426,9 @@ public enum Version {
MC115("1.15", 573), MC115("1.15", 573),
MC1151("1.15.1", 575), MC1151("1.15.1", 575),
MC1152("1.15.2", 578), MC1152("1.15.2", 578),
MC1161("1.16.1", 736); MC1161("1.16.1", 736),
MC1162("1.16.2", 751),
MC1163("1.16.3", 753);
private static final Map<String, Version> VERSIONS; private static final Map<String, Version> VERSIONS;
static { static {

View file

@ -1,12 +1,14 @@
package pro.gravit.launcher.profiles.optional; package pro.gravit.launcher.profiles.optional;
import pro.gravit.launcher.LauncherNetworkAPI; import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.serialize.HInput; import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.serialize.HOutput; import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -16,10 +18,14 @@ public class OptionalFile {
@LauncherNetworkAPI @LauncherNetworkAPI
public final long permissions = 0L; public final long permissions = 0L;
@LauncherNetworkAPI @LauncherNetworkAPI
@Deprecated
public String[] list; public String[] list;
@LauncherNetworkAPI @LauncherNetworkAPI
@Deprecated
public OptionalType type; public OptionalType type;
@LauncherNetworkAPI @LauncherNetworkAPI
public List<OptionalAction> actions;
@LauncherNetworkAPI
public boolean mark; public boolean mark;
@LauncherNetworkAPI @LauncherNetworkAPI
public boolean visible = true; public boolean visible = true;
@ -41,6 +47,7 @@ public class OptionalFile {
public int subTreeLevel = 1; public int subTreeLevel = 1;
@LauncherNetworkAPI @LauncherNetworkAPI
public boolean isPreset; public boolean isPreset;
@Deprecated
public transient Set<OptionalFile> dependenciesCount; public transient Set<OptionalFile> dependenciesCount;
private volatile transient Collection<BiConsumer<OptionalFile, Boolean>> watchList = null; private volatile transient Collection<BiConsumer<OptionalFile, Boolean>> watchList = null;
@ -138,8 +145,6 @@ public void clearAllWatchers() {
public void watchEvent(boolean isMark) { public void watchEvent(boolean isMark) {
if (watchList == null) return; if (watchList == null) return;
watchList.forEach((e) -> { watchList.forEach((e) -> e.accept(this, isMark));
e.accept(this, isMark);
});
} }
} }

View file

@ -0,0 +1,107 @@
package pro.gravit.launcher.profiles.optional;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class OptionalView {
public Set<OptionalFile> enabled = new HashSet<>();
public Map<OptionalFile, Set<OptionalFile>> dependenciesCountMap = new HashMap<>();
public Set<OptionalFile> all;
@SuppressWarnings("unchecked")
public <T extends OptionalAction> Set<T> getActionsByClass(Class<T> clazz) {
Set<T> results = new HashSet<>();
for (OptionalFile e : enabled) {
if (e.actions != null) {
for (OptionalAction a : e.actions) {
if (clazz.isAssignableFrom(a.getClass())) {
results.add((T) a);
}
}
}
}
return results;
}
public Set<OptionalAction> getEnabledActions() {
Set<OptionalAction> results = new HashSet<>();
for (OptionalFile e : enabled) {
if (e.actions != null) {
results.addAll(e.actions);
}
}
return results;
}
public Set<OptionalAction> getDisabledActions() {
Set<OptionalAction> results = new HashSet<>();
for (OptionalFile e : all) {
if (enabled.contains(e)) continue;
if (e.actions != null) {
results.addAll(e.actions);
}
}
return results;
}
public void enable(OptionalFile file) {
if (enabled.contains(file)) return;
enabled.add(file);
file.watchEvent(true);
if (file.dependencies != null) {
for (OptionalFile dep : file.dependencies) {
Set<OptionalFile> dependenciesCount = dependenciesCountMap.computeIfAbsent(dep, k -> new HashSet<>());
dependenciesCount.add(file);
enable(dep);
}
}
if (file.conflict != null) {
for (OptionalFile conflict : file.conflict) {
disable(conflict);
}
}
}
public void disable(OptionalFile file) {
if (!enabled.remove(file)) return;
file.watchEvent(false);
Set<OptionalFile> dependenciesCount = dependenciesCountMap.get(file);
if (dependenciesCount != null) {
for (OptionalFile f : dependenciesCount) {
if (f.isPreset) continue;
disable(f);
}
dependenciesCount.clear();
}
if (file.dependencies != null) {
for (OptionalFile f : file.dependencies) {
if (!enabled.contains(f)) continue;
dependenciesCount = dependenciesCountMap.get(f);
if (dependenciesCount == null) {
disable(f);
} else if (dependenciesCount.size() <= 1) {
dependenciesCount.clear();
disable(f);
}
}
}
}
public OptionalView(ClientProfile profile) {
this.all = profile.getOptional();
for (OptionalFile f : this.all) {
if (f.mark) enable(f);
}
}
public OptionalView(OptionalView view) {
this.enabled = new HashSet<>(view.enabled);
this.dependenciesCountMap = new HashMap<>(view.dependenciesCountMap);
this.all = view.all;
}
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launcher.profiles.optional.actions;
import pro.gravit.utils.ProviderMap;
public class OptionalAction {
public static final ProviderMap<OptionalAction> providers = new ProviderMap<>();
private static boolean registerProviders = false;
public static void registerProviders() {
if (!registerProviders) {
providers.register("file", OptionalActionFile.class);
providers.register("clientArgs", OptionalActionClientArgs.class);
providers.register("jvmArgs", OptionalActionJvmArgs.class);
providers.register("classpath", OptionalActionClassPath.class);
registerProviders = true;
}
}
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launcher.profiles.optional.actions;
public class OptionalActionClassPath extends OptionalAction {
public String[] args;
public boolean useAltClasspath = false;
public OptionalActionClassPath() {
}
public OptionalActionClassPath(String[] args) {
this.args = args;
}
public OptionalActionClassPath(String[] args, boolean useAltClasspath) {
this.args = args;
this.useAltClasspath = useAltClasspath;
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launcher.profiles.optional.actions;
import java.util.List;
public class OptionalActionClientArgs extends OptionalAction {
public List<String> args;
public OptionalActionClientArgs() {
}
public OptionalActionClientArgs(List<String> args) {
this.args = args;
}
}

View file

@ -0,0 +1,38 @@
package pro.gravit.launcher.profiles.optional.actions;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.utils.helper.LogHelper;
import java.util.Map;
public class OptionalActionFile extends OptionalAction {
public Map<String, String> files;
public void injectToHashedDir(HashedDir dir) {
if (files == null) return;
files.forEach((k, v) -> {
HashedDir.FindRecursiveResult firstPath = dir.findRecursive(k);
if (v != null && !v.isEmpty()) {
LogHelper.dev("Debug findRecursive: name %s, parent: ", firstPath.name, firstPath.parent == null ? "null" : "not null", firstPath.entry == null ? "null" : "not null");
HashedDir.FindRecursiveResult secondPath = dir.findRecursive(v);
LogHelper.dev("Debug findRecursive: name %s, parent: ", secondPath.name, secondPath.parent == null ? "null" : "not null", secondPath.entry == null ? "null" : "not null");
firstPath.parent.moveTo(firstPath.name, secondPath.parent, secondPath.name);
}
});
}
public void disableInHashedDir(HashedDir dir) {
if (files == null) return;
files.forEach((k, v) -> {
HashedDir.FindRecursiveResult firstPath = dir.findRecursive(k);
firstPath.parent.remove(firstPath.name);
});
}
public OptionalActionFile() {
}
public OptionalActionFile(Map<String, String> files) {
this.files = files;
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launcher.profiles.optional.actions;
import java.util.List;
public class OptionalActionJvmArgs extends OptionalAction {
public List<String> args;
public OptionalActionJvmArgs() {
}
public OptionalActionJvmArgs(List<String> args) {
this.args = args;
}
}

View file

@ -4,23 +4,22 @@
import pro.gravit.launcher.LauncherNetworkAPI; import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.request.websockets.WebSocketRequest; import pro.gravit.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.helper.SecurityHelper;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest { public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest {
public static StdWebSocketService service; public static StdWebSocketService service;
private static long session = SecurityHelper.secureRandom.nextLong(); private static UUID session = UUID.randomUUID();
@LauncherNetworkAPI @LauncherNetworkAPI
public final UUID requestUUID = UUID.randomUUID(); public final UUID requestUUID = UUID.randomUUID();
private transient final AtomicBoolean started = new AtomicBoolean(false); private transient final AtomicBoolean started = new AtomicBoolean(false);
public static long getSession() { public static UUID getSession() {
return Request.session; return Request.session;
} }
public static void setSession(long session) { public static void setSession(UUID session) {
Request.session = session; Request.session = session;
} }

View file

@ -2,10 +2,8 @@
import pro.gravit.launcher.LauncherNetworkAPI; import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.password.AuthECPassword; import pro.gravit.launcher.request.auth.password.*;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launcher.request.websockets.WebSocketRequest; import pro.gravit.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.VerifyHelper; import pro.gravit.utils.helper.VerifyHelper;
@ -23,8 +21,6 @@ public final class AuthRequest extends Request<AuthRequestEvent> implements WebS
private final boolean getSession; private final boolean getSession;
@LauncherNetworkAPI @LauncherNetworkAPI
private final ConnectTypes authType; private final ConnectTypes authType;
@LauncherNetworkAPI
public boolean initProxy;
public AuthRequest(String login, byte[] password) { public AuthRequest(String login, byte[] password) {
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty"); this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty");
@ -43,15 +39,6 @@ public AuthRequest(String login, byte[] password, String auth_id) {
authType = ConnectTypes.CLIENT; authType = ConnectTypes.CLIENT;
} }
@Deprecated
public AuthRequest(String login, byte[] password, HWID hwid, String auth_id) {
this.login = VerifyHelper.verify(login, VerifyHelper.NOT_EMPTY, "Login can't be empty");
this.password = new AuthECPassword(password.clone());
this.auth_id = auth_id;
getSession = true;
authType = ConnectTypes.CLIENT;
}
public AuthRequest(String login, byte[] encryptedPassword, String auth_id, ConnectTypes authType) { public AuthRequest(String login, byte[] encryptedPassword, String auth_id, ConnectTypes authType) {
this.login = login; this.login = login;
this.password = new AuthECPassword(encryptedPassword.clone()); this.password = new AuthECPassword(encryptedPassword.clone());
@ -68,10 +55,21 @@ public AuthRequest(String login, String password, String auth_id, ConnectTypes a
this.getSession = false; this.getSession = false;
} }
public AuthRequest(String login, AuthPasswordInterface password, String auth_id, boolean getSession, ConnectTypes authType) {
this.login = login;
this.password = password;
this.auth_id = auth_id;
this.getSession = getSession;
this.authType = authType;
}
public static void registerProviders() { public static void registerProviders() {
if (!registerProviders) { if (!registerProviders) {
providers.register("plain", AuthPlainPassword.class); providers.register("plain", AuthPlainPassword.class);
providers.register("rsa", AuthECPassword.class); providers.register("rsa", AuthECPassword.class);
providers.register("2fa", Auth2FAPassword.class);
providers.register("signature", AuthSignaturePassword.class);
providers.register("totp", AuthTOTPPassword.class);
registerProviders = true; registerProviders = true;
} }
} }

View file

@ -8,9 +8,9 @@
public final class CheckServerRequest extends Request<CheckServerRequestEvent> implements WebSocketRequest { public final class CheckServerRequest extends Request<CheckServerRequestEvent> implements WebSocketRequest {
@LauncherNetworkAPI @LauncherNetworkAPI
private final String username; public final String username;
@LauncherNetworkAPI @LauncherNetworkAPI
private final String serverID; public final String serverID;
public CheckServerRequest(String username, String serverID) { public CheckServerRequest(String username, String serverID) {

View file

@ -0,0 +1,11 @@
package pro.gravit.launcher.request.auth;
import pro.gravit.launcher.events.request.CurrentUserRequestEvent;
import pro.gravit.launcher.request.Request;
public class CurrentUserRequest extends Request<CurrentUserRequestEvent> {
@Override
public String getType() {
return "currentUser";
}
}

View file

@ -10,11 +10,11 @@ public final class JoinServerRequest extends Request<JoinServerRequestEvent> imp
// Instance // Instance
@LauncherNetworkAPI @LauncherNetworkAPI
private final String username; public final String username;
@LauncherNetworkAPI @LauncherNetworkAPI
private final String accessToken; public final String accessToken;
@LauncherNetworkAPI @LauncherNetworkAPI
private final String serverID; public final String serverID;
public JoinServerRequest(String username, String accessToken, String serverID) { public JoinServerRequest(String username, String accessToken, String serverID) {

View file

@ -5,14 +5,22 @@
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.websockets.WebSocketRequest; import pro.gravit.launcher.request.websockets.WebSocketRequest;
import java.util.UUID;
public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest { public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest {
@LauncherNetworkAPI @LauncherNetworkAPI
public final long session; public final UUID session;
public boolean needUserInfo;
public RestoreSessionRequest(long session) { public RestoreSessionRequest(UUID session) {
this.session = session; this.session = session;
} }
public RestoreSessionRequest(UUID session, boolean needUserInfo) {
this.session = session;
this.needUserInfo = needUserInfo;
}
@Override @Override
public String getType() { public String getType() {
return "restoreSession"; return "restoreSession";

View file

@ -0,0 +1,13 @@
package pro.gravit.launcher.request.auth.password;
import pro.gravit.launcher.request.auth.AuthRequest;
public class Auth2FAPassword implements AuthRequest.AuthPasswordInterface {
public AuthRequest.AuthPasswordInterface firstPassword;
public AuthRequest.AuthPasswordInterface secondPassword;
@Override
public boolean check() {
return firstPassword != null && firstPassword.check() && secondPassword != null && secondPassword.check();
}
}

View file

@ -0,0 +1,14 @@
package pro.gravit.launcher.request.auth.password;
import pro.gravit.launcher.request.auth.AuthRequest;
public class AuthSignaturePassword implements AuthRequest.AuthPasswordInterface {
public byte[] signature;
public byte[] publicKey;
public byte[] salt;
@Override
public boolean check() {
return true;
}
}

View file

@ -0,0 +1,12 @@
package pro.gravit.launcher.request.auth.password;
import pro.gravit.launcher.request.auth.AuthRequest;
public class AuthTOTPPassword implements AuthRequest.AuthPasswordInterface {
public String totp;
@Override
public boolean check() {
return true;
}
}

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