package launchserver.response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.net.SocketException;
import launcher.Launcher;
import launcher.helper.IOHelper;
import launcher.helper.LogHelper;
import launcher.helper.SecurityHelper;
import launcher.helper.VerifyHelper;
import launcher.request.Request;
import launcher.request.RequestException;
import launcher.serialize.HInput;
import launcher.serialize.HOutput;
import launchserver.LaunchServer;
import launchserver.response.auth.AuthResponse;
import launchserver.response.auth.CheckServerResponse;
import launchserver.response.auth.JoinServerResponse;
import launchserver.response.profile.BatchProfileByUsernameResponse;
import launchserver.response.profile.ProfileByUUIDResponse;
import launchserver.response.profile.ProfileByUsernameResponse;
import launchserver.response.update.LauncherResponse;
import launchserver.response.update.UpdateResponse;
public final class ResponseThread implements Runnable {
private static final boolean LOG_CONNECTIONS = Boolean.getBoolean("launcher.logConnections");
// Instance
private final LaunchServer server;
private final Socket socket;
public ResponseThread(LaunchServer server, Socket socket) throws SocketException {
this.server = server;
this.socket = socket;
IOHelper.setSocketFlags(socket);
}
@Override
public void run() {
LogHelper.debug("Connection from %s", IOHelper.getIP(socket.getRemoteSocketAddress()));
try (InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream();
HInput input = new HInput(is); HOutput output = new HOutput(os)) {
readHandshake(input, output);
try {
respond(input, output);
} catch (RequestException e) {
output.writeString(e.toString(), 0);
}
} catch (Exception e) {
LogHelper.error(e);
} finally {
server.serverSocketHandler.onDisconnected(socket);
IOHelper.close(socket);
}
}
private void readHandshake(HInput input, HOutput output) throws IOException {
// Verify magic number
int magicNumber = input.readInt();
if (magicNumber != Launcher.PROTOCOL_MAGIC) {
output.writeBoolean(false);
throw new IOException("Protocol magic mismatch");
}
// Verify key modulus
BigInteger keyModulus = input.readBigInteger(SecurityHelper.RSA_KEY_LENGTH + 1);
if (!keyModulus.equals(server.getPrivateKey().getModulus())) {
output.writeBoolean(false);
throw new IOException("Key modulus mismatch");
}
// Protocol successfully verified
output.writeBoolean(true);
output.flush();
}
private void respond(HInput input, HOutput output) throws Exception {
Request.Type type = Request.Type.read(input);
if (LOG_CONNECTIONS) {
LogHelper.info("Connection from %s: %s", IOHelper.getIP(socket.getRemoteSocketAddress()), type.name());
} else {
LogHelper.subDebug("Type: " + type.name());
}
// Choose response based on type
Response response;
switch (type) {
case PING:
response = new PingResponse(server, input, output);
break;
case AUTH:
response = new AuthResponse(server, input, output);
break;
case JOIN_SERVER:
response = new JoinServerResponse(server, input, output);
break;
case CHECK_SERVER:
response = new CheckServerResponse(server, input, output);
break;
case LAUNCHER:
response = new LauncherResponse(server, input, output);
break;
case UPDATE:
response = new UpdateResponse(server, input, output);
break;
case PROFILE_BY_USERNAME:
response = new ProfileByUsernameResponse(server, input, output);
break;
case PROFILE_BY_UUID:
response = new ProfileByUUIDResponse(server, input, output);
break;
case BATCH_PROFILE_BY_USERNAME:
response = new BatchProfileByUsernameResponse(server, input, output);
break;
case CUSTOM:
String name = VerifyHelper.verifyIDName(input.readASCII(255));
response = server.serverSocketHandler.newCustomResponse(name, input, output);
break;
default:
throw new AssertionError("Unsupported request type: " + type.name());
}
// Reply
response.reply();
LogHelper.subDebug("Successfully replied");
}
}