deleting to reupload

This commit is contained in:
Zach Tibbitts 2007-02-08 20:22:05 +00:00
parent 8f6c97c7ba
commit fcf85bd815
32 changed files with 0 additions and 21616 deletions

View File

@ -1,124 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg, Daniel Wallin
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.
*/
#include "libtorrent/alert.hpp"
namespace libtorrent {
alert::alert(severity_t severity, const std::string& msg)
: m_msg(msg)
, m_severity(severity)
, m_timestamp(boost::posix_time::second_clock::universal_time())
{
}
alert::~alert()
{
}
boost::posix_time::ptime alert::timestamp() const
{
return m_timestamp;
}
const std::string& alert::msg() const
{
return m_msg;
}
alert::severity_t alert::severity() const
{
return m_severity;
}
alert_manager::alert_manager()
: m_severity(alert::none)
{}
alert_manager::~alert_manager()
{
while (!m_alerts.empty())
{
delete m_alerts.front();
m_alerts.pop();
}
}
void alert_manager::post_alert(const alert& alert_)
{
boost::mutex::scoped_lock lock(m_mutex);
if (m_severity > alert_.severity()) return;
// the internal limit is 100 alerts
if (m_alerts.size() == 100)
{
alert* result = m_alerts.front();
m_alerts.pop();
delete result;
}
m_alerts.push(alert_.clone().release());
}
std::auto_ptr<alert> alert_manager::get()
{
boost::mutex::scoped_lock lock(m_mutex);
assert(!m_alerts.empty());
alert* result = m_alerts.front();
m_alerts.pop();
return std::auto_ptr<alert>(result);
}
bool alert_manager::pending() const
{
boost::mutex::scoped_lock lock(m_mutex);
return !m_alerts.empty();
}
void alert_manager::set_severity(alert::severity_t severity)
{
boost::mutex::scoped_lock lock(m_mutex);
m_severity = severity;
}
bool alert_manager::should_post(alert::severity_t severity) const
{
return severity >= m_severity;
}
} // namespace libtorrent

View File

@ -1,201 +0,0 @@
/*
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
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.
*/
//The Standard Library defines the two template functions std::min()
//and std::max() in the <algorithm> header. In general, you should
//use these template functions for calculating the min and max values
//of a pair. Unfortunately, Visual C++ does not define these function
// templates. This is because the names min and max clash with
//the traditional min and max macros defined in <windows.h>.
//As a workaround, Visual C++ defines two alternative templates with
//identical functionality called _cpp_min() and _cpp_max(). You can
//use them instead of std::min() and std::max().To disable the
//generation of the min and max macros in Visual C++, #define
//NOMINMAX before #including <windows.h>.
#ifdef _WIN32
//support boost1.32.0(2004-11-19 18:47)
//now all libs can be compiled and linked with static module
#define NOMINMAX
#endif
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/aux_/allocate_resources_impl.hpp"
#include <cassert>
#include <algorithm>
#include <boost/limits.hpp>
#if defined(_MSC_VER) && _MSC_VER < 1310
#define for if (false) {} else for
#else
#include <boost/iterator/transform_iterator.hpp>
#endif
namespace libtorrent
{
int saturated_add(int a, int b)
{
assert(a >= 0);
assert(b >= 0);
assert(a <= resource_request::inf);
assert(b <= resource_request::inf);
assert(resource_request::inf + resource_request::inf < 0);
unsigned int sum = unsigned(a) + unsigned(b);
if (sum > unsigned(resource_request::inf))
sum = resource_request::inf;
assert(sum >= unsigned(a) && sum >= unsigned(b));
return int(sum);
}
#if defined(_MSC_VER) && _MSC_VER < 1310
namespace detail
{
struct iterator_wrapper
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
orig_iter iter;
iterator_wrapper(orig_iter i): iter(i) {}
void operator++() { ++iter; }
torrent& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper& i) const
{ return iter != i.iter; }
};
struct iterator_wrapper2
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
orig_iter iter;
iterator_wrapper2(orig_iter i): iter(i) {}
void operator++() { ++iter; }
peer_connection& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper2& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper2& i) const
{ return iter != i.iter; }
};
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper(c.begin())
, detail::iterator_wrapper(c.end())
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper2(c.begin())
, detail::iterator_wrapper2(c.end())
, res);
}
#else
namespace aux
{
peer_connection& pick_peer(
std::pair<boost::shared_ptr<stream_socket>
, boost::intrusive_ptr<peer_connection> > const& p)
{
return *p.second;
}
peer_connection& pick_peer2(
std::pair<tcp::endpoint, peer_connection*> const& p)
{
return *p.second;
}
torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
{
return *p.second;
}
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::deref)
, new_iter(c.end(), &aux::deref)
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::pick_peer2)
, new_iter(c.end(), &aux::pick_peer2)
, res);
}
#endif
} // namespace libtorrent

File diff suppressed because it is too large Load Diff

View File

@ -1,344 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <algorithm>
#include <iomanip>
#include "libtorrent/entry.hpp"
#include "libtorrent/config.hpp"
#include <boost/bind.hpp>
#include <boost/next_prior.hpp>
#if defined(_MSC_VER)
namespace std
{
using ::isprint;
}
#define for if (false) {} else for
#endif
namespace
{
template <class T>
void call_destructor(T* o)
{
assert(o);
o->~T();
}
struct compare_string
{
compare_string(char const* s): m_str(s) {}
bool operator()(
std::pair<std::string
, libtorrent::entry> const& e) const
{
return m_str && e.first == m_str;
}
char const* m_str;
};
}
namespace libtorrent
{
namespace detail
{
TORRENT_EXPORT char const* integer_to_str(char* buf, int size, entry::integer_type val)
{
int sign = 0;
if (val < 0)
{
sign = 1;
val = -val;
}
buf[--size] = '\0';
if (val == 0) buf[--size] = '0';
for (; size > sign && val != 0;)
{
buf[--size] = '0' + char(val % 10);
val /= 10;
}
if (sign) buf[--size] = '-';
return buf + size;
}
}
entry& entry::operator[](char const* key)
{
dictionary_type::iterator i = dict().find(key);
if (i != dict().end()) return i->second;
dictionary_type::iterator ret = dict().insert(
dict().begin()
, std::make_pair(std::string(key), entry()));
return ret->second;
}
entry& entry::operator[](std::string const& key)
{
return (*this)[key.c_str()];
}
entry* entry::find_key(char const* key)
{
dictionary_type::iterator i = std::find_if(
dict().begin()
, dict().end()
, compare_string(key));
if (i == dict().end()) return 0;
return &i->second;
}
entry const* entry::find_key(char const* key) const
{
dictionary_type::const_iterator i = dict().find(key);
if (i == dict().end()) return 0;
return &i->second;
}
const entry& entry::operator[](char const* key) const
{
dictionary_type::const_iterator i = dict().find(key);
if (i == dict().end()) throw type_error(
(std::string("key not found: ") + key).c_str());
return i->second;
}
const entry& entry::operator[](std::string const& key) const
{
return (*this)[key.c_str()];
}
entry::entry(const dictionary_type& v)
{
new(data) dictionary_type(v);
m_type = dictionary_t;
}
entry::entry(const string_type& v)
{
new(data) string_type(v);
m_type = string_t;
}
entry::entry(const list_type& v)
{
new(data) list_type(v);
m_type = list_t;
}
entry::entry(const integer_type& v)
{
new(data) integer_type(v);
m_type = int_t;
}
void entry::operator=(const dictionary_type& v)
{
destruct();
new(data) dictionary_type(v);
m_type = dictionary_t;
}
void entry::operator=(const string_type& v)
{
destruct();
new(data) string_type(v);
m_type = string_t;
}
void entry::operator=(const list_type& v)
{
destruct();
new(data) list_type(v);
m_type = list_t;
}
void entry::operator=(const integer_type& v)
{
destruct();
new(data) integer_type(v);
m_type = int_t;
}
bool entry::operator==(entry const& e) const
{
if (m_type != e.m_type) return false;
switch(m_type)
{
case int_t:
return integer() == e.integer();
case string_t:
return string() == e.string();
case list_t:
return list() == e.list();
case dictionary_t:
return dict() == e.dict();
default:
assert(m_type == undefined_t);
return true;
}
}
void entry::construct(data_type t)
{
m_type = t;
switch(m_type)
{
case int_t:
new(data) integer_type;
break;
case string_t:
new(data) string_type;
break;
case list_t:
new(data) list_type;
break;
case dictionary_t:
new (data) dictionary_type;
break;
default:
assert(m_type == undefined_t);
m_type = undefined_t;
}
}
void entry::copy(const entry& e)
{
m_type = e.m_type;
switch(m_type)
{
case int_t:
new(data) integer_type(e.integer());
break;
case string_t:
new(data) string_type(e.string());
break;
case list_t:
new(data) list_type(e.list());
break;
case dictionary_t:
new (data) dictionary_type(e.dict());
break;
default:
m_type = undefined_t;
}
}
void entry::destruct()
{
switch(m_type)
{
case int_t:
call_destructor(reinterpret_cast<integer_type*>(data));
break;
case string_t:
call_destructor(reinterpret_cast<string_type*>(data));
break;
case list_t:
call_destructor(reinterpret_cast<list_type*>(data));
break;
case dictionary_t:
call_destructor(reinterpret_cast<dictionary_type*>(data));
break;
default:
assert(m_type == undefined_t);
break;
}
}
void entry::print(std::ostream& os, int indent) const
{
assert(indent >= 0);
for (int i = 0; i < indent; ++i) os << " ";
switch (m_type)
{
case int_t:
os << integer() << "\n";
break;
case string_t:
{
bool binary_string = false;
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
{
if (!std::isprint(static_cast<unsigned char>(*i)))
{
binary_string = true;
break;
}
}
if (binary_string)
{
os.unsetf(std::ios_base::dec);
os.setf(std::ios_base::hex);
for (std::string::const_iterator i = string().begin(); i != string().end(); ++i)
os << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>((unsigned char)*i);
os.unsetf(std::ios_base::hex);
os.setf(std::ios_base::dec);
os << "\n";
}
else
{
os << string() << "\n";
}
} break;
case list_t:
{
os << "list\n";
for (list_type::const_iterator i = list().begin(); i != list().end(); ++i)
{
i->print(os, indent+1);
}
} break;
case dictionary_t:
{
os << "dictionary\n";
for (dictionary_type::const_iterator i = dict().begin(); i != dict().end(); ++i)
{
for (int j = 0; j < indent+1; ++j) os << " ";
os << "[" << i->first << "]";
if (i->second.type() != entry::string_t
&& i->second.type() != entry::int_t)
os << "\n";
else os << " ";
i->second.print(os, indent+2);
}
} break;
default:
os << "<uninitialized>\n";
}
}
}

View File

@ -1,148 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <string>
#include <cassert>
#include <stdexcept>
#include <sstream>
#include <iomanip>
#include <cctype>
#include <algorithm>
namespace libtorrent
{
std::string unescape_string(std::string const& s)
{
std::string ret;
for (std::string::const_iterator i = s.begin(); i != s.end(); ++i)
{
if(*i == '+')
{
ret += ' ';
}
else if (*i != '%')
{
ret += *i;
}
else
{
++i;
if (i == s.end())
throw std::runtime_error("invalid escaped string");
int high;
if(*i >= '0' && *i <= '9') high = *i - '0';
else if(*i >= 'A' && *i <= 'F') high = *i + 10 - 'A';
else if(*i >= 'a' && *i <= 'f') high = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
++i;
if (i == s.end())
throw std::runtime_error("invalid escaped string");
int low;
if(*i >= '0' && *i <= '9') low = *i - '0';
else if(*i >= 'A' && *i <= 'F') low = *i + 10 - 'A';
else if(*i >= 'a' && *i <= 'f') low = *i + 10 - 'a';
else throw std::runtime_error("invalid escaped string");
ret += char(high * 16 + low);
}
}
return ret;
}
std::string escape_string(const char* str, int len)
{
assert(str != 0);
assert(len >= 0);
// http://www.ietf.org/rfc/rfc2396.txt
// section 2.3
// some trackers seems to require that ' is escaped
// static const char unreserved_chars[] = "-_.!~*'()";
static const char unreserved_chars[] = "-_.!~*()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789";
std::stringstream ret;
ret << std::hex << std::setfill('0');
for (int i = 0; i < len; ++i)
{
if (std::count(
unreserved_chars
, unreserved_chars+sizeof(unreserved_chars)-1
, *str))
{
ret << *str;
}
else
{
ret << '%'
<< std::setw(2)
<< (int)static_cast<unsigned char>(*str);
}
++str;
}
return ret.str();
}
std::string escape_path(const char* str, int len)
{
assert(str != 0);
assert(len >= 0);
static const char unreserved_chars[] = "/-_.!~*()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789";
std::stringstream ret;
ret << std::hex << std::setfill('0');
for (int i = 0; i < len; ++i)
{
if (std::count(
unreserved_chars
, unreserved_chars+sizeof(unreserved_chars)-1
, *str))
{
ret << *str;
}
else
{
ret << '%'
<< std::setw(2)
<< (int)static_cast<unsigned char>(*str);
}
++str;
}
return ret.str();
}
}

View File

@ -1,313 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#ifdef _WIN32
// windows part
#include "libtorrent/utf8.hpp"
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef _MODE_T_
typedef int mode_t;
#endif
#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif
#else
// unix part
#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <boost/static_assert.hpp>
// make sure the _FILE_OFFSET_BITS define worked
// on this platform
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
#endif
#include <boost/filesystem/operations.hpp>
#include "libtorrent/file.hpp"
#include <sstream>
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_RANDOM
#define O_RANDOM 0
#endif
#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif
namespace fs = boost::filesystem;
namespace
{
enum { mode_in = 1, mode_out = 2 };
mode_t map_open_mode(int m)
{
if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
assert(false);
return 0;
}
#ifdef WIN32
std::string utf8_native(std::string const& s)
{
try
{
std::wstring ws;
libtorrent::utf8_wchar(s, ws);
std::size_t size = wcstombs(0, ws.c_str(), 0);
if (size == std::size_t(-1)) return s;
std::string ret;
ret.resize(size);
size = wcstombs(&ret[0], ws.c_str(), size + 1);
if (size == wchar_t(-1)) return s;
ret.resize(size);
return ret;
}
catch(std::exception)
{
return s;
}
}
#else
std::string utf8_native(std::string const& s)
{
return s;
}
#endif
}
namespace libtorrent
{
const file::open_mode file::in(mode_in);
const file::open_mode file::out(mode_out);
const file::seek_mode file::begin(1);
const file::seek_mode file::end(2);
struct file::impl
{
impl()
: m_fd(-1)
, m_open_mode(0)
{}
impl(fs::path const& path, int mode)
: m_fd(-1)
, m_open_mode(0)
{
open(path, mode);
}
~impl()
{
close();
}
void open(fs::path const& path, int mode)
{
assert(path.is_complete());
close();
#if defined(_WIN32) && defined(UNICODE)
std::wstring wpath(safe_convert(path.native_file_string()));
m_fd = ::_wopen(
wpath.c_str()
, map_open_mode(mode)
, S_IREAD | S_IWRITE);
#else
m_fd = ::open(
utf8_native(path.native_file_string()).c_str()
, map_open_mode(mode)
#ifdef _WIN32
, S_IREAD | S_IWRITE);
#else
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
#endif
if (m_fd == -1)
{
std::stringstream msg;
msg << "open failed: '" << path.native_file_string() << "'. "
<< strerror(errno);
throw file_error(msg.str());
}
m_open_mode = mode;
}
void close()
{
if (m_fd == -1) return;
::close(m_fd);
m_fd = -1;
m_open_mode = 0;
}
size_type read(char* buf, size_type num_bytes)
{
assert(m_open_mode & mode_in);
assert(m_fd != -1);
size_type ret = ::read(m_fd, buf, num_bytes);
if (ret == -1)
{
std::stringstream msg;
msg << "read failed: " << strerror(errno);
throw file_error(msg.str());
}
return ret;
}
size_type write(const char* buf, size_type num_bytes)
{
assert(m_open_mode & mode_out);
assert(m_fd != -1);
// TODO: Test this a bit more, what happens with random failures in
// the files?
// if ((rand() % 100) > 80)
// throw file_error("debug");
size_type ret = ::write(m_fd, buf, num_bytes);
if (ret == -1)
{
std::stringstream msg;
msg << "write failed: " << strerror(errno);
throw file_error(msg.str());
}
return ret;
}
size_type seek(size_type offset, int m)
{
assert(m_open_mode);
assert(m_fd != -1);
int seekdir = (m == 1)?SEEK_SET:SEEK_END;
#ifdef _WIN32
size_type ret = _lseeki64(m_fd, offset, seekdir);
#else
size_type ret = lseek(m_fd, offset, seekdir);
#endif
// For some strange reason this fails
// on win32. Use windows specific file
// wrapper instead.
if (ret == -1)
{
std::stringstream msg;
msg << "seek failed: '" << strerror(errno)
<< "' fd: " << m_fd
<< " offset: " << offset
<< " seekdir: " << seekdir;
throw file_error(msg.str());
}
return ret;
}
size_type tell()
{
assert(m_open_mode);
assert(m_fd != -1);
#ifdef _WIN32
return _telli64(m_fd);
#else
return lseek(m_fd, 0, SEEK_CUR);
#endif
}
int m_fd;
int m_open_mode;
};
// pimpl forwardings
file::file() : m_impl(new impl()) {}
file::file(boost::filesystem::path const& p, file::open_mode m)
: m_impl(new impl(p, m.m_mask))
{}
file::~file() {}
void file::open(boost::filesystem::path const& p, file::open_mode m)
{
m_impl->open(p, m.m_mask);
}
void file::close()
{
m_impl->close();
}
size_type file::write(const char* buf, size_type num_bytes)
{
return m_impl->write(buf, num_bytes);
}
size_type file::read(char* buf, size_type num_bytes)
{
return m_impl->read(buf, num_bytes);
}
size_type file::seek(size_type pos, file::seek_mode m)
{
return m_impl->seek(pos, m.m_val);
}
size_type file::tell()
{
return m_impl->tell();
}
}

View File

@ -1,845 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/io.hpp"
using namespace libtorrent;
using boost::bind;
namespace
{
enum
{
minimum_tracker_response_length = 3,
http_buffer_size = 2048
};
enum
{
FTEXT = 0x01,
FHCRC = 0x02,
FEXTRA = 0x04,
FNAME = 0x08,
FCOMMENT = 0x10,
FRESERVED = 0xe0,
GZIP_MAGIC0 = 0x1f,
GZIP_MAGIC1 = 0x8b
};
}
using namespace boost::posix_time;
namespace libtorrent
{
http_parser::http_parser()
: m_recv_pos(0)
, m_status_code(-1)
, m_content_length(-1)
, m_content_encoding(plain)
, m_state(read_status)
, m_recv_buffer(0, 0)
, m_body_start_pos(0)
, m_finished(false)
{}
boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
{
m_recv_buffer = recv_buffer;
boost::tuple<int, int> ret(0, 0);
char const* pos = recv_buffer.begin + m_recv_pos;
if (m_state == read_status)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
// if we don't have a full line yet, wait.
if (newline == recv_buffer.end) return ret;
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
std::istringstream line(std::string(pos, newline - 1));
++newline;
int incoming = (int)std::distance(pos, newline);
m_recv_pos += incoming;
boost::get<1>(ret) += incoming;
pos = newline;
line >> m_protocol;
if (m_protocol.substr(0, 5) != "HTTP/")
{
throw std::runtime_error("unknown protocol in HTTP response: "
+ m_protocol);
}
line >> m_status_code;
std::getline(line, m_server_message);
m_state = read_header;
}
if (m_state == read_header)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
std::string line;
while (newline != recv_buffer.end && m_state == read_header)
{
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
line.assign(pos, newline - 1);
m_recv_pos += newline - pos;
boost::get<1>(ret) += newline - pos;
pos = newline;
std::string::size_type separator = line.find(": ");
if (separator == std::string::npos)
{
++pos;
++m_recv_pos;
boost::get<1>(ret) += 1;
m_state = read_body;
m_body_start_pos = m_recv_pos;
break;
}
std::string name = line.substr(0, separator);
std::string value = line.substr(separator + 2, std::string::npos);
m_header.insert(std::make_pair(name, value));
if (name == "Content-Length")
{
try
{
m_content_length = boost::lexical_cast<int>(value);
}
catch(boost::bad_lexical_cast&) {}
}
else if (name == "Content-Encoding")
{
if (value == "gzip" || value == "x-gzip")
{
m_content_encoding = gzip;
}
else
{
std::string error_str = "unknown content encoding in response: \"";
error_str += value;
error_str += "\"";
throw std::runtime_error(error_str);
}
}
// TODO: make sure we don't step outside of the buffer
++pos;
++m_recv_pos;
assert(m_recv_pos <= (int)recv_buffer.left());
newline = std::find(pos, recv_buffer.end, '\n');
}
}
if (m_state == read_body)
{
int incoming = recv_buffer.end - pos;
if (m_recv_pos - m_body_start_pos + incoming > m_content_length
&& m_content_length >= 0)
incoming = m_content_length - m_recv_pos + m_body_start_pos;
assert(incoming >= 0);
m_recv_pos += incoming;
boost::get<0>(ret) += incoming;
if (m_content_length >= 0
&& m_recv_pos - m_body_start_pos >= m_content_length)
{
m_finished = true;
}
}
return ret;
}
buffer::const_interval http_parser::get_body()
{
char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
char const* body_end = m_recv_buffer.begin + m_recv_pos;
m_recv_pos = 0;
m_body_start_pos = 0;
m_status_code = -1;
m_content_length = -1;
m_finished = false;
m_state = read_status;
m_header.clear();
return buffer::const_interval(body_begin, body_end);
}
http_tracker_connection::http_tracker_connection(
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, std::string request
, boost::weak_ptr<request_callback> c
, session_settings const& stn
, std::string const& auth)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_state(read_status)
, m_content_encoding(plain)
, m_content_length(0)
, m_name_lookup(d)
, m_port(port)
, m_recv_pos(0)
, m_buffer(http_buffer_size)
, m_settings(stn)
, m_password(auth)
, m_code(0)
, m_timed_out(false)
{
const std::string* connect_to_host;
bool using_proxy = false;
m_send_buffer.assign("GET ");
// should we use the proxy?
if (!m_settings.proxy_ip.empty())
{
connect_to_host = &m_settings.proxy_ip;
using_proxy = true;
m_send_buffer += "http://";
m_send_buffer += hostname;
if (port != 80)
{
m_send_buffer += ":";
m_send_buffer += boost::lexical_cast<std::string>(port);
}
m_port = m_settings.proxy_port != 0
? m_settings.proxy_port : 80 ;
}
else
{
connect_to_host = &hostname;
}
if (tracker_req().kind == tracker_request::scrape_request)
{
// find and replace "announce" with "scrape"
// in request
std::size_t pos = request.find("announce");
if (pos == std::string::npos)
throw std::runtime_error("scrape is not available on url: '"
+ tracker_req().url +"'");
request.replace(pos, 8, "scrape");
}
m_send_buffer += request;
// if request-string already contains
// some parameters, append an ampersand instead
// of a question mark
if (request.find('?') != std::string::npos)
m_send_buffer += "&";
else
m_send_buffer += "?";
m_send_buffer += "info_hash=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
if (tracker_req().kind == tracker_request::announce_request)
{
m_send_buffer += "&peer_id=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.pid.begin()), 20);
m_send_buffer += "&port=";
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
m_send_buffer += "&uploaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.uploaded);
m_send_buffer += "&downloaded=";
m_send_buffer += boost::lexical_cast<std::string>(req.downloaded);
m_send_buffer += "&left=";
m_send_buffer += boost::lexical_cast<std::string>(req.left);
if (req.event != tracker_request::none)
{
const char* event_string[] = {"completed", "started", "stopped"};
m_send_buffer += "&event=";
m_send_buffer += event_string[req.event - 1];
}
m_send_buffer += "&key=";
std::stringstream key_string;
key_string << std::hex << req.key;
m_send_buffer += key_string.str();
m_send_buffer += "&compact=1";
m_send_buffer += "&numwant=";
m_send_buffer += boost::lexical_cast<std::string>(
std::min(req.num_want, 999));
// extension that tells the tracker that
// we don't need any peer_id's in the response
m_send_buffer += "&no_peer_id=1";
}
m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
"User-Agent: ";
m_send_buffer += m_settings.user_agent;
m_send_buffer += "\r\n"
"Host: ";
m_send_buffer += hostname;
if (port != 80)
{
m_send_buffer += ':';
m_send_buffer += boost::lexical_cast<std::string>(port);
}
if (using_proxy && !m_settings.proxy_login.empty())
{
m_send_buffer += "\r\nProxy-Authorization: Basic ";
m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password);
}
if (auth != "")
{
m_send_buffer += "\r\nAuthorization: Basic ";
m_send_buffer += base64encode(auth);
}
m_send_buffer += "\r\n\r\n";
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> TRACKER_REQUEST [ str: " + m_send_buffer + " ]");
std::stringstream info_hash_str;
info_hash_str << req.info_hash;
requester().debug_log("info_hash: " + info_hash_str.str() + "\n");
}
#endif
tcp::resolver::query q(*connect_to_host, "0");
m_name_lookup.async_resolve(q
, boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
void http_tracker_connection::on_timeout()
{
m_timed_out = true;
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void http_tracker_connection::name_lookup(asio::error const& error
, tcp::resolver::iterator i) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error || i == tcp::resolver::iterator())
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker name lookup successful");
#endif
restart_read_timeout();
m_socket.reset(new stream_socket(m_name_lookup.io_service()));
tcp::endpoint a(i->endpoint().address(), m_port);
if (has_requester()) requester().m_tracker_address = a;
m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
};
void http_tracker_connection::connected(asio::error const& error) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection successful");
#endif
restart_read_timeout();
async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
, m_send_buffer.size()), bind(&http_tracker_connection::sent
, self(), _1));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}
void http_tracker_connection::sent(asio::error const& error) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker send data completed");
#endif
restart_read_timeout();
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}; // msvc 7.1 seems to require this semi-colon
void http_tracker_connection::receive(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
if (error == asio::error::eof)
{
on_response();
close();
return;
}
fail(-1, error.what());
return;
}
restart_read_timeout();
assert(bytes_transferred > 0);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection reading "
+ boost::lexical_cast<std::string>(bytes_transferred));
#endif
m_recv_pos += bytes_transferred;
// if the receive buffer is full, expand it with http_buffer_size
if ((int)m_buffer.size() == m_recv_pos)
{
if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
{
fail(200, "too large tracker response");
return;
}
assert(http_buffer_size > 0);
if ((int)m_buffer.size() + http_buffer_size
> m_settings.tracker_maximum_response_length)
m_buffer.resize(m_settings.tracker_maximum_response_length);
else
m_buffer.resize(m_buffer.size() + http_buffer_size);
}
if (m_state == read_status)
{
std::vector<char>::iterator end = m_buffer.begin()+m_recv_pos;
std::vector<char>::iterator newline = std::find(m_buffer.begin(), end, '\n');
// if we don't have a full line yet, wait.
if (newline != end)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline));
#endif
std::istringstream line(std::string(m_buffer.begin(), newline));
++newline;
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
m_buffer.erase(m_buffer.begin(), newline);
std::string protocol;
line >> m_server_protocol;
if (m_server_protocol.substr(0, 5) != "HTTP/")
{
std::string error_msg = "unknown protocol in response: " + m_server_protocol;
fail(-1, error_msg.c_str());
return;
}
line >> m_code;
std::getline(line, m_server_message);
m_state = read_header;
}
}
if (m_state == read_header)
{
std::vector<char>::iterator end = m_buffer.begin() + m_recv_pos;
std::vector<char>::iterator newline
= std::find(m_buffer.begin(), end, '\n');
std::string line;
while (newline != end && m_state == read_header)
{
line.assign(m_buffer.begin(), newline);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log(line);
#endif
if (line.substr(0, 16) == "Content-Length: ")
{
try
{
m_content_length = boost::lexical_cast<int>(
line.substr(16, line.length() - 17));
}
catch(boost::bad_lexical_cast&)
{
fail(-1, "invalid content-length in tracker response");
return;
}
if (m_content_length > m_settings.tracker_maximum_response_length)
{
fail(-1, "content-length is greater than maximum response length");
return;
}
if (m_content_length < minimum_tracker_response_length && m_code == 200)
{
fail(-1, "content-length is smaller than minimum response length");
return;
}
}
else if (line.substr(0, 18) == "Content-Encoding: ")
{
if (line.substr(18, 4) == "gzip" || line.substr(18, 6) == "x-gzip")
{
m_content_encoding = gzip;
}
else
{
std::string error_str = "unknown content encoding in response: \"";
error_str += line.substr(18, line.length() - 18 - 2);
error_str += "\"";
fail(-1, error_str.c_str());
return;
}
}
else if (line.substr(0, 10) == "Location: ")
{
m_location.assign(line.begin() + 10, line.end());
}
else if (line.substr(0, 7) == "Server: ")
{
m_server.assign(line.begin() + 7, line.end());
}
else if (line.size() < 3)
{
m_state = read_body;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("end of http header");
#endif
if (m_code >= 300 && m_code < 400)
{
if (m_location.empty())
{
std::string error_str = "got redirection response (";
error_str += boost::lexical_cast<std::string>(m_code);
error_str += ") without 'Location' header";
fail(-1, error_str.c_str());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\"");
#endif
tracker_request req = tracker_req();
std::string::size_type i = m_location.find('?');
if (i == std::string::npos)
req.url = m_location;
else
req.url.assign(m_location.begin(), m_location.begin() + i);
m_man.queue_request(m_socket->io_service(), req
, m_password, m_requester);
close();
return;
}
}
++newline;
assert(m_recv_pos <= (int)m_buffer.size());
m_recv_pos -= (int)std::distance(m_buffer.begin(), newline);
m_buffer.erase(m_buffer.begin(), newline);
assert(m_recv_pos <= (int)m_buffer.size());
end = m_buffer.begin() + m_recv_pos;
newline = std::find(m_buffer.begin(), end, '\n');
}
}
if (m_state == read_body)
{
if (m_recv_pos == m_content_length)
{
on_response();
close();
return;
}
}
else if (m_recv_pos > m_content_length && m_content_length > 0)
{
fail(-1, "invalid tracker response (body > content_length)");
return;
}
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
};
void http_tracker_connection::on_response()
{
// GZIP
if (m_content_encoding == gzip)
{
boost::shared_ptr<request_callback> r = m_requester.lock();
if (!r)
{
close();
return;
}
if (inflate_gzip(m_buffer, tracker_request(), r.get(),
m_settings.tracker_maximum_response_length))
{
close();
return;
}
}
// handle tracker response
try
{
entry e = bdecode(m_buffer.begin(), m_buffer.end());
parse(e);
}
catch (std::exception& e)
{
std::string error_str(e.what());
error_str += ": ";
error_str.append(m_buffer.begin(), m_buffer.end());
fail(m_code, error_str.c_str());
}
#ifndef NDEBUG
catch (...)
{
assert(false);
}
#endif
}
peer_entry http_tracker_connection::extract_peer_info(const entry& info)
{
peer_entry ret;
// extract peer id (if any)
entry const* i = info.find_key("peer id");
if (i != 0)
{
if (i->string().length() != 20)
throw std::runtime_error("invalid response from tracker");
std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
}
else
{
// if there's no peer_id, just initialize it to a bunch of zeroes
std::fill_n(ret.pid.begin(), 20, 0);
}
// extract ip
i = info.find_key("ip");
if (i == 0) throw std::runtime_error("invalid response from tracker");
ret.ip = i->string();
// extract port
i = info.find_key("port");
if (i == 0) throw std::runtime_error("invalid response from tracker");
ret.port = (unsigned short)i->integer();
return ret;
}
void http_tracker_connection::parse(entry const& e)
{
if (!has_requester()) return;
try
{
// parse the response
try
{
entry const& failure = e["failure reason"];
fail(m_code, failure.string().c_str());
return;
}
catch (type_error const&) {}
try
{
entry const& warning = e["warning message"];
if (has_requester())
requester().tracker_warning(warning.string());
}
catch(type_error const&) {}
std::vector<peer_entry> peer_list;
if (tracker_req().kind == tracker_request::scrape_request)
{
std::string ih;
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
, std::back_inserter(ih));
entry scrape_data = e["files"][ih];
int complete = scrape_data["complete"].integer();
int incomplete = scrape_data["incomplete"].integer();
requester().tracker_response(tracker_request(), peer_list, 0, complete
, incomplete);
return;
}
int interval = (int)e["interval"].integer();
if (e["peers"].type() == entry::string_t)
{
std::string const& peers = e["peers"].string();
for (std::string::const_iterator i = peers.begin();
i != peers.end();)
{
if (std::distance(i, peers.end()) < 6) break;
peer_entry p;
p.pid.clear();
std::stringstream ip_str;
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i);
p.ip = ip_str.str();
p.port = detail::read_uint16(i);
peer_list.push_back(p);
}
}
else
{
entry::list_type const& l = e["peers"].list();
for(entry::list_type::const_iterator i = l.begin(); i != l.end(); ++i)
{
peer_entry p = extract_peer_info(*i);
peer_list.push_back(p);
}
}
// look for optional scrape info
int complete = -1;
int incomplete = -1;
try { complete = e["complete"].integer(); }
catch(type_error&) {}
try { incomplete = e["incomplete"].integer(); }
catch(type_error&) {}
requester().tracker_response(tracker_request(), peer_list, interval, complete
, incomplete);
}
catch(type_error& e)
{
requester().tracker_request_error(tracker_request(), m_code, e.what());
}
catch(std::runtime_error& e)
{
requester().tracker_request_error(tracker_request(), m_code, e.what());
}
}
}

View File

@ -1,326 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/optional.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/identify_client.hpp"
#include "libtorrent/fingerprint.hpp"
namespace
{
using namespace libtorrent;
int decode_digit(char c)
{
if (std::isdigit(c)) return c - '0';
return unsigned(c) - 'A' + 10;
}
// takes a peer id and returns a valid boost::optional
// object if the peer id matched the azureus style encoding
// the returned fingerprint contains information about the
// client's id
boost::optional<fingerprint> parse_az_style(const peer_id& id)
{
fingerprint ret("..", 0, 0, 0, 0);
if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
|| (id[3] < '0') || (id[4] < '0')
|| (id[5] < '0') || (id[6] < '0')
|| id[7] != '-')
return boost::optional<fingerprint>();
ret.name[0] = id[1];
ret.name[1] = id[2];
ret.major_version = decode_digit(id[3]);
ret.minor_version = decode_digit(id[4]);
ret.revision_version = decode_digit(id[5]);
ret.tag_version = decode_digit(id[6]);
return boost::optional<fingerprint>(ret);
}
// checks if a peer id can possibly contain a shadow-style
// identification
boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
{
fingerprint ret("..", 0, 0, 0, 0);
if (!std::isalnum(id[0]))
return boost::optional<fingerprint>();
if (std::equal(id.begin()+4, id.begin()+6, "--"))
{
if ((id[1] < '0') || (id[2] < '0')
|| (id[3] < '0'))
return boost::optional<fingerprint>();
ret.major_version = decode_digit(id[1]);
ret.minor_version = decode_digit(id[2]);
ret.revision_version = decode_digit(id[3]);
}
else
{
if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
return boost::optional<fingerprint>();
ret.major_version = id[1];
ret.minor_version = id[2];
ret.revision_version = id[3];
}
ret.name[0] = id[0];
ret.name[1] = 0;
ret.tag_version = 0;
return boost::optional<fingerprint>(ret);
}
// checks if a peer id can possibly contain a mainline-style
// identification
boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
{
char ids[21];
std::copy(id.begin(), id.end(), ids);
ids[20] = 0;
fingerprint ret("..", 0, 0, 0, 0);
ret.name[1] = 0;
ret.tag_version = 0;
if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
, &ret.revision_version) != 4
|| !std::isprint(ret.name[0]))
return boost::optional<fingerprint>();
return boost::optional<fingerprint>(ret);
}
typedef std::pair<char const*, char const*> map_entry;
// only support BitTorrentSpecification
// must be ordered alphabetically
map_entry name_map[] =
{
map_entry("A", "ABC")
, map_entry("AR", "Arctic Torrent")
, map_entry("AX", "BitPump")
, map_entry("AZ", "Azureus")
, map_entry("BB", "BitBuddy")
, map_entry("BC", "BitComet")
, map_entry("BS", "BTSlave")
, map_entry("BX", "BittorrentX")
, map_entry("CD", "Enhanced CTorrent")
, map_entry("CT", "CTorrent")
, map_entry("DE", "Deluge")
, map_entry("ES", "electric sheep")
, map_entry("KT", "KTorrent")
, map_entry("LP", "lphant")
, map_entry("LT", "libtorrent")
, map_entry("M", "Mainline")
, map_entry("MP", "MooPolice")
, map_entry("MT", "Moonlight Torrent")
, map_entry("O", "Osprey Permaseed")
, map_entry("R", "Tribler")
, map_entry("S", "Shadow")
, map_entry("SB", "Swiftbit")
, map_entry("SN", "ShareNet")
, map_entry("SS", "SwarmScope")
, map_entry("SZ", "Shareaza")
, map_entry("T", "BitTornado")
, map_entry("TN", "Torrent.NET")
, map_entry("TR", "Transmission")
, map_entry("TS", "TorrentStorm")
, map_entry("U", "UPnP")
, map_entry("UL", "uLeecher")
, map_entry("UT", "MicroTorrent")
, map_entry("XT", "XanTorrent")
, map_entry("ZT", "ZipTorrent")
, map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
, map_entry("pX", "pHoeniX")
, map_entry("qB", "qBittorrent")
};
bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
{
return lhs.first[0] < rhs.first[0]
|| ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
}
std::string lookup(fingerprint const& f)
{
std::stringstream identity;
const int size = sizeof(name_map)/sizeof(name_map[0]);
map_entry* i =
std::lower_bound(name_map, name_map + size
, map_entry(f.name, ""), &compare_first_string);
#ifndef NDEBUG
for (int i = 1; i < size; ++i)
{
assert(compare_first_string(name_map[i-1]
, name_map[i]));
}
#endif
if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
identity << i->second;
else
{
identity << f.name[0];
if (f.name[1] != 0) identity << f.name[1];
}
identity << " " << (int)f.major_version
<< "." << (int)f.minor_version
<< "." << (int)f.revision_version;
if (f.name[1] != 0)
identity << "." << (int)f.tag_version;
return identity.str();
}
bool find_string(unsigned char const* id, char const* search)
{
return std::equal(search, search + std::strlen(search), id);
}
}
namespace libtorrent
{
boost::optional<fingerprint> client_fingerprint(peer_id const& p)
{
// look for azureus style id
boost::optional<fingerprint> f;
f = parse_az_style(p);
if (f) return f;
// look for shadow style id
f = parse_shadow_style(p);
if (f) return f;
// look for mainline style id
f = parse_mainline_style(p);
if (f) return f;
return f;
}
std::string identify_client(peer_id const& p)
{
peer_id::const_iterator PID = p.begin();
boost::optional<fingerprint> f;
if (p.is_all_zeros()) return "Unknown";
// ----------------------
// non standard encodings
// ----------------------
if (find_string(PID, "Deadman Walking-")) return "Deadman";
if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
if (find_string(PID, "DansClient")) return "XanTorrent";
if (find_string(PID + 4, "btfans")) return "SimpleBT";
if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
if (find_string(PID, "martini")) return "Martini Man";
if (find_string(PID, "Plus---")) return "Bittorrent Plus";
if (find_string(PID, "turbobt")) return "TurboBT";
if (find_string(PID, "a00---0")) return "Swarmy";
if (find_string(PID, "a02---0")) return "Swarmy";
if (find_string(PID, "T00---0")) return "Teeweety";
if (find_string(PID, "BTDWV-")) return "Deadman Walking";
if (find_string(PID + 2, "BS")) return "BitSpirit";
if (find_string(PID, "btuga")) return "BTugaXP";
if (find_string(PID, "oernu")) return "BTugaXP";
if (find_string(PID, "Mbrst")) return "Burst!";
if (find_string(PID, "Plus")) return "Plus!";
if (find_string(PID, "-Qt-")) return "Qt";
if (find_string(PID, "exbc")) return "BitComet";
if (find_string(PID, "-G3")) return "G3 Torrent";
if (find_string(PID, "XBT")) return "XBT";
if (find_string(PID, "OP")) return "Opera";
if (find_string(PID, "-BOW") && PID[7] == '-')
return "Bits on Wheels " + std::string(PID + 4, PID + 7);
if (find_string(PID, "eX"))
{
std::string user(PID + 2, PID + 14);
return std::string("eXeem ('") + user.c_str() + "')";
}
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
return "Experimental 3.2.1b2";
if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
return "Experimental 3.1";
// look for azureus style id
f = parse_az_style(p);
if (f) return lookup(*f);
// look for shadow style id
f = parse_shadow_style(p);
if (f) return lookup(*f);
// look for mainline style id
f = parse_mainline_style(p);
if (f) return lookup(*f);
if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
return "Generic";
std::string unknown("Unknown [");
for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
{
unknown += std::isprint(*i)?*i:'.';
}
unknown += "]";
return unknown;
}
}

View File

@ -1,80 +0,0 @@
/*
Copyright (c) 2005, Arvid Norberg
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.
*/
#include "libtorrent/ip_filter.hpp"
#include <boost/utility.hpp>
//#include <iostream>
namespace libtorrent
{
void ip_filter::add_rule(address first, address last, int flags)
{
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);
}
int ip_filter::access(address const& addr) const
{
if (addr.is_v4())
return m_filter4.access(addr.to_v4());
assert(addr.is_v6());
return m_filter6.access(addr.to_v6());
}
ip_filter::filter_tuple_t ip_filter::export_filter() const
{
return boost::make_tuple(m_filter4.export_filter()
, m_filter6.export_filter());
}
/*
void ip_filter::print() const
{
for (range_t::iterator i = m_access_list.begin(); i != m_access_list.end(); ++i)
{
std::cout << i->start.as_string() << " " << i->access << "\n";
}
}
*/
}

View File

@ -1,145 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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.
*/
#include <libtorrent/kademlia/closest_nodes.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef boost::shared_ptr<observer> observer_ptr;
class closest_nodes_observer : public observer
{
public:
closest_nodes_observer(
boost::intrusive_ptr<traversal_algorithm> const& algorithm
, node_id self
, node_id target
)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& p)
{
p.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<traversal_algorithm> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void closest_nodes_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void closest_nodes_observer::timeout()
{
m_algorithm->failed(m_self);
}
closest_nodes::closest_nodes(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
{
boost::intrusive_ptr<closest_nodes> self(this);
add_requests();
}
void closest_nodes::invoke(node_id const& id, udp::endpoint addr)
{
observer_ptr p(new closest_nodes_observer(this, id, m_target));
m_rpc.invoke(messages::find_node, addr, p);
}
void closest_nodes::done()
{
std::vector<node_entry> results;
int result_size = m_table.bucket_size();
if (result_size > (int)m_results.size()) result_size = (int)m_results.size();
for (std::vector<result>::iterator i = m_results.begin()
, end(m_results.begin() + result_size); i != end; ++i)
{
results.push_back(node_entry(i->id, i->addr));
}
m_done_callback(results);
}
void closest_nodes::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
new closest_nodes(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

View File

@ -1,905 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg
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.
*/
#include <fstream>
#include <set>
#include <numeric>
#include <stdexcept>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/ref.hpp>
#include <boost/optional.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/operations.hpp>
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/traversal_algorithm.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/version.hpp"
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::second_clock;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::hours;
using boost::posix_time::milliseconds;
using boost::ref;
using boost::lexical_cast;
using libtorrent::dht::node_impl;
using libtorrent::dht::node_id;
using libtorrent::dht::packet_t;
using libtorrent::dht::msg;
using libtorrent::dht::packet_iterator;
namespace messages = libtorrent::dht::messages;
using namespace libtorrent::detail;
using asio::ip::udp;
typedef asio::ip::address_v4 address;
namespace
{
const int tick_period = 1; // minutes
struct count_peers
{
int& count;
count_peers(int& c): count(c) {}
void operator()(std::pair<libtorrent::dht::node_id
, libtorrent::dht::torrent_entry> const& t)
{
count += std::distance(t.second.peers.begin()
, t.second.peers.end());
}
};
boost::optional<node_id> read_id(libtorrent::entry const& d)
{
using namespace libtorrent;
using libtorrent::dht::node_id;
if (d.type() != entry::dictionary_t) return boost::optional<node_id>();
entry const* nid = d.find_key("node-id");
if (!nid
|| nid->type() != entry::string_t
|| nid->string().length() != 40)
return boost::optional<node_id>();
return boost::optional<node_id>(
boost::lexical_cast<node_id>(nid->string()));
}
template <class EndpointType>
void read_endpoint_list(libtorrent::entry const* n, std::vector<EndpointType>& epl)
{
using namespace libtorrent;
entry::list_type const& contacts = n->list();
for (entry::list_type::const_iterator i = contacts.begin()
, end(contacts.end()); i != end; ++i)
{
std::string const& p = i->string();
if (p.size() < 6) continue;
std::string::const_iterator in = p.begin();
if (p.size() == 6)
epl.push_back(read_v4_endpoint<EndpointType>(in));
else if (p.size() == 18)
epl.push_back(read_v6_endpoint<EndpointType>(in));
}
}
}
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(dht_tracker)
#endif
// class that puts the networking and the kademlia node in a single
// unit and connecting them together.
dht_tracker::dht_tracker(asio::io_service& d, dht_settings const& settings
, asio::ip::address listen_interface, entry const& bootstrap)
: m_demuxer(d)
, m_socket(m_demuxer, udp::endpoint(listen_interface, settings.service_port))
, m_dht(bind(&dht_tracker::send_packet, this, _1), settings
, read_id(bootstrap))
, m_buffer(0)
, m_last_refresh(second_clock::universal_time() - hours(1))
, m_timer(m_demuxer)
, m_connection_timer(m_demuxer)
, m_refresh_timer(m_demuxer)
, m_settings(settings)
, m_refresh_bucket(160)
, m_host_resolver(d)
{
using boost::bind;
m_in_buf[0].resize(1000);
m_in_buf[1].resize(1000);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
m_counter = 0;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
m_mp_message_input = 0;
m_gr_message_input = 0;
m_total_in_bytes = 0;
m_total_out_bytes = 0;
m_queries_out_bytes = 0;
// turns on and off individual components' logging
// rpc_log().enable(false);
// node_log().enable(false);
// traversal_log().enable(false);
// dht_tracker_log.enable(false);
#endif
std::vector<udp::endpoint> initial_nodes;
if (bootstrap.type() == entry::dictionary_t)
{
try
{
if (entry const* nodes = bootstrap.find_key("nodes"))
read_endpoint_list<udp::endpoint>(nodes, initial_nodes);
} catch (std::exception&) {}
}
m_dht.bootstrap(initial_nodes, bind(&dht_tracker::on_bootstrap, this));
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
m_timer.expires_from_now(seconds(1));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_connection_timer.expires_from_now(seconds(10));
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
m_refresh_timer.expires_from_now(minutes(15));
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
}
void dht_tracker::dht_status(session_status& s)
{
boost::tie(s.m_dht_nodes, s.m_dht_node_cache) = m_dht.size();
s.m_dht_torrents = m_dht.data_size();
}
void dht_tracker::connection_timeout(asio::error const& e)
try
{
if (e) return;
time_duration d = m_dht.connection_timeout();
m_connection_timer.expires_from_now(d);
m_connection_timer.async_wait(bind(&dht_tracker::connection_timeout, this, _1));
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::refresh_timeout(asio::error const& e)
try
{
if (e) return;
time_duration d = m_dht.refresh_timeout();
m_refresh_timer.expires_from_now(d);
m_refresh_timer.async_wait(bind(&dht_tracker::refresh_timeout, this, _1));
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::rebind(asio::ip::address listen_interface, int listen_port)
{
m_socket.close();
m_socket.open(asio::ip::udp::v4());
m_socket.bind(udp::endpoint(listen_interface, listen_port));
}
void dht_tracker::tick(asio::error const& e)
try
{
if (e) return;
m_timer.expires_from_now(minutes(tick_period));
m_timer.async_wait(bind(&dht_tracker::tick, this, _1));
m_dht.new_write_key();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
static bool first = true;
if (first)
{
boost::filesystem::create_directory("libtorrent_logs");
}
std::ofstream st("libtorrent_logs/routing_table_state.txt", std::ios_base::trunc);
m_dht.print_state(st);
// count torrents
int torrents = std::distance(m_dht.begin_data(), m_dht.end_data());
// count peers
int peers = 0;
std::for_each(m_dht.begin_data(), m_dht.end_data(), count_peers(peers));
std::ofstream pc("libtorrent_logs/dht_stats.log", std::ios_base::app);
if (first)
{
first = false;
using boost::posix_time::to_simple_string;
pc << "\n\n ***** starting log at " << to_simple_string(
second_clock::universal_time()) << " *****\n\n"
<< "minute:active nodes:passive nodes"
":ping replies sent:ping queries recvd:ping"
":ping replies sent:ping queries recvd:ping"
":find_node replies bytes sent:find_node queries bytes recv"
":find_node replies bytes sent:find_node queries bytes recv"
":get_peers replies sent:get_peers queries recvd:get_peers"
":get_peers replies bytes sent:get_peers queries bytes recv"
":announce_peer replies sent:announce_peer queries recvd:announce_peer"
":announce_peer replies bytes sent:announce_peer queries bytes recv"
":error replies sent:error queries recvd:error"
":error replies bytes sent:error queries bytes recv"
":num torrents:num peers:announces per min"
":failed announces per min:total msgs per min"
":ut msgs per min:lt msgs per min:mp msgs per min"
":gr msgs per min:bytes in per sec:bytes out per sec"
":queries out bytes per sec\n\n";
}
int active;
int passive;
boost::tie(active, passive) = m_dht.size();
pc << (m_counter * tick_period)
<< "\t" << active
<< "\t" << passive;
for (int i = 0; i < 5; ++i)
pc << "\t" << (m_replies_sent[i] / float(tick_period))
<< "\t" << (m_queries_received[i] / float(tick_period))
<< "\t" << (m_replies_bytes_sent[i] / float(tick_period*60))
<< "\t" << (m_queries_bytes_received[i] / float(tick_period*60));
pc << "\t" << torrents
<< "\t" << peers
<< "\t" << m_announces / float(tick_period)
<< "\t" << m_failed_announces / float(tick_period)
<< "\t" << (m_total_message_input / float(tick_period))
<< "\t" << (m_ut_message_input / float(tick_period))
<< "\t" << (m_lt_message_input / float(tick_period))
<< "\t" << (m_mp_message_input / float(tick_period))
<< "\t" << (m_gr_message_input / float(tick_period))
<< "\t" << (m_total_in_bytes / float(tick_period*60))
<< "\t" << (m_total_out_bytes / float(tick_period*60))
<< "\t" << (m_queries_out_bytes / float(tick_period*60))
<< std::endl;
++m_counter;
std::fill_n(m_replies_bytes_sent, 5, 0);
std::fill_n(m_queries_bytes_received, 5, 0);
std::fill_n(m_replies_sent, 5, 0);
std::fill_n(m_queries_received, 5, 0);
m_announces = 0;
m_failed_announces = 0;
m_total_message_input = 0;
m_ut_message_input = 0;
m_lt_message_input = 0;
m_total_in_bytes = 0;
m_total_out_bytes = 0;
m_queries_out_bytes = 0;
#endif
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::announce(sha1_hash const& ih, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&
, sha1_hash const&)> f)
{
m_dht.announce(ih, listen_port, f);
}
// translate bittorrent kademlia message into the generice kademlia message
// used by the library
void dht_tracker::on_receive(asio::error const& error, size_t bytes_transferred)
try
{
if (error == asio::error::operation_aborted) return;
int current_buffer = m_buffer;
m_buffer = (m_buffer + 1) & 1;
m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
, bind(&dht_tracker::on_receive, this, _1, _2));
if (error) return;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_total_message_input;
m_total_in_bytes += bytes_transferred;
#endif
try
{
using libtorrent::entry;
using libtorrent::bdecode;
assert(bytes_transferred > 0);
entry e = bdecode(m_in_buf[current_buffer].begin()
, m_in_buf[current_buffer].end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " RECEIVED [" << m_remote_endpoint[current_buffer]
<< "]:";
#endif
libtorrent::dht::msg m;
m.message_id = 0;
m.addr = m_remote_endpoint[current_buffer];
m.transaction_id = e["t"].string();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
try
{
entry const* ver = e.find_key("v");
if (!ver) throw std::exception();
std::string const& client = ver->string();
if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
{
++m_ut_message_input;
TORRENT_LOG(dht_tracker) << " client: uTorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
{
++m_lt_message_input;
TORRENT_LOG(dht_tracker) << " client: libtorrent";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
{
++m_mp_message_input;
TORRENT_LOG(dht_tracker) << " client: MooPolice";
}
else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
{
++m_gr_message_input;
TORRENT_LOG(dht_tracker) << " client: GetRight";
}
else
{
TORRENT_LOG(dht_tracker) << " client: generic";
}
}
catch (std::exception&)
{
TORRENT_LOG(dht_tracker) << " client: generic";
};
#endif
std::string const& msg_type = e["y"].string();
if (msg_type == "r")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: transaction: "
<< m.transaction_id;
#endif
m.reply = true;
entry const& r = e["r"];
std::string const& id = r["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
if (entry const* n = r.find_key("values"))
{
m.peers.clear();
read_endpoint_list<tcp::endpoint>(n, m.peers);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
m.nodes.clear();
if (entry const* n = r.find_key("nodes"))
{
std::string const& nodes = n->string();
std::string::const_iterator i = nodes.begin();
std::string::const_iterator end = nodes.end();
while (std::distance(i, end) >= 26)
{
node_id id;
std::copy(i, i + 20, id.begin());
i += 20;
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v4_endpoint<udp::endpoint>(i)));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
if (entry const* n = r.find_key("nodes2"))
{
entry::list_type const& contacts = n->list();
for (entry::list_type::const_iterator i = contacts.begin()
, end(contacts.end()); i != end; ++i)
{
std::string const& p = i->string();
if (p.size() < 6 + 20) continue;
std::string::const_iterator in = p.begin();
node_id id;
std::copy(in, in + 20, id.begin());
in += 20;
if (p.size() == 6 + 20)
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v4_endpoint<udp::endpoint>(in)));
else if (p.size() == 18 + 20)
m.nodes.push_back(libtorrent::dht::node_entry(
id, read_v6_endpoint<udp::endpoint>(in)));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes2 + nodes: " << m.nodes.size();
#endif
}
entry const* token = r.find_key("token");
if (token) m.write_token = *token;
}
else if (msg_type == "q")
{
m.reply = false;
entry const& a = e["a"];
std::string const& id = a["id"].string();
if (id.size() != 20) throw std::runtime_error("invalid size of id");
std::copy(id.begin(), id.end(), m.id.begin());
std::string request_kind(e["q"].string());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: " << request_kind;
#endif
if (request_kind == "ping")
{
m.message_id = libtorrent::dht::messages::ping;
}
else if (request_kind == "find_node")
{
std::string const& target = a["target"].string();
if (target.size() != 20) throw std::runtime_error("invalid size of target id");
std::copy(target.begin(), target.end(), m.info_hash.begin());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
m.message_id = libtorrent::dht::messages::find_node;
}
else if (request_kind == "get_peers")
{
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.message_id = libtorrent::dht::messages::get_peers;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
}
else if (request_kind == "announce_peer")
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
++m_announces;
#endif
std::string const& info_hash = a["info_hash"].string();
if (info_hash.size() != 20)
throw std::runtime_error("invalid size of info-hash");
std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
m.port = a["port"].integer();
m.write_token = a["token"];
m.message_id = libtorrent::dht::messages::announce_peer;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
TORRENT_LOG(dht_tracker) << " port: " << m.port;
if (!m_dht.verify_token(m))
++m_failed_announces;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED REQUEST *** : "
<< request_kind;
#endif
throw std::runtime_error("unsupported request: " + request_kind);
}
}
else if (msg_type == "e")
{
entry::list_type const& list = e["e"].list();
m.message_id = messages::error;
m.error_msg = list.back().string();
m.error_code = list.front().integer();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " *** UNSUPPORTED MESSAGE TYPE *** : "
<< msg_type;
#endif
throw std::runtime_error("unsupported message type: " + msg_type);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
if (!m.reply)
{
++m_queries_received[m.message_id];
m_queries_bytes_received[m.message_id] += int(bytes_transferred);
}
TORRENT_LOG(dht_tracker) << e;
#endif
m_dht.incoming(m);
}
catch (std::exception& e)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
<< e.what();
#endif
}
}
catch (std::exception& e)
{
assert(false);
};
entry dht_tracker::state() const
{
entry ret(entry::dictionary_t);
{
entry nodes(entry::list_t);
for (node_impl::iterator i(m_dht.begin())
, end(m_dht.end()); i != end; ++i)
{
std::string node;
std::back_insert_iterator<std::string> out(node);
write_endpoint(i->addr, out);
nodes.list().push_back(entry(node));
}
bucket_t cache;
m_dht.replacement_cache(cache);
for (bucket_t::iterator i(cache.begin())
, end(cache.end()); i != end; ++i)
{
std::string node;
std::back_insert_iterator<std::string> out(node);
write_endpoint(i->addr, out);
nodes.list().push_back(entry(node));
}
if (!nodes.list().empty())
ret["nodes"] = nodes;
}
ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
return ret;
}
void dht_tracker::add_node(udp::endpoint node)
{
m_dht.add_node(node);
}
void dht_tracker::add_node(std::pair<std::string, int> const& node)
{
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_name_lookup
, this, _1, _2));
}
void dht_tracker::on_name_lookup(asio::error const& e
, udp::resolver::iterator host) try
{
if (e || host == udp::resolver::iterator()) return;
add_node(host->endpoint());
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::add_router_node(std::pair<std::string, int> const& node)
{
udp::resolver::query q(node.first, lexical_cast<std::string>(node.second));
m_host_resolver.async_resolve(q, bind(&dht_tracker::on_router_name_lookup
, this, _1, _2));
}
void dht_tracker::on_router_name_lookup(asio::error const& e
, udp::resolver::iterator host) try
{
if (e || host == udp::resolver::iterator()) return;
m_dht.add_router_node(host->endpoint());
}
catch (std::exception&)
{
assert(false);
};
void dht_tracker::on_bootstrap()
{}
void dht_tracker::send_packet(msg const& m)
{
using libtorrent::bencode;
using libtorrent::entry;
entry e(entry::dictionary_t);
e["t"] = m.transaction_id;
std::string version_str("LT ");
std::string::iterator i = version_str.begin() + 2;
detail::write_uint8(LIBTORRENT_VERSION_MAJOR, i);
detail::write_uint8(LIBTORRENT_VERSION_MINOR, i);
e["v"] = version_str;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << microsec_clock::universal_time()
<< " SENDING [" << m.addr << "]:";
TORRENT_LOG(dht_tracker) << " transaction: " << m.transaction_id;
// e.print(std::cerr);
#endif
if (m.message_id == messages::error)
{
assert(m.reply);
e["y"] = "e";
entry error_list(entry::list_t);
error_list.list().push_back(entry(m.error_code));
error_list.list().push_back(entry(m.error_msg));
e["e"] = error_list;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " error: " << m.error_code << " "
<< m.error_msg;
#endif
}
else if (m.reply)
{
e["y"] = "r";
e["r"] = entry(entry::dictionary_t);
entry& r = e["r"];
r["id"] = std::string(m.id.begin(), m.id.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " reply: "
<< messages::ids[m.message_id];
#endif
if (m.write_token.type() != entry::undefined_t)
r["token"] = m.write_token;
switch (m.message_id)
{
case messages::ping:
break;
case messages::find_node:
{
bool ipv6_nodes = false;
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
if (!i->addr.address().is_v4())
{
ipv6_nodes = true;
continue;
}
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
if (ipv6_nodes)
{
r["nodes2"] = entry(entry::list_t);
entry& p = r["nodes2"];
std::string endpoint;
endpoint.resize(6);
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
std::string::iterator out = endpoint.begin();
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
p.list().push_back(entry(endpoint));
}
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
break;
}
case messages::get_peers:
{
if (m.peers.empty())
{
r["nodes"] = entry(entry::string_t);
entry& n = r["nodes"];
std::back_insert_iterator<std::string> out(n.string());
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
if (!i->addr.address().is_v4()) continue;
std::copy(i->id.begin(), i->id.end(), out);
write_endpoint(i->addr, out);
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " nodes: " << m.nodes.size();
#endif
}
else
{
r["values"] = entry(entry::list_t);
entry& p = r["values"];
std::string endpoint;
endpoint.resize(6);
for (msg::peers_t::const_iterator i = m.peers.begin()
, end(m.peers.end()); i != end; ++i)
{
std::string::iterator out = endpoint.begin();
write_endpoint(*i, out);
p.list().push_back(entry(endpoint));
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " peers: " << m.peers.size();
#endif
}
break;
}
case messages::announce_peer:
break;
break;
}
}
else
{
e["y"] = "q";
e["a"] = entry(entry::dictionary_t);
entry& a = e["a"];
a["id"] = std::string(m.id.begin(), m.id.end());
if (m.write_token.type() != entry::undefined_t)
a["token"] = m.write_token;
assert(m.message_id <= messages::error);
e["q"] = messages::ids[m.message_id];
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " query: "
<< messages::ids[m.message_id];
#endif
switch (m.message_id)
{
case messages::find_node:
{
a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " target: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::get_peers:
{
a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " info_hash: "
<< boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
}
case messages::announce_peer:
a["port"] = m_settings.service_port;
a["info_hash"] = boost::lexical_cast<std::string>(m.info_hash);
a["token"] = m.write_token;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << " port: "
<< m_settings.service_port
<< " info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
#endif
break;
default: break;
}
}
m_send_buf.clear();
bencode(std::back_inserter(m_send_buf), e);
m_socket.send_to(asio::buffer(&m_send_buf[0]
, (int)m_send_buf.size()), m.addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
m_total_out_bytes += m_send_buf.size();
if (m.reply)
{
++m_replies_sent[m.message_id];
m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
}
else
{
m_queries_out_bytes += m_send_buf.size();
}
TORRENT_LOG(dht_tracker) << e;
#endif
if (!m.piggy_backed_ping) return;
msg pm;
pm.reply = false;
pm.piggy_backed_ping = false;
pm.message_id = messages::ping;
pm.transaction_id = m.ping_transaction_id;
pm.id = m.id;
pm.addr = m.addr;
send_packet(pm);
}
}}

View File

@ -1,156 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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.
*/
#include <libtorrent/kademlia/find_data.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/io.hpp>
namespace libtorrent { namespace dht
{
typedef boost::shared_ptr<observer> observer_ptr;
class find_data_observer : public observer
{
public:
find_data_observer(
boost::intrusive_ptr<find_data> const& algorithm
, node_id self
, node_id target)
: m_algorithm(algorithm)
, m_target(target)
, m_self(self)
{}
void send(msg& m)
{
m.reply = false;
m.message_id = messages::get_peers;
m.info_hash = m_target;
}
void timeout();
void reply(msg const&);
private:
boost::intrusive_ptr<find_data> m_algorithm;
node_id const m_target;
node_id const m_self;
};
void find_data_observer::reply(msg const& m)
{
if (!m.peers.empty())
{
m_algorithm->got_data(&m);
}
else
{
for (msg::nodes_t::const_iterator i = m.nodes.begin()
, end(m.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void find_data_observer::timeout()
{
m_algorithm->failed(m_self);
}
find_data::find_data(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
: traversal_algorithm(
target
, branch_factor
, max_results
, table
, rpc
, table.begin()
, table.end()
)
, m_done_callback(callback)
, m_done(false)
{
boost::intrusive_ptr<find_data> self(this);
add_requests();
}
void find_data::invoke(node_id const& id, asio::ip::udp::endpoint addr)
{
if (m_done)
{
m_invoke_count = -1;
return;
}
observer_ptr p(new find_data_observer(this, id, m_target));
m_rpc.invoke(messages::get_peers, addr, p);
}
void find_data::got_data(msg const* m)
{
m_done = true;
m_done_callback(m);
}
void find_data::done()
{
if (m_invoke_count != 0) return;
if (!m_done) m_done_callback(0);
}
void find_data::initiate(
node_id target
, int branch_factor
, int max_results
, routing_table& table
, rpc_manager& rpc
, done_callback const& callback
)
{
std::cerr << "find_data::initiate, key: " << target << "\n";
new find_data(target, branch_factor, max_results, table, rpc, callback);
}
} } // namespace libtorrent::dht

View File

@ -1,539 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg
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.
*/
#include <utility>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/function.hpp>
#include <boost/iterator_adaptors.hpp>
#include "libtorrent/io.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/random_sample.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/kademlia/rpc_manager.hpp"
#include "libtorrent/kademlia/packet_iterator.hpp"
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node.hpp"
#include "libtorrent/kademlia/refresh.hpp"
#include "libtorrent/kademlia/closest_nodes.hpp"
#include "libtorrent/kademlia/find_data.hpp"
using boost::bind;
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::minutes;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
namespace libtorrent { namespace dht
{
#ifdef _MSC_VER
namespace
{
char rand() { return (char)std::rand(); }
}
#endif
typedef boost::shared_ptr<observer> observer_ptr;
// TODO: configurable?
enum { announce_interval = 30 };
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(node)
#endif
node_id generate_id()
{
char random[20];
std::srand(std::time(0));
#ifdef _MSC_VER
std::generate(random, random + 20, &rand);
#else
std::generate(random, random + 20, &std::rand);
#endif
hasher h;
h.update(random, 20);
return h.final();
}
// remove peers that have timed out
void purge_peers(std::set<peer_entry>& peers)
{
for (std::set<peer_entry>::iterator i = peers.begin()
, end(peers.end()); i != end;)
{
// the peer has timed out
if (i->added + minutes(int(announce_interval * 1.5f)) < second_clock::universal_time())
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "peer timed out at: " << i->addr.address();
#endif
peers.erase(i++);
}
else
++i;
}
}
void nop() {}
node_impl::node_impl(boost::function<void(msg const&)> const& f
, dht_settings const& settings, boost::optional<node_id> node_id)
: m_settings(settings)
, m_id(node_id ? *node_id : generate_id())
, m_table(m_id, 8, settings)
, m_rpc(bind(&node_impl::incoming_request, this, _1)
, m_id, m_table, f)
, m_last_tracker_tick(boost::posix_time::second_clock::universal_time())
{
m_secret[0] = std::rand();
m_secret[1] = std::rand();
}
bool node_impl::verify_token(msg const& m)
{
if (m.write_token.type() != entry::string_t)
return false;
std::string const& token = m.write_token.string();
if (token.length() != 4) return false;
hasher h1;
std::string address = m.addr.address().to_string();
h1.update(&address[0], address.length());
h1.update((char*)&m_secret[0], sizeof(m_secret[0]));
h1.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash h = h1.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
hasher h2;
h2.update(&address[0], address.length());
h2.update((char*)&m_secret[1], sizeof(m_secret[1]));
h = h2.final();
if (std::equal(token.begin(), token.end(), (signed char*)&h[0]))
return true;
return false;
}
entry node_impl::generate_token(msg const& m)
{
std::string token;
token.resize(4);
hasher h;
std::string address = m.addr.address().to_string();
h.update(&address[0], address.length());
h.update((char*)&m_secret[0], sizeof(m_secret[0]));
h.update((char*)&m.info_hash[0], sha1_hash::size);
sha1_hash hash = h.final();
std::copy(hash.begin(), hash.begin() + 4, (signed char*)&token[0]);
return entry(token);
}
void node_impl::refresh(node_id const& id
, boost::function0<void> f)
{
// use the 'bucket size' closest nodes
// to start the refresh with
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(id, start, false);
refresh::initiate(id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::bootstrap(std::vector<udp::endpoint> const& nodes
, boost::function0<void> f)
{
std::vector<node_entry> start;
start.reserve(nodes.size());
std::copy(nodes.begin(), nodes.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, f);
}
void node_impl::refresh()
{
std::vector<node_entry> start;
start.reserve(m_table.size().get<0>());
std::copy(m_table.begin(), m_table.end(), std::back_inserter(start));
refresh::initiate(m_id, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
}
int node_impl::bucket_size(int bucket)
{
return m_table.bucket_size(bucket);
}
void node_impl::new_write_key()
{
m_secret[1] = m_secret[0];
m_secret[0] = std::rand();
}
void node_impl::refresh_bucket(int bucket)
{
assert(bucket >= 0 && bucket < 160);
// generate a random node_id within the given bucket
node_id target = generate_id();
int num_bits = 160 - bucket;
node_id mask(0);
for (int i = 0; i < num_bits; ++i)
{
int byte = i / 8;
mask[byte] |= 0x80 >> (i % 8);
}
node_id root = m_id;
root &= mask;
target &= ~mask;
target |= root;
// make sure this is in another subtree than m_id
// clear the (num_bits - 1) bit and then set it to the
// inverse of m_id's corresponding bit.
target[(num_bits - 1) / 8] &= ~(0x80 >> ((num_bits - 1) % 8));
target[(num_bits - 1) / 8] |=
(~(m_id[(num_bits - 1) / 8])) & (0x80 >> ((num_bits - 1) % 8));
assert(distance_exp(m_id, target) == bucket);
std::vector<node_entry> start;
start.reserve(m_table.bucket_size());
m_table.find_node(target, start, false, m_table.bucket_size());
refresh::initiate(target, m_settings.search_branching, 10, m_table.bucket_size()
, m_table, start.begin(), start.end(), m_rpc, bind(&nop));
m_table.touch_bucket(bucket);
}
void node_impl::incoming(msg const& m)
{
if (m_rpc.incoming(m))
{
refresh();
}
}
namespace
{
class announce_observer : public observer
{
public:
announce_observer(sha1_hash const& info_hash, int listen_port
, entry const& write_token)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_token(write_token)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
m.write_token = m_token;
}
void timeout() {}
void reply(msg const&) {}
private:
sha1_hash m_info_hash;
int m_listen_port;
entry m_token;
};
class get_peers_observer : public observer
{
public:
get_peers_observer(sha1_hash const& info_hash, int listen_port
, rpc_manager& rpc
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
: m_info_hash(info_hash)
, m_listen_port(listen_port)
, m_rpc(rpc)
, m_fun(f)
{}
void send(msg& m)
{
m.port = m_listen_port;
m.info_hash = m_info_hash;
}
void timeout() {}
void reply(msg const& r)
{
m_rpc.invoke(messages::announce_peer, r.addr
, boost::shared_ptr<observer>(
new announce_observer(m_info_hash, m_listen_port, r.write_token)));
m_fun(r.peers, m_info_hash);
}
private:
sha1_hash m_info_hash;
int m_listen_port;
rpc_manager& m_rpc;
boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> m_fun;
};
void announce_fun(std::vector<node_entry> const& v, rpc_manager& rpc
, int listen_port, sha1_hash const& ih
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
bool nodes = false;
// only store on the first k nodes
for (std::vector<node_entry>::const_iterator i = v.begin()
, end(v.end()); i != end; ++i)
{
rpc.invoke(messages::get_peers, i->addr, boost::shared_ptr<observer>(
new get_peers_observer(ih, listen_port, rpc, f)));
nodes = true;
}
}
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void node_impl::add_router_node(udp::endpoint router)
{
m_table.add_router_node(router);
}
void node_impl::add_node(udp::endpoint node)
{
// ping the node, and if we get a reply, it
// will be added to the routing table
observer_ptr p(new dummy_observer());
m_rpc.invoke(messages::ping, node, p);
}
void node_impl::announce(sha1_hash const& info_hash, int listen_port
, boost::function<void(std::vector<tcp::endpoint> const&, sha1_hash const&)> f)
{
// search for nodes with ids close to id, and then invoke the
// get_peers and then announce_peer rpc on them.
closest_nodes::initiate(info_hash, m_settings.search_branching
, m_table.bucket_size(), m_table, m_rpc
, boost::bind(&announce_fun, _1, boost::ref(m_rpc), listen_port
, info_hash, f));
}
time_duration node_impl::refresh_timeout()
{
int refresh = -1;
ptime now = second_clock::universal_time();
ptime next = now + minutes(15);
for (int i = 0; i < 160; ++i)
{
ptime r = m_table.next_refresh(i);
if (r <= now)
{
if (refresh == -1) refresh = i;
}
else if (r < next)
{
next = r;
}
}
if (refresh != -1)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(node) << "refreshing bucket: " << refresh;
#endif
refresh_bucket(refresh);
}
if (next < now + seconds(5)) return seconds(5);
return next - now;
}
time_duration node_impl::connection_timeout()
{
time_duration d = m_rpc.tick();
ptime now(second_clock::universal_time());
if (now - m_last_tracker_tick < minutes(10)) return d;
m_last_tracker_tick = now;
// look through all peers and see if any have timed out
for (data_iterator i = begin_data(), end(end_data()); i != end;)
{
torrent_entry& t = i->second;
node_id const& key = i->first;
++i;
purge_peers(t.peers);
// if there are no more peers, remove the entry altogether
if (t.peers.empty())
{
table_t::iterator i = m_map.find(key);
if (i != m_map.end()) m_map.erase(i);
}
}
return d;
}
void node_impl::on_announce(msg const& m, msg& reply)
{
if (!verify_token(m))
{
reply.message_id = messages::error;
reply.error_code = 203;
reply.error_msg = "Incorrect write token in announce_peer message";
return;
}
// the token was correct. That means this
// node is not spoofing its address. So, let
// the table get a chance to add it.
m_table.node_seen(m.id, m.addr);
torrent_entry& v = m_map[m.info_hash];
peer_entry e;
e.addr = tcp::endpoint(m.addr.address(), m.addr.port());
e.added = second_clock::universal_time();
std::set<peer_entry>::iterator i = v.peers.find(e);
if (i != v.peers.end()) v.peers.erase(i++);
v.peers.insert(i, e);
}
namespace
{
tcp::endpoint get_endpoint(peer_entry const& p)
{
return p.addr;
}
}
bool node_impl::on_find(msg const& m, std::vector<tcp::endpoint>& peers) const
{
table_t::const_iterator i = m_map.find(m.info_hash);
if (i == m_map.end()) return false;
torrent_entry const& v = i->second;
int num = (std::min)((int)v.peers.size(), m_settings.max_peers_reply);
peers.clear();
peers.reserve(num);
random_sample_n(boost::make_transform_iterator(v.peers.begin(), &get_endpoint)
, boost::make_transform_iterator(v.peers.end(), &get_endpoint)
, std::back_inserter(peers), num);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<tcp::endpoint>::iterator i = peers.begin()
, end(peers.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << *i;
}
#endif
return true;
}
void node_impl::incoming_request(msg const& m)
{
msg reply;
switch (m.message_id)
{
case messages::ping:
break;
case messages::get_peers:
{
reply.info_hash = m.info_hash;
reply.write_token = generate_token(m);
if (!on_find(m, reply.peers))
{
// we don't have any peers for this info_hash,
// return nodes instead
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
}
break;
case messages::find_node:
{
reply.info_hash = m.info_hash;
m_table.find_node(m.info_hash, reply.nodes, false);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
for (std::vector<node_entry>::iterator i = reply.nodes.begin()
, end(reply.nodes.end()); i != end; ++i)
{
TORRENT_LOG(node) << " " << i->id << " " << i->addr;
}
#endif
}
break;
case messages::announce_peer:
{
on_announce(m, reply);
}
break;
};
if (m_table.need_node(m.id))
m_rpc.reply_with_ping(reply, m);
else
m_rpc.reply(reply, m);
}
} } // namespace libtorrent::dht

View File

@ -1,97 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg
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.
*/
#include <algorithm>
#include <iomanip>
#include <cassert>
#include <boost/bind.hpp>
#include "libtorrent/kademlia/node_id.hpp"
using boost::bind;
namespace libtorrent { namespace dht
{
// returns the distance between the two nodes
// using the kademlia XOR-metric
node_id distance(node_id const& n1, node_id const& n2)
{
node_id ret;
node_id::iterator k = ret.begin();
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, ++k)
{
*k = *i ^ *j;
}
return ret;
}
// returns true if: distance(n1, ref) < distance(n2, ref)
bool compare_ref(node_id const& n1, node_id const& n2, node_id const& ref)
{
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, k = ref.begin(), end(n1.end()); i != end; ++i, ++j, ++k)
{
boost::uint8_t lhs = (*i ^ *k);
boost::uint8_t rhs = (*j ^ *k);
if (lhs < rhs) return true;
if (lhs > rhs) return false;
}
return false;
}
// returns n in: 2^n <= distance(n1, n2) < 2^(n+1)
// useful for finding out which bucket a node belongs to
int distance_exp(node_id const& n1, node_id const& n2)
{
int byte = node_id::size - 1;
for (node_id::const_iterator i = n1.begin(), j = n2.begin()
, end(n1.end()); i != end; ++i, ++j, --byte)
{
assert(byte >= 0);
boost::uint8_t t = *i ^ *j;
if (t == 0) continue;
// we have found the first non-zero byte
// return the bit-number of the first bit
// that differs
int bit = byte * 8;
for (int b = 7; b > 0; --b)
if (t >= (1 << b)) return bit + b;
return bit;
}
return 0;
}
} } // namespace libtorrent::dht

View File

@ -1,190 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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.
*/
#include <libtorrent/kademlia/refresh.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/io.hpp>
#include <boost/bind.hpp>
using boost::bind;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(refresh)
#endif
typedef boost::shared_ptr<observer> observer_ptr;
class refresh_observer : public observer
{
public:
refresh_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
, node_id target
)
: m_target(target)
, m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& m)
{
m.info_hash = m_target;
}
void timeout();
void reply(msg const& m);
private:
node_id const m_target;
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void refresh_observer::reply(msg const& in)
{
if (!in.nodes.empty())
{
for (msg::nodes_t::const_iterator i = in.nodes.begin()
, end(in.nodes.end()); i != end; ++i)
{
m_algorithm->traverse(i->id, i->addr);
}
}
m_algorithm->finished(m_self);
}
void refresh_observer::timeout()
{
m_algorithm->failed(m_self);
}
class ping_observer : public observer
{
public:
ping_observer(
boost::intrusive_ptr<refresh> const& algorithm
, node_id self
)
: m_self(self)
, m_algorithm(algorithm)
{}
void send(msg& p) {}
void timeout();
void reply(msg const& m);
private:
node_id const m_self;
boost::intrusive_ptr<refresh> m_algorithm;
};
void ping_observer::reply(msg const& m)
{
m_algorithm->ping_reply(m_self);
}
void ping_observer::timeout()
{
m_algorithm->ping_timeout(m_self);
}
void refresh::invoke(node_id const& nid, udp::endpoint addr)
{
observer_ptr p(new refresh_observer(
this
, nid
, m_target
));
m_rpc.invoke(messages::find_node, addr, p);
}
void refresh::done()
{
m_leftover_nodes_iterator = (int)m_results.size() > m_max_results ?
m_results.begin() + m_max_results : m_results.end();
invoke_pings_or_finish();
}
void refresh::ping_reply(node_id nid)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::ping_timeout(node_id nid)
{
m_active_pings--;
invoke_pings_or_finish();
}
void refresh::invoke_pings_or_finish()
{
while (m_active_pings < m_max_active_pings)
{
if (m_leftover_nodes_iterator == m_results.end()) break;
result const& node = *m_leftover_nodes_iterator;
// Skip initial nodes
if (node.flags & result::initial)
{
++m_leftover_nodes_iterator;
continue;
}
observer_ptr p(new ping_observer(this, node.id));
m_rpc.invoke(messages::ping, node.addr, p);
++m_active_pings;
++m_leftover_nodes_iterator;
}
if (m_active_pings == 0)
{
m_done_callback();
}
}
} } // namespace libtorrent::dht

View File

@ -1,434 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg
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.
*/
#include <vector>
#include <deque>
#include <algorithm>
#include <functional>
#include <numeric>
#include <boost/cstdint.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "libtorrent/kademlia/routing_table.hpp"
#include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/session_settings.hpp"
using boost::bind;
using boost::uint8_t;
using boost::posix_time::second_clock;
using boost::posix_time::minutes;
using boost::posix_time::seconds;
using boost::posix_time::hours;
namespace pt = boost::posix_time;
namespace libtorrent { namespace dht
{
using asio::ip::udp;
typedef asio::ip::address_v4 address;
routing_table::routing_table(node_id const& id, int bucket_size
, dht_settings const& settings)
: m_bucket_size(bucket_size)
, m_settings(settings)
, m_id(id)
, m_lowest_active_bucket(160)
{
// distribute the refresh times for the buckets in an
// attempt do even out the network load
for (int i = 0; i < 160; ++i)
m_bucket_activity[i] = second_clock::universal_time() - seconds(15*60 - i*5);
}
boost::tuple<int, int> routing_table::size() const
{
int nodes = 0;
int replacements = 0;
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
nodes += i->first.size();
replacements += i->second.size();
}
return boost::make_tuple(nodes, replacements);
}
void routing_table::print_state(std::ostream& os) const
{
os << "kademlia routing table state\n"
<< "bucket_size: " << m_bucket_size << "\n"
<< "node_id: " << m_id << "\n\n";
os << "number of nodes per bucket:\n"
"live\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->first.size()) > (7 - k) ? "|" : " ");
}
os << "\n";
}
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << "+";
}
os << "\n";
for (int k = 0; k < 8; ++k)
{
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
os << (int(i->second.size()) > k ? "|" : " ");
}
os << "\n";
}
os << "cached\n-----------\n";
os << "nodes:\n";
for (table_t::const_iterator i = m_buckets.begin(), end(m_buckets.end());
i != end; ++i)
{
int bucket_index = int(i - m_buckets.begin());
os << "bucket " << bucket_index << " "
<< to_simple_string(m_bucket_activity[bucket_index])
<< " " << (bucket_index >= m_lowest_active_bucket?"active":"inactive")
<< "\n";
for (bucket_t::const_iterator j = i->first.begin()
, end(i->first.end()); j != end; ++j)
{
os << "ip: " << j->addr << " fails: " << j->fail_count
<< " id: " << j->id << "\n";
}
}
}
void routing_table::touch_bucket(int bucket)
{
m_bucket_activity[bucket] = second_clock::universal_time();
}
boost::posix_time::ptime routing_table::next_refresh(int bucket)
{
assert(bucket < 160);
assert(bucket >= 0);
// lower than or equal to since a refresh of bucket 0 will
// effectively refresh the lowest active bucket as well
if (bucket <= m_lowest_active_bucket && bucket > 0)
return second_clock::universal_time() + minutes(15);
return m_bucket_activity[bucket] + minutes(15);
}
void routing_table::replacement_cache(bucket_t& nodes) const
{
for (table_t::const_iterator i = m_buckets.begin()
, end(m_buckets.end()); i != end; ++i)
{
std::copy(i->second.begin(), i->second.end()
, std::back_inserter(nodes));
}
}
bool routing_table::need_node(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
// if the replacement cache is full, we don't
// need another node. The table is fine the
// way it is.
if ((int)rb.size() >= m_bucket_size) return false;
// if the node already exists, we don't need it
if (std::find_if(b.begin(), b.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != b.end()) return false;
if (std::find_if(rb.begin(), rb.end(), bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id)) != rb.end()) return false;
return true;
}
void routing_table::node_failed(node_id const& id)
{
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t& rb = m_buckets[bucket_index].second;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
if (i == b.end()) return;
// if messages to ourself fails, ignore it
if (bucket_index == 0) return;
if (rb.empty())
{
++i->fail_count;
if (i->fail_count >= m_settings.max_fail_count)
{
b.erase(i);
assert(m_lowest_active_bucket <= bucket_index);
while (m_buckets[m_lowest_active_bucket].first.empty()
&& m_lowest_active_bucket < 160)
{
++m_lowest_active_bucket;
}
}
return;
}
b.erase(i);
b.push_back(rb.back());
rb.erase(rb.end() - 1);
}
void routing_table::add_router_node(udp::endpoint router)
{
m_router_nodes.insert(router);
}
// this function is called every time the node sees
// a sign of a node being alive. This node will either
// be inserted in the k-buckets or be moved to the top
// of its bucket.
// the return value indicates if the table needs a refresh.
// if true, the node should refresh the table (i.e. do a find_node
// on its own id)
bool routing_table::node_seen(node_id const& id, udp::endpoint addr)
{
if (m_router_nodes.find(addr) != m_router_nodes.end()) return false;
int bucket_index = distance_exp(m_id, id);
assert(bucket_index < (int)m_buckets.size());
assert(bucket_index >= 0);
bucket_t& b = m_buckets[bucket_index].first;
bucket_t::iterator i = std::find_if(b.begin(), b.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
bool ret = need_bootstrap();
m_bucket_activity[bucket_index] = second_clock::universal_time();
if (i != b.end())
{
// TODO: what do we do if we see a node with
// the same id as a node at a different address?
// assert(i->addr == addr);
// we already have the node in our bucket
// just move it to the back since it was
// the last node we had any contact with
// in this bucket
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing node: " << id << " " << addr;
return ret;
}
// if the node was not present in our list
// we will only insert it if there is room
// for it, or if some of our nodes have gone
// offline
if ((int)b.size() < m_bucket_size)
{
b.push_back(node_entry(id, addr));
// if bucket index is 0, the node is ourselves
// don't updated m_lowest_active_bucket
if (bucket_index < m_lowest_active_bucket
&& bucket_index > 0)
m_lowest_active_bucket = bucket_index;
// TORRENT_LOG(table) << "inserting node: " << id << " " << addr;
return ret;
}
// if there is no room, we look for nodes marked as stale
// in the k-bucket. If we find one, we can replace it.
// A node is considered stale if it has failed at least one
// time. Here we choose the node that has failed most times.
// If we don't find one, place this node in the replacement-
// cache and replace any nodes that will fail in the future
// with nodes from that cache.
i = std::max_element(b.begin(), b.end()
, bind(std::less<int>()
, bind(&node_entry::fail_count, _1)
, bind(&node_entry::fail_count, _2)));
if (i != b.end() && i->fail_count > 0)
{
// i points to a node that has been marked
// as stale. Replace it with this new one
b.erase(i);
b.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "replacing stale node: " << id << " " << addr;
return ret;
}
// if we don't have any identified stale nodes in
// the bucket, and the bucket is full, we have to
// cache this node and wait until some node fails
// and then replace it.
bucket_t& rb = m_buckets[bucket_index].second;
i = std::find_if(rb.begin(), rb.end()
, bind(std::equal_to<node_id>()
, bind(&node_entry::id, _1), id));
// if the node is already in the replacement bucket
// just return.
if (i != rb.end()) return ret;
if ((int)rb.size() > m_bucket_size) rb.erase(rb.begin());
rb.push_back(node_entry(id, addr));
// TORRENT_LOG(table) << "inserting node in replacement cache: " << id << " " << addr;
return ret;
}
bool routing_table::need_bootstrap() const
{
for (const_iterator i = begin(); i != end(); ++i)
{
if (i->fail_count == 0) return false;
}
return true;
}
// fills the vector with the k nodes from our buckets that
// are nearest to the given id.
void routing_table::find_node(node_id const& target
, std::vector<node_entry>& l, bool include_self, int count)
{
l.clear();
if (count == 0) count = m_bucket_size;
l.reserve(count);
int bucket_index = distance_exp(m_id, target);
bucket_t& b = m_buckets[bucket_index].first;
// copy all nodes that hasn't failed into the target
// vector.
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
assert((int)l.size() <= count);
if ((int)l.size() == count)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
// if we didn't have enough nodes in that bucket
// we have to reply with nodes from buckets closer
// to us. i.e. all the buckets in the range
// [0, bucket_index) if we are to include ourself
// or [1, bucket_index) if not.
bucket_t tmpb;
for (int i = include_self?0:1; i < count; ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(tmpb)
, bind(&node_entry::fail_count, _1));
}
std::random_shuffle(tmpb.begin(), tmpb.end());
size_t to_copy = (std::min)(m_bucket_size - l.size()
, tmpb.size());
std::copy(tmpb.begin(), tmpb.begin() + to_copy
, std::back_inserter(l));
assert((int)l.size() <= m_bucket_size);
// return if we have enough nodes or if the bucket index
// is the biggest index available (there are no more buckets)
// to look in.
if ((int)l.size() == count
|| bucket_index == (int)m_buckets.size() - 1)
{
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
for (size_t i = bucket_index + 1; i < m_buckets.size(); ++i)
{
bucket_t& b = m_buckets[i].first;
std::remove_copy_if(b.begin(), b.end(), std::back_inserter(l)
, bind(&node_entry::fail_count, _1));
if ((int)l.size() >= count)
{
l.erase(l.begin() + count, l.end());
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
return;
}
}
assert((int)l.size() == count
|| std::distance(l.begin(), l.end()) < m_bucket_size);
assert((int)l.size() <= count);
assert(std::count_if(l.begin(), l.end()
, boost::bind(std::not_equal_to<int>()
, boost::bind(&node_entry::fail_count, _1), 0)) == 0);
}
routing_table::iterator routing_table::begin() const
{
return iterator(m_buckets.begin(), m_buckets.end());
}
routing_table::iterator routing_table::end() const
{
return iterator(m_buckets.end(), m_buckets.end());
}
} } // namespace libtorrent::dht

View File

@ -1,356 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg
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.
*/
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/bind.hpp>
#include <libtorrent/io.hpp>
#include <libtorrent/invariant_check.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/hasher.hpp>
#include <fstream>
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::milliseconds;
using boost::shared_ptr;
using boost::bind;
namespace libtorrent { namespace dht
{
namespace io = libtorrent::detail;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(rpc)
#endif
node_id generate_id();
rpc_manager::rpc_manager(fun const& f, node_id const& our_id
, routing_table& table, send_fun const& sf)
: m_next_transaction_id(rand() % max_transactions)
, m_oldest_transaction_id(m_next_transaction_id)
, m_incoming(f)
, m_send(sf)
, m_our_id(our_id)
, m_table(table)
, m_timer(boost::posix_time::microsec_clock::universal_time())
, m_random_number(generate_id())
{
std::srand(time(0));
}
rpc_manager::~rpc_manager()
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Destructing";
#endif
}
#ifndef NDEBUG
void rpc_manager::check_invariant() const
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
assert(m_next_transaction_id >= 0);
assert(m_next_transaction_id < max_transactions);
assert(!m_transactions[m_next_transaction_id]);
for (int i = (m_next_transaction_id + 1) % max_transactions;
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
}
#endif
bool rpc_manager::incoming(msg const& m)
{
INVARIANT_CHECK;
if (m.reply)
{
// if we don't have the transaction id in our
// request list, ignore the packet
if (m.transaction_id.size() != 2)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
<< m.transaction_id.size() << " from " << m.addr;
#endif
return false;
}
std::string::const_iterator i = m.transaction_id.begin();
int tid = io::read_uint16(i);
if (tid >= (int)m_transactions.size()
|| tid < 0)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
boost::shared_ptr<observer> o = m_transactions[tid];
if (!o)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
if (m.addr != o->target_addr)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
<< tid << " from " << m.addr;
#endif
return false;
}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
- o->sent).total_milliseconds() << std::endl;
#endif
o->reply(m);
m_transactions[tid].reset();
if (m.piggy_backed_ping)
{
// there is a ping request piggy
// backed in this reply
msg ph;
ph.message_id = messages::ping;
ph.transaction_id = m.ping_transaction_id;
ph.id = m_our_id;
ph.addr = m.addr;
msg empty;
reply(empty, ph);
}
return m_table.node_seen(m.id, m.addr);
}
else
{
// this is an incoming request
m_incoming(m);
}
return false;
}
time_duration rpc_manager::tick()
{
INVARIANT_CHECK;
using boost::posix_time::microsec_clock;
const int timeout_ms = 20 * 1000;
// look for observers that has timed out
if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);
for (;m_next_transaction_id != m_oldest_transaction_id;
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
{
assert(m_oldest_transaction_id >= 0);
assert(m_oldest_transaction_id < max_transactions);
boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
if (!o) continue;
time_duration diff = o->sent + milliseconds(timeout_ms)
- microsec_clock::universal_time();
if (diff > seconds(0))
{
if (diff < seconds(1)) return seconds(1);
return diff;
}
m_transactions[m_oldest_transaction_id].reset();
o->timeout();
}
return milliseconds(timeout_ms);
}
unsigned int rpc_manager::new_transaction_id()
{
INVARIANT_CHECK;
unsigned int tid = m_next_transaction_id;
m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
// boost::shared_ptr<observer> o = m_transactions[m_next_transaction_id];
if (m_transactions[m_next_transaction_id])
{
m_transactions[m_next_transaction_id].reset();
assert(m_oldest_transaction_id == m_next_transaction_id);
}
if (m_oldest_transaction_id == m_next_transaction_id)
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
" messages! limit: " << (int)max_transactions;
#endif
update_oldest_transaction_id();
}
#ifndef NDEBUG
assert(!m_transactions[m_next_transaction_id]);
for (int i = (m_next_transaction_id + 1) % max_transactions;
i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
{
assert(!m_transactions[i]);
}
#endif
// hopefully this wouldn't happen, but unfortunately, the
// traversal algorithm will simply fail in case its connections
// are overwritten. If timeout() is called, it will likely spawn
// another connection, which in turn will close the next one
// and so on.
// if (o) o->timeout();
return tid;
}
void rpc_manager::update_oldest_transaction_id()
{
INVARIANT_CHECK;
assert(m_oldest_transaction_id != m_next_transaction_id);
while (!m_transactions[m_oldest_transaction_id])
{
m_oldest_transaction_id = (m_oldest_transaction_id + 1)
% max_transactions;
if (m_oldest_transaction_id == m_next_transaction_id)
break;
}
}
void rpc_manager::invoke(int message_id, udp::endpoint target_addr
, shared_ptr<observer> o)
{
INVARIANT_CHECK;
msg m;
m.message_id = message_id;
m.reply = false;
m.id = m_our_id;
m.addr = target_addr;
int tid = new_transaction_id();
m.transaction_id.clear();
std::back_insert_iterator<std::string> out(m.transaction_id);
io::write_uint16(tid, out);
o->send(m);
m_transactions[tid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = target_addr;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
<< " -> " << target_addr;
#endif
m_send(m);
}
void rpc_manager::reply(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = false;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
m_send(m);
}
namespace
{
struct dummy_observer : observer
{
virtual void reply(msg const&) {}
virtual void timeout() {}
virtual void send(msg&) {}
};
}
void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
INVARIANT_CHECK;
if (m.message_id != messages::error)
m.message_id = reply_to.message_id;
m.addr = reply_to.addr;
m.reply = true;
m.piggy_backed_ping = true;
m.id = m_our_id;
m.transaction_id = reply_to.transaction_id;
int ptid = new_transaction_id();
m.ping_transaction_id.clear();
std::back_insert_iterator<std::string> out(m.ping_transaction_id);
io::write_uint16(ptid, out);
boost::shared_ptr<observer> o(new dummy_observer);
m_transactions[ptid] = o;
o->sent = boost::posix_time::microsec_clock::universal_time();
o->target_addr = m.addr;
m_send(m);
}
} } // namespace libtorrent::dht

View File

@ -1,162 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg & Daniel Wallin
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.
*/
#include <libtorrent/kademlia/traversal_algorithm.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <boost/bind.hpp>
using boost::bind;
using asio::ip::udp;
namespace libtorrent { namespace dht
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(traversal)
#endif
void traversal_algorithm::add_entry(node_id const& id, udp::endpoint addr, unsigned char flags)
{
if (m_failed.find(addr) != m_failed.end()) return;
result const entry(id, addr, flags);
std::vector<result>::iterator i = std::lower_bound(
m_results.begin()
, m_results.end()
, entry
, bind(
compare_ref
, bind(&result::id, _1)
, bind(&result::id, _2)
, m_target
)
);
if (i == m_results.end() || i->id != id)
{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "adding result: " << id << " " << addr;
#endif
m_results.insert(i, entry);
}
}
void traversal_algorithm::traverse(node_id const& id, udp::endpoint addr)
{
add_entry(id, addr, 0);
}
void traversal_algorithm::finished(node_id const& id)
{
--m_invoke_count;
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::failed(node_id const& id)
{
m_invoke_count--;
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, m_results.end()
, bind(
std::equal_to<node_id>()
, bind(&result::id, _1)
, id
)
);
assert(i != m_results.end());
assert(i->flags & result::queried);
m_failed.insert(i->addr);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "failed: " << i->id << " " << i->addr;
#endif
m_results.erase(i);
m_table.node_failed(id);
add_requests();
if (m_invoke_count == 0) done();
}
void traversal_algorithm::add_request(node_id const& id, udp::endpoint addr)
{
invoke(id, addr);
++m_invoke_count;
}
namespace
{
bool bitwise_nand(unsigned char lhs, unsigned char rhs)
{
return (lhs & rhs) == 0;
}
}
void traversal_algorithm::add_requests()
{
while (m_invoke_count < m_branch_factor)
{
// Find the first node that hasn't already been queried.
// TODO: Better heuristic
std::vector<result>::iterator i = std::find_if(
m_results.begin()
, last_iterator()
, bind(
&bitwise_nand
, bind(&result::flags, _1)
, (unsigned char)result::queried
)
);
#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(traversal) << "nodes left (" << this << "): " << (last_iterator() - i);
#endif
if (i == last_iterator()) break;
add_request(i->id, i->addr);
i->flags |= result::queried;
}
}
std::vector<traversal_algorithm::result>::iterator traversal_algorithm::last_iterator()
{
return (int)m_results.size() >= m_max_results ?
m_results.begin() + m_max_results
: m_results.end();
}
} } // namespace libtorrent::dht

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,287 +0,0 @@
/*
Copyright (c) 2006, Arvid Norberg, Magnus Jonsson
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.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/limits.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/fingerprint.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/file.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp"
using namespace boost::posix_time;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::bind;
using boost::mutex;
using libtorrent::aux::session_impl;
namespace libtorrent
{
session::session(
fingerprint const& id
, std::pair<int, int> listen_port_range
, char const* listen_interface)
: m_impl(new session_impl(listen_port_range, id, listen_interface))
{
// turn off the filename checking in boost.filesystem
using namespace boost::filesystem;
if (path::default_name_check_writable())
path::default_name_check(no_check);
assert(listen_port_range.first > 0);
assert(listen_port_range.first < listen_port_range.second);
#ifndef NDEBUG
// this test was added after it came to my attention
// that devstudios managed c++ failed to generate
// correct code for boost.function
boost::function0<void> test = boost::ref(*m_impl);
assert(!test.empty());
#endif
}
session::session(fingerprint const& id)
: m_impl(new session_impl(std::make_pair(0, 0), id))
{
#ifndef NDEBUG
boost::function0<void> test = boost::ref(*m_impl);
assert(!test.empty());
#endif
}
session::~session()
{
assert(m_impl);
// if there is at least one destruction-proxy
// abort the session and let the destructor
// of the proxy to syncronize
if (!m_impl.unique())
m_impl->abort();
}
void session::disable_extensions()
{
m_impl->disable_extensions();
}
void session::set_ip_filter(ip_filter const& f)
{
m_impl->set_ip_filter(f);
}
void session::set_peer_id(peer_id const& id)
{
m_impl->set_peer_id(id);
}
void session::set_key(int key)
{
m_impl->set_key(key);
}
void session::enable_extension(extension_index i)
{
m_impl->enable_extension(i);
}
std::vector<torrent_handle> session::get_torrents() const
{
return m_impl->get_torrents();
}
// if the torrent already exists, this will throw duplicate_torrent
torrent_handle session::add_torrent(
torrent_info const& ti
, boost::filesystem::path const& save_path
, entry const& resume_data
, bool compact_mode
, int block_size)
{
return m_impl->add_torrent(ti, save_path, resume_data
, compact_mode, block_size);
}
torrent_handle session::add_torrent(
char const* tracker_url
, sha1_hash const& info_hash
, boost::filesystem::path const& save_path
, entry const& e
, bool compact_mode
, int block_size)
{
return m_impl->add_torrent(tracker_url, info_hash, save_path, e
, compact_mode, block_size);
}
void session::remove_torrent(const torrent_handle& h)
{
m_impl->remove_torrent(h);
}
bool session::listen_on(
std::pair<int, int> const& port_range
, const char* net_interface)
{
return m_impl->listen_on(port_range, net_interface);
}
unsigned short session::listen_port() const
{
return m_impl->listen_port();
}
session_status session::status() const
{
return m_impl->status();
}
#ifndef TORRENT_DISABLE_DHT
void session::start_dht(entry const& startup_state)
{
m_impl->start_dht(startup_state);
}
void session::stop_dht()
{
m_impl->stop_dht();
}
void session::set_dht_settings(dht_settings const& settings)
{
m_impl->set_dht_settings(settings);
}
entry session::dht_state() const
{
return m_impl->dht_state();
}
void session::add_dht_node(std::pair<std::string, int> const& node)
{
m_impl->add_dht_node(node);
}
void session::add_dht_router(std::pair<std::string, int> const& node)
{
m_impl->add_dht_router(node);
}
#endif
bool session::is_listening() const
{
return m_impl->is_listening();
}
void session::set_settings(session_settings const& s)
{
m_impl->set_settings(s);
}
session_settings const& session::settings()
{
return m_impl->settings();
}
void session::set_max_uploads(int limit)
{
m_impl->set_max_uploads(limit);
}
void session::set_max_connections(int limit)
{
m_impl->set_max_connections(limit);
}
void session::set_max_half_open_connections(int limit)
{
m_impl->set_max_half_open_connections(limit);
}
void session::set_upload_rate_limit(int bytes_per_second)
{
m_impl->set_upload_rate_limit(bytes_per_second);
}
void session::set_download_rate_limit(int bytes_per_second)
{
m_impl->set_download_rate_limit(bytes_per_second);
}
std::auto_ptr<alert> session::pop_alert()
{
return m_impl->pop_alert();
}
void session::set_severity_level(alert::severity_t s)
{
m_impl->set_severity_level(s);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,314 +0,0 @@
/*
SHA-1 C++ conversion
original version:
SHA-1 in C
By Steve Reid <sreid@sea-to-sky.net>
100% Public Domain
changelog at the end of the file.
*/
#include <cstdio>
#include <cstring>
// if you don't want boost
// replace with
// #include <stdint.h>
#include <boost/cstdint.hpp>
using boost::uint32_t;
using boost::uint8_t;
#include "libtorrent/config.hpp"
struct TORRENT_EXPORT SHA1_CTX
{
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
};
TORRENT_EXPORT void SHA1Init(SHA1_CTX* context);
TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len);
TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, uint8_t* digest);
namespace
{
union CHAR64LONG16
{
uint8_t c[64];
uint32_t l[16];
};
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
// blk0() and blk() perform the initial expand.
// I got the idea of expanding during the round function from SSLeay
struct little_endian_blk0
{
static uint32_t apply(CHAR64LONG16* block, int i)
{
return block->l[i] = (rol(block->l[i],24)&0xFF00FF00)
| (rol(block->l[i],8)&0x00FF00FF);
}
};
struct big_endian_blk0
{
static uint32_t apply(CHAR64LONG16* block, int i)
{
return block->l[i];
}
};
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
^block->l[(i+2)&15]^block->l[i&15],1))
// (R0+R1), R2, R3, R4 are the different operations used in SHA1
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+BlkFun::apply(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
// Hash a single 512-bit block. This is the core of the algorithm.
template <class BlkFun>
void SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
{
using namespace std;
uint32_t a, b, c, d, e;
CHAR64LONG16* block;
uint8_t workspace[64];
block = (CHAR64LONG16*)workspace;
memcpy(block, buffer, 64);
// Copy context->state[] to working vars
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
// 4 rounds of 20 operations each. Loop unrolled.
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
// Add the working vars back into context.state[]
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
// Wipe variables
a = b = c = d = e = 0;
}
void SHAPrintContext(SHA1_CTX *context, char *msg)
{
using namespace std;
printf("%s (%d,%d) %x %x %x %x %x\n"
, msg, context->count[0], context->count[1]
, context->state[0], context->state[1]
, context->state[2], context->state[3]
, context->state[4]);
}
template <class BlkFun>
void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
{
using namespace std;
uint32_t i, j; // JHB
#ifdef VERBOSE
SHAPrintContext(context, "before");
#endif
j = (context->count[0] >> 3) & 63;
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
context->count[1] += (len >> 29);
if ((j + len) > 63)
{
memcpy(&context->buffer[j], data, (i = 64-j));
SHA1Transform<BlkFun>(context->state, context->buffer);
for ( ; i + 63 < len; i += 64)
{
SHA1Transform<BlkFun>(context->state, &data[i]);
}
j = 0;
}
else
{
i = 0;
}
memcpy(&context->buffer[j], &data[i], len - i);
#ifdef VERBOSE
SHAPrintContext(context, "after ");
#endif
}
bool is_big_endian()
{
uint32_t test = 1;
return *reinterpret_cast<uint8_t*>(&test) == 0;
}
}
// SHA1Init - Initialize new context
void SHA1Init(SHA1_CTX* context)
{
// SHA1 initialization constants
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
context->state[4] = 0xC3D2E1F0;
context->count[0] = context->count[1] = 0;
}
// Run your data through this.
void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len)
{
#if defined __BIG_ENDIAN__
internal_update<big_endian_blk0>(context, data, len);
#elif defined LITTLE_ENDIAN
internal_update<little_endian_blk0>(context, data, len);
#else
// select different functions depending on endianess
// and figure out the endianess runtime
if (is_big_endian())
internal_update<big_endian_blk0>(context, data, len);
else
internal_update<little_endian_blk0>(context, data, len);
#endif
}
// Add padding and return the message digest.
void SHA1Final(SHA1_CTX* context, uint8_t* digest)
{
uint8_t finalcount[8];
for (uint32_t i = 0; i < 8; ++i)
{
// Endian independent
finalcount[i] = static_cast<uint8_t>(
(context->count[(i >= 4 ? 0 : 1)]
>> ((3-(i & 3)) * 8) ) & 255);
}
SHA1Update(context, (uint8_t const*)"\200", 1);
while ((context->count[0] & 504) != 448)
SHA1Update(context, (uint8_t const*)"\0", 1);
SHA1Update(context, finalcount, 8); // Should cause a SHA1Transform()
for (uint32_t i = 0; i < 20; ++i)
{
digest[i] = static_cast<unsigned char>(
(context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
}
/************************************************************
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
Still 100% Public Domain
Corrected a problem which generated improper hash values on 16 bit machines
Routine SHA1Update changed from
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
len)
to
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
long len)
The 'len' parameter was declared an int which works fine on 32 bit machines.
However, on 16 bit machines an int is too small for the shifts being done
against
it. This caused the hash function to generate incorrect values if len was
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
Since the file IO in main() reads 16K at a time, any file 8K or larger would
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
"a"s).
I also changed the declaration of variables i & j in SHA1Update to
unsigned long from unsigned int for the same reason.
These changes should make no difference to any 32 bit implementations since
an
int and a long are the same size in those environments.
--
I also corrected a few compiler warnings generated by Borland C.
1. Added #include <process.h> for exit() prototype
2. Removed unused variable 'j' in SHA1Final
3. Changed exit(0) to return(0) at end of main.
ALL changes I made can be located by searching for comments containing 'JHB'
-----------------
Modified 8/98
By Steve Reid <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> and used return() instead of exit()
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
-----------------
Modified 4/01
By Saul Kravitz <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Converted to C++ 6/04
By Arvid Norberg <arvidn@sourceforge.net>
1- made the input buffer const, and made the
previous SHA1HANDSOFF implicit
2- uses C99 types with size guarantees
from boost
3- if none of __BIG_ENDIAN__ or LITTLE_ENDIAN
are defined, endianess is determined
at runtime. templates are used to duplicate
the transform function for each endianess
4- using anonymous namespace to avoid external
linkage on internal functions
5- using standard C++ includes
still 100% PD
*/
/*
Test Vectors (from FIPS PUB 180-1)
"abc"
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
A million repetitions of "a"
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
*/

View File

@ -1,91 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
// TODO: Use two algorithms to estimate transfer rate.
// one (simple) for transfer rates that are >= 1 packet
// per second and one (low pass-filter) for rates < 1
// packet per second.
#include <numeric>
#include "libtorrent/stat.hpp"
#include "libtorrent/invariant_check.hpp"
#include <algorithm>
#if defined _MSC_VER && _MSC_VER <= 1200
#define for if (false) {} else for
#endif
using namespace libtorrent;
void libtorrent::stat::second_tick(float tick_interval)
{
INVARIANT_CHECK;
for (int i = history - 2; i >= 0; --i)
{
m_download_rate_history[i + 1] = m_download_rate_history[i];
m_upload_rate_history[i + 1] = m_upload_rate_history[i];
m_download_payload_rate_history[i + 1] = m_download_payload_rate_history[i];
m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i];
}
m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol)
/ tick_interval;
m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol)
/ tick_interval;
m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval;
m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval;
m_downloaded_payload = 0;
m_uploaded_payload = 0;
m_downloaded_protocol = 0;
m_uploaded_protocol = 0;
m_mean_download_rate = 0;
m_mean_upload_rate = 0;
m_mean_download_payload_rate = 0;
m_mean_upload_payload_rate = 0;
for (int i = 0; i < history; ++i)
{
m_mean_download_rate += m_download_rate_history[i];
m_mean_upload_rate += m_upload_rate_history[i];
m_mean_download_payload_rate += m_download_payload_rate_history[i];
m_mean_upload_payload_rate += m_upload_payload_rate_history[i];
}
m_mean_download_rate /= history;
m_mean_upload_rate /= history;
m_mean_download_payload_rate /= history;
m_mean_upload_payload_rate /= history;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,729 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/invariant_check.hpp"
#if defined(_MSC_VER) && _MSC_VER < 1300
namespace std
{
using ::srand;
using ::isalnum;
};
#endif
using boost::bind;
using boost::mutex;
using libtorrent::aux::session_impl;
namespace libtorrent
{
namespace
{
void throw_invalid_handle()
{
throw invalid_handle();
}
template<class Ret, class F>
Ret call_member(
session_impl* ses
, aux::checker_impl* chk
, sha1_hash const& hash
, F f)
{
if (ses == 0) throw_invalid_handle();
if (chk)
{
mutex::scoped_lock l(chk->m_mutex);
aux::piece_checker_data* d = chk->find_torrent(hash);
if (d != 0) return f(*d->torrent_ptr);
}
{
session_impl::mutex_t::scoped_lock l(ses->m_mutex);
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
if (t) return f(*t);
}
throw invalid_handle();
}
}
#ifndef NDEBUG
void torrent_handle::check_invariant() const
{
assert((m_ses == 0 && m_chk == 0) || (m_ses != 0));
}
#endif
void torrent_handle::set_max_uploads(int max_uploads) const
{
INVARIANT_CHECK;
assert(max_uploads >= 2 || max_uploads == -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_max_uploads, _1, max_uploads));
}
void torrent_handle::use_interface(const char* net_interface) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::use_interface, _1, net_interface));
}
void torrent_handle::set_max_connections(int max_connections) const
{
INVARIANT_CHECK;
assert(max_connections >= 2 || max_connections == -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_max_connections, _1, max_connections));
}
void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_upload_limit, _1, ip, limit));
}
void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_download_limit, _1, ip, limit));
}
void torrent_handle::set_upload_limit(int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_upload_limit, _1, limit));
}
void torrent_handle::set_download_limit(int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_download_limit, _1, limit));
}
bool torrent_handle::move_storage(
boost::filesystem::path const& save_path) const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::move_storage, _1, save_path));
}
bool torrent_handle::has_metadata() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::valid_metadata, _1));
}
bool torrent_handle::is_seed() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_seed, _1));
}
bool torrent_handle::is_paused() const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_paused, _1));
}
void torrent_handle::pause() const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::pause, _1));
}
void torrent_handle::resume() const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::resume, _1));
}
void torrent_handle::set_tracker_login(std::string const& name
, std::string const& password) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_tracker_login, _1, name, password));
}
void torrent_handle::file_progress(std::vector<float>& progress)
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
if (!d->processing)
{
torrent_info const& info = d->torrent_ptr->torrent_file();
progress.clear();
progress.resize(info.num_files(), 0.f);
return;
}
d->torrent_ptr->file_progress(progress);
return;
}
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->file_progress(progress);
}
throw_invalid_handle();
}
torrent_status torrent_handle::status() const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
{
torrent_status st;
if (d->processing)
{
if (d->torrent_ptr->is_allocating())
st.state = torrent_status::allocating;
else
st.state = torrent_status::checking_files;
}
else
st.state = torrent_status::queued_for_checking;
st.progress = d->progress;
st.paused = d->torrent_ptr->is_paused();
return st;
}
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->status();
}
throw_invalid_handle();
return torrent_status();
}
void torrent_handle::set_sequenced_download_threshold(int threshold) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_sequenced_download_threshold, _1, threshold));
}
void torrent_handle::filter_piece(int index, bool filter) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_piece, _1, index, filter));
}
void torrent_handle::filter_pieces(std::vector<bool> const& pieces) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_pieces, _1, pieces));
}
bool torrent_handle::is_piece_filtered(int index) const
{
INVARIANT_CHECK;
return call_member<bool>(m_ses, m_chk, m_info_hash
, bind(&torrent::is_piece_filtered, _1, index));
}
std::vector<bool> torrent_handle::filtered_pieces() const
{
INVARIANT_CHECK;
std::vector<bool> ret;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filtered_pieces, _1, boost::ref(ret)));
return ret;
}
void torrent_handle::filter_files(std::vector<bool> const& files) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_files, _1, files));
}
std::vector<announce_entry> const& torrent_handle::trackers() const
{
INVARIANT_CHECK;
return call_member<std::vector<announce_entry> const&>(m_ses
, m_chk, m_info_hash, bind(&torrent::trackers, _1));
}
void torrent_handle::add_url_seed(std::string const& url)
{
INVARIANT_CHECK;
return call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::add_url_seed, _1, url));
}
void torrent_handle::replace_trackers(
std::vector<announce_entry> const& urls) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::replace_trackers, _1, urls));
}
const torrent_info& torrent_handle::get_torrent_info() const
{
INVARIANT_CHECK;
if (!has_metadata()) throw_invalid_handle();
return call_member<torrent_info const&>(m_ses, m_chk, m_info_hash
, bind(&torrent::torrent_file, _1));
}
bool torrent_handle::is_valid() const
{
INVARIANT_CHECK;
if (m_ses == 0) return false;
if (m_chk)
{
mutex::scoped_lock l(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0) return true;
}
{
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
if (!t.expired()) return true;
}
return false;
}
entry torrent_handle::write_resume_data() const
{
INVARIANT_CHECK;
std::vector<int> piece_index;
if (m_ses == 0) return entry();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return entry();
if (!t->valid_metadata()) return entry();
t->filesystem().export_piece_map(piece_index);
entry ret(entry::dictionary_t);
ret["file-format"] = "libtorrent resume file";
ret["file-version"] = 1;
const sha1_hash& info_hash = t->torrent_file().info_hash();
ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end());
ret["slots"] = entry(entry::list_t);
entry::list_type& slots = ret["slots"].list();
std::copy(piece_index.begin(), piece_index.end(), std::back_inserter(slots));
const piece_picker& p = t->picker();
const std::vector<piece_picker::downloading_piece>& q
= p.get_download_queue();
// blocks per piece
int num_blocks_per_piece =
static_cast<int>(t->torrent_file().piece_length()) / t->block_size();
ret["blocks per piece"] = num_blocks_per_piece;
// unfinished pieces
ret["unfinished"] = entry::list_type();
entry::list_type& up = ret["unfinished"].list();
// info for each unfinished piece
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin(); i != q.end(); ++i)
{
if (i->finished_blocks.count() == 0) continue;
entry piece_struct(entry::dictionary_t);
// the unfinished piece's index
piece_struct["piece"] = i->index;
std::string bitmask;
const int num_bitmask_bytes
= std::max(num_blocks_per_piece / 8, 1);
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char v = 0;
for (int k = 0; k < 8; ++k)
v |= i->finished_blocks[j*8+k]?(1 << k):0;
bitmask.insert(bitmask.end(), v);
}
piece_struct["bitmask"] = bitmask;
assert(t->filesystem().slot_for_piece(i->index) >= 0);
unsigned long adler
= t->filesystem().piece_crc(
t->filesystem().slot_for_piece(i->index)
, t->block_size()
, i->finished_blocks);
piece_struct["adler32"] = adler;
// push the struct onto the unfinished-piece list
up.push_back(piece_struct);
}
// write local peers
ret["peers"] = entry::list_type();
entry::list_type& peer_list = ret["peers"].list();
policy& pol = t->get_policy();
for (policy::iterator i = pol.begin_peer()
, end(pol.end_peer()); i != end; ++i)
{
// we cannot save remote connection
// since we don't know their listen port
// unless they gave us their listen port
// through the extension handshake
// so, if the peer is not connectable (i.e. we
// don't know its listen port) or if it has
// been banned, don't save it.
if (i->type == policy::peer::not_connectable
|| i->banned) continue;
tcp::endpoint ip = i->ip;
entry peer(entry::dictionary_t);
peer["ip"] = ip.address().to_string();
peer["port"] = ip.port();
peer_list.push_back(peer);
}
std::vector<std::pair<size_type, std::time_t> > file_sizes
= get_filesizes(t->torrent_file(), t->save_path());
ret["file sizes"] = entry::list_type();
entry::list_type& fl = ret["file sizes"].list();
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
{
entry::list_type p;
p.push_back(entry(i->first));
p.push_back(entry(i->second));
fl.push_back(entry(p));
}
return ret;
}
boost::filesystem::path torrent_handle::save_path() const
{
INVARIANT_CHECK;
return call_member<boost::filesystem::path>(m_ses, m_chk, m_info_hash
, bind(&torrent::save_path, _1));
}
std::vector<char> const& torrent_handle::metadata() const
{
INVARIANT_CHECK;
return call_member<std::vector<char> const&>(m_ses, m_chk, m_info_hash
, bind(&torrent::metadata, _1));
}
void torrent_handle::connect_peer(tcp::endpoint const& adr) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t)
{
// the torrent is being checked. Add the peer to its
// peer list. The entries in there will be connected
// once the checking is complete.
mutex::scoped_lock l2(m_chk->m_mutex);
aux::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d == 0) throw_invalid_handle();
d->peers.push_back(adr);
return;
}
peer_id id;
std::fill(id.begin(), id.end(), 0);
t->get_policy().peer_from_tracker(adr, id);
}
void torrent_handle::force_reannounce(
boost::posix_time::time_duration duration) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
using boost::posix_time::second_clock;
t->force_tracker_request(second_clock::universal_time()
+ duration);
}
void torrent_handle::force_reannounce() const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
t->force_tracker_request();
}
void torrent_handle::set_ratio(float ratio) const
{
INVARIANT_CHECK;
assert(ratio >= 0.f);
if (ratio < 1.f && ratio > 0.f)
ratio = 1.f;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_ratio, _1, ratio));
}
void torrent_handle::get_peer_info(std::vector<peer_info>& v) const
{
INVARIANT_CHECK;
v.clear();
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
{
peer_connection* peer = i->second;
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent().expired()) continue;
v.push_back(peer_info());
peer_info& p = v.back();
peer->get_peer_info(p);
}
}
bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const
{
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return false;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
{
peer_connection* peer = i->second;
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent().expired()) continue;
tcp::endpoint sender = peer->get_socket()->remote_endpoint();
// loop until we find the required ip tcp::endpoint
if (ip != sender) continue;
bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
if (!p) return false;
// peers that don's support chat message extension
// should not be included either
if (!p->supports_extension(extended_chat_message))
return false;
// send the message
p->write_chat_message(message);
return true;
}
return false;
}
void torrent_handle::get_download_queue(std::vector<partial_piece_info>& queue) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
queue.clear();
if (!t) return;
if (!t->valid_metadata()) return;
const piece_picker& p = t->picker();
const std::vector<piece_picker::downloading_piece>& q
= p.get_download_queue();
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin(); i != q.end(); ++i)
{
partial_piece_info pi;
pi.finished_blocks = i->finished_blocks;
pi.requested_blocks = i->requested_blocks;
for (int j = 0; j < partial_piece_info::max_blocks_per_piece; ++j)
{
pi.peer[j] = i->info[j].peer;
pi.num_downloads[j] = i->info[j].num_downloads;
}
pi.piece_index = i->index;
pi.blocks_in_piece = p.blocks_in_piece(i->index);
queue.push_back(pi);
}
}
}

View File

@ -1,833 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/next_prior.hpp>
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
using namespace libtorrent;
using namespace boost::filesystem;
namespace
{
void convert_to_utf8(std::string& str, unsigned char chr)
{
str += 0xc0 | ((chr & 0xff) >> 6);
str += 0x80 | (chr & 0x3f);
}
void verify_encoding(file_entry& target)
{
std::string tmp_path;
std::string file_path = target.path.string();
bool valid_encoding = true;
for (std::string::iterator i = file_path.begin()
, end(file_path.end()); i != end; ++i)
{
// valid ascii-character
if ((*i & 0x80) == 0)
{
tmp_path += *i;
continue;
}
if (std::distance(i, end) < 2)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 2-byte utf-8 character
if ((i[0] & 0xe0) == 0xc0
&& (i[1] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
i += 1;
continue;
}
if (std::distance(i, end) < 3)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 3-byte utf-8 character
if ((i[0] & 0xf0) == 0xe0
&& (i[1] & 0xc0) == 0x80
&& (i[2] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
tmp_path += i[2];
i += 2;
continue;
}
if (std::distance(i, end) < 4)
{
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
continue;
}
// valid 4-byte utf-8 character
if ((i[0] & 0xf0) == 0xe0
&& (i[1] & 0xc0) == 0x80
&& (i[2] & 0xc0) == 0x80
&& (i[3] & 0xc0) == 0x80)
{
tmp_path += i[0];
tmp_path += i[1];
tmp_path += i[2];
tmp_path += i[3];
i += 3;
continue;
}
convert_to_utf8(tmp_path, *i);
valid_encoding = false;
}
// the encoding was not valid utf-8
// save the original encoding and replace the
// commonly used path with the correctly
// encoded string
if (!valid_encoding)
{
target.orig_path.reset(new path(target.path));
target.path = tmp_path;
}
}
void extract_single_file(const entry& dict, file_entry& target
, std::string const& root_dir)
{
target.size = dict["length"].integer();
target.path = root_dir;
// prefer the name.utf-8
// because if it exists, it is more
// likely to be correctly encoded
const entry::list_type* list = 0;
if (entry const* p = dict.find_key("path.utf-8"))
{
list = &p->list();
}
else
{
list = &dict["path"].list();
}
for (entry::list_type::const_iterator i = list->begin();
i != list->end(); ++i)
{
if (i->string() != "..")
target.path /= i->string();
}
verify_encoding(target);
if (target.path.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '"
+ target.path.native_file_string() + "'");
}
void extract_files(const entry::list_type& list, std::vector<file_entry>& target
, std::string const& root_dir)
{
size_type offset = 0;
for (entry::list_type::const_iterator i = list.begin(); i != list.end(); ++i)
{
target.push_back(file_entry());
extract_single_file(*i, target.back(), root_dir);
target.back().offset = offset;
offset += target.back().size;
}
}
void remove_dir(path& p)
{
assert(p.begin() != p.end());
path tmp;
for (path::iterator i = boost::next(p.begin()); i != p.end(); ++i)
tmp /= *i;
p = tmp;
}
}
namespace libtorrent
{
using namespace boost::gregorian;
using namespace boost::posix_time;
// standard constructor that parses a torrent file
torrent_info::torrent_info(const entry& torrent_file)
: m_creation_date(date(not_a_date_time))
, m_multifile(false)
, m_private(false)
, m_extra_info(entry::dictionary_t)
{
try
{
read_torrent_info(torrent_file);
}
catch(type_error&)
{
throw invalid_torrent_file();
}
}
// constructor used for creating new torrents
// will not contain any hashes, comments, creation date
// just the necessary to use it with piece manager
// used for torrents with no metadata
torrent_info::torrent_info(sha1_hash const& info_hash)
: m_piece_length(256 * 1024)
, m_total_size(0)
, m_info_hash(info_hash)
, m_name()
, m_creation_date(second_clock::universal_time())
, m_multifile(false)
, m_extra_info(entry::dictionary_t)
{
}
torrent_info::torrent_info()
: m_piece_length(256 * 1024)
, m_total_size(0)
, m_info_hash(0)
, m_name()
, m_creation_date(second_clock::universal_time())
, m_multifile(false)
, m_extra_info(entry::dictionary_t)
{
}
torrent_info::~torrent_info()
{}
void torrent_info::set_piece_size(int size)
{
// make sure the size is an even power of 2
#ifndef NDEBUG
for (int i = 0; i < 32; ++i)
{
if (size & (1 << i))
{
assert((size & ~(1 << i)) == 0);
break;
}
}
#endif
m_piece_length = size;
int num_pieces = static_cast<int>(
(m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = static_cast<int>(m_piece_hash.size());
m_piece_hash.resize(num_pieces);
for (int i = old_num_pieces; i < num_pieces; ++i)
{
m_piece_hash[i].clear();
}
}
void torrent_info::parse_info_section(entry const& info)
{
// encode the info-field in order to calculate it's sha1-hash
std::vector<char> buf;
bencode(std::back_inserter(buf), info);
hasher h;
h.update(&buf[0], (int)buf.size());
m_info_hash = h.final();
// extract piece length
m_piece_length = (int)info["piece length"].integer();
if (m_piece_length <= 0) throw std::runtime_error("invalid torrent. piece length <= 0");
// extract file name (or the directory name if it's a multifile libtorrent)
if (entry const* e = info.find_key("name.utf-8"))
{ m_name = e->string(); }
else
{ m_name = info["name"].string(); }
path tmp = m_name;
if (tmp.is_complete()) throw std::runtime_error("torrent contains "
"a file with an absolute path: '" + m_name + "'");
if (tmp.has_branch_path()) throw std::runtime_error(
"torrent contains name with directories: '" + m_name + "'");
// extract file list
entry const* i = info.find_key("files");
if (i == 0)
{
// if there's no list of files, there has to be a length
// field.
file_entry e;
e.path = m_name;
e.offset = 0;
e.size = info["length"].integer();
m_files.push_back(e);
}
else
{
extract_files(i->list(), m_files, m_name);
m_multifile = true;
}
// calculate total size of all pieces
m_total_size = 0;
for (std::vector<file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i)
m_total_size += i->size;
// extract sha-1 hashes for all pieces
// we want this division to round upwards, that's why we have the
// extra addition
int num_pieces = static_cast<int>((m_total_size + m_piece_length - 1) / m_piece_length);
m_piece_hash.resize(num_pieces);
const std::string& hash_string = info["pieces"].string();
if ((int)hash_string.length() != num_pieces * 20)
throw invalid_torrent_file();
for (int i = 0; i < num_pieces; ++i)
std::copy(
hash_string.begin() + i*20
, hash_string.begin() + (i+1)*20
, m_piece_hash[i].begin());
for (entry::dictionary_type::const_iterator i = info.dict().begin()
, end(info.dict().end()); i != end; ++i)
{
if (i->first == "pieces"
|| i->first == "piece length"
|| i->first == "length")
continue;
m_extra_info[i->first] = i->second;
}
if (entry const* priv = info.find_key("private"))
{
if (priv->type() != entry::int_t
|| priv->integer() != 0)
{
// this key exists and it's not 0.
// consider the torrent private
m_private = true;
}
}
#ifndef NDEBUG
std::vector<char> info_section_buf;
entry gen_info_section = create_info_metadata();
bencode(std::back_inserter(info_section_buf), gen_info_section);
assert(hasher(&info_section_buf[0], info_section_buf.size()).final()
== m_info_hash);
#endif
}
// extracts information from a libtorrent file and fills in the structures in
// the torrent object
void torrent_info::read_torrent_info(const entry& torrent_file)
{
// extract the url of the tracker
if (entry const* i = torrent_file.find_key("announce-list"))
{
const entry::list_type& l = i->list();
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
{
const entry::list_type& ll = j->list();
for (entry::list_type::const_iterator k = ll.begin(); k != ll.end(); ++k)
{
announce_entry e(k->string());
e.tier = (int)std::distance(l.begin(), j);
m_urls.push_back(e);
}
}
if (m_urls.size() == 0)
{
// the announce-list is empty
// fall back to look for announce
m_urls.push_back(announce_entry(
torrent_file["announce"].string()));
}
// shuffle each tier
std::vector<announce_entry>::iterator start = m_urls.begin();
std::vector<announce_entry>::iterator stop;
int current_tier = m_urls.front().tier;
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
{
if (stop->tier != current_tier)
{
std::random_shuffle(start, stop);
start = stop;
current_tier = stop->tier;
}
}
std::random_shuffle(start, stop);
}
else if (entry const* i = torrent_file.find_key("announce"))
{
m_urls.push_back(announce_entry(i->string()));
}
if (entry const* i = torrent_file.find_key("nodes"))
{
entry::list_type const& list = i->list();
for (entry::list_type::const_iterator i(list.begin())
, end(list.end()); i != end; ++i)
{
if (i->type() != entry::list_t) continue;
entry::list_type const& l = i->list();
entry::list_type::const_iterator iter = l.begin();
if (l.size() < 1) continue;
std::string const& hostname = iter->string();
++iter;
int port = 6881;
if (l.end() != iter) port = iter->integer();
m_nodes.push_back(std::make_pair(hostname, port));
}
}
// extract creation date
try
{
m_creation_date = ptime(date(1970, Jan, 1))
+ seconds(long(torrent_file["creation date"].integer()));
}
catch (type_error) {}
// if there are any url-seeds, extract them
try
{
entry const& url_seeds = torrent_file["url-list"];
if (url_seeds.type() == entry::string_t)
{
m_url_seeds.push_back(url_seeds.string());
}
else if (url_seeds.type() == entry::list_t)
{
entry::list_type const& l = url_seeds.list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
{
m_url_seeds.push_back(i->string());
}
}
}
catch (type_error&) {}
// extract comment
if (entry const* e = torrent_file.find_key("comment.utf-8"))
{ m_comment = e->string(); }
else if (entry const* e = torrent_file.find_key("comment"))
{ m_comment = e->string(); }
if (entry const* e = torrent_file.find_key("created by.utf-8"))
{ m_created_by = e->string(); }
else if (entry const* e = torrent_file.find_key("created by"))
{ m_created_by = e->string(); }
parse_info_section(torrent_file["info"]);
}
boost::optional<ptime>
torrent_info::creation_date() const
{
if (m_creation_date != ptime(date(not_a_date_time)))
{
return boost::optional<ptime>(m_creation_date);
}
return boost::optional<ptime>();
}
void torrent_info::add_tracker(std::string const& url, int tier)
{
announce_entry e(url);
e.tier = tier;
m_urls.push_back(e);
using boost::bind;
std::sort(m_urls.begin(), m_urls.end(), boost::bind<bool>(std::less<int>()
, bind(&announce_entry::tier, _1), bind(&announce_entry::tier, _2)));
}
void torrent_info::add_file(boost::filesystem::path file, size_type size)
{
assert(file.begin() != file.end());
if (!file.has_branch_path())
{
// you have already added at least one file with a
// path to the file (branch_path), which means that
// all the other files need to be in the same top
// directory as the first file.
assert(m_files.empty());
assert(!m_multifile);
m_name = file.string();
}
else
{
#ifndef NDEBUG
if (!m_files.empty())
assert(m_name == *file.begin());
#endif
m_multifile = true;
m_name = *file.begin();
}
file_entry e;
e.path = file;
e.size = size;
m_files.push_back(e);
m_total_size += size;
int num_pieces = static_cast<int>(
(m_total_size + m_piece_length - 1) / m_piece_length);
int old_num_pieces = static_cast<int>(m_piece_hash.size());
m_piece_hash.resize(num_pieces);
for (std::vector<sha1_hash>::iterator i = m_piece_hash.begin() + old_num_pieces;
i != m_piece_hash.end(); ++i)
{
i->clear();
}
}
void torrent_info::add_url_seed(std::string const& url)
{
m_url_seeds.push_back(url);
}
void torrent_info::set_comment(char const* str)
{
m_comment = str;
}
void torrent_info::set_creator(char const* str)
{
m_created_by = str;
}
entry torrent_info::create_info_metadata() const
{
namespace fs = boost::filesystem;
// you have to add files to the torrent first
assert(!m_files.empty());
entry info(m_extra_info);
if (!info.find_key("name"))
info["name"] = m_name;
if (!m_multifile)
{
info["length"] = m_files.front().size;
}
else
{
if (!info.find_key("files"))
{
entry& files = info["files"];
files = entry(entry::list_t);
for (std::vector<file_entry>::const_iterator i = m_files.begin();
i != m_files.end(); ++i)
{
files.list().push_back(entry(entry::dictionary_t));
entry& file_e = files.list().back();
file_e["length"] = i->size;
entry& path_e = file_e["path"];
path_e = entry(entry::list_t);
fs::path const* file_path;
if (i->orig_path) file_path = &(*i->orig_path);
else file_path = &i->path;
assert(file_path->has_branch_path());
assert(*file_path->begin() == m_name);
for (fs::path::iterator j = boost::next(file_path->begin());
j != file_path->end(); ++j)
{
path_e.list().push_back(entry(*j));
}
}
}
}
info["piece length"] = piece_length();
entry& pieces = info["pieces"];
pieces = entry(entry::string_t);
std::string& p = pieces.string();
for (std::vector<sha1_hash>::const_iterator i = m_piece_hash.begin();
i != m_piece_hash.end(); ++i)
{
p.append((char*)i->begin(), (char*)i->end());
}
return info;
}
entry torrent_info::create_torrent() const
{
assert(m_piece_length > 0);
using namespace boost::gregorian;
using namespace boost::posix_time;
namespace fs = boost::filesystem;
entry dict(entry::dictionary_t);
if ((m_urls.empty() && m_nodes.empty()) || m_files.empty())
{
// TODO: throw something here
// throw
return entry();
}
if (m_private) dict["private"] = 1;
if (!m_urls.empty())
dict["announce"] = m_urls.front().url;
if (!m_nodes.empty())
{
entry& nodes = dict["nodes"];
nodes = entry(entry::list_t);
entry::list_type& nodes_list = nodes.list();
for (nodes_t::const_iterator i = m_nodes.begin()
, end(m_nodes.end()); i != end; ++i)
{
entry::list_type node;
node.push_back(entry(i->first));
node.push_back(entry(i->second));
nodes_list.push_back(entry(node));
}
}
if (m_urls.size() > 1)
{
entry trackers(entry::list_t);
entry tier(entry::list_t);
int current_tier = m_urls.front().tier;
for (std::vector<announce_entry>::const_iterator i = m_urls.begin();
i != m_urls.end(); ++i)
{
if (i->tier != current_tier)
{
current_tier = i->tier;
trackers.list().push_back(tier);
tier.list().clear();
}
tier.list().push_back(entry(i->url));
}
trackers.list().push_back(tier);
dict["announce-list"] = trackers;
}
if (!m_comment.empty())
dict["comment"] = m_comment;
dict["creation date"] =
(m_creation_date - ptime(date(1970, Jan, 1))).total_seconds();
if (!m_created_by.empty())
dict["created by"] = m_created_by;
if (!m_url_seeds.empty())
{
if (m_url_seeds.size() == 1)
{
dict["url-list"] = m_url_seeds.front();
}
else
{
entry& list = dict["url-list"];
list = entry(entry::list_t);
for (std::vector<std::string>::const_iterator i
= m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
{
list.list().push_back(entry(*i));
}
}
}
dict["info"] = create_info_metadata();
entry const& info_section = dict["info"];
std::vector<char> buf;
bencode(std::back_inserter(buf), info_section);
m_info_hash = hasher(&buf[0], buf.size()).final();
return dict;
}
void torrent_info::set_hash(int index, const sha1_hash& h)
{
assert(index >= 0);
assert(index < (int)m_piece_hash.size());
m_piece_hash[index] = h;
}
void torrent_info::convert_file_names()
{
assert(false);
}
void torrent_info::print(std::ostream& os) const
{
os << "trackers:\n";
for (std::vector<announce_entry>::const_iterator i = trackers().begin();
i != trackers().end(); ++i)
{
os << i->tier << ": " << i->url << "\n";
}
if (!m_comment.empty())
os << "comment: " << m_comment << "\n";
if (m_creation_date != ptime(date(not_a_date_time)))
os << "creation date: " << to_simple_string(m_creation_date) << "\n";
os << "private: " << (m_private?"yes":"no") << "\n";
os << "number of pieces: " << num_pieces() << "\n";
os << "piece length: " << piece_length() << "\n";
os << "files:\n";
for (file_iterator i = begin_files(); i != end_files(); ++i)
os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n";
}
size_type torrent_info::piece_size(int index) const
{
assert(index >= 0 && index < num_pieces());
if (index == num_pieces()-1)
{
size_type size = total_size()
- (num_pieces() - 1) * piece_length();
assert(size > 0);
assert(size <= piece_length());
return size;
}
else
return piece_length();
}
void torrent_info::add_node(std::pair<std::string, int> const& node)
{
m_nodes.push_back(node);
}
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
, int size) const
{
assert(num_files() > 0);
std::vector<file_slice> ret;
size_type start = piece * (size_type)m_piece_length + offset;
assert(start + size <= m_total_size);
// find the file iterator and file offset
// TODO: make a vector that can map piece -> file index in O(1)
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
int counter = 0;
for (file_iter = begin_files();; ++counter, ++file_iter)
{
assert(file_iter != end_files());
if (file_offset < file_iter->size)
{
file_slice f;
f.file_index = counter;
f.offset = file_offset;
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size;
file_offset += f.size;
ret.push_back(f);
}
assert(size >= 0);
if (size <= 0) break;
file_offset -= file_iter->size;
}
return ret;
}
peer_request torrent_info::map_file(int file_index, size_type file_offset
, int size) const
{
assert(file_index < (int)m_files.size());
assert(file_index >= 0);
size_type offset = file_offset + m_files[file_index].offset;
peer_request ret;
ret.piece = offset / piece_length();
ret.start = offset - ret.piece * piece_length();
ret.length = size;
return ret;
}
}

View File

@ -1,569 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#include <boost/bind.hpp>
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
using namespace libtorrent;
using boost::tuples::make_tuple;
using boost::tuples::tuple;
using boost::bind;
namespace
{
enum
{
minimum_tracker_response_length = 3,
http_buffer_size = 2048
};
enum
{
FTEXT = 0x01,
FHCRC = 0x02,
FEXTRA = 0x04,
FNAME = 0x08,
FCOMMENT = 0x10,
FRESERVED = 0xe0,
GZIP_MAGIC0 = 0x1f,
GZIP_MAGIC1 = 0x8b
};
}
namespace libtorrent
{
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
// returns -1 if gzip header is invalid or the header size in bytes
int gzip_header(const char* buf, int size)
{
assert(buf != 0);
assert(size > 0);
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
const int total_size = size;
// The zip header cannot be shorter than 10 bytes
if (size < 10) return -1;
// check the magic header of gzip
if ((buffer[0] != GZIP_MAGIC0) || (buffer[1] != GZIP_MAGIC1)) return -1;
int method = buffer[2];
int flags = buffer[3];
// check for reserved flag and make sure it's compressed with the correct metod
if (method != Z_DEFLATED || (flags & FRESERVED) != 0) return -1;
// skip time, xflags, OS code
size -= 10;
buffer += 10;
if (flags & FEXTRA)
{
int extra_len;
if (size < 2) return -1;
extra_len = (buffer[1] << 8) | buffer[0];
if (size < (extra_len+2)) return -1;
size -= (extra_len + 2);
buffer += (extra_len + 2);
}
if (flags & FNAME)
{
while (size && *buffer)
{
--size;
++buffer;
}
if (!size || *buffer) return -1;
--size;
++buffer;
}
if (flags & FCOMMENT)
{
while (size && *buffer)
{
--size;
++buffer;
}
if (!size || *buffer) return -1;
--size;
++buffer;
}
if (flags & FHCRC)
{
if (size < 2) return -1;
size -= 2;
buffer += 2;
}
return total_size - size;
}
bool inflate_gzip(
std::vector<char>& buffer
, tracker_request const& req
, request_callback* requester
, int maximum_tracker_response_length)
{
assert(maximum_tracker_response_length > 0);
int header_len = gzip_header(&buffer[0], (int)buffer.size());
if (header_len < 0)
{
requester->tracker_request_error(req, 200, "invalid gzip header in tracker response");
return true;
}
// start off wth one kilobyte and grow
// if needed
std::vector<char> inflate_buffer(1024);
// initialize the zlib-stream
z_stream str;
// subtract 8 from the end of the buffer since that's CRC32 and input size
// and those belong to the gzip file
str.avail_in = (int)buffer.size() - header_len - 8;
str.next_in = reinterpret_cast<Bytef*>(&buffer[header_len]);
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[0]);
str.avail_out = (int)inflate_buffer.size();
str.zalloc = Z_NULL;
str.zfree = Z_NULL;
str.opaque = 0;
// -15 is really important. It will make inflate() not look for a zlib header
// and just deflate the buffer
if (inflateInit2(&str, -15) != Z_OK)
{
requester->tracker_request_error(req, 200, "gzip out of memory");
return true;
}
// inflate and grow inflate_buffer as needed
int ret = inflate(&str, Z_SYNC_FLUSH);
while (ret == Z_OK)
{
if (str.avail_out == 0)
{
if (inflate_buffer.size() >= (unsigned)maximum_tracker_response_length)
{
inflateEnd(&str);
requester->tracker_request_error(req, 200
, "tracker response too large");
return true;
}
int new_size = (int)inflate_buffer.size() * 2;
if (new_size > maximum_tracker_response_length) new_size = maximum_tracker_response_length;
int old_size = (int)inflate_buffer.size();
inflate_buffer.resize(new_size);
str.next_out = reinterpret_cast<Bytef*>(&inflate_buffer[old_size]);
str.avail_out = new_size - old_size;
}
ret = inflate(&str, Z_SYNC_FLUSH);
}
inflate_buffer.resize(inflate_buffer.size() - str.avail_out);
inflateEnd(&str);
if (ret != Z_STREAM_END)
{
requester->tracker_request_error(req, 200, "gzip error");
return true;
}
// commit the resulting buffer
std::swap(buffer, inflate_buffer);
return false;
}
std::string base64encode(const std::string& s)
{
static const char base64_table[] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
unsigned char inbuf[3];
unsigned char outbuf[4];
std::string ret;
for (std::string::const_iterator i = s.begin(); i != s.end();)
{
// available input is 1,2 or 3 bytes
// since we read 3 bytes at a time at most
int available_input = std::min(3, (int)std::distance(i, s.end()));
// clear input buffer
std::fill(inbuf, inbuf+3, 0);
// read a chunk of input into inbuf
for (int j = 0; j < available_input; ++j)
{
inbuf[j] = *i;
++i;
}
// encode inbuf to outbuf
outbuf[0] = (inbuf[0] & 0xfc) >> 2;
outbuf[1] = ((inbuf[0] & 0x03) << 4) | ((inbuf [1] & 0xf0) >> 4);
outbuf[2] = ((inbuf[1] & 0x0f) << 2) | ((inbuf [2] & 0xc0) >> 6);
outbuf[3] = inbuf[2] & 0x3f;
// write output
for (int j = 0; j < available_input+1; ++j)
{
ret += base64_table[outbuf[j]];
}
// write pad
for (int j = 0; j < 3 - available_input; ++j)
{
ret += '=';
}
}
return ret;
}
void intrusive_ptr_add_ref(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs >= 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
++c->m_refs;
}
void intrusive_ptr_release(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs > 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
--c->m_refs;
if (c->m_refs == 0)
{
l.unlock();
delete c;
}
}
timeout_handler::timeout_handler(demuxer& d)
: m_demuxer(d)
, m_start_time(second_clock::universal_time())
, m_read_time(second_clock::universal_time())
, m_timeout(d)
, m_completion_timeout(0)
, m_read_timeout(0)
, m_refs(0)
{}
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
{
m_completion_timeout = completion_timeout;
m_read_timeout = read_timeout;
m_start_time = second_clock::universal_time();
m_read_time = second_clock::universal_time();
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
void timeout_handler::restart_read_timeout()
{
m_read_time = second_clock::universal_time();
}
void timeout_handler::cancel()
{
m_completion_timeout = 0;
m_timeout.cancel();
}
void timeout_handler::timeout_callback(asio::error const& error) try
{
if (error) return;
if (m_completion_timeout == 0) return;
ptime now(second_clock::universal_time());
time_duration receive_timeout = now - m_read_time;
time_duration completion_timeout = now - m_start_time;
if (m_read_timeout
< receive_timeout.total_seconds()
|| m_completion_timeout
< completion_timeout.total_seconds())
{
on_timeout();
return;
}
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
catch (std::exception& e)
{
assert(false);
}
tracker_connection::tracker_connection(
tracker_manager& man
, tracker_request req
, demuxer& d
, boost::weak_ptr<request_callback> r)
: timeout_handler(d)
, m_requester(r)
, m_man(man)
, m_req(req)
{}
request_callback& tracker_connection::requester()
{
boost::shared_ptr<request_callback> r = m_requester.lock();
assert(r);
return *r;
}
void tracker_connection::fail(int code, char const* msg)
{
if (has_requester()) requester().tracker_request_error(
m_req, code, msg);
close();
}
void tracker_connection::fail_timeout()
{
if (has_requester()) requester().tracker_request_timed_out(m_req);
close();
}
void tracker_connection::close()
{
cancel();
m_man.remove_request(this);
}
void tracker_manager::remove_request(tracker_connection const* c)
{
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t::iterator i = std::find(m_connections.begin()
, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
if (i == m_connections.end()) return;
m_connections.erase(i);
}
tuple<std::string, std::string, int, std::string>
parse_url_components(std::string url)
{
std::string hostname; // hostname only
std::string protocol; // should be http
int port = 80;
// PARSE URL
std::string::iterator start = url.begin();
// remove white spaces in front of the url
while (start != url.end() && (*start == ' ' || *start == '\t'))
++start;
std::string::iterator end
= std::find(url.begin(), url.end(), ':');
protocol = std::string(start, end);
if (end == url.end()) throw std::runtime_error("invalid url");
++end;
if (end == url.end()) throw std::runtime_error("invalid url");
if (*end != '/') throw std::runtime_error("invalid url");
++end;
if (end == url.end()) throw std::runtime_error("invalid url");
if (*end != '/') throw std::runtime_error("invalid url");
++end;
start = end;
end = std::find(start, url.end(), '/');
std::string::iterator port_pos
= std::find(start, url.end(), ':');
if (port_pos < end)
{
hostname.assign(start, port_pos);
++port_pos;
try
{
port = boost::lexical_cast<int>(std::string(port_pos, end));
}
catch(boost::bad_lexical_cast&)
{
throw std::runtime_error("invalid url: \"" + url
+ "\", port number expected");
}
}
else
{
hostname.assign(start, end);
}
start = end;
return make_tuple(protocol, hostname, port
, std::string(start, url.end()));
}
void tracker_manager::queue_request(
demuxer& d
, tracker_request req
, std::string const& auth
, boost::weak_ptr<request_callback> c)
{
mutex_t::scoped_lock l(m_mutex);
assert(req.num_want >= 0);
if (req.event == tracker_request::stopped)
req.num_want = 0;
try
{
std::string protocol;
std::string hostname;
int port;
std::string request_string;
boost::tie(protocol, hostname, port, request_string)
= parse_url_components(req.url);
boost::intrusive_ptr<tracker_connection> con;
if (protocol == "http")
{
con = new http_tracker_connection(
d
, *this
, req
, hostname
, port
, request_string
, c
, m_settings
, auth);
}
else if (protocol == "udp")
{
con = new udp_tracker_connection(
d
, *this
, req
, hostname
, port
, c
, m_settings);
}
else
{
throw std::runtime_error("unkown protocol in tracker url");
}
m_connections.push_back(con);
if (con->has_requester()) con->requester().m_manager = this;
}
catch (std::exception& e)
{
if (boost::shared_ptr<request_callback> r = c.lock())
r->tracker_request_error(req, -1, e.what());
}
}
void tracker_manager::abort_all_requests()
{
// removes all connections from m_connections
// except those with a requester == 0 (since those are
// 'event=stopped'-requests)
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t keep_connections;
for (tracker_connections_t::const_iterator i =
m_connections.begin(); i != m_connections.end(); ++i)
{
tracker_request const& req = (*i)->tracker_req();
if (req.event == tracker_request::stopped)
keep_connections.push_back(*i);
}
std::swap(m_connections, keep_connections);
}
bool tracker_manager::empty() const
{
mutex_t::scoped_lock l(m_mutex);
return m_connections.empty();
}
}

View File

@ -1,522 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"
namespace
{
enum
{
udp_connection_retries = 4,
udp_announce_retries = 15,
udp_connect_timeout = 15,
udp_announce_timeout = 10,
udp_buffer_size = 2048
};
}
using namespace boost::posix_time;
using boost::bind;
using boost::lexical_cast;
namespace libtorrent
{
udp_tracker_connection::udp_tracker_connection(
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, boost::weak_ptr<request_callback> c
, session_settings const& stn)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_name_lookup(d)
, m_port(port)
, m_transaction_id(0)
, m_connection_id(0)
, m_settings(stn)
, m_attempts(0)
{
m_socket.reset(new datagram_socket(d));
tcp::resolver::query q(hostname, "0");
m_name_lookup.async_resolve(q
, boost::bind(&udp_tracker_connection::name_lookup, self(), _1, _2));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
void udp_tracker_connection::name_lookup(asio::error const& error
, tcp::resolver::iterator i) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error || i == tcp::resolver::iterator())
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("udp tracker name lookup successful");
#endif
restart_read_timeout();
m_target = udp::endpoint(i->endpoint().address(), m_port);
if (has_requester()) requester().m_tracker_address
= tcp::endpoint(i->endpoint().address(), m_port);
m_socket->connect(m_target);
send_udp_connect();
}
catch (std::exception& e)
{
fail(-1, e.what());
};
void udp_tracker_connection::on_timeout()
{
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void udp_tracker_connection::send_udp_connect()
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_CONNECT ["
+ lexical_cast<std::string>(tracker_req().info_hash) + "]");
}
#endif
if (!m_socket) return; // the operation was aborted
char send_buf[16];
char* ptr = send_buf;
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
// connection_id
detail::write_uint32(0x417, ptr);
detail::write_uint32(0x27101980, ptr);
// action (connect)
detail::write_int32(action_connect, ptr);
// transaction_id
detail::write_int32(m_transaction_id, ptr);
m_socket->send(asio::buffer((void*)send_buf, 16), 0);
++m_attempts;
m_buffer.resize(udp_buffer_size);
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
}
void udp_tracker_connection::connect_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
const char* ptr = &m_buffer[0];
int action = detail::read_int32(ptr);
int transaction = detail::read_int32(ptr);
if (action == action_error)
{
fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
return;
}
if (action != action_connect)
{
fail(-1, "invalid action in connect reply");
return;
}
if (m_transaction_id != transaction)
{
fail(-1, "incorrect transaction id");
return;
}
if (bytes_transferred < 16)
{
fail(-1, "udp_tracker_connection: "
"got a message with size < 16");
return;
}
// reset transaction
m_transaction_id = 0;
m_attempts = 0;
m_connection_id = detail::read_int64(ptr);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
+ lexical_cast<std::string>(m_connection_id) + "]");
}
#endif
if (tracker_req().kind == tracker_request::announce_request)
send_udp_announce();
else if (tracker_req().kind == tracker_request::scrape_request)
send_udp_scrape();
}
catch (std::exception& e)
{
fail(-1, e.what());
}
void udp_tracker_connection::send_udp_announce()
{
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
if (!m_socket) return; // the operation was aborted
std::vector<char> buf;
std::back_insert_iterator<std::vector<char> > out(buf);
tracker_request const& req = tracker_req();
// connection_id
detail::write_int64(m_connection_id, out);
// action (announce)
detail::write_int32(action_announce, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(req.info_hash.begin(), req.info_hash.end(), out);
// peer_id
std::copy(req.pid.begin(), req.pid.end(), out);
// downloaded
detail::write_int64(req.downloaded, out);
// left
detail::write_int64(req.left, out);
// uploaded
detail::write_int64(req.uploaded, out);
// event
detail::write_int32(req.event, out);
// ip address
detail::write_int32(0, out);
// key
detail::write_int32(req.key, out);
// num_want
detail::write_int32(req.num_want, out);
// port
detail::write_uint16(req.listen_port, out);
// extensions
detail::write_uint16(0, out);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_ANNOUNCE ["
+ lexical_cast<std::string>(req.info_hash) + "]");
}
#endif
m_socket->send(asio::buffer(buf), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::announce_response, self(), _1, _2));
}
void udp_tracker_connection::send_udp_scrape()
{
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
if (!m_socket) return; // the operation was aborted
std::vector<char> buf;
std::back_insert_iterator<std::vector<char> > out(buf);
// connection_id
detail::write_int64(m_connection_id, out);
// action (scrape)
detail::write_int32(action_scrape, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
m_socket->send(asio::buffer(&buf[0], buf.size()), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
}
void udp_tracker_connection::announce_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
fail(-1, "incorrect transaction id");
return;
}
if (action == action_error)
{
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != action_announce)
{
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int interval = detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
int complete = detail::read_int32(buf);
int num_peers = (bytes_transferred - 20) / 6;
if ((bytes_transferred - 20) % 6 != 0)
{
fail(-1, "invalid udp tracker response length");
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
}
#endif
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
for (int i = 0; i < num_peers; ++i)
{
peer_entry e;
std::stringstream s;
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf) << ".";
s << (int)detail::read_uint8(buf);
e.ip = s.str();
e.port = detail::read_uint16(buf);
e.pid.clear();
peer_list.push_back(e);
}
requester().tracker_response(tracker_req(), peer_list, interval
, complete, incomplete);
m_man.remove_request(this);
return;
}
catch (std::exception& e)
{
fail(-1, e.what());
}; // msvc 7.1 seems to require this
void udp_tracker_connection::scrape_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (!m_socket) return; // the operation was aborted
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
fail(-1, "incorrect transaction id");
return;
}
if (action == action_error)
{
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != action_scrape)
{
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int complete = detail::read_int32(buf);
/*int downloaded = */detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
requester().tracker_response(tracker_req(), peer_list, 0
, complete, incomplete);
m_man.remove_request(this);
}
catch (std::exception& e)
{
fail(-1, e.what());
}
}

View File

@ -1,455 +0,0 @@
/*
Copyright (c) 2003, Arvid Norberg
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.
*/
#include <vector>
#include <iostream>
#include <iomanip>
#include <limits>
#include <boost/bind.hpp>
#include <sstream>
#include "libtorrent/web_peer_connection.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/identify_client.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/version.hpp"
#include "libtorrent/aux_/session_impl.hpp"
using namespace boost::posix_time;
using boost::bind;
using boost::shared_ptr;
using libtorrent::aux::session_impl;
namespace libtorrent
{
web_peer_connection::web_peer_connection(
session_impl& ses
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote
, std::string const& url)
: peer_connection(ses, t, s, remote)
, m_url(url)
, m_first_request(true)
{
INVARIANT_CHECK;
m_max_out_request_queue = ses.settings().urlseed_pipeline_size;
// since this is a web seed, change the timeout
// according to the settings.
set_timeout(ses.settings().urlseed_timeout);
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << "*** web_peer_connection\n";
#endif
std::string protocol;
boost::tie(protocol, m_host, m_port, m_path)
= parse_url_components(url);
m_server_string = "URL seed @ ";
m_server_string += m_host;
}
web_peer_connection::~web_peer_connection()
{}
boost::optional<piece_block_progress>
web_peer_connection::downloading_piece_progress() const
{
if (!m_parser.header_finished() || m_requests.empty())
return boost::optional<piece_block_progress>();
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
int body_start = m_parser.body_start();
buffer::const_interval recv_buffer = receive_buffer();
assert(body_start <= recv_buffer.left());
piece_block_progress ret;
ret.piece_index = m_requests.front().piece;
ret.block_index = m_requests.front().start / t->block_size();
ret.bytes_downloaded = recv_buffer.left() - body_start;
ret.full_block_bytes = m_requests.front().length;
return ret;
}
void web_peer_connection::on_connected()
{
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
// this is always a seed
incoming_bitfield(std::vector<bool>(
t->torrent_file().num_pieces(), true));
// it is always possible to request pieces
incoming_unchoke();
reset_recv_buffer(512*1024+1024);
}
void web_peer_connection::write_request(peer_request const& r)
{
INVARIANT_CHECK;
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
assert(t->valid_metadata());
bool single_file_request = false;
if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
single_file_request = true;
torrent_info const& info = t->torrent_file();
std::string request;
m_requests.push_back(r);
bool using_proxy = false;
if (!m_ses.settings().proxy_ip.empty())
using_proxy = true;
if (single_file_request)
{
request += "GET ";
if (using_proxy) request += m_url;
else request += escape_path(m_path.c_str(), m_path.length());
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += m_ses.settings().user_agent;
}
if (using_proxy && !m_ses.settings().proxy_login.empty())
{
request += "\r\nProxy-Authorization: Basic ";
request += base64encode(m_ses.settings().proxy_login + ":"
+ m_ses.settings().proxy_password);
}
if (using_proxy)
{
request += "\r\nProxy-Connection: keep-alive";
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start);
request += "-";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start + r.length - 1);
if (m_first_request || using_proxy)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(0);
}
else
{
std::vector<file_slice> files = info.map_block(r.piece, r.start
, r.length);
for (std::vector<file_slice>::iterator i = files.begin();
i != files.end(); ++i)
{
file_slice const& f = *i;
request += "GET ";
if (using_proxy)
{
request += m_url;
std::string path = info.file_at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
else
{
std::string path = m_path;
path += info.file_at(f.file_index).path.string();
request += escape_path(path.c_str(), path.length());
}
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += m_ses.settings().user_agent;
}
if (using_proxy && !m_ses.settings().proxy_login.empty())
{
request += "\r\nProxy-Authorization: Basic ";
request += base64encode(m_ses.settings().proxy_login + ":"
+ m_ses.settings().proxy_password);
}
if (using_proxy)
{
request += "\r\nProxy-Connection: keep-alive";
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(f.offset);
request += "-";
request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
if (m_first_request || using_proxy)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(f.file_index);
}
}
send_buffer(request.c_str(), request.c_str() + request.size());
}
// --------------------------
// RECEIVE DATA
// --------------------------
// throws exception when the client should be disconnected
void web_peer_connection::on_receive(const asio::error& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error)
{
return;
}
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
incoming_piece_fragment();
for (;;)
{
buffer::const_interval recv_buffer = receive_buffer();
int payload;
int protocol;
boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
m_statistics.received_bytes(payload, protocol);
if (m_parser.status_code() != 206 && m_parser.status_code() != -1)
{
// we should not try this server again.
t->remove_url_seed(m_url);
if (m_parser.status_code() == 404)
throw std::runtime_error("File not found on server");
throw std::runtime_error("HTTP server does not support byte range requests");
}
if (!m_parser.finished()) break;
std::string server_version = m_parser.header<std::string>("Server");
if (!server_version.empty())
{
m_server_string = "URL seed @ ";
m_server_string += m_host;
m_server_string += " (";
m_server_string += server_version;
m_server_string += ")";
}
std::stringstream range_str(m_parser.header<std::string>("Content-Range"));
size_type range_start;
size_type range_end;
char dummy;
std::string bytes;
range_str >> bytes >> range_start >> dummy >> range_end;
if (!range_str)
{
// we should not try this server again.
t->remove_url_seed(m_url);
throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
}
// the http range is inclusive
range_end++;
torrent_info const& info = t->torrent_file();
if (m_requests.empty() || m_file_requests.empty())
throw std::runtime_error("unexpected HTTP response");
int file_index = m_file_requests.front();
m_file_requests.pop_front();
peer_request r = info.map_file(file_index, range_start
, range_end - range_start);
buffer::const_interval http_body = m_parser.get_body();
if (r == m_requests.front())
{
m_requests.pop_front();
incoming_piece(r, http_body.begin);
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
return;
}
if (!m_piece.empty())
{
// this is not the first partial request we get
if (m_intermediate_piece.start + m_intermediate_piece.length != r.start
|| m_intermediate_piece.piece != r.piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
}
else
{
// this is the first part of a partial request
if (r.start != m_requests.front().start
|| r.piece != m_requests.front().piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
m_intermediate_piece.piece = r.piece;
m_intermediate_piece.start = r.start;
m_intermediate_piece.length = 0;
}
m_piece.reserve(info.piece_length());
std::copy(http_body.begin, http_body.end, back_inserter(m_piece));
m_intermediate_piece.length += r.length;
if (m_intermediate_piece.length == m_requests.front().length)
{
assert(m_requests.front() == m_intermediate_piece);
assert(int(m_piece.size()) == m_intermediate_piece.length);
m_requests.pop_front();
incoming_piece(m_intermediate_piece, &m_piece[0]);
m_piece.clear();
}
else if (m_intermediate_piece.length > m_requests.front().length)
{
throw std::runtime_error("too large HTTP response body");
}
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
}
}
// --------------------------
// SEND DATA
// --------------------------
void web_peer_connection::get_peer_info(peer_info& p) const
{
assert(!associated_torrent().expired());
p.down_speed = statistics().download_rate();
p.up_speed = statistics().upload_rate();
p.payload_down_speed = statistics().download_payload_rate();
p.payload_up_speed = statistics().upload_payload_rate();
p.pid = pid();
p.ip = remote();
p.total_download = statistics().total_payload_download();
p.total_upload = statistics().total_payload_upload();
if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
p.upload_limit = -1;
else
p.upload_limit = m_ul_bandwidth_quota.given;
if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
p.download_limit = -1;
else
p.download_limit = m_dl_bandwidth_quota.given;
p.load_balancing = total_free_upload();
p.download_queue_length = (int)download_queue().size();
p.upload_queue_length = (int)upload_queue().size();
if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
{
p.downloading_piece_index = ret->piece_index;
p.downloading_block_index = ret->block_index;
p.downloading_progress = ret->bytes_downloaded;
p.downloading_total = ret->full_block_bytes;
}
else
{
p.downloading_piece_index = -1;
p.downloading_block_index = -1;
p.downloading_progress = 0;
p.downloading_total = 0;
}
p.flags = 0;
if (is_interesting()) p.flags |= peer_info::interesting;
if (is_choked()) p.flags |= peer_info::choked;
if (is_peer_interested()) p.flags |= peer_info::remote_interested;
if (has_peer_choked()) p.flags |= peer_info::remote_choked;
if (is_local()) p.flags |= peer_info::local_connection;
if (!is_connecting() && m_server_string.empty())
p.flags |= peer_info::handshake;
if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting;
if (is_queued()) p.flags |= peer_info::queued;
p.pieces = get_bitfield();
p.seed = is_seed();
p.client = m_server_string;
p.connection_type = peer_info::web_seed;
}
// throws exception when the client should be disconnected
void web_peer_connection::on_sent(asio::error const& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error) return;
m_statistics.sent_bytes(0, bytes_transferred);
}
#ifndef NDEBUG
void web_peer_connection::check_invariant() const
{
/*
assert(m_num_pieces == std::count(
m_have_piece.begin()
, m_have_piece.end()
, true));
*/ }
#endif
}