Upstream tarball 9608
[amule.git] / src / CorruptionBlackBox.cpp
blob5b4ff426fd557a07e76de7c272f450b7b5934a69
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2008-2009 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2008-2009 Stu Redman ( sturedman@amule.org )
6 // Copyright (C) 2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "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.
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
27 #include "CorruptionBlackBox.h"
28 #include <protocol/ed2k/Constants.h> // needed for PARTSIZE
29 #include "updownclient.h"
30 #include "Logger.h"
31 #include "amule.h" // needed for theApp
32 #include "ClientList.h"
33 #include "NetworkFunctions.h" // needed for Uint32toStringIP
34 #include <common/Format.h> // needed for CFormat
36 #define CBB_BANTHRESHOLD 32 //% max corrupted data
38 #ifdef __DEBUG__
39 #define DEBUG_ONLY(statement) statement
40 #else
41 #define DEBUG_ONLY(statement)
42 #endif
44 // Record to store information which piece of data was downloaded from which client
46 CCorruptionBlackBox::CCBBRecord::CCBBRecord(uint32 nStartPos, uint32 nEndPos, uint32 dwIP)
48 if (nStartPos > nEndPos) {
49 wxASSERT( false );
50 return;
52 m_nStartPos = nStartPos;
53 m_nEndPos = nEndPos;
54 m_dwIP = dwIP;
57 // Try to merge new data to an existing record
58 bool CCorruptionBlackBox::CCBBRecord::Merge(uint32 nStartPos, uint32 nEndPos, uint32 dwIP)
60 if (m_dwIP == dwIP) {
61 if (nStartPos == m_nEndPos + 1) {
62 m_nEndPos = nEndPos;
63 return true;
64 } else if (nEndPos + 1 == m_nStartPos) {
65 m_nStartPos = nStartPos;
66 return true;
69 return false;
72 // Release memory (after download completes)
73 void CCorruptionBlackBox::Free()
75 m_Records.clear();
76 m_goodClients.clear();
77 m_badClients.clear();
80 // Store a piece of received data (don't know if it's good or bad yet).
81 // Data is stored in a list for the chunk in belongs to.
82 void CCorruptionBlackBox::TransferredData(uint64 nStartPos, uint64 nEndPos, uint32 senderIP)
84 if (nStartPos > nEndPos) {
85 wxASSERT( false );
86 return;
89 // convert pos to relative block pos
90 uint16 nPart = (uint16)(nStartPos / PARTSIZE);
91 uint32 nRelStartPos = nStartPos - nPart*PARTSIZE;
92 uint32 nRelEndPos = nEndPos - nPart*PARTSIZE;
93 if (nRelEndPos >= PARTSIZE) {
94 // data crosses the partborder, split it
95 // (for the fun of it, this should never happen)
96 nRelEndPos = PARTSIZE-1;
97 TransferredData((nPart+1)*PARTSIZE, nEndPos, senderIP);
100 // Let's keep things simple.
101 // We don't request data we already have.
102 // We check if received data exceeds block boundaries.
103 // -> There should not be much overlap here.
104 // So just stuff everything received into the list and only join adjacent blocks.
106 CRecordList & list = m_Records[nPart]; // this creates the entry if it doesn't exist yet
107 bool merged = false;
108 for (CRecordList::iterator it = list.begin(); it != list.end() && !merged; ++it) {
109 merged = it->Merge(nRelStartPos, nRelEndPos, senderIP);
111 if (!merged) {
112 list.push_back(CCBBRecord(nRelStartPos, nRelEndPos, senderIP));
113 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): transferred: new record for part %d (%d - %d, %s)"))
114 % m_partNumber % nPart % nRelStartPos % nRelEndPos % Uint32toStringIP(senderIP));
118 // Mark a piece of data as good or bad.
119 // Piece is removed from the chunk list and added to the client's record.
120 void CCorruptionBlackBox::VerifiedData(bool ok, uint16 nPart, uint32 nRelStartPos, uint32 nRelEndPos) {
121 if (nRelStartPos > nRelEndPos) {
122 wxASSERT( false );
123 return;
126 CRecordList & list = m_Records[nPart];
127 #ifdef __DEBUG__
128 std::map<uint32, bool> mapDebug;
129 uint32 nDbgVerifiedBytes = 0;
130 size_t listsize1 = list.size();
131 #endif
132 for (CRecordList::iterator it1 = list.begin(); it1 != list.end();) {
133 CRecordList::iterator it = it1++;
134 uint32 & curStart = it->m_nStartPos;
135 uint32 & curEnd = it->m_nEndPos;
136 uint32 ip = it->m_dwIP;
137 uint32 data = 0;
138 if (curStart >= nRelStartPos && curStart <= nRelEndPos) {
139 // [arg
140 // [cur
141 if (curEnd > nRelEndPos) {
142 // [arg]
143 // [cur]
144 data = nRelEndPos - curStart + 1;
145 curStart = nRelEndPos + 1;
146 } else {
147 // [arg ]
148 // [cur]
149 data = curEnd - curStart + 1;
150 list.erase(it);
152 } else if (curStart < nRelStartPos && curEnd >= nRelStartPos) {
153 // [arg
154 // [cur
155 if (curEnd > nRelEndPos) {
156 // [arg]
157 // [cur ]
158 data = nRelEndPos - nRelStartPos + 1;
159 // split it: insert new block before current block
160 list.insert(it, CCBBRecord(curStart, nRelStartPos - 1, ip));
161 curStart = nRelEndPos + 1;
162 } else {
163 // [arg]
164 // [cur]
165 data = curEnd - nRelStartPos + 1;
166 curEnd = nRelStartPos - 1;
168 // else no overlap
170 if (data) {
171 if (ok) {
172 m_goodClients[ip].m_downloaded += data;
173 } else {
174 // corrupted data records are always counted as at least blocksize or bigger
175 m_badClients[ip].m_downloaded += (data > EMBLOCKSIZE) ? data : EMBLOCKSIZE;;
177 DEBUG_ONLY(nDbgVerifiedBytes += data);
178 DEBUG_ONLY(mapDebug[ip] = 1);
181 DEBUG_ONLY(size_t listsize2 = list.size());
182 // when everything is added to the stats drop the whole record
183 if (list.empty()) {
184 m_Records.erase(nPart);
186 #ifdef __DEBUG__
187 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): found and marked %d recorded bytes of %d as %s in part %d, %d records found, %d records left, %d different clients"))
188 % m_partNumber % nDbgVerifiedBytes % (nRelEndPos-nRelStartPos+1) % (ok ? wxT("verified") : wxT("corrupt"))
189 % nPart % listsize1 % listsize2 % mapDebug.size());
190 #endif
193 // Check all clients that uploaded corrupted data,
194 // and ban them if they didn't upload enough good data too.
195 void CCorruptionBlackBox::EvaluateData()
197 CCBBClientMap::iterator it = m_badClients.begin();
198 for (; it != m_badClients.end(); ++it) {
199 uint32 ip = it->first;
200 uint64 bad = it->second.m_downloaded;
201 if (!bad) {
202 wxFAIL; // this should not happen
203 continue;
205 uint64 good = 0;
206 CCBBClientMap::iterator it2 = m_goodClients.find(ip);
207 if (it2 != m_goodClients.end()) {
208 good = it2->second.m_downloaded;
211 int nCorruptPercentage = bad * 100 / (bad + good);
213 if (nCorruptPercentage > CBB_BANTHRESHOLD) {
214 CUpDownClient* pEvilClient = theApp->clientlist->FindClientByIP(ip);
215 if (pEvilClient != NULL) {
216 AddLogLineN(CFormat(wxT("CorruptionBlackBox(%s): Banning: Found client which sent %d of %d corrupted data, %s"))
217 % m_partNumber % bad % (good + bad) % pEvilClient->GetClientFullInfo());
218 theApp->clientlist->AddTrackClient(pEvilClient);
219 pEvilClient->Ban(); // Identified as sender of corrupt data
220 // Stop download right away
221 pEvilClient->SetDownloadState(DS_BANNED);
222 if (pEvilClient->Disconnected(wxT("Upload of corrupted data"))) {
223 pEvilClient->Safe_Delete();
225 } else {
226 AddLogLineN(CFormat(wxT("CorruptionBlackBox(%s): Banning: Found client which sent %d of %d corrupted data, %s"))
227 % m_partNumber % bad % (good + bad) % Uint32toStringIP(ip));
228 theApp->clientlist->AddBannedClient(ip);
230 } else {
231 CUpDownClient* pSuspectClient = theApp->clientlist->FindClientByIP(ip);
232 if (pSuspectClient != NULL) {
233 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): Reporting: Found client which probably sent %d of %d corrupted data, but it is within the acceptable limit, %s"))
234 % m_partNumber % bad % (good + bad) % pSuspectClient->GetClientFullInfo());
235 theApp->clientlist->AddTrackClient(pSuspectClient);
236 } else {
237 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): Reporting: Found client which probably sent %d of %d corrupted data, but it is within the acceptable limit, %s"))
238 % m_partNumber % bad % (good + bad) % Uint32toStringIP(ip));
244 // Full debug output of all data
245 void CCorruptionBlackBox::DumpAll()
247 #ifdef __DEBUG__
248 AddDebugLogLineN(logPartFile, wxT("CBB Dump Records"));
249 std::map<uint16, CRecordList>::iterator it = m_Records.begin();
250 for (; it != m_Records.end(); ++it) {
251 uint16 block = it->first;
252 CRecordList & list = it->second;
253 for (CRecordList::iterator it2 = list.begin(); it2 != list.end(); ++it2) {
254 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %6d %.16s %10d - %10d"))
255 % block % Uint32toStringIP(it2->m_dwIP) % it2->m_nStartPos % it2->m_nEndPos);
258 if (!m_goodClients.empty()) {
259 AddDebugLogLineN(logPartFile, wxT("CBB Dump good Clients"));
260 CCBBClientMap::iterator it3 = m_goodClients.begin();
261 for (; it3 != m_goodClients.end(); ++it3) {
262 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %.16s good %10d"))
263 % Uint32toStringIP(it3->first) % it3->second.m_downloaded);
266 if (!m_badClients.empty()) {
267 AddDebugLogLineN(logPartFile, wxT("CBB Dump bad Clients"));
268 CCBBClientMap::iterator it3 = m_badClients.begin();
269 for (; it3 != m_badClients.end(); ++it3) {
270 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %.16s bad %10d"))
271 % Uint32toStringIP(it3->first) % it3->second.m_downloaded);
274 #endif