2 // This file is part of the aMule Project.
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 )
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
29 #include "SHAHashSet.h"
32 #include "Preferences.h"
34 #include "updownclient.h"
35 #include "DownloadQueue.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 /////////////////////////////////////////////////////////////////////////////////////////
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 /////////////////////////////////////////////////////////////////////////////////////////
78 CAICHHashTree::CAICHHashTree(uint64 nDataSize
, bool bLeftBranch
, uint64 nBaseSize
)
80 m_nDataSize
= nDataSize
;
81 m_nBaseSize
= nBaseSize
;
82 m_bIsLeftBranch
= bLeftBranch
;
89 CAICHHashTree::~CAICHHashTree()
97 CAICHHashTree
* CAICHHashTree::FindHash(uint64 nStartPos
, uint64 nSize
, uint8
* 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
108 } else if (m_nDataSize
<= m_nBaseSize
) { // sanity
109 // this is already the last level, cant go deeper
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
122 if (m_pLeftTree
== NULL
) {
123 m_pLeftTree
= new CAICHHashTree(nLeft
, true, (nLeft
<= PARTSIZE
) ? EMBLOCKSIZE
: PARTSIZE
);
125 wxASSERT( m_pLeftTree
->m_nDataSize
== nLeft
);
128 return m_pLeftTree
->FindHash(nStartPos
, nSize
, nLevel
);
131 if (nStartPos
+ nSize
> nRight
) { // sanity
136 if (m_pRightTree
== NULL
) {
137 m_pRightTree
= new CAICHHashTree(nRight
, false, (nRight
<= PARTSIZE
) ? EMBLOCKSIZE
: PARTSIZE
);
139 wxASSERT( m_pRightTree
->m_nDataSize
== nRight
);
142 return m_pRightTree
->FindHash(nStartPos
, nSize
, nLevel
);
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
) ) {
158 if (bDontReplace
&& m_bHashValid
) {
161 if (m_pRightTree
->m_bHashValid
&& m_pLeftTree
->m_bHashValid
) {
163 hashalg
->Add(m_pLeftTree
->m_Hash
.GetRawHash(), HASHSIZE
);
164 hashalg
->Add(m_pRightTree
->m_Hash
.GetRawHash(), HASHSIZE
);
165 hashalg
->Finish(m_Hash
);
171 } else if (m_pLeftTree
== NULL
&& m_pRightTree
== NULL
) {
174 AddDebugLogLineN(logSHAHashSet
, wxT("ReCalculateHash failed - Hashtree incomplete"));
179 bool CAICHHashTree::VerifyHashTree(CAICHHashAlgo
* hashalg
, bool bDeleteBadTrees
)
183 if (bDeleteBadTrees
) {
193 AddDebugLogLineN(logSHAHashSet
, wxT("VerifyHashTree - No masterhash available"));
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
) {
217 AddDebugLogLineN(logSHAHashSet
, wxT("VerifyHashSet failed - Hashtree incomplete"));
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
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
) {
242 return m_pLeftTree
->VerifyHashTree(hashalg
, bDeleteBadTrees
) && m_pRightTree
->VerifyHashTree(hashalg
, bDeleteBadTrees
);
244 // last hash in branch - nothing below to verify
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
256 AddDebugLogLineN(logSHAHashSet
, wxT("Critical Error: Failed to Insert SHA-HashBlock, FindHash() failed!"));
261 if (pToInsert
->m_nBaseSize
!= EMBLOCKSIZE
|| pToInsert
->m_nDataSize
!= nSize
) {
263 AddDebugLogLineN(logSHAHashSet
, wxT("Critical Error: Logical error on values in SetBlockHashFromData"));
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
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
) {
296 if (nStartPos
< nLeft
) {
297 if (nStartPos
+ nSize
> nLeft
|| !m_pRightTree
->m_bHashValid
) { // sanity
301 m_pRightTree
->WriteHash(fileDataOut
, wHashIdent
, b32BitIdent
);
302 return m_pLeftTree
->CreatePartRecoveryData(nStartPos
, nSize
, fileDataOut
, wHashIdent
, b32BitIdent
);
305 if (nStartPos
+ nSize
> nRight
|| !m_pLeftTree
->m_bHashValid
) { // sanity
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
);
321 wHashIdent
|= (m_bIsLeftBranch
) ? 1: 0;
324 wxASSERT( wHashIdent
<= 0xFFFF );
325 fileDataOut
->WriteUInt16((uint16
)wHashIdent
);
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
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
);
354 } else if (m_pLeftTree
== NULL
|| m_pRightTree
== NULL
) {
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
);
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
);
378 wxASSERT( m_pLeftTree
->m_nDataSize
== nLeft
);
380 if (m_pRightTree
== NULL
) {
381 m_pRightTree
= new CAICHHashTree(nRight
, false, (nRight
<= PARTSIZE
) ? EMBLOCKSIZE
: PARTSIZE
);
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
397 for (; i
!= 32 && (wHashIdent
& 0x80000000) == 0; ++i
) {
401 AddDebugLogLineN(logSHAHashSet
, wxT("CAICHHashTree::SetHash - found invalid HashIdent (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
);
414 m_Hash
.Read(fileInput
);
417 } else if (m_nDataSize
<= m_nBaseSize
) { // sanity
418 // this is already the last level, cant go deeper
422 // adjust ident to point the path to the next node
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
);
432 wxASSERT( m_pLeftTree
->m_nDataSize
== nLeft
);
434 return m_pLeftTree
->SetHash(fileInput
, wHashIdent
, nLevel
);
436 if (m_pRightTree
== NULL
) {
437 m_pRightTree
= new CAICHHashTree(nRight
, false, (nRight
<= PARTSIZE
) ? EMBLOCKSIZE
: PARTSIZE
);
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 /////////////////////////////////////////////////////////////////////////////////////////
459 CAICHHashSet::CAICHHashSet(CKnownFile
* pOwner
)
460 : m_pHashTree(0, true, PARTSIZE
)
462 m_eStatus
= AICH_EMPTY
;
466 CAICHHashSet::~CAICHHashSet(void)
471 bool CAICHHashSet::CreatePartRecoveryData(uint64 nPartStartPos
, CFileDataIO
* fileDataOut
, bool bDbgDontLoad
)
473 wxASSERT( m_pOwner
);
474 if (m_pOwner
->IsPartFile() || m_eStatus
!= AICH_HASHSETCOMPLETE
) {
478 if (m_pHashTree
.m_nDataSize
<= EMBLOCKSIZE
) {
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
);
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
) {
506 AddDebugLogLineN( logSHAHashSet
,
507 CFormat(wxT("Created RecoveryData has wrong length. File: %s")) % m_pOwner
->GetFileName() );
509 SetStatus(AICH_ERROR
);
514 AddDebugLogLineN(logSHAHashSet
,
515 CFormat(wxT("Failed to create RecoveryData for '%s'")) % m_pOwner
->GetFileName());
517 SetStatus(AICH_ERROR
);
519 if (!bUse32BitIdentifier
) {
520 fileDataOut
->WriteUInt16(0); // no 32bit hashs to write
529 bool CAICHHashSet::ReadRecoveryData(uint64 nPartStartPos
, CMemFile
* fileDataIn
)
531 if (/*eMule TODO !m_pOwner->IsPartFile() ||*/ !(m_eStatus
== AICH_VERIFIED
|| m_eStatus
== AICH_TRUSTED
) ) {
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)
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());
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
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() );
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
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() );
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());
617 AddDebugLogLineN(logSHAHashSet
,
618 CFormat(wxT("Failed to read RecoveryData for '%s' - Verifying received hashtree failed"))
619 % m_pOwner
->GetFileName());
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
) {
632 if ( !m_pHashTree
.m_bHashValid
|| m_pHashTree
.m_nDataSize
!= m_pOwner
->GetFileSize()) {
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!"));
648 uint64 nExistingSize
= file
.GetLength();
650 uint8 header
= file
.ReadUInt8();
651 if (header
!= KNOWN2_MET_VERSION
) {
652 AddDebugLogLineC(logSHAHashSet
, wxT("Saving failed: Current file is not a met-file!"));
656 AddDebugLogLineN(logSHAHashSet
, CFormat(wxT("Met file is version 0x%2.2x.")) % header
);
658 file
.WriteUInt8(KNOWN2_MET_VERSION
);
659 // Update the recorded size, in order for the sanity check below to work.
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
671 uint32 nHashCount
= file
.ReadUInt32();
672 if (file
.GetPosition() + nHashCount
*HASHSIZE
> nExistingSize
) {
673 AddDebugLogLineC(logSHAHashSet
, wxT("Saving failed: File contains fewer entries than specified!"));
676 // skip the rest of this hashset
677 file
.Seek(nHashCount
*HASHSIZE
, wxFromCurrent
);
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!"));
693 if (file
.GetLength() != nExistingSize
+ (nHashCount
+1)*HASHSIZE
+ 4) {
695 file
.SetLength(nExistingSize
);
696 AddDebugLogLineC(logSHAHashSet
, wxT("Failed to save HashSet: Calculated and real size of hashset differ!"));
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());
710 bool CAICHHashSet::LoadHashSet()
712 if (m_eStatus
!= AICH_HASHSETCOMPLETE
) {
716 if ( !m_pHashTree
.m_bHashValid
|| m_pHashTree
.m_nDataSize
!= m_pOwner
->GetFileSize() || m_pHashTree
.m_nDataSize
== 0) {
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
);
731 uint8 header
= file
.ReadUInt8();
732 if (header
!= KNOWN2_MET_VERSION
) {
733 AddDebugLogLineC(logSHAHashSet
, wxT("Loading failed: Current file is not a met-file!"));
737 CAICHHash CurrentHash
;
738 uint64 nExistingSize
= file
.GetLength();
740 while (file
.GetPosition() < nExistingSize
) {
741 CurrentHash
.Read(&file
);
742 if (m_pHashTree
.m_Hash
== CurrentHash
) {
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!"));
753 if (!m_pHashTree
.LoadLowestLevelHashs(&file
)) {
754 AddDebugLogLineC(logSHAHashSet
, wxT("Failed to load HashSet: LoadLowestLevelHashs failed!"));
757 if (!ReCalculateHash(false)) {
758 AddDebugLogLineC(logSHAHashSet
, wxT("Failed to load HashSet: Calculating loaded hashs failed!"));
761 if (CurrentHash
!= m_pHashTree
.m_Hash
) {
762 AddDebugLogLineC(logSHAHashSet
, wxT("Failed to load HashSet: Calculated Masterhash differs from given Masterhash - hashset corrupt!"));
767 nHashCount
= file
.ReadUInt32();
768 if (file
.GetPosition() + nHashCount
*HASHSIZE
> nExistingSize
) {
769 AddDebugLogLineC(logSHAHashSet
, wxT("Saving failed: File contains fewer entries than specified!"));
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());
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()
808 bool CAICHHashSet::ReCalculateHash(bool bDontReplace
)
810 CAICHHashAlgo
* hashalg
= GetNewHashAlgo();
811 bool bResult
= m_pHashTree
.ReCalculateHash(hashalg
, bDontReplace
);
816 bool CAICHHashSet::VerifyHashTree(bool bDeleteBadTrees
)
818 CAICHHashAlgo
* hashalg
= GetNewHashAlgo();
819 bool bResult
= m_pHashTree
.VerifyHashTree(hashalg
, bDeleteBadTrees
);
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()) {
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
);
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();
869 if (nMostTrustedPos
== (-1) || nSigningIPsTotal
== 0) {
873 // the check if we trust any hash
874 if ( thePrefs::IsTrustingEveryHash() ||
875 (nMostTrustedIPs
>= MINUNIQUEIPS_TOTRUST
&& (100 * nMostTrustedIPs
)/nSigningIPsTotal
>= MINPERCENTAGE_TOTRUST
)) {
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()
883 % m_aUntrustedHashs
[nMostTrustedPos
].m_Hash
.GetString()
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
);
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()
901 % m_aUntrustedHashs
[nMostTrustedPos
].m_Hash
.GetString()
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
);
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
) {
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
);
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
) {
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
) {
962 CAICHRequestedData empty
;
966 bool CAICHHashSet::IsPartDataAvailable(uint64 nPartStartPos
)
968 if (!(m_eStatus
== AICH_VERIFIED
|| m_eStatus
== AICH_TRUSTED
|| m_eStatus
== AICH_HASHSETCOMPLETE
) ) {
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
) {
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()
989 //define TESTSIZE 4294567295
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);
1001 CAICHHashTree* pTest2 = new CAICHHashTree(TESTSIZE-i, true, EMBLOCKSIZE);
1002 pTest->ReplaceHashTree(i, (TESTSIZE-i), &pTest2);
1005 #define TESTSIZE m_pHashTree.m_nDataSize
1006 if (m_pHashTree
.m_nDataSize
<= EMBLOCKSIZE
) {
1009 CAICHHashSet
TestHashSet(m_pOwner
);
1010 TestHashSet
.SetFileSize(m_pOwner
->GetFileSize());
1011 TestHashSet
.SetMasterHash(GetMasterHash(), AICH_VERIFIED
);
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();
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
);
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
);
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
);
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
);
1057 // File_checked_for_headers