fix piece_picker piece-shuffle bug
[libtorrent.git] / src / smart_ban.cpp
blob759377ed835a5d29888cdd24ff9d40d9be5f58db
1 /*
3 Copyright (c) 2007, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
35 #ifdef _MSC_VER
36 #pragma warning(push, 1)
37 #endif
39 #include <boost/shared_ptr.hpp>
40 #include <boost/enable_shared_from_this.hpp>
41 #include <boost/lexical_cast.hpp>
43 #ifdef _MSC_VER
44 #pragma warning(pop)
45 #endif
47 #include <vector>
48 #include <map>
49 #include <utility>
50 #include <numeric>
51 #include <cstdio>
53 #include "libtorrent/peer_connection.hpp"
54 #include "libtorrent/bt_peer_connection.hpp"
55 #include "libtorrent/hasher.hpp"
56 #include "libtorrent/bencode.hpp"
57 #include "libtorrent/torrent.hpp"
58 #include "libtorrent/extensions.hpp"
59 #include "libtorrent/extensions/smart_ban.hpp"
60 #include "libtorrent/alert_types.hpp"
61 #include "libtorrent/disk_io_thread.hpp"
62 #include "libtorrent/aux_/session_impl.hpp"
64 namespace libtorrent { namespace
67 struct smart_ban_plugin : torrent_plugin, boost::enable_shared_from_this<smart_ban_plugin>
69 smart_ban_plugin(torrent& t)
70 : m_torrent(t)
71 , m_salt(rand())
75 void on_piece_pass(int p)
77 #ifdef TORRENT_LOGGING
78 (*m_torrent.session().m_logger) << time_now_string() << " PIECE PASS [ p: " << p
79 << " | block_crc_size: " << m_block_crc.size() << " ]\n";
80 #endif
81 // has this piece failed earlier? If it has, go through the
82 // CRCs from the time it failed and ban the peers that
83 // sent bad blocks
84 std::map<piece_block, block_entry>::iterator i = m_block_crc.lower_bound(piece_block(p, 0));
85 if (i == m_block_crc.end() || i->first.piece_index != p) return;
87 int size = m_torrent.torrent_file().piece_size(p);
88 peer_request r = {p, 0, (std::min)(16*1024, size)};
89 piece_block pb(p, 0);
90 while (size > 0)
92 if (i->first.block_index == pb.block_index)
94 m_torrent.filesystem().async_read(r, bind(&smart_ban_plugin::on_read_ok_block
95 , shared_from_this(), *i, _1, _2));
96 m_block_crc.erase(i++);
98 else
100 TORRENT_ASSERT(i->first.block_index > pb.block_index);
103 if (i == m_block_crc.end() || i->first.piece_index != p)
104 break;
106 r.start += 16*1024;
107 size -= 16*1024;
108 r.length = (std::min)(16*1024, size);
109 ++pb.block_index;
112 #ifndef NDEBUG
113 // make sure we actually removed all the entries for piece 'p'
114 i = m_block_crc.lower_bound(piece_block(p, 0));
115 TORRENT_ASSERT(i == m_block_crc.end() || i->first.piece_index != p);
116 #endif
118 if (m_torrent.is_seed())
120 std::map<piece_block, block_entry>().swap(m_block_crc);
121 return;
125 void on_piece_failed(int p)
127 // The piece failed the hash check. Record
128 // the CRC and origin peer of every block
130 // if the torrent is aborted, no point in starting
131 // a bunch of read operations on it
132 if (m_torrent.is_aborted()) return;
134 std::vector<void*> downloaders;
135 m_torrent.picker().get_downloaders(downloaders, p);
137 int size = m_torrent.torrent_file().piece_size(p);
138 peer_request r = {p, 0, (std::min)(16*1024, size)};
139 piece_block pb(p, 0);
140 for (std::vector<void*>::iterator i = downloaders.begin()
141 , end(downloaders.end()); i != end; ++i)
143 if (*i != 0)
145 m_torrent.filesystem().async_read(r, bind(&smart_ban_plugin::on_read_failed_block
146 , shared_from_this(), pb, (policy::peer*)*i, _1, _2));
149 r.start += 16*1024;
150 size -= 16*1024;
151 r.length = (std::min)(16*1024, size);
152 ++pb.block_index;
154 TORRENT_ASSERT(size <= 0);
157 private:
159 // this entry ties a specific block CRC to
160 // a peer.
161 struct block_entry
163 policy::peer* peer;
164 unsigned long crc;
167 void on_read_failed_block(piece_block b, policy::peer* p, int ret, disk_io_job const& j)
169 TORRENT_ASSERT(p);
170 // ignore read errors
171 if (ret != j.buffer_size) return;
173 adler32_crc crc;
174 crc.update(j.buffer, j.buffer_size);
175 crc.update((char const*)&m_salt, sizeof(m_salt));
177 block_entry e = {p, crc.final()};
179 // since this callback is called directory from the disk io
180 // thread, the session mutex is not locked when we get here
181 aux::session_impl::mutex_t::scoped_lock l(m_torrent.session().m_mutex);
183 std::map<piece_block, block_entry>::iterator i = m_block_crc.lower_bound(b);
184 if (i != m_block_crc.end() && i->first == b && i->second.peer == p)
186 // this peer has sent us this block before
187 if (i->second.crc != e.crc)
189 // this time the crc of the block is different
190 // from the first time it sent it
191 // at least one of them must be bad
193 if (p == 0) return;
194 if (!m_torrent.get_policy().has_peer(p)) return;
196 #ifdef TORRENT_LOGGING
197 char const* client = "-";
198 peer_info info;
199 if (p->connection)
201 p->connection->get_peer_info(info);
202 client = info.client.c_str();
204 (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.piece_index
205 << " | b: " << b.block_index
206 << " | c: " << client
207 << " | crc1: " << i->second.crc
208 << " | crc2: " << e.crc
209 << " | ip: " << p->ip() << " ]\n";
210 #endif
211 p->banned = true;
212 if (p->connection) p->connection->disconnect("banning peer for sending bad data");
214 // we already have this exact entry in the map
215 // we don't have to insert it
216 return;
219 m_block_crc.insert(i, std::make_pair(b, e));
221 #ifdef TORRENT_LOGGING
222 char const* client = "-";
223 peer_info info;
224 if (p->connection)
226 p->connection->get_peer_info(info);
227 client = info.client.c_str();
229 (*m_torrent.session().m_logger) << time_now_string() << " STORE BLOCK CRC [ p: " << b.piece_index
230 << " | b: " << b.block_index
231 << " | c: " << client
232 << " | crc: " << e.crc
233 << " | ip: " << p->ip() << " ]\n";
234 #endif
237 void on_read_ok_block(std::pair<piece_block, block_entry> b, int ret, disk_io_job const& j)
239 // since this callback is called directory from the disk io
240 // thread, the session mutex is not locked when we get here
241 aux::session_impl::mutex_t::scoped_lock l(m_torrent.session().m_mutex);
243 // ignore read errors
244 if (ret != j.buffer_size) return;
246 adler32_crc crc;
247 crc.update(j.buffer, j.buffer_size);
248 crc.update((char const*)&m_salt, sizeof(m_salt));
249 unsigned long ok_crc = crc.final();
251 if (b.second.crc == ok_crc) return;
253 policy::peer* p = b.second.peer;
255 if (p == 0) return;
256 if (!m_torrent.get_policy().has_peer(p)) return;
258 #ifdef TORRENT_LOGGING
259 char const* client = "-";
260 peer_info info;
261 if (p->connection)
263 p->connection->get_peer_info(info);
264 client = info.client.c_str();
266 (*m_torrent.session().m_logger) << time_now_string() << " BANNING PEER [ p: " << b.first.piece_index
267 << " | b: " << b.first.block_index
268 << " | c: " << client
269 << " | ok_crc: " << ok_crc
270 << " | bad_crc: " << b.second.crc
271 << " | ip: " << p->ip() << " ]\n";
272 #endif
273 p->banned = true;
274 if (p->connection) p->connection->disconnect("banning peer for sending bad data");
277 torrent& m_torrent;
279 // This table maps a piece_block (piece and block index
280 // pair) to a peer and the block CRC. The CRC is calculated
281 // from the data in the block + the salt
282 std::map<piece_block, block_entry> m_block_crc;
284 // This salt is a random value used to calculate the block CRCs
285 // Since the CRC function that is used is not a one way function
286 // the salt is required to avoid attacks where bad data is sent
287 // that is forged to match the CRC of the good data.
288 int m_salt;
293 namespace libtorrent
296 boost::shared_ptr<torrent_plugin> create_smart_ban_plugin(torrent* t, void*)
298 return boost::shared_ptr<torrent_plugin>(new smart_ban_plugin(*t));