3 Copyright (c) 2007, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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"
36 #pragma warning(push, 1)
39 #include <boost/shared_ptr.hpp>
40 #include <boost/enable_shared_from_this.hpp>
41 #include <boost/lexical_cast.hpp>
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
)
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";
81 // has this piece failed earlier? If it has, go through the
82 // CRCs from the time it failed and ban the peers that
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
)};
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
++);
100 TORRENT_ASSERT(i
->first
.block_index
> pb
.block_index
);
103 if (i
== m_block_crc
.end() || i
->first
.piece_index
!= p
)
108 r
.length
= (std::min
)(16*1024, size
);
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
);
118 if (m_torrent
.is_seed())
120 std::map
<piece_block
, block_entry
>().swap(m_block_crc
);
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
)
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
));
151 r
.length
= (std::min
)(16*1024, size
);
154 TORRENT_ASSERT(size
<= 0);
159 // this entry ties a specific block CRC to
167 void on_read_failed_block(piece_block b
, policy::peer
* p
, int ret
, disk_io_job
const& j
)
170 // ignore read errors
171 if (ret
!= j
.buffer_size
) return;
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
194 if (!m_torrent
.get_policy().has_peer(p
)) return;
196 #ifdef TORRENT_LOGGING
197 char const* client
= "-";
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";
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
219 m_block_crc
.insert(i
, std::make_pair(b
, e
));
221 #ifdef TORRENT_LOGGING
222 char const* client
= "-";
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";
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;
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
;
256 if (!m_torrent
.get_policy().has_peer(p
)) return;
258 #ifdef TORRENT_LOGGING
259 char const* client
= "-";
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";
274 if (p
->connection
) p
->connection
->disconnect("banning peer for sending bad data");
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.
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
));