more UPnP work. updated ChangeLog and features documentation
This commit is contained in:
parent
b247f280b4
commit
9e1123eb9e
|
@ -1,3 +1,4 @@
|
||||||
|
* added UPnP support
|
||||||
* fixed problem where peer interested flags were not updated correctly
|
* fixed problem where peer interested flags were not updated correctly
|
||||||
when pieces were filtered
|
when pieces were filtered
|
||||||
* improvements to ut_pex messages, including support for seed flag
|
* improvements to ut_pex messages, including support for seed flag
|
||||||
|
|
|
@ -50,7 +50,7 @@ following features:</p>
|
||||||
<li>trackerless torrents (using the Mainline kademlia DHT protocol) with
|
<li>trackerless torrents (using the Mainline kademlia DHT protocol) with
|
||||||
some <a class="reference" href="dht_extensions.html">DHT extensions</a>.</li>
|
some <a class="reference" href="dht_extensions.html">DHT extensions</a>.</li>
|
||||||
<li>support for IPv6</li>
|
<li>support for IPv6</li>
|
||||||
<li>NAT-PMP support (automatic port mapping on routers that supports it)</li>
|
<li>NAT-PMP and UPnP support (automatic port mapping on routers that supports it)</li>
|
||||||
<li>piece-wise, unordered, incremental file allocation</li>
|
<li>piece-wise, unordered, incremental file allocation</li>
|
||||||
<li>uses separate threads for checking files and for main downloader, with a
|
<li>uses separate threads for checking files and for main downloader, with a
|
||||||
fool-proof thread-safe library interface. (i.e. There's no way for the
|
fool-proof thread-safe library interface. (i.e. There's no way for the
|
||||||
|
|
|
@ -32,7 +32,7 @@ following features:
|
||||||
* trackerless torrents (using the Mainline kademlia DHT protocol) with
|
* trackerless torrents (using the Mainline kademlia DHT protocol) with
|
||||||
some `DHT extensions`_.
|
some `DHT extensions`_.
|
||||||
* support for IPv6
|
* support for IPv6
|
||||||
* NAT-PMP support (automatic port mapping on routers that supports it)
|
* NAT-PMP and UPnP support (automatic port mapping on routers that supports it)
|
||||||
* piece-wise, unordered, incremental file allocation
|
* piece-wise, unordered, incremental file allocation
|
||||||
* uses separate threads for checking files and for main downloader, with a
|
* uses separate threads for checking files and for main downloader, with a
|
||||||
fool-proof thread-safe library interface. (i.e. There's no way for the
|
fool-proof thread-safe library interface. (i.e. There's no way for the
|
||||||
|
|
|
@ -78,6 +78,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/file_pool.hpp"
|
#include "libtorrent/file_pool.hpp"
|
||||||
#include "libtorrent/bandwidth_manager.hpp"
|
#include "libtorrent/bandwidth_manager.hpp"
|
||||||
#include "libtorrent/natpmp.hpp"
|
#include "libtorrent/natpmp.hpp"
|
||||||
|
#include "libtorrent/upnp.hpp"
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent
|
||||||
{
|
{
|
||||||
|
@ -393,6 +394,7 @@ namespace libtorrent
|
||||||
int m_external_udp_port;
|
int m_external_udp_port;
|
||||||
#endif
|
#endif
|
||||||
natpmp m_natpmp;
|
natpmp m_natpmp;
|
||||||
|
upnp m_upnp;
|
||||||
|
|
||||||
// the timer used to fire the second_tick
|
// the timer used to fire the second_tick
|
||||||
deadline_timer m_timer;
|
deadline_timer m_timer;
|
||||||
|
|
|
@ -39,6 +39,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/condition.hpp>
|
||||||
|
|
||||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -57,6 +59,7 @@ class upnp : boost::noncopyable
|
||||||
public:
|
public:
|
||||||
upnp(io_service& ios, address const& listen_interface
|
upnp(io_service& ios, address const& listen_interface
|
||||||
, std::string const& user_agent, portmap_callback_t const& cb);
|
, std::string const& user_agent, portmap_callback_t const& cb);
|
||||||
|
~upnp();
|
||||||
|
|
||||||
void rebind(address const& listen_interface);
|
void rebind(address const& listen_interface);
|
||||||
|
|
||||||
|
@ -67,6 +70,9 @@ public:
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
enum { num_mappings = 2 };
|
||||||
|
enum { default_lease_time = 3600 };
|
||||||
|
|
||||||
void update_mapping(int i, int port);
|
void update_mapping(int i, int port);
|
||||||
void resend_request(asio::error_code const& e);
|
void resend_request(asio::error_code const& e);
|
||||||
|
@ -81,32 +87,24 @@ private:
|
||||||
void on_upnp_map_response(asio::error_code const& e
|
void on_upnp_map_response(asio::error_code const& e
|
||||||
, libtorrent::http_parser const& p, rootdevice& d
|
, libtorrent::http_parser const& p, rootdevice& d
|
||||||
, int mapping);
|
, int mapping);
|
||||||
|
void on_upnp_unmap_response(asio::error_code const& e
|
||||||
/*
|
, libtorrent::http_parser const& p, rootdevice& d
|
||||||
void send_map_request(int i);
|
, int mapping);
|
||||||
void try_next_mapping(int i);
|
void on_expire(asio::error_code const& e);
|
||||||
void update_expiration_timer();
|
|
||||||
void refresh_mapping(int i);
|
|
||||||
void mapping_expired(asio::error_code const& e, int i);
|
|
||||||
*/
|
|
||||||
|
|
||||||
void post(rootdevice& d, std::stringstream const& s
|
void post(rootdevice& d, std::stringstream const& s
|
||||||
, std::string const& soap_action);
|
, std::string const& soap_action);
|
||||||
void map_port(rootdevice& d, int i);
|
void map_port(rootdevice& d, int i);
|
||||||
|
void unmap_port(rootdevice& d, int i);
|
||||||
|
|
||||||
struct mapping_t
|
struct mapping_t
|
||||||
{
|
{
|
||||||
mapping_t()
|
mapping_t()
|
||||||
: need_update(false)
|
: local_port(0)
|
||||||
, local_port(0)
|
|
||||||
, external_port(0)
|
, external_port(0)
|
||||||
, protocol(1)
|
, protocol(1)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// indicates that the mapping has changed
|
|
||||||
// and needs an update
|
|
||||||
bool need_update;
|
|
||||||
|
|
||||||
// the time the port mapping will expire
|
// the time the port mapping will expire
|
||||||
boost::posix_time::ptime expires;
|
boost::posix_time::ptime expires;
|
||||||
|
|
||||||
|
@ -125,8 +123,12 @@ private:
|
||||||
|
|
||||||
struct rootdevice
|
struct rootdevice
|
||||||
{
|
{
|
||||||
rootdevice(): ports_mapped(false), lease_duration(3600)
|
rootdevice(): lease_duration(default_lease_time)
|
||||||
, supports_specific_external(true) {}
|
, supports_specific_external(true)
|
||||||
|
{
|
||||||
|
mapping[0].protocol = 0;
|
||||||
|
mapping[1].protocol = 1;
|
||||||
|
}
|
||||||
// the interface url, through which the list of
|
// the interface url, through which the list of
|
||||||
// supported interfaces are fetched
|
// supported interfaces are fetched
|
||||||
std::string url;
|
std::string url;
|
||||||
|
@ -136,10 +138,7 @@ private:
|
||||||
// either the WANIP namespace or the WANPPP namespace
|
// either the WANIP namespace or the WANPPP namespace
|
||||||
std::string service_namespace;
|
std::string service_namespace;
|
||||||
|
|
||||||
mapping_t mapping[2];
|
mapping_t mapping[num_mappings];
|
||||||
|
|
||||||
// true if we already mapped the ports on this device
|
|
||||||
bool ports_mapped;
|
|
||||||
|
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
int port;
|
int port;
|
||||||
|
@ -188,7 +187,12 @@ private:
|
||||||
// timer used to refresh mappings
|
// timer used to refresh mappings
|
||||||
deadline_timer m_refresh_timer;
|
deadline_timer m_refresh_timer;
|
||||||
|
|
||||||
|
// locks m_closing and m_devices
|
||||||
|
boost::mutex m_mutex;
|
||||||
|
boost::condition m_condvar;
|
||||||
|
|
||||||
bool m_disabled;
|
bool m_disabled;
|
||||||
|
bool m_closing;
|
||||||
|
|
||||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
std::ofstream m_log;
|
std::ofstream m_log;
|
||||||
|
|
|
@ -496,6 +496,9 @@ namespace libtorrent { namespace detail
|
||||||
#endif
|
#endif
|
||||||
, m_natpmp(m_io_service, m_listen_interface.address()
|
, m_natpmp(m_io_service, m_listen_interface.address()
|
||||||
, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
||||||
|
, m_upnp(m_io_service, m_listen_interface.address()
|
||||||
|
, m_settings.user_agent
|
||||||
|
, bind(&session_impl::on_port_mapping, this, _1, _2, _3))
|
||||||
, m_timer(m_io_service)
|
, m_timer(m_io_service)
|
||||||
, m_checker_impl(*this)
|
, m_checker_impl(*this)
|
||||||
{
|
{
|
||||||
|
@ -687,6 +690,7 @@ namespace libtorrent { namespace detail
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
m_natpmp.set_mappings(m_listen_interface.port(), 0);
|
m_natpmp.set_mappings(m_listen_interface.port(), 0);
|
||||||
|
m_upnp.set_mappings(m_listen_interface.port(), 0);
|
||||||
if (m_listen_socket) async_accept();
|
if (m_listen_socket) async_accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1078,6 +1082,7 @@ namespace libtorrent { namespace detail
|
||||||
deadline_timer tracker_timer(m_io_service);
|
deadline_timer tracker_timer(m_io_service);
|
||||||
// this will remove the port mappings
|
// this will remove the port mappings
|
||||||
m_natpmp.close();
|
m_natpmp.close();
|
||||||
|
m_upnp.close();
|
||||||
|
|
||||||
session_impl::mutex_t::scoped_lock l(m_mutex);
|
session_impl::mutex_t::scoped_lock l(m_mutex);
|
||||||
|
|
||||||
|
@ -1471,8 +1476,12 @@ namespace libtorrent { namespace detail
|
||||||
m_dht->rebind(new_interface.address()
|
m_dht->rebind(new_interface.address()
|
||||||
, m_dht_settings.service_port);
|
, m_dht_settings.service_port);
|
||||||
if (new_listen_address)
|
if (new_listen_address)
|
||||||
|
{
|
||||||
m_natpmp.rebind(new_interface.address());
|
m_natpmp.rebind(new_interface.address());
|
||||||
|
m_upnp.rebind(new_interface.address());
|
||||||
|
}
|
||||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||||
|
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1595,6 +1604,7 @@ namespace libtorrent { namespace detail
|
||||||
}
|
}
|
||||||
m_external_udp_port = m_dht_settings.service_port;
|
m_external_udp_port = m_dht_settings.service_port;
|
||||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||||
|
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||||
m_dht = new dht::dht_tracker(m_io_service
|
m_dht = new dht::dht_tracker(m_io_service
|
||||||
, m_dht_settings, m_listen_interface.address()
|
, m_dht_settings, m_listen_interface.address()
|
||||||
, startup_state);
|
, startup_state);
|
||||||
|
@ -1625,6 +1635,7 @@ namespace libtorrent { namespace detail
|
||||||
m_dht->rebind(m_listen_interface.address()
|
m_dht->rebind(m_listen_interface.address()
|
||||||
, settings.service_port);
|
, settings.service_port);
|
||||||
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
m_natpmp.set_mappings(0, m_dht_settings.service_port);
|
||||||
|
m_upnp.set_mappings(0, m_dht_settings.service_port);
|
||||||
m_external_udp_port = settings.service_port;
|
m_external_udp_port = settings.service_port;
|
||||||
}
|
}
|
||||||
m_dht_settings = settings;
|
m_dht_settings = settings;
|
||||||
|
|
162
src/upnp.cpp
162
src/upnp.cpp
|
@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
#include <asio/ip/host_name.hpp>
|
#include <asio/ip/host_name.hpp>
|
||||||
#include <asio/ip/multicast.hpp>
|
#include <asio/ip/multicast.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
using boost::bind;
|
using boost::bind;
|
||||||
|
@ -48,6 +49,8 @@ using namespace libtorrent;
|
||||||
using boost::posix_time::microsec_clock;
|
using boost::posix_time::microsec_clock;
|
||||||
using boost::posix_time::milliseconds;
|
using boost::posix_time::milliseconds;
|
||||||
using boost::posix_time::seconds;
|
using boost::posix_time::seconds;
|
||||||
|
using boost::posix_time::second_clock;
|
||||||
|
using boost::posix_time::ptime;
|
||||||
|
|
||||||
enum { num_mappings = 2 };
|
enum { num_mappings = 2 };
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@ upnp::upnp(io_service& ios, address const& listen_interface
|
||||||
, m_broadcast_timer(ios)
|
, m_broadcast_timer(ios)
|
||||||
, m_refresh_timer(ios)
|
, m_refresh_timer(ios)
|
||||||
, m_disabled(false)
|
, m_disabled(false)
|
||||||
|
, m_closing(false)
|
||||||
{
|
{
|
||||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc);
|
m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc);
|
||||||
|
@ -71,6 +75,13 @@ upnp::upnp(io_service& ios, address const& listen_interface
|
||||||
rebind(listen_interface);
|
rebind(listen_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upnp::~upnp()
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
while (!m_devices.empty())
|
||||||
|
m_condvar.wait(l);
|
||||||
|
}
|
||||||
|
|
||||||
void upnp::rebind(address const& listen_interface)
|
void upnp::rebind(address const& listen_interface)
|
||||||
{
|
{
|
||||||
if (listen_interface.is_v4() && listen_interface != address_v4::from_string("0.0.0.0"))
|
if (listen_interface.is_v4() && listen_interface != address_v4::from_string("0.0.0.0"))
|
||||||
|
@ -269,7 +280,9 @@ void upnp::on_reply(asio::error_code const& e
|
||||||
|
|
||||||
rootdevice d;
|
rootdevice d;
|
||||||
d.url = url;
|
d.url = url;
|
||||||
|
|
||||||
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
|
||||||
std::set<rootdevice>::iterator i = m_devices.find(d);
|
std::set<rootdevice>::iterator i = m_devices.find(d);
|
||||||
|
|
||||||
if (i == m_devices.end())
|
if (i == m_devices.end())
|
||||||
|
@ -316,11 +329,6 @@ void upnp::on_reply(asio::error_code const& e
|
||||||
, boost::bind(&upnp::on_upnp_xml, this, _1, _2, boost::ref(d))));
|
, boost::bind(&upnp::on_upnp_xml, this, _1, _2, boost::ref(d))));
|
||||||
d.upnp_connection->get(d.url);
|
d.upnp_connection->get(d.url);
|
||||||
}
|
}
|
||||||
else if (!i->ports_mapped)
|
|
||||||
{
|
|
||||||
// TODO: initiate a port map operation if any is pending
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void upnp::post(rootdevice& d, std::stringstream const& soap
|
void upnp::post(rootdevice& d, std::stringstream const& soap
|
||||||
|
@ -372,6 +380,33 @@ void upnp::map_port(rootdevice& d, int i)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void upnp::unmap_port(rootdevice& d, int i)
|
||||||
|
{
|
||||||
|
d.upnp_connection.reset(new http_connection(m_socket.io_service()
|
||||||
|
, boost::bind(&upnp::on_upnp_unmap_response, this, _1, _2
|
||||||
|
, boost::ref(d), i)));
|
||||||
|
|
||||||
|
std::string soap_action = "DeletePortMapping";
|
||||||
|
|
||||||
|
std::stringstream soap;
|
||||||
|
|
||||||
|
soap << "<?xml version=\"1.0\"?>\n"
|
||||||
|
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||||
|
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||||
|
"<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
|
||||||
|
|
||||||
|
soap << "<NewRemoteHost></NewRemoteHost>"
|
||||||
|
"<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
|
||||||
|
"<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>";
|
||||||
|
soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
|
||||||
|
|
||||||
|
post(d, soap, soap_action);
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " ==> DeletePortMapping: " << soap.str() << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
struct parse_state
|
struct parse_state
|
||||||
|
@ -507,6 +542,10 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
if (m_closing) return;
|
||||||
|
l.unlock();
|
||||||
|
|
||||||
// error code response may look like this:
|
// error code response may look like this:
|
||||||
// <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
|
// <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
// s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
// s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
@ -568,9 +607,13 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
||||||
m_callback(0, 0, "UPnP mapping error " + boost::lexical_cast<std::string>(s.error_code)
|
m_callback(0, 0, "UPnP mapping error " + boost::lexical_cast<std::string>(s.error_code)
|
||||||
+ ": " + error_codes[s.error_code]);
|
+ ": " + error_codes[s.error_code]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: update d.mapping[mapping].expires
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
std::cerr << std::string(p.get_body().begin, p.get_body().end) << std::endl;
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== map response: " << std::string(p.get_body().begin, p.get_body().end)
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (s.error_code == -1)
|
if (s.error_code == -1)
|
||||||
{
|
{
|
||||||
int tcp = 0;
|
int tcp = 0;
|
||||||
|
@ -582,16 +625,111 @@ void upnp::on_upnp_map_response(asio::error_code const& e
|
||||||
udp = d.mapping[mapping].external_port;
|
udp = d.mapping[mapping].external_port;
|
||||||
|
|
||||||
m_callback(tcp, udp, "");
|
m_callback(tcp, udp, "");
|
||||||
|
if (d.lease_duration > 0)
|
||||||
|
{
|
||||||
|
d.mapping[mapping].expires = second_clock::universal_time() + seconds(d.lease_duration * 0.75);
|
||||||
|
ptime next_expire = m_refresh_timer.expires_at();
|
||||||
|
if (next_expire == ptime(boost::date_time::not_a_date_time)
|
||||||
|
|| next_expire < second_clock::universal_time()
|
||||||
|
|| next_expire > d.mapping[mapping].expires)
|
||||||
|
{
|
||||||
|
m_refresh_timer.expires_at(d.mapping[mapping].expires);
|
||||||
|
m_refresh_timer.async_wait(bind(&upnp::on_expire, this, _1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.mapping[mapping].expires = ptime(boost::date_time::not_a_date_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapping < 1) map_port(d, mapping + 1);
|
if (mapping < num_mappings - 1)
|
||||||
|
map_port(d, mapping + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::on_upnp_unmap_response(asio::error_code const& e
|
||||||
|
, libtorrent::http_parser const& p, rootdevice& d, int mapping)
|
||||||
|
{
|
||||||
|
d.upnp_connection.reset();
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== error while deleting portmap: " << e << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ignore errors and continue with the next mapping for this device
|
||||||
|
if (mapping < num_mappings - 1)
|
||||||
|
{
|
||||||
|
unmap_port(d, mapping + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
// the main thread is likely to be waiting for
|
||||||
|
// all the unmap operations to complete
|
||||||
|
m_devices.erase(d);
|
||||||
|
m_condvar.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::on_expire(asio::error_code const& e)
|
||||||
|
{
|
||||||
|
if (e) return;
|
||||||
|
|
||||||
|
ptime now = second_clock::universal_time();
|
||||||
|
ptime next_expire = ptime(boost::date_time::not_a_date_time);
|
||||||
|
|
||||||
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||||
|
, end(m_devices.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||||
|
for (int m = 0; m < num_mappings; ++m)
|
||||||
|
{
|
||||||
|
if (d.mapping[m].expires != ptime(boost::date_time::not_a_date_time))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (d.mapping[m].expires < now)
|
||||||
|
{
|
||||||
|
d.mapping[m].expires = ptime(boost::date_time::not_a_date_time);
|
||||||
|
map_port(d, m);
|
||||||
|
}
|
||||||
|
else if (next_expire == ptime(boost::date_time::not_a_date_time)
|
||||||
|
|| d.mapping[m].expires < next_expire)
|
||||||
|
{
|
||||||
|
next_expire = d.mapping[m].expires;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (next_expire != ptime(boost::date_time::not_a_date_time))
|
||||||
|
{
|
||||||
|
m_refresh_timer.expires_at(next_expire);
|
||||||
|
m_refresh_timer.async_wait(bind(&upnp::on_expire, this, _1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void upnp::close()
|
void upnp::close()
|
||||||
{
|
{
|
||||||
if (m_disabled) return;
|
if (m_disabled) return;
|
||||||
m_socket.close();
|
m_socket.close();
|
||||||
std::for_each(m_devices.begin(), m_devices.end()
|
|
||||||
, bind(&rootdevice::close, _1));
|
boost::mutex::scoped_lock l(m_mutex);
|
||||||
|
m_closing = true;
|
||||||
|
|
||||||
|
for (std::set<rootdevice>::iterator i = m_devices.begin()
|
||||||
|
, end(m_devices.end()); i != end; ++i)
|
||||||
|
{
|
||||||
|
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||||
|
unmap_port(d, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue