fixed potential deadlock in natpmp and upnp

This commit is contained in:
Arvid Norberg 2009-06-13 08:04:53 +00:00
parent 2da772210c
commit 7328b30517
4 changed files with 146 additions and 123 deletions

View File

@ -68,17 +68,19 @@ public:
private: private:
void update_mapping(int i); typedef boost::mutex mutex_t;
void send_map_request(int i);
void update_mapping(int i, mutex_t::scoped_lock& l);
void send_map_request(int i, mutex_t::scoped_lock& l);
void resend_request(int i, error_code const& e); void resend_request(int i, error_code const& e);
void on_reply(error_code const& e void on_reply(error_code const& e
, std::size_t bytes_transferred); , std::size_t bytes_transferred);
void try_next_mapping(int i); void try_next_mapping(int i, mutex_t::scoped_lock& l);
void update_expiration_timer(); void update_expiration_timer();
void mapping_expired(error_code const& e, int i); void mapping_expired(error_code const& e, int i);
void log(char const* msg); void log(char const* msg, mutex_t::scoped_lock& l);
void disable(error_code const& ec); void disable(error_code const& ec, mutex_t::scoped_lock& l);
struct mapping_t struct mapping_t
{ {
@ -153,8 +155,7 @@ private:
bool m_abort; bool m_abort;
typedef boost::mutex mutex_t; mutable mutex_t m_mutex;
mutex_t m_mutex;
}; };
} }

View File

@ -123,7 +123,9 @@ public:
private: private:
void discover_device_impl(); typedef boost::mutex mutex_t;
void discover_device_impl(mutex_t::scoped_lock& l);
static address_v4 upnp_multicast_address; static address_v4 upnp_multicast_address;
static udp::endpoint upnp_multicast_endpoint; static udp::endpoint upnp_multicast_endpoint;
@ -134,8 +136,8 @@ private:
, std::size_t bytes_transferred); , std::size_t bytes_transferred);
struct rootdevice; struct rootdevice;
void next(rootdevice& d, int i); void next(rootdevice& d, int i, mutex_t::scoped_lock& l);
void update_map(rootdevice& d, int i); void update_map(rootdevice& d, int i, mutex_t::scoped_lock& l);
void on_upnp_xml(error_code const& e void on_upnp_xml(error_code const& e
@ -149,14 +151,14 @@ private:
, int mapping, http_connection& c); , int mapping, http_connection& c);
void on_expire(error_code const& e); void on_expire(error_code const& e);
void disable(error_code const& ec); void disable(error_code const& ec, mutex_t::scoped_lock& l);
void return_error(int mapping, int code); void return_error(int mapping, int code, mutex_t::scoped_lock& l);
void log(char const* msg); void log(char const* msg, mutex_t::scoped_lock& l);
void delete_port_mapping(rootdevice& d, int i); void delete_port_mapping(rootdevice& d, int i);
void create_port_mapping(http_connection& c, rootdevice& d, int i); void create_port_mapping(http_connection& c, rootdevice& d, int i);
void post(upnp::rootdevice const& d, char const* soap void post(upnp::rootdevice const& d, char const* soap
, char const* soap_action); , char const* soap_action, mutex_t::scoped_lock& l);
int num_mappings() const { return int(m_mappings.size()); } int num_mappings() const { return int(m_mappings.size()); }
@ -304,7 +306,6 @@ private:
connection_queue& m_cc; connection_queue& m_cc;
typedef boost::mutex mutex_t;
mutex_t m_mutex; mutex_t m_mutex;
std::string m_model; std::string m_model;

View File

@ -75,8 +75,8 @@ void natpmp::rebind(address const& listen_interface)
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "failed to find default route: %s", ec.message().c_str()); snprintf(msg, sizeof(msg), "failed to find default route: %s", ec.message().c_str());
log(msg); log(msg, l);
disable(ec); disable(ec, l);
return; return;
} }
@ -89,18 +89,18 @@ void natpmp::rebind(address const& listen_interface)
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "found router at: %s" snprintf(msg, sizeof(msg), "found router at: %s"
, print_address(m_nat_endpoint.address()).c_str()); , print_address(m_nat_endpoint.address()).c_str());
log(msg); log(msg, l);
m_socket.open(udp::v4(), ec); m_socket.open(udp::v4(), ec);
if (ec) if (ec)
{ {
disable(ec); disable(ec, l);
return; return;
} }
m_socket.bind(udp::endpoint(address_v4::any(), 0), ec); m_socket.bind(udp::endpoint(address_v4::any(), 0), ec);
if (ec) if (ec)
{ {
disable(ec); disable(ec, l);
return; return;
} }
@ -114,12 +114,14 @@ void natpmp::rebind(address const& listen_interface)
|| i->action != mapping_t::action_none) || i->action != mapping_t::action_none)
continue; continue;
i->action = mapping_t::action_add; i->action = mapping_t::action_add;
update_mapping(i - m_mappings.begin()); update_mapping(i - m_mappings.begin(), l);
} }
} }
bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const
{ {
mutex_t::scoped_lock l(m_mutex);
TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0);
if (index >= int(m_mappings.size()) || index < 0) return false; if (index >= int(m_mappings.size()) || index < 0) return false;
mapping_t const& m = m_mappings[index]; mapping_t const& m = m_mappings[index];
@ -130,12 +132,14 @@ bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& pr
return true; return true;
} }
void natpmp::log(char const* msg) void natpmp::log(char const* msg, mutex_t::scoped_lock& l)
{ {
l.unlock();
m_log_callback(msg); m_log_callback(msg);
l.lock();
} }
void natpmp::disable(error_code const& ec) void natpmp::disable(error_code const& ec, mutex_t::scoped_lock& l)
{ {
m_disabled = true; m_disabled = true;
@ -144,13 +148,18 @@ void natpmp::disable(error_code const& ec)
{ {
if (i->protocol == none) continue; if (i->protocol == none) continue;
i->protocol = none; i->protocol = none;
m_callback(i - m_mappings.begin(), 0, ec); int index = i - m_mappings.begin();
l.unlock();
m_callback(index, 0, ec);
l.lock();
} }
close(); close();
} }
void natpmp::delete_mapping(int index) void natpmp::delete_mapping(int index)
{ {
mutex_t::scoped_lock l(m_mutex);
TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0);
if (index >= int(m_mappings.size()) || index < 0) return; if (index >= int(m_mappings.size()) || index < 0) return;
mapping_t& m = m_mappings[index]; mapping_t& m = m_mappings[index];
@ -164,7 +173,7 @@ void natpmp::delete_mapping(int index)
} }
m.action = mapping_t::action_delete; m.action = mapping_t::action_delete;
update_mapping(index); update_mapping(index, l);
} }
int natpmp::add_mapping(protocol_type p, int external_port, int local_port) int natpmp::add_mapping(protocol_type p, int external_port, int local_port)
@ -187,11 +196,11 @@ int natpmp::add_mapping(protocol_type p, int external_port, int local_port)
int mapping_index = i - m_mappings.begin(); int mapping_index = i - m_mappings.begin();
update_mapping(mapping_index); update_mapping(mapping_index, l);
return mapping_index; return mapping_index;
} }
void natpmp::try_next_mapping(int i) void natpmp::try_next_mapping(int i, mutex_t::scoped_lock& l)
{ {
/* /*
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
@ -211,7 +220,7 @@ void natpmp::try_next_mapping(int i)
*/ */
if (i < int(m_mappings.size()) - 1) if (i < int(m_mappings.size()) - 1)
{ {
update_mapping(i + 1); update_mapping(i + 1, l);
return; return;
} }
@ -237,10 +246,10 @@ void natpmp::try_next_mapping(int i)
// m_log << " updating " << (m - m_mappings.begin()) << std::endl; // m_log << " updating " << (m - m_mappings.begin()) << std::endl;
//#endif //#endif
update_mapping(m - m_mappings.begin()); update_mapping(m - m_mappings.begin(), l);
} }
void natpmp::update_mapping(int i) void natpmp::update_mapping(int i, mutex_t::scoped_lock& l)
{ {
if (i == m_mappings.size()) if (i == m_mappings.size())
{ {
@ -260,7 +269,7 @@ void natpmp::update_mapping(int i)
if (m.action == mapping_t::action_none if (m.action == mapping_t::action_none
|| m.protocol == none) || m.protocol == none)
{ {
try_next_mapping(i); try_next_mapping(i, l);
return; return;
} }
@ -269,11 +278,11 @@ void natpmp::update_mapping(int i)
// the socket is not currently in use // the socket is not currently in use
// send out a mapping request // send out a mapping request
m_retry_count = 0; m_retry_count = 0;
send_map_request(i); send_map_request(i, l);
} }
} }
void natpmp::send_map_request(int i) void natpmp::send_map_request(int i, mutex_t::scoped_lock& l)
{ {
using namespace libtorrent::detail; using namespace libtorrent::detail;
@ -298,7 +307,7 @@ void natpmp::send_map_request(int i)
, m.action == mapping_t::action_add ? "add" : "delete" , m.action == mapping_t::action_add ? "add" : "delete"
, m.protocol == udp ? "udp" : "tcp" , m.protocol == udp ? "udp" : "tcp"
, m.local_port, m.external_port, ttl); , m.local_port, m.external_port, ttl);
log(msg); log(msg, l);
error_code ec; error_code ec;
m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint, 0, ec); m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint, 0, ec);
@ -310,7 +319,7 @@ void natpmp::send_map_request(int i)
// immediately // immediately
m_currently_mapping = -1; m_currently_mapping = -1;
m.action = mapping_t::action_none; m.action = mapping_t::action_none;
try_next_mapping(i); try_next_mapping(i, l);
} }
else else
{ {
@ -335,21 +344,23 @@ void natpmp::resend_request(int i, error_code const& e)
m_mappings[i].action = mapping_t::action_none; m_mappings[i].action = mapping_t::action_none;
// try again in two hours // try again in two hours
m_mappings[i].expires = time_now() + hours(2); m_mappings[i].expires = time_now() + hours(2);
try_next_mapping(i); try_next_mapping(i, l);
return; return;
} }
send_map_request(i); send_map_request(i, l);
} }
void natpmp::on_reply(error_code const& e void natpmp::on_reply(error_code const& e
, std::size_t bytes_transferred) , std::size_t bytes_transferred)
{ {
mutex_t::scoped_lock l(m_mutex);
using namespace libtorrent::detail; using namespace libtorrent::detail;
if (e) if (e)
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error on receiving reply: %s", e.message().c_str()); snprintf(msg, sizeof(msg), "error on receiving reply: %s", e.message().c_str());
log(msg); log(msg, l);
return; return;
} }
@ -360,7 +371,7 @@ void natpmp::on_reply(error_code const& e
/* /*
if ((rand() % 2) == 0) if ((rand() % 2) == 0)
{ {
log(" simulating drop"); log(" simulating drop", l);
return; return;
} }
*/ */
@ -369,12 +380,10 @@ void natpmp::on_reply(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "received packet from wrong IP: %s" snprintf(msg, sizeof(msg), "received packet from wrong IP: %s"
, print_endpoint(m_remote).c_str()); , print_endpoint(m_remote).c_str());
log(msg); log(msg, l);
return; return;
} }
mutex_t::scoped_lock l(m_mutex);
error_code ec; error_code ec;
m_send_timer.cancel(ec); m_send_timer.cancel(ec);
@ -401,7 +410,7 @@ void natpmp::on_reply(error_code const& e
{ {
snprintf(msg + num_chars, sizeof(msg) - num_chars, "unexpected version: %u" snprintf(msg + num_chars, sizeof(msg) - num_chars, "unexpected version: %u"
, version); , version);
log(msg); log(msg, l);
} }
mapping_t* m = 0; mapping_t* m = 0;
@ -420,10 +429,10 @@ void natpmp::on_reply(error_code const& e
if (m == 0) if (m == 0)
{ {
snprintf(msg + num_chars, sizeof(msg) - num_chars, " not found in map table"); snprintf(msg + num_chars, sizeof(msg) - num_chars, " not found in map table");
log(msg); log(msg, l);
return; return;
} }
log(msg); log(msg, l);
if (public_port == 0 || lifetime == 0) if (public_port == 0 || lifetime == 0)
{ {
@ -451,18 +460,22 @@ void natpmp::on_reply(error_code const& e
if (result >= 1 && result <= 5) ev = errors[result - 1]; if (result >= 1 && result <= 5) ev = errors[result - 1];
m->expires = time_now() + hours(2); m->expires = time_now() + hours(2);
l.unlock();
m_callback(index, 0, error_code(ev, libtorrent_category)); m_callback(index, 0, error_code(ev, libtorrent_category));
l.lock();
} }
else if (m->action == mapping_t::action_add) else if (m->action == mapping_t::action_add)
{ {
l.unlock();
m_callback(index, m->external_port, error_code(errors::no_error, libtorrent_category)); m_callback(index, m->external_port, error_code(errors::no_error, libtorrent_category));
l.lock();
} }
m_currently_mapping = -1; m_currently_mapping = -1;
m->action = mapping_t::action_none; m->action = mapping_t::action_none;
m_send_timer.cancel(ec); m_send_timer.cancel(ec);
update_expiration_timer(); update_expiration_timer();
try_next_mapping(index); try_next_mapping(index, l);
} }
void natpmp::update_expiration_timer() void natpmp::update_expiration_timer()
@ -527,17 +540,17 @@ void natpmp::mapping_expired(error_code const& e, int i)
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "mapping %u expired", i); snprintf(msg, sizeof(msg), "mapping %u expired", i);
log(msg); log(msg, l);
m_mappings[i].action = mapping_t::action_add; m_mappings[i].action = mapping_t::action_add;
if (m_next_refresh == i) m_next_refresh = -1; if (m_next_refresh == i) m_next_refresh = -1;
update_mapping(i); update_mapping(i, l);
} }
void natpmp::close() void natpmp::close()
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
m_abort = true; m_abort = true;
log("closing"); log("closing", l);
/* /*
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string() << " close" << std::endl; m_log << time_now_string() << " close" << std::endl;
@ -564,6 +577,6 @@ void natpmp::close()
} }
error_code ec; error_code ec;
m_refresh_timer.cancel(ec); m_refresh_timer.cancel(ec);
update_mapping(0); update_mapping(0, l);
} }

View File

@ -107,17 +107,19 @@ void upnp::discover_device()
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
if (m_socket.num_send_sockets() == 0) if (m_socket.num_send_sockets() == 0)
log("No network interfaces to broadcast to"); log("No network interfaces to broadcast to", l);
discover_device_impl(); discover_device_impl(l);
} }
void upnp::log(char const* msg) void upnp::log(char const* msg, mutex_t::scoped_lock& l)
{ {
l.unlock();
m_log_callback(msg); m_log_callback(msg);
l.lock();
} }
void upnp::discover_device_impl() void upnp::discover_device_impl(mutex_t::scoped_lock& l)
{ {
const char msearch[] = const char msearch[] =
"M-SEARCH * HTTP/1.1\r\n" "M-SEARCH * HTTP/1.1\r\n"
@ -138,8 +140,8 @@ void upnp::discover_device_impl()
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting.", ec.message().c_str()); snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting.", ec.message().c_str());
log(msg); log(msg, l);
disable(ec); disable(ec, l);
return; return;
} }
@ -148,7 +150,7 @@ void upnp::discover_device_impl()
m_broadcast_timer.async_wait(bind(&upnp::resend_request m_broadcast_timer.async_wait(bind(&upnp::resend_request
, self(), _1)); , self(), _1));
log("broadcasting search for rootdevice"); log("broadcasting search for rootdevice", l);
} }
// returns a reference to a mapping or -1 on failure // returns a reference to a mapping or -1 on failure
@ -160,7 +162,7 @@ int upnp::add_mapping(upnp::protocol_type p, int external_port, int local_port)
snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u " snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u "
"local_port: %u ] %s", (p == tcp?"tcp":"udp"), external_port "local_port: %u ] %s", (p == tcp?"tcp":"udp"), external_port
, local_port, m_disabled ? "DISABLED": ""); , local_port, m_disabled ? "DISABLED": "");
log(msg); log(msg, l);
if (m_disabled) return -1; if (m_disabled) return -1;
std::vector<global_mapping_t>::iterator i = std::find_if( std::vector<global_mapping_t>::iterator i = std::find_if(
@ -194,7 +196,7 @@ int upnp::add_mapping(upnp::protocol_type p, int external_port, int local_port)
m.external_port = external_port; m.external_port = external_port;
m.local_port = local_port; m.local_port = local_port;
if (d.service_namespace) update_map(d, mapping_index); if (d.service_namespace) update_map(d, mapping_index, l);
} }
return mapping_index; return mapping_index;
@ -212,7 +214,7 @@ void upnp::delete_mapping(int mapping)
snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u " snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u "
"local_port: %u ]", (m.protocol == tcp?"tcp":"udp"), m.external_port "local_port: %u ]", (m.protocol == tcp?"tcp":"udp"), m.external_port
, m.local_port); , m.local_port);
log(msg); log(msg, l);
if (m.protocol == none) return; if (m.protocol == none) return;
@ -225,7 +227,7 @@ void upnp::delete_mapping(int mapping)
TORRENT_ASSERT(mapping < int(d.mapping.size())); TORRENT_ASSERT(mapping < int(d.mapping.size()));
d.mapping[mapping].action = mapping_t::action_delete; d.mapping[mapping].action = mapping_t::action_delete;
if (d.service_namespace) update_map(d, mapping); if (d.service_namespace) update_map(d, mapping, l);
} }
} }
@ -252,13 +254,13 @@ void upnp::resend_request(error_code const& e)
if (m_retry_count < 12 if (m_retry_count < 12
&& (m_devices.empty() || m_retry_count < 4)) && (m_devices.empty() || m_retry_count < 4))
{ {
discover_device_impl(); discover_device_impl(l);
return; return;
} }
if (m_devices.empty()) if (m_devices.empty())
{ {
disable(error_code(errors::no_router, libtorrent_category)); disable(error_code(errors::no_router, libtorrent_category), l);
return; return;
} }
@ -277,7 +279,7 @@ void upnp::resend_request(error_code const& e)
#endif #endif
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str()); snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str());
log(msg); log(msg, l);
if (d.upnp_connection) d.upnp_connection->close(); if (d.upnp_connection) d.upnp_connection->close();
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2 , m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
@ -290,7 +292,7 @@ void upnp::resend_request(error_code const& e)
(void)e; (void)e;
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), e.what()); snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), e.what());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
} }
#endif #endif
@ -339,7 +341,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" snprintf(msg, sizeof(msg), "when receiving response from: %s: %s"
, print_endpoint(from).c_str(), ec.message().c_str()); , print_endpoint(from).c_str(), ec.message().c_str());
log(msg); log(msg, l);
} }
else else
{ {
@ -347,7 +349,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
int num_chars = snprintf(msg, sizeof(msg) int num_chars = snprintf(msg, sizeof(msg)
, "ignoring response from: %s. IP is not on local network. " , "ignoring response from: %s. IP is not on local network. "
, print_endpoint(from).c_str()); , print_endpoint(from).c_str());
log(msg); log(msg, l);
std::vector<ip_interface> net = enum_net_interfaces(m_io_service, ec); std::vector<ip_interface> net = enum_net_interfaces(m_io_service, ec);
for (std::vector<ip_interface>::const_iterator i = net.begin() for (std::vector<ip_interface>::const_iterator i = net.begin()
@ -356,7 +358,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) "
, print_address(i->interface_address).c_str(), print_address(i->netmask).c_str()); , print_address(i->interface_address).c_str(), print_address(i->netmask).c_str());
} }
log(msg); log(msg, l);
} }
return; return;
} }
@ -374,7 +376,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "when receiving response from: %s: %s" snprintf(msg, sizeof(msg), "when receiving response from: %s: %s"
, print_endpoint(from).c_str(), ec.message().c_str()); , print_endpoint(from).c_str(), ec.message().c_str());
log(msg); log(msg, l);
} }
else else
{ {
@ -387,7 +389,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) " num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) "
, print_address(i->gateway).c_str(), print_address(i->netmask).c_str()); , print_address(i->gateway).c_str(), print_address(i->netmask).c_str());
} }
log(msg); log(msg, l);
} }
return; return;
} }
@ -402,7 +404,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "received malformed HTTP from: %s" snprintf(msg, sizeof(msg), "received malformed HTTP from: %s"
, print_endpoint(from).c_str()); , print_endpoint(from).c_str());
log(msg); log(msg, l);
return; return;
} }
@ -413,14 +415,14 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "HTTP status %u from %s" snprintf(msg, sizeof(msg), "HTTP status %u from %s"
, p.status_code(), print_endpoint(from).c_str()); , p.status_code(), print_endpoint(from).c_str());
log(msg); log(msg, l);
} }
else else
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "HTTP method %s from %s" snprintf(msg, sizeof(msg), "HTTP method %s from %s"
, p.method().c_str(), print_endpoint(from).c_str()); , p.method().c_str(), print_endpoint(from).c_str());
log(msg); log(msg, l);
} }
return; return;
} }
@ -430,7 +432,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s" snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s"
, print_endpoint(from).c_str()); , print_endpoint(from).c_str());
log(msg); log(msg, l);
return; return;
} }
@ -440,7 +442,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "missing location header from %s" snprintf(msg, sizeof(msg), "missing location header from %s"
, print_endpoint(from).c_str()); , print_endpoint(from).c_str());
log(msg); log(msg, l);
return; return;
} }
@ -464,7 +466,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s" snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s"
, d.url.c_str(), print_endpoint(from).c_str(), ec.message().c_str()); , d.url.c_str(), print_endpoint(from).c_str(), ec.message().c_str());
log(msg); log(msg, l);
return; return;
} }
@ -476,7 +478,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "unsupported protocol %s from %s" snprintf(msg, sizeof(msg), "unsupported protocol %s from %s"
, protocol.c_str(), print_endpoint(from).c_str()); , protocol.c_str(), print_endpoint(from).c_str());
log(msg); log(msg, l);
return; return;
} }
@ -485,21 +487,21 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "URL with port 0 from %s" snprintf(msg, sizeof(msg), "URL with port 0 from %s"
, print_endpoint(from).c_str()); , print_endpoint(from).c_str());
log(msg); log(msg, l);
return; return;
} }
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)" snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)"
, d.url.c_str(), int(m_devices.size())); , d.url.c_str(), int(m_devices.size()));
log(msg); log(msg, l);
if (m_devices.size() >= 50) if (m_devices.size() >= 50)
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s" snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s"
, int(m_devices.size()), d.url.c_str()); , int(m_devices.size()), d.url.c_str());
log(msg); log(msg, l);
return; return;
} }
@ -536,7 +538,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "connecting to: %s" snprintf(msg, sizeof(msg), "connecting to: %s"
, d.url.c_str()); , d.url.c_str());
log(msg); log(msg, l);
if (d.upnp_connection) d.upnp_connection->close(); if (d.upnp_connection) d.upnp_connection->close();
d.upnp_connection.reset(new http_connection(m_io_service d.upnp_connection.reset(new http_connection(m_io_service
@ -552,7 +554,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "connection failed to: %s %s" snprintf(msg, sizeof(msg), "connection failed to: %s %s"
, d.url.c_str(), e.what()); , d.url.c_str(), e.what());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
} }
#endif #endif
@ -562,7 +564,7 @@ void upnp::on_reply(udp::endpoint const& from, char* buffer
} }
void upnp::post(upnp::rootdevice const& d, char const* soap void upnp::post(upnp::rootdevice const& d, char const* soap
, char const* soap_action) , char const* soap_action, mutex_t::scoped_lock& l)
{ {
TORRENT_ASSERT(d.magic == 1337); TORRENT_ASSERT(d.magic == 1337);
TORRENT_ASSERT(d.upnp_connection); TORRENT_ASSERT(d.upnp_connection);
@ -582,7 +584,7 @@ void upnp::post(upnp::rootdevice const& d, char const* soap
char msg[400]; char msg[400];
snprintf(msg, sizeof(msg), "sending: %s", header); snprintf(msg, sizeof(msg), "sending: %s", header);
log(msg); log(msg, l);
} }
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i) void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
@ -596,7 +598,7 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
TORRENT_ASSERT(d.disabled); TORRENT_ASSERT(d.disabled);
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "mapping %u aborted", i); snprintf(msg, sizeof(msg), "mapping %u aborted", i);
log(msg); log(msg, l);
return; return;
} }
@ -623,14 +625,14 @@ void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
, print_address(c.socket().local_endpoint(ec).address()).c_str() , print_address(c.socket().local_endpoint(ec).address()).c_str()
, m_user_agent.c_str(), d.lease_duration, soap_action); , m_user_agent.c_str(), d.lease_duration, soap_action);
post(d, soap, soap_action); post(d, soap, soap_action, l);
} }
void upnp::next(rootdevice& d, int i) void upnp::next(rootdevice& d, int i, mutex_t::scoped_lock& l)
{ {
if (i < num_mappings() - 1) if (i < num_mappings() - 1)
{ {
update_map(d, i + 1); update_map(d, i + 1, l);
} }
else else
{ {
@ -639,11 +641,11 @@ void upnp::next(rootdevice& d, int i)
, boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none)); , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none));
if (i == d.mapping.end()) return; if (i == d.mapping.end()) return;
update_map(d, i - d.mapping.begin()); update_map(d, i - d.mapping.begin(), l);
} }
} }
void upnp::update_map(rootdevice& d, int i) void upnp::update_map(rootdevice& d, int i, mutex_t::scoped_lock& l)
{ {
TORRENT_ASSERT(d.magic == 1337); TORRENT_ASSERT(d.magic == 1337);
TORRENT_ASSERT(i < int(d.mapping.size())); TORRENT_ASSERT(i < int(d.mapping.size()));
@ -658,9 +660,9 @@ void upnp::update_map(rootdevice& d, int i)
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i); snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i);
log(msg); log(msg, l);
m.action = mapping_t::action_none; m.action = mapping_t::action_none;
next(d, i); next(d, i, l);
return; return;
} }
@ -669,14 +671,14 @@ void upnp::update_map(rootdevice& d, int i)
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str()); snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str());
log(msg); log(msg, l);
if (m.action == mapping_t::action_add) if (m.action == mapping_t::action_add)
{ {
if (m.failcount > 5) if (m.failcount > 5)
{ {
m.action = mapping_t::action_none; m.action = mapping_t::action_none;
// giving up // giving up
next(d, i); next(d, i, l);
return; return;
} }
@ -714,7 +716,7 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
TORRENT_ASSERT(d.disabled); TORRENT_ASSERT(d.disabled);
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "unmapping %u aborted", i); snprintf(msg, sizeof(msg), "unmapping %u aborted", i);
log(msg); log(msg, l);
return; return;
} }
@ -734,7 +736,7 @@ void upnp::delete_port_mapping(rootdevice& d, int i)
, (d.mapping[i].protocol == udp ? "UDP" : "TCP") , (d.mapping[i].protocol == udp ? "UDP" : "TCP")
, soap_action); , soap_action);
post(d, soap, soap_action); post(d, soap, soap_action, l);
} }
namespace namespace
@ -848,7 +850,7 @@ void upnp::on_upnp_xml(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s"
, d.url.c_str(), e.message().c_str()); , d.url.c_str(), e.message().c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
@ -858,7 +860,7 @@ void upnp::on_upnp_xml(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message" snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message"
, d.url.c_str()); , d.url.c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
@ -868,7 +870,7 @@ void upnp::on_upnp_xml(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s" snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s"
, d.url.c_str(), p.message().c_str()); , d.url.c_str(), p.message().c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
@ -899,7 +901,7 @@ void upnp::on_upnp_xml(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s" snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s"
, d.url.c_str()); , d.url.c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
@ -932,7 +934,7 @@ void upnp::on_upnp_xml(error_code const& e
"urlbase: %s in response from %s" "urlbase: %s in response from %s"
, d.control_url.c_str(), d.service_namespace , d.control_url.c_str(), d.service_namespace
, s.url_base.c_str(), d.url.c_str()); , s.url_base.c_str(), d.url.c_str());
log(msg); log(msg, l);
boost::tie(protocol, auth, d.hostname, d.port, d.path) boost::tie(protocol, auth, d.hostname, d.port, d.path)
= parse_url_components(d.control_url, ec); = parse_url_components(d.control_url, ec);
@ -942,15 +944,15 @@ void upnp::on_upnp_xml(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s" snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s"
, d.control_url.c_str(), ec.message().c_str()); , d.control_url.c_str(), ec.message().c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
if (num_mappings() > 0) update_map(d, 0); if (num_mappings() > 0) update_map(d, 0, l);
} }
void upnp::disable(error_code const& ec) void upnp::disable(error_code const& ec, mutex_t::scoped_lock& l)
{ {
m_disabled = true; m_disabled = true;
@ -960,7 +962,9 @@ void upnp::disable(error_code const& ec)
{ {
if (i->protocol == none) continue; if (i->protocol == none) continue;
i->protocol = none; i->protocol = none;
l.unlock();
m_callback(i - m_mappings.begin(), 0, ec); m_callback(i - m_mappings.begin(), 0, ec);
l.lock();
} }
// we cannot clear the devices since there // we cannot clear the devices since there
@ -1077,7 +1081,7 @@ void upnp::on_upnp_map_response(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while adding port map: %s" snprintf(msg, sizeof(msg), "error while adding port map: %s"
, e.message().c_str()); , e.message().c_str());
log(msg); log(msg, l);
d.disabled = true; d.disabled = true;
return; return;
} }
@ -1103,8 +1107,8 @@ void upnp::on_upnp_map_response(error_code const& e
if (!p.header_finished()) if (!p.header_finished())
{ {
log("error while adding port map: incomplete http message"); log("error while adding port map: incomplete http message", l);
next(d, mapping); next(d, mapping, l);
return; return;
} }
@ -1120,7 +1124,7 @@ void upnp::on_upnp_map_response(error_code const& e
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while adding port map, code: %u" snprintf(msg, sizeof(msg), "error while adding port map, code: %u"
, s.error_code); , s.error_code);
log(msg); log(msg, l);
} }
mapping_t& m = d.mapping[mapping]; mapping_t& m = d.mapping[mapping];
@ -1131,7 +1135,7 @@ void upnp::on_upnp_map_response(error_code const& e
d.lease_duration = 0; d.lease_duration = 0;
m.action = mapping_t::action_add; m.action = mapping_t::action_add;
++m.failcount; ++m.failcount;
update_map(d, mapping); update_map(d, mapping, l);
return; return;
} }
else if (s.error_code == 718 || s.error_code == 727) else if (s.error_code == 718 || s.error_code == 727)
@ -1143,10 +1147,10 @@ void upnp::on_upnp_map_response(error_code const& e
m.external_port = 0; m.external_port = 0;
m.action = mapping_t::action_add; m.action = mapping_t::action_add;
++m.failcount; ++m.failcount;
update_map(d, mapping); update_map(d, mapping, l);
return; return;
} }
return_error(mapping, s.error_code); return_error(mapping, s.error_code, l);
} }
else if (s.error_code == 716) else if (s.error_code == 716)
{ {
@ -1155,22 +1159,24 @@ void upnp::on_upnp_map_response(error_code const& e
m.external_port = 40000 + (std::rand() % 10000); m.external_port = 40000 + (std::rand() % 10000);
m.action = mapping_t::action_add; m.action = mapping_t::action_add;
++m.failcount; ++m.failcount;
update_map(d, mapping); update_map(d, mapping, l);
return; return;
} }
else if (s.error_code != -1) else if (s.error_code != -1)
{ {
return_error(mapping, s.error_code); return_error(mapping, s.error_code, l);
} }
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "map response: %s" snprintf(msg, sizeof(msg), "map response: %s"
, std::string(p.get_body().begin, p.get_body().end).c_str()); , std::string(p.get_body().begin, p.get_body().end).c_str());
log(msg); log(msg, l);
if (s.error_code == -1) if (s.error_code == -1)
{ {
l.unlock();
m_callback(mapping, m.external_port, error_code()); m_callback(mapping, m.external_port, error_code());
l.lock();
if (d.lease_duration > 0) if (d.lease_duration > 0)
{ {
m.expires = time_now() m.expires = time_now()
@ -1191,10 +1197,10 @@ void upnp::on_upnp_map_response(error_code const& e
m.failcount = 0; m.failcount = 0;
} }
next(d, mapping); next(d, mapping, l);
} }
void upnp::return_error(int mapping, int code) void upnp::return_error(int mapping, int code, mutex_t::scoped_lock& l)
{ {
int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
error_code_t* end = error_codes + num_errors; error_code_t* end = error_codes + num_errors;
@ -1208,7 +1214,9 @@ void upnp::return_error(int mapping, int code)
error_string += ": "; error_string += ": ";
error_string += e->msg; error_string += e->msg;
} }
l.unlock();
m_callback(mapping, 0, error_code(code, upnp_category)); m_callback(mapping, 0, error_code(code, upnp_category));
l.lock();
} }
void upnp::on_upnp_unmap_response(error_code const& e void upnp::on_upnp_unmap_response(error_code const& e
@ -1228,29 +1236,29 @@ void upnp::on_upnp_unmap_response(error_code const& e
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while deleing portmap: %s", e.message().c_str()); snprintf(msg, sizeof(msg), "error while deleing portmap: %s", e.message().c_str());
log(msg); log(msg, l);
} }
else if (!p.header_finished()) else if (!p.header_finished())
{ {
log("error while deleting portmap: incomplete http message"); log("error while deleting portmap: incomplete http message", l);
} }
else if (p.status_code() != 200) else if (p.status_code() != 200)
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "error while deleing portmap: %s", p.message().c_str()); snprintf(msg, sizeof(msg), "error while deleing portmap: %s", p.message().c_str());
log(msg); log(msg, l);
} }
else else
{ {
char msg[200]; char msg[200];
snprintf(msg, sizeof(msg), "unmap response: %s" snprintf(msg, sizeof(msg), "unmap response: %s"
, std::string(p.get_body().begin, p.get_body().end).c_str()); , std::string(p.get_body().begin, p.get_body().end).c_str());
log(msg); log(msg, l);
} }
d.mapping[mapping].protocol = none; d.mapping[mapping].protocol = none;
next(d, mapping); next(d, mapping, l);
} }
void upnp::on_expire(error_code const& e) void upnp::on_expire(error_code const& e)
@ -1275,7 +1283,7 @@ void upnp::on_expire(error_code const& e)
if (d.mapping[m].expires < now) if (d.mapping[m].expires < now)
{ {
d.mapping[m].expires = max_time(); d.mapping[m].expires = max_time();
update_map(d, m); update_map(d, m, l);
} }
else if (d.mapping[m].expires < next_expire) else if (d.mapping[m].expires < next_expire)
{ {
@ -1319,7 +1327,7 @@ void upnp::close()
j->action = mapping_t::action_delete; j->action = mapping_t::action_delete;
m_mappings[j - d.mapping.begin()].protocol = none; m_mappings[j - d.mapping.begin()].protocol = none;
} }
if (num_mappings() > 0) update_map(d, 0); if (num_mappings() > 0) update_map(d, 0, l);
} }
} }