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'
targetCompatibility = '1.8'
sourceCompatibility = '11'
targetCompatibility = '11'
configurations {
compileOnlyA
@ -33,6 +33,7 @@
from(parent.childProjects.Launcher.tasks.genRuntimeJS)
manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Multi-Release": "true",
"Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "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-api', version: rootProject['verSlf4j']
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: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn']
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'
}
}
developers {
developer {
id = 'gravita'
name = 'Gravita'
email = 'gravita@gravit.pro'
}
developer {
id = 'zaxar163'
name = 'Zaxar163'
email = 'zahar.vcherachny@yandex.ru'
}
}
scm {
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
newProfies.sort(Comparator.comparing(a -> a));
profilesList = Collections.unmodifiableList(newProfies);
if(pingServerManager != null)
if (pingServerManager != null)
pingServerManager.syncServers();
}

View file

@ -4,6 +4,7 @@
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager;
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.launchserver.auth.handler.AuthHandler;
import pro.gravit.launchserver.auth.protect.ProtectHandler;
@ -213,6 +214,7 @@ public static void registerAll() {
DaoProvider.registerProviders();
AuthRequest.registerProviders();
HWIDProvider.registerProviders();
OptionalAction.registerProviders();
}
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) {
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;
try {
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);
}
private final Path filef;
private final boolean fixLib;
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");
if (fixLib) {
try {

View file

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

View file

@ -42,6 +42,21 @@ public MySQLSourceConfig(String 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
public synchronized void close() {
if (hikari)

View file

@ -9,7 +9,7 @@ public class HibernateAuthHandler extends CachedAuthHandler {
protected Entry fetchEntry(String username) {
User user = srv.config.dao.userDAO.findByUsername(username);
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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
package pro.gravit.launchserver.auth.protect.hwid;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.helper.DamerauHelper;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.ProviderMap;
@ -12,107 +13,102 @@
public abstract class HWIDProvider {
public static final ProviderMap<HWIDProvider> providers = new ProviderMap<>("HWIDProvider");
private static boolean registredProv = false;
public static void registerProviders() {
if(!registredProv)
{
if (!registredProv) {
providers.register("memory", MemoryHWIDProvider.class);
providers.register("mysql", MysqlHWIDProvider.class);
providers.register("json", JsonHWIDProvider.class);
registredProv = true;
}
}
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 boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException;
public void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo)
{
if(hardwareInfo.baseboardSerialNumber != null) hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.trim();
if(hardwareInfo.hwDiskId != null) hardwareInfo.hwDiskId = hardwareInfo.hwDiskId.trim();
public void normalizeHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo) {
if (hardwareInfo.baseboardSerialNumber != null)
hardwareInfo.baseboardSerialNumber = hardwareInfo.baseboardSerialNumber.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 secondSpoofingLevel = 0.0;
public double compareLevel;
}
//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();
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.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty()) result.firstSpoofingLevel += 0.2;
if(second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9;
if(second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3;
if(second.baseboardSerialNumber == null || second.baseboardSerialNumber.trim().isEmpty()) result.secondSpoofingLevel += 0.2;
if(first.hwDiskId != null && second.hwDiskId != null)
{
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.baseboardSerialNumber == null || first.baseboardSerialNumber.trim().isEmpty())
result.firstSpoofingLevel += 0.2;
if (second.hwDiskId == null || second.hwDiskId.trim().isEmpty()) result.secondSpoofingLevel += 0.9;
if (second.displayId == null || second.displayId.length < 4) result.secondSpoofingLevel += 0.3;
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());
if(hwDIskIdRate == 0) // 100% compare
if (hwDIskIdRate == 0) // 100% compare
{
result.compareLevel += 0.99;
}
else if(hwDIskIdRate < 3) //Very small change
} else if (hwDIskIdRate < 3) //Very small change
{
result.compareLevel += 0.85;
}
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;
} 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;
}
}
if(first.baseboardSerialNumber != null && second.baseboardSerialNumber != null)
{
if (first.baseboardSerialNumber != null && second.baseboardSerialNumber != null) {
int baseboardSerialRate = DamerauHelper.calculateDistance(first.baseboardSerialNumber.toLowerCase(), second.baseboardSerialNumber.toLowerCase());
if(baseboardSerialRate == 0) // 100% compare
if (baseboardSerialRate == 0) // 100% compare
{
result.compareLevel += 0.3;
}
else if(baseboardSerialRate < 3) //Very small change
} else if (baseboardSerialRate < 3) //Very small change
{
result.compareLevel += 0.15;
}
}
if(first.displayId != null && second.displayId != null)
{
if(Arrays.equals(first.displayId, second.displayId))
{
if (first.displayId != null && second.displayId != null) {
if (Arrays.equals(first.displayId, second.displayId)) {
result.compareLevel += 0.75;
}
}
//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;
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;
if(first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors)
if (first.physicalProcessors == second.physicalProcessors && first.logicalProcessors == second.logicalProcessors)
result.compareLevel += 0.05;
if(first.battery != second.battery)
if (first.battery != second.battery)
result.compareLevel -= 0.05;
if(first.processorMaxFreq == second.processorMaxFreq)
if (first.processorMaxFreq == second.processorMaxFreq)
result.compareLevel += 0.1;
if(first.totalMemory == second.totalMemory)
if (first.totalMemory == second.totalMemory)
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;
return result;
}
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] 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);
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] 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);
}
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() {
@Override
public void invoke(String... args) throws Exception {
for(MemoryHWIDEntity e : db)
{
for (MemoryHWIDEntity e : db) {
printHardwareInfo(LogHelper.Level.INFO, e.hardware);
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)));
@ -37,10 +36,8 @@ public void invoke(String... args) throws Exception {
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
long id = Long.parseLong(args[0]);
for(MemoryHWIDEntity e : db)
{
if(e.id == id)
{
for (MemoryHWIDEntity e : db) {
if (e.id == id) {
e.banned = true;
LogHelper.info("HardwareID %d banned", e.id);
}
@ -50,8 +47,7 @@ public void invoke(String... args) throws Exception {
return commands;
}
static class MemoryHWIDEntity
{
static class MemoryHWIDEntity {
public HardwareReportRequest.HardwareInfo hardware;
public byte[] publicKey;
public boolean banned;
@ -63,17 +59,17 @@ public MemoryHWIDEntity(HardwareReportRequest.HardwareInfo hardware, byte[] publ
this.id = SecurityHelper.newRandom().nextLong();
}
}
public Set<MemoryHWIDEntity> db = ConcurrentHashMap.newKeySet();
@Override
public HardwareReportRequest.HardwareInfo findHardwareInfoByPublicKey(byte[] publicKey, Client client) throws HWIDException {
for(MemoryHWIDEntity e : db) {
if(Arrays.equals(e.publicKey, publicKey))
{
if(e.banned) throw new HWIDException("You HWID banned");
for (MemoryHWIDEntity e : db) {
if (Arrays.equals(e.publicKey, publicKey)) {
if (e.banned) throw new HWIDException("You HWID banned");
return e.hardware;
}
};
}
return null;
}
@ -85,17 +81,15 @@ public void createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo,
@Override
public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, Client client) throws HWIDException {
boolean isAlreadyWarning = false;
for(MemoryHWIDEntity e : db) {
for (MemoryHWIDEntity e : db) {
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);
isAlreadyWarning = true;
}
if(result.compareLevel > criticalCompareLevel)
{
if (result.compareLevel > criticalCompareLevel) {
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;
return true;
}

View file

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

View file

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

View file

@ -32,8 +32,8 @@ default void verifySecureLevelKey(byte[] publicKey, byte[] data, byte[] signatur
default SecurityReportRequestEvent onSecurityReport(SecurityReportResponse report, Client client) {
return new SecurityReportRequestEvent();
}
default VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client)
{
default VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
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");
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

View file

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

View file

@ -14,8 +14,8 @@
import java.util.Map;
public final class RejectAuthProvider extends AuthProvider implements Reconfigurable {
private String message;
private ArrayList<String> whitelist = new ArrayList<>();
public String message;
public ArrayList<String> whitelist = new ArrayList<>();
public RejectAuthProvider() {
}

View file

@ -11,15 +11,24 @@
import pro.gravit.utils.helper.SecurityHelper;
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.Pattern;
public final class RequestAuthProvider extends AuthProvider {
private String url;
private transient Pattern pattern;
private String response;
private boolean flagsEnabled;
public String url;
public transient Pattern pattern;
public String response;
public boolean flagsEnabled;
public boolean usePermission = true;
public int timeout = 5000;
private final HttpClient client = HttpClient.newBuilder()
.build();
@Override
public void init(LaunchServer srv) {
@ -30,15 +39,21 @@ public void init(LaunchServer srv) {
}
@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");
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
String currentResponse = response.body();
Matcher matcher = pattern.matcher(currentResponse);
return matcher.matches() && matcher.groupCount() >= 1 ?
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);
}

View file

@ -52,19 +52,19 @@ public String[] buildConfig(Path inputJar, Path outputJar) {
List<String> confStrs = new ArrayList<>();
prepare(false);
if (srv.config.launcher.proguardGenMappings)
confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'");
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'");
confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");
confStrs.add("-printmapping '" + mappings.toFile().getName() + "'");
confStrs.add("-obfuscationdictionary '" + words.toFile().getName() + "'");
confStrs.add("-injar '" + inputJar.toAbsolutePath() + "'");
confStrs.add("-outjar '" + outputJar.toAbsolutePath() + "'");
Collections.addAll(confStrs, JVMHelper.JVM_VERSION >= 9 ? JAVA9_OPTS : JAVA8_OPTS);
srv.launcherBinary.coreLibs.stream()
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'")
.map(e -> "-libraryjars '" + e.toAbsolutePath().toString() + "'")
.forEach(confStrs::add);
srv.launcherBinary.addonLibs.stream()
.map(e -> "-libraryjars \'" + e.toAbsolutePath().toString() + "\'")
.map(e -> "-libraryjars '" + e.toAbsolutePath().toString() + "'")
.forEach(confStrs::add);
confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add("-classobfuscationdictionary '" + words.toFile().getName() + "'");
confStrs.add("@".concat(config.toFile().getName()));
return confStrs.toArray(new String[0]);
}

View file

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

View file

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

View file

@ -32,19 +32,18 @@ public Path process(Path inputFile) throws IOException {
Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar),
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");
if(!IOHelper.exists(javaJModsPath))
{
if (!IOHelper.exists(javaJModsPath)) {
LogHelper.warning("Directory %s not found. It is not good", javaJModsPath);
}
else
{
} else {
//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.graphics.jmod"))) LogHelper.warning("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.warning("javafx.controls.jmod not found. Launcher can be assembled incorrectly. Maybe you need to install OpenJFX?");
if (!IOHelper.exists(javaJModsPath.resolve("javafx.base.jmod")))
LogHelper.error("javafx.base.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 {

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.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
private static final String VERSION = Version.getVersion().getVersionString();
private static final int BUILD = Version.getVersion().build;

View file

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

View file

@ -1,10 +1,14 @@
package pro.gravit.launchserver.command.basic;
import org.bouncycastle.cert.X509CertificateHolder;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
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.socket.handlers.NettyServerSocketHandler;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.LogHelper;
import java.nio.file.Paths;
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.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("signJar", new SignJarCommand(server));
service.registerCommand("signDir", new SignDirCommand(server));
service.registerCommand("securitycheck", new SecurityCheckCommand(server));
Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components");
handler.registerCategory(serviceCategory);
}

View file

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

View file

@ -2,6 +2,8 @@
import pro.gravit.launcher.Launcher;
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.command.Command;
import pro.gravit.utils.helper.IOHelper;
@ -12,6 +14,9 @@
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;
public class SaveProfilesCommand extends Command {
@ -19,11 +24,10 @@ public SaveProfilesCommand(LaunchServer server) {
super(server);
}
@SuppressWarnings("deprecated")
@SuppressWarnings("deprecation")
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
if(profile.getServers().size() == 0)
{
if (profile.getServers().size() == 0) {
ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile();
serverProfile.isDefault = true;
serverProfile.name = profile.getTitle();
@ -31,6 +35,34 @@ public static void saveProfile(ClientProfile profile, Path path) throws IOExcept
serverProfile.serverPort = profile.getServerPort();
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)) {
Launcher.gsonManager.configGson.toJson(profile, w);
}

View file

@ -25,7 +25,7 @@ public String getUsageDescription() {
public void invoke(String... args) throws IOException {
server.syncProfilesDir();
LogHelper.subInfo("Profiles successfully resynced");
server.syncUpdatesDir(null);
LogHelper.subInfo("Updates dir successfully resynced");
}

View file

@ -29,7 +29,7 @@ public void invoke(String... args) {
service.channels.forEach((channel -> {
WebSocketFrameHandler frameHandler = channel.pipeline().get(WebSocketFrameHandler.class);
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)
LogHelper.info("Channel %s | connectUUID %s | checkSign %s", ip, frameHandler.getConnectUUID(), client.checkSign ? "true" : "false");
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 SignJarTask task;
private final SignJarTask task;
public SignJarVisitor(SignJarTask task) {
this.task = task;

View file

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

View file

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

View file

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

View file

@ -27,32 +27,7 @@
public class SignHelper {
public static final OutputStream NULL = new OutputStream() {
@Override
public String toString() {
return "NullOutputStream";
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b) {
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b, int off, int len) {
}
/** Discards the specified byte. */
@Override
public void write(int b) {
}
/** Never closes */
@Override
public void close() {
}
};
public static final OutputStream NULL = OutputStream.nullOutputStream();
public static final String hashFunctionName = "SHA-256";
private SignHelper() {

View file

@ -29,7 +29,8 @@ public class LauncherModuleLoader {
private final 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() {
@ -140,19 +141,17 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
return super.visitFile(file, attrs);
}
}
public void addClassFieldsToProperties(Map<String, Object> propertyMap, String prefix, Object object, Class<?> classOfObject) throws IllegalAccessException {
Field[] fields = classOfObject.getFields();
for (Field field : fields) {
if ((field.getModifiers() & Modifier.STATIC) != 0) continue;
Object obj = field.get(object);
String propertyName = prefix.concat(".").concat(field.getName().toLowerCase(Locale.US));
if(InjectClassAcceptor.isSerializableValue(obj))
{
if (InjectClassAcceptor.isSerializableValue(obj)) {
LogHelper.dev("Property name %s", propertyName);
propertyMap.put(propertyName, obj);
}
else
{
} else {
//Try recursive add fields
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<>();
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
IOHelper.walk(dir, new SimpleFileVisitor<Path>() {
IOHelper.walk(dir, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".crt")) {

View file

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

View file

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

View file

@ -3,17 +3,14 @@
import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.socket.Client;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class SessionManager implements NeedGarbageCollection {
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) {
@ -31,21 +28,21 @@ public void garbageCollection() {
}
public Client getClient(long session) {
public Client getClient(UUID session) {
return clientSet.get(session);
}
public Client getOrNewClient(long session) {
public Client getOrNewClient(UUID session) {
return clientSet.computeIfAbsent(session, Client::new);
}
public Client removeClient(long session) {
public Client removeClient(UUID session) {
return clientSet.remove(session);
}
public void updateClient(long session) {
public void updateClient(UUID session) {
Client c = clientSet.get(session);
if (c != null) {
c.up();

View file

@ -7,10 +7,13 @@
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.dao.User;
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 long session;
public UUID session;
public String auth_id;
public long timestamp;
public AuthResponse.ConnectTypes type;
@ -20,13 +23,14 @@ public class Client {
public ClientPermissions permissions;
public String username;
public TrustLevel trustLevel;
public transient LogHelper.OutputEnity logOutput;
public transient AuthProviderPair auth;
public transient User daoObject;
public Client(long session) {
public transient Map<String, Object> properties;
public Client(UUID session) {
this.session = session;
timestamp = System.currentTimeMillis();
type = null;
@ -58,4 +62,15 @@ public static class TrustLevel {
public byte[] publicKey;
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.config.LaunchServerConfig;
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.fileserver.FileServerHandler;
import pro.gravit.utils.BiHookSet;
@ -41,8 +42,8 @@ public LauncherNettyServer(LaunchServer server) {
if (config.performance.usingEpoll && !Epoll.isAvailable()) {
LogHelper.error("Epoll is not available: (netty,perfomance.usingEpoll configured wrongly)", Epoll.unavailabilityCause());
}
bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread);
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread);
bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread, "LauncherNettyServer.bossGroup");
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread, "LauncherNettyServer.workerGroup");
serverBootstrap = new ServerBootstrap();
service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server);
serverBootstrap.group(bossGroup, workerGroup)
@ -60,6 +61,8 @@ public void initChannel(SocketChannel ch) {
pipeline.addLast("forward-http", new NettyIpForwardHandler(context));
pipeline.addLast("websock-comp", new WebSocketServerCompressionHandler());
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)
pipeline.addLast("fileserver", new FileServerHandler(server.updatesDir, true, config.showHiddenFiles));
pipeline.addLast("launchserver", new WebSocketFrameHandler(context, server, service));

View file

@ -15,7 +15,7 @@ public static void setUsingEpoll(boolean value) {
epoll = value;
}
public static EventLoopGroup newEventLoopGroup(int threads) {
public static EventLoopGroup newEventLoopGroup(int threads, String poolName) {
if (epoll)
return new EpollEventLoopGroup(threads);
else
@ -28,5 +28,4 @@ public static ChannelFactory<? extends ServerChannel> getServerSocketChannelFact
else
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.request.WebSocketEvent;
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.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*;
@ -36,6 +37,7 @@
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
public class WebSocketService {
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
@ -45,16 +47,16 @@ public class WebSocketService {
private final Gson gson;
//Statistic data
public AtomicLong shortRequestLatency = new AtomicLong();
public AtomicLong shortRequestCounter = new AtomicLong();
public final AtomicLong shortRequestLatency = new AtomicLong();
public final AtomicLong shortRequestCounter = new AtomicLong();
public AtomicLong middleRequestLatency = new AtomicLong();
public AtomicLong middleRequestCounter = new AtomicLong();
public final AtomicLong middleRequestLatency = new AtomicLong();
public final AtomicLong middleRequestCounter = new AtomicLong();
public AtomicLong longRequestLatency = new AtomicLong();
public AtomicLong longRequestCounter = new AtomicLong();
public final AtomicLong longRequestLatency = 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) {
this.channels = channels;
@ -65,6 +67,15 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
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() {
providers.register("auth", AuthResponse.class);
providers.register("checkServer", CheckServerResponse.class);
@ -79,8 +90,6 @@ public static void registerResponses() {
providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("register", RegisterResponse.class);
providers.register("setPassword", SetPasswordResponse.class);
providers.register("exit", ExitResponse.class);
providers.register("getSecureLevelInfo", GetSecureLevelInfoResponse.class);
providers.register("verifySecureLevelKey", VerifySecureLevelKeyResponse.class);
@ -89,6 +98,7 @@ public static void registerResponses() {
providers.register("serverStatus", ServerStatusResponse.class);
providers.register("pingServerReport", PingServerReportResponse.class);
providers.register("pingServer", PingServerResponse.class);
providers.register("currentUser", CurrentUserResponse.class);
}
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);
long executeTime = System.nanoTime() - startTimeNanos;
if(executeTime > 0)
{
if (executeTime > 0) {
addRequestTimeToStats(executeTime);
}
}
public void addRequestTimeToStats(long nanos)
{
if(nanos < 100_000_000L) // < 100 millis
public void addRequestTimeToStats(long nanos) {
if (nanos < 100_000_000L) // < 100 millis
{
shortRequestCounter.getAndIncrement();
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();
middleRequestLatency.getAndAdd(nanos);
}
else // > 1 second
} else // > 1 second
{
longRequestCounter.getAndIncrement();
longRequestLatency.getAndAdd(nanos);
}
long lastTime = lastRequestTime.get();
long currentTime = System.currentTimeMillis();
if(currentTime - lastTime > 60*1000) //1 minute
if (currentTime - lastTime > 60 * 1000) //1 minute
{
lastRequestTime.set(currentTime);
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.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.concurrent.ScheduledFuture;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.NettyConnectContext;
import pro.gravit.launchserver.socket.WebSocketService;
import pro.gravit.utils.BiHookSet;
import pro.gravit.utils.HookSet;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
@ -17,15 +17,14 @@
import java.util.concurrent.TimeUnit;
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
static {
}
public final LaunchServer srv;
public final WebSocketService service;
private final UUID connectUUID = UUID.randomUUID();
public NettyConnectContext context;
public BiHookSet<ChannelHandlerContext, WebSocketFrame> hooks = new BiHookSet<>();
public final BiHookSet<ChannelHandlerContext, WebSocketFrame> hooks = new BiHookSet<>();
private Client client;
private ScheduledFuture<?> future;
public WebSocketFrameHandler(NettyConnectContext context, LaunchServer srv, WebSocketService service) {
this.context = context;
@ -50,20 +49,35 @@ public void channelActive(ChannelHandlerContext ctx) {
if (LogHelper.isDevEnabled()) {
LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
}
client = new Client(0);
client = new Client(null);
Channel ch = ctx.channel();
service.registerClient(ch);
ctx.executor().schedule(() -> {
ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise());
}, 30L, TimeUnit.SECONDS);
future = ctx.executor().scheduleAtFixedRate(() -> ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise()), 30L, 30L, TimeUnit.SECONDS);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
// 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) {
service.process(ctx, (TextWebSocketFrame) frame, client, context.ip);
try {
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)) {
frame.content().retain();
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.
}
}
@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.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.time.Clock;
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 static io.netty.handler.codec.http.HttpMethod.GET;
@ -26,7 +35,7 @@
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 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"));
@ -34,8 +43,7 @@ public class FileServerHandler extends SimpleChannelInboundHandler<FullHttpReque
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
static {
dateFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
dateFormatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US).withZone(ZoneId.of("UTC"));
}
private final Path base;
@ -66,7 +74,7 @@ private static void sendListing(ChannelHandlerContext ctx, File dir, String dirP
.append("<ul>")
.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()) {
continue;
}
@ -128,7 +136,7 @@ private static void sendNotModified(ChannelHandlerContext ctx) {
* @param response HTTP 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) {
// Date header
Calendar time = new GregorianCalendar();
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
LocalDateTime time = LocalDateTime.now(Clock.systemUTC());
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time));
// Add cache headers
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.plus(HTTP_CACHE_SECONDS, ChronoUnit.SECONDS)));
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
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
String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE);
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
// does not have milliseconds
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.get(ChronoField.INSTANT_SECONDS);
long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
sendNotModified(ctx);

View file

@ -57,8 +57,15 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
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;
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);
if (pair == null) {
sendError("auth_id incorrect");
@ -81,19 +88,18 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
clientData.permissions = aresult.permissions;
clientData.auth_id = auth_id;
clientData.updateAuth(server);
if (result.playerProfile != null)
clientData.username = result.playerProfile.username;
if (aresult.username != null)
clientData.username = aresult.username;
else
clientData.username = login;
if(aresult instanceof AuthProviderDAOResult)
{
if (aresult instanceof AuthProviderDAOResult) {
clientData.daoObject = ((AuthProviderDAOResult) aresult).daoObject;
}
result.accessToken = aresult.accessToken;
result.permissions = clientData.permissions;
if (getSession) {
if (clientData.session == 0) {
clientData.session = random.nextLong();
if (clientData.session == null) {
clientData.session = UUID.randomUUID();
server.sessionManager.addClient(clientData);
}
result.session = clientData.session;
@ -104,9 +110,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString());
}
}
else
{
} else {
uuid = pair.handler.usernameToUUID(aresult.username);
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;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.ExitRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse;
@ -24,7 +26,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
return;
}
if (username == null) {
if (client.session == 0 && exitAll) {
if (client.session == null && exitAll) {
sendError("Session invalid");
return;
}
@ -33,47 +35,42 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendError("Exit internal error");
return;
}
Client newClient = new Client(0);
Client newClient = new Client(null);
newClient.checkSign = client.checkSign;
handler.setClient(newClient);
if (client.session != 0) server.sessionManager.removeClient(client.session);
if (client.session != null) server.sessionManager.removeClient(client.session);
if (exitAll) {
service.channels.forEach((channel) -> {
if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null || wsHandler == handler) return;
Client chClient = wsHandler.getClient();
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient();
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 {
if (chClient.session != client.session) return;
if (client1.session != client.session) 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);
});
exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER);
}));
}
sendResult(new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
} else {
service.channels.forEach((channel -> {
if (channel == null || channel.pipeline() == null) return;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) return;
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);
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient();
if (client1 != null && client.isAuth && client.username != null && client1.username.equals(username)) {
exit(server, webSocketFrameHandler, channel, ExitRequestEvent.ExitReason.SERVER);
}
}));
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");
return;
}
if (username == null || accessToken == null || serverID == null) {
sendError("Invalid request");
return;
}
boolean success;
try {
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);
if(!success)
{
if (!success) {
sendResult(new JoinServerRequestEvent(false));
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.response.SimpleResponse;
import java.util.UUID;
public class RestoreSessionResponse extends SimpleResponse {
@LauncherNetworkAPI
public long session;
public UUID session;
public boolean needUserInfo;
@Override
public String getType() {
@ -17,13 +20,18 @@ public String getType() {
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client rClient = server.sessionManager.getClient(session);
if (rClient == null) {
sendError("Session invalid");
return;
}
WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class);
frameHandler.setClient(rClient);
sendResult(new RestoreSessionRequestEvent());
if (needUserInfo) {
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient)));
} else {
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 PingServerReportRequest.PingServerReport data;
public String name;
@Override
public String getType() {
return "pingServerReport";
@ -17,9 +18,9 @@ public String getType() {
@Override
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");
return;
}
server.pingServerManager.updateServer(name, data);
sendResult(new PingServerReportRequestEvent());

View file

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

View file

@ -3,7 +3,6 @@
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.ServerStatusRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.WebSocketService;
import pro.gravit.launchserver.socket.response.SimpleResponse;
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);
event.totalJavaMemory = JVMHelper.RUNTIME.totalMemory();
event.freeJavaMemory = JVMHelper.RUNTIME.freeMemory();
event.shortLatency = ( service.shortRequestLatency.get() / service.shortRequestCounter.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.latency = ( ( service.shortRequestLatency.get() + service.middleRequestLatency.get() + service.longRequestLatency.get() ) /
( service.shortRequestCounter.get() + service.middleRequestCounter.get() + service.longRequestCounter.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.longLatency = (service.longRequestLatency.get() / service.longRequestCounter.get()) / 1_000_000;
event.latency = ((service.shortRequestLatency.get() + service.middleRequestLatency.get() + service.longRequestLatency.get()) /
(service.shortRequestCounter.get() + service.middleRequestCounter.get() + service.longRequestCounter.get())) / 1_000_000;
sendResult(event);
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -23,6 +23,10 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Access denied");
return;
}
if (dirName == null) {
sendError("Invalid request");
return;
}
HashedDir dir = server.updatesDirMap.get(dirName);
if (dir == null) {
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());
}
@SuppressWarnings("unchecked")
@Test
void testASM() throws Throwable {
ClassReader reader = new ClassReader(JarHelper.getClassBytes(TestClass.class));

View file

@ -27,6 +27,7 @@
manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Can-Redefine-Classes": "true",
"Multi-Release": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true",
"Multi-Release-Jar": "true")
@ -93,6 +94,18 @@ task dumpLibs(type: Copy) {
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 {
connection = 'scm:git:https://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 boolean noJavaCheck = Boolean.getBoolean(NO_JAVA_CHECK_PROPERTY);
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
public static class JavaVersion
{
public static class JavaVersion {
public final Path jvmDir;
public final int version;
public boolean enabledJavaFX;
@ -33,31 +33,29 @@ public JavaVersion(Path jvmDir, int version) {
this.version = version;
this.enabledJavaFX = true;
}
public static JavaVersion getCurrentJavaVersion()
{
public static JavaVersion getCurrentJavaVersion() {
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion());
}
public static JavaVersion getByPath(Path jvmDir) throws IOException {
Path releaseFile = jvmDir.resolve("release");
if(!IOHelper.isFile(releaseFile)) return null;
if (!IOHelper.isFile(releaseFile)) return null;
Properties properties = new Properties();
properties.load(IOHelper.newReader(releaseFile));
int javaVersion = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, javaVersion);
if(javaVersion <= 8)
{
if (javaVersion <= 8) {
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
}
else
{
} else {
resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir, "javafx.base") != null;
if(!resultJavaVersion.enabledJavaFX)
if (!resultJavaVersion.enabledJavaFX)
resultJavaVersion.enabledJavaFX = tryFindModule(jvmDir.resolve("jre"), "javafx.base") != null;
}
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 jdkPath = jvmDir.resolve("jre").resolve("lib").resolve("ext").resolve(name.concat(".jar"));
return IOHelper.isFile(jrePath) || IOHelper.isFile(jdkPath);
@ -94,12 +92,11 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
JavaVersion javaVersion = null;
try {
if(!noJavaCheck) javaVersion = findJava();
if (!noJavaCheck) javaVersion = findJava();
} catch (Throwable e) {
LogHelper.error(e);
}
if (javaVersion == null)
{
if (javaVersion == null) {
javaVersion = JavaVersion.getCurrentJavaVersion();
}
@ -187,64 +184,54 @@ public static boolean tryAddModule(Path[] paths, String moduleName, StringBuilde
return false;
}
public static JavaVersion findJavaByProgramFiles(Path path)
{
public static JavaVersion findJavaByProgramFiles(Path path) {
LogHelper.debug("Check Java in %s", path.toString());
JavaVersion selectedJava = null;
File[] candidates = path.toFile().listFiles(File::isDirectory);
if(candidates == null) return null;
for(File candidate : candidates)
{
if (candidates == null) return null;
for (File candidate : candidates) {
Path javaPath = candidate.toPath();
try {
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");
if(javaVersion.enabledJavaFX && (selectedJava == null || !selectedJava.enabledJavaFX))
{
if (javaVersion.enabledJavaFX && (selectedJava == null || !selectedJava.enabledJavaFX)) {
selectedJava = javaVersion;
continue;
}
if(selectedJava != null && javaVersion.enabledJavaFX && javaVersion.version < selectedJava.version)
{
if (selectedJava != null && javaVersion.enabledJavaFX && javaVersion.version < selectedJava.version) {
selectedJava = javaVersion;
}
} catch (IOException 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");
}
return selectedJava;
}
public static JavaVersion findJava()
{
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
{
public static JavaVersion findJava() {
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
JavaVersion result = null;
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");
if(IOHelper.isDir(programFiles64))
{
if (IOHelper.isDir(programFiles64)) {
result = findJavaByProgramFiles(programFiles64);
}
}
if(result == null)
{
if (result == null) {
result = findJavaByProgramFiles(defaultJvmContainerDir);
}
return result;
}
return null;
return null;
}
public static int getJavaVersion(String version)
{
public static int getJavaVersion(String version) {
if (version.startsWith("1.")) {
version = version.substring(2, 3);
} else {

View file

@ -13,8 +13,10 @@
import pro.gravit.launcher.managers.ClientGsonManager;
import pro.gravit.launcher.managers.ConsoleManager;
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.RequestException;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.RestoreSessionRequest;
import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.utils.NativeJVMHalt;
@ -113,6 +115,8 @@ public static void main(String... args) throws Throwable {
}
public static void initGson(ClientModuleManager modulesManager) {
AuthRequest.registerProviders();
OptionalAction.registerProviders();
Launcher.gsonManager = new ClientGsonManager(modulesManager);
Launcher.gsonManager.initGson();
}

View file

@ -14,8 +14,12 @@
import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.patches.FMLPatcher;
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.RequestException;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.RestoreSessionRequest;
import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.utils.DirWatcher;
@ -50,7 +54,9 @@ private static ClientLauncherProcess.ClientParams readParams(SocketAddress addre
ClientLauncherProcess.ClientParams params = Launcher.gsonManager.gson.fromJson(new String(serialized, IOHelper.UNICODE_CHARSET), ClientLauncherProcess.ClientParams.class);
params.clientHDir = new HashedDir(input);
params.assetHDir = new HashedDir(input);
params.javaHDir = new HashedDir(input);
boolean isNeedReadJavaDir = input.readBoolean();
if (isNeedReadJavaDir)
params.javaHDir = new HashedDir(input);
return params;
}
}
@ -93,9 +99,10 @@ public static void main(String[] args) throws Throwable {
List<URL> classpath = new LinkedList<>();
resolveClassPathStream(clientDir, params.profile.getClassPath()).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
params.profile.pushOptionalClassPath((opt) -> {
resolveClassPathStream(clientDir, opt).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
});
for (OptionalAction a : params.actions) {
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());
Thread.currentThread().setContextClassLoader(classLoader);
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");
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
Path javaDir = Paths.get(System.getProperty("java.home"));
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
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
//for (OptionalFile s : Launcher.profile.getOptional()) {
// if (params.updateOptional.contains(s)) s.mark = true;
// else hdir.removeR(s.file);
//}
Launcher.profile.pushOptionalFile(params.clientHDir, false);
// Start WatchService, and only then client
CommonHelper.newThread("Asset Directory Watcher", true, assetWatcher).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(clientDir, params.clientHDir, clientMatcher, digest);
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
if (javaWatcher != null)
verifyHDir(javaDir, params.javaHDir, null, digest);
if (params.javaHDir != null)
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
launch(profile, params);
}
}
private static void initGson(ClientModuleManager moduleManager) {
AuthRequest.registerProviders();
OptionalAction.registerProviders();
Launcher.gsonManager = new ClientGsonManager(moduleManager);
Launcher.gsonManager.initGson();
}
@ -223,7 +238,12 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir);
}
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);
for (int i = 0, l = copy.size(); i < l; i++) {
String s = copy.get(i);

View file

@ -10,6 +10,10 @@
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.profiles.ClientProfile;
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.serialize.HOutput;
import pro.gravit.utils.Version;
@ -34,6 +38,7 @@ public class ClientLauncherProcess {
public Path executeFile;
public Path workDir;
public Path javaDir;
public int bits;
public boolean useLegacyJavaClassPathProperty;
public boolean isStarted;
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,
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
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,
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
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,
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this.workDir = clientDir.toAbsolutePath();
this.javaDir = javaDir;
@ -73,6 +78,10 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
this.params.assetHDir = assetHDir;
this.params.clientHDir = clientHDir;
this.params.javaHDir = jvmHDir;
if (view != null) {
this.params.actions = view.getEnabledActions();
}
this.bits = JVMHelper.JVM_BITS;
applyClientProfile();
}
@ -86,7 +95,11 @@ public static String getPathSeparator() {
private void applyClientProfile() {
this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString());
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());
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
if (params.ram > 0) {
@ -148,7 +161,12 @@ public void runWriteParams(SocketAddress address) throws IOException {
output.writeByteArray(serializedMainParams, 0);
params.clientHDir.write(output);
params.assetHDir.write(output);
params.javaHDir.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);
}
}
}
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
@ -185,9 +203,11 @@ public static class ClientParams {
public int height;
public Set<OptionalAction> actions = new HashSet<>();
//========
public long session;
public UUID session;
public transient HashedDir assetHDir;
@ -255,7 +275,11 @@ private void addModernClientArgs(Collection<String> args) {
Collections.addAll(args, "--server", profile.getServerAddress());
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
if (fullScreen)
Collections.addAll(args, "--fullscreen", Boolean.toString(true));

View file

@ -36,8 +36,15 @@ public final class ServerPinger {
private Instant cacheTime = null;
public ServerPinger(ClientProfile profile) {
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
this.version = Objects.requireNonNull(profile.getVersion(), "version");
this(profile.getDefaultServerProfile(), profile.getVersion());
}
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 {

View file

@ -9,9 +9,9 @@
import pro.gravit.launcher.request.secure.HardwareReportRequest;
public class HWIDProvider {
public SystemInfo systemInfo;
public OperatingSystem system;
public HardwareAbstractionLayer hardware;
public final SystemInfo systemInfo;
public final OperatingSystem system;
public final HardwareAbstractionLayer hardware;
public HWIDProvider() {
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 {
into('META-INF/versions/11') {
from sourceSets.java11.output
}
classifier = 'clean'
}
compileJava11Java {
sourceCompatibility = 11
targetCompatibility = 11
}
task sourcesJar(type: Jar) {
from sourceSets.main.allJava
archiveClassifier = 'sources'
@ -47,6 +66,18 @@ task javadocJar(type: Jar) {
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 {
connection = 'scm:git:https://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() {
ClientPermissions perm = new ClientPermissions();
perm.setPermission(PermissionConsts.ADMIN, true);
return perm;
}

View file

@ -5,8 +5,10 @@
import pro.gravit.launcher.events.RequestEvent;
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
public ClientPermissions permissions;
@LauncherNetworkAPI
@ -16,7 +18,7 @@ public class AuthRequestEvent extends RequestEvent {
@LauncherNetworkAPI
public String protectToken;
@LauncherNetworkAPI
public long session;
public UUID session;
public AuthRequestEvent() {
}
@ -34,7 +36,7 @@ public AuthRequestEvent(ClientPermissions permissions, PlayerProfile playerProfi
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.playerProfile = playerProfile;
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;
public class RestoreSessionRequestEvent extends RequestEvent {
public CurrentUserRequestEvent.UserInfo userInfo;
public RestoreSessionRequestEvent() {
}
public RestoreSessionRequestEvent(CurrentUserRequestEvent.UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public String getType() {
return "restoreSession";

View file

@ -1,7 +1,6 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.utils.helper.JVMHelper;
public class ServerStatusRequestEvent extends RequestEvent {
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
private String mainClass;
public static class ServerProfile
{
public static class ServerProfile {
public String name;
public String serverAddress;
public int serverPort;
public boolean isDefault = true;
public InetSocketAddress toSocketAddress() {
return InetSocketAddress.createUnresolved(serverAddress, serverPort);
}
}
@LauncherNetworkAPI
private List<ServerProfile> servers = new ArrayList<>(1);
public ServerProfile getDefaultServerProfile()
{
for(ServerProfile profile : servers)
{
if(profile.isDefault) return profile;
public ServerProfile getDefaultServerProfile() {
for (ServerProfile profile : servers) {
if (profile.isDefault) return profile;
}
return null;
}
@ -127,6 +130,10 @@ public String getAssetDir() {
return assetDir;
}
public List<String> getUpdateExclusions() {
return Collections.unmodifiableList(updateExclusions);
}
public FileNameMatcher getClientUpdateMatcher(/*boolean excludeOptional*/) {
String[] updateArray = update.toArray(new String[0]);
String[] verifyArray = updateVerify.toArray(new String[0]);
@ -169,28 +176,36 @@ public void updateOptionalGraph() {
if (file.dependenciesFile != null) {
file.dependencies = new OptionalFile[file.dependenciesFile.length];
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) {
file.conflict = new OptionalFile[file.conflictFile.length];
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) {
for (OptionalFile f : updateOptional)
if (f.type.equals(type) && f.name.equals(file)) return f;
return null;
}
public OptionalFile getOptionalFile(String file) {
for (OptionalFile f : updateOptional)
if (f.name.equals(file)) return f;
return null;
}
public Collection<String> getShared() {
return updateShared;
}
@Deprecated
public void markOptional(OptionalFile file) {
if (file.mark) return;
@ -210,6 +225,7 @@ public void markOptional(OptionalFile file) {
}
}
@Deprecated
public void unmarkOptional(OptionalFile file) {
if (!file.mark) return;
file.mark = false;
@ -236,6 +252,7 @@ public void unmarkOptional(OptionalFile file) {
}
}
@Deprecated
public void pushOptionalFile(HashedDir dir, boolean digest) {
for (OptionalFile opt : updateOptional) {
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) {
for (OptionalFile opt : updateOptional) {
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) {
for (OptionalFile opt : updateOptional) {
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 {
for (OptionalFile opt : updateOptional) {
if (opt.type.equals(OptionalType.CLASSPATH) && opt.mark) {
@ -273,6 +293,7 @@ public int getServerPort() {
ServerProfile profile = getDefaultServerProfile();
return profile == null ? 25565 : profile.serverPort;
}
@Deprecated
public InetSocketAddress getServerSocketAddress() {
return InetSocketAddress.createUnresolved(getServerAddress(), getServerPort());
@ -358,11 +379,6 @@ public void verify() {
for (OptionalFile f : 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.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 (s == null)
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),
MC1151("1.15.1", 575),
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;
static {

View file

@ -1,12 +1,14 @@
package pro.gravit.launcher.profiles.optional;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -16,10 +18,14 @@ public class OptionalFile {
@LauncherNetworkAPI
public final long permissions = 0L;
@LauncherNetworkAPI
@Deprecated
public String[] list;
@LauncherNetworkAPI
@Deprecated
public OptionalType type;
@LauncherNetworkAPI
public List<OptionalAction> actions;
@LauncherNetworkAPI
public boolean mark;
@LauncherNetworkAPI
public boolean visible = true;
@ -41,6 +47,7 @@ public class OptionalFile {
public int subTreeLevel = 1;
@LauncherNetworkAPI
public boolean isPreset;
@Deprecated
public transient Set<OptionalFile> dependenciesCount;
private volatile transient Collection<BiConsumer<OptionalFile, Boolean>> watchList = null;
@ -138,8 +145,6 @@ public void clearAllWatchers() {
public void watchEvent(boolean isMark) {
if (watchList == null) return;
watchList.forEach((e) -> {
e.accept(this, isMark);
});
watchList.forEach((e) -> 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.request.websockets.StdWebSocketService;
import pro.gravit.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.helper.SecurityHelper;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest {
public static StdWebSocketService service;
private static long session = SecurityHelper.secureRandom.nextLong();
private static UUID session = UUID.randomUUID();
@LauncherNetworkAPI
public final UUID requestUUID = UUID.randomUUID();
private transient final AtomicBoolean started = new AtomicBoolean(false);
public static long getSession() {
public static UUID getSession() {
return Request.session;
}
public static void setSession(long session) {
public static void setSession(UUID session) {
Request.session = session;
}

View file

@ -2,10 +2,8 @@
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.password.AuthECPassword;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launcher.request.auth.password.*;
import pro.gravit.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.VerifyHelper;
@ -23,8 +21,6 @@ public final class AuthRequest extends Request<AuthRequestEvent> implements WebS
private final boolean getSession;
@LauncherNetworkAPI
private final ConnectTypes authType;
@LauncherNetworkAPI
public boolean initProxy;
public AuthRequest(String login, byte[] password) {
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;
}
@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) {
this.login = login;
this.password = new AuthECPassword(encryptedPassword.clone());
@ -68,10 +55,21 @@ public AuthRequest(String login, String password, String auth_id, ConnectTypes a
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() {
if (!registerProviders) {
providers.register("plain", AuthPlainPassword.class);
providers.register("rsa", AuthECPassword.class);
providers.register("2fa", Auth2FAPassword.class);
providers.register("signature", AuthSignaturePassword.class);
providers.register("totp", AuthTOTPPassword.class);
registerProviders = true;
}
}

View file

@ -8,9 +8,9 @@
public final class CheckServerRequest extends Request<CheckServerRequestEvent> implements WebSocketRequest {
@LauncherNetworkAPI
private final String username;
public final String username;
@LauncherNetworkAPI
private final String serverID;
public final 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
@LauncherNetworkAPI
private final String username;
public final String username;
@LauncherNetworkAPI
private final String accessToken;
public final String accessToken;
@LauncherNetworkAPI
private final String serverID;
public final 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.websockets.WebSocketRequest;
import java.util.UUID;
public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest {
@LauncherNetworkAPI
public final long session;
public final UUID session;
public boolean needUserInfo;
public RestoreSessionRequest(long session) {
public RestoreSessionRequest(UUID session) {
this.session = session;
}
public RestoreSessionRequest(UUID session, boolean needUserInfo) {
this.session = session;
this.needUserInfo = needUserInfo;
}
@Override
public String getType() {
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;
}
}

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