Improve the code by static code analysis [2/3]: Performance
[amule.git] / src / SHAHashSet.cpp
bloba98c3fa19caa3c575c30e17e6857a4855ffeb0fd
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include <wx/file.h>
29 #include "SHAHashSet.h"
30 #include "amule.h"
31 #include "MemFile.h"
32 #include "Preferences.h"
33 #include "SHA.h"
34 #include "updownclient.h"
35 #include "DownloadQueue.h"
36 #include "PartFile.h"
37 #include "Logger.h"
38 #include <common/Format.h>
42 // for this version the limits are set very high, they might be lowered later
43 // to make a hash trustworthy, at least 10 unique Ips (255.255.128.0) must have sent it
44 // and if we have received more than one hash for the file, one hash has to be sent by more than 95% of all unique IPs
45 #define MINUNIQUEIPS_TOTRUST 10 // how many unique IPs have to send us a hash to make it trustworthy
46 #define MINPERCENTAGE_TOTRUST 92 // how many percentage of clients have to send the same hash to make it trustworthy
48 CAICHRequestedDataList CAICHHashSet::m_liRequestedData;
50 /////////////////////////////////////////////////////////////////////////////////////////
51 ///CAICHHash
52 wxString CAICHHash::GetString() const
54 return EncodeBase32(m_abyBuffer, HASHSIZE);
58 void CAICHHash::Read(CFileDataIO* file)
60 file->Read(m_abyBuffer,HASHSIZE);
64 void CAICHHash::Write(CFileDataIO* file) const
66 file->Write(m_abyBuffer,HASHSIZE);
69 unsigned int CAICHHash::DecodeBase32(const wxString &base32)
71 return ::DecodeBase32(base32, HASHSIZE, m_abyBuffer);
75 /////////////////////////////////////////////////////////////////////////////////////////
76 ///CAICHHashTree
78 CAICHHashTree::CAICHHashTree(uint64 nDataSize, bool bLeftBranch, uint64 nBaseSize)
80 m_nDataSize = nDataSize;
81 m_nBaseSize = nBaseSize;
82 m_bIsLeftBranch = bLeftBranch;
83 m_pLeftTree = NULL;
84 m_pRightTree = NULL;
85 m_bHashValid = false;
89 CAICHHashTree::~CAICHHashTree()
91 delete m_pLeftTree;
92 delete m_pRightTree;
96 // recursive
97 CAICHHashTree* CAICHHashTree::FindHash(uint64 nStartPos, uint64 nSize, uint8* nLevel)
99 (*nLevel)++;
101 wxCHECK(*nLevel <= 22, NULL);
102 wxCHECK(nStartPos + nSize <= m_nDataSize, NULL);
103 wxCHECK(nSize <= m_nDataSize, NULL);
105 if (nStartPos == 0 && nSize == m_nDataSize) {
106 // this is the searched hash
107 return this;
108 } else if (m_nDataSize <= m_nBaseSize) { // sanity
109 // this is already the last level, cant go deeper
110 wxFAIL;
111 return NULL;
112 } else {
113 uint64 nBlocks = m_nDataSize / m_nBaseSize + ((m_nDataSize % m_nBaseSize != 0 )? 1:0);
114 uint64 nLeft = (((m_bIsLeftBranch) ? nBlocks+1:nBlocks) / 2)* m_nBaseSize;
115 uint64 nRight = m_nDataSize - nLeft;
116 if (nStartPos < nLeft) {
117 if (nStartPos + nSize > nLeft) { // sanity
118 wxFAIL;
119 return NULL;
122 if (m_pLeftTree == NULL) {
123 m_pLeftTree = new CAICHHashTree(nLeft, true, (nLeft <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
124 } else {
125 wxASSERT( m_pLeftTree->m_nDataSize == nLeft );
128 return m_pLeftTree->FindHash(nStartPos, nSize, nLevel);
129 } else {
130 nStartPos -= nLeft;
131 if (nStartPos + nSize > nRight) { // sanity
132 wxFAIL;
133 return NULL;
136 if (m_pRightTree == NULL) {
137 m_pRightTree = new CAICHHashTree(nRight, false, (nRight <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
138 } else {
139 wxASSERT( m_pRightTree->m_nDataSize == nRight );
142 return m_pRightTree->FindHash(nStartPos, nSize, nLevel);
148 // recursive
149 // calculates missing hash from the existing ones
150 // overwrites existing hashs
151 // fails if no hash is found for any branch
152 bool CAICHHashTree::ReCalculateHash(CAICHHashAlgo* hashalg, bool bDontReplace)
154 if (m_pLeftTree && m_pRightTree) {
155 if ( !m_pLeftTree->ReCalculateHash(hashalg, bDontReplace) || !m_pRightTree->ReCalculateHash(hashalg, bDontReplace) ) {
156 return false;
158 if (bDontReplace && m_bHashValid) {
159 return true;
161 if (m_pRightTree->m_bHashValid && m_pLeftTree->m_bHashValid) {
162 hashalg->Reset();
163 hashalg->Add(m_pLeftTree->m_Hash.GetRawHash(), HASHSIZE);
164 hashalg->Add(m_pRightTree->m_Hash.GetRawHash(), HASHSIZE);
165 hashalg->Finish(m_Hash);
166 m_bHashValid = true;
167 return true;
168 } else {
169 return m_bHashValid;
171 } else if (m_pLeftTree == NULL && m_pRightTree == NULL) {
172 return true;
173 } else {
174 AddDebugLogLineN(logSHAHashSet, wxT("ReCalculateHash failed - Hashtree incomplete"));
175 return false;
179 bool CAICHHashTree::VerifyHashTree(CAICHHashAlgo* hashalg, bool bDeleteBadTrees)
181 if (!m_bHashValid) {
182 wxFAIL;
183 if (bDeleteBadTrees) {
184 if (m_pLeftTree) {
185 delete m_pLeftTree;
186 m_pLeftTree = NULL;
188 if (m_pRightTree) {
189 delete m_pRightTree;
190 m_pRightTree = NULL;
193 AddDebugLogLineN(logSHAHashSet, wxT("VerifyHashTree - No masterhash available"));
194 return false;
197 // calculated missing hashs without overwriting anything
198 if (m_pLeftTree && !m_pLeftTree->m_bHashValid) {
199 m_pLeftTree->ReCalculateHash(hashalg, true);
201 if (m_pRightTree && !m_pRightTree->m_bHashValid) {
202 m_pRightTree->ReCalculateHash(hashalg, true);
205 if ((m_pRightTree && m_pRightTree->m_bHashValid) ^ (m_pLeftTree && m_pLeftTree->m_bHashValid)) {
206 // one branch can never be verified
207 if (bDeleteBadTrees) {
208 if (m_pLeftTree) {
209 delete m_pLeftTree;
210 m_pLeftTree = NULL;
212 if (m_pRightTree) {
213 delete m_pRightTree;
214 m_pRightTree = NULL;
217 AddDebugLogLineN(logSHAHashSet, wxT("VerifyHashSet failed - Hashtree incomplete"));
218 return false;
220 if ((m_pRightTree && m_pRightTree->m_bHashValid) && (m_pLeftTree && m_pLeftTree->m_bHashValid)) {
221 // check verify the hashs of both child nodes against my hash
223 CAICHHash CmpHash;
224 hashalg->Reset();
225 hashalg->Add(m_pLeftTree->m_Hash.GetRawHash(), HASHSIZE);
226 hashalg->Add(m_pRightTree->m_Hash.GetRawHash(), HASHSIZE);
227 hashalg->Finish(CmpHash);
229 if (m_Hash != CmpHash) {
230 if (bDeleteBadTrees) {
231 if (m_pLeftTree) {
232 delete m_pLeftTree;
233 m_pLeftTree = NULL;
235 if (m_pRightTree) {
236 delete m_pRightTree;
237 m_pRightTree = NULL;
240 return false;
242 return m_pLeftTree->VerifyHashTree(hashalg, bDeleteBadTrees) && m_pRightTree->VerifyHashTree(hashalg, bDeleteBadTrees);
243 } else {
244 // last hash in branch - nothing below to verify
245 return true;
250 void CAICHHashTree::SetBlockHash(uint64 nSize, uint64 nStartPos, CAICHHashAlgo* pHashAlg)
252 wxASSERT ( nSize <= EMBLOCKSIZE );
253 CAICHHashTree* pToInsert = FindHash(nStartPos, nSize);
254 if (pToInsert == NULL) { // sanity
255 wxFAIL;
256 AddDebugLogLineN(logSHAHashSet, wxT("Critical Error: Failed to Insert SHA-HashBlock, FindHash() failed!"));
257 return;
260 //sanity
261 if (pToInsert->m_nBaseSize != EMBLOCKSIZE || pToInsert->m_nDataSize != nSize) {
262 wxFAIL;
263 AddDebugLogLineN(logSHAHashSet, wxT("Critical Error: Logical error on values in SetBlockHashFromData"));
264 return;
267 pHashAlg->Finish(pToInsert->m_Hash);
268 pToInsert->m_bHashValid = true;
272 bool CAICHHashTree::CreatePartRecoveryData(uint64 nStartPos, uint64 nSize, CFileDataIO* fileDataOut, uint32 wHashIdent, bool b32BitIdent)
274 wxCHECK(nStartPos + nSize <= m_nDataSize, false);
275 wxCHECK(nSize <= m_nDataSize, false);
277 if (nStartPos == 0 && nSize == m_nDataSize) {
278 // this is the searched part, now write all blocks of this part
279 // hashident for this level will be adjsuted by WriteLowestLevelHash
280 return WriteLowestLevelHashs(fileDataOut, wHashIdent, false, b32BitIdent);
281 } else if (m_nDataSize <= m_nBaseSize) { // sanity
282 // this is already the last level, cant go deeper
283 wxFAIL;
284 return false;
285 } else {
286 wHashIdent <<= 1;
287 wHashIdent |= (m_bIsLeftBranch) ? 1: 0;
289 uint64 nBlocks = m_nDataSize / m_nBaseSize + ((m_nDataSize % m_nBaseSize != 0 )? 1:0);
290 uint64 nLeft = ( ((m_bIsLeftBranch) ? nBlocks+1:nBlocks) / 2)* m_nBaseSize;
291 uint64 nRight = m_nDataSize - nLeft;
292 if (m_pLeftTree == NULL || m_pRightTree == NULL) {
293 wxFAIL;
294 return false;
296 if (nStartPos < nLeft) {
297 if (nStartPos + nSize > nLeft || !m_pRightTree->m_bHashValid) { // sanity
298 wxFAIL;
299 return false;
301 m_pRightTree->WriteHash(fileDataOut, wHashIdent, b32BitIdent);
302 return m_pLeftTree->CreatePartRecoveryData(nStartPos, nSize, fileDataOut, wHashIdent, b32BitIdent);
303 } else {
304 nStartPos -= nLeft;
305 if (nStartPos + nSize > nRight || !m_pLeftTree->m_bHashValid) { // sanity
306 wxFAIL;
307 return false;
309 m_pLeftTree->WriteHash(fileDataOut, wHashIdent, b32BitIdent);
310 return m_pRightTree->CreatePartRecoveryData(nStartPos, nSize, fileDataOut, wHashIdent, b32BitIdent);
317 void CAICHHashTree::WriteHash(CFileDataIO* fileDataOut, uint32 wHashIdent, bool b32BitIdent) const
319 wxASSERT( m_bHashValid );
320 wHashIdent <<= 1;
321 wHashIdent |= (m_bIsLeftBranch) ? 1: 0;
323 if (!b32BitIdent) {
324 wxASSERT( wHashIdent <= 0xFFFF );
325 fileDataOut->WriteUInt16((uint16)wHashIdent);
326 } else {
327 fileDataOut->WriteUInt32(wHashIdent);
330 m_Hash.Write(fileDataOut);
334 // write lowest level hashs into file, ordered from left to right optional without identifier
335 bool CAICHHashTree::WriteLowestLevelHashs(CFileDataIO* fileDataOut, uint32 wHashIdent, bool bNoIdent, bool b32BitIdent) const
337 wHashIdent <<= 1;
338 wHashIdent |= (m_bIsLeftBranch) ? 1: 0;
339 if (m_pLeftTree == NULL && m_pRightTree == NULL) {
340 if (m_nDataSize <= m_nBaseSize && m_bHashValid ) {
341 if (!bNoIdent && !b32BitIdent) {
342 wxASSERT( wHashIdent <= 0xFFFF );
343 fileDataOut->WriteUInt16((uint16)wHashIdent);
344 } else if (!bNoIdent && b32BitIdent) {
345 fileDataOut->WriteUInt32(wHashIdent);
348 m_Hash.Write(fileDataOut);
349 return true;
350 } else {
351 wxFAIL;
352 return false;
354 } else if (m_pLeftTree == NULL || m_pRightTree == NULL) {
355 wxFAIL;
356 return false;
357 } else {
358 return m_pLeftTree->WriteLowestLevelHashs(fileDataOut, wHashIdent, bNoIdent, b32BitIdent)
359 && m_pRightTree->WriteLowestLevelHashs(fileDataOut, wHashIdent, bNoIdent, b32BitIdent);
363 // recover all low level hashs from given data. hashs are assumed to be ordered in left to right - no identifier used
364 bool CAICHHashTree::LoadLowestLevelHashs(CFileDataIO* fileInput)
366 if (m_nDataSize <= m_nBaseSize) { // sanity
367 // lowest level, read hash
368 m_Hash.Read(fileInput);
369 m_bHashValid = true;
370 return true;
371 } else {
372 uint64 nBlocks = m_nDataSize / m_nBaseSize + ((m_nDataSize % m_nBaseSize != 0 )? 1:0);
373 uint64 nLeft = ( ((m_bIsLeftBranch) ? nBlocks+1:nBlocks) / 2)* m_nBaseSize;
374 uint64 nRight = m_nDataSize - nLeft;
375 if (m_pLeftTree == NULL) {
376 m_pLeftTree = new CAICHHashTree(nLeft, true, (nLeft <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
377 } else {
378 wxASSERT( m_pLeftTree->m_nDataSize == nLeft );
380 if (m_pRightTree == NULL) {
381 m_pRightTree = new CAICHHashTree(nRight, false, (nRight <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
382 } else {
383 wxASSERT( m_pRightTree->m_nDataSize == nRight );
385 return m_pLeftTree->LoadLowestLevelHashs(fileInput)
386 && m_pRightTree->LoadLowestLevelHashs(fileInput);
391 // write the hash, specified by wHashIdent, with Data from fileInput.
392 bool CAICHHashTree::SetHash(CFileDataIO* fileInput, uint32 wHashIdent, sint8 nLevel, bool bAllowOverwrite)
394 if (nLevel == (-1)) {
395 // first call, check how many level we need to go
396 uint8 i = 0;
397 for (; i != 32 && (wHashIdent & 0x80000000) == 0; ++i) {
398 wHashIdent <<= 1;
400 if (i > 31) {
401 AddDebugLogLineN(logSHAHashSet, wxT("CAICHHashTree::SetHash - found invalid HashIdent (0)"));
402 return false;
403 } else {
404 nLevel = 31 - i;
407 if (nLevel == 0) {
408 // this is the searched hash
409 if (m_bHashValid && !bAllowOverwrite) {
410 // not allowed to overwrite this hash, however move the filepointer as if we read a hash
411 fileInput->Seek(HASHSIZE, wxFromCurrent);
412 return true;
414 m_Hash.Read(fileInput);
415 m_bHashValid = true;
416 return true;
417 } else if (m_nDataSize <= m_nBaseSize) { // sanity
418 // this is already the last level, cant go deeper
419 wxFAIL;
420 return false;
421 } else {
422 // adjust ident to point the path to the next node
423 wHashIdent <<= 1;
424 nLevel--;
425 uint64 nBlocks = m_nDataSize / m_nBaseSize + ((m_nDataSize % m_nBaseSize != 0 )? 1:0);
426 uint64 nLeft = ( ((m_bIsLeftBranch) ? nBlocks+1:nBlocks) / 2)* m_nBaseSize;
427 uint64 nRight = m_nDataSize - nLeft;
428 if ((wHashIdent & 0x80000000) > 0) {
429 if (m_pLeftTree == NULL) {
430 m_pLeftTree = new CAICHHashTree(nLeft, true, (nLeft <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
431 } else {
432 wxASSERT( m_pLeftTree->m_nDataSize == nLeft );
434 return m_pLeftTree->SetHash(fileInput, wHashIdent, nLevel);
435 } else {
436 if (m_pRightTree == NULL) {
437 m_pRightTree = new CAICHHashTree(nRight, false, (nRight <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE);
438 } else {
439 wxASSERT( m_pRightTree->m_nDataSize == nRight );
441 return m_pRightTree->SetHash(fileInput, wHashIdent, nLevel);
447 /////////////////////////////////////////////////////////////////////////////////////////
448 ///CAICHUntrustedHash
449 bool CAICHUntrustedHash::AddSigningIP(uint32 dwIP)
451 dwIP &= 0x00F0FFFF; // we use only the 20 most significant bytes for unique IPs
452 return m_adwIpsSigning.insert(dwIP).second;
457 /////////////////////////////////////////////////////////////////////////////////////////
458 ///CAICHHashSet
459 CAICHHashSet::CAICHHashSet(CKnownFile* pOwner)
460 : m_pHashTree(0, true, PARTSIZE)
462 m_eStatus = AICH_EMPTY;
463 m_pOwner = pOwner;
466 CAICHHashSet::~CAICHHashSet(void)
468 FreeHashSet();
471 bool CAICHHashSet::CreatePartRecoveryData(uint64 nPartStartPos, CFileDataIO* fileDataOut, bool bDbgDontLoad)
473 wxASSERT( m_pOwner );
474 if (m_pOwner->IsPartFile() || m_eStatus != AICH_HASHSETCOMPLETE) {
475 wxFAIL;
476 return false;
478 if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE) {
479 wxFAIL;
480 return false;
482 if (!bDbgDontLoad) {
483 if (!LoadHashSet()) {
484 AddDebugLogLineN(logSHAHashSet,
485 CFormat(wxT("Created RecoveryData error: failed to load hashset. File: %s")) % m_pOwner->GetFileName());
486 SetStatus(AICH_ERROR);
487 return false;
490 bool bResult;
491 uint8 nLevel = 0;
492 uint32 nPartSize = min<uint64>(PARTSIZE, m_pOwner->GetFileSize()-nPartStartPos);
493 m_pHashTree.FindHash(nPartStartPos, nPartSize,&nLevel);
494 uint16 nHashsToWrite = (nLevel-1) + nPartSize/EMBLOCKSIZE + ((nPartSize % EMBLOCKSIZE != 0 )? 1:0);
495 const bool bUse32BitIdentifier = m_pOwner->IsLargeFile();
497 if (bUse32BitIdentifier) {
498 fileDataOut->WriteUInt16(0); // no 16bit hashs to write
501 fileDataOut->WriteUInt16(nHashsToWrite);
502 uint64 nCheckFilePos = fileDataOut->GetPosition();
503 if (m_pHashTree.CreatePartRecoveryData(nPartStartPos, nPartSize, fileDataOut, 0, bUse32BitIdentifier)) {
504 if (nHashsToWrite*(HASHSIZE+(bUse32BitIdentifier? 4u:2u)) != fileDataOut->GetPosition() - nCheckFilePos) {
505 wxFAIL;
506 AddDebugLogLineN( logSHAHashSet,
507 CFormat(wxT("Created RecoveryData has wrong length. File: %s")) % m_pOwner->GetFileName() );
508 bResult = false;
509 SetStatus(AICH_ERROR);
510 } else {
511 bResult = true;
513 } else {
514 AddDebugLogLineN(logSHAHashSet,
515 CFormat(wxT("Failed to create RecoveryData for '%s'")) % m_pOwner->GetFileName());
516 bResult = false;
517 SetStatus(AICH_ERROR);
519 if (!bUse32BitIdentifier) {
520 fileDataOut->WriteUInt16(0); // no 32bit hashs to write
523 if (!bDbgDontLoad) {
524 FreeHashSet();
526 return bResult;
529 bool CAICHHashSet::ReadRecoveryData(uint64 nPartStartPos, CMemFile* fileDataIn)
531 if (/*eMule TODO !m_pOwner->IsPartFile() ||*/ !(m_eStatus == AICH_VERIFIED || m_eStatus == AICH_TRUSTED) ) {
532 wxFAIL;
533 return false;
536 /* V2 AICH Hash Packet:
537 <count1 uint16> 16bit-hashs-to-read
538 (<identifier uint16><hash HASHSIZE>)[count1] AICH hashs
539 <count2 uint16> 32bit-hashs-to-read
540 (<identifier uint32><hash HASHSIZE>)[count2] AICH hashs
543 // at this time we check the recoverydata for the correct ammounts of hashs only
544 // all hash are then taken into the tree, depending on there hashidentifier (except the masterhash)
546 uint8 nLevel = 0;
547 uint32 nPartSize = min<uint64>(PARTSIZE, m_pOwner->GetFileSize()-nPartStartPos);
548 m_pHashTree.FindHash(nPartStartPos, nPartSize,&nLevel);
549 uint16 nHashsToRead = (nLevel-1) + nPartSize/EMBLOCKSIZE + ((nPartSize % EMBLOCKSIZE != 0 )? 1:0);
551 // read hashs with 16 bit identifier
552 uint16 nHashsAvailable = fileDataIn->ReadUInt16();
553 if (fileDataIn->GetLength()-fileDataIn->GetPosition() < nHashsToRead*(HASHSIZE+2u) || (nHashsToRead != nHashsAvailable && nHashsAvailable != 0)) {
554 // this check is redunant, CSafememfile would catch such an error too
555 AddDebugLogLineN(logSHAHashSet,
556 CFormat(wxT("Failed to read RecoveryData for '%s' - Received datasize/amounts of hashs was invalid"))
557 % m_pOwner->GetFileName());
558 return false;
560 for (uint32 i = 0; i != nHashsAvailable; i++) {
561 uint16 wHashIdent = fileDataIn->ReadUInt16();
562 if (wHashIdent == 1 /*never allow masterhash to be overwritten*/
563 || !m_pHashTree.SetHash(fileDataIn, wHashIdent,(-1), false))
565 AddDebugLogLineN(logSHAHashSet,
566 CFormat(wxT("Failed to read RecoveryData for '%s' - Error when trying to read hash into tree"))
567 % m_pOwner->GetFileName());
568 VerifyHashTree(true); // remove invalid hashes which we have already written
569 return false;
574 // read hashs with 32bit identifier
575 if (nHashsAvailable == 0 && fileDataIn->GetLength() - fileDataIn->GetPosition() >= 2) {
576 nHashsAvailable = fileDataIn->ReadUInt16();
577 if (fileDataIn->GetLength()-fileDataIn->GetPosition() < nHashsToRead*(HASHSIZE+4u) || (nHashsToRead != nHashsAvailable && nHashsAvailable != 0)) {
578 // this check is redunant, CSafememfile would catch such an error too
579 // TODO: theApp->QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Failed to read RecoveryData for %s - Received datasize/amounts of hashs was invalid (2)"), m_pOwner->GetFileName() );
580 return false;
583 // TODO: DEBUG_ONLY( theApp->QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("read RecoveryData for %s - Received packet with %u 32bit hash identifiers)"), m_pOwner->GetFileName(), nHashsAvailable ) );
584 for (uint32 i = 0; i != nHashsToRead; i++) {
585 uint32 wHashIdent = fileDataIn->ReadUInt32();
586 if (wHashIdent == 1 /*never allow masterhash to be overwritten*/
587 || wHashIdent > 0x400000
588 || !m_pHashTree.SetHash(fileDataIn, wHashIdent,(-1), false))
590 // TODO: theApp->QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Failed to read RecoveryData for %s - Error when trying to read hash into tree (2)"), m_pOwner->GetFileName() );
591 VerifyHashTree(true); // remove invalid hashes which we have already written
592 return false;
597 if (nHashsAvailable == 0) {
598 // TODO: theApp->QueueDebugLogLine(/*DLP_VERYHIGH,*/ false, _T("Failed to read RecoveryData for %s - Packet didn't contained any hashs"), m_pOwner->GetFileName() );
599 return false;
603 if (VerifyHashTree(true)) {
604 // some final check if all hashs we wanted are there
605 for (uint32 nPartPos = 0; nPartPos < nPartSize; nPartPos += EMBLOCKSIZE) {
606 CAICHHashTree* phtToCheck = m_pHashTree.FindHash(nPartStartPos+nPartPos, min<uint64>(EMBLOCKSIZE, nPartSize-nPartPos));
607 if (phtToCheck == NULL || !phtToCheck->m_bHashValid) {
608 AddDebugLogLineN(logSHAHashSet,
609 CFormat(wxT("Failed to read RecoveryData for '%s' - Error while verifying presence of all lowest level hashes"))
610 % m_pOwner->GetFileName());
611 return false;
614 // all done
615 return true;
616 } else {
617 AddDebugLogLineN(logSHAHashSet,
618 CFormat(wxT("Failed to read RecoveryData for '%s' - Verifying received hashtree failed"))
619 % m_pOwner->GetFileName());
620 return false;
624 // this function is only allowed to be called right after successfully calculating the hashset (!)
625 // will delete the hashset, after saving to free the memory
626 bool CAICHHashSet::SaveHashSet()
628 if (m_eStatus != AICH_HASHSETCOMPLETE) {
629 wxFAIL;
630 return false;
632 if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize()) {
633 wxFAIL;
634 return false;
638 try {
639 const wxString fullpath = theApp->ConfigDir + KNOWN2_MET_FILENAME;
640 const bool exists = wxFile::Exists(fullpath);
642 CFile file(fullpath, exists ? CFile::read_write : CFile::write);
643 if (!file.IsOpened()) {
644 AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: opening met file failed!"));
645 return false;
648 uint64 nExistingSize = file.GetLength();
649 if (nExistingSize) {
650 uint8 header = file.ReadUInt8();
651 if (header != KNOWN2_MET_VERSION) {
652 AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: Current file is not a met-file!"));
653 return false;
656 AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Met file is version 0x%2.2x.")) % header);
657 } else {
658 file.WriteUInt8(KNOWN2_MET_VERSION);
659 // Update the recorded size, in order for the sanity check below to work.
660 nExistingSize += 1;
663 // first we check if the hashset we want to write is already stored
664 CAICHHash CurrentHash;
665 while (file.GetPosition() < nExistingSize) {
666 CurrentHash.Read(&file);
667 if (m_pHashTree.m_Hash == CurrentHash) {
668 // this hashset if already available, no need to save it again
669 return true;
671 uint32 nHashCount = file.ReadUInt32();
672 if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize) {
673 AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: File contains fewer entries than specified!"));
674 return false;
676 // skip the rest of this hashset
677 file.Seek(nHashCount*HASHSIZE, wxFromCurrent);
680 // write hashset
681 m_pHashTree.m_Hash.Write(&file);
682 uint32 nHashCount = (PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE);
683 if (m_pHashTree.m_nDataSize % PARTSIZE != 0) {
684 nHashCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0);
686 file.WriteUInt32(nHashCount);
687 if (!m_pHashTree.WriteLowestLevelHashs(&file, 0, true, true)) {
688 // thats bad... really
689 file.SetLength(nExistingSize);
690 AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: WriteLowestLevelHashs() failed!"));
691 return false;
693 if (file.GetLength() != nExistingSize + (nHashCount+1)*HASHSIZE + 4) {
694 // thats even worse
695 file.SetLength(nExistingSize);
696 AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: Calculated and real size of hashset differ!"));
697 return false;
699 AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Successfully saved eMuleAC Hashset, %u Hashs + 1 Masterhash written")) % nHashCount);
700 } catch (const CSafeIOException& e) {
701 AddDebugLogLineC(logSHAHashSet, wxT("IO error while saving AICH HashSet: ") + e.what());
702 return false;
705 FreeHashSet();
706 return true;
710 bool CAICHHashSet::LoadHashSet()
712 if (m_eStatus != AICH_HASHSETCOMPLETE) {
713 wxFAIL;
714 return false;
716 if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize() || m_pHashTree.m_nDataSize == 0) {
717 wxFAIL;
718 return false;
720 wxString fullpath = theApp->ConfigDir + KNOWN2_MET_FILENAME;
721 CFile file(fullpath, CFile::read);
722 if (!file.IsOpened()) {
723 if (wxFileExists(fullpath)) {
724 wxString strError(wxT("Failed to load ") KNOWN2_MET_FILENAME wxT(" file"));
725 AddDebugLogLineC(logSHAHashSet, strError);
727 return false;
730 try {
731 uint8 header = file.ReadUInt8();
732 if (header != KNOWN2_MET_VERSION) {
733 AddDebugLogLineC(logSHAHashSet, wxT("Loading failed: Current file is not a met-file!"));
734 return false;
737 CAICHHash CurrentHash;
738 uint64 nExistingSize = file.GetLength();
739 uint32 nHashCount;
740 while (file.GetPosition() < nExistingSize) {
741 CurrentHash.Read(&file);
742 if (m_pHashTree.m_Hash == CurrentHash) {
743 // found Hashset
744 uint32 nExpectedCount = (PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE);
745 if (m_pHashTree.m_nDataSize % PARTSIZE != 0) {
746 nExpectedCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0);
748 nHashCount = file.ReadUInt32();
749 if (nHashCount != nExpectedCount) {
750 AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Available Hashs and expected hashcount differ!"));
751 return false;
753 if (!m_pHashTree.LoadLowestLevelHashs(&file)) {
754 AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: LoadLowestLevelHashs failed!"));
755 return false;
757 if (!ReCalculateHash(false)) {
758 AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Calculating loaded hashs failed!"));
759 return false;
761 if (CurrentHash != m_pHashTree.m_Hash) {
762 AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Calculated Masterhash differs from given Masterhash - hashset corrupt!"));
763 return false;
765 return true;
767 nHashCount = file.ReadUInt32();
768 if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize) {
769 AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: File contains fewer entries than specified!"));
770 return false;
772 // skip the rest of this hashset
773 file.Seek(nHashCount*HASHSIZE, wxFromCurrent);
775 AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: HashSet not found!"));
776 } catch (const CSafeIOException& e) {
777 AddDebugLogLineC(logSHAHashSet, wxT("IO error while loading AICH HashSet: ") + e.what());
780 return false;
783 // delete the hashset except the masterhash (we dont keep aich hashsets in memory to save ressources)
784 void CAICHHashSet::FreeHashSet()
786 if (m_pHashTree.m_pLeftTree) {
787 delete m_pHashTree.m_pLeftTree;
788 m_pHashTree.m_pLeftTree = NULL;
790 if (m_pHashTree.m_pRightTree) {
791 delete m_pHashTree.m_pRightTree;
792 m_pHashTree.m_pRightTree = NULL;
796 void CAICHHashSet::SetMasterHash(const CAICHHash& Hash, EAICHStatus eNewStatus)
798 m_pHashTree.m_Hash = Hash;
799 m_pHashTree.m_bHashValid = true;
800 SetStatus(eNewStatus);
803 CAICHHashAlgo* CAICHHashSet::GetNewHashAlgo()
805 return new CSHA();
808 bool CAICHHashSet::ReCalculateHash(bool bDontReplace)
810 CAICHHashAlgo* hashalg = GetNewHashAlgo();
811 bool bResult = m_pHashTree.ReCalculateHash(hashalg, bDontReplace);
812 delete hashalg;
813 return bResult;
816 bool CAICHHashSet::VerifyHashTree(bool bDeleteBadTrees)
818 CAICHHashAlgo* hashalg = GetNewHashAlgo();
819 bool bResult = m_pHashTree.VerifyHashTree(hashalg, bDeleteBadTrees);
820 delete hashalg;
821 return bResult;
825 void CAICHHashSet::SetFileSize(uint64 nSize)
827 m_pHashTree.m_nDataSize = nSize;
828 m_pHashTree.m_nBaseSize = (nSize <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE;
832 void CAICHHashSet::UntrustedHashReceived(const CAICHHash& Hash, uint32 dwFromIP)
834 switch(GetStatus()) {
835 case AICH_EMPTY:
836 case AICH_UNTRUSTED:
837 case AICH_TRUSTED:
838 break;
839 default:
840 return;
842 bool bFound = false;
843 bool bAdded = false;
844 for (uint32 i = 0; i < m_aUntrustedHashs.size(); ++i) {
845 if (m_aUntrustedHashs[i].m_Hash == Hash) {
846 bAdded = m_aUntrustedHashs[i].AddSigningIP(dwFromIP);
847 bFound = true;
848 break;
851 if (!bFound) {
852 bAdded = true;
853 CAICHUntrustedHash uhToAdd;
854 uhToAdd.m_Hash = Hash;
855 uhToAdd.AddSigningIP(dwFromIP);
856 m_aUntrustedHashs.push_back(uhToAdd);
859 uint32 nSigningIPsTotal = 0; // unique clients who send us a hash
860 int nMostTrustedPos = (-1); // the hash which most clients send us
861 uint32 nMostTrustedIPs = 0;
862 for (uint32 i = 0; i < (uint32)m_aUntrustedHashs.size(); ++i) {
863 nSigningIPsTotal += m_aUntrustedHashs[i].m_adwIpsSigning.size();
864 if ((uint32)m_aUntrustedHashs[i].m_adwIpsSigning.size() > nMostTrustedIPs) {
865 nMostTrustedIPs = m_aUntrustedHashs[i].m_adwIpsSigning.size();
866 nMostTrustedPos = i;
869 if (nMostTrustedPos == (-1) || nSigningIPsTotal == 0) {
870 wxFAIL;
871 return;
873 // the check if we trust any hash
874 if ( thePrefs::IsTrustingEveryHash() ||
875 (nMostTrustedIPs >= MINUNIQUEIPS_TOTRUST && (100 * nMostTrustedIPs)/nSigningIPsTotal >= MINPERCENTAGE_TOTRUST)) {
876 //trusted
877 AddDebugLogLineN(logSHAHashSet,
878 CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ")
879 wxT("We trust the Hash %s from %u client(s) (%u%%). File: %s"))
880 % (bAdded ? wxT("") : wxT("not "))
881 % m_aUntrustedHashs.size()
882 % nSigningIPsTotal
883 % m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString()
884 % nMostTrustedIPs
885 % ((100 * nMostTrustedIPs) / nSigningIPsTotal)
886 % m_pOwner->GetFileName());
888 SetStatus(AICH_TRUSTED);
889 if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) {
890 SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_TRUSTED);
891 FreeHashSet();
893 } else {
894 // untrusted
895 AddDebugLogLineN(logSHAHashSet,
896 CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ")
897 wxT("Best Hash %s from %u clients (%u%%) - but we don't trust it yet. File: %s"))
898 % (bAdded ? wxT(""): wxT("not "))
899 % m_aUntrustedHashs.size()
900 % nSigningIPsTotal
901 % m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString()
902 % nMostTrustedIPs
903 % ((100 * nMostTrustedIPs) / nSigningIPsTotal)
904 % m_pOwner->GetFileName());
906 SetStatus(AICH_UNTRUSTED);
907 if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) {
908 SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_UNTRUSTED);
909 FreeHashSet();
912 if (bAdded) {} // get rid of unused variable warning
916 void CAICHHashSet::ClientAICHRequestFailed(CUpDownClient* pClient)
918 pClient->SetReqFileAICHHash(NULL);
919 CAICHRequestedData data = GetAICHReqDetails(pClient);
920 RemoveClientAICHRequest(pClient);
921 if (data.m_pClient.GetClient() != pClient) {
922 return;
924 if( theApp->downloadqueue->IsPartFile(data.m_pPartFile)) {
925 AddDebugLogLineN(logSHAHashSet,
926 CFormat(wxT("AICH Request failed, Trying to ask another client (File: '%s', Part: %u, Client '%s'"))
927 % data.m_pPartFile->GetFileName() % data.m_nPart % pClient->GetClientFullInfo());
928 data.m_pPartFile->RequestAICHRecovery(data.m_nPart);
933 void CAICHHashSet::RemoveClientAICHRequest(const CUpDownClient* pClient)
935 for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
936 if (it->m_pClient.GetClient() == pClient) {
937 m_liRequestedData.erase(it);
938 return;
941 wxFAIL;
944 bool CAICHHashSet::IsClientRequestPending(const CPartFile* pForFile, uint16 nPart)
946 for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
947 if (it->m_pPartFile == pForFile && it->m_nPart == nPart) {
948 return true;
951 return false;
954 CAICHRequestedData CAICHHashSet::GetAICHReqDetails(const CUpDownClient* pClient)
956 for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
957 if (it->m_pClient.GetClient() == pClient) {
958 return *(it);
961 wxFAIL;
962 CAICHRequestedData empty;
963 return empty;
966 bool CAICHHashSet::IsPartDataAvailable(uint64 nPartStartPos)
968 if (!(m_eStatus == AICH_VERIFIED || m_eStatus == AICH_TRUSTED || m_eStatus == AICH_HASHSETCOMPLETE) ) {
969 wxFAIL;
970 return false;
972 uint64 nPartSize = min<uint64>(PARTSIZE, m_pOwner->GetFileSize()-nPartStartPos);
973 for (uint64 nPartPos = 0; nPartPos < nPartSize; nPartPos += EMBLOCKSIZE) {
974 CAICHHashTree* phtToCheck = m_pHashTree.FindHash(nPartStartPos+nPartPos, min<uint64>(EMBLOCKSIZE, nPartSize-nPartPos));
975 if (phtToCheck == NULL || !phtToCheck->m_bHashValid) {
976 return false;
979 return true;
982 // VC++ defines Assert as ASSERT. VC++ also defines VERIFY MACRO, which is the equivalent of ASSERT but also works in Released builds.
984 #define VERIFY(x) wxASSERT(x)
986 void CAICHHashSet::DbgTest()
988 #ifdef _DEBUG
989 //define TESTSIZE 4294567295
990 uint8 maxLevel = 0;
991 uint32 cHash = 1;
992 uint8 curLevel = 0;
993 //uint32 cParts = 0;
994 maxLevel = 0;
995 /* CAICHHashTree* pTest = new CAICHHashTree(TESTSIZE, true, 9728000);
996 for (uint64 i = 0; i+9728000 < TESTSIZE; i += 9728000) {
997 CAICHHashTree* pTest2 = new CAICHHashTree(9728000, true, EMBLOCKSIZE);
998 pTest->ReplaceHashTree(i, 9728000, &pTest2);
999 cParts++;
1001 CAICHHashTree* pTest2 = new CAICHHashTree(TESTSIZE-i, true, EMBLOCKSIZE);
1002 pTest->ReplaceHashTree(i, (TESTSIZE-i), &pTest2);
1003 cParts++;
1005 #define TESTSIZE m_pHashTree.m_nDataSize
1006 if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE) {
1007 return;
1009 CAICHHashSet TestHashSet(m_pOwner);
1010 TestHashSet.SetFileSize(m_pOwner->GetFileSize());
1011 TestHashSet.SetMasterHash(GetMasterHash(), AICH_VERIFIED);
1012 CMemFile file;
1013 uint64 i;
1014 for (i = 0; i+9728000 < TESTSIZE; i += 9728000) {
1015 VERIFY( CreatePartRecoveryData(i, &file) );
1017 /*uint32 nRandomCorruption = (rand() * rand()) % (file.GetLength()-4);
1018 file.Seek(nRandomCorruption, CFile::begin);
1019 file.Write(&nRandomCorruption, 4);*/
1021 file.Seek(0,wxFromStart);
1022 VERIFY( TestHashSet.ReadRecoveryData(i, &file) );
1023 file.Seek(0,wxFromStart);
1024 TestHashSet.FreeHashSet();
1025 uint32 j;
1026 for (j = 0; j+EMBLOCKSIZE < 9728000; j += EMBLOCKSIZE) {
1027 VERIFY( m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel) );
1028 //TRACE(wxT("%u - %s\r\n"), cHash, m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel)->m_Hash.GetString());
1029 maxLevel = max(curLevel, maxLevel);
1030 curLevel = 0;
1031 cHash++;
1033 VERIFY( m_pHashTree.FindHash(i+j, 9728000-j, &curLevel) );
1034 //TRACE(wxT("%u - %s\r\n"), cHash, m_pHashTree.FindHash(i+j, 9728000-j, &curLevel)->m_Hash.GetString());
1035 maxLevel = max(curLevel, maxLevel);
1036 curLevel = 0;
1037 cHash++;
1040 VERIFY( CreatePartRecoveryData(i, &file) );
1041 file.Seek(0,wxFromStart);
1042 VERIFY( TestHashSet.ReadRecoveryData(i, &file) );
1043 file.Seek(0,wxFromStart);
1044 TestHashSet.FreeHashSet();
1045 for (uint64 j = 0; j+EMBLOCKSIZE < TESTSIZE-i; j += EMBLOCKSIZE) {
1046 VERIFY( m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel) );
1047 //TRACE(wxT("%u - %s\r\n"), cHash,m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel)->m_Hash.GetString());
1048 maxLevel = max(curLevel, maxLevel);
1049 curLevel = 0;
1050 cHash++;
1052 //VERIFY( m_pHashTree.FindHash(i+j, (TESTSIZE-i)-j, &curLevel) );
1053 //TRACE(wxT("%u - %s\r\n"), cHash,m_pHashTree.FindHash(i+j, (TESTSIZE-i)-j, &curLevel)->m_Hash.GetString());
1054 maxLevel = max(curLevel, maxLevel);
1055 #endif
1057 // File_checked_for_headers