2007-12-18 07:04:54 +01:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2007-2016, Arvid Norberg
|
2007-12-18 07:04:54 +01:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2011-01-29 13:13:49 +01:00
|
|
|
#ifndef TORRENT_DISABLE_EXTENSIONS
|
|
|
|
|
2007-12-18 07:04:54 +01:00
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
|
|
|
#include <utility>
|
|
|
|
#include <numeric>
|
|
|
|
#include <cstdio>
|
2016-05-25 06:31:52 +02:00
|
|
|
#include <functional>
|
2007-12-18 07:04:54 +01:00
|
|
|
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/torrent.hpp"
|
2015-07-25 18:39:25 +02:00
|
|
|
#include "libtorrent/torrent_handle.hpp"
|
2007-12-18 07:04:54 +01:00
|
|
|
#include "libtorrent/extensions.hpp"
|
|
|
|
#include "libtorrent/extensions/smart_ban.hpp"
|
|
|
|
#include "libtorrent/disk_io_thread.hpp"
|
|
|
|
#include "libtorrent/aux_/session_impl.hpp"
|
2009-11-23 18:05:26 +01:00
|
|
|
#include "libtorrent/peer_connection.hpp"
|
2009-11-26 22:05:57 +01:00
|
|
|
#include "libtorrent/peer_info.hpp"
|
2011-02-26 08:55:51 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2015-02-15 06:17:09 +01:00
|
|
|
#include "libtorrent/operations.hpp" // for operation_t enum
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/socket_io.hpp"
|
2016-06-04 16:01:43 +02:00
|
|
|
#include "libtorrent/hex.hpp" // to_hex
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
using namespace std::placeholders;
|
|
|
|
|
2009-11-23 09:38:50 +01:00
|
|
|
namespace libtorrent {
|
|
|
|
|
2016-05-23 14:15:39 +02:00
|
|
|
class torrent;
|
2009-11-23 09:38:50 +01:00
|
|
|
|
|
|
|
namespace
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
|
|
|
|
2016-04-30 17:05:54 +02:00
|
|
|
struct smart_ban_plugin final
|
2015-11-20 05:37:45 +01:00
|
|
|
: torrent_plugin
|
2016-08-17 20:30:24 +02:00
|
|
|
, std::enable_shared_from_this<smart_ban_plugin>
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2016-07-10 20:27:42 +02:00
|
|
|
explicit smart_ban_plugin(torrent& t)
|
2007-12-18 07:04:54 +01:00
|
|
|
: m_torrent(t)
|
2016-08-06 19:18:48 +02:00
|
|
|
, m_salt(random(0xffffffff))
|
2016-05-23 14:15:39 +02:00
|
|
|
{}
|
2012-03-26 05:57:15 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void on_piece_pass(piece_index_t const p) override
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2014-07-06 21:18:00 +02:00
|
|
|
m_torrent.debug_log(" PIECE PASS [ p: %d | block_hash_size: %d ]"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(p), int(m_block_hashes.size()));
|
2007-12-18 07:04:54 +01:00
|
|
|
#endif
|
|
|
|
// has this piece failed earlier? If it has, go through the
|
|
|
|
// CRCs from the time it failed and ban the peers that
|
|
|
|
// sent bad blocks
|
2010-03-16 07:14:22 +01:00
|
|
|
std::map<piece_block, block_entry>::iterator i = m_block_hashes.lower_bound(piece_block(p, 0));
|
2016-08-21 01:46:55 +02:00
|
|
|
if (i == m_block_hashes.end() || i->first.piece_index != p) return;
|
2007-12-18 07:04:54 +01:00
|
|
|
|
|
|
|
int size = m_torrent.torrent_file().piece_size(p);
|
2016-08-21 01:46:55 +02:00
|
|
|
peer_request r = {p, 0, (std::min)(16 * 1024, size)};
|
2007-12-18 07:04:54 +01:00
|
|
|
piece_block pb(p, 0);
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
if (i->first.block_index == pb.block_index)
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
m_torrent.session().disk_thread().async_read(&m_torrent.storage()
|
2016-05-25 06:31:52 +02:00
|
|
|
, r, std::bind(&smart_ban_plugin::on_read_ok_block
|
2016-12-01 18:01:15 +01:00
|
|
|
, shared_from_this(), *i, i->second.peer->address(), _1, _2, r.length, _3, _4)
|
2015-08-11 02:03:24 +02:00
|
|
|
, reinterpret_cast<void*>(1));
|
2010-03-16 07:14:22 +01:00
|
|
|
m_block_hashes.erase(i++);
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(i->first.block_index > pb.block_index);
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
if (i == m_block_hashes.end() || i->first.piece_index != p)
|
2007-12-18 07:04:54 +01:00
|
|
|
break;
|
|
|
|
|
2016-08-21 01:46:55 +02:00
|
|
|
r.start += 16 * 1024;
|
|
|
|
size -= 16 * 1024;
|
|
|
|
r.length = (std::min)(16 * 1024, size);
|
2007-12-18 07:04:54 +01:00
|
|
|
++pb.block_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// make sure we actually removed all the entries for piece 'p'
|
2010-03-16 07:14:22 +01:00
|
|
|
i = m_block_hashes.lower_bound(piece_block(p, 0));
|
2016-12-22 16:42:33 +01:00
|
|
|
TORRENT_ASSERT(i == m_block_hashes.end() || i->first.piece_index != p);
|
2007-12-18 07:04:54 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (m_torrent.is_seed())
|
|
|
|
{
|
2010-03-16 07:14:22 +01:00
|
|
|
std::map<piece_block, block_entry>().swap(m_block_hashes);
|
2007-12-18 07:04:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void on_piece_failed(piece_index_t p) override
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
|
|
|
// The piece failed the hash check. Record
|
|
|
|
// the CRC and origin peer of every block
|
|
|
|
|
2008-06-22 22:30:43 +02:00
|
|
|
// if the torrent is aborted, no point in starting
|
|
|
|
// a bunch of read operations on it
|
|
|
|
if (m_torrent.is_aborted()) return;
|
|
|
|
|
2015-08-18 10:25:13 +02:00
|
|
|
std::vector<torrent_peer*> downloaders;
|
2007-12-18 07:04:54 +01:00
|
|
|
m_torrent.picker().get_downloaders(downloaders, p);
|
|
|
|
|
|
|
|
int size = m_torrent.torrent_file().piece_size(p);
|
2008-01-02 18:12:33 +01:00
|
|
|
peer_request r = {p, 0, (std::min)(16*1024, size)};
|
2007-12-18 07:04:54 +01:00
|
|
|
piece_block pb(p, 0);
|
2015-08-18 10:25:13 +02:00
|
|
|
for (std::vector<torrent_peer*>::iterator i = downloaders.begin()
|
2007-12-18 07:04:54 +01:00
|
|
|
, end(downloaders.end()); i != end; ++i)
|
|
|
|
{
|
2016-06-20 17:32:06 +02:00
|
|
|
if (*i != nullptr)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// for very sad and involved reasons, this read need to force a copy out of the cache
|
|
|
|
// since the piece has failed, this block is very likely to be replaced with a newly
|
|
|
|
// downloaded one very soon, and to get a block by reference would fail, since the
|
|
|
|
// block read will have been deleted by the time it gets back to the network thread
|
|
|
|
m_torrent.session().disk_thread().async_read(&m_torrent.storage(), r
|
2016-05-25 06:31:52 +02:00
|
|
|
, std::bind(&smart_ban_plugin::on_read_failed_block
|
2016-12-01 18:01:15 +01:00
|
|
|
, shared_from_this(), pb, (*i)->address(), _1, _2, r.length, _3, _4)
|
2015-08-18 10:25:13 +02:00
|
|
|
, reinterpret_cast<torrent_peer*>(1)
|
2014-07-06 21:18:00 +02:00
|
|
|
, disk_io_job::force_copy);
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
r.start += 16*1024;
|
|
|
|
size -= 16*1024;
|
2008-01-02 18:12:33 +01:00
|
|
|
r.length = (std::min)(16*1024, size);
|
2007-12-18 07:04:54 +01:00
|
|
|
++pb.block_index;
|
|
|
|
}
|
|
|
|
TORRENT_ASSERT(size <= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
// this entry ties a specific block CRC to
|
|
|
|
// a peer.
|
|
|
|
struct block_entry
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_peer* peer;
|
2010-03-16 07:14:22 +01:00
|
|
|
sha1_hash digest;
|
2007-12-18 07:04:54 +01:00
|
|
|
};
|
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
void on_read_failed_block(piece_block b, address a
|
2016-12-01 18:01:15 +01:00
|
|
|
, aux::block_cache_reference ref, char* disk_block, int const block_size, int
|
2016-11-21 05:58:48 +01:00
|
|
|
, storage_error const& error)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(m_torrent.session().is_single_thread());
|
2015-08-11 02:03:24 +02:00
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
disk_buffer_holder buffer(m_torrent.session(), ref, disk_block);
|
2009-09-14 19:07:39 +02:00
|
|
|
|
2007-12-18 07:04:54 +01:00
|
|
|
// ignore read errors
|
2016-11-21 05:58:48 +01:00
|
|
|
if (error) return;
|
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
hasher h;
|
2016-12-01 18:01:15 +01:00
|
|
|
h.update({disk_block, std::size_t(block_size)});
|
2015-08-11 02:03:24 +02:00
|
|
|
h.update(reinterpret_cast<char const*>(&m_salt), sizeof(m_salt));
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2016-07-24 00:57:04 +02:00
|
|
|
std::pair<peer_list::iterator, peer_list::iterator> const range
|
2014-07-06 21:18:00 +02:00
|
|
|
= m_torrent.find_peers(a);
|
2009-02-25 06:53:24 +01:00
|
|
|
|
|
|
|
// there is no peer with this address anymore
|
|
|
|
if (range.first == range.second) return;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_peer* p = (*range.first);
|
2010-03-16 07:14:22 +01:00
|
|
|
block_entry e = {p, h.final()};
|
2009-02-25 06:53:24 +01:00
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
std::map<piece_block, block_entry>::iterator i = m_block_hashes.lower_bound(b);
|
2009-02-25 06:53:24 +01:00
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
if (i != m_block_hashes.end() && i->first == b && i->second.peer == p)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
|
|
|
// this peer has sent us this block before
|
2014-07-06 21:18:00 +02:00
|
|
|
// if the peer is already banned, it doesn't matter if it sent
|
|
|
|
// good or bad data. Nothings going to change it
|
|
|
|
if (!p->banned && i->second.digest != e.digest)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2010-03-16 07:14:22 +01:00
|
|
|
// this time the digest of the block is different
|
2007-12-18 07:04:54 +01:00
|
|
|
// from the first time it sent it
|
|
|
|
// at least one of them must be bad
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_torrent.should_log())
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
char const* client = "-";
|
|
|
|
peer_info info;
|
|
|
|
if (p->connection)
|
|
|
|
{
|
|
|
|
p->connection->get_peer_info(info);
|
|
|
|
client = info.client.c_str();
|
|
|
|
}
|
|
|
|
m_torrent.debug_log(" BANNING PEER [ p: %d | b: %d | c: %s"
|
|
|
|
" | hash1: %s | hash2: %s | ip: %s ]"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(b.piece_index), b.block_index, client
|
2016-09-14 04:46:07 +02:00
|
|
|
, aux::to_hex(i->second.digest).c_str()
|
|
|
|
, aux::to_hex(e.digest).c_str()
|
|
|
|
, print_endpoint(p->ip()).c_str());
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
m_torrent.ban_peer(p);
|
2009-06-12 18:40:38 +02:00
|
|
|
if (p->connection) p->connection->disconnect(
|
2015-02-15 06:17:09 +01:00
|
|
|
errors::peer_banned, op_bittorrent);
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
// we already have this exact entry in the map
|
|
|
|
// we don't have to insert it
|
|
|
|
return;
|
|
|
|
}
|
2016-05-23 14:15:39 +02:00
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
m_block_hashes.insert(i, std::pair<const piece_block, block_entry>(b, e));
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_torrent.should_log())
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
char const* client = "-";
|
|
|
|
peer_info info;
|
|
|
|
if (p->connection)
|
|
|
|
{
|
|
|
|
p->connection->get_peer_info(info);
|
|
|
|
client = info.client.c_str();
|
|
|
|
}
|
|
|
|
m_torrent.debug_log(" STORE BLOCK CRC [ p: %d | b: %d | c: %s"
|
|
|
|
" | digest: %s | ip: %s ]"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(b.piece_index), b.block_index, client
|
2016-09-14 04:46:07 +02:00
|
|
|
, aux::to_hex(e.digest).c_str()
|
|
|
|
, print_address(p->ip().address()).c_str());
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2015-11-20 05:37:45 +01:00
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
void on_read_ok_block(std::pair<piece_block, block_entry> b, address a
|
2016-12-01 18:01:15 +01:00
|
|
|
, aux::block_cache_reference ref, char* disk_block, int const block_size, int
|
2016-11-21 05:58:48 +01:00
|
|
|
, storage_error const& error)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(m_torrent.session().is_single_thread());
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2016-11-21 05:58:48 +01:00
|
|
|
disk_buffer_holder buffer(m_torrent.session(), ref, disk_block);
|
2009-09-14 19:07:39 +02:00
|
|
|
|
2007-12-18 07:04:54 +01:00
|
|
|
// ignore read errors
|
2016-11-21 05:58:48 +01:00
|
|
|
if (error) return;
|
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
hasher h;
|
2016-12-01 18:01:15 +01:00
|
|
|
h.update({disk_block, std::size_t(block_size)});
|
2015-08-11 02:03:24 +02:00
|
|
|
h.update(reinterpret_cast<char const*>(&m_salt), sizeof(m_salt));
|
2016-07-24 00:57:04 +02:00
|
|
|
sha1_hash const ok_digest = h.final();
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2010-03-16 07:14:22 +01:00
|
|
|
if (b.second.digest == ok_digest) return;
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// find the peer
|
2014-10-26 08:34:31 +01:00
|
|
|
std::pair<peer_list::iterator, peer_list::iterator> range
|
2014-07-06 21:18:00 +02:00
|
|
|
= m_torrent.find_peers(a);
|
|
|
|
if (range.first == range.second) return;
|
2016-06-20 17:32:06 +02:00
|
|
|
torrent_peer* p = nullptr;
|
2014-07-06 21:18:00 +02:00
|
|
|
for (; range.first != range.second; ++range.first)
|
|
|
|
{
|
|
|
|
if (b.second.peer != *range.first) continue;
|
|
|
|
p = *range.first;
|
|
|
|
}
|
2016-06-20 17:32:06 +02:00
|
|
|
if (p == nullptr) return;
|
2007-12-18 07:04:54 +01:00
|
|
|
|
2015-04-17 03:15:33 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-14 04:46:07 +02:00
|
|
|
if (m_torrent.should_log())
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2016-09-14 04:46:07 +02:00
|
|
|
char const* client = "-";
|
|
|
|
peer_info info;
|
|
|
|
if (p->connection)
|
|
|
|
{
|
|
|
|
p->connection->get_peer_info(info);
|
|
|
|
client = info.client.c_str();
|
|
|
|
}
|
|
|
|
m_torrent.debug_log(" BANNING PEER [ p: %d | b: %d | c: %s"
|
|
|
|
" | ok_digest: %s | bad_digest: %s | ip: %s ]"
|
2016-12-22 16:42:33 +01:00
|
|
|
, static_cast<int>(b.first.piece_index), b.first.block_index, client
|
2016-09-14 04:46:07 +02:00
|
|
|
, aux::to_hex(ok_digest).c_str()
|
|
|
|
, aux::to_hex(b.second.digest).c_str()
|
|
|
|
, print_address(p->ip().address()).c_str());
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
m_torrent.ban_peer(p);
|
2009-06-12 18:40:38 +02:00
|
|
|
if (p->connection) p->connection->disconnect(
|
2015-02-15 06:17:09 +01:00
|
|
|
errors::peer_banned, op_bittorrent);
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
2015-11-20 05:37:45 +01:00
|
|
|
|
2007-12-18 07:04:54 +01:00
|
|
|
torrent& m_torrent;
|
|
|
|
|
|
|
|
// This table maps a piece_block (piece and block index
|
|
|
|
// pair) to a peer and the block CRC. The CRC is calculated
|
|
|
|
// from the data in the block + the salt
|
2010-03-16 07:14:22 +01:00
|
|
|
std::map<piece_block, block_entry> m_block_hashes;
|
2007-12-18 07:04:54 +01:00
|
|
|
|
|
|
|
// This salt is a random value used to calculate the block CRCs
|
|
|
|
// Since the CRC function that is used is not a one way function
|
|
|
|
// the salt is required to avoid attacks where bad data is sent
|
|
|
|
// that is forged to match the CRC of the good data.
|
|
|
|
int m_salt;
|
2012-03-26 05:57:15 +02:00
|
|
|
|
2015-11-20 05:37:45 +01:00
|
|
|
// explicitly disallow assignment, to silence msvc warning
|
|
|
|
smart_ban_plugin& operator=(smart_ban_plugin const&);
|
2007-12-18 07:04:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
} }
|
|
|
|
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
2016-08-17 20:30:24 +02:00
|
|
|
std::shared_ptr<torrent_plugin> create_smart_ban_plugin(torrent_handle const& th, void*)
|
2007-12-18 07:04:54 +01:00
|
|
|
{
|
2015-07-22 04:11:41 +02:00
|
|
|
torrent* t = th.native_handle().get();
|
2016-08-17 20:30:24 +02:00
|
|
|
return std::make_shared<smart_ban_plugin>(*t);
|
2007-12-18 07:04:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-29 13:13:49 +01:00
|
|
|
#endif
|