2 // This file is part of the aMule Project.
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 )
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
27 #include "CorruptionBlackBox.h"
28 #include <protocol/ed2k/Constants.h> // needed for PARTSIZE
29 #include "updownclient.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
) {
48 m_nStartPos
= nStartPos
;
53 // Try to merge new data to an existing record
54 bool CCorruptionBlackBox::CCBBRecord::Merge(uint32 nStartPos
, uint32 nEndPos
, uint32 dwIP
)
57 if (nStartPos
== m_nEndPos
+ 1) {
60 } else if (nEndPos
+ 1 == m_nStartPos
) {
61 m_nStartPos
= nStartPos
;
68 // Release memory (after download completes)
69 void CCorruptionBlackBox::Free()
72 m_goodClients
.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
) {
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
104 for (CRecordList::iterator it
= list
.begin(); it
!= list
.end() && !merged
; ++it
) {
105 merged
= it
->Merge(nRelStartPos
, nRelEndPos
, senderIP
);
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
) {
122 CRecordList
& list
= m_Records
[nPart
];
124 std::map
<uint32
, bool> mapDebug
;
125 uint32 nDbgVerifiedBytes
= 0;
126 size_t listsize1
= list
.size();
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
;
134 if (curStart
>= nRelStartPos
&& curStart
<= nRelEndPos
) {
137 if (curEnd
> nRelEndPos
) {
140 data
= nRelEndPos
- curStart
+ 1;
141 curStart
= nRelEndPos
+ 1;
145 data
= curEnd
- curStart
+ 1;
148 } else if (curStart
< nRelStartPos
&& curEnd
>= nRelStartPos
) {
151 if (curEnd
> nRelEndPos
) {
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;
161 data
= curEnd
- nRelStartPos
+ 1;
162 curEnd
= nRelStartPos
- 1;
168 m_goodClients
[ip
].m_downloaded
+= data
;
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
180 m_Records
.erase(nPart
);
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());
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
;
198 wxFAIL
; // this should not happen
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
);
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();
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
);
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
);
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()
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
);