From fe0d570f053919334d16dc08c01ee925a0593833 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 23 Sep 2006 21:24:28 +0000 Subject: [PATCH] completed IPv6 support in ip_filter and updated test_ip_filter and documentation. Documented recently added extensions to DHT. --- ChangeLog | 3 + docs/building.html | 8 ++ docs/building.rst | 10 ++ docs/dht_extensions.html | 66 +++++++++ docs/dht_extensions.rst | 7 +- docs/examples.html | 3 +- docs/examples.rst | 3 +- docs/features.html | 33 +++-- docs/features.rst | 32 +++-- docs/index.html | 1 + docs/index.rst | 3 +- docs/manual.html | 39 ++++-- docs/manual.rst | 41 ++++-- include/libtorrent/ip_filter.hpp | 233 +++++++++++++++++++++++++++---- src/ip_filter.cpp | 112 +++------------ test/test_ip_filter.cpp | 100 +++++++------ 16 files changed, 473 insertions(+), 221 deletions(-) create mode 100644 docs/dht_extensions.html diff --git a/ChangeLog b/ChangeLog index 7efcb99ee..ad8545970 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 diff --git a/docs/building.html b/docs/building.html index 0d47b5d29..cd5e346fd 100644 --- a/docs/building.html +++ b/docs/building.html @@ -242,6 +242,14 @@ logging=none dht-support=on dht-support=logging dht-support=off

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

No build system is present if libtorrent is checked out from CVS - it diff --git a/docs/building.rst b/docs/building.rst index b891463af..271d34114 100644 --- a/docs/building.rst +++ b/docs/building.rst @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/dht_extensions.html b/docs/dht_extensions.html new file mode 100644 index 000000000..12188037d --- /dev/null +++ b/docs/dht_extensions.html @@ -0,0 +1,66 @@ + + + + + + + + + + + +

+ +++ + + + +
Author:Arvid Norberg, arvid@rasterbar.com
+
+

Mainline DHT extensions

+

libtorrent implements a few extensions to the Mainline DHT protocol.

+
+

client identification

+

In each DHT packet, an extra key is inserted named "v". 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.

+

Currently known clients:

+ ++++ + + + + + + + + + + + + + + +
uTorrentUT
libtorrentLT
MooPoliceMP
GetRightGR
+
+
+

IPv6 support

+

The only DHT messages that don't support IPv6 is the nodes 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 +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).

+
+
+
+ + diff --git a/docs/dht_extensions.rst b/docs/dht_extensions.rst index 60fd4a1b3..424f90978 100644 --- a/docs/dht_extensions.rst +++ b/docs/dht_extensions.rst @@ -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). + diff --git a/docs/examples.html b/docs/examples.html index 7e15f2235..57aec57c3 100644 --- a/docs/examples.html +++ b/docs/examples.html @@ -34,7 +34,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.

dump_torrent

This is an example of a program that will take a torrent-file as a parameter and diff --git a/docs/examples.rst b/docs/examples.rst index cd8e21cf6..2845a29f9 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -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 diff --git a/docs/features.html b/docs/features.html index 90fa2f7f5..7261a4e6e 100644 --- a/docs/features.html +++ b/docs/features.html @@ -47,44 +47,47 @@ example client.

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 an extension protocol. See extensions.
  • 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
diff --git a/docs/features.rst b/docs/features.rst index 7f490ee5f..9d58bb9c3 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -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 diff --git a/docs/index.html b/docs/index.html index 6b00b125c..e21db8deb 100755 --- a/docs/index.html +++ b/docs/index.html @@ -10,6 +10,7 @@
    +
  • download
  • features
  • building libtorrent
  • examples
  • diff --git a/docs/index.rst b/docs/index.rst index cf472507e..97ecf3737 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -2,7 +2,7 @@
    - +* 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 diff --git a/docs/manual.html b/docs/manual.html index 725083753..1ccda3298 100755 --- a/docs/manual.html +++ b/docs/manual.html @@ -1939,28 +1939,32 @@ the number of outstanding requests to use with url-seeds. Default is 5.

    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,15 @@ ip_filter()

    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]

    This means that in a case of overlapping ranges, the last one applied takes @@ -1995,7 +2001,7 @@ precedence.

    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 @@ -2007,13 +2013,16 @@ the current filter.

    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.

diff --git a/docs/manual.rst b/docs/manual.rst index 06b63def9..fe555bbfe 100755 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -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 + 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 > > filter_tuple_t; - std::vector 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 export_filter() const; + boost::tuple > + , std::vector > > 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 ========== diff --git a/include/libtorrent/ip_filter.hpp b/include/libtorrent/ip_filter.hpp index 43229f181..c55ed1763 100644 --- a/include/libtorrent/ip_filter.hpp +++ b/include/libtorrent/ip_filter.hpp @@ -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 +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + #include "libtorrent/config.hpp" #include "libtorrent/socket.hpp" #include @@ -47,6 +60,189 @@ inline bool operator<=(address const& lhs return lhs < rhs || lhs == rhs; } +template +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 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(i->start) = first; + const_cast(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 > export_filter() const + { + std::vector > 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 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::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::max(); + } + return Addr(tmp); + } + + Addr max_addr() const + { + typename Addr::bytes_type tmp; + std::fill(tmp.begin(), tmp.end() + , std::numeric_limits::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_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 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 > > 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_t; - range_t m_access_list; + detail::filter_impl m_filter4; + detail::filter_impl m_filter6; }; } diff --git a/src/ip_filter.cpp b/src/ip_filter.cpp index a6b3bde87..38d92a7b1 100644 --- a/src/ip_filter.cpp +++ b/src/ip_filter.cpp @@ -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(i->start) = first; - const_cast(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::export_filter() const - { - std::vector 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 diff --git a/test/test_ip_filter.cpp b/test/test_ip_filter.cpp index ee8679b55..1d701ce2c 100644 --- a/test/test_ip_filter.cpp +++ b/test/test_ip_filter.cpp @@ -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 +bool compare(ip_range const& lhs + , ip_range const& rhs) { return lhs.first == rhs.first && lhs.last == rhs.last && lhs.flags == rhs.flags; } -void test_rules_invariant(std::vector const& r, ip_filter const& f) +void test_rules_invariant(std::vector > const& r, ip_filter const& f) { - typedef std::vector::const_iterator iterator; + typedef std::vector >::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 const& r, ip_filter c int test_main() { using namespace libtorrent; - std::vector range; + + std::vector > range; // **** test joining of ranges at the end **** - ip_filter::ip_range expected1[] = + ip_range 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)); + } // **** 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)); + } @@ -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)); + } @@ -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)); + } @@ -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 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)); + } // **** 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 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)); + } return 0;