diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs
index cd6716eb..e5ced59f 100644
--- a/MinecraftClient/Program.cs
+++ b/MinecraftClient/Program.cs
@@ -596,7 +596,8 @@ namespace MinecraftClient
if ((Config.Main.General.AccountType == LoginType.microsoft || Config.Main.General.AccountType == LoginType.yggdrasil)
&& (InternalConfig.Account.Password != "-" || Config.Main.General.Method == LoginMethod.browser)
&& Config.Signature.LoginWithSecureProfile
- && protocolversion >= 759 /* 1.19 and above */)
+ && protocolversion >= 759 /* 1.19 and above */
+ && !string.IsNullOrWhiteSpace(session.ID))
{
// Load cached profile key from disk if necessary
if (Config.Main.Advanced.ProfileKeyCache == CacheType.disk)
diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs
index ab70ae6d..579dd925 100644
--- a/MinecraftClient/Protocol/Handlers/Protocol18.cs
+++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs
@@ -3520,81 +3520,113 @@ namespace MinecraftClient.Protocol.Handlers
/// True if ping was successful
public static bool DoPing(string host, int port, ref int protocolVersion, ref ForgeInfo? forgeInfo)
{
- var version = "";
- var tcp = ProxyHandler.NewTcpClient(host, port);
- tcp.ReceiveTimeout = 30000; // 30 seconds
- tcp.ReceiveBufferSize = 1024 * 1024;
- SocketWrapper socketWrapper = new(tcp);
- DataTypes dataTypes = new(MC_1_8_Version);
+ SocketWrapper? socketWrapper = null;
- var serverPort = BitConverter.GetBytes((ushort)port);
- Array.Reverse(serverPort);
-
- // Ping Packet
- var pingPacket = dataTypes.ConcatBytes(
- // Packet Id
- DataTypes.GetVarInt(0),
-
- // Protocol Version
- DataTypes.GetVarInt(-1),
-
- // Server IP (Host)
- dataTypes.GetString(host),
-
- // Server port
- serverPort,
-
- // Next State
- DataTypes.GetVarInt(1));
-
- socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket));
-
- // Status Request Packet
- var statusRequest = DataTypes.GetVarInt(0);
- socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
-
- // Read Response length
- var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
- if (packetLength <= 0) return false;
-
- // Read the Packet Id
- var packetData = new Queue(socketWrapper.ReadDataRAW(packetLength));
- if (dataTypes.ReadNextVarInt(packetData) != 0x00) return false;
-
- var result = dataTypes.ReadNextString(packetData); // Get the Json data
-
- if (Config.Logging.DebugMessages)
+ try
{
- // May contain formatting codes, cannot use WriteLineFormatted
- Console.ForegroundColor = ConsoleColor.DarkGray;
- ConsoleIO.WriteLine(result);
- Console.ForegroundColor = ConsoleColor.Gray;
+ var version = "";
+ var tcp = ProxyHandler.NewTcpClient(host, port);
+ tcp.ReceiveTimeout = 30000; // 30 seconds
+ tcp.ReceiveBufferSize = 1024 * 1024;
+ socketWrapper = new SocketWrapper(tcp);
+ DataTypes dataTypes = new(MC_1_8_Version);
+
+ var serverPort = BitConverter.GetBytes((ushort)port);
+ Array.Reverse(serverPort);
+
+ // Ping Packet
+ var pingPacket = dataTypes.ConcatBytes(
+ // Packet Id
+ DataTypes.GetVarInt(0),
+
+ // Protocol Version
+ DataTypes.GetVarInt(-1),
+
+ // Server IP (Host)
+ dataTypes.GetString(host),
+
+ // Server port
+ serverPort,
+
+ // Next State
+ DataTypes.GetVarInt(1));
+
+ socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingPacket.Length), pingPacket));
+
+ // Status Request Packet
+ var statusRequest = DataTypes.GetVarInt(0);
+ socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(statusRequest.Length), statusRequest));
+
+ // Read Response length
+ var packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
+ if (packetLength <= 0)
+ return false;
+
+ // Read the Packet Id
+ var packetData = new Queue(socketWrapper.ReadDataRAW(packetLength));
+ if (dataTypes.ReadNextVarInt(packetData) != 0x00)
+ return false;
+
+ var result = dataTypes.ReadNextString(packetData); // Get the Json data
+
+ if (Config.Logging.DebugMessages)
+ {
+ // May contain formatting codes, cannot use WriteLineFormatted
+ Console.ForegroundColor = ConsoleColor.DarkGray;
+ ConsoleIO.WriteLine(result);
+ Console.ForegroundColor = ConsoleColor.Gray;
+ }
+
+ if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}"))
+ return false;
+
+ var jsonData = Json.ParseJson(result);
+ if (jsonData is not System.Text.Json.Nodes.JsonObject jsonObj || !jsonObj.ContainsKey("version"))
+ return false;
+
+ var versionData = jsonObj["version"]!.AsObject();
+
+ // Retrieve display name of the Minecraft version
+ if (versionData["name"] is { } nameNode)
+ version = nameNode.GetStringValue();
+
+ // Retrieve protocol version number for handling this server
+ if (versionData["protocol"] is { } protocolNode)
+ protocolVersion = int.Parse(protocolNode.GetStringValue(),
+ NumberStyles.Any, CultureInfo.CurrentCulture);
+
+ // Check for forge on the server.
+ Protocol18Forge.ServerInfoCheckForge(jsonObj, ref forgeInfo);
+
+ // Complete the normal status exchange so the probe connection closes cleanly server-side.
+ try
+ {
+ long pingPayload = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+ var pingRequest = dataTypes.ConcatBytes(DataTypes.GetVarInt(0x01), DataTypes.GetLong(pingPayload));
+ socketWrapper.SendDataRAW(dataTypes.ConcatBytes(DataTypes.GetVarInt(pingRequest.Length), pingRequest));
+
+ packetLength = dataTypes.ReadNextVarIntRAW(socketWrapper);
+ if (packetLength > 0)
+ {
+ packetData = new Queue(socketWrapper.ReadDataRAW(packetLength));
+ if (dataTypes.ReadNextVarInt(packetData) == 0x01)
+ dataTypes.ReadNextLong(packetData);
+ }
+ }
+ catch
+ {
+ // Some servers may close the probe connection immediately after the status response.
+ }
+
+ ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version,
+ protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
+
+ return true;
+ }
+ finally
+ {
+ socketWrapper?.Disconnect();
}
-
- if (string.IsNullOrEmpty(result) || !result.StartsWith("{") || !result.EndsWith("}")) return false;
-
- var jsonData = Json.ParseJson(result);
- if (jsonData is not System.Text.Json.Nodes.JsonObject jsonObj || !jsonObj.ContainsKey("version"))
- return false;
-
- var versionData = jsonObj["version"]!.AsObject();
-
- //Retrieve display name of the Minecraft version
- if (versionData["name"] is { } nameNode)
- version = nameNode.GetStringValue();
-
- //Retrieve protocol version number for handling this server
- if (versionData["protocol"] is { } protocolNode)
- protocolVersion = int.Parse(protocolNode.GetStringValue(),
- NumberStyles.Any, CultureInfo.CurrentCulture);
-
- // Check for forge on the server.
- Protocol18Forge.ServerInfoCheckForge(jsonObj, ref forgeInfo);
-
- ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_server_protocol, version,
- protocolVersion + (forgeInfo != null ? Translations.mcc_with_forge : "")));
-
- return true;
}
///
diff --git a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs
index ba5e5610..604385b0 100644
--- a/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs
+++ b/MinecraftClient/Protocol/ProfileKey/KeyUtils.cs
@@ -20,6 +20,9 @@ namespace MinecraftClient.Protocol.ProfileKey
ProxiedWebRequest.Response? response = null;
try
{
+ if (!isYggdrasil && string.IsNullOrWhiteSpace(accessToken))
+ return null;
+
if (!isYggdrasil)
{
var request = new ProxiedWebRequest(certificates)
@@ -34,11 +37,28 @@ namespace MinecraftClient.Protocol.ProfileKey
{
ConsoleIO.WriteLine(response.Body.ToString());
}
+
+ if (response.StatusCode < 200 || response.StatusCode >= 300)
+ {
+ throw new InvalidOperationException(string.IsNullOrWhiteSpace(response.Body)
+ ? "Certificate endpoint returned an error response."
+ : response.Body);
+ }
}
// see https://github.com/yushijinhun/authlib-injector/blob/da910956eaa30d2f6c2c457222d188aeb53b0d1f/src/main/java/moe/yushi/authlibinjector/httpd/ProfileKeyFilter.java#L49
// POST to "https://api.minecraftservices.com/player/certificates" with authlib-injector will get a dummy response
var json = isYggdrasil ? MakeDummyResponse() : Json.ParseJson(response!.Body);
+ if (json?["keyPair"]?["publicKey"] == null
+ || json["keyPair"]?["privateKey"] == null
+ || json["publicKeySignature"] == null
+ || json["publicKeySignatureV2"] == null
+ || json["expiresAt"] == null
+ || json["refreshedAfter"] == null)
+ {
+ throw new InvalidOperationException("Certificate endpoint returned an unexpected payload.");
+ }
+
// Error here
PublicKey publicKey = new(pemKey: json!["keyPair"]!["publicKey"]!.GetStringValue(),
sig: json["publicKeySignature"]!.GetStringValue(),