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