Upstream tarball 9959
[amule.git] / src / CorruptionBlackBox.cpp
blobd5b045aab8ddfa6828d3b0f70bc5362ca79958f2
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 "OtherFunctions.h" // needed for CastItoXBytes
35 #include <common/Format.h> // needed for CFormat
36 #include <common/Macros.h>
38 #define CBB_BANTHRESHOLD 32 //% max corrupted data
40 // Record to store information which piece of data was downloaded from which client
42 CCorruptionBlackBox::CCBBRecord::CCBBRecord(uint32 nStartPos, uint32 nEndPos, uint32 dwIP)
44 if (nStartPos > nEndPos) {
45 wxASSERT( false );
46 return;
48 m_nStartPos = nStartPos;
49 m_nEndPos = nEndPos;
50 m_dwIP = dwIP;
53 // Try to merge new data to an existing record
54 bool CCorruptionBlackBox::CCBBRecord::Merge(uint32 nStartPos, uint32 nEndPos, uint32 dwIP)
56 if (m_dwIP == dwIP) {
57 if (nStartPos == m_nEndPos + 1) {
58 m_nEndPos = nEndPos;
59 return true;
60 } else if (nEndPos + 1 == m_nStartPos) {
61 m_nStartPos = nStartPos;
62 return true;
65 return false;
68 // Release memory (after download completes)
69 void CCorruptionBlackBox::Free()
71 m_Records.clear();
72 m_goodClients.clear();
73 m_badClients.clear();
76 // Store a piece of received data (don't know if it's good or bad yet).
77 // Data is stored in a list for the chunk in belongs to.
78 void CCorruptionBlackBox::TransferredData(uint64 nStartPos, uint64 nEndPos, uint32 senderIP)
80 if (nStartPos > nEndPos) {
81 wxASSERT( false );
82 return;
85 // convert pos to relative block pos
86 uint16 nPart = (uint16)(nStartPos / PARTSIZE);
87 uint32 nRelStartPos = nStartPos - nPart*PARTSIZE;
88 uint32 nRelEndPos = nEndPos - nPart*PARTSIZE;
89 if (nRelEndPos >= PARTSIZE) {
90 // data crosses the partborder, split it
91 // (for the fun of it, this should never happen)
92 nRelEndPos = PARTSIZE-1;
93 TransferredData((nPart+1)*PARTSIZE, nEndPos, senderIP);
96 // Let's keep things simple.
97 // We don't request data we already have.
98 // We check if received data exceeds block boundaries.
99 // -> There should not be much overlap here.
100 // So just stuff everything received into the list and only join adjacent blocks.
102 CRecordList & list = m_Records[nPart]; // this creates the entry if it doesn't exist yet
103 bool merged = false;
104 for (CRecordList::iterator it = list.begin(); it != list.end() && !merged; ++it) {
105 merged = it->Merge(nRelStartPos, nRelEndPos, senderIP);
107 if (!merged) {
108 list.push_back(CCBBRecord(nRelStartPos, nRelEndPos, senderIP));
109 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): transferred: new record for part %d (%d - %d, %s)"))
110 % m_partNumber % nPart % nRelStartPos % nRelEndPos % Uint32toStringIP(senderIP));
114 // Mark a piece of data as good or bad.
115 // Piece is removed from the chunk list and added to the client's record.
116 void CCorruptionBlackBox::VerifiedData(bool ok, uint16 nPart, uint32 nRelStartPos, uint32 nRelEndPos) {
117 if (nRelStartPos > nRelEndPos) {
118 wxASSERT( false );
119 return;
122 CRecordList & list = m_Records[nPart];
123 #ifdef __DEBUG__
124 std::map<uint32, bool> mapDebug;
125 uint32 nDbgVerifiedBytes = 0;
126 size_t listsize1 = list.size();
127 #endif
128 for (CRecordList::iterator it1 = list.begin(); it1 != list.end();) {
129 CRecordList::iterator it = it1++;
130 uint32 & curStart = it->m_nStartPos;
131 uint32 & curEnd = it->m_nEndPos;
132 uint32 ip = it->m_dwIP;
133 uint32 data = 0;
134 if (curStart >= nRelStartPos && curStart <= nRelEndPos) {
135 // [arg
136 // [cur
137 if (curEnd > nRelEndPos) {
138 // [arg]
139 // [cur]
140 data = nRelEndPos - curStart + 1;
141 curStart = nRelEndPos + 1;
142 } else {
143 // [arg ]
144 // [cur]
145 data = curEnd - curStart + 1;
146 list.erase(it);
148 } else if (curStart < nRelStartPos && curEnd >= nRelStartPos) {
149 // [arg
150 // [cur
151 if (curEnd > nRelEndPos) {
152 // [arg]
153 // [cur ]
154 data = nRelEndPos - nRelStartPos + 1;
155 // split it: insert new block before current block
156 list.insert(it, CCBBRecord(curStart, nRelStartPos - 1, ip));
157 curStart = nRelEndPos + 1;
158 } else {
159 // [arg]
160 // [cur]
161 data = curEnd - nRelStartPos + 1;
162 curEnd = nRelStartPos - 1;
164 // else no overlap
166 if (data) {
167 if (ok) {
168 m_goodClients[ip].m_downloaded += data;
169 } else {
170 // corrupted data records are always counted as at least blocksize or bigger
171 m_badClients[ip].m_downloaded += (data > EMBLOCKSIZE) ? data : EMBLOCKSIZE;;
173 DEBUG_ONLY(nDbgVerifiedBytes += data);
174 DEBUG_ONLY(mapDebug[ip] = 1);
177 DEBUG_ONLY(size_t listsize2 = list.size());
178 // when everything is added to the stats drop the whole record
179 if (list.empty()) {
180 m_Records.erase(nPart);
182 #ifdef __DEBUG__
183 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"))
184 % m_partNumber % nDbgVerifiedBytes % (nRelEndPos-nRelStartPos+1) % (ok ? wxT("verified") : wxT("corrupt"))
185 % nPart % listsize1 % listsize2 % mapDebug.size());
186 #endif
189 // Check all clients that uploaded corrupted data,
190 // and ban them if they didn't upload enough good data too.
191 void CCorruptionBlackBox::EvaluateData()
193 CCBBClientMap::iterator it = m_badClients.begin();
194 for (; it != m_badClients.end(); ++it) {
195 uint32 ip = it->first;
196 uint64 bad = it->second.m_downloaded;
197 if (!bad) {
198 wxFAIL; // this should not happen
199 continue;
201 uint64 good = 0;
202 CCBBClientMap::iterator it2 = m_goodClients.find(ip);
203 if (it2 != m_goodClients.end()) {
204 good = it2->second.m_downloaded;
207 int nCorruptPercentage = bad * 100 / (bad + good);
209 if (nCorruptPercentage > CBB_BANTHRESHOLD) {
210 CUpDownClient* pEvilClient = theApp->clientlist->FindClientByIP(ip);
211 wxString clientName;
212 if (pEvilClient != NULL) {
213 clientName = pEvilClient->GetClientShortInfo();
214 AddDebugLogLineN(logPartFile, CFormat(wxT("CorruptionBlackBox(%s): Banning: Found client which sent %d of %d corrupted data, %s"))
215 % m_partNumber % bad % (good + bad) % pEvilClient->GetClientFullInfo());
216 theApp->clientlist->AddTrackClient(pEvilClient);
217 pEvilClient->Ban(); // Identified as sender of corrupt data
218 // Stop download right away
219 pEvilClient->SetDownloadState(DS_BANNED);
220 if (pEvilClient->Disconnected(wxT("Upload of corrupted data"))) {
221 pEvilClient->Safe_Delete();
223 } else {
224 clientName = Uint32toStringIP(ip);
225 theApp->clientlist->AddBannedClient(ip);
227 AddLogLineN(CFormat(_("Banned client %s for sending %s corrupt data of %s total for the file '%s'"))
228 % clientName % CastItoXBytes(bad) % CastItoXBytes(good + bad) % m_fileName);
229 } else {
230 CUpDownClient* pSuspectClient = theApp->clientlist->FindClientByIP(ip);
231 if (pSuspectClient != NULL) {
232 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"))
233 % m_partNumber % bad % (good + bad) % pSuspectClient->GetClientFullInfo());
234 theApp->clientlist->AddTrackClient(pSuspectClient);
235 } else {
236 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"))
237 % m_partNumber % bad % (good + bad) % Uint32toStringIP(ip));
243 // Full debug output of all data
244 void CCorruptionBlackBox::DumpAll()
246 #ifdef __DEBUG__
247 AddDebugLogLineN(logPartFile, wxT("CBB Dump Records"));
248 std::map<uint16, CRecordList>::iterator it = m_Records.begin();
249 for (; it != m_Records.end(); ++it) {
250 uint16 block = it->first;
251 CRecordList & list = it->second;
252 for (CRecordList::iterator it2 = list.begin(); it2 != list.end(); ++it2) {
253 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %6d %.16s %10d - %10d"))
254 % block % Uint32toStringIP(it2->m_dwIP) % it2->m_nStartPos % it2->m_nEndPos);
257 if (!m_goodClients.empty()) {
258 AddDebugLogLineN(logPartFile, wxT("CBB Dump good Clients"));
259 CCBBClientMap::iterator it3 = m_goodClients.begin();
260 for (; it3 != m_goodClients.end(); ++it3) {
261 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %.16s good %10d"))
262 % Uint32toStringIP(it3->first) % it3->second.m_downloaded);
265 if (!m_badClients.empty()) {
266 AddDebugLogLineN(logPartFile, wxT("CBB Dump bad Clients"));
267 CCBBClientMap::iterator it3 = m_badClients.begin();
268 for (; it3 != m_badClients.end(); ++it3) {
269 AddDebugLogLineN(logPartFile, CFormat(wxT("CBBD %.16s bad %10d"))
270 % Uint32toStringIP(it3->first) % it3->second.m_downloaded);
273 #endif