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.
11 //
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.
16 //
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.
21 //
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
25 //
26 
27 #include <wx/file.h>
28 
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>
39 
40 
41 
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
47 
48 CAICHRequestedDataList CAICHHashSet::m_liRequestedData;
49 
50 /////////////////////////////////////////////////////////////////////////////////////////
51 ///CAICHHash
GetString() const52 wxString CAICHHash::GetString() const
53 {
54 	return EncodeBase32(m_abyBuffer, HASHSIZE);
55 }
56 
57 
Read(CFileDataIO * file)58 void CAICHHash::Read(CFileDataIO* file)
59 {
60 	file->Read(m_abyBuffer,HASHSIZE);
61 }
62 
63 
Write(CFileDataIO * file) const64 void CAICHHash::Write(CFileDataIO* file) const
65 {
66 	file->Write(m_abyBuffer,HASHSIZE);
67 }
68 
DecodeBase32(const wxString & base32)69 unsigned int CAICHHash::DecodeBase32(const wxString &base32)
70 {
71 	return ::DecodeBase32(base32, HASHSIZE, m_abyBuffer);
72 }
73 
74 
75 /////////////////////////////////////////////////////////////////////////////////////////
76 ///CAICHHashTree
77 
CAICHHashTree(uint64 nDataSize,bool bLeftBranch,uint64 nBaseSize)78 CAICHHashTree::CAICHHashTree(uint64 nDataSize, bool bLeftBranch, uint64 nBaseSize)
79 {
80 	m_nDataSize = nDataSize;
81 	m_nBaseSize = nBaseSize;
82 	m_bIsLeftBranch = bLeftBranch;
83 	m_pLeftTree = NULL;
84 	m_pRightTree = NULL;
85 	m_bHashValid = false;
86 }
87 
88 
~CAICHHashTree()89 CAICHHashTree::~CAICHHashTree()
90 {
91 	delete m_pLeftTree;
92 	delete m_pRightTree;
93 }
94 
95 
96 // recursive
FindHash(uint64 nStartPos,uint64 nSize,uint8 * nLevel)97 CAICHHashTree* CAICHHashTree::FindHash(uint64 nStartPos, uint64 nSize, uint8* nLevel)
98 {
99 	(*nLevel)++;
100 
101 	wxCHECK(*nLevel <= 22, NULL);
102 	wxCHECK(nStartPos + nSize <= m_nDataSize, NULL);
103 	wxCHECK(nSize <= m_nDataSize, NULL);
104 
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;
120 			}
121 
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 );
126 			}
127 
128 			return m_pLeftTree->FindHash(nStartPos, nSize, nLevel);
129 		} else {
130 			nStartPos -= nLeft;
131 			if (nStartPos + nSize > nRight) { // sanity
132 				wxFAIL;
133 				return NULL;
134 			}
135 
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 );
140 			}
141 
142 			return m_pRightTree->FindHash(nStartPos, nSize, nLevel);
143 		}
144 	}
145 }
146 
147 
148 // recursive
149 // calculates missing hash from the existing ones
150 // overwrites existing hashs
151 // fails if no hash is found for any branch
ReCalculateHash(CAICHHashAlgo * hashalg,bool bDontReplace)152 bool CAICHHashTree::ReCalculateHash(CAICHHashAlgo* hashalg, bool bDontReplace)
153 {
154 	if (m_pLeftTree && m_pRightTree) {
155 		if ( !m_pLeftTree->ReCalculateHash(hashalg, bDontReplace) || !m_pRightTree->ReCalculateHash(hashalg, bDontReplace) ) {
156 			return false;
157 		}
158 		if (bDontReplace && m_bHashValid) {
159 			return true;
160 		}
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;
170 		}
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;
176 	}
177 }
178 
VerifyHashTree(CAICHHashAlgo * hashalg,bool bDeleteBadTrees)179 bool CAICHHashTree::VerifyHashTree(CAICHHashAlgo* hashalg, bool bDeleteBadTrees)
180 {
181 	if (!m_bHashValid) {
182 		wxFAIL;
183 		if (bDeleteBadTrees) {
184 			if (m_pLeftTree) {
185 				delete m_pLeftTree;
186 				m_pLeftTree = NULL;
187 			}
188 			if (m_pRightTree) {
189 				delete m_pRightTree;
190 				m_pRightTree = NULL;
191 			}
192 		}
193 		AddDebugLogLineN(logSHAHashSet, wxT("VerifyHashTree - No masterhash available"));
194 		return false;
195 	}
196 
197 		// calculated missing hashs without overwriting anything
198 		if (m_pLeftTree && !m_pLeftTree->m_bHashValid) {
199 			m_pLeftTree->ReCalculateHash(hashalg, true);
200 		}
201 		if (m_pRightTree && !m_pRightTree->m_bHashValid) {
202 			m_pRightTree->ReCalculateHash(hashalg, true);
203 		}
204 
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;
211 				}
212 				if (m_pRightTree) {
213 					delete m_pRightTree;
214 					m_pRightTree = NULL;
215 				}
216 			}
217 			AddDebugLogLineN(logSHAHashSet, wxT("VerifyHashSet failed - Hashtree incomplete"));
218 			return false;
219 		}
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
222 
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);
228 
229 		if (m_Hash != CmpHash) {
230 			if (bDeleteBadTrees) {
231 				if (m_pLeftTree) {
232 					delete m_pLeftTree;
233 					m_pLeftTree = NULL;
234 				}
235 				if (m_pRightTree) {
236 					delete m_pRightTree;
237 					m_pRightTree = NULL;
238 				}
239 			}
240 			return false;
241 		}
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;
246 	}
247 }
248 
249 
SetBlockHash(uint64 nSize,uint64 nStartPos,CAICHHashAlgo * pHashAlg)250 void CAICHHashTree::SetBlockHash(uint64 nSize, uint64 nStartPos, CAICHHashAlgo* pHashAlg)
251 {
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;
258 	}
259 
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;
265 	}
266 
267 	pHashAlg->Finish(pToInsert->m_Hash);
268 	pToInsert->m_bHashValid = true;
269 }
270 
271 
CreatePartRecoveryData(uint64 nStartPos,uint64 nSize,CFileDataIO * fileDataOut,uint32 wHashIdent,bool b32BitIdent)272 bool CAICHHashTree::CreatePartRecoveryData(uint64 nStartPos, uint64 nSize, CFileDataIO* fileDataOut, uint32 wHashIdent, bool b32BitIdent)
273 {
274 	wxCHECK(nStartPos + nSize <= m_nDataSize, false);
275 	wxCHECK(nSize <= m_nDataSize, false);
276 
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;
288 
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;
295 		}
296 		if (nStartPos < nLeft) {
297 			if (nStartPos + nSize > nLeft || !m_pRightTree->m_bHashValid) { // sanity
298 				wxFAIL;
299 				return false;
300 			}
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;
308 			}
309 			m_pLeftTree->WriteHash(fileDataOut, wHashIdent, b32BitIdent);
310 			return m_pRightTree->CreatePartRecoveryData(nStartPos, nSize, fileDataOut, wHashIdent, b32BitIdent);
311 
312 		}
313 	}
314 }
315 
316 
WriteHash(CFileDataIO * fileDataOut,uint32 wHashIdent,bool b32BitIdent) const317 void CAICHHashTree::WriteHash(CFileDataIO* fileDataOut, uint32 wHashIdent, bool b32BitIdent) const
318 {
319 	wxASSERT( m_bHashValid );
320 	wHashIdent <<= 1;
321 	wHashIdent |= (m_bIsLeftBranch) ? 1: 0;
322 
323 	if (!b32BitIdent) {
324 		wxASSERT( wHashIdent <= 0xFFFF );
325 		fileDataOut->WriteUInt16((uint16)wHashIdent);
326 	} else {
327 		fileDataOut->WriteUInt32(wHashIdent);
328 	}
329 
330 	m_Hash.Write(fileDataOut);
331 }
332 
333 
334 // write lowest level hashs into file, ordered from left to right optional without identifier
WriteLowestLevelHashs(CFileDataIO * fileDataOut,uint32 wHashIdent,bool bNoIdent,bool b32BitIdent) const335 bool CAICHHashTree::WriteLowestLevelHashs(CFileDataIO* fileDataOut, uint32 wHashIdent, bool bNoIdent, bool b32BitIdent) const
336 {
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);
346 			}
347 
348 			m_Hash.Write(fileDataOut);
349 			return true;
350 		} else {
351 			wxFAIL;
352 			return false;
353 		}
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);
360 	}
361 }
362 
363 // recover all low level hashs from given data. hashs are assumed to be ordered in left to right - no identifier used
LoadLowestLevelHashs(CFileDataIO * fileInput)364 bool CAICHHashTree::LoadLowestLevelHashs(CFileDataIO* fileInput)
365 {
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 );
379 		}
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 );
384 		}
385 		return m_pLeftTree->LoadLowestLevelHashs(fileInput)
386 				&& m_pRightTree->LoadLowestLevelHashs(fileInput);
387 	}
388 }
389 
390 
391 // write the hash, specified by wHashIdent, with Data from fileInput.
SetHash(CFileDataIO * fileInput,uint32 wHashIdent,sint8 nLevel,bool bAllowOverwrite)392 bool CAICHHashTree::SetHash(CFileDataIO* fileInput, uint32 wHashIdent, sint8 nLevel, bool bAllowOverwrite)
393 {
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;
399 		}
400 		if (i > 31) {
401 			AddDebugLogLineN(logSHAHashSet, wxT("CAICHHashTree::SetHash - found invalid HashIdent (0)"));
402 			return false;
403 		} else {
404 			nLevel = 31 - i;
405 		}
406 	}
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;
413 		}
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 );
433 			}
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 );
440 			}
441 			return m_pRightTree->SetHash(fileInput, wHashIdent, nLevel);
442 		}
443 	}
444 }
445 
446 
447 /////////////////////////////////////////////////////////////////////////////////////////
448 ///CAICHUntrustedHash
AddSigningIP(uint32 dwIP)449 bool CAICHUntrustedHash::AddSigningIP(uint32 dwIP)
450 {
451 	dwIP &= 0x00F0FFFF; // we use only the 20 most significant bytes for unique IPs
452 	return m_adwIpsSigning.insert(dwIP).second;
453 }
454 
455 
456 
457 /////////////////////////////////////////////////////////////////////////////////////////
458 ///CAICHHashSet
CAICHHashSet(CKnownFile * pOwner)459 CAICHHashSet::CAICHHashSet(CKnownFile* pOwner)
460 	: m_pHashTree(0, true, PARTSIZE)
461 {
462 	m_eStatus = AICH_EMPTY;
463 	m_pOwner = pOwner;
464 }
465 
~CAICHHashSet(void)466 CAICHHashSet::~CAICHHashSet(void)
467 {
468 	FreeHashSet();
469 }
470 
CreatePartRecoveryData(uint64 nPartStartPos,CFileDataIO * fileDataOut,bool bDbgDontLoad)471 bool CAICHHashSet::CreatePartRecoveryData(uint64 nPartStartPos, CFileDataIO* fileDataOut, bool bDbgDontLoad)
472 {
473 	wxASSERT( m_pOwner );
474 	if (m_pOwner->IsPartFile() || m_eStatus != AICH_HASHSETCOMPLETE) {
475 		wxFAIL;
476 		return false;
477 	}
478 	if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE) {
479 		wxFAIL;
480 		return false;
481 	}
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;
488 		}
489 	}
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();
496 
497 	if (bUse32BitIdentifier) {
498 		fileDataOut->WriteUInt16(0); // no 16bit hashs to write
499 	}
500 
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;
512 		}
513 	} else {
514 		AddDebugLogLineN(logSHAHashSet,
515 			CFormat(wxT("Failed to create RecoveryData for '%s'")) % m_pOwner->GetFileName());
516 		bResult = false;
517 		SetStatus(AICH_ERROR);
518 	}
519 	if (!bUse32BitIdentifier) {
520 		fileDataOut->WriteUInt16(0); // no 32bit hashs to write
521 	}
522 
523 	if (!bDbgDontLoad) {
524 		FreeHashSet();
525 	}
526 	return bResult;
527 }
528 
ReadRecoveryData(uint64 nPartStartPos,CMemFile * fileDataIn)529 bool CAICHHashSet::ReadRecoveryData(uint64 nPartStartPos, CMemFile* fileDataIn)
530 {
531 	if (/*eMule TODO !m_pOwner->IsPartFile() ||*/ !(m_eStatus == AICH_VERIFIED || m_eStatus == AICH_TRUSTED) ) {
532 		wxFAIL;
533 		return false;
534 	}
535 
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
541 	*/
542 
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)
545 
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);
550 
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;
559 	}
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))
564 		{
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;
570 		}
571 	}
572 
573 
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;
581 		}
582 
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))
589 			{
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;
593 			}
594 		}
595 	}
596 
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;
600 	}
601 
602 
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;
612 			}
613 		}
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;
621 	}
622 }
623 
624 // this function is only allowed to be called right after successfully calculating the hashset (!)
SaveHashSet()625 bool CAICHHashSet::SaveHashSet()
626 {
627 	if (m_eStatus != AICH_HASHSETCOMPLETE) {
628 		wxFAIL;
629 		return false;
630 	}
631 	if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize()) {
632 		wxFAIL;
633 		return false;
634 	}
635 
636 
637 	try {
638 		const wxString fullpath = thePrefs::GetConfigDir() + KNOWN2_MET_FILENAME;
639 		const bool exists = wxFile::Exists(fullpath);
640 
641 		CFile file(fullpath, exists ? CFile::read_write : CFile::write);
642 		if (!file.IsOpened()) {
643 			AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: opening met file failed!"));
644 			return false;
645 		}
646 
647 		uint64 nExistingSize = file.GetLength();
648 		if (nExistingSize) {
649 			uint8 header = file.ReadUInt8();
650 			if (header != KNOWN2_MET_VERSION) {
651 				AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: Current file is not a met-file!"));
652 				return false;
653 			}
654 
655 			AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Met file is version 0x%2.2x.")) % header);
656 		} else {
657 			file.WriteUInt8(KNOWN2_MET_VERSION);
658 			// Update the recorded size, in order for the sanity check below to work.
659 			nExistingSize += 1;
660 		}
661 
662 		// first we check if the hashset we want to write is already stored
663 		CAICHHash CurrentHash;
664 		while (file.GetPosition() < nExistingSize) {
665 			CurrentHash.Read(&file);
666 			if (m_pHashTree.m_Hash == CurrentHash) {
667 				// this hashset if already available, no need to save it again
668 				return true;
669 			}
670 			uint32 nHashCount = file.ReadUInt32();
671 			if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize) {
672 				AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: File contains fewer entries than specified!"));
673 				return false;
674 			}
675 			// skip the rest of this hashset
676 			file.Seek(nHashCount*HASHSIZE, wxFromCurrent);
677 		}
678 
679 		// write hashset
680 		m_pHashTree.m_Hash.Write(&file);
681 		uint32 nHashCount = (PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE);
682 		if (m_pHashTree.m_nDataSize % PARTSIZE != 0) {
683 			nHashCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0);
684 		}
685 		file.WriteUInt32(nHashCount);
686 		if (!m_pHashTree.WriteLowestLevelHashs(&file, 0, true, true)) {
687 			// thats bad... really
688 			file.SetLength(nExistingSize);
689 			AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: WriteLowestLevelHashs() failed!"));
690 			return false;
691 		}
692 		if (file.GetLength() != nExistingSize + (nHashCount+1)*HASHSIZE + 4) {
693 			// thats even worse
694 			file.SetLength(nExistingSize);
695 			AddDebugLogLineC(logSHAHashSet, wxT("Failed to save HashSet: Calculated and real size of hashset differ!"));
696 			return false;
697 		}
698 		AddDebugLogLineN(logSHAHashSet, CFormat(wxT("Successfully saved eMuleAC Hashset, %u Hashs + 1 Masterhash written")) % nHashCount);
699 	} catch (const CSafeIOException& e) {
700 		AddDebugLogLineC(logSHAHashSet, wxT("IO error while saving AICH HashSet: ") + e.what());
701 		return false;
702 	}
703 
704 	return true;
705 }
706 
707 
LoadHashSet()708 bool CAICHHashSet::LoadHashSet()
709 {
710 	if (m_eStatus != AICH_HASHSETCOMPLETE) {
711 		wxFAIL;
712 		return false;
713 	}
714 	if ( !m_pHashTree.m_bHashValid || m_pHashTree.m_nDataSize != m_pOwner->GetFileSize() || m_pHashTree.m_nDataSize == 0) {
715 		wxFAIL;
716 		return false;
717 	}
718 	wxString fullpath = thePrefs::GetConfigDir() + KNOWN2_MET_FILENAME;
719 	CFile file(fullpath, CFile::read);
720 	if (!file.IsOpened()) {
721 		if (wxFileExists(fullpath)) {
722 			wxString strError(wxT("Failed to load ") KNOWN2_MET_FILENAME wxT(" file"));
723 			AddDebugLogLineC(logSHAHashSet, strError);
724 		}
725 		return false;
726 	}
727 
728 	try {
729 		uint8 header = file.ReadUInt8();
730 		if (header != KNOWN2_MET_VERSION) {
731 			AddDebugLogLineC(logSHAHashSet, wxT("Loading failed: Current file is not a met-file!"));
732 			return false;
733 		}
734 
735 		CAICHHash CurrentHash;
736 		uint64 nExistingSize = file.GetLength();
737 		uint32 nHashCount;
738 		while (file.GetPosition() < nExistingSize) {
739 			CurrentHash.Read(&file);
740 			if (m_pHashTree.m_Hash == CurrentHash) {
741 				// found Hashset
742 				uint32 nExpectedCount =	(PARTSIZE/EMBLOCKSIZE + ((PARTSIZE % EMBLOCKSIZE != 0)? 1 : 0)) * (m_pHashTree.m_nDataSize/PARTSIZE);
743 				if (m_pHashTree.m_nDataSize % PARTSIZE != 0) {
744 					nExpectedCount += (m_pHashTree.m_nDataSize % PARTSIZE)/EMBLOCKSIZE + (((m_pHashTree.m_nDataSize % PARTSIZE) % EMBLOCKSIZE != 0)? 1 : 0);
745 				}
746 				nHashCount = file.ReadUInt32();
747 				if (nHashCount != nExpectedCount) {
748 					AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Available Hashs and expected hashcount differ!"));
749 					return false;
750 				}
751 				if (!m_pHashTree.LoadLowestLevelHashs(&file)) {
752 					AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: LoadLowestLevelHashs failed!"));
753 					return false;
754 				}
755 				if (!ReCalculateHash(false)) {
756 					AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Calculating loaded hashs failed!"));
757 					return false;
758 				}
759 				if (CurrentHash != m_pHashTree.m_Hash) {
760 					AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: Calculated Masterhash differs from given Masterhash - hashset corrupt!"));
761 					return false;
762 				}
763 				return true;
764 			}
765 			nHashCount = file.ReadUInt32();
766 			if (file.GetPosition() + nHashCount*HASHSIZE > nExistingSize) {
767 				AddDebugLogLineC(logSHAHashSet, wxT("Saving failed: File contains fewer entries than specified!"));
768 				return false;
769 			}
770 			// skip the rest of this hashset
771 			file.Seek(nHashCount*HASHSIZE, wxFromCurrent);
772 		}
773 		AddDebugLogLineC(logSHAHashSet, wxT("Failed to load HashSet: HashSet not found!"));
774 	} catch (const CSafeIOException& e) {
775 		AddDebugLogLineC(logSHAHashSet, wxT("IO error while loading AICH HashSet: ") + e.what());
776 	}
777 
778 	return false;
779 }
780 
781 // delete the hashset except the masterhash (we dont keep aich hashsets in memory to save ressources)
FreeHashSet()782 void CAICHHashSet::FreeHashSet()
783 {
784 	if (m_pHashTree.m_pLeftTree) {
785 		delete m_pHashTree.m_pLeftTree;
786 		m_pHashTree.m_pLeftTree = NULL;
787 	}
788 	if (m_pHashTree.m_pRightTree) {
789 		delete m_pHashTree.m_pRightTree;
790 		m_pHashTree.m_pRightTree = NULL;
791 	}
792 }
793 
SetMasterHash(const CAICHHash & Hash,EAICHStatus eNewStatus)794 void CAICHHashSet::SetMasterHash(const CAICHHash& Hash, EAICHStatus eNewStatus)
795 {
796 	m_pHashTree.m_Hash = Hash;
797 	m_pHashTree.m_bHashValid = true;
798 	SetStatus(eNewStatus);
799 }
800 
GetNewHashAlgo()801 CAICHHashAlgo*	CAICHHashSet::GetNewHashAlgo()
802 {
803 	return new CSHA();
804 }
805 
ReCalculateHash(bool bDontReplace)806 bool CAICHHashSet::ReCalculateHash(bool bDontReplace)
807 {
808 	CAICHHashAlgo* hashalg = GetNewHashAlgo();
809 	bool bResult = m_pHashTree.ReCalculateHash(hashalg, bDontReplace);
810 	delete hashalg;
811 	return bResult;
812 }
813 
VerifyHashTree(bool bDeleteBadTrees)814 bool CAICHHashSet::VerifyHashTree(bool bDeleteBadTrees)
815 {
816 	CAICHHashAlgo* hashalg = GetNewHashAlgo();
817 	bool bResult = m_pHashTree.VerifyHashTree(hashalg, bDeleteBadTrees);
818 	delete hashalg;
819 	return bResult;
820 }
821 
822 
SetFileSize(uint64 nSize)823 void CAICHHashSet::SetFileSize(uint64 nSize)
824 {
825 	m_pHashTree.m_nDataSize = nSize;
826 	m_pHashTree.m_nBaseSize = (nSize <= PARTSIZE) ? EMBLOCKSIZE : PARTSIZE;
827 }
828 
829 
UntrustedHashReceived(const CAICHHash & Hash,uint32 dwFromIP)830 void CAICHHashSet::UntrustedHashReceived(const CAICHHash& Hash, uint32 dwFromIP)
831 {
832 	switch(GetStatus()) {
833 		case AICH_EMPTY:
834 		case AICH_UNTRUSTED:
835 		case AICH_TRUSTED:
836 			break;
837 		default:
838 			return;
839 	}
840 	bool bFound = false;
841 	bool bAdded = false;
842 	for (uint32 i = 0; i < m_aUntrustedHashs.size(); ++i) {
843 		if (m_aUntrustedHashs[i].m_Hash == Hash) {
844 			bAdded = m_aUntrustedHashs[i].AddSigningIP(dwFromIP);
845 			bFound = true;
846 			break;
847 		}
848 	}
849 	if (!bFound) {
850 		bAdded = true;
851 		CAICHUntrustedHash uhToAdd;
852 		uhToAdd.m_Hash = Hash;
853 		uhToAdd.AddSigningIP(dwFromIP);
854 		m_aUntrustedHashs.push_back(uhToAdd);
855 	}
856 
857 	uint32 nSigningIPsTotal = 0;	// unique clients who send us a hash
858 	int nMostTrustedPos = (-1);  // the hash which most clients send us
859 	uint32 nMostTrustedIPs = 0;
860 	for (uint32 i = 0; i < (uint32)m_aUntrustedHashs.size(); ++i) {
861 		nSigningIPsTotal += m_aUntrustedHashs[i].m_adwIpsSigning.size();
862 		if ((uint32)m_aUntrustedHashs[i].m_adwIpsSigning.size() > nMostTrustedIPs) {
863 			nMostTrustedIPs = m_aUntrustedHashs[i].m_adwIpsSigning.size();
864 			nMostTrustedPos = i;
865 		}
866 	}
867 	if (nMostTrustedPos == (-1) || nSigningIPsTotal == 0) {
868 		wxFAIL;
869 		return;
870 	}
871 	// the check if we trust any hash
872 	if ( thePrefs::IsTrustingEveryHash() ||
873 		(nMostTrustedIPs >= MINUNIQUEIPS_TOTRUST && (100 * nMostTrustedIPs)/nSigningIPsTotal >= MINPERCENTAGE_TOTRUST)) {
874 		//trusted
875 		AddDebugLogLineN(logSHAHashSet,
876 			CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ")
877 					wxT("We trust the Hash %s from %u client(s) (%u%%). File: %s"))
878 				% (bAdded ? wxT("") : wxT("not "))
879 				% m_aUntrustedHashs.size()
880 				% nSigningIPsTotal
881 				% m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString()
882 				% nMostTrustedIPs
883 				% ((100 * nMostTrustedIPs) / nSigningIPsTotal)
884 				% m_pOwner->GetFileName());
885 
886 		SetStatus(AICH_TRUSTED);
887 		if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) {
888 			SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_TRUSTED);
889 			FreeHashSet();
890 		}
891 	} else {
892 		// untrusted
893 		AddDebugLogLineN(logSHAHashSet,
894 			CFormat(wxT("AICH Hash received (%sadded), We have now %u hash(es) from %u unique IP(s). ")
895 					wxT("Best Hash %s from %u clients (%u%%) - but we don't trust it yet. File: %s"))
896 				% (bAdded ? wxT(""): wxT("not "))
897 				% m_aUntrustedHashs.size()
898 				% nSigningIPsTotal
899 				% m_aUntrustedHashs[nMostTrustedPos].m_Hash.GetString()
900 				% nMostTrustedIPs
901 				% ((100 * nMostTrustedIPs) / nSigningIPsTotal)
902 				% m_pOwner->GetFileName());
903 
904 		SetStatus(AICH_UNTRUSTED);
905 		if (!HasValidMasterHash() || GetMasterHash() != m_aUntrustedHashs[nMostTrustedPos].m_Hash) {
906 			SetMasterHash(m_aUntrustedHashs[nMostTrustedPos].m_Hash, AICH_UNTRUSTED);
907 			FreeHashSet();
908 		}
909 	}
910 	if (bAdded) {}	// get rid of unused variable warning
911 }
912 
913 
ClientAICHRequestFailed(CUpDownClient * pClient)914 void CAICHHashSet::ClientAICHRequestFailed(CUpDownClient* pClient)
915 {
916 	pClient->SetReqFileAICHHash(NULL);
917 	CAICHRequestedData data = GetAICHReqDetails(pClient);
918 	RemoveClientAICHRequest(pClient);
919 	if (data.m_pClient.GetClient() != pClient) {
920 		return;
921 	}
922 	if( theApp->downloadqueue->IsPartFile(data.m_pPartFile)) {
923 		AddDebugLogLineN(logSHAHashSet,
924 			CFormat(wxT("AICH Request failed, Trying to ask another client (File: '%s', Part: %u, Client '%s'"))
925 				% data.m_pPartFile->GetFileName() % data.m_nPart % pClient->GetClientFullInfo());
926 		data.m_pPartFile->RequestAICHRecovery(data.m_nPart);
927 	}
928 }
929 
930 
RemoveClientAICHRequest(const CUpDownClient * pClient)931 void CAICHHashSet::RemoveClientAICHRequest(const CUpDownClient* pClient)
932 {
933 	for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
934 		if (it->m_pClient.GetClient() == pClient) {
935 			m_liRequestedData.erase(it);
936 			return;
937 		}
938 	}
939 	wxFAIL;
940 }
941 
IsClientRequestPending(const CPartFile * pForFile,uint16 nPart)942 bool CAICHHashSet::IsClientRequestPending(const CPartFile* pForFile, uint16 nPart)
943 {
944 	for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
945 		if (it->m_pPartFile == pForFile && it->m_nPart == nPart) {
946 			return true;
947 		}
948 	}
949 	return false;
950 }
951 
GetAICHReqDetails(const CUpDownClient * pClient)952 CAICHRequestedData CAICHHashSet::GetAICHReqDetails(const  CUpDownClient* pClient)
953 {
954 	for (CAICHRequestedDataList::iterator it = m_liRequestedData.begin();it != m_liRequestedData.end(); ++it) {
955 		if (it->m_pClient.GetClient() == pClient) {
956 			return *(it);
957 		}
958 	}
959 	wxFAIL;
960 	CAICHRequestedData empty;
961 	return empty;
962 }
963 
IsPartDataAvailable(uint64 nPartStartPos)964 bool CAICHHashSet::IsPartDataAvailable(uint64 nPartStartPos)
965 {
966 	if (!(m_eStatus == AICH_VERIFIED || m_eStatus == AICH_TRUSTED || m_eStatus == AICH_HASHSETCOMPLETE) ) {
967 		wxFAIL;
968 		return false;
969 	}
970 	uint64 nPartSize = min<uint64>(PARTSIZE, m_pOwner->GetFileSize()-nPartStartPos);
971 	for (uint64 nPartPos = 0; nPartPos < nPartSize; nPartPos += EMBLOCKSIZE) {
972 		CAICHHashTree* phtToCheck = m_pHashTree.FindHash(nPartStartPos+nPartPos, min<uint64>(EMBLOCKSIZE, nPartSize-nPartPos));
973 		if (phtToCheck == NULL || !phtToCheck->m_bHashValid) {
974 			return false;
975 		}
976 	}
977 	return true;
978 }
979 
980 // VC++ defines Assert as ASSERT. VC++ also defines VERIFY MACRO, which is the equivalent of ASSERT but also works in Released builds.
981 
982 #define VERIFY(x) wxASSERT(x)
983 
DbgTest()984 void CAICHHashSet::DbgTest()
985 {
986 #ifdef _DEBUG
987 	//define TESTSIZE 4294567295
988 	uint8 maxLevel = 0;
989 	uint32 cHash = 1;
990 	uint8 curLevel = 0;
991 	//uint32 cParts = 0;
992 	maxLevel = 0;
993 /*	CAICHHashTree* pTest = new CAICHHashTree(TESTSIZE, true, 9728000);
994 	for (uint64 i = 0; i+9728000 < TESTSIZE; i += 9728000) {
995 		CAICHHashTree* pTest2 = new CAICHHashTree(9728000, true, EMBLOCKSIZE);
996 		pTest->ReplaceHashTree(i, 9728000, &pTest2);
997 		cParts++;
998 	}
999 	CAICHHashTree* pTest2 = new CAICHHashTree(TESTSIZE-i, true, EMBLOCKSIZE);
1000 	pTest->ReplaceHashTree(i, (TESTSIZE-i), &pTest2);
1001 	cParts++;
1002 */
1003 #define TESTSIZE m_pHashTree.m_nDataSize
1004 	if (m_pHashTree.m_nDataSize <= EMBLOCKSIZE) {
1005 		return;
1006 	}
1007 	CAICHHashSet TestHashSet(m_pOwner);
1008 	TestHashSet.SetFileSize(m_pOwner->GetFileSize());
1009 	TestHashSet.SetMasterHash(GetMasterHash(), AICH_VERIFIED);
1010 	CMemFile file;
1011 	uint64 i;
1012 	for (i = 0; i+9728000 < TESTSIZE; i += 9728000) {
1013 		VERIFY( CreatePartRecoveryData(i, &file) );
1014 
1015 		/*uint32 nRandomCorruption = (rand() * rand()) % (file.GetLength()-4);
1016 		file.Seek(nRandomCorruption, CFile::begin);
1017 		file.Write(&nRandomCorruption, 4);*/
1018 
1019 		file.Seek(0,wxFromStart);
1020 		VERIFY( TestHashSet.ReadRecoveryData(i, &file) );
1021 		file.Seek(0,wxFromStart);
1022 		TestHashSet.FreeHashSet();
1023 		uint32 j;
1024 		for (j = 0; j+EMBLOCKSIZE < 9728000; j += EMBLOCKSIZE) {
1025 			VERIFY( m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel) );
1026 			//TRACE(wxT("%u - %s\r\n"), cHash, m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel)->m_Hash.GetString());
1027 			maxLevel = max(curLevel, maxLevel);
1028 			curLevel = 0;
1029 			cHash++;
1030 		}
1031 		VERIFY( m_pHashTree.FindHash(i+j, 9728000-j, &curLevel) );
1032 		//TRACE(wxT("%u - %s\r\n"), cHash, m_pHashTree.FindHash(i+j, 9728000-j, &curLevel)->m_Hash.GetString());
1033 		maxLevel = max(curLevel, maxLevel);
1034 		curLevel = 0;
1035 		cHash++;
1036 
1037 	}
1038 	VERIFY( CreatePartRecoveryData(i, &file) );
1039 	file.Seek(0,wxFromStart);
1040 	VERIFY( TestHashSet.ReadRecoveryData(i, &file) );
1041 	file.Seek(0,wxFromStart);
1042 	TestHashSet.FreeHashSet();
1043 	for (uint64 j = 0; j+EMBLOCKSIZE < TESTSIZE-i; j += EMBLOCKSIZE) {
1044 		VERIFY( m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel) );
1045 		//TRACE(wxT("%u - %s\r\n"), cHash,m_pHashTree.FindHash(i+j, EMBLOCKSIZE, &curLevel)->m_Hash.GetString());
1046 		maxLevel = max(curLevel, maxLevel);
1047 		curLevel = 0;
1048 		cHash++;
1049 	}
1050 	//VERIFY( m_pHashTree.FindHash(i+j, (TESTSIZE-i)-j, &curLevel) );
1051 	//TRACE(wxT("%u - %s\r\n"), cHash,m_pHashTree.FindHash(i+j, (TESTSIZE-i)-j, &curLevel)->m_Hash.GetString());
1052 	maxLevel = max(curLevel, maxLevel);
1053 #endif
1054 }
1055 // File_checked_for_headers
1056