virtual void OnReceivedData(const char * a_Data, size_t a_Size) override { ASSERT(m_Link != nullptr); // Log the incoming data size: AString Hex; CreateHexDump(Hex, a_Data, a_Size, 16); LOGD("Incoming data: %u bytes:\n%s", static_cast<unsigned>(a_Size), Hex.c_str()); }
int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) { // Uncompress the GZIPped data: AString Uncompressed; if (UncompressStringGZIP(a_Metadata.data(), a_Metadata.size(), Uncompressed) != Z_OK) { AString HexDump; CreateHexDump(HexDump, a_Metadata.data(), a_Metadata.size(), 16); LOG("Cannot unGZIP item metadata:\n%s", HexDump.c_str()); return PARSE_ERROR; } // Parse into NBT: cParsedNBT NBT(Uncompressed.data(), Uncompressed.size()); if (!NBT.IsValid()) { AString HexDump; CreateHexDump(HexDump, Uncompressed.data(), Uncompressed.size(), 16); LOG("Cannot parse NBT item metadata:\n%s", HexDump.c_str()); return PARSE_ERROR; } // Load enchantments from the NBT: for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag)) { if ( (NBT.GetType(tag) == TAG_List) && ( (NBT.GetName(tag) == "ench") || (NBT.GetName(tag) == "StoredEnchantments") ) ) { EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag); } } return PARSE_OK; }
void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) { // Decrypt EncNonce using privkey cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; int res = rsaDecryptor.Decrypt((const Byte *)a_EncNonce.data(), a_EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce)); if (res != 4) { LOGD("Bad nonce length"); m_Client->Kick("Hacked client"); return; } if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this) { LOGD("Bad nonce value"); m_Client->Kick("Hacked client"); return; } // Decrypt the symmetric encryption key using privkey: Byte DecryptedKey[MAX_ENC_LEN]; res = rsaDecryptor.Decrypt((const Byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey, sizeof(DecryptedKey)); if (res != 16) { LOGD("Bad key length"); m_Client->Kick("Hacked client"); return; } { // Send encryption key response: cCSLock Lock(m_CSPacket); WriteByte(0xfc); WriteShort(0); WriteShort(0); Flush(); } #ifdef _DEBUG AString DecryptedKeyHex; CreateHexDump(DecryptedKeyHex, DecryptedKey, res, 16); LOGD("Received encryption key, %d bytes:\n%s", res, DecryptedKeyHex.c_str()); #endif StartEncryption(DecryptedKey); return; }
void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID) { ASSERT(a_UUID.size() == 32); // Check if already present: { if (m_UUIDToProfile.find(a_UUID) != m_UUIDToProfile.end()) { return; } } // Create the request address: AString Address = m_UUIDToProfileAddress; ReplaceString(Address, "%UUID%", a_UUID); // Create the HTTP request: AString Request; Request += "GET " + Address + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding Request += "Host: " + m_UUIDToProfileServer + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "Content-Length: 0\r\n"; Request += "\r\n"; // Get the response from the server: AString Response; if (!SecureRequest(m_UUIDToProfileServer, Request, Response)) { return; } // Check the HTTP status line: const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; if (Response.compare(0, Prefix.size(), Prefix)) { LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return; } // Erase the HTTP headers from the response: size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return; } Response.erase(0, idxHeadersEnd + 4); // Parse the returned string into Json: Json::Reader reader; Json::Value root; if (!reader.parse(Response, root, false) || !root.isObject()) { LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__); LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return; } /* Example response: { "id": "b1caf24202a841a78055a079c460eee7", "name": "xoft", "properties": [ { "name": "textures", "value": "eyJ0aW1lc3RhbXAiOjE0MDcwNzAzMjEyNzEsInByb2ZpbGVJZCI6ImIxY2FmMjQyMDJhODQxYTc4MDU1YTA3OWM0NjBlZWU3IiwicHJvZmlsZU5hbWUiOiJ4b2Z0IiwiaXNQdWJsaWMiOnRydWUsInRleHR1cmVzIjp7IlNLSU4iOnsidXJsIjoiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS9iNzc5YmFiZjVhNTg3Zjk0OGFkNjc0N2VhOTEyNzU0MjliNjg4Mjk1YWUzYzA3YmQwZTJmNWJmNGQwNTIifX19", "signature": "XCty+jGEF39hEPrPhYNnCX087kPaoCjYruzYI/DS4nkL5hbjnkSM5Rh15hnUyv/FHhC8OF5rif3D1tQjtMI19KSVaXoUFXpbJM8/+PB8GDgEbX8Fc3u9nYkzOcM/xfxdYsFAdFhLQMkvase/BZLSuPhdy9DdI+TCrO7xuSTZfYmmwVuWo3w5gCY+mSIAnqltnOzaOOTcly75xvO0WYpVk7nJdnR2tvSi0wfrQPDrIg/uzhX7p0SnDqijmBU4QaNez/TNKiFxy69dAzt0RSotlQzqkDbyVKhhv9a4eY8h3pXi4UMftKEj4FAKczxLImkukJXuOn5NN15/Q+le0rJVBC60/xjKIVzltEsMN6qjWD0lQjey7WEL+4pGhCVuWY5KzuZjFvgqszuJTFz7lo+bcHiceldJtea8/fa02eTRObZvdLxbWC9ZfFY0IhpOVKfcLdno/ddDMNMQMi5kMrJ8MZZ/PcW1w5n7MMGWPGCla1kOaC55AL0QYSMGRVEZqgU9wXI5M7sHGZKGM4mWxkbEJYBkpI/p3GyxWgV6v33ZWlsz65TqlNrR1gCLaoFCm7Sif8NqPBZUAONHYon0roXhin/DyEanS93WV6i6FC1Wisscjq2AcvnOlgTo/5nN/1QsMbjNumuMGo37sqjRqlXoPb8zEUbAhhztYuJjEfQ2Rd8=" } ] } */ // Store the returned result into caches: AString PlayerName = root.get("name", "").asString(); if (PlayerName.empty()) { // No valid playername, bail out return; } Json::Value Properties = root.get("properties", ""); Int64 Now = time(NULL); { cCSLock Lock(m_CSUUIDToProfile); m_UUIDToProfile[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now); } { cCSLock Lock(m_CSUUIDToName); m_UUIDToName[a_UUID] = sProfile(PlayerName, a_UUID, Properties, Now); } { cCSLock Lock(m_CSNameToUUID); m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now); } }
void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames) { // Create a list of names to query, by removing those that are already cached: AStringVector NamesToQuery; NamesToQuery.reserve(a_PlayerNames.size()); { cCSLock Lock(m_CSNameToUUID); for (AStringVector::const_iterator itr = a_PlayerNames.begin(), end = a_PlayerNames.end(); itr != end; ++itr) { if (m_NameToUUID.find(*itr) == m_NameToUUID.end()) { NamesToQuery.push_back(*itr); } } // for itr - a_PlayerNames[] } // Lock(m_CSNameToUUID) while (!NamesToQuery.empty()) { // Create the request body - a JSON containing up to MAX_PER_QUERY playernames: Json::Value root; int Count = 0; AStringVector::iterator itr = NamesToQuery.begin(), end = NamesToQuery.end(); for (; (itr != end) && (Count < MAX_PER_QUERY); ++itr, ++Count) { Json::Value req(*itr); root.append(req); } // for itr - a_PlayerNames[] NamesToQuery.erase(NamesToQuery.begin(), itr); Json::FastWriter Writer; AString RequestBody = Writer.write(root); // Create the HTTP request: AString Request; Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding Request += "Host: " + m_NameToUUIDServer + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "Content-Type: application/json\r\n"; Request += Printf("Content-Length: %u\r\n", (unsigned)RequestBody.length()); Request += "\r\n"; Request += RequestBody; // Get the response from the server: AString Response; if (!SecureRequest(m_NameToUUIDServer, Request, Response)) { continue; } // Check the HTTP status line: const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; if (Response.compare(0, Prefix.size(), Prefix)) { LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); continue; } // Erase the HTTP headers from the response: size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); continue; } Response.erase(0, idxHeadersEnd + 4); // Parse the returned string into Json: Json::Reader reader; if (!reader.parse(Response, root, false) || !root.isArray()) { LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON!", __FUNCTION__); LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); continue; } // Store the returned results into cache: size_t JsonCount = root.size(); Int64 Now = time(NULL); { cCSLock Lock(m_CSNameToUUID); for (size_t idx = 0; idx < JsonCount; ++idx) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); if (JsonUUID.empty()) { continue; } m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now); } // for idx - root[] } // cCSLock (m_CSNameToUUID) // Also cache the UUIDToName: { cCSLock Lock(m_CSUUIDToName); for (size_t idx = 0; idx < JsonCount; ++idx) { Json::Value & Val = root[idx]; AString JsonName = Val.get("name", "").asString(); AString JsonUUID = MakeUUIDShort(Val.get("id", "").asString()); if (JsonUUID.empty()) { continue; } m_UUIDToName[JsonUUID] = sProfile(JsonName, JsonUUID, "", "", Now); } // for idx - root[] } } // while (!NamesToQuery.empty()) }
/** Called for each chunk of the incoming body data. */ virtual void OnBodyData(const void * a_Data, size_t a_Size) override { AString hexDump; CreateHexDump(hexDump, a_Data, a_Size, 16); printf("Body data: %u bytes\n%s", static_cast<unsigned>(a_Size), hexDump.c_str()); }
bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties) { LOGD("Trying to authenticate user %s", a_UserName.c_str()); // Create the GET request: AString ActualAddress = m_Address; ReplaceString(ActualAddress, "%USERNAME%", a_UserName); ReplaceString(ActualAddress, "%SERVERID%", a_ServerId); AString Request; Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; Request += "Host: " + m_Server + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "\r\n"; AString Response; if (!SecureGetFromAddress(StarfieldCACert(), m_Server, Request, Response)) { return false; } // Check the HTTP status line: const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; if (Response.compare(0, Prefix.size(), Prefix)) { LOGINFO("User %s failed to auth, bad HTTP status line received", a_UserName.c_str()); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } // Erase the HTTP headers from the response: size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { LOGINFO("User %s failed to authenticate, bad HTTP response header received", a_UserName.c_str()); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } Response.erase(0, idxHeadersEnd + 4); // Parse the Json response: if (Response.empty()) { return false; } Json::Value root; Json::Reader reader; if (!reader.parse(Response, root, false)) { LOGWARNING("cAuthenticator: Cannot parse received data (authentication) to JSON!"); return false; } a_UserName = root.get("name", "Unknown").asString(); a_UUID = root.get("id", "").asString(); a_Properties = root["properties"]; // If the UUID doesn't contain the hashes, insert them at the proper places: if (a_UUID.size() == 32) { a_UUID.insert(8, "-"); a_UUID.insert(13, "-"); a_UUID.insert(18, "-"); a_UUID.insert(23, "-"); } return true; }
bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, AString & a_UUID, Json::Value & a_Properties) { LOGD("Trying to authenticate user %s", a_UserName.c_str()); // Create the GET request: AString ActualAddress = m_Address; ReplaceString(ActualAddress, "%USERNAME%", a_UserName); ReplaceString(ActualAddress, "%SERVERID%", a_ServerId); AString Request; Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; Request += "Host: " + m_Server + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "\r\n"; AString Response; if (!cMojangAPI::SecureRequest(m_Server, Request, Response)) { return false; } // Check the HTTP status line: const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; if (Response.compare(0, Prefix.size(), Prefix)) { LOGINFO("User %s failed to auth, bad HTTP status line received", a_UserName.c_str()); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } // Erase the HTTP headers from the response: size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { LOGINFO("User %s failed to authenticate, bad HTTP response header received", a_UserName.c_str()); LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } Response.erase(0, idxHeadersEnd + 4); // Parse the Json response: if (Response.empty()) { return false; } Json::Value root; Json::Reader reader; if (!reader.parse(Response, root, false)) { LOGWARNING("cAuthenticator: Cannot parse received data (authentication) to JSON!"); return false; } a_UserName = root.get("name", "Unknown").asString(); a_UUID = cMojangAPI::MakeUUIDShort(root.get("id", "").asString()); a_Properties = root["properties"]; // Store the player's profile in the MojangAPI caches: cRoot::Get()->GetMojangAPI().AddPlayerProfile(a_UserName, a_UUID, a_Properties); return true; }