forked from GeyserMC/Geyser
Merge Floodgate changes
This commit is contained in:
commit
2fc591e341
16 changed files with 351 additions and 24 deletions
|
@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.v389.Bedrock_v389;
|
|||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.geysermc.common.AuthType;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.common.bootstrap.IGeyserBootstrap;
|
||||
import org.geysermc.common.logger.IGeyserLogger;
|
||||
|
@ -65,6 +66,7 @@ public class GeyserConnector {
|
|||
private static GeyserConnector instance;
|
||||
|
||||
private RemoteServer remoteServer;
|
||||
private AuthType authType;
|
||||
|
||||
private IGeyserLogger logger;
|
||||
private IGeyserConfiguration config;
|
||||
|
@ -105,6 +107,7 @@ public class GeyserConnector {
|
|||
|
||||
commandMap = new GeyserCommandMap(this);
|
||||
remoteServer = new RemoteServer(config.getRemote().getAddress(), config.getRemote().getPort());
|
||||
authType = AuthType.getByName(config.getRemote().getAuthType());
|
||||
|
||||
passthroughThread = new PingPassthroughThread(this);
|
||||
if (config.isPingPassthrough())
|
||||
|
@ -125,7 +128,7 @@ public class GeyserConnector {
|
|||
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1));
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", config.getRemote()::getAuthType));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", authType.name()::toLowerCase));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
||||
}
|
||||
|
||||
|
|
|
@ -126,9 +126,4 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
boolean defaultHandler(BedrockPacket packet) {
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(InventoryTransactionPacket packet) {
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,11 +29,9 @@ import com.github.steveice10.mc.auth.data.GameProfile;
|
|||
import com.github.steveice10.mc.auth.exception.request.RequestException;
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket;
|
||||
import com.github.steveice10.packetlib.Client;
|
||||
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.PacketReceivedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.SessionAdapter;
|
||||
import com.github.steveice10.packetlib.event.session.*;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
|
||||
import com.nukkitx.math.vector.Vector2f;
|
||||
|
@ -49,6 +47,7 @@ import com.nukkitx.protocol.bedrock.packet.*;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.geysermc.common.AuthType;
|
||||
import org.geysermc.common.window.FormWindow;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
|
@ -56,12 +55,20 @@ import org.geysermc.connector.entity.PlayerEntity;
|
|||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.remote.RemoteServer;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||
import org.geysermc.connector.network.session.cache.*;
|
||||
import org.geysermc.connector.network.translators.Registry;
|
||||
import org.geysermc.connector.network.translators.TranslatorsInit;
|
||||
import org.geysermc.connector.utils.Toolbox;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
|
@ -71,7 +78,8 @@ public class GeyserSession implements CommandSender {
|
|||
private final UpstreamSession upstream;
|
||||
private RemoteServer remoteServer;
|
||||
private Client downstream;
|
||||
private AuthData authData;
|
||||
@Setter private AuthData authData;
|
||||
@Setter private BedrockClientData clientData;
|
||||
|
||||
private PlayerEntity playerEntity;
|
||||
private PlayerInventory inventory;
|
||||
|
@ -123,8 +131,13 @@ public class GeyserSession implements CommandSender {
|
|||
public void connect(RemoteServer remoteServer) {
|
||||
startGame();
|
||||
this.remoteServer = remoteServer;
|
||||
if (!(connector.getConfig().getRemote().getAuthType().hashCode() == "online".hashCode())) {
|
||||
connector.getLogger().info("Attempting to login using offline mode... authentication is disabled.");
|
||||
if (connector.getAuthType() != AuthType.ONLINE) {
|
||||
connector.getLogger().info(
|
||||
"Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " +
|
||||
(connector.getAuthType() == AuthType.OFFLINE ?
|
||||
"authentication is disabled." : "authentication will be encrypted"
|
||||
)
|
||||
);
|
||||
authenticate(authData.getName());
|
||||
}
|
||||
|
||||
|
@ -177,8 +190,56 @@ public class GeyserSession implements CommandSender {
|
|||
protocol = new MinecraftProtocol(username);
|
||||
}
|
||||
|
||||
boolean floodgate = connector.getAuthType() == AuthType.FLOODGATE;
|
||||
final PublicKey publicKey;
|
||||
|
||||
if (floodgate) {
|
||||
PublicKey key = null;
|
||||
try {
|
||||
key = EncryptionUtil.getKeyFromFile(
|
||||
Paths.get(connector.getConfig().getFloodgateKeyFile()),
|
||||
PublicKey.class
|
||||
);
|
||||
} catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
connector.getLogger().error("Error while reading Floodgate key file", e);
|
||||
}
|
||||
publicKey = key;
|
||||
} else publicKey = null;
|
||||
|
||||
if (publicKey != null) {
|
||||
connector.getLogger().info("Loaded Floodgate key!");
|
||||
}
|
||||
|
||||
downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory());
|
||||
downstream.getSession().addListener(new SessionAdapter() {
|
||||
@Override
|
||||
public void packetSending(PacketSendingEvent event) {
|
||||
//todo move this somewhere else
|
||||
if (event.getPacket() instanceof HandshakePacket && floodgate) {
|
||||
String encrypted = "";
|
||||
try {
|
||||
encrypted = EncryptionUtil.encryptBedrockData(publicKey, new BedrockData(
|
||||
clientData.getGameVersion(),
|
||||
authData.getName(),
|
||||
authData.getXboxUUID(),
|
||||
clientData.getDeviceOS().ordinal(),
|
||||
clientData.getLanguageCode(),
|
||||
clientData.getCurrentInputMode().ordinal(),
|
||||
upstream.getSession().getAddress().getAddress().getHostAddress()
|
||||
));
|
||||
} catch (Exception e) {
|
||||
connector.getLogger().error("Failed to encrypt message", e);
|
||||
}
|
||||
|
||||
HandshakePacket handshakePacket = event.getPacket();
|
||||
event.setPacket(new HandshakePacket(
|
||||
handshakePacket.getProtocolVersion(),
|
||||
handshakePacket.getHostname() + '\0' + BedrockData.FLOODGATE_IDENTIFIER + '\0' + encrypted,
|
||||
handshakePacket.getPort(),
|
||||
handshakePacket.getIntent()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(ConnectedEvent event) {
|
||||
|
@ -227,10 +288,6 @@ public class GeyserSession implements CommandSender {
|
|||
closed = true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
disconnect("Server closed.");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package org.geysermc.connector.network.session.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
@Getter
|
||||
public class BedrockClientData {
|
||||
@JsonProperty(value = "GameVersion")
|
||||
private String gameVersion;
|
||||
@JsonProperty(value = "ServerAddress")
|
||||
private String serverAddress;
|
||||
@JsonProperty(value = "ThirdPartyName")
|
||||
private String username;
|
||||
@JsonProperty(value = "LanguageCode")
|
||||
private String languageCode;
|
||||
|
||||
@JsonProperty(value = "SkinId")
|
||||
private String skinId;
|
||||
@JsonProperty(value = "SkinData")
|
||||
private String skinData;
|
||||
@JsonProperty(value = "CapeId")
|
||||
private String capeId;
|
||||
@JsonProperty(value = "CapeData")
|
||||
private byte[] capeData;
|
||||
@JsonProperty(value = "CapeOnClassicSkin")
|
||||
private boolean capeOnClassicSkin;
|
||||
@JsonProperty(value = "SkinResourcePatch")
|
||||
private String geometryName;
|
||||
@JsonProperty(value = "SkinGeometryData")
|
||||
private String geometryData;
|
||||
@JsonProperty(value = "PremiumSkin")
|
||||
private boolean premiumSkin;
|
||||
|
||||
@JsonProperty(value = "DeviceId")
|
||||
private String deviceId;
|
||||
@JsonProperty(value = "DeviceModel")
|
||||
private String deviceModel;
|
||||
@JsonProperty(value = "DeviceOS")
|
||||
private DeviceOS deviceOS;
|
||||
@JsonProperty(value = "UIProfile")
|
||||
private UIProfile uiProfile;
|
||||
@JsonProperty(value = "GuiScale")
|
||||
private int guiScale;
|
||||
@JsonProperty(value = "CurrentInputMode")
|
||||
private InputMode currentInputMode;
|
||||
@JsonProperty(value = "DefaultInputMode")
|
||||
private InputMode defaultInputMode;
|
||||
@JsonProperty("PlatformOnlineId")
|
||||
private String platformOnlineId;
|
||||
@JsonProperty(value = "PlatformOfflineId")
|
||||
private String platformOfflineId;
|
||||
@JsonProperty(value = "SelfSignedId")
|
||||
private UUID selfSignedId;
|
||||
@JsonProperty(value = "ClientRandomId")
|
||||
private long clientRandomId;
|
||||
|
||||
public enum UIProfile {
|
||||
@JsonEnumDefaultValue
|
||||
CLASSIC,
|
||||
POCKET
|
||||
}
|
||||
|
||||
public enum DeviceOS {
|
||||
@JsonEnumDefaultValue
|
||||
UNKOWN,
|
||||
ANDROID,
|
||||
IOS,
|
||||
OSX,
|
||||
FIREOS,
|
||||
GEARVR,
|
||||
HOLOLENS,
|
||||
WIN10,
|
||||
WIN32,
|
||||
DEDICATED,
|
||||
ORBIS,
|
||||
NX
|
||||
}
|
||||
|
||||
public enum InputMode {
|
||||
@JsonEnumDefaultValue
|
||||
UNKNOWN,
|
||||
KEYBOARD_MOUSE,
|
||||
TOUCH, // I guess Touch?
|
||||
CONTROLLER
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.geysermc.common.window.response.CustomFormResponse;
|
|||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||
import org.geysermc.connector.network.session.cache.WindowCache;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
@ -73,7 +74,7 @@ public class LoginEncryptionUtils {
|
|||
encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certChainData);
|
||||
}
|
||||
|
||||
private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) {
|
||||
private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String clientData, JsonNode certChainData) {
|
||||
try {
|
||||
boolean validChain = validateChainData(certChainData);
|
||||
|
||||
|
@ -86,17 +87,23 @@ public class LoginEncryptionUtils {
|
|||
throw new RuntimeException("AuthData was not found!");
|
||||
}
|
||||
|
||||
JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData");
|
||||
session.setAuthenticationData(new AuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID")));
|
||||
JsonNode extraData = payload.get("extraData");
|
||||
session.setAuthenticationData(new AuthData(
|
||||
extraData.get("displayName").asText(),
|
||||
UUID.fromString(extraData.get("identity").asText()),
|
||||
extraData.get("XUID").asText()
|
||||
));
|
||||
|
||||
if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) {
|
||||
throw new RuntimeException("Identity Public Key was not found!");
|
||||
}
|
||||
|
||||
ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue());
|
||||
JWSObject clientJwt = JWSObject.parse(playerSkin);
|
||||
JWSObject clientJwt = JWSObject.parse(clientData);
|
||||
EncryptionUtils.verifyJwt(clientJwt, identityPublicKey);
|
||||
|
||||
session.setClientData(JSON_MAPPER.convertValue(JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()), BedrockClientData.class));
|
||||
|
||||
if (EncryptionUtils.canUseEncryption()) {
|
||||
LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.nukkitx.protocol.bedrock.packet.PlayerListPacket;
|
|||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import org.geysermc.common.AuthType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -20,6 +21,7 @@ import java.util.UUID;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
public class SkinUtils {
|
||||
|
||||
public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) {
|
||||
GameProfileData data = GameProfileData.from(profile);
|
||||
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
|
||||
|
@ -97,7 +99,7 @@ public class SkinUtils {
|
|||
|
||||
return new GameProfileData(skinUrl, capeUrl, isAlex);
|
||||
} catch (Exception exception) {
|
||||
if (!GeyserConnector.getInstance().getConfig().getRemote().getAuthType().equals("offline")) {
|
||||
if (GeyserConnector.getInstance().getAuthType() != AuthType.OFFLINE) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Got invalid texture data for " + profile.getName() + " " + exception.getMessage());
|
||||
}
|
||||
// return default skin with default cape when texture data is invalid
|
||||
|
@ -157,6 +159,7 @@ public class SkinUtils {
|
|||
|
||||
if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,14 @@ remote:
|
|||
address: 127.0.0.1
|
||||
# The port of the remote (Java Edition) server
|
||||
port: 25565
|
||||
|
||||
# Authentication type. Can be offline, online, or hybrid (see the wiki).
|
||||
# Authentication type. Can be offline, online, or floodgate (see the wiki).
|
||||
auth-type: online
|
||||
|
||||
# Floodgate uses encryption to ensure use from authorised sources.
|
||||
# This should point to the public key generated by Floodgate (Bungee or CraftBukkit)
|
||||
# You can ignore this when not using Floodgate.
|
||||
floodgate-key-file: public-key.pem
|
||||
|
||||
## the Xbox/MCPE username is the key for the Java server auth-info
|
||||
## this allows automatic configuration/login to the remote Java server
|
||||
## if you are brave/stupid enough to put your Mojang account info into
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue