bool CAICHHashSet::CreatePartRecoveryData(uint64 nPartStartPos, CFileDataIO* fileDataOut, bool bDbgDontLoad) { wxASSERT( m_pOwner ); if (m_pOwner->IsPartFile() || m_eStatus != AICH_HASHSETCOMPLETE) { wxFAIL; return false; } if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE) { wxFAIL; return false; } if (!bDbgDontLoad) { if (!LoadHashSet()) { AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Created RecoveryData error: failed to load hashset. File: %s")) % m_pOwner->GetFileName()); SetStatus(AICH_ERROR); return false; } } bool bResult; uint8 nLevel = 0; uint32 nPartSize = min<uint64>(PARTSIZE, m_pOwner->GetFileSize()-nPartStartPos); m_pHashTree.FindHash(nPartStartPos, nPartSize,&nLevel); uint16 nHashsToWrite = (nLevel-1) + nPartSize/EMBLOCKSIZE + ((nPartSize % EMBLOCKSIZE != 0 )? 1:0); const bool bUse32BitIdentifier = m_pOwner->IsLargeFile(); if (bUse32BitIdentifier) { fileDataOut->WriteUInt16(0); // no 16bit hashs to write } fileDataOut->WriteUInt16(nHashsToWrite); uint64 nCheckFilePos = fileDataOut->GetPosition(); if (m_pHashTree.CreatePartRecoveryData(nPartStartPos, nPartSize, fileDataOut, 0, bUse32BitIdentifier)) { if (nHashsToWrite*(HASHSIZE+(bUse32BitIdentifier? 4u:2u)) != fileDataOut->GetPosition() - nCheckFilePos) { wxFAIL; AddDebugLogLineN( logSHAHashSet, CFormat(wxT("Created RecoveryData has wrong length. File: %s")) % m_pOwner->GetFileName() ); bResult = false; SetStatus(AICH_ERROR); } else { bResult = true; } } else { AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Failed to create RecoveryData for '%s'")) % m_pOwner->GetFileName()); bResult = false; SetStatus(AICH_ERROR); } if (!bUse32BitIdentifier) { fileDataOut->WriteUInt16(0); // no 32bit hashs to write } if (!bDbgDontLoad) { FreeHashSet(); } return bResult; }
bool CAICHRecoveryHashSet::CreatePartRecoveryData(uint64 nPartStartPos, CFileDataIO* fileDataOut, bool bDbgDontLoad){ ASSERT( m_pOwner ); if (m_pOwner->IsPartFile() || m_eStatus != AICH_HASHSETCOMPLETE){ ASSERT( false ); return false; } if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE){ ASSERT( false ); return false; } if (!bDbgDontLoad){ if (!LoadHashSet()){ theApp.QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Created RecoveryData error: failed to load hashset (file: %s)"), m_pOwner->GetFileName() ); SetStatus(AICH_ERROR); return false; } } bool bResult; uint8 nLevel = 0; uint32 nPartSize = (uint32)min(PARTSIZE, (uint64)m_pOwner->GetFileSize()-nPartStartPos); m_pHashTree.FindHash(nPartStartPos, nPartSize,&nLevel); uint16 nHashsToWrite = (uint16)((nLevel-1) + nPartSize/EMBLOCKSIZE + ((nPartSize % EMBLOCKSIZE != 0 )? 1:0)); const bool bUse32BitIdentifier = m_pOwner->IsLargeFile(); if (bUse32BitIdentifier) fileDataOut->WriteUInt16(0); // no 16bit hashs to write fileDataOut->WriteUInt16(nHashsToWrite); uint32 nCheckFilePos = (UINT)fileDataOut->GetPosition(); if (m_pHashTree.CreatePartRecoveryData(nPartStartPos, nPartSize, fileDataOut, 0, bUse32BitIdentifier)){ if (nHashsToWrite*(HASHSIZE+(bUse32BitIdentifier? 4:2)) != fileDataOut->GetPosition() - nCheckFilePos){ ASSERT( false ); theApp.QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Created RecoveryData has wrong length (file: %s)"), m_pOwner->GetFileName() ); bResult = false; SetStatus(AICH_ERROR); } else bResult = true; } else{ theApp.QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Failed to create RecoveryData for %s"), m_pOwner->GetFileName() ); bResult = false; SetStatus(AICH_ERROR); } if (!bUse32BitIdentifier) fileDataOut->WriteUInt16(0); // no 32bit hashs to write if (!bDbgDontLoad){ FreeHashSet(); } return bResult; }
void CAICHRecoveryHashSet::UntrustedHashReceived(const CAICHHash& Hash, uint32 dwFromIP){ switch(GetStatus()){ case AICH_EMPTY: case AICH_UNTRUSTED: case AICH_TRUSTED: break; default: return; } bool bFound = false; bool bAdded = false; for (int i = 0; i < m_aUntrustedHashs.GetCount(); i++){ if (m_aUntrustedHashs[i].m_Hash == Hash){ bAdded = m_aUntrustedHashs[i].AddSigningIP(dwFromIP, false); bFound = true; break; } } if (!bFound) { for (int i = 0; i < m_aUntrustedHashs.GetCount(); i++) { if (!m_aUntrustedHashs[i].AddSigningIP(dwFromIP, true)) { AddDebugLogLine(DLP_LOW, false, _T("Received different AICH hashs for file %s from IP/20 %s, ignored"), (m_pOwner != NULL) ? m_pOwner->GetFileName() : _T(""), dwFromIP); // nothing changed, so we can return early without any rechecks return; } } bAdded = true; CAICHUntrustedHash uhToAdd; uhToAdd.m_Hash = Hash; uhToAdd.AddSigningIP(dwFromIP, false); m_aUntrustedHashs.Add(uhToAdd); } uint32 nSigningIPsTotal = 0; // unique clients who send us a hash int nMostTrustedPos = (-1); // the hash which most clients send us uint32 nMostTrustedIPs = 0; for (uint32 i = 0; i < (uint32)m_aUntrustedHashs.GetCount(); i++){ nSigningIPsTotal += m_aUntrustedHashs[i].m_adwIpsSigning.GetCount(); if ((uint32)m_aUntrustedHashs[i].m_adwIpsSigning.GetCount() > nMostTrustedIPs){ nMostTrustedIPs = m_aUntrustedHashs[i].m_adwIpsSigning.GetCount(); nMostTrustedPos = i; } } if (nMostTrustedPos == (-1) || nSigningIPsTotal == 0){ ASSERT( false ); return; } // the check if we trust any hash if ( thePrefs.IsTrustingEveryHash() || (nMostTrustedIPs >= MINUNIQUEIPS_TOTRUST && (100 * nMostTrustedIPs)/nSigningIPsTotal >= MINPERCENTAGE_TOTRUST)){ //trusted //theApp.QueueDebugLogLine(false, _T("AICH Hash received: %s (%sadded), We have now %u hash from %u unique IPs. We trust the Hash %s from %u clients (%u%%). Added IP:%s, file: %s") //, Hash.GetString(), bAdded? _T(""):_T("not "), m_aUntrustedHashs.GetCount(), nSigningIPsTotal, m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString() //, nMostTrustedIPs, (100 * nMostTrustedIPs)/nSigningIPsTotal, ipstr(dwFromIP & 0x00F0FFFF), m_pOwner->GetFileName()); SetStatus(AICH_TRUSTED); if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash){ SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_TRUSTED); FreeHashSet(); } } else{ // untrusted //theApp.QueueDebugLogLine(false, _T("AICH Hash received: %s (%sadded), We have now %u hash from %u unique IPs. Best Hash (%s) is from %u clients (%u%%) - but we dont trust it yet. Added IP:%s, file: %s") // , Hash.GetString(), bAdded? _T(""):_T("not "), m_aUntrustedHashs.GetCount(), nSigningIPsTotal, m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString() // , nMostTrustedIPs, (100 * nMostTrustedIPs)/nSigningIPsTotal, ipstr(dwFromIP & 0x00F0FFFF), m_pOwner->GetFileName()); SetStatus(AICH_UNTRUSTED); if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash){ SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_UNTRUSTED); FreeHashSet(); } } }
// this function is only allowed to be called right after successfully calculating the hashset (!) // will delete the hashset, after saving to free the memory bool CAICHRecoveryHashSet::SaveHashSet(){ if (m_eStatus != AICH_HASHSETCOMPLETE){ ASSERT( false ); return false; } if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize()){ ASSERT( false ); return false; } CSingleLock lockKnown2Met(&m_mutKnown2File, false); if (!lockKnown2Met.Lock(5000)){ return false; } CString fullpath = thePrefs.GetMuleDirectory(EMULE_CONFIGDIR); fullpath.Append(KNOWN2_MET_FILENAME); CSafeFile file; CFileException fexp; if (!file.Open(fullpath,CFile::modeCreate|CFile::modeReadWrite|CFile::modeNoTruncate|CFile::osSequentialScan|CFile::typeBinary|CFile::shareDenyNone, &fexp)){ if (fexp.m_cause != CFileException::fileNotFound){ CString strError(_T("Failed to load ") KNOWN2_MET_FILENAME _T(" file")); TCHAR szError[MAX_CFEXP_ERRORMSG]; if (fexp.GetErrorMessage(szError, ARRSIZE(szError))){ strError += _T(" - "); strError += szError; } theApp.QueueLogLine(true, _T("%s"), strError); } return false; } try { //setvbuf(file.m_pStream, NULL, _IOFBF, 16384); uint8 header = file.ReadUInt8(); if (header != KNOWN2_MET_VERSION){ AfxThrowFileException(CFileException::endOfFile, 0, file.GetFileName()); } // first we check if the hashset we want to write is already stored if (m_liAICHHashsStored.Find(m_pHashTree.m_Hash) != NULL) { theApp.QueueDebugLogLine(false, _T("AICH Hashset to write should be already present in known2.met - %s"), m_pHashTree.m_Hash.GetString()); // this hashset if already available, no need to save it again return true; } /*CAICHHash CurrentHash; while (file.GetPosition() < nExistingSize){ CurrentHash.Read(&file); if (m_pHashTree.m_Hash == CurrentHash){ // this hashset if already available, no need to save it again return true; } nHashCount = file.ReadUInt32(); if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize){ AfxThrowFileException(CFileException::endOfFile, 0, file.GetFileName()); } // skip the rest of this hashset file.Seek(nHashCount*HASHSIZE, CFile::current); }*/ // write hashset uint32 nExistingSize = (UINT)file.GetLength(); file.SeekToEnd(); m_pHashTree.m_Hash.Write(&file); uint32 nHashCount = (uint32)((PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE)); if (m_pHashTree.m_nDataSize % PARTSIZE != 0) nHashCount += (uint32)((m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0)); file.WriteUInt32(nHashCount); if (!m_pHashTree.WriteLowestLevelHashs(&file, 0, true, true)){ // thats bad... really file.SetLength(nExistingSize); theApp.QueueDebugLogLine(true, _T("Failed to save HashSet: WriteLowestLevelHashs() failed!")); return false; } if (file.GetLength() != nExistingSize + (nHashCount+1)*HASHSIZE + 4){ // thats even worse file.SetLength(nExistingSize); theApp.QueueDebugLogLine(true, _T("Failed to save HashSet: Calculated and real size of hashset differ!")); return false; } CAICHRecoveryHashSet::AddStoredAICHHash(m_pHashTree.m_Hash); theApp.QueueDebugLogLine(false, _T("Successfully saved eMuleAC Hashset, %u Hashs + 1 Masterhash written"), nHashCount); file.Flush(); file.Close(); } catch(CFileException* error){ if (error->m_cause == CFileException::endOfFile) theApp.QueueLogLine(true, GetResString(IDS_ERR_MET_BAD), KNOWN2_MET_FILENAME); else{ TCHAR buffer[MAX_CFEXP_ERRORMSG]; error->GetErrorMessage(buffer, ARRSIZE(buffer)); theApp.QueueLogLine(true,GetResString(IDS_ERR_SERVERMET_UNKNOWN),buffer); } error->Delete(); return false; } FreeHashSet(); return true; }
CAICHRecoveryHashSet::~CAICHRecoveryHashSet(void) { FreeHashSet(); }
void CAICHHashSet::UntrustedHashReceived(const CAICHHash& Hash, uint32 dwFromIP) { switch(GetStatus()) { case AICH_EMPTY: case AICH_UNTRUSTED: case AICH_TRUSTED: break; default: return; } bool bFound = false; bool bAdded = false; for (uint32 i = 0; i < m_aUntrustedHashs.size(); ++i) { if (m_aUntrustedHashs[i].m_Hash == Hash) { bAdded = m_aUntrustedHashs[i].AddSigningIP(dwFromIP); bFound = true; break; } } if (!bFound) { bAdded = true; CAICHUntrustedHash uhToAdd; uhToAdd.m_Hash = Hash; uhToAdd.AddSigningIP(dwFromIP); m_aUntrustedHashs.push_back(uhToAdd); } uint32 nSigningIPsTotal = 0; // unique clients who send us a hash int nMostTrustedPos = (-1); // the hash which most clients send us uint32 nMostTrustedIPs = 0; for (uint32 i = 0; i < (uint32)m_aUntrustedHashs.size(); ++i) { nSigningIPsTotal += m_aUntrustedHashs[i].m_adwIpsSigning.size(); if ((uint32)m_aUntrustedHashs[i].m_adwIpsSigning.size() > nMostTrustedIPs) { nMostTrustedIPs = m_aUntrustedHashs[i].m_adwIpsSigning.size(); nMostTrustedPos = i; } } if (nMostTrustedPos == (-1) || nSigningIPsTotal == 0) { wxFAIL; return; } // the check if we trust any hash if ( thePrefs::IsTrustingEveryHash() || (nMostTrustedIPs >= MINUNIQUEIPS_TOTRUST && (100 * nMostTrustedIPs)/nSigningIPsTotal >= MINPERCENTAGE_TOTRUST)) { //trusted AddDebugLogLineM(false, logSHAHashSet, CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ") wxT("We trust the Hash %s from %u client(s) (%u%%). File: %s")) % (bAdded ? wxT("") : wxT("not ")) % m_aUntrustedHashs.size() % nSigningIPsTotal % m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString() % nMostTrustedIPs % ((100 * nMostTrustedIPs) / nSigningIPsTotal) % m_pOwner->GetFileName()); SetStatus(AICH_TRUSTED); if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) { SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_TRUSTED); FreeHashSet(); } } else { // untrusted AddDebugLogLineM(false, logSHAHashSet, CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ") wxT("Best Hash %s from %u clients (%u%%) - but we dont trust it yet. File: %s")) % (bAdded ? wxT(""): wxT("not ")) % m_aUntrustedHashs.size() % nSigningIPsTotal % m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString() % nMostTrustedIPs % ((100 * nMostTrustedIPs) / nSigningIPsTotal) % m_pOwner->GetFileName()); SetStatus(AICH_UNTRUSTED); if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) { SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_UNTRUSTED); FreeHashSet(); } } }
// this function is only allowed to be called right after successfully calculating the hashset (!) // will delete the hashset, after saving to free the memory bool CAICHHashSet::SaveHashSet() { if (m_eStatus != AICH_HASHSETCOMPLETE) { wxFAIL; return false; } if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize()) { wxFAIL; return false; } try { const wxString fullpath = theApp->ConfigDir + KNOWN2_MET_FILENAME; const bool exists = wxFile::Exists(fullpath); CFile file(fullpath, exists ? CFile::read_write : CFile::write); if (!file.IsOpened()) { AddDebugLogLineM( true, logSHAHashSet, wxT("Failed to save HashSet: opening met file failed!")); return false; } uint64 nExistingSize = file.GetLength(); if (nExistingSize) { uint8 header = file.ReadUInt8(); if (header != KNOWN2_MET_VERSION) { AddDebugLogLineM( true, logSHAHashSet, wxT("Saving failed: Current file is not a met-file!")); return false; } AddDebugLogLineM( false, logSHAHashSet, wxString::Format(wxT("Met file is version 0x%2.2x."),header)); } else { file.WriteUInt8(KNOWN2_MET_VERSION); // Update the recorded size, in order for the sanity check below to work. nExistingSize += 1; } // first we check if the hashset we want to write is already stored CAICHHash CurrentHash; while (file.GetPosition() < nExistingSize) { CurrentHash.Read(&file); if (m_pHashTree.m_Hash == CurrentHash) { // this hashset if already available, no need to save it again return true; } uint32 nHashCount = file.ReadUInt32(); if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize) { AddDebugLogLineM( true, logSHAHashSet, wxT("Saving failed: File contains fewer entries than specified!")); return false; } // skip the rest of this hashset file.Seek(nHashCount*HASHSIZE, wxFromCurrent); } // write hashset m_pHashTree.m_Hash.Write(&file); uint32 nHashCount = (PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE); if (m_pHashTree.m_nDataSize % PARTSIZE != 0) { nHashCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0); } file.WriteUInt32(nHashCount); if (!m_pHashTree.WriteLowestLevelHashs(&file, 0, true, true)) { // thats bad... really file.SetLength(nExistingSize); AddDebugLogLineM( true, logSHAHashSet, wxT("Failed to save HashSet: WriteLowestLevelHashs() failed!")); return false; } if (file.GetLength() != nExistingSize + (nHashCount+1)*HASHSIZE + 4) { // thats even worse file.SetLength(nExistingSize); AddDebugLogLineM( true, logSHAHashSet, wxT("Failed to save HashSet: Calculated and real size of hashset differ!")); return false; } AddDebugLogLineM( false, logSHAHashSet, wxString::Format(wxT("Sucessfully saved eMuleAC Hashset, %u Hashs + 1 Masterhash written"), nHashCount)); } catch (const CSafeIOException& e) { AddDebugLogLineM(true, logSHAHashSet, wxT("IO error while saving AICH HashSet: ") + e.what()); return false; } FreeHashSet(); return true; }
CAICHHashSet::~CAICHHashSet(void) { FreeHashSet(); }