completed IPv6 support in ip_filter and updated test_ip_filter and documentation. Documented recently added extensions to DHT.

This commit is contained in:
Arvid Norberg 2006-09-23 21:24:28 +00:00
parent a42189af62
commit fe0d570f05
16 changed files with 473 additions and 221 deletions

View File

@ -1,3 +1,6 @@
* added an extension to the DHT network protocol to support the
exchange of nodes with IPv6 addresses.
* modified the ip_filter api slightly to support IPv6
* modified the api slightly to make sequenced download threshold
a per torrent-setting.
* changed the address type to support IPv6

View File

@ -242,6 +242,14 @@ logging=none dht-support=on dht-support=logging dht-support=off
<h2><a name="building-with-autotools">building with autotools</a></h2>
<p>First of all, you need to install <tt class="docutils literal"><span class="pre">automake</span></tt> and <tt class="docutils literal"><span class="pre">autoconf</span></tt>. Many
unix/linux systems comes with these preinstalled.</p>
<p>The prerequisites for building libtorrent is boost.thread, boost.date_time
and boost.filesystem. Those are the <em>compiled</em> boost libraries needed. The
headers-only libraries needed include (but is not necessarily limited to)
boost.bind, boost.ref, boost.multi_index, boost.optional, boost.lexical_cast,
boost.integer, boost.iterator, boost.tuple, boost.array, boost.function,
boost.smart_ptr, boost.preprocessor, boost.static_assert.</p>
<p>If you want to build the <tt class="docutils literal"><span class="pre">client_test</span></tt> example, you'll also need boost.regex
and boost.program_options.</p>
<div class="section" id="step-1-generating-the-build-system">
<h3><a name="step-1-generating-the-build-system">Step 1: Generating the build system</a></h3>
<p>No build system is present if libtorrent is checked out from CVS - it

View File

@ -232,6 +232,16 @@ building with autotools
First of all, you need to install ``automake`` and ``autoconf``. Many
unix/linux systems comes with these preinstalled.
The prerequisites for building libtorrent is boost.thread, boost.date_time
and boost.filesystem. Those are the *compiled* boost libraries needed. The
headers-only libraries needed include (but is not necessarily limited to)
boost.bind, boost.ref, boost.multi_index, boost.optional, boost.lexical_cast,
boost.integer, boost.iterator, boost.tuple, boost.array, boost.function,
boost.smart_ptr, boost.preprocessor, boost.static_assert.
If you want to build the ``client_test`` example, you'll also need boost.regex
and boost.program_options.
Step 1: Generating the build system
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

66
docs/dht_extensions.html Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.3.9: http://docutils.sourceforge.net/" />
<title></title>
<meta name="author" content="Arvid Norberg, arvid&#64;rasterbar.com" />
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<div class="document">
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr><th class="docinfo-name">Author:</th>
<td>Arvid Norberg, <a class="last reference" href="mailto:arvid&#64;rasterbar.com">arvid&#64;rasterbar.com</a></td></tr>
</tbody>
</table>
<div class="section" id="mainline-dht-extensions">
<h1><a name="mainline-dht-extensions">Mainline DHT extensions</a></h1>
<p>libtorrent implements a few extensions to the Mainline DHT protocol.</p>
<div class="section" id="client-identification">
<h2><a name="client-identification">client identification</a></h2>
<p>In each DHT packet, an extra key is inserted named &quot;v&quot;. This is a string
describing the client and version used. This can help alot when debugging
and finding errors in client implementations. The string is encoded as four
characters, two characters describing the client and two characters interpreted
as a binary number describing the client version.</p>
<p>Currently known clients:</p>
<table border="1" class="docutils">
<colgroup>
<col width="65%" />
<col width="35%" />
</colgroup>
<tbody valign="top">
<tr><td>uTorrent</td>
<td><tt class="docutils literal"><span class="pre">UT</span></tt></td>
</tr>
<tr><td>libtorrent</td>
<td><tt class="docutils literal"><span class="pre">LT</span></tt></td>
</tr>
<tr><td>MooPolice</td>
<td><tt class="docutils literal"><span class="pre">MP</span></tt></td>
</tr>
<tr><td>GetRight</td>
<td><tt class="docutils literal"><span class="pre">GR</span></tt></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="ipv6-support">
<h2><a name="ipv6-support">IPv6 support</a></h2>
<p>The only DHT messages that don't support IPv6 is the <tt class="docutils literal"><span class="pre">nodes</span></tt> reply. It
encodes all the contacts as 6 bytes sequences packed together in sequence in
string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but
needs 18 bytes. The extension libtorrent applies is to add another key, called
<tt class="docutils literal"><span class="pre">nodes2</span></tt> which is encoded as a list of strings. Each string represents one
contact and is encoded as 20 bytes node-id and then a variable length encoded
IP address (6 bytes in IPv4 case and 18 bytes in IPv6 case).</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -30,10 +30,11 @@ IPv6 support
------------
The only DHT messages that don't support IPv6 is the ``nodes`` reply. It
encodes all the contacts as 6 bytes sequences packet together in sequence in
string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but
18 bytes. The extension libtorrent applies is to add another key, called
encodes all the contacts as 6 bytes sequences packed together in sequence in
string. The problem is that IPv6 endpoints cannot be encoded as 6 bytes, but
needs 18 bytes. The extension libtorrent applies is to add another key, called
``nodes2`` which is encoded as a list of strings. Each string represents one
contact and is encoded as 20 bytes node-id and then a variable length encoded
IP address (6 bytes in IPv4 case and 18 bytes in IPv6 case).

View File

@ -34,7 +34,8 @@
<h1><a name="examples">examples</a></h1>
<p>Except for the example programs in this manual, there's also a bigger example
of a (little bit) more complete client, <tt class="docutils literal"><span class="pre">client_test</span></tt>. There are separate
instructions for how to use it <a class="reference" href="client_test.html">here</a> if you'd like to try it.</p>
instructions for how to use it <a class="reference" href="client_test.html">here</a> if you'd like to try it. Note that building
<tt class="docutils literal"><span class="pre">client_test</span></tt> also requires boost.regex and boost.program_options library.</p>
<div class="section" id="dump-torrent">
<h2><a name="dump-torrent">dump_torrent</a></h2>
<p>This is an example of a program that will take a torrent-file as a parameter and

View File

@ -13,7 +13,8 @@ examples
Except for the example programs in this manual, there's also a bigger example
of a (little bit) more complete client, ``client_test``. There are separate
instructions for how to use it here__ if you'd like to try it.
instructions for how to use it here__ if you'd like to try it. Note that building
``client_test`` also requires boost.regex and boost.program_options library.
__ client_test.html

View File

@ -47,44 +47,47 @@ example client.</p>
project (including this documentation). The current state includes the
following features:</p>
<ul class="simple">
<li>Trackerless torrents (using a kademlia DHT)</li>
<li>trackerless torrents (using the Mainline kademlia DHT protocol) with
some <a class="reference" href="dht_extensions.html">DHT extensions</a>.</li>
<li>support for IPv6</li>
<li>piece-wise, unordered, incremental file allocation</li>
<li>uses separate threads for checking files and for main downloader, with a
fool-proof thread-safe library interface. (i.e. There's no way for the
user to cause a deadlock). (see <a class="reference" href="manual.html#threads">threads</a>)</li>
<li>adjusts the length of the request queue depending on download rate.</li>
<li>multitracker extension support (as <a class="reference" href="http://home.elp.rr.com/tur/multitracker-spec.txt">specified by John Hoffman</a>)</li>
<li>supports files &gt; 2 gigabytes.</li>
<li>serves multiple torrents on a single port and in a single thread</li>
<li>gzipped tracker-responses</li>
<li>fast resume support, a way to get rid of the costly piece check at the
start of a resumed torrent. Saves the storage state, piece_picker state
as well as all local peers in a separate fast-resume file.</li>
<li><a class="reference" href="manual.html#http-seeding">HTTP seeding</a>, as <a class="reference" href="http://www.getright.com/seedtorrent.html">specified by Michael Burford of GetRight</a>.</li>
<li>piece picking on block-level (as opposed to piece-level).
This means it can download parts of the same piece from different peers.
It will also prefer to download whole pieces from single peers if the
download speed is high enough from that particular peer.</li>
<li>supports the <a class="reference" href="extension_protocol.html">udp-tracker protocol</a> by Olaf van der Spek.</li>
<li>queues torrents for file check, instead of checking all of them in parallel.</li>
<li>supports http proxies and proxy authentication</li>
<li>uses separate threads for checking files and for main downloader, with a
fool-proof thread-safe library interface. (i.e. There's no way for the
user to cause a deadlock). (see <a class="reference" href="manual.html#threads">threads</a>)</li>
<li>supports http proxies and basic proxy authentication</li>
<li>gzipped tracker-responses</li>
<li>can limit the upload and download bandwidth usage and the maximum number of
unchoked peers</li>
<li>piece-wise, unordered, incremental file allocation</li>
<li>implements fair trade. User settable trade-ratio, must at least be 1:1,
but one can choose to trade 1 for 2 or any other ratio that isn't unfair
to the other party.</li>
<li>fast resume support, a way to get rid of the costly piece check at the
start of a resumed torrent. Saves the storage state, piece_picker state
as well as all local peers in a separate fast-resume file.</li>
<li>supports an <a class="reference" href="extension_protocol.html">extension protocol</a>. See <a class="reference" href="manual.html#extensions">extensions</a>.</li>
<li>supports files &gt; 2 gigabytes.</li>
<li>supports an <a class="reference" href="udp_tracker_protocol.html">extension protocol</a>. See <a class="reference" href="manual.html#extensions">extensions</a>.</li>
<li>supports the <tt class="docutils literal"><span class="pre">no_peer_id=1</span></tt> extension that will ease the load off trackers.</li>
<li>supports the <a class="reference" href="udp_tracker_protocol.html">udp-tracker protocol</a> by Olaf van der Spek.</li>
<li>possibility to limit the number of connections.</li>
<li>delays have messages if there's no other outgoing traffic to the peer, and
doesn't send have messages to peers that already has the piece. This saves
bandwidth.</li>
<li>does not have any requirements on the piece order in a torrent that it
resumes. This means it can resume a torrent downloaded by any client.</li>
<li>adjusts the length of the request queue depending on download rate.</li>
<li>supports the <tt class="docutils literal"><span class="pre">compact=1</span></tt> tracker parameter.</li>
<li>selective downloading. The ability to select which parts of a torrent you
want to download.</li>
<li>ip filter</li>
<li>ip filter to disallow ip addresses and ip ranges from connecting and
being connected</li>
</ul>
</div>
<div class="section" id="portability">

View File

@ -29,45 +29,49 @@ libtorrent is still being developed, however it is stable. It is an ongoing
project (including this documentation). The current state includes the
following features:
* Trackerless torrents (using a kademlia DHT)
* trackerless torrents (using the Mainline kademlia DHT protocol) with
some `DHT extensions`_.
* support for IPv6
* piece-wise, unordered, incremental file allocation
* uses separate threads for checking files and for main downloader, with a
fool-proof thread-safe library interface. (i.e. There's no way for the
user to cause a deadlock). (see threads_)
* adjusts the length of the request queue depending on download rate.
* multitracker extension support (as `specified by John Hoffman`__)
* supports files > 2 gigabytes.
* serves multiple torrents on a single port and in a single thread
* gzipped tracker-responses
* fast resume support, a way to get rid of the costly piece check at the
start of a resumed torrent. Saves the storage state, piece_picker state
as well as all local peers in a separate fast-resume file.
* `HTTP seeding`_, as `specified by Michael Burford of GetRight`__.
* piece picking on block-level (as opposed to piece-level).
This means it can download parts of the same piece from different peers.
It will also prefer to download whole pieces from single peers if the
download speed is high enough from that particular peer.
* supports the `udp-tracker protocol`__ by Olaf van der Spek.
* queues torrents for file check, instead of checking all of them in parallel.
* supports http proxies and proxy authentication
* uses separate threads for checking files and for main downloader, with a
fool-proof thread-safe library interface. (i.e. There's no way for the
user to cause a deadlock). (see threads_)
* supports http proxies and basic proxy authentication
* gzipped tracker-responses
* can limit the upload and download bandwidth usage and the maximum number of
unchoked peers
* piece-wise, unordered, incremental file allocation
* implements fair trade. User settable trade-ratio, must at least be 1:1,
but one can choose to trade 1 for 2 or any other ratio that isn't unfair
to the other party.
* fast resume support, a way to get rid of the costly piece check at the
start of a resumed torrent. Saves the storage state, piece_picker state
as well as all local peers in a separate fast-resume file.
* supports an `extension protocol`__. See extensions_.
* supports files > 2 gigabytes.
* supports the ``no_peer_id=1`` extension that will ease the load off trackers.
* supports the `udp-tracker protocol`__ by Olaf van der Spek.
* possibility to limit the number of connections.
* delays have messages if there's no other outgoing traffic to the peer, and
doesn't send have messages to peers that already has the piece. This saves
bandwidth.
* does not have any requirements on the piece order in a torrent that it
resumes. This means it can resume a torrent downloaded by any client.
* adjusts the length of the request queue depending on download rate.
* supports the ``compact=1`` tracker parameter.
* selective downloading. The ability to select which parts of a torrent you
want to download.
* ip filter
* ip filter to disallow ip addresses and ip ranges from connecting and
being connected
.. _`DHT extensions`: dht_extensions.html
__ http://home.elp.rr.com/tur/multitracker-spec.txt
__ http://www.getright.com/seedtorrent.html
__ extension_protocol.html

View File

@ -10,6 +10,7 @@
<body>
<div class="document">
<div id="librarySidebar"><ul class="simple">
<li><a class="reference" href="http://sourceforge.net/project/showfiles.php?group_id=79942">download</a></li>
<li><a class="reference" href="features.html">features</a></li>
<li><a class="reference" href="building.html">building libtorrent</a></li>
<li><a class="reference" href="examples.html">examples</a></li>

View File

@ -2,7 +2,7 @@
<div id="librarySidebar">
* download_
* features_
* `building libtorrent`_
* examples_
@ -22,6 +22,7 @@
libtorrent
==========
.. _download: http://sourceforge.net/project/showfiles.php?group_id=79942
.. _features: features.html
.. _`building libtorrent`: building.html
.. _examples: examples.html

View File

@ -1939,28 +1939,32 @@ the number of outstanding requests to use with url-seeds. Default is 5.</p>
<h1><a name="ip-filter">ip_filter</a></h1>
<p>The <tt class="docutils literal"><span class="pre">ip_filter</span></tt> class is a set of rules that uniquely categorizes all
ip addresses as allowed or disallowed. The default constructor creates
a single rule that allows all addresses (0.0.0.0 - 255.255.255.255).
The <tt class="docutils literal"><span class="pre">address</span></tt> type here is <tt class="docutils literal"><span class="pre">asio::ip::address_v4</span></tt>. It can also be
accessed as <tt class="docutils literal"><span class="pre">libtorrent::address</span></tt>.</p>
a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for
the IPv4 range, and the equivalent range covering all addresses for the
IPv6 range).</p>
<blockquote>
<pre class="literal-block">
template &lt;class Addr&gt;
struct ip_range
{
Addr first;
Addr last;
int flags;
};
class ip_filter
{
public:
enum access_flags { blocked = 1 };
ip_filter();
void add_rule(address_v4 first, address_v4 last, int flags);
int access(address_v4 const&amp; addr) const;
void add_rule(address first, address last, int flags);
int access(address const&amp; addr) const;
struct ip_range
{
address_v4 first;
address_v4 last;
int flags;
};
typedef boost::tuple&lt;std::vector&lt;ip_range&lt;address_v4&gt; &gt;
, std::vector&lt;ip_range&lt;address_v6&gt; &gt; &gt; filter_tuple_t;
std::vector&lt;ip_range&gt; export_filter() const;
filter_tuple_t export_filter() const;
};
</pre>
</blockquote>
@ -1979,13 +1983,15 @@ ip_filter()
<h2><a name="add-rule">add_rule()</a></h2>
<blockquote>
<pre class="literal-block">
void add_rule(address_v4 first, address_v4 last, int flags);
void add_rule(address first, address last, int flags);
</pre>
</blockquote>
<p>Adds a rule to the filter. <tt class="docutils literal"><span class="pre">first</span></tt> and <tt class="docutils literal"><span class="pre">last</span></tt> defines a range of
ip addresses that will be marked with the given flags. The <tt class="docutils literal"><span class="pre">flags</span></tt>
can currently be 0, which means allowed, or <tt class="docutils literal"><span class="pre">ip_filter::blocked</span></tt>, which
means disallowed.</p>
<p>precondition:
<tt class="docutils literal"><span class="pre">first.is_v4()</span> <span class="pre">==</span> <span class="pre">last.is_v4()</span> <span class="pre">&amp;&amp;</span> <span class="pre">first.is_v6()</span> <span class="pre">==</span> <span class="pre">last.is_v6()</span></tt></p>
<p>postcondition:
<tt class="docutils literal"><span class="pre">access(x)</span> <span class="pre">==</span> <span class="pre">flags</span></tt> for every <tt class="docutils literal"><span class="pre">x</span></tt> in the range [<tt class="docutils literal"><span class="pre">first</span></tt>, <tt class="docutils literal"><span class="pre">last</span></tt>]</p>
<p>This means that in a case of overlapping ranges, the last one applied takes
@ -1995,7 +2001,7 @@ precedence.</p>
<h2><a name="access">access()</a></h2>
<blockquote>
<pre class="literal-block">
int access(address_v4 const&amp; addr) const;
int access(address const&amp; addr) const;
</pre>
</blockquote>
<p>Returns the access permissions for the given address (<tt class="docutils literal"><span class="pre">addr</span></tt>). The permission
@ -2007,13 +2013,16 @@ the current filter.</p>
<h2><a name="export-filter">export_filter()</a></h2>
<blockquote>
<pre class="literal-block">
std::vector&lt;ip_range&gt; export_filter() const;
boost::tuple&lt;std::vector&lt;ip_range&lt;address_v4&gt; &gt;
, std::vector&lt;ip_range&lt;address_v6&gt; &gt; &gt; export_filter() const;
</pre>
</blockquote>
<p>This function will return the current state of the filter in the minimum number of
ranges possible. They are sorted from ranges in low addresses to high addresses. Each
entry in the returned vector is a range with the access control specified in its
<tt class="docutils literal"><span class="pre">flags</span></tt> field.</p>
<p>The return value is a tuple containing two range-lists. One for IPv4 addresses
and one for IPv6 addresses.</p>
</div>
</div>
<div class="section" id="big-number">

View File

@ -1935,29 +1935,33 @@ ip_filter
The ``ip_filter`` class is a set of rules that uniquely categorizes all
ip addresses as allowed or disallowed. The default constructor creates
a single rule that allows all addresses (0.0.0.0 - 255.255.255.255).
The ``address`` type here is ``asio::ip::address_v4``. It can also be
accessed as ``libtorrent::address``.
a single rule that allows all addresses (0.0.0.0 - 255.255.255.255 for
the IPv4 range, and the equivalent range covering all addresses for the
IPv6 range).
::
template <class Addr>
struct ip_range
{
Addr first;
Addr last;
int flags;
};
class ip_filter
{
public:
enum access_flags { blocked = 1 };
ip_filter();
void add_rule(address_v4 first, address_v4 last, int flags);
int access(address_v4 const& addr) const;
void add_rule(address first, address last, int flags);
int access(address const& addr) const;
struct ip_range
{
address_v4 first;
address_v4 last;
int flags;
};
typedef boost::tuple<std::vector<ip_range<address_v4> >
, std::vector<ip_range<address_v6> > > filter_tuple_t;
std::vector<ip_range> export_filter() const;
filter_tuple_t export_filter() const;
};
@ -1979,13 +1983,16 @@ add_rule()
::
void add_rule(address_v4 first, address_v4 last, int flags);
void add_rule(address first, address last, int flags);
Adds a rule to the filter. ``first`` and ``last`` defines a range of
ip addresses that will be marked with the given flags. The ``flags``
can currently be 0, which means allowed, or ``ip_filter::blocked``, which
means disallowed.
precondition:
``first.is_v4() == last.is_v4() && first.is_v6() == last.is_v6()``
postcondition:
``access(x) == flags`` for every ``x`` in the range [``first``, ``last``]
@ -1998,7 +2005,7 @@ access()
::
int access(address_v4 const& addr) const;
int access(address const& addr) const;
Returns the access permissions for the given address (``addr``). The permission
can currently be 0 or ``ip_filter::blocked``. The complexity of this operation
@ -2011,13 +2018,17 @@ export_filter()
::
std::vector<ip_range> export_filter() const;
boost::tuple<std::vector<ip_range<address_v4> >
, std::vector<ip_range<address_v6> > > export_filter() const;
This function will return the current state of the filter in the minimum number of
ranges possible. They are sorted from ranges in low addresses to high addresses. Each
entry in the returned vector is a range with the access control specified in its
``flags`` field.
The return value is a tuple containing two range-lists. One for IPv4 addresses
and one for IPv6 addresses.
big_number
==========

View File

@ -33,6 +33,19 @@ POSSIBILITY OF SUCH DAMAGE.
#ifndef TORRENT_IP_FILTER_HPP
#define TORRENT_IP_FILTER_HPP
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/limits.hpp>
#include <boost/utility.hpp>
#include <boost/tuple/tuple.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/config.hpp"
#include "libtorrent/socket.hpp"
#include <set>
@ -47,6 +60,189 @@ inline bool operator<=(address const& lhs
return lhs < rhs || lhs == rhs;
}
template <class Addr>
struct ip_range
{
Addr first;
Addr last;
int flags;
};
namespace detail
{
// this is the generic implementation of
// a filter for a specific address type.
// it works with IPv4 and IPv6
template<class Addr>
class filter_impl
{
public:
filter_impl()
{
typename Addr::bytes_type zero;
std::fill(zero.begin(), zero.end(), 0);
// make the entire ip-range non-blocked
m_access_list.insert(range(Addr(zero), 0));
}
void add_rule(Addr first, Addr last, int flags)
{
using boost::next;
using boost::prior;
assert(!m_access_list.empty());
assert(first < last || first == last);
typename range_t::iterator i = m_access_list.upper_bound(first);
typename range_t::iterator j = m_access_list.upper_bound(last);
if (i != m_access_list.begin()) --i;
assert(j != m_access_list.begin());
assert(j != i);
int first_access = i->access;
int last_access = prior(j)->access;
if (i->start != first && first_access != flags)
{
i = m_access_list.insert(i, range(first, flags));
}
else if (i != m_access_list.begin() && prior(i)->access == flags)
{
--i;
first_access = i->access;
}
assert(!m_access_list.empty());
assert(i != m_access_list.end());
if (i != j) m_access_list.erase(next(i), j);
if (i->start == first)
{
// we can do this const-cast because we know that the new
// start address will keep the set correctly ordered
const_cast<Addr&>(i->start) = first;
const_cast<int&>(i->access) = flags;
}
else if (first_access != flags)
{
m_access_list.insert(i, range(first, flags));
}
if ((j != m_access_list.end()
&& minus_one(j->start) != last)
|| (j == m_access_list.end()
&& last != max_addr()))
{
assert(j == m_access_list.end() || last < minus_one(j->start));
if (last_access != flags)
j = m_access_list.insert(j, range(plus_one(last), last_access));
}
if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j);
assert(!m_access_list.empty());
}
int access(Addr const& addr) const
{
assert(!m_access_list.empty());
typename range_t::const_iterator i = m_access_list.upper_bound(addr);
if (i != m_access_list.begin()) --i;
assert(i != m_access_list.end());
assert(i->start <= addr && (boost::next(i) == m_access_list.end()
|| addr < boost::next(i)->start));
return i->access;
}
std::vector<ip_range<Addr> > export_filter() const
{
std::vector<ip_range<Addr> > ret;
ret.reserve(m_access_list.size());
for (typename range_t::const_iterator i = m_access_list.begin()
, end(m_access_list.end()); i != end;)
{
ip_range<Addr> r;
r.first = i->start;
r.flags = i->access;
++i;
if (i == end)
r.last = max_addr();
else
r.last = minus_one(i->start);
ret.push_back(r);
}
return ret;
}
private:
Addr plus_one(Addr const& a) const
{
typename Addr::bytes_type tmp(a.to_bytes());
typedef typename Addr::bytes_type::reverse_iterator iter;
for (iter i = tmp.rbegin()
, end(tmp.rend()); i != end; ++i)
{
if (*i < std::numeric_limits<typename iter::value_type>::max())
{
*i += 1;
break;
}
*i = 0;
}
return Addr(tmp);
}
Addr minus_one(Addr const& a) const
{
typename Addr::bytes_type tmp(a.to_bytes());
typedef typename Addr::bytes_type::reverse_iterator iter;
for (iter i = tmp.rbegin()
, end(tmp.rend()); i != end; ++i)
{
if (*i > 0)
{
*i -= 1;
break;
}
*i = std::numeric_limits<typename iter::value_type>::max();
}
return Addr(tmp);
}
Addr max_addr() const
{
typename Addr::bytes_type tmp;
std::fill(tmp.begin(), tmp.end()
, std::numeric_limits<typename Addr::bytes_type::value_type>::max());
return Addr(tmp);
}
struct range
{
range(Addr addr, int access = 0): start(addr), access(access) {}
bool operator<(range const& r) const
{ return start < r.start; }
bool operator<(Addr const& a) const
{ return start < a; }
Addr start;
// the end of the range is implicit
// and given by the next entry in the set
int access;
};
typedef std::set<range> range_t;
range_t m_access_list;
};
}
class TORRENT_EXPORT ip_filter
{
public:
@ -55,38 +251,23 @@ public:
{
blocked = 1
};
ip_filter();
void add_rule(address_v4 first, address_v4 last, int flags);
int access(address_v4 const& addr) const;
struct ip_range
{
address_v4 first;
address_v4 last;
int flags;
};
std::vector<ip_range> export_filter() const;
// both addresses MUST be of the same type (i.e. both must
// be either IPv4 or both must be IPv6)
void add_rule(address first, address last, int flags);
int access(address const& addr) const;
typedef boost::tuple<std::vector<ip_range<address_v4> >
, std::vector<ip_range<address_v6> > > filter_tuple_t;
filter_tuple_t export_filter() const;
// void print() const;
private:
struct range
{
range(address_v4 addr, int access = 0): start(addr), access(access) {}
bool operator<(range const& r) const
{ return start < r.start; }
bool operator<(address const& a) const
{ return start < a; }
address_v4 start;
// the end of the range is implicit
// and given by the next entry in the set
int access;
};
typedef std::set<range> range_t;
range_t m_access_list;
detail::filter_impl<address_v4> m_filter4;
detail::filter_impl<address_v6> m_filter6;
};
}

View File

@ -37,103 +37,37 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
ip_filter::ip_filter()
void ip_filter::add_rule(address first, address last, int flags)
{
// make the entire ip-range non-blocked
m_access_list.insert(range(address_v4(0UL), 0));
if (first.is_v4())
{
assert(last.is_v4());
m_filter4.add_rule(first.to_v4(), last.to_v4(), flags);
}
else if (first.is_v6())
{
assert(last.is_v6());
m_filter6.add_rule(first.to_v6(), last.to_v6(), flags);
}
else
assert(false);
}
void ip_filter::add_rule(address_v4 first, address_v4 last, int flags)
int ip_filter::access(address const& addr) const
{
using boost::next;
using boost::prior;
assert(!m_access_list.empty());
assert(first <= last);
range_t::iterator i = m_access_list.upper_bound(first);
range_t::iterator j = m_access_list.upper_bound(last);
if (i != m_access_list.begin()) --i;
assert(j != m_access_list.begin());
assert(j != i);
int first_access = i->access;
int last_access = prior(j)->access;
if (i->start != first && first_access != flags)
{
i = m_access_list.insert(i, range(first, flags));
}
else if (i != m_access_list.begin() && prior(i)->access == flags)
{
--i;
first_access = i->access;
}
assert(!m_access_list.empty());
assert(i != m_access_list.end());
if (i != j)
m_access_list.erase(next(i), j);
if (i->start == first)
{
// we can do this const-cast because we know that the new
// start address will keep the set correctly ordered
const_cast<address_v4&>(i->start) = first;
const_cast<int&>(i->access) = flags;
}
else if (first_access != flags)
{
m_access_list.insert(i, range(first, flags));
}
if ((j != m_access_list.end() && j->start.to_ulong() - 1 != last.to_ulong())
|| (j == m_access_list.end() && last.to_ulong() != 0xffffffff))
{
assert(j == m_access_list.end() || last.to_ulong() < j->start.to_ulong() - 1);
if (last_access != flags)
j = m_access_list.insert(j, range(address_v4(last.to_ulong() + 1), last_access));
}
if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j);
assert(!m_access_list.empty());
if (addr.is_v4())
return m_filter4.access(addr.to_v4());
else if (addr.is_v6())
return m_filter6.access(addr.to_v6());
else
assert(false);
}
int ip_filter::access(address_v4 const& addr) const
ip_filter::filter_tuple_t ip_filter::export_filter() const
{
assert(!m_access_list.empty());
range_t::const_iterator i = m_access_list.upper_bound(addr);
if (i != m_access_list.begin()) --i;
assert(i != m_access_list.end());
assert(i->start <= addr && (boost::next(i) == m_access_list.end()
|| addr < boost::next(i)->start));
return i->access;
return boost::make_tuple(m_filter4.export_filter()
, m_filter6.export_filter());
}
std::vector<ip_filter::ip_range> ip_filter::export_filter() const
{
std::vector<ip_range> ret;
ret.reserve(m_access_list.size());
for (range_t::const_iterator i = m_access_list.begin()
, end(m_access_list.end()); i != end;)
{
ip_range r;
r.first = i->start;
r.flags = i->access;
++i;
if (i == end)
r.last = address_v4(0xffffffff);
else
r.last = address_v4(i->start.to_ulong() - 1);
ret.push_back(r);
}
return ret;
}
/*
void ip_filter::print() const

View File

@ -3,24 +3,35 @@
#include "test.hpp"
/*
Currently this test only tests that the filter can handle
IPv4 addresses. Maybe it should be extended to IPv6 as well,
but the actual code is just a template, so it is probably
pretty safe to assume that as long as it works for IPv4 it
also works for IPv6.
*/
using namespace libtorrent;
bool compare(ip_filter::ip_range const& lhs
, ip_filter::ip_range const& rhs)
template <class Addr>
bool compare(ip_range<Addr> const& lhs
, ip_range<Addr> const& rhs)
{
return lhs.first == rhs.first
&& lhs.last == rhs.last
&& lhs.flags == rhs.flags;
}
void test_rules_invariant(std::vector<ip_filter::ip_range> const& r, ip_filter const& f)
void test_rules_invariant(std::vector<ip_range<address_v4> > const& r, ip_filter const& f)
{
typedef std::vector<ip_filter::ip_range>::const_iterator iterator;
typedef std::vector<ip_range<address_v4> >::const_iterator iterator;
TEST_CHECK(!r.empty());
if (r.empty()) return;
TEST_CHECK(r.front().first == address_v4::from_string("0.0.0.0"));
TEST_CHECK(r.back().last == address_v4::from_string("255.255.255.255"));
TEST_CHECK(r.front().first == address::from_string("0.0.0.0"));
TEST_CHECK(r.back().last == address::from_string("255.255.255.255"));
iterator i = r.begin();
iterator j = boost::next(i);
@ -36,10 +47,11 @@ void test_rules_invariant(std::vector<ip_filter::ip_range> const& r, ip_filter c
int test_main()
{
using namespace libtorrent;
std::vector<ip_filter::ip_range> range;
std::vector<ip_range<address_v4> > range;
// **** test joining of ranges at the end ****
ip_filter::ip_range expected1[] =
ip_range<address_v4> expected1[] =
{
{address_v4::from_string("0.0.0.0"), address_v4::from_string("0.255.255.255"), 0}
, {address_v4::from_string("1.0.0.0"), address_v4::from_string("3.0.0.0"), ip_filter::blocked}
@ -48,28 +60,30 @@ int test_main()
{
ip_filter f;
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("2.0.0.1"), address_v4::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("2.0.0.1"), address::from_string("3.0.0.0"), ip_filter::blocked);
range = f.export_filter();
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));
}
// **** test joining of ranges at the start ****
{
ip_filter f;
f.add_rule(address_v4::from_string("2.0.0.1"), address_v4::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("2.0.0.1"), address::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.0.0.0"), ip_filter::blocked);
range = f.export_filter();
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));
}
@ -77,14 +91,15 @@ int test_main()
{
ip_filter f;
f.add_rule(address_v4::from_string("2.0.0.1"), address_v4::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.4.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("2.0.0.1"), address::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.4.0.0"), ip_filter::blocked);
range = f.export_filter();
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));
}
@ -92,14 +107,15 @@ int test_main()
{
ip_filter f;
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.4.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("2.0.0.1"), address_v4::from_string("3.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.4.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("2.0.0.1"), address::from_string("3.0.0.0"), ip_filter::blocked);
range = f.export_filter();
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected1, &compare<address_v4>));
}
@ -107,50 +123,52 @@ int test_main()
{
ip_filter f;
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("3.0.0.0"), address_v4::from_string("4.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("5.0.0.0"), address_v4::from_string("6.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("7.0.0.0"), address_v4::from_string("8.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("3.0.0.0"), address::from_string("4.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("5.0.0.0"), address::from_string("6.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("7.0.0.0"), address::from_string("8.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("1.0.1.0"), address_v4::from_string("9.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.1.0"), address::from_string("9.0.0.0"), ip_filter::blocked);
range = f.export_filter();
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
ip_filter::ip_range expected[] =
ip_range<address_v4> expected[] =
{
{address_v4::from_string("0.0.0.0"), address_v4::from_string("0.255.255.255"), 0}
, {address_v4::from_string("1.0.0.0"), address_v4::from_string("9.0.0.0"), ip_filter::blocked}
, {address_v4::from_string("9.0.0.1"), address_v4::from_string("255.255.255.255"), 0}
};
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));
}
// **** test joining of multiple overlapping ranges 2 ****
{
ip_filter f;
f.add_rule(address_v4::from_string("1.0.0.0"), address_v4::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("3.0.0.0"), address_v4::from_string("4.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("5.0.0.0"), address_v4::from_string("6.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("7.0.0.0"), address_v4::from_string("8.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("1.0.0.0"), address::from_string("2.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("3.0.0.0"), address::from_string("4.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("5.0.0.0"), address::from_string("6.0.0.0"), ip_filter::blocked);
f.add_rule(address::from_string("7.0.0.0"), address::from_string("8.0.0.0"), ip_filter::blocked);
f.add_rule(address_v4::from_string("0.0.1.0"), address_v4::from_string("7.0.4.0"), ip_filter::blocked);
range = f.export_filter();
f.add_rule(address::from_string("0.0.1.0"), address::from_string("7.0.4.0"), ip_filter::blocked);
range = boost::get<0>(f.export_filter());
test_rules_invariant(range, f);
TEST_CHECK(range.size() == 3);
ip_filter::ip_range expected[] =
ip_range<address_v4> expected[] =
{
{address_v4::from_string("0.0.0.0"), address_v4::from_string("0.0.0.255"), 0}
, {address_v4::from_string("0.0.1.0"), address_v4::from_string("8.0.0.0"), ip_filter::blocked}
, {address_v4::from_string("8.0.0.1"), address_v4::from_string("255.255.255.255"), 0}
};
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare));
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare<address_v4>));
}
return 0;